feat(nmNum): 增加曲线高亮和表头全选

- 选中组高亮,其他组及基准曲线变灰,关闭后恢复颜色。
- Show 表头支持全选和取消全选,替代原有两个按钮。
feature/Sensitivity-20261012
lvjunjie 4 days ago
parent 2384ddb76a
commit f9f1997970

@ -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<nmDataSensitive::BatchResult>& 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

@ -48,6 +48,8 @@
#include <QKeyEvent>
#include <QMouseEvent>
#include <QHeaderView>
#include <QStyleOptionButton>
#include <QStyleOptionHeader>
#include <QSplitter>
#include <QVBoxLayout>
@ -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<ZxObjCurve*> 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<ZxObjCurve*> 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<ZxObjCurve*> 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<ZxObjCurve> curve;
QPen pen;
ZxDot dot;
};
QVector<HighlightStyle> m_highlightStyles;
QSharedPointer<nmDataSensitive::BatchResult> m_results;
QMap<int, bool> m_pendingVisibility;
QPointer<iGuiPlot> 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<SensitivityResultsModel*>(model())->setAllVisible(state.toInt() != Qt::Checked);
return;
}
QHeaderView::mousePressEvent(event);
}
};
nmWxSensitivityShow::nmWxSensitivityShow(
const QSharedPointer<nmDataSensitive::BatchResult>& 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()
{
// 只修改待应用的采样组选择。
m_model->setAllVisible(true);
}
void nmWxSensitivityShow::onHideAll()
void nmWxSensitivityShow::updateHighlight()
{
// 参考曲线不属于采样组,保持原来的显示方式。
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(

Loading…
Cancel
Save