添加峰显示

This commit is contained in:
徐海 2026-03-12 20:23:55 +08:00
parent fcb94825ca
commit 96e9fe9e60
12 changed files with 246 additions and 58 deletions

View File

@ -2,6 +2,8 @@
#include <QwtPlotCurve>
#include <QwtLegend>
#include <QwtPlotCanvas>
#include <QwtText>
#include <QwtPlotMarker>
#include <QPen>
CustomQwtPlot::CustomQwtPlot(QWidget *parent)
@ -10,20 +12,75 @@ CustomQwtPlot::CustomQwtPlot(QWidget *parent)
}
const QList<QwtPlotCurve *> &CustomQwtPlot::GetCurveList() const
QwtPlotCurve *CustomQwtPlot::GetCurve(const QString &curve_name)
{
return _curves;
return _curves.value(curve_name, nullptr);
}
QList<QwtPlotCurve *> CustomQwtPlot::GetCurveList() const
{
return _curves.values();
}
void CustomQwtPlot::AddCurve(QwtPlotCurve *curve)
{
if (curve) {
curve->setPen(QPen(getDistinctColorForManyCurves(_curves.count())));
curve->setPen(QPen(getDistinctColorForManyCurves(_curves.count()), 1));
curve->attach(this);
_curves.append(curve);
_curves[curve->title().text()] = curve;
}
}
QwtPlotMarker *CustomQwtPlot::GetMarker(const QString &marker_name, const QString &postion)
{
return _markers.value(marker_name).value(postion);
}
QList<QwtPlotMarker *> CustomQwtPlot::GetMarkerList() const
{
QList<QwtPlotMarker *> markers;
for (auto tmp_markers : this->_markers) {
markers.append(tmp_markers.values());
}
return markers;
}
void CustomQwtPlot::AddMarker(QwtPlotMarker *marker, const QString &marker_name, const QString &postion)
{
if (marker) {
QwtPlotCurve* curve = GetCurve(marker_name);
if (curve) {
QPen pen = curve->pen();
pen.setWidth(2);
marker->setLinePen(pen);
marker->attach(this);
_markers[marker_name][postion] = marker;
}
}
}
void CustomQwtPlot::RemoveMarker(const QString &marker_name, const QString &postion)
{
QwtPlotMarker* marker = GetMarker(marker_name, postion);
if (marker) {
marker->detach();
delete marker;
}
_markers[marker_name].remove(postion);
}
void CustomQwtPlot::CleanMarkers()
{
QList<QwtPlotMarker *> markers = GetMarkerList();
for (auto marker : markers) {
if (marker) {
marker->detach();
delete marker;
}
}
this->_markers.clear();
}
QColor getDistinctColorForManyCurves(int curve_index)
{
// 1. 定义基础色相覆盖不同主色系0-360度

View File

@ -1,23 +1,33 @@
#ifndef CUSTOMQWTPLOT_H
#define CUSTOMQWTPLOT_H
#include <QList>
#include <QMap>
#include <QwtPlot>
class QwtPlotCurve;
class QwtPlotMarker;
class CustomQwtPlot : public QwtPlot
{
public:
explicit CustomQwtPlot(QWidget* parent = nullptr);
const QList<QwtPlotCurve*>& GetCurveList() const;
QwtPlotCurve* GetCurve(const QString& curve_name);
QList<QwtPlotCurve*> GetCurveList() const;
void AddCurve(QwtPlotCurve* curve);
QwtPlotMarker* GetMarker(const QString& marker_name, const QString& postion);
QList<QwtPlotMarker*> GetMarkerList() const;
void AddMarker(QwtPlotMarker *marker, const QString &marker_name, const QString &postion);
void RemoveMarker(const QString& marker_name, const QString& postion);
void CleanMarkers();
private:
QList<QwtPlotCurve*> _curves;
QMap<QString, QwtPlotCurve*> _curves;
QMap<QString, QMap<QString, QwtPlotMarker*> > _markers;
};
QColor getDistinctColorForManyCurves(int curve_index);
#endif // CUSTOMQWTPLOT_H

View File

@ -4,6 +4,7 @@
#include "MainWindow.h"
#include "QsLog.h"
#include <QTextCodec>
#include <QDebug>
// 转换Qt字符串路径为系统编码的C字符串解决中文路径问题
static const char* QStrToSysPath(const QString& qstr_path)

View File

@ -209,14 +209,9 @@ void MainWindow::initAction()
const QString& text = QStringLiteral(u"是否关闭测量分析项目\"%1\"").arg(project_name);
auto result = QMessageBox::question(this, title, text, QMessageBox::Yes | QMessageBox::No);
if (QMessageBox::Yes == result) {
project_model->SaveProjectModel();
if(ProjectList::Instance()->RmProjectModel(project_name)) {
const QString& info_text = QStringLiteral(u"测试分析项\"%1\"已关闭.").arg(project_name);
LOG_INFO(info_text);
}
this->closeProject(project_name);
}
}
});
connect(ui->action_manage_measurement_analysis, &QAction::triggered, this->_action_central_dock_widget, &QAction::triggered);
connect(ui->action_device_config_mrg, &QAction::triggered, []() {
@ -245,7 +240,6 @@ void MainWindow::initAction()
AboutDlg about_dlg;
about_dlg.exec();
});
connect(_tree_measure_analysis, &MeasureAnalysisTreeView::currentItemView, [this](MeasureAnalysisView* view) {
if (view && this->_dock_manager) {
bool view_exist = false;
@ -266,13 +260,6 @@ void MainWindow::initAction()
dock_widget->setMinimumSizeHintMode(ads::CDockWidget::MinimumSizeHintFromDockWidget);
if (view->IsDeleteOnClose()) {
dock_widget->setFeatures(dock_widget->features() | ads::CDockWidget::DockWidgetDeleteOnClose);
} else {
// dock_widget->setFeatures(dock_widget->features() | ads::CDockWidget::CustomCloseHandling);
// connect(dock_widget, &CDockWidget::closeRequested, [dock_widget](){
// dock_widget->takeWidget();
// dock_widget->deleteDockWidget();
// });
dock_widget->setProperty("TakeWidget", true);
}
if ( view->GetViewType() == MeasureAnalysisView::DataTable ) {
_menu_view_data_table_list->addAction(dock_widget->toggleViewAction());
@ -285,7 +272,6 @@ void MainWindow::initAction()
}
}
});
connect(_tree_measure_analysis, &MeasureAnalysisTreeView::removeItemView, [this](MeasureAnalysisView* view) {
if (this->_dock_manager) {
QList<CDockWidget*> dock_widget_list = this->_dock_manager->dockWidgetsMap().values();
@ -293,7 +279,6 @@ void MainWindow::initAction()
CDockWidget* dock_widget = *it;
if (dock_widget) {
if ( dock_widget->widget() == view ) {
QWidget* content = dock_widget->takeWidget();
dock_widget->deleteDockWidget();
}
}
@ -324,23 +309,42 @@ void MainWindow::applyStyleSheet()
}
}
void MainWindow::closeEvent(QCloseEvent* event)
void MainWindow::closeProject(const QString& project_name)
{
// Delete dock manager here to delete all floating widgets. This ensures
// that all top level windows of the dock manager are properly closed
if (this->_dock_manager) {
QList<CDockWidget*> dock_widget_list = this->_dock_manager->dockWidgetsMap().values();
for (auto it = dock_widget_list.constBegin(); it != dock_widget_list.constEnd(); ++it) {
CDockWidget* dock_widget = *it;
if (dock_widget) {
bool need_take_widget = dock_widget->property("TakeWidget").toBool();
if (need_take_widget) {
dock_widget->takeWidget();
auto closeAnalysisView = [this](const QString& project_name){
if (this->_dock_manager) {
QList<CDockWidget*> dock_widget_list = this->_dock_manager->dockWidgetsMap().values();
for (auto it = dock_widget_list.constBegin(); it != dock_widget_list.constEnd(); ++it) {
CDockWidget* dock_widget = *it;
if (dock_widget) {
MeasureAnalysisView* view = dynamic_cast<MeasureAnalysisView*>(dock_widget->widget());
if (view ) {
if ( view->GetProjectName() == project_name ) {
dock_widget->deleteDockWidget();
}
}
}
}
}
};
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model) {
closeAnalysisView(project_name);
project_model->SaveProjectModel();
if(ProjectList::Instance()->RmProjectModel(project_name)) {
const QString& info_text = QStringLiteral(u"测试分析项\"%1\"已关闭.").arg(project_name);
LOG_INFO(info_text);
}
}
}
void MainWindow::closeEvent(QCloseEvent* event)
{
QList<MeasureAnalysisProjectModel *> models = ProjectList::Instance()->GetProjectModels();
for (auto model : models) {
if (model) {
closeProject(model->GetProjectName());
}
}
_dock_manager->deleteLater();
_dock_manager = nullptr;
QMainWindow::closeEvent(event);
}

View File

@ -46,8 +46,7 @@ private:
void initMainWindow();
void initAction();
void applyStyleSheet();
void AddProjectModelToTreeWidget();
void closeProject(const QString &project_name);
protected:
virtual void closeEvent(QCloseEvent* event) override;

View File

@ -3,6 +3,7 @@
#include "VirtualTable/VirtualTableModel.h"
#include "VirtualTable/VirtualTableView.h"
#include <QHBoxLayout>
#include "GlobalDefine.h"
MeasureAnalysisDataTableView::MeasureAnalysisDataTableView(QWidget* parent)
: MeasureAnalysisView { parent }
@ -20,6 +21,11 @@ MeasureAnalysisDataTableView::MeasureAnalysisDataTableView(QWidget* parent)
layout->addWidget(_tableView);
}
MeasureAnalysisDataTableView::~MeasureAnalysisDataTableView()
{
LOG_DEBUG(QStringLiteral(u"%1析构.").arg(this->GetViewName()));
}
void MeasureAnalysisDataTableView::InitViewWorkspace(const QString &project_name)
{
Q_UNUSED(project_name);

View File

@ -15,6 +15,7 @@ class MeasureAnalysisDataTableView : public MeasureAnalysisView
Q_OBJECT
public:
MeasureAnalysisDataTableView(QWidget *parent = nullptr);
virtual ~MeasureAnalysisDataTableView();
virtual void InitViewWorkspace(const QString& project_name) override final;
virtual void SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set);

View File

@ -23,6 +23,18 @@
#include <QTableWidget>
#include <QSpinBox>
#include <fstream>
#include <QHeaderView>
#include <QwtPlotMarker>
static auto extractNumber = [](const QString& str) {
int ret_num = 0;
QRegularExpression regex("\\d+");
QRegularExpressionMatch match = regex.match(str);
if (match.hasMatch()) {
ret_num = match.captured().toInt();
}
return ret_num;
};
MeasureAnalysisParticleCountPlotView::MeasureAnalysisParticleCountPlotView(QWidget* parent)
: MeasureAnalysisView { parent }
@ -39,6 +51,11 @@ MeasureAnalysisParticleCountPlotView::MeasureAnalysisParticleCountPlotView(QWidg
setupMenu();
}
MeasureAnalysisParticleCountPlotView::~MeasureAnalysisParticleCountPlotView()
{
LOG_DEBUG(QStringLiteral(u"%1析构.").arg(this->GetViewName()));
}
void MeasureAnalysisParticleCountPlotView::InitViewWorkspace(const QString &project_name)
{
if (project_name.isEmpty()) {
@ -59,17 +76,8 @@ void MeasureAnalysisParticleCountPlotView::InitViewWorkspace(const QString &proj
void MeasureAnalysisParticleCountPlotView::SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set)
{
auto extractNumber = [](const QString& str) {
int ret_num = 0;
QRegularExpression regex("\\d+");
QRegularExpressionMatch match = regex.match(str);
if (match.hasMatch()) {
ret_num = match.captured().toInt();
}
return ret_num;
};
QStringList ch_count_data_name = data_files_set.keys();
std::sort(ch_count_data_name.begin(), ch_count_data_name.end(), [extractNumber](const QString& a, const QString& b) {
std::sort(ch_count_data_name.begin(), ch_count_data_name.end(), [](const QString& a, const QString& b) {
int num_a = extractNumber(a);
int num_b = extractNumber(b);
return num_a < num_b;
@ -164,14 +172,17 @@ void MeasureAnalysisParticleCountPlotView::loadDataFromFile(const QString& data_
void MeasureAnalysisParticleCountPlotView::loadPeaksResultToTable(QTableWidget *peaks_result_table)
{
this->_plot->CleanMarkers();
this->_plot->replot();
if (!peaks_result_table)
return;
peaks_result_table->setCurrentItem(nullptr);
peaks_result_table->setProperty("WatchItemChanged", false);
auto row_count = peaks_result_table->rowCount();
for (int i = 0; i < row_count - 1; i++) {
for (int i = row_count - 1; i >= 0; i--) {
peaks_result_table->removeRow(i);
}
const QString& channel_col_name = QString(QStringLiteral(u"通道"));
const QString& peak_pos_col_name = QString(QStringLiteral(u"峰位"));
const QString& left_bound_col_name = QString(QStringLiteral(u"左边界"));
@ -207,7 +218,9 @@ void MeasureAnalysisParticleCountPlotView::loadPeaksResultToTable(QTableWidget *
QTableWidgetItem* item = new QTableWidgetItem;
peaks_result_table->setItem(row, 5, item);
QPushButton* btn_remove_row = new QPushButton(QStringLiteral(u"删除"));
connect(btn_remove_row, &QPushButton::clicked, [peaks_result_table, item, btn_remove_row](){
connect(btn_remove_row, &QPushButton::clicked, [this, peaks_result_table, item, btn_remove_row](){
item->setCheckState(Qt::Unchecked);
this->updatePlotPeakInfoByTableItem(item, false);
int remove_row = item->row();
peaks_result_table->removeRow(remove_row);
btn_remove_row->deleteLater();
@ -215,6 +228,55 @@ void MeasureAnalysisParticleCountPlotView::loadPeaksResultToTable(QTableWidget *
peaks_result_table->setCellWidget(row, 5, btn_remove_row);
}
}
peaks_result_table->setProperty("WatchItemChanged", true);
}
void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfo(QVariantMap peak_infos)
{
const QString& channel = peak_infos["channel"].toString();
int peak_pos = peak_infos["peak_pos"].toInt();
int left_bound = peak_infos["left_bound"].toInt();
int right_bound = peak_infos["right_bound"].toInt();
int peak_width = peak_infos["peak_width"].toInt();
bool is_checked = peak_infos["checked"].toBool();
const QString& postion = QString::number(peak_pos);
QwtPlotMarker* peak_marker = this->_plot->GetMarker(channel, postion);
if (!peak_marker && is_checked) {
peak_marker = new QwtPlotMarker();
peak_marker->setLineStyle(QwtPlotMarker::VLine);
peak_marker->setValue(peak_pos, 0.0);
peak_marker->setLabelAlignment(Qt::AlignTop | Qt::AlignRight);
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 {
this->_plot->RemoveMarker(channel, postion);
}
this->_plot->replot();
}
void MeasureAnalysisParticleCountPlotView::updatePlotPeakInfoByTableItem(QTableWidgetItem *item, bool checked)
{
if (item) {
auto peaks_result_table = item->tableWidget();
int row = item->row();
bool is_checked = bool(peaks_result_table->item(row, 0)->checkState() == Qt::Checked);
const QString& channel = peaks_result_table->item(row, 0)->text();
int peak_pos = peaks_result_table->item(row, 1)->text().toInt();
int left_bound = peaks_result_table->item(row, 2)->text().toInt();
int right_bound = peaks_result_table->item(row, 3)->text().toInt();
int peak_width = peaks_result_table->item(row, 4)->text().toInt();
QVariantMap peak_infos;
peak_infos["channel"] = channel;
peak_infos["peak_pos"] = peak_pos;
peak_infos["left_bound"] = left_bound;
peak_infos["right_bound"] = right_bound;
peak_infos["peak_width"] = peak_width;
peak_infos["checked"] = is_checked || checked;
this->updatePlotPeakInfo(peak_infos);
qDebug() << channel << ", " << peak_pos << ", " << bool(is_checked || checked);
}
}
void MeasureAnalysisParticleCountPlotView::onAutoFindPeaksFinished(const QString &project_name)
@ -314,9 +376,18 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
QComboBox* filter_channel_combo_box = new QComboBox();
filter_channel_combo_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
filter_channel_combo_box->addItem(QString(QStringLiteral(u"所有通道")));
QStringList list_ch_names;
for (QwtPlotCurve* curve : this->_plot->GetCurveList()) {
filter_channel_combo_box->addItem(curve->title().text());
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;
});
filter_channel_combo_box->addItems(list_ch_names);
filter_channel_combo_box->setMaxVisibleItems(10);
filter_channel_combo_box->view()->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
QPushButton* btn_save = new QPushButton(QString(QStringLiteral(u"保存")));
QHBoxLayout* top_layout = new QHBoxLayout();
top_layout->addWidget(btn_all_select);
@ -339,8 +410,12 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
peaks_result_table->setHorizontalHeaderLabels({
channel_col_name, peak_pos_col_name, left_bound_col_name, right_bound_col_name, peak_width_col_name, operation_col_name
});
peaks_result_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
peaks_result_table->horizontalHeader()->setSectionResizeMode(peaks_result_table->columnCount() -1, QHeaderView::ResizeToContents);
peaks_result_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter);
peaks_result_table->setSelectionBehavior(QAbstractItemView::SelectRows);
peaks_result_table->setSelectionMode(QAbstractItemView::SingleSelection);
peaks_result_table->setEditTriggers(QTableWidget::NoEditTriggers);
this->loadPeaksResultToTable(peaks_result_table);
connect(filter_channel_combo_box, &QComboBox::currentTextChanged, [this, peaks_result_table](const QString& text){
@ -404,6 +479,19 @@ void MeasureAnalysisParticleCountPlotView::onFindPeaksResult()
}
LOG_INFO(QStringLiteral(u"保存峰信息完成."));
});
connect(peaks_result_table, &QTableWidget::itemChanged, [this, peaks_result_table](QTableWidgetItem *item){
bool is_watch_item_changed = peaks_result_table->property("WatchItemChanged").toBool();
if (is_watch_item_changed && bool(item->column() == 0)) {
this->updatePlotPeakInfoByTableItem(item);
}
});
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);
}
});
QVBoxLayout* layout = new QVBoxLayout(_find_peaks_result_dlg);
layout->addLayout(top_layout);

View File

@ -9,12 +9,14 @@ class QDialog;
class QMenu;
class CustomQwtPlot;
class QTableWidget;
class QTableWidgetItem;
class MeasureAnalysisParticleCountPlotView : public MeasureAnalysisView
{
Q_OBJECT
public:
MeasureAnalysisParticleCountPlotView(QWidget *parent = nullptr);
virtual ~MeasureAnalysisParticleCountPlotView();
virtual void InitViewWorkspace(const QString& project_name) override final;
virtual void SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set);
@ -24,6 +26,8 @@ private:
void setupPlot();
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);
private slots:
void onAutoFindPeaksFinished(const QString& project_name);

View File

@ -7,6 +7,11 @@
#include <QJsonObject>
MeasureAnalysisProjectModel::~MeasureAnalysisProjectModel()
{
LOG_DEBUG(QStringLiteral(u"%1数据模型析构.").arg(this->GetProjectName()));
}
void MeasureAnalysisProjectModel::SetProjectDir(const QString& project_dir)
{
this->_project_dir = project_dir;
@ -493,6 +498,11 @@ MeasureAnalysisProjectModel* MeasureAnalysisProjectModelList::GetCurrentProjectM
return _current_project_model;
}
QList<MeasureAnalysisProjectModel *> MeasureAnalysisProjectModelList::GetProjectModels()
{
return _project_models.values();
}
void MeasureAnalysisProjectModelList::SetCurrentProjectModel(const QString& project_name)
{
if (_project_models.contains(project_name)) {
@ -528,8 +538,8 @@ MeasureAnalysisProjectModelList::MeasureAnalysisProjectModelList(QObject* parent
QStandardItem* MeasureAnalysisProjectModelList::GetItemFromIndex(const QModelIndex& index) const
{
QModelIndex nameIndex = index.sibling(index.row(), NameColumn);
return itemFromIndex(nameIndex);
QModelIndex name_index = index.sibling(index.row(), NameColumn);
return itemFromIndex(name_index);
}
QStandardItem* MeasureAnalysisProjectModelList::AddChildNode(

View File

@ -6,6 +6,8 @@
#include <QStandardItemModel>
#include "AnalysisTypeDefine.h"
class MeasureAnalysisView;
class MeasureAnalysisProjectModel
{
public:
@ -16,6 +18,8 @@ public:
};
public:
virtual ~MeasureAnalysisProjectModel();
void SetProjectDir(const QString& project_dir);
void SetProjectName(const QString& project_name);
void SetSpectrumType(SpectrumType spec_type);
@ -132,6 +136,7 @@ public:
MeasureAnalysisProjectModel* GetProjectModel(const QString& project_name);
MeasureAnalysisProjectModel* GetCurrentProjectModel();
QList<MeasureAnalysisProjectModel*> GetProjectModels();
void SetCurrentProjectModel(const QString& project_name);
public:

View File

@ -9,10 +9,13 @@ MeasureAnalysisTreeView::MeasureAnalysisTreeView(QWidget* parent)
_model = ProjectList::Instance();
this->setModel(_model);
QHeaderView* header_view = header();
header_view->setSectionResizeMode(QHeaderView::Interactive);
header_view->setMinimumSectionSize(10);
header_view->resizeSection(1, 20);
QHeaderView* header_view = this->header();
// header_view->setSectionResizeMode(QHeaderView::Interactive);
// header_view->setMinimumSectionSize(10);
// header_view->resizeSection(1, 20);
header_view->setSectionResizeMode(0, QHeaderView::Stretch);
header_view->setSectionResizeMode(_model->columnCount() - 1, QHeaderView::Fixed);
header_view->resizeSection(_model->columnCount() - 1, 20);
this->setEditTriggers(NoEditTriggers);
this->setSelectionBehavior(SelectRows);