添加峰区域显示控制;Plot的X轴缩放拖动;修改部分BUG;

This commit is contained in:
徐海 2026-03-13 15:36:08 +08:00
parent 96e9fe9e60
commit 2e7528ac40
6 changed files with 244 additions and 11 deletions

View File

@ -4,12 +4,35 @@
#include <QwtPlotCanvas>
#include <QwtText>
#include <QwtPlotMarker>
#include <QwtPlotZoneItem>
#include <QwtScaleMap>
#include <QwtScaleDiv>
#include <QPen>
#include <QMouseEvent>
#include <QwtScaleWidget>
CustomQwtPlot::CustomQwtPlot(QWidget *parent)
: QwtPlot(parent)
{
}
void CustomQwtPlot::SetEventFilterFunc(std::function<bool (QObject *, QEvent *)> event_filter_func)
{
this->_event_filter_func = event_filter_func;
}
void CustomQwtPlot::SetXaxisDragScale(bool enable)
{
QwtScaleWidget *x_scale = axisWidget(QwtPlot::xBottom);
x_scale->setMouseTracking(enable);
x_scale->installEventFilter(enable ? this : nullptr);
}
void CustomQwtPlot::ResetPlot()
{
this->setAxisScale(QwtPlot::xBottom, _init_x_min, _init_x_max);
this->setAxisScale(QwtPlot::yLeft, _init_y_min, _init_y_max);
this->replot();
}
QwtPlotCurve *CustomQwtPlot::GetCurve(const QString &curve_name)
@ -81,6 +104,141 @@ void CustomQwtPlot::CleanMarkers()
this->_markers.clear();
}
QwtPlotZoneItem* CustomQwtPlot::GetZoneItem(const QString& zone_item_name)
{
return _zone_items.value(zone_item_name);
}
QList<QwtPlotZoneItem *> CustomQwtPlot::GetZoneItemList() const
{
return this->_zone_items.values();
}
void CustomQwtPlot::AddZoneItem(QwtPlotZoneItem *zone_item, const QString &zone_item_name)
{
if (zone_item) {
zone_item->setPen(Qt::transparent); // 无边框
zone_item->setBrush(QColor(0, 120, 215, 95)); // 半透明蓝色RGBAA=80 透明度)
zone_item->attach(this);
_zone_items[zone_item_name] = zone_item;
}
}
void CustomQwtPlot::RemoveZoneItem(const QString& zone_item_name)
{
QwtPlotZoneItem* zone_item = GetZoneItem(zone_item_name);
if (zone_item) {
zone_item->detach();
delete zone_item;
}
_zone_items.remove(zone_item_name);
}
void CustomQwtPlot::CleanZoneItems()
{
QList<QwtPlotZoneItem *> zone_items = GetZoneItemList();
for (auto zone_item : zone_items) {
if (zone_item) {
zone_item->detach();
delete zone_item;
}
}
this->_zone_items.clear();
}
bool CustomQwtPlot::eventFilter(QObject *obj, QEvent *event)
{
if (this->_event_filter_func) {
bool filted = this->_event_filter_func(obj, event);
if (filted) {
return filted;
}
}
if (dynamic_cast<QwtScaleWidget*>(obj) != this->axisWidget(QwtPlot::xBottom))
return false;
QMouseEvent *me = static_cast<QMouseEvent*>(event);
QwtScaleMap map = this->canvasMap(QwtPlot::xBottom);
// ===================== 1. 鼠标拖动 X 轴 =====================
if (event->type() == QEvent::MouseButtonPress) {
this->_is_dragging = true;
this->_last_pos = me->pos();
return true;
} else if (event->type() == QEvent::MouseMove && this->_is_dragging) {
// 计算坐标偏移
double old_x = map.invTransform(_last_pos.x());
double new_x = map.invTransform(me->pos().x());
double dx = old_x - new_x;
// 移动 X 轴
double x_min = axisScaleDiv(QwtPlot::xBottom).lowerBound() + dx;
double x_max = axisScaleDiv(QwtPlot::xBottom).upperBound() + dx;
this->setAxisScale(QwtPlot::xBottom, x_min, x_max);
this->replot();
this->_last_pos = me->pos();
return true;
} else if (event->type() == QEvent::MouseButtonRelease) {
this->_is_dragging = false;
return true;
}
// ===================== 2. 滚轮缩放 X 轴 =====================
if (event->type() == QEvent::Wheel) {
QWheelEvent *we = static_cast<QWheelEvent*>(event);
double scale = we->angleDelta().y() > 0 ? 0.8 : 1.25; // 放大/缩小
// 以鼠标所在位置为中心缩放
double mouse_x = map.invTransform(we->pos().x());
double x1 = this->axisScaleDiv(QwtPlot::xBottom).lowerBound();
double x2 = this->axisScaleDiv(QwtPlot::xBottom).upperBound();
double new1 = mouse_x - (mouse_x - x1) * scale;
double new2 = mouse_x + (x2 - mouse_x) * scale;
this->setAxisScale(QwtPlot::xBottom, new1, new2);
this->replot();
return true;
}
return QwtPlot::eventFilter(obj, event);
}
void CustomQwtPlot::showEvent(QShowEvent *event)
{
Q_UNUSED(event);
this->updateAxes();
double x_min = this->axisScaleDiv(QwtPlot::xBottom).lowerBound();
double x_max = this->axisScaleDiv(QwtPlot::xBottom).upperBound();
double y_min = this->axisScaleDiv(QwtPlot::yLeft).lowerBound();
double y_max = this->axisScaleDiv(QwtPlot::yLeft).upperBound();
this->_init_x_min = x_min;
this->_init_x_max = x_max;
this->_init_y_min = y_min;
this->_init_y_max = y_max;
}
CustomQwtPlotXaxisPanner::CustomQwtPlotXaxisPanner(QWidget *canvas)
: QwtPlotPanner(canvas)
{
}
void CustomQwtPlotXaxisPanner::moveCanvas(int dx, int dy)
{
QwtPlotPanner::moveCanvas(dx, 0);
}
CustomQwtPlotXaxisMagnifier::CustomQwtPlotXaxisMagnifier(QWidget *canvas)
: QwtPlotMagnifier(canvas)
{
}
void CustomQwtPlotXaxisMagnifier::rescale(double factor)
{
factor = qBound(0.1, factor, 10.0);
QwtScaleMap x_map = plot()->canvasMap(QwtPlot::xBottom);
double center = x_map.invTransform(plot()->canvas()->width() / 2);
plot()->setAxisScale(
QwtPlot::xBottom,
center - (center - x_map.s1()) * factor,
center + (x_map.s2() - center) * factor
);
plot()->replot();
}
QColor getDistinctColorForManyCurves(int curve_index)
{
// 1. 定义基础色相覆盖不同主色系0-360度

View File

@ -3,15 +3,22 @@
#include <QMap>
#include <QwtPlot>
#include <QwtPlotPanner>
#include <QwtPlotMagnifier>
class QwtPlotCurve;
class QwtPlotMarker;
class QwtPlotZoneItem;
class CustomQwtPlot : public QwtPlot
{
public:
explicit CustomQwtPlot(QWidget* parent = nullptr);
void SetEventFilterFunc(std::function<bool(QObject*, QEvent*)> event_filter_func);
void SetXaxisDragScale(bool enable);
void ResetPlot();
QwtPlotCurve* GetCurve(const QString& curve_name);
QList<QwtPlotCurve*> GetCurveList() const;
void AddCurve(QwtPlotCurve* curve);
@ -22,11 +29,44 @@ public:
void RemoveMarker(const QString& marker_name, const QString& postion);
void CleanMarkers();
QwtPlotZoneItem* GetZoneItem(const QString& zone_item_name);
QList<QwtPlotZoneItem*> GetZoneItemList() const;
void AddZoneItem(QwtPlotZoneItem *zone_item, const QString &zone_item_name);
void RemoveZoneItem(const QString& zone_item_name);
void CleanZoneItems();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
virtual void showEvent(QShowEvent *event) override;
private:
double _init_x_min = 0, _init_x_max = 10;
double _init_y_min = 0, _init_y_max = 10;
bool _is_dragging = false;
QPoint _last_pos;
std::function<bool(QObject*, QEvent*)> _event_filter_func = nullptr;
private:
QMap<QString, QwtPlotCurve*> _curves;
QMap<QString, QMap<QString, QwtPlotMarker*> > _markers;
QMap<QString, QwtPlotZoneItem*> _zone_items;
};
class CustomQwtPlotXaxisPanner : public QwtPlotPanner
{
public:
CustomQwtPlotXaxisPanner(QWidget *canvas);
protected:
virtual void moveCanvas(int dx, int dy) override;
};
class CustomQwtPlotXaxisMagnifier : public QwtPlotMagnifier
{
public:
CustomQwtPlotXaxisMagnifier(QWidget *canvas);
protected:
virtual void rescale(double factor) override;
};
QColor getDistinctColorForManyCurves(int curve_index);

View File

@ -25,6 +25,8 @@
#include <fstream>
#include <QHeaderView>
#include <QwtPlotMarker>
#include <QwtPlotZoneItem>
#include <QwtScaleMap>
static auto extractNumber = [](const QString& str) {
int ret_num = 0;
@ -97,6 +99,12 @@ void MeasureAnalysisParticleCountPlotView::setupMenu()
connect(this, &MeasureAnalysisParticleCountPlotView::customContextMenuRequested, [this](const QPoint &pos){
this->_menu->exec(this->mapToGlobal(pos));
});
QAction* action_plot_reset = this->_menu->addAction(QStringLiteral(u"还原"));
action_plot_reset->setObjectName("curve_show_setting");
connect(action_plot_reset, &QAction::triggered, [this](){
this->_plot->ResetPlot();
});
this->_menu->addSeparator();
QAction* action_set_curve_show = this->_menu->addAction(QStringLiteral(u"选择曲线"));
action_set_curve_show->setObjectName("curve_show_setting");
connect(action_set_curve_show, &QAction::triggered, this, &MeasureAnalysisParticleCountPlotView::onCurveShowSetting);
@ -137,8 +145,12 @@ void MeasureAnalysisParticleCountPlotView::setupPlot()
// 设置QWT图例
QwtLegend* legend = new QwtLegend();
legend->setDefaultItemMode(QwtLegendData::Checkable);
legend->setDefaultItemMode(QwtLegendData::ReadOnly);
_plot->insertLegend(legend, QwtPlot::RightLegend);
// new CustomQwtPlotXaxisPanner(_plot->canvas());
new CustomQwtPlotXaxisMagnifier(_plot->canvas());
_plot->SetXaxisDragScale(true);
}
void MeasureAnalysisParticleCountPlotView::loadDataFromFile(const QString& data_name, const QString& filename)
@ -239,10 +251,11 @@ void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfo(QVariantMap peak_i
int right_bound = peak_infos["right_bound"].toInt();
int peak_width = peak_infos["peak_width"].toInt();
bool is_checked = peak_infos["checked"].toBool();
bool is_show_peak_area = peak_infos["show_peak_area"].toBool();
const QString& postion = QString::number(peak_pos);
QwtPlotMarker* peak_marker = this->_plot->GetMarker(channel, postion);
if (!peak_marker && is_checked) {
if ((!peak_marker) && is_checked) {
peak_marker = new QwtPlotMarker();
peak_marker->setLineStyle(QwtPlotMarker::VLine);
peak_marker->setValue(peak_pos, 0.0);
@ -250,13 +263,22 @@ void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfo(QVariantMap peak_i
const QString& label_text = QStringLiteral(u"峰位:%1\n峰宽:%2\n左界:%3\n右界:%4\n").arg(postion).arg(peak_width).arg(left_bound).arg(right_bound);
peak_marker->setLabel(label_text);
this->_plot->AddMarker(peak_marker, channel, postion);
} else {
} else if (!is_checked) {
this->_plot->RemoveMarker(channel, postion);
}
QwtPlotZoneItem* peak_area_zone_item = this->_plot->GetZoneItem(channel);
if (!peak_area_zone_item && is_show_peak_area) {
peak_area_zone_item = new QwtPlotZoneItem;
peak_area_zone_item->setOrientation(Qt::Vertical);
peak_area_zone_item->setInterval(left_bound, right_bound);
this->_plot->AddZoneItem(peak_area_zone_item, channel);
} else if ((!is_checked) || (!is_show_peak_area)) {
this->_plot->RemoveZoneItem(channel);
}
this->_plot->replot();
}
void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfoByTableItem(QTableWidgetItem *item, bool checked)
void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfoByTableItem(QTableWidgetItem *item, bool checked, bool show_peak_area)
{
if (item) {
auto peaks_result_table = item->tableWidget();
@ -274,6 +296,7 @@ void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfoByTableItem(QTableW
peak_infos["right_bound"] = right_bound;
peak_infos["peak_width"] = peak_width;
peak_infos["checked"] = is_checked || checked;
peak_infos["show_peak_area"] = show_peak_area || checked;
this->updatePlotPeakInfo(peak_infos);
qDebug() << channel << ", " << peak_pos << ", " << bool(is_checked || checked);
}
@ -312,7 +335,17 @@ void MeasureAnalysisParticleCountPlotView::onCurveShowSetting()
if (num_columns == 0) num_columns = 1;
QVBoxLayout* checkbox_layout = new QVBoxLayout();
QHBoxLayout* checkbox_column_layout = new QHBoxLayout();
QStringList list_ch_names;
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
list_ch_names.append(curve->title().text());
}
std::sort(list_ch_names.begin(), list_ch_names.end(), [](const QString& a, const QString& b) {
int num_a = extractNumber(a);
int num_b = extractNumber(b);
return num_a < num_b;
});
for (const QString& ch_name : list_ch_names) {
QwtPlotCurve* curve = this->_plot->GetCurve(ch_name);
QCheckBox* check_box = new QCheckBox(curve->title().text());
check_box->setChecked(curve->isVisible());
checkbox_column_layout->addWidget(check_box);
@ -419,6 +452,7 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
this->loadPeaksResultToTable(peaks_result_table);
connect(filter_channel_combo_box, &QComboBox::currentTextChanged, [this, peaks_result_table](const QString& text){
peaks_result_table->setCurrentItem(nullptr);
auto row_count = peaks_result_table->rowCount();
if (text == QString(QStringLiteral(u"所有通道"))) {
for (int i = 0; i < row_count - 1; i++) {
@ -433,6 +467,7 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
}
});
connect(btn_all_select, &QPushButton::clicked, [peaks_result_table](){
peaks_result_table->setCurrentItem(nullptr);
auto row_count = peaks_result_table->rowCount();
for (int i = 0; i < row_count - 1; i++) {
if (peaks_result_table->isRowHidden(i)) {
@ -445,6 +480,7 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
}
});
connect(btn_reserve_select, &QPushButton::clicked, [peaks_result_table](){
peaks_result_table->setCurrentItem(nullptr);
auto row_count = peaks_result_table->rowCount();
for (int i = 0; i < row_count - 1; i++) {
if (peaks_result_table->isRowHidden(i)) {
@ -488,8 +524,8 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
connect(peaks_result_table, &QTableWidget::currentItemChanged, [this, peaks_result_table](QTableWidgetItem *current, QTableWidgetItem *previous){
bool is_watch_item_changed = peaks_result_table->property("WatchItemChanged").toBool();
if (is_watch_item_changed) {
this->updatePlotPeakInfoByTableItem(previous, false);
this->updatePlotPeakInfoByTableItem(current, true);
this->updatePlotPeakInfoByTableItem(previous, false, false);
this->updatePlotPeakInfoByTableItem(current, true, true);
}
});
@ -541,7 +577,6 @@ void MeasureAnalysisParticleCountPlotView::onAutoFindPeaks()
auto_find_peaks_task->SetFinishedNotifier(this, "onAutoFindPeaksFinished", project_name);
auto_find_peaks_task->StartTask();
}
}
void MeasureAnalysisParticleCountPlotView::onManualFindPeaks()

View File

@ -27,7 +27,7 @@ private:
void loadDataFromFile(const QString &data_name, const QString& filename);
void loadPeaksResultToTable(QTableWidget* peaks_result_table);
void updatePlotPeakInfo(QVariantMap peak_infos);
void updatePlotPeakInfoByTableItem(QTableWidgetItem* item, bool checked = false);
void updatePlotPeakInfoByTableItem(QTableWidgetItem* item, bool checked = false, bool show_peak_area = false);
private slots:
void onAutoFindPeaksFinished(const QString& project_name);

View File

@ -13,7 +13,7 @@ MeasureAnalysisTreeView::MeasureAnalysisTreeView(QWidget* parent)
// header_view->setSectionResizeMode(QHeaderView::Interactive);
// header_view->setMinimumSectionSize(10);
// header_view->resizeSection(1, 20);
header_view->setSectionResizeMode(0, QHeaderView::Stretch);
header_view->setSectionResizeMode(0, QHeaderView::Interactive);
header_view->setSectionResizeMode(_model->columnCount() - 1, QHeaderView::Fixed);
header_view->resizeSection(_model->columnCount() - 1, 20);

View File

@ -48,7 +48,7 @@ int main(int argc, char *argv[])
app.setFont(f);
// 设置应用图标和关闭属性
// 设置样式
app.setStyle(QStyleFactory::create("Fusion"));
// app.setStyle(QStyleFactory::create("Fusion"));
app.setWindowIcon(QIcon(":/logo/256.png"));
app.setQuitOnLastWindowClosed(true);