From 8e7563c0f50d692b58a126ce63ea1d7fc16d7278 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Wed, 10 Jun 2026 09:48:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(ela):=20=E6=95=B0=E6=8D=AE=E9=9B=86/?= =?UTF-8?q?=E6=96=87=E4=BB=B6/=E5=BC=82=E5=B8=B8=E5=88=97=E8=A1=A8=20?= =?UTF-8?q?=E2=86=92=20ElaListView=20+=20QStandardItemModel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DatasetListPanel/AnomalyListPanel: populate 签名 QListWidget*→QStandardItemModel*, QListWidgetItem→QStandardItem(setData(value,role)/setCheckable/setIcon/setForeground) - main.cpp: 3 列表 → ElaListView + QStandardItemModel; removeLoadMore/addLoadMore 改 model (rowCount/item/removeRow/appendRow); itemClicked→clicked(QModelIndex); anomaly itemChanged→model itemChanged; 加载更多/勾选显隐/点击 行为保持 - 注: 列表交互(异常显隐/加载更多/数据集点击)为活逻辑, 需运行验证 --- src/app/main.cpp | 97 ++++++++++++++--------------- src/app/panels/AnomalyListPanel.cpp | 18 +++--- src/app/panels/AnomalyListPanel.hpp | 4 +- src/app/panels/DatasetListPanel.cpp | 37 ++++++----- src/app/panels/DatasetListPanel.hpp | 10 +-- 5 files changed, 88 insertions(+), 78 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 2e8fb92..0d80ffd 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -34,8 +34,9 @@ #include #include #include -#include -#include +#include +#include +#include #include #include #include @@ -64,6 +65,7 @@ #include #include #include +#include #include #include @@ -502,17 +504,13 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 左下 dock:数据真实显示栏(选中测线后列其采集批次=数据集;tab 数据/文件)。 auto* datasetTabs = new QTabWidget(); - auto* datasetList = new QListWidget(); - // 简洁分割:去隔行变色,改为 item 间极淡分割线 + 内边距 + hover/选中反馈(专业、不误导)。随主题着色。 - const QString kListQss = QStringLiteral( - "QListWidget{ background:#FFFFFF; border:none; outline:none; }" - "QListWidget::item{ padding:9px 12px; border-bottom:1px solid #EEF1F5; color:#1F2A3D; }" - "QListWidget::item:hover{ background:#EEF3FB; }" - "QListWidget::item:selected{ background:#EAF1FB; color:#1F2A3D; }"); - geopro::app::applyThemedStyleSheet(datasetList, kListQss); + auto* datasetList = new ElaListView(); // Fluent 列表(自绘 hover/选中, 随主题) + auto* datasetModel = new QStandardItemModel(datasetList); + datasetList->setModel(datasetModel); datasetTabs->addTab(datasetList, QStringLiteral("数据")); - auto* fileList = new QListWidget(); - geopro::app::applyThemedStyleSheet(fileList, kListQss); // 与数据页签同款简洁分割 + auto* fileList = new ElaListView(); + auto* fileModel = new QStandardItemModel(fileList); + fileList->setModel(fileModel); datasetTabs->addTab(fileList, QStringLiteral("文件")); auto* datasetDock = new ads::CDockWidget(QStringLiteral("数据真实显示栏")); auto* datasetBox = wrapWithHeader( @@ -525,8 +523,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re dockManager->addDockWidget(ads::BottomDockWidgetArea, datasetDock, leftArea); // 右上 dock:异常列表 / 对象属性 合并为带 Tab 表头的面板(对齐原型上半)。 - auto* anomalyList = new QListWidget(); - anomalyList->setAlternatingRowColors(true); + auto* anomalyList = new ElaListView(); + auto* anomalyModel = new QStandardItemModel(anomalyList); + anomalyList->setModel(anomalyModel); auto* objAttrLabel = new QLabel(QStringLiteral("(选中对象后显示其属性)")); objAttrLabel->setWordWrap(true); objAttrLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); @@ -686,7 +685,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re }; // 加载某数据集到「数据详情 + 异常列表 + 属性」(数据列表单击与启动默认共用)。 - auto loadDataset = [&repo, propLabel, currentDsId, rebuildDetail, anomalyList, hiddenAnoms, + auto loadDataset = [&repo, propLabel, currentDsId, rebuildDetail, anomalyModel, hiddenAnoms, anomalyBadge](const QString& dsId, const QString& name) { if (dsId.isEmpty()) return; *currentDsId = dsId; @@ -695,8 +694,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re const auto anomalies = repo.loadAnomalies(dsId.toStdString()); hiddenAnoms->clear(); { - const QSignalBlocker block(anomalyList); // 重填触发 itemChanged,先屏蔽 - geopro::app::populateAnomalyList(anomalyList, anomalies); + const QSignalBlocker block(anomalyModel); // 重填触发 itemChanged,先屏蔽 + geopro::app::populateAnomalyList(anomalyModel, anomalies); } // 异常列表 Tab 数量徽标。 if (anomalyBadge) { @@ -722,14 +721,13 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // ── 单击左下数据列表的采集批次(DS) → 占位(真实剖面/反演渲染下一阶段接 dd 接口)── // 接 dd 那轮:把本处占位改为 loadDataset(id, name) 即接通详情渲染,并自动激活 overdrive-A 揭示动画。 - QObject::connect(datasetList, &QListWidget::itemClicked, datasetList, - [propLabel, detailRendererPtr, detailRenderWindowPtr, &nav](QListWidgetItem* item) { - if (item->data(geopro::app::kDsLoadMoreRole).toBool()) { + QObject::connect(datasetList, &QAbstractItemView::clicked, datasetList, + [propLabel, detailRendererPtr, detailRenderWindowPtr, &nav](const QModelIndex& idx) { + if (idx.data(geopro::app::kDsLoadMoreRole).toBool()) { nav.loadMoreData(); return; } - const QString name = - item->data(Qt::DisplayRole).toString().section('\n', 0, 0); + const QString name = idx.data(Qt::DisplayRole).toString().section('\n', 0, 0); detailRendererPtr->RemoveAllViewProps(); detailRenderWindowPtr->Render(); propLabel->setText(QStringLiteral( @@ -737,8 +735,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re }); // ── 异常列表勾选(显隐) → 更新隐藏集 → 重建数据详情 ── - QObject::connect(anomalyList, &QListWidget::itemChanged, anomalyList, - [hiddenAnoms, rebuildDetail](QListWidgetItem* item) { + QObject::connect(anomalyModel, &QStandardItemModel::itemChanged, anomalyList, + [hiddenAnoms, rebuildDetail](QStandardItem* item) { const int idx = item->data(geopro::app::kAnomalyIndexRole).toInt(); if (item->checkState() == Qt::Checked) hiddenAnoms->erase(idx); @@ -851,19 +849,20 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // ── 控制器 ↔ UI 信号接线(导航壳)────────────────────────────────────── // "加载更多"行:列表末尾若已加载数 < 总数,放一行可点击的"加载更多(已/共)"。 - auto removeLoadMore = [](QListWidget* lw) { - if (lw->count() > 0 && - lw->item(lw->count() - 1)->data(geopro::app::kDsLoadMoreRole).toBool()) - delete lw->takeItem(lw->count() - 1); + auto removeLoadMore = [](QStandardItemModel* mdl) { + const int n = mdl->rowCount(); + if (n > 0 && mdl->item(n - 1)->data(geopro::app::kDsLoadMoreRole).toBool()) + mdl->removeRow(n - 1); }; - auto addLoadMore = [](QListWidget* lw, int total) { - const int loaded = lw->count(); + auto addLoadMore = [](QStandardItemModel* mdl, int total) { + const int loaded = mdl->rowCount(); if (loaded < total) { - auto* m = new QListWidgetItem( - QStringLiteral("加载更多(%1/%2)").arg(loaded).arg(total), lw); - m->setData(geopro::app::kDsLoadMoreRole, true); - m->setTextAlignment(Qt::AlignCenter); - m->setForeground(QColor("#2D6CB5")); + auto* it = new QStandardItem(QStringLiteral("加载更多(%1/%2)").arg(loaded).arg(total)); + it->setData(true, geopro::app::kDsLoadMoreRole); + it->setTextAlignment(Qt::AlignCenter); + it->setForeground(QColor("#2D6CB5")); + it->setEditable(false); + mdl->appendRow(it); } return loaded; }; @@ -902,42 +901,42 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re topBar->setProjects(list, cur, total > static_cast(list.size())); }); QObject::connect(&nav, &geopro::controller::WorkbenchNavController::structureLoaded, objectTree, - [objectTree, datasetList, fileList, datasetTitle, datasetTabs]( + [objectTree, datasetModel, fileModel, datasetTitle, datasetTabs]( const QString& projectName, const std::vector& nodes) { objectTree->setStructure(projectName, nodes); - datasetList->clear(); - fileList->clear(); + datasetModel->clear(); + fileModel->clear(); if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏")); datasetTabs->setTabText(0, QStringLiteral("数据")); datasetTabs->setTabText(1, QStringLiteral("文件")); }); QObject::connect(&nav, &geopro::controller::WorkbenchNavController::datasetsLoaded, datasetList, - [removeLoadMore, addLoadMore, datasetList, datasetTitle, datasetTabs]( + [removeLoadMore, addLoadMore, datasetModel, datasetTitle, datasetTabs]( const QString&, const std::vector& rows, int total, bool append) { - removeLoadMore(datasetList); - geopro::app::populateDatasetList(datasetList, rows, append); - const int loaded = addLoadMore(datasetList, total); + removeLoadMore(datasetModel); + geopro::app::populateDatasetList(datasetModel, rows, append); + const int loaded = addLoadMore(datasetModel, total); if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏")); datasetTabs->setTabText( 0, total > 0 ? QStringLiteral("数据 (%1/%2)").arg(loaded).arg(total) : QStringLiteral("数据")); }); QObject::connect(&nav, &geopro::controller::WorkbenchNavController::filesLoaded, fileList, - [removeLoadMore, addLoadMore, fileList, datasetTabs]( + [removeLoadMore, addLoadMore, fileModel, datasetTabs]( const QString&, const std::vector& rows, int total, bool append) { - removeLoadMore(fileList); - geopro::app::populateFileList(fileList, rows, append); - const int loaded = addLoadMore(fileList, total); + removeLoadMore(fileModel); + geopro::app::populateFileList(fileModel, rows, append); + const int loaded = addLoadMore(fileModel, total); datasetTabs->setTabText( 1, total > 0 ? QStringLiteral("文件 (%1/%2)").arg(loaded).arg(total) : QStringLiteral("文件")); }); - QObject::connect(fileList, &QListWidget::itemClicked, fileList, - [&nav](QListWidgetItem* item) { - if (item->data(geopro::app::kDsLoadMoreRole).toBool()) nav.loadMoreFiles(); + QObject::connect(fileList, &QAbstractItemView::clicked, fileList, + [&nav](const QModelIndex& idx) { + if (idx.data(geopro::app::kDsLoadMoreRole).toBool()) nav.loadMoreFiles(); }); QObject::connect(&nav, &geopro::controller::WorkbenchNavController::loadFailed, objectTree, [objectTree, &window](const QString& stage, const QString& msg) { diff --git a/src/app/panels/AnomalyListPanel.cpp b/src/app/panels/AnomalyListPanel.cpp index 6f5fe2d..198a83d 100644 --- a/src/app/panels/AnomalyListPanel.cpp +++ b/src/app/panels/AnomalyListPanel.cpp @@ -5,9 +5,9 @@ #include #include -#include -#include #include +#include +#include #include #include "model/ColorScale.hpp" @@ -54,10 +54,10 @@ QPixmap swatch(const std::string& colorStr) } // namespace -void populateAnomalyList(QListWidget* list, const std::vector& anomalies) +void populateAnomalyList(QStandardItemModel* model, const std::vector& anomalies) { - if (!list) return; - list->clear(); + if (!model) return; + model->clear(); for (std::size_t i = 0; i < anomalies.size(); ++i) { const auto& a = anomalies[i]; const QString name = QString::fromStdString(a.name.empty() ? "异常" : a.name); @@ -66,10 +66,12 @@ void populateAnomalyList(QListWidget* list, const std::vectorsetData(kAnomalyIndexRole, static_cast(i)); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + auto* item = new QStandardItem(QIcon(swatch(a.lineColor)), text); + item->setEditable(false); + item->setData(static_cast(i), kAnomalyIndexRole); + item->setCheckable(true); item->setCheckState(Qt::Checked); // 默认显示 + model->appendRow(item); } } diff --git a/src/app/panels/AnomalyListPanel.hpp b/src/app/panels/AnomalyListPanel.hpp index e5566ed..326d488 100644 --- a/src/app/panels/AnomalyListPanel.hpp +++ b/src/app/panels/AnomalyListPanel.hpp @@ -3,7 +3,7 @@ #include "model/Anomaly.hpp" -class QListWidget; +class QStandardItemModel; namespace geopro::app { @@ -14,6 +14,6 @@ constexpr int kAnomalyIndexRole = 0x0100; // Qt::UserRole // 派生「位置 Xm · 深 Ym · 尺寸 Zm」(由 location.coordinate 质心/包络算)。 // 条目可勾选:勾=显示(默认全勾);勾选状态变化由调用方连接驱动该异常 actor 显隐。 // 清空旧条目后重填。 -void populateAnomalyList(QListWidget* list, const std::vector& anomalies); +void populateAnomalyList(QStandardItemModel* model, const std::vector& anomalies); } // namespace geopro::app diff --git a/src/app/panels/DatasetListPanel.cpp b/src/app/panels/DatasetListPanel.cpp index 2ca911f..5022835 100644 --- a/src/app/panels/DatasetListPanel.cpp +++ b/src/app/panels/DatasetListPanel.cpp @@ -1,8 +1,8 @@ #include "panels/DatasetListPanel.hpp" #include -#include -#include +#include +#include #include namespace geopro::app { @@ -16,29 +16,34 @@ QString humanSize(long long b) { } } // namespace -void populateDatasetList(QListWidget* list, const std::vector& rows, bool append) { - if (!list) return; - if (!append) list->clear(); +void populateDatasetList(QStandardItemModel* model, const std::vector& rows, + bool append) { + if (!model) return; + if (!append) model->clear(); for (const auto& d : rows) { QString text = QString::fromStdString(d.dsName); QString sub = QString::fromStdString(d.createTime); // 名称下先创建时间 if (!d.typeName.empty()) sub += QStringLiteral(" · %1").arg(QString::fromStdString(d.typeName)); // 再跟类型 if (!sub.isEmpty()) text += QStringLiteral("\n%1").arg(sub); - auto* item = new QListWidgetItem(text, list); - item->setData(kDsIdRole, QString::fromStdString(d.id)); - item->setData(kDsDdTypeRole, QString::fromStdString(d.ddCode)); + auto* item = new QStandardItem(text); + item->setEditable(false); + item->setData(QString::fromStdString(d.id), kDsIdRole); + item->setData(QString::fromStdString(d.ddCode), kDsDdTypeRole); + model->appendRow(item); } } -void populateFileList(QListWidget* list, const std::vector& rows, bool append) { - if (!list) return; - if (!append) list->clear(); +void populateFileList(QStandardItemModel* model, const std::vector& rows, + bool append) { + if (!model) return; + if (!append) model->clear(); if (!append && rows.empty()) { - auto* hint = new QListWidgetItem(QStringLiteral("(暂无文件)"), list); + auto* hint = new QStandardItem(QStringLiteral("(暂无文件)")); hint->setFlags(Qt::NoItemFlags); hint->setForeground(QColor("#9AA6B6")); hint->setTextAlignment(Qt::AlignCenter); + model->appendRow(hint); return; } for (const auto& d : rows) { @@ -47,9 +52,11 @@ void populateFileList(QListWidget* list, const std::vector& QString sub = QString::fromStdString(d.createTime); // 名称下先创建时间 sub += QStringLiteral(" · %1").arg(humanSize(d.fileSize)); // 再跟大小 const QString text = fname + QStringLiteral("\n%1").arg(sub); - auto* item = new QListWidgetItem(text, list); - item->setData(kDsIdRole, QString::fromStdString(d.id)); - item->setData(kDsFileUrlRole, QString::fromStdString(d.fileUrl)); + auto* item = new QStandardItem(text); + item->setEditable(false); + item->setData(QString::fromStdString(d.id), kDsIdRole); + item->setData(QString::fromStdString(d.fileUrl), kDsFileUrlRole); + model->appendRow(item); } } diff --git a/src/app/panels/DatasetListPanel.hpp b/src/app/panels/DatasetListPanel.hpp index 0356760..f512c2c 100644 --- a/src/app/panels/DatasetListPanel.hpp +++ b/src/app/panels/DatasetListPanel.hpp @@ -3,7 +3,7 @@ #include "repo/RepoTypes.hpp" -class QListWidget; +class QStandardItemModel; namespace geopro::app { @@ -13,9 +13,11 @@ constexpr int kDsDdTypeRole = 0x0101; // Qt::UserRole + 1 constexpr int kDsFileUrlRole = 0x0102; // Qt::UserRole + 2(文件下载 url,备用) constexpr int kDsLoadMoreRole = 0x0103; // 标记"加载更多"行 -// 数据页签:每条 = dsName +(类型名);UserRole 存 dsId、+1 存 ddCode。 -void populateDatasetList(QListWidget* list, const std::vector& rows, bool append); +// 数据页签:每条 = dsName +(类型名);UserRole 存 dsId、+1 存 ddCode。填入标准 model。 +void populateDatasetList(QStandardItemModel* model, const std::vector& rows, + bool append); // 文件页签:每条 = 文件名 +(可读大小);UserRole 存 dsId、+2 存文件 url。空时显示占位。 -void populateFileList(QListWidget* list, const std::vector& rows, bool append); +void populateFileList(QStandardItemModel* model, const std::vector& rows, + bool append); } // namespace geopro::app