feat(app): ObjectTreePanel 被动对象树(项目→GS→TM)
This commit is contained in:
parent
6e78e50b0b
commit
c78022a6b6
|
|
@ -21,7 +21,8 @@ add_executable(geopro_desktop WIN32
|
|||
PanelHeader.cpp
|
||||
login/LoginWindow.cpp
|
||||
panels/AnomalyListPanel.cpp
|
||||
panels/DatasetListPanel.cpp)
|
||||
panels/DatasetListPanel.cpp
|
||||
panels/ObjectTreePanel.cpp)
|
||||
|
||||
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_net # Phase 3:登录(验证码 + RSA + login2)
|
||||
geopro_render # Phase 4:render 层(Scene / GridContourActor / 相机预设)
|
||||
geopro_controller # Phase 5:导航编排(WorkbenchNavController)
|
||||
)
|
||||
|
||||
vtk_module_autoinit(TARGETS geopro_desktop MODULES ${VTK_LIBRARIES})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue