From f9f19979702f0fdc6a40b930f677d69fe168a2ee Mon Sep 17 00:00:00 2001 From: lvjunjie Date: Mon, 19 Oct 2026 11:26:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(nmNum):=20=E5=A2=9E=E5=8A=A0=E6=9B=B2?= =?UTF-8?q?=E7=BA=BF=E9=AB=98=E4=BA=AE=E5=92=8C=E8=A1=A8=E5=A4=B4=E5=85=A8?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 选中组高亮,其他组及基准曲线变灰,关闭后恢复颜色。 - Show 表头支持全选和取消全选,替代原有两个按钮。 --- Include/nmNum/nmSubWxs/nmWxSensitive.h | 7 +- Src/nmNum/nmSubWxs/nmWxSensitive.cpp | 161 ++++++++++++++++++++++--- 2 files changed, 149 insertions(+), 19 deletions(-) diff --git a/Include/nmNum/nmSubWxs/nmWxSensitive.h b/Include/nmNum/nmSubWxs/nmWxSensitive.h index 3de2f69b..08deb608 100644 --- a/Include/nmNum/nmSubWxs/nmWxSensitive.h +++ b/Include/nmNum/nmSubWxs/nmWxSensitive.h @@ -16,6 +16,7 @@ class QSpinBox; class QLineEdit; class nmDataAnalyzeManager; class QTableWidget; +class QTableView; class QLabel; class QIcon; class SensitivityRunsView; @@ -82,15 +83,17 @@ class NM_SUB_WXS_EXPORT nmWxSensitivityShow : public iDlgBase public: nmWxSensitivityShow(const QSharedPointer& results, iGuiPlot* plot, QWidget* parent); + void done(int result) override; private slots: void onApply(); void onAccept(); - void onShowAll(); - void onHideAll(); + void updateHighlight(); private: SensitivityResultsModel* m_model; + QTableView* m_table; + QPushButton* m_highlight; }; class NM_SUB_WXS_EXPORT nmWxSensitive : public iDlgBase diff --git a/Src/nmNum/nmSubWxs/nmWxSensitive.cpp b/Src/nmNum/nmSubWxs/nmWxSensitive.cpp index 3811d8fb..f57de299 100644 --- a/Src/nmNum/nmSubWxs/nmWxSensitive.cpp +++ b/Src/nmNum/nmSubWxs/nmWxSensitive.cpp @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -216,6 +218,12 @@ public: iGuiPlot* plot, QObject* parent, bool statistics = false) : QAbstractTableModel(parent), m_results(results), m_plot(plot), m_statistics(statistics) {} + ~SensitivityResultsModel() + { + // 直接销毁 Show 窗口时也恢复原色。 + highlightRun(-1); + } + QList runCurves(int row) const { // 双对数图的一组包含压力与导数,历史压力图只包含压力。 @@ -252,7 +260,20 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const override { // 表格仅保留显示选择、运行编号与冻结参数。 - if(orientation != Qt::Horizontal || role != Qt::DisplayRole) return QVariant(); + if(orientation != Qt::Horizontal) return QVariant(); + if(!m_statistics && section == 0 && role == Qt::CheckStateRole) { + // 全选状态只统计确实有曲线、允许勾选的运行。 + int total = 0, checked = 0; + for(int row = 0; row < rowCount(); ++row) { + const QModelIndex cell = index(row, 0); + if(!(flags(cell) & Qt::ItemIsUserCheckable)) continue; + ++total; + if(data(cell, Qt::CheckStateRole).toInt() == Qt::Checked) ++checked; + } + if(!total) return QVariant(); + return checked == total ? Qt::Checked : checked == 0 ? Qt::Unchecked : Qt::PartiallyChecked; + } + if(role != Qt::DisplayRole) return QVariant(); if(m_statistics) { if(section >= 3 && section - 3 < m_results->m_fitParameters.size()) { const nmDataAttribute& parameter = m_results->m_fitParameters[section - 3]; @@ -315,6 +336,7 @@ public: if(visible == isRunVisible(index.row())) m_pendingVisibility.remove(index.row()); else m_pendingVisibility.insert(index.row(), visible); emit dataChanged(index, index); + emit headerDataChanged(Qt::Horizontal, 0, 0); return true; } @@ -329,6 +351,7 @@ public: else m_pendingVisibility.insert(i, visible); } emit dataChanged(index(0, 0), index(rowCount() - 1, 0)); + emit headerDataChanged(Qt::Horizontal, 0, 0); } void applyVisibility() @@ -346,13 +369,103 @@ public: } } + void highlightRun(int row) + { + // 绘图窗口已销毁时,残留图元的坐标轴也已失效,不能再修改样式。 + if(m_plot.isNull()) { m_highlightStyles.clear(); return; } + // 高亮不改曲线数据或可见性,先恢复原色再淡化其他采样与基准曲线。 + for(int i = 0; i < m_highlightStyles.size(); ++i) { + const HighlightStyle& style = m_highlightStyles[i]; + if(style.curve.isNull()) continue; + style.curve->setPen(style.pen); + style.curve->setDot(style.dot); + } + m_highlightStyles.clear(); + if(row >= 0 && row < rowCount()) { + QList dimmed; + for(int other = 0; other < rowCount(); ++other) { + if(other == row) continue; + dimmed.append(runCurves(other)); + } + // 双对数的基准压力、导数和历史图的基准压力共用样式恢复逻辑。 + if(ZxObjCurve* base = m_plot->getCurve(nmWxSensitive::tr("Base model - Pressure"))) dimmed.append(base); + if(ZxObjCurve* base = m_plot->getCurve(nmWxSensitive::tr("Base model - Derivative"))) dimmed.append(base); + for(int i = 0; i < dimmed.size(); ++i) { + HighlightStyle style; + style.curve = dimmed[i]; + style.pen = dimmed[i]->getPen(); + style.dot = dimmed[i]->getDot(); + m_highlightStyles.append(style); + dimmed[i]->setColor(QColor(210, 210, 210)); + dimmed[i]->setDotColor(QColor(210, 210, 210)); + } + // 重合曲线也能看清选中组,压力和导数一同放到上层。 + const QList selected = runCurves(row); + for(int i = 0; i < selected.size(); ++i) m_plot->m_pPlot->bringToFront(selected[i]); + } + if(!m_plot.isNull()) m_plot->m_pPlot->update(); + } + private: + struct HighlightStyle { + QPointer curve; + QPen pen; + ZxDot dot; + }; + QVector m_highlightStyles; QSharedPointer m_results; QMap m_pendingVisibility; QPointer m_plot; bool m_statistics; }; +class SensitivityShowHeader : public QHeaderView +{ +public: + explicit SensitivityShowHeader(QWidget* parent) : QHeaderView(Qt::Horizontal, parent) {} +protected: + QSize sectionSizeFromContents(int section) const override + { + // Show 列额外留出原生复选框的宽度。 + QSize size = QHeaderView::sectionSizeFromContents(section); + if(section == 0) size.rwidth() += style()->pixelMetric(QStyle::PM_IndicatorWidth) + 8; + return size; + } + void paintSection(QPainter* painter, const QRect& rect, int section) const override + { + // 仅替换第一列表头,其他列沿用原来的标题与样式。 + if(section != 0) { QHeaderView::paintSection(painter, rect, section); return; } + QStyleOptionHeader header; + initStyleOption(&header); + header.rect = rect; + header.text.clear(); + painter->save(); + style()->drawControl(QStyle::CE_Header, &header, painter, this); + painter->restore(); + QStyleOptionButton check; + check.initFrom(this); + check.rect = rect.adjusted(4, 0, -2, 0); + check.text = model()->headerData(0, Qt::Horizontal).toString(); + const QVariant state = model()->headerData(0, Qt::Horizontal, Qt::CheckStateRole); + if(!state.isValid()) check.state &= ~QStyle::State_Enabled; + check.state |= state.toInt() == Qt::Checked ? QStyle::State_On + : state.toInt() == Qt::PartiallyChecked ? QStyle::State_NoChange : QStyle::State_Off; + painter->save(); + style()->drawControl(QStyle::CE_CheckBox, &check, painter, this); + painter->restore(); + } + void mousePressEvent(QMouseEvent* event) override + { + // 部分选中时点击全选;全选时点击取消,全程沿用待应用选择。 + if(event->button() == Qt::LeftButton && logicalIndexAt(event->pos()) == 0) { + const QVariant state = model()->headerData(0, Qt::Horizontal, Qt::CheckStateRole); + if(state.isValid()) static_cast(model())->setAllVisible(state.toInt() != Qt::Checked); + return; + } + QHeaderView::mousePressEvent(event); + } +}; + nmWxSensitivityShow::nmWxSensitivityShow( const QSharedPointer& results, iGuiPlot* plot, QWidget* parent) : iDlgBase(parent) @@ -362,9 +475,10 @@ nmWxSensitivityShow::nmWxSensitivityShow( setObjectName("nmSensitivityShowDialog"); resize(700, 380); QVBoxLayout* layout = new QVBoxLayout(this); - QTableView* table = new QTableView(this); + QTableView* table = m_table = new QTableView(this); table->setStyleSheet("QTableView { background-color: white; }"); m_model = new SensitivityResultsModel(results, plot, this); + table->setHorizontalHeader(new SensitivityShowHeader(table)); table->setModel(m_model); table->setSelectionBehavior(QAbstractItemView::SelectRows); table->setSelectionMode(QAbstractItemView::SingleSelection); @@ -376,8 +490,10 @@ nmWxSensitivityShow::nmWxSensitivityShow( table->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch); layout->addWidget(table); QHBoxLayout* buttons = new QHBoxLayout(); - QPushButton* showAll = new QPushButton(tr("Show all"), this); - QPushButton* hideAll = new QPushButton(tr("Hide all"), this); + m_highlight = new QPushButton(tr("Highlight"), this); + m_highlight->setCheckable(true); + m_highlight->setAutoDefault(false); + m_highlight->setToolTip(tr("Highlight the selected run; click again to restore colors.")); // 明确按钮顺序,避免系统的标准按钮布局把 Apply 排到最后。 QPushButton* apply = new QPushButton(tr("Apply"), this); QPushButton* ok = new QPushButton(tr("OK"), this); @@ -385,18 +501,26 @@ nmWxSensitivityShow::nmWxSensitivityShow( apply->setAutoDefault(false); cancel->setAutoDefault(false); ok->setDefault(true); - buttons->addWidget(showAll); - buttons->addWidget(hideAll); + buttons->addWidget(m_highlight); buttons->addStretch(); buttons->addWidget(apply); buttons->addWidget(ok); buttons->addWidget(cancel); layout->addLayout(buttons); - connect(showAll, SIGNAL(clicked()), this, SLOT(onShowAll())); - connect(hideAll, SIGNAL(clicked()), this, SLOT(onHideAll())); + connect(m_highlight, SIGNAL(toggled(bool)), this, SLOT(updateHighlight())); + connect(table->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(updateHighlight())); + connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateHighlight())); connect(apply, SIGNAL(clicked()), this, SLOT(onApply())); connect(ok, SIGNAL(clicked()), this, SLOT(onAccept())); connect(cancel, SIGNAL(clicked()), this, SLOT(reject())); + updateHighlight(); +} + +void nmWxSensitivityShow::done(int result) +{ + // 确认、取消及关闭都结束临时高亮,不改变已应用的显示选择。 + m_model->highlightRun(-1); + iDlgBase::done(result); } void nmWxSensitivityShow::onApply() @@ -412,16 +536,19 @@ void nmWxSensitivityShow::onAccept() accept(); } -void nmWxSensitivityShow::onShowAll() +void nmWxSensitivityShow::updateHighlight() { - // 只修改待应用的采样组选择。 - m_model->setAllVisible(true); -} - -void nmWxSensitivityShow::onHideAll() -{ - // 参考曲线不属于采样组,保持原来的显示方式。 - m_model->setAllVisible(false); + // 只有当前选中且勾选显示的有效运行可以高亮,切换行时同步跟随。 + const QModelIndex cell = m_model->index(m_table->currentIndex().row(), 0); + const bool enabled = (m_model->flags(cell) & Qt::ItemIsUserCheckable) + && m_model->data(cell, Qt::CheckStateRole).toInt() == Qt::Checked; + m_highlight->setEnabled(enabled); + if(!enabled) { + m_highlight->blockSignals(true); + m_highlight->setChecked(false); + m_highlight->blockSignals(false); + } + m_model->highlightRun(enabled && m_highlight->isChecked() ? cell.row() : -1); } nmWxSensitivityHistogramOptions::nmWxSensitivityHistogramOptions(