feat(app): ObjectTreePanel 被动对象树(项目→GS→TM)

This commit is contained in:
gaozheng 2026-06-09 11:52:31 +08:00
parent 6e78e50b0b
commit c78022a6b6
3 changed files with 128 additions and 1 deletions

View File

@ -21,7 +21,8 @@ add_executable(geopro_desktop WIN32
PanelHeader.cpp PanelHeader.cpp
login/LoginWindow.cpp login/LoginWindow.cpp
panels/AnomalyListPanel.cpp panels/AnomalyListPanel.cpp
panels/DatasetListPanel.cpp) panels/DatasetListPanel.cpp
panels/ObjectTreePanel.cpp)
target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
@ -34,6 +35,7 @@ target_link_libraries(geopro_desktop PRIVATE
geopro_data # Phase 2 / / geopro_data # Phase 2 / /
geopro_net # Phase 3 + RSA + login2 geopro_net # Phase 3 + RSA + login2
geopro_render # Phase 4render Scene / GridContourActor / geopro_render # Phase 4render Scene / GridContourActor /
geopro_controller # Phase 5WorkbenchNavController
) )
vtk_module_autoinit(TARGETS geopro_desktop MODULES ${VTK_LIBRARIES}) vtk_module_autoinit(TARGETS geopro_desktop MODULES ${VTK_LIBRARIES})

View File

@ -0,0 +1,95 @@
#include "panels/ObjectTreePanel.hpp"
#include <QColor>
#include <QLabel>
#include <QSignalBlocker>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include "Glyphs.hpp"
#include "dto/NavDto.hpp"
namespace geopro::app {
namespace {
// TM 节点把 tmObjectId 存在该角色GS/项目根节点为空。
constexpr int kRoleTmId = Qt::UserRole + 2;
void addNodes(QTreeWidgetItem* parent, const std::vector<data::dto::StructTreeNode>& nodes) {
for (const auto& n : nodes) {
auto* item = new QTreeWidgetItem(parent);
item->setText(0, QString::fromStdString(n.node.name));
if (n.isTm) {
item->setData(0, kRoleTmId, QString::fromStdString(n.node.id));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(0, Qt::Unchecked); // 真实数据渲染下一轮接入,默认不勾
}
addNodes(item, n.children);
}
}
} // namespace
ObjectTreePanel::ObjectTreePanel(QWidget* parent) : QWidget(parent) {
auto* lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
tree_ = new QTreeWidget(this);
tree_->setHeaderHidden(true);
{
const QString openArrow = writeChevronIcon(true, QColor("#8A93A3"));
const QString closedArrow = writeChevronIcon(false, QColor("#8A93A3"));
tree_->setStyleSheet(
QStringLiteral("QTreeView::branch { background: #FFFFFF; }"
"QTreeView::branch:has-children:!has-siblings:closed,"
"QTreeView::branch:closed:has-children:has-siblings { image: url(%1); }"
"QTreeView::branch:open:has-children:!has-siblings,"
"QTreeView::branch:open:has-children:has-siblings { image: url(%2); }")
.arg(closedArrow, openArrow));
}
lay->addWidget(tree_, 1);
hint_ = new QLabel(QStringLiteral("(加载中…)"), this);
hint_->setAlignment(Qt::AlignCenter);
hint_->setStyleSheet(QStringLiteral("color:#9AA6B6; padding:16px;"));
hint_->setVisible(false);
lay->addWidget(hint_);
QObject::connect(tree_, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int) {
const QString tmId = item->data(0, kRoleTmId).toString();
if (!tmId.isEmpty()) emit tmClicked(tmId);
});
QObject::connect(tree_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem* item, int) {
const QString tmId = item->data(0, kRoleTmId).toString();
if (!tmId.isEmpty())
emit tmCheckToggled(tmId, item->checkState(0) == Qt::Checked);
});
}
void ObjectTreePanel::setStructure(const QString& projectName,
const std::vector<data::StructNode>& nodes) {
const QSignalBlocker block(tree_); // 重建触发 itemChanged先屏蔽
tree_->clear();
const auto roots = data::dto::buildStructTree(nodes);
if (roots.empty()) {
showMessage(projectName.isEmpty() ? QStringLiteral("(暂无项目)")
: QStringLiteral("(该项目暂无结构)"));
return;
}
hint_->setVisible(false);
tree_->setVisible(true);
auto* rootItem = new QTreeWidgetItem(tree_);
rootItem->setText(0, projectName.isEmpty() ? QStringLiteral("项目") : projectName);
addNodes(rootItem, roots);
tree_->expandAll();
}
void ObjectTreePanel::showMessage(const QString& message) {
tree_->clear();
tree_->setVisible(false);
hint_->setText(message);
hint_->setVisible(true);
}
} // namespace geopro::app

View File

@ -0,0 +1,30 @@
#pragma once
#include <QWidget>
#include <vector>
#include "repo/RepoTypes.hpp"
class QTreeWidget;
class QLabel;
namespace geopro::app {
// 被动对象树:项目根 → GS → TM叶子=TM可勾选。数据来自控制器自身不发请求。
class ObjectTreePanel : public QWidget {
Q_OBJECT
public:
explicit ObjectTreePanel(QWidget* parent = nullptr);
// 用扁平结构节点重建树(内部调 dto::buildStructTree
void setStructure(const QString& projectName, const std::vector<data::StructNode>& nodes);
void showMessage(const QString& message); // 错误/空状态占位
signals:
void tmClicked(const QString& tmObjectId);
void tmCheckToggled(const QString& tmObjectId, bool checked);
private:
QTreeWidget* tree_ = nullptr;
QLabel* hint_ = nullptr;
};
} // namespace geopro::app