geopro/src/app/panels/chart/GridDataChartView.cpp

234 lines
8.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "panels/chart/GridDataChartView.hpp"
#include <QCheckBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
#include <QToolButton>
#include <QVBoxLayout>
#include <qwt_plot.h>
#include <qwt_plot_grid.h>
#include <QSplitter>
#include <QScrollArea>
#include <QFrame>
#include <qwt_plot_rescaler.h>
#include "PanelHeader.hpp"
#include "panels/AnomalyTablePanel.hpp"
#include "panels/DescriptionPanel.hpp"
#include "panels/chart/ColorBarWidget.hpp"
#include "panels/chart/ColorMapService.hpp"
#include "panels/chart/ContourPlotItem.hpp"
#include "panels/chart/LivePanner.hpp"
namespace geopro::app {
GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) {
auto* lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
// ---- 工具条 ----
auto* toolbar = new QWidget(this);
auto* tbLay = new QHBoxLayout(toolbar);
tbLay->setContentsMargins(4, 4, 4, 4);
tbLay->setSpacing(4);
auto* btnGrid = new QToolButton(toolbar);
btnGrid->setText(QStringLiteral("网格"));
auto* btnColorScale = new QToolButton(toolbar);
btnColorScale->setText(QStringLiteral("色阶配置"));
auto* btnWhiten = new QToolButton(toolbar);
btnWhiten->setText(QStringLiteral("白化"));
auto* btnFilter = new QToolButton(toolbar);
btnFilter->setText(QStringLiteral("滤波处理"));
auto* chkShowAnom = new QCheckBox(QStringLiteral("显示异常"), toolbar);
chkShowAnom->setChecked(true);
auto* chkShowContourLabel = new QCheckBox(QStringLiteral("显示等值线标注"), toolbar);
chkShowContourLabel->setChecked(true);
auto* chkContourTip = new QCheckBox(QStringLiteral("显示等值线提示信息"), toolbar);
chkContourTip->setChecked(false);
auto* lblSimplify = new QLabel(QStringLiteral("简化容差:"), toolbar);
simplifySlider_ = new QSlider(Qt::Horizontal, toolbar);
simplifySlider_->setRange(0, 100);
simplifySlider_->setValue(50);
simplifySlider_->setFixedWidth(80);
simplifyValueLabel_ = new QLabel(QStringLiteral("0.5"), toolbar);
simplifyValueLabel_->setFixedWidth(28);
connect(simplifySlider_, &QSlider::valueChanged, this, [this](int v) {
simplifyValueLabel_->setText(QString::number(v / 100.0, 'f', 1));
});
auto* btnAnomalyLabel = new QToolButton(toolbar);
btnAnomalyLabel->setText(QStringLiteral("异常标注"));
auto* btnAutoLabel = new QToolButton(toolbar);
btnAutoLabel->setText(QStringLiteral("自动标注"));
auto* btnSaveAs = new QToolButton(toolbar);
btnSaveAs->setText(QStringLiteral("另存为"));
tbLay->addWidget(btnGrid);
tbLay->addWidget(btnColorScale);
tbLay->addWidget(btnWhiten);
tbLay->addWidget(btnFilter);
tbLay->addWidget(chkShowAnom);
tbLay->addWidget(chkShowContourLabel);
tbLay->addWidget(chkContourTip);
tbLay->addWidget(lblSimplify);
tbLay->addWidget(simplifySlider_);
tbLay->addWidget(simplifyValueLabel_);
tbLay->addWidget(btnAnomalyLabel);
tbLay->addWidget(btnAutoLabel);
tbLay->addWidget(btnSaveAs);
tbLay->addStretch();
lay->addWidget(toolbar);
// ---- QwtPlot仿 RawDataChartView差异x 轴在底部、不画过原点零线、网格线被填充覆盖故省)----
plot_ = new QwtPlot(this);
plot_->setObjectName(QStringLiteral("gridPlotArea"));
plot_->enableAxis(QwtPlot::xBottom, true);
plot_->enableAxis(QwtPlot::xTop, false);
plot_->enableAxis(QwtPlot::yLeft, true);
// 白底浅色(对齐原版 web 图表)。
plot_->setCanvasBackground(QBrush(Qt::white));
plot_->setAutoFillBackground(true);
{
QPalette pal = plot_->palette();
pal.setColor(QPalette::Window, Qt::white);
pal.setColor(QPalette::WindowText, QColor(90, 90, 90));
pal.setColor(QPalette::Text, QColor(90, 90, 90));
plot_->setPalette(pal);
}
// 交互LivePanner 统一左键实时平移 + 滚轮缩放(消费滚轮事件,不冒泡触发滚动条)。
new LivePanner(plot_, QwtPlot::xBottom, QwtPlot::yLeft, this);
plot_->setMinimumSize(0, 0);
// 锁定 x:y 真实比尺(参考轴 xBottomExpanding
rescaler_ = new QwtPlotRescaler(plot_->canvas(), QwtPlot::xBottom, QwtPlotRescaler::Expanding);
rescaler_->setAspectRatio(1.0);
rescaler_->setEnabled(true);
// 图表区 = 绘图 + 色阶条(色阶条固定高 36
colorBar_ = new ColorBarWidget(this);
colorBar_->setObjectName(QStringLiteral("gridColorScaleBar"));
auto* chartArea = new QWidget(this);
auto* chartLay = new QVBoxLayout(chartArea);
chartLay->setContentsMargins(0, 0, 0, 0);
chartLay->setSpacing(0);
chartLay->addWidget(plot_, 1);
chartLay->addWidget(colorBar_);
// ---- 底部双页签(异常列表 / 描述)----
anomalyTable_ = new AnomalyTablePanel(this);
descriptionPanel_ = new DescriptionPanel(this);
const QVector<PanelTab> bottomTabs = {
{Glyph::Anomaly, QStringLiteral("异常列表"), anomalyTable_, true},
{Glyph::Property, QStringLiteral("描述"), descriptionPanel_, false},
};
auto tabbedBottom = buildTabbedPanel(bottomTabs, {});
// 图表区 | 底部表 竖直 QSplitter可拖拽调整比例。给定最小高度→分割器有最小尺寸。
chartArea->setMinimumHeight(280);
tabbedBottom.container->setMinimumHeight(160);
auto* splitter = new QSplitter(Qt::Vertical);
splitter->addWidget(chartArea);
splitter->addWidget(tabbedBottom.container);
splitter->setStretchFactor(0, 3);
splitter->setStretchFactor(1, 1);
splitter->setChildrenCollapsible(false);
// 页签内滚动:把(图表+异常)分割器放进 QScrollArea。dock 够高→分割器填满(可拖动调整)
// dock 太矮→在页签内部出现竖滚动条(工具条/页签/标题固定),而非整个面板滚动。
auto* scroll = new QScrollArea(this);
scroll->setWidgetResizable(true);
scroll->setFrameShape(QFrame::NoFrame);
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scroll->setWidget(splitter);
lay->addWidget(scroll, 1);
// ---- 工具条开关 → 重建/重绘 ----
connect(chkShowAnom, &QCheckBox::toggled, this, [this](bool on) {
showAnomalies_ = on;
if (contourItem_) { contourItem_->setShowAnomalies(on); plot_->replot(); }
});
connect(chkShowContourLabel, &QCheckBox::toggled, this, [this](bool on) {
showLabels_ = on;
if (contourItem_) { contourItem_->setShowLabels(on); plot_->replot(); }
});
}
GridDataChartView::~GridDataChartView() {
// colorSvc_ 非 QObject、无 parent需手动释放contourItem_ 由 QwtPlot autoDelete 处理)。
delete colorSvc_;
}
void GridDataChartView::setData(const geopro::controller::DatasetDetailController::ChartData& d) {
data_ = d;
// 开页:仅把 anomalies 喂给底部异常列表;图表区待网格数据懒加载后填充。
anomalyTable_->setAnomalies(d.anomalies, {}, {});
}
void GridDataChartView::setGridData(const geopro::core::Grid& grid,
const geopro::core::ColorScale& gridScale,
const std::vector<geopro::core::Anomaly>& anoms) {
grid_ = grid;
gridScale_ = gridScale;
anoms_ = anoms;
hasGrid_ = true;
// 重建色彩服务(旧 contourItem 已 detach/delete
delete colorSvc_;
colorSvc_ = new ColorMapService(gridScale_);
rebuildContour();
// 色阶条 + 底部异常表(懒加载结果含真实异常)。
colorBar_->setColorScale(gridScale_);
anomalyTable_->setAnomalies(anoms_, {}, {});
}
void GridDataChartView::rebuildContour() {
if (!hasGrid_ || !colorSvc_) return;
// 卸载旧项QwtPlot 默认 autoDelete=true析构时 delete 仍在 dict 的 item
// 必须先 detach()(从 dict 移除)再 delete否则 QwtPlot 析构时会 double-free。
if (contourItem_) {
contourItem_->detach();
delete contourItem_;
contourItem_ = nullptr;
}
contourItem_ = new ContourPlotItem();
contourItem_->setData(grid_, colorSvc_, anoms_, /*showLines*/ true, showLabels_);
contourItem_->setShowAnomalies(showAnomalies_);
contourItem_->attach(plot_);
// 轴范围 = 数据范围x=距离、y=深度/高程)。
const QRectF bbox = contourItem_->boundingRect();
if (!bbox.isNull()) {
plot_->setAxisScale(QwtPlot::xBottom, bbox.left(), bbox.right());
plot_->setAxisScale(QwtPlot::yLeft, bbox.top(), bbox.bottom());
}
plot_->updateAxes();
if (rescaler_) rescaler_->rescale(); // 应用真实比尺
plot_->replot();
}
} // namespace geopro::app