#include "treeviewmanager.h" #include "OriginalWnd.h" // 构造函数 TreeViewManager::TreeViewManager(OriginalWnd* originalWnd, QWidget *widget2, QWidget *parent) : QWidget(parent), widget2(widget2), treeViewDown(new QTreeView(widget2)), m_originalWnd(originalWnd), navigationWidget(nullptr), buttonOpenFile(nullptr), buttonUp(nullptr), buttonDown(nullptr), buttonLeft(nullptr), buttonRight(nullptr), restoring(false) { if (!widget2) { qWarning() << "TreeViewManager: widget2 未初始化"; return; } downModel = new QStandardItemModel(this); // 初始化目录树模型 treeViewDown->setModel(downModel); applyCustomStyles(); // 目录树样式表 treeViewDown->setHeaderHidden(true); // 隐藏显示头部 treeViewDown->setGeometry(16, 106, widget2->width()-16, widget2->height() - 106); //目录树大小和位置 treeViewDown->setEditTriggers(QAbstractItemView::NoEditTriggers); //禁止目录输入 treeViewDown->viewport()->installEventFilter(this); // 给 viewport() 安装事件过滤器,以便拦截其 Paint 事件 QFrame *lineFrame1 = createUnifiedSeparator(widget2, 2); // 使用统一的分隔线创建函数 lineFrame1->setGeometry(16, 89, 428, 2); lineFrame1->show(); setupButton(); // 创建右上角按钮并设置布局 navigationWidget = new QWidget(widget2); // 创建导航栏 navigationWidget->setGeometry(15, 15, 300, 74); loadVisitedPaths(); // 加载并展开上次访问过的路径 // 连接目录前的复选框信号与槽 connect(downModel, &QStandardItemModel::itemChanged, this, &TreeViewManager::onItemChanged); // 目录树连接点击信号 connect(treeViewDown, &QTreeView::clicked, this, [=](const QModelIndex &index) { QStandardItem *item = downModel->itemFromIndex(index); if (!item) return; if (!restoring) { // 仅在非恢复状态下记录选中路径 QStringList path = buildItemPath(item); addVisitedPath(path); } QVariant data = item->data(Qt::UserRole); if (data.canConvert()) { QJsonObject fields = data.toJsonObject(); if (fields.contains("isThirdLevel") && fields["isThirdLevel"].toBool()) { updateNavigationBar(index); // 更新导航栏 displayThirdLevelFields(fields); // 显示三级目录字段内容 treeViewDown->hide(); // 隐藏目录 updateSeparatorLine(); // 隐藏目录分割线 return; } } updateNavigationBar(index); // 即使是非三级目录,也更新导航栏 // qDebug() << "更新导航栏,目录项:" << item->text(); }); // 连接目录 expanded 和 collapsed 信号 connect(treeViewDown, &QTreeView::expanded, this, [=](const QModelIndex &index) { QStandardItem *item = downModel->itemFromIndex(index); if (!item) return; QStringList path = buildItemPath(item); addExpandedPath(path); updateSeparatorLine(); }); connect(treeViewDown, &QTreeView::collapsed, this, [=](const QModelIndex &index) { QStandardItem *item = downModel->itemFromIndex(index); if (!item) return; QStringList path = buildItemPath(item); removeExpandedPath(path); updateSeparatorLine(); }); // 所有展开操作完成后更新分隔线 QTimer::singleShot(0, this, [=]() { QStandardItem *rootItem = downModel->invisibleRootItem(); QStandardItem *thirdItem = findFirstThirdLevelItemDFS(rootItem); if (thirdItem) { QJsonObject thirdLevelObj = thirdItem->data(Qt::UserRole).toJsonObject(); if (thirdLevelObj.contains("isThirdLevel") && thirdLevelObj["isThirdLevel"].toBool()) { loadButtonConfigForThirdLevel(thirdLevelObj); } } }); connect(buttonOpenFile, &QPushButton::clicked, this, &TreeViewManager::onButtonOpenFileClicked); connect(buttonUp, &QPushButton::clicked, this, &TreeViewManager::onButtonUpClicked); connect(buttonDown, &QPushButton::clicked, this, &TreeViewManager::onButtonDownClicked); connect(buttonLeft, &QPushButton::clicked, this, &TreeViewManager::onButtonLeftClicked); connect(buttonRight, &QPushButton::clicked, this, &TreeViewManager::onButtonRightClicked); } TreeViewManager::~TreeViewManager() { } // 隐藏并删除所有分割线 void TreeViewManager::clearAllSeparators() { for (auto separator : firstLevelSeparators.values()) { if (separator) { separator->hide(); separator->deleteLater(); } } firstLevelSeparators.clear(); // 清空map } // 查找非三级目录的内容添加分隔线 QStandardItem* TreeViewManager::findFirstThirdLevelItemDFS(QStandardItem *parentItem) { if (!parentItem) { return nullptr; } // 遍历当前 parentItem 的所有子 for (int i = 0; i < parentItem->rowCount(); ++i) { QStandardItem *child = parentItem->child(i); if (!child) continue; // 检查这个子节点是否是三级目录 QVariant data = child->data(Qt::UserRole); if (data.canConvert()) { QJsonObject obj = data.toJsonObject(); if (obj.contains("isThirdLevel") && obj["isThirdLevel"].toBool()) { // 找到第一个 isThirdLevel = true 的节点就返回 return child; } } // 如果不是 isThirdLevel,继续往子节点里找 QStandardItem* deeperFound = findFirstThirdLevelItemDFS(child); if (deeperFound) { return deeperFound; } } return nullptr; } // 递归更新复选框子项状态 void TreeViewManager::updateChildItems(QStandardItem *parentItem, Qt::CheckState state) { if (!parentItem) return; // qDebug() << "Updating child items of:" << parentItem->text() << "to state:" << state; for (int i = 0; i < parentItem->rowCount(); ++i) { QStandardItem *child = parentItem->child(i); if (child) { child->setCheckState(state); updateChildItems(child, state); // 递归更新子项 //qDebug() << "updateChildItems:" << parentItem->text() << "to state:" << state; } } } // 收集所有被选中的复选框路径 QStringList TreeViewManager::collectCheckedPathsRecursive(QStandardItem *item, QStringList path) { QStringList checkedList; if (!item) { item = downModel->invisibleRootItem(); } for (int i = 0; i < item->rowCount(); ++i) { QStandardItem *child = item->child(i); if (child) { QStringList currentPath = path; currentPath << child->text(); if (child->checkState() == Qt::Checked) { checkedList << currentPath.join("/"); } // 递归收集子项 checkedList << collectCheckedPathsRecursive(child, currentPath); } } return checkedList; } // 无参数版本的 collectCheckedPaths 函数定义 QStringList TreeViewManager::collectCheckedPaths() { return collectCheckedPathsRecursive(downModel->invisibleRootItem(), QStringList()); } void TreeViewManager::setCheckedPaths(const QStringList &checkedPathsList) { //qDebug() << "Setting checked paths:" << checkedPathsList; m_blockItemChanged = true; // qDebug() << "setCheckedPaths: m_blockItemChanged set to true"; for (const QString &pathStr : checkedPathsList) { QStringList path = pathStr.split("/"); QModelIndex idx = findItemByPath(path); if (idx.isValid()) { QStandardItem *item = downModel->itemFromIndex(idx); if (item) { qDebug() << " - Setting item:" << item->text() << "to Checked"; item->setCheckState(Qt::Checked); visitedPaths.insert(pathStr); qDebug() << "Added visited path:" << pathStr; } } else { qDebug() << "Path not found:" << pathStr; } } m_blockItemChanged = false; //qDebug() << "setCheckedPaths: m_blockItemChanged set to false"; } // 保存复选框状态 void TreeViewManager::saveCheckedPaths() { if (currentConfigFilePath.isEmpty()) { qWarning() << "当前配置文件路径为空,无法保存复选框状态。"; return; } QSettings settings("RunCloudTech", "David"); QString configKey = currentConfigKey(currentConfigFilePath); settings.beginGroup("TreeViewCheckedState"); // 保存选中路径 QString keyChecked = QString("checkedPaths/%1").arg(configKey); QStringList checkedList = collectCheckedPaths(); settings.setValue(keyChecked, checkedList); settings.endGroup(); qDebug() << "保存复选框状态路径:" << checkedList; } // 加载复选框状态 void TreeViewManager::loadCheckedPaths() { if (currentConfigFilePath.isEmpty()) { qWarning() << "当前配置文件路径为空,无法加载复选框状态。"; return; } QSettings settings("RunCloudTech", "David"); QString configKey = currentConfigKey(currentConfigFilePath); settings.beginGroup("TreeViewCheckedState"); // Read checked paths QString keyChecked = QString("checkedPaths/%1").arg(configKey); QStringList loadedChecked = settings.value(keyChecked).toStringList(); settings.endGroup(); qDebug() << "加载复选框状态路径:" << loadedChecked; m_blockItemChanged = true; //qDebug() << "loadCheckedPaths: m_blockItemChanged set to true"; setCheckedPaths(loadedChecked); m_blockItemChanged = false; //qDebug() << "loadCheckedPaths: m_blockItemChanged set to false"; } void TreeViewManager::onItemChanged(QStandardItem *item) { //qDebug() << "onItemChanged called for item:" << item->text(); //qDebug() << "Flags - m_blockItemChanged:" << m_blockItemChanged << ", restoring:" << restoring; if (m_blockItemChanged || restoring) { qDebug() << "Exiting onItemChanged due to flags."; return; // 防止递归和恢复期间触发 } m_blockItemChanged = true; qDebug() << "Processing item:" << item->text(); Qt::CheckState state = item->checkState(); updateChildItems(item, state); // 更新所有子项的复选框状态 updateParentItems(item->parent()); // 更新所有父项的复选框状态 m_blockItemChanged = false; // 如果不在恢复模式下,记录被选中的路径 if (!restoring) { QStringList path = buildItemPath(item); if (state == Qt::Checked) { addVisitedPath(path); } else { // 如果需要在取消选中时执行某些操作,例如从 visitedPaths 中移除 QString joinedPath = path.join("/"); if (visitedPaths.contains(joinedPath)) { visitedPaths.remove(joinedPath); qDebug() << "移除选中路径:" << joinedPath; } } // 记录复选框状态 saveCheckedPaths(); } } //更新所有父项的复选框状态 void TreeViewManager::updateParentItems(QStandardItem *parentItem) { if (!parentItem) return; int checkedCount = 0; int totalCount = parentItem->rowCount(); qDebug() << "Updating child items of:" ; for (int i = 0; i < totalCount; ++i) { QStandardItem *child = parentItem->child(i); if (child && child->checkState() == Qt::Checked) { checkedCount++; } } if (checkedCount == totalCount) { parentItem->setCheckState(Qt::Checked); } else { parentItem->setCheckState(Qt::Unchecked); } // 递归更新上层父项 updateParentItems(parentItem->parent()); } //创建横线样式 QFrame* TreeViewManager::createUnifiedSeparator(QWidget *parent, int height) { QFrame *separator = new QFrame(parent); separator->setFrameShape(QFrame::NoFrame); // 移除默认框架 separator->setFixedHeight(height); // 设置固定高度 separator->setStyleSheet("background-color: #C7CAEB;"); // 设置背景颜色 separator->hide(); // 根据需要初始化为隐藏 //qDebug() << "创建统一分隔符 QFrame,父级:" << parent; return separator; } // 目录树的横线 void TreeViewManager::updateSeparatorLine() { // 检查 treeViewDown 是否可见 if (!treeViewDown->isVisible()) { // 隐藏所有横线 for (auto separator : firstLevelSeparators) { if (separator) separator->hide(); } return; } // 遍历记录的一级目录 for (auto it = firstLevelSeparators.begin(); it != firstLevelSeparators.end(); ++it) { QStandardItem *firstLevelItem = it.key(); QFrame *separator = it.value(); if (!firstLevelItem || !separator) continue; QModelIndex firstLevelIndex = downModel->indexFromItem(firstLevelItem); QRect firstLevelRect = treeViewDown->visualRect(firstLevelIndex); if (!firstLevelRect.isValid()) { separator->hide(); continue; } if (treeViewDown->isExpanded(firstLevelIndex) && firstLevelItem->hasChildren()) { // 找到最后一个可见子目录 QModelIndex lastVisibleChild = findLastVisibleChild(firstLevelIndex); if (lastVisibleChild.isValid()) { QRect lastChildRect = treeViewDown->visualRect(lastVisibleChild); if (lastChildRect.isValid()) { // 将横线放置在最后一个子目录下方 separator->setGeometry(16, lastChildRect.bottom() + 105, widget2->width() - 32, 2); separator->show(); } else { // 如果子目录不可见,则将横线放在一级目录下方 separator->setGeometry(16, firstLevelRect.bottom() + 105, widget2->width() - 32, 2); separator->show(); } } else { // 没有可见子目录,则放在一级目录下方 separator->setGeometry(16, firstLevelRect.bottom() + 105, widget2->width() - 32, 2); separator->show(); } } else { // 一级目录收起,横线直接在下方 separator->setGeometry(16, firstLevelRect.bottom() + 105, widget2->width() - 32, 2); separator->show(); } } } // 分割线 找到最后一个可见子项 QModelIndex TreeViewManager::findLastVisibleChild(const QModelIndex &parentIndex) { if (!parentIndex.isValid()) return QModelIndex(); int childCount = downModel->rowCount(parentIndex); QModelIndex lastVisibleChild; for (int i = childCount - 1; i >= 0; --i) { QModelIndex childIndex = downModel->index(i, 0, parentIndex); if (!treeViewDown->isRowHidden(i, parentIndex)) { // 确保子项未被隐藏 if (treeViewDown->isExpanded(childIndex) && downModel->rowCount(childIndex) > 0) { QModelIndex deeperChild = findLastVisibleChild(childIndex); if (deeperChild.isValid()) { return deeperChild; } } lastVisibleChild = childIndex; break; // 找到最后一个可见子项后退出循环 } } return lastVisibleChild; } // 使用文件名作为唯一标识符 QString TreeViewManager::currentConfigKey(const QString &configFilePath) { QFileInfo fileInfo(configFilePath); QString key = fileInfo.fileName(); qDebug() << "Generated config key:" << key; return key; } void TreeViewManager::saveVisitedPaths() { if (currentConfigFilePath.isEmpty()) { qWarning() << "当前配置文件路径为空,无法保存复选框状态。"; return; } QSettings settings("RunCloudTech", "David"); QString configKey = currentConfigKey(currentConfigFilePath); settings.beginGroup("TreeViewState"); // 保存选中路径 QString keyVisited = QString("visitedPaths/%1").arg(configKey); QStringList visitedList(visitedPaths.begin(), visitedPaths.end()); settings.setValue(keyVisited, visitedList); // 保存展开路径 QString keyExpanded = QString("expandedPaths/%1").arg(configKey); QStringList expandedList(expandedPaths.begin(), expandedPaths.end()); settings.setValue(keyExpanded, expandedList); settings.endGroup(); qDebug() << "保存选中路径:" << visitedList; qDebug() << "保存展开路径:" << expandedList; // 调用保存复选框状态 saveCheckedPaths(); } void TreeViewManager::loadVisitedPaths() { if (currentConfigFilePath.isEmpty()) { qWarning() << "当前配置文件路径为空,无法加载访问路径。"; return; } QSettings settings("RunCloudTech", "David"); QString configKey = currentConfigKey(currentConfigFilePath); settings.beginGroup("TreeViewState"); // 读取选中路径 QString keyVisited = QString("visitedPaths/%1").arg(configKey); QStringList loadedVisited = settings.value(keyVisited).toStringList(); // 读取展开路径 QString keyExpanded = QString("expandedPaths/%1").arg(configKey); QStringList loadedExpanded = settings.value(keyExpanded).toStringList(); settings.endGroup(); qDebug() << "加载选中路径:" << loadedVisited; qDebug() << "加载展开路径:" << loadedExpanded; restoring = true; // 进入恢复模式 // 恢复展开路径 for (const QString &p : loadedExpanded) { QStringList path = p.split("/"); QModelIndex idx = findItemByPath(path); if (idx.isValid()) { treeViewDown->expand(idx); expandedPaths.insert(p); // 重新插入 qDebug() << "成功恢复展开路径:" << p; } else { qDebug() << "未找到展开路径部分: " << p; } } // 恢复选中路径 for (const QString &p : loadedVisited) { QStringList path = p.split("/"); QModelIndex idx = findItemByPath(path); if (idx.isValid()) { QStandardItem *item = downModel->itemFromIndex(idx); if (item) { item->setCheckState(Qt::Checked); visitedPaths.insert(p); qDebug() << "成功恢复选中路径:" << p; } } else { qDebug() << "未找到选中路径部分: " << p; } } // 恢复复选框状态 loadCheckedPaths(); restoring = false; // 退出恢复模式 // 更新导航栏 if (!loadedVisited.isEmpty()) { QString lastPathStr = loadedVisited.last(); QStringList lastPath = lastPathStr.split("/"); QModelIndex lastIdx = findItemByPath(lastPath); if (lastIdx.isValid()) { treeViewDown->setCurrentIndex(lastIdx); updateNavigationBar(lastIdx); } } else { // 如果没有加载到任何路径,自动选择第一个目录 QStandardItem *rootItem = downModel->invisibleRootItem(); if (rootItem->rowCount() > 0) { QModelIndex firstIndex = downModel->index(0, 0, QModelIndex()); if (firstIndex.isValid()) { treeViewDown->setCurrentIndex(firstIndex); updateNavigationBar(firstIndex); treeViewDown->expand(firstIndex); // 展开第一个目录 QStandardItem *firstItem = downModel->itemFromIndex(firstIndex); QVariant data = firstItem->data(Qt::UserRole); if (data.canConvert()) { QJsonObject thirdLevelObj = data.toJsonObject(); if (thirdLevelObj.contains("isThirdLevel") && thirdLevelObj["isThirdLevel"].toBool()) { loadButtonConfigForThirdLevel(thirdLevelObj); } } } } } // 使用 singleShot 确保在所有展开操作完成后更新分隔线 QTimer::singleShot(0, this, &TreeViewManager::updateSeparatorLine); } //记录选中的路径 void TreeViewManager::addVisitedPath(const QStringList &path) { QString joined = path.join("/"); if (!visitedPaths.contains(joined)) { visitedPaths.insert(joined); qDebug() << "记录[选中]路径:" << joined; } } //记录展开的路径 void TreeViewManager::addExpandedPath(const QStringList &path) { QString joined = path.join("/"); if (!expandedPaths.contains(joined)) { expandedPaths.insert(joined); qDebug() << "记录[展开]路径:" << joined; } } //移除展开的路径 void TreeViewManager::removeExpandedPath(const QStringList &path) { QString joinedPath = path.join("/"); if (expandedPaths.contains(joinedPath)) { expandedPaths.remove(joinedPath); qDebug() << "移除展开路径:" << joinedPath; } } // 构建节点的完整路径 QStringList TreeViewManager::buildItemPath(QStandardItem *item) { QStringList path; QStandardItem *currentItem = item; while (currentItem) { path.prepend(currentItem->text()); currentItem = currentItem->parent(); } return path; } // 根据路径查找对应的节点 QModelIndex TreeViewManager::findItemByPath(const QStringList &path) { if (path.isEmpty()) return QModelIndex(); QStandardItem *currentItem = downModel->invisibleRootItem(); QModelIndex currentIndex; for (const QString &part : path) { bool found = false; for (int i = 0; i < currentItem->rowCount(); ++i) { QStandardItem *child = currentItem->child(i); if (child->text() == part) { currentIndex = downModel->indexFromItem(child); currentItem = child; found = true; break; } } if (!found) { qWarning() << "未找到路径部分:" << part; return QModelIndex(); } } return currentIndex; } bool TreeViewManager::eventFilter(QObject *watched, QEvent *event) { // 拦截 Paint 事件 if (watched == treeViewDown->viewport() && event->type() == QEvent::Paint) { // 进行默认绘制“树节点”都画出来 bool handled = QWidget::eventFilter(watched, event); //使用 QPainter 叠加画“拐角线” QPainter painter(treeViewDown->viewport()); if (!painter.isActive()) { qWarning() << "Painter not active"; return handled; } painter.save(); painter.setPen(QPen(Qt::gray, 1, Qt::DashLine)); // 灰色、1px 宽、虚线 // 调用一个递归或遍历函数,把所有节点的父->子线、兄弟延续竖线都画上 paintAllBranches(QModelIndex(), painter); painter.restore(); return handled; } // 其余事件交给父类默认处理 return QWidget::eventFilter(watched, event); } void TreeViewManager::paintAllBranches(const QModelIndex &parentIndex, QPainter &painter) { int rowCount = downModel->rowCount(parentIndex); for(int i = 0; i < rowCount; ++i) { // 当前子节点 QModelIndex childIndex = downModel->index(i, 0, parentIndex); if (!childIndex.isValid()) continue; // 1) 父->子拐角线 drawParentChildLine(childIndex, painter); // 2) 兄弟延续竖线(如果本节点不是最后一个兄弟,就在拐点列画条向下的线) if (i < rowCount - 1) { drawSiblingLine(childIndex, painter); } // 3) 递归处理子节点 paintAllBranches(childIndex, painter); } } //在“父节点 -> 子节点”间画一条“L”型拐角线,调整横向线段的长度 void TreeViewManager::drawParentChildLine(const QModelIndex &childIndex, QPainter &painter) { QModelIndex parentIndex = childIndex.parent(); if (!parentIndex.isValid()) { // “顶层节点”定义一个固定的起点 (rootX, rootY) int indent = treeViewDown->indentation(); int depth = 0; // 顶层节点深度为0 int branchX = (depth + 1) * indent - indent / 2; // 计算 branchX 基于缩进和深度 // 定义 rootY 为节点中心 Y QRect childRect = treeViewDown->visualRect(childIndex); if (!childRect.isValid()) return; int rootY = childRect.center().y(); // 定义横向偏移量 const int hOffset = -20; // 绘制竖线 painter.drawLine(QPoint(branchX, rootY), QPoint(branchX, childRect.center().y())); // 计算新的横线终点 int newX = childRect.left() + hOffset; // 绘制横线 painter.drawLine(QPoint(branchX, childRect.center().y()), QPoint(newX, childRect.center().y())); return; } QRect parentRect = treeViewDown->visualRect(parentIndex); QRect childRect = treeViewDown->visualRect(childIndex); if (!parentRect.isValid() || !childRect.isValid()) { // 父或子超出可视区域 return; } int pMidY = parentRect.center().y(); int cMidY = childRect.center().y(); // 计算节点深度 int depth = 0; QModelIndex p = parentIndex; while (p.isValid()) { depth++; p = p.parent(); } int indent = treeViewDown->indentation(); int branchX = depth * indent - indent / 2; // branchX 不超出视图范围 branchX = std::max(branchX, 0); // 定义横向偏移量 const int hOffset = -15; // 绘制竖线 painter.drawLine(QPoint(branchX, pMidY), QPoint(branchX, cMidY)); // 计算新的横线终点 int newX = childRect.left() + hOffset; // 绘制横线 painter.drawLine(QPoint(branchX, cMidY), QPoint(newX, cMidY)); } // 节点下面还有兄弟,则在拐点列那里继续往下画竖线 void TreeViewManager::drawSiblingLine(const QModelIndex &childIndex, QPainter &painter) { QModelIndex parentIndex = childIndex.parent(); if (!parentIndex.isValid()) { return; // 没有父节点 } // 下一个兄弟 int row = childIndex.row(); int lastRow = downModel->rowCount(parentIndex) - 1; if (row >= lastRow) { return; // 说明是最后一个兄弟,不用画延伸线 } QModelIndex nextSibling = downModel->index(row + 1, 0, parentIndex); QRect currRect = treeViewDown->visualRect(childIndex); QRect nextRect = treeViewDown->visualRect(nextSibling); if (!currRect.isValid() || !nextRect.isValid()) { return; } // 计算节点深度 int depth = 0; QModelIndex p = parentIndex; while (p.isValid()) { depth++; p = p.parent(); } int indent = treeViewDown->indentation(); int branchX = depth * indent - indent / 2; // 确保 branchX 不超出视图范围 branchX = std::max(branchX, 0); // 从当前节点底部向下延伸到下一个兄弟节点顶部 int startY = currRect.bottom(); int endY = nextRect.top(); painter.drawLine(QPoint(branchX, startY), QPoint(branchX, endY)); } // 一次性读取 configPaths 里的所有 JSON 文件并解析存到 m_configCache void TreeViewManager::preloadAllConfigs(const QStringList &configPaths) { m_configCache.clear(); // 先清空 for (const QString &path : configPaths) { QFile file(path); if (!file.exists()) { qWarning() << "preloadAllConfigs: JSON 文件不存在:" << path; continue; } if (!file.open(QIODevice::ReadOnly)) { qWarning() << "preloadAllConfigs: 无法打开 JSON 文件:" << path; continue; } QByteArray data = file.readAll(); file.close(); QJsonDocument doc = QJsonDocument::fromJson(data); if (doc.isNull() || !doc.isObject()) { qWarning() << "preloadAllConfigs: JSON 文件格式错误:" << path; continue; } // 以 fileName 作为 key,比如 "home_config.json" QString key = QFileInfo(path).fileName(); m_configCache.insert(key, doc); qDebug() << "preloadAllConfigs: 已缓存配置文件" << key; } } //切换配置文件 void TreeViewManager::switchConfig(const QString &configKey) { qDebug() << "切换配置文件到:" << configKey; // 先保存当前配置的状态 saveVisitedPaths(); // 清理控件列表和焦点索引 m_fieldWidgets.clear(); m_currentFieldIndex = -1; // 检查 configKey 是否存在于缓存中 if (!m_configCache.contains(configKey)) { qWarning() << "switchConfig: 未找到 configKey =" << configKey << ",无法切换"; return; } // 获取对应的 QJsonDocument QJsonDocument doc = m_configCache.value(configKey); // 清理上一个配置产生的横线 clearAllSeparators(); // 隐藏三级目录菜单内容 clearThirdLevelMenu(); //加载新的 JSON 配置 loadJsonDoc(doc, configKey); } //清理并隐藏三级目录菜单内容 void TreeViewManager::clearThirdLevelMenu() { // 遍历所有子控件,找到标题为 "字段展示" 的窗口并关闭 foreach (QObject *child, widget2->children()) { QWidget *childWidget = qobject_cast(child); if (childWidget && childWidget->windowTitle() == "字段展示") { qDebug() << "关闭现有的字段展示窗口"; childWidget->close(); } } // 显示主目录树和分隔线 treeViewDown->show(); for (auto separator : firstLevelSeparators) { separator->show(); } } void TreeViewManager::loadJsonDoc(const QJsonDocument &doc, const QString &configFilePath) { currentConfigFilePath = configFilePath; //qDebug() << "[DEBUG] loadJsonDoc 完成,对应 configKey =" << configFilePath; m_blockItemChanged = true; //qDebug() << "Loading JSON config, m_blockItemChanged set to true"; // 清空 downModel->clear(); firstLevelSeparators.clear(); visitedPaths.clear(); expandedPaths.clear(); // 构建树 if (!doc.isObject()) { qWarning() << "loadJsonDoc: doc 不是对象结构"; return; } QJsonObject rootObj = doc.object(); buildTree(rootObj, downModel->invisibleRootItem()); loadVisitedPaths();// 恢复 记忆路径 + 复选框状态 m_blockItemChanged = false; // qDebug() << "Finished loading JSON config, m_blockItemChanged set to false"; } //目录树样式 void TreeViewManager::applyCustomStyles() { treeViewDown->setStyleSheet(R"( /* 设置分支图标 */ QTreeView::branch:closed:has-children { border-image: none; image: url(:/images/home_add.png); } QTreeView::branch:open:has-children { border-image: none; image: url(:/images/home_minus.png); } /* 设置多选框 */ QTreeView::indicator:unchecked { image: url(:/images/home_NotSelecte.png); } QTreeView::indicator:checked { image: url(:/images/home_selected.png); } /* 背景透明,行间距 */ QTreeView { background: transparent; border: none; } /* 设置项目选中的背景色 */ QTreeView::item:selected { background-color: #A9B4FF; } /* 设置项目的行间距 */ QTreeView::item { padding-top: 5px; /* 上边距 */ padding-bottom: 5px; /* 下边距 */ } )"); } //构建目录树 void TreeViewManager::buildTree(const QJsonObject &jsonObj, QStandardItem *parent) { for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it) { // 跳过不需要的字段 if (it.key() == "isThirdLevel" || it.key() == "separator" || it.key() == "buttons") { continue; } QStandardItem *item = new QStandardItem(it.key()); item->setCheckable(true); // 添加多选框 item->setCheckState(Qt::Unchecked); // 默认状态为未选中 parent->appendRow(item); //qDebug() << "创建目录项:" << it.key(); if (it.value().isObject()) { QJsonObject childObj = it.value().toObject(); if (childObj.contains("isThirdLevel") && childObj["isThirdLevel"].toBool()) { // 是三级目录,存储整个对象到 UserRole item->setData(childObj, Qt::UserRole); //qDebug() << "目录项为三级目录:" << it.key(); } else { // 递归处理二级目录 buildTree(childObj, item); } } // 如果是一级目录,检查是否需要创建分隔线 if (parent == downModel->invisibleRootItem()) { QJsonObject currentObj = it.value().toObject(); bool hasSeparator = currentObj.contains("separator") && currentObj["separator"].toBool(); if (hasSeparator) { QFrame *separator = createUnifiedSeparator(widget2, 2); separator->hide(); // 初始隐藏 firstLevelSeparators.insert(item, separator); //qDebug() << "为一级目录创建统一分隔线:" << it.key(); } } } // 在所有子项添加完成后,更新父项的复选框状态 if (parent != downModel->invisibleRootItem()) { updateParentItems(parent); } } //导航栏动态更新 void TreeViewManager::updateNavigationBar(const QModelIndex &index) { //如果当前 index 无效,尝试从 QSettings 读取“上次导航路径”并恢复 if (!index.isValid()) { //qWarning() << "updateNavigationBar(): 当前 index 无效,尝试从 QSettings 恢复上次导航"; QSettings settings("RunCloudTech", "David"); settings.beginGroup("TreeViewNav"); QStringList lastNavPath = settings.value("lastNavPath").toStringList(); settings.endGroup(); if (!lastNavPath.isEmpty()) { // 找到对应的节点索引 QModelIndex savedIndex = findItemByPath(lastNavPath); if (savedIndex.isValid()) { qDebug() << "成功从 QSettings 恢复导航路径:" << lastNavPath << ",对应项:" << downModel->itemFromIndex(savedIndex)->text(); updateNavigationBar(savedIndex); // 再次调用本函数,更新导航栏 } else { qWarning() << "QSettings 里存的路径无法找到对应节点,无法恢复。"; } } else { qWarning() << "QSettings 里没有存任何导航路径,或为空。"; } return; } QStandardItem *item = downModel->itemFromIndex(index); if (!item) { qWarning() << "导航栏更新失败:未找到对应项"; return; } //qDebug() << "导航栏更新,目录项:" << item->text(); // 如果导航栏已有布局,先清理 if (navigationWidget->layout()) { QLayoutItem *child; while ((child = navigationWidget->layout()->takeAt(0)) != nullptr) { if (child->widget()) { child->widget()->deleteLater(); } delete child; } delete navigationWidget->layout(); } // 构建路径列表,从当前项回溯到根节点 QList path; QStandardItem *temp = item; while (temp) { if (temp->text() != "isThirdLevel") { // 排除 "isThirdLevel" 标识 path.prepend(temp); // 从根节点开始 } temp = temp->parent(); } qDebug() << "导航路径:" << [path]() { QStringList pathNames; for (QStandardItem *p : path) { pathNames.append(p->text()); } return pathNames.join(" -> "); }(); QVBoxLayout *newLayout = new QVBoxLayout; // 创建新的导航栏布局 newLayout->setContentsMargins(0, 0, 3, 3); newLayout->setSpacing(0); // 确保显示三行 for (int i = 0; i < 3; ++i) { QLabel *label = new QLabel; if (i < path.size()) { QString text = path[i]->text(); if (i == 1) text = " " + text; // 二级目录缩进 if (i == 2) text = " " + text; // 三级目录缩进 label->setText(text); } else { label->setText(""); // 填充空白行 } QFont font = label->font(); // 设置字体和样式 font.setPointSize(14); font.setFamily("思源黑体"); font.setBold(true); //设置文字为粗体 font.setLetterSpacing(QFont::PercentageSpacing,105); //设置字间距100%为默认 label->setFont(font); label->setFixedHeight(navigationWidget->height() / 3); newLayout->addWidget(label); } // 设置布局并更新导航栏 navigationWidget->setLayout(newLayout); navigationWidget->update(); //qDebug() << "导航栏更新完成:" << path.size() << "项"; //当前路径存到 QSettings,供下次启动恢复 QStringList pathStrings; for (QStandardItem *p : path) { pathStrings << p->text(); } QSettings settings("RunCloudTech", "David"); settings.beginGroup("TreeViewNav"); settings.setValue("lastNavPath", pathStrings); settings.endGroup(); //qDebug() << "已将当前导航路径写入 QSettings:" << pathStrings; } //加载并显示三级目录对应按钮配置信息 void TreeViewManager::loadButtonConfigForThirdLevel(const QJsonObject &thirdLevelObj) { if (!m_originalWnd) { qWarning() << "OriginalWnd 指针为空,无法加载按钮配置"; return; } if (!thirdLevelObj.contains("buttons")) { qWarning() << "三级目录配置中不包含 'buttons' 字段"; return; } QJsonArray buttonsArray = thirdLevelObj.value("buttons").toArray(); // 获取 widget_left QWidget* widgetLeft = m_originalWnd->getWidgetLeft(); if (!widgetLeft) { qWarning() << "无法访问 widget_left"; return; } // 清空 widget_left 中由 loadButtonConfigForThirdLevel 创建的按钮 QList existingButtons = widgetLeft->findChildren(); for (QPushButton* button : existingButtons) { if (button->objectName().startsWith("thirdLevelBtn_")) { // 仅删除特定按钮 button->deleteLater(); } } // 使用绝对定位创建按钮 for (int i = 0; i < buttonsArray.size() && i < 12; ++i) { QJsonObject buttonObj = buttonsArray[i].toObject(); QString buttonId = buttonObj.value("id").toString(); QString buttonIcon = buttonObj.value("icon").toString(); QString buttonText = buttonObj.value("text").toString(); bool isEnabled = buttonObj.value("enabled").toBool(); // 创建按钮 QPushButton *button = new QPushButton(widgetLeft); button->setObjectName("thirdLevelBtn_" + buttonId); // 设置带前缀的对象名称 //设置按钮的样式,调整图标和文本的位置 button->setStyleSheet(R"( QPushButton { position: absolute; border-radius: 6px; opacity: 1; background: #CBD0FF; border: none; } QPushButton:hover { background-color: #A9B4FF; /* 鼠标悬停效果 */ } )"); // 设置按钮的位置和大小 int x = 16; int y = 245 + i * (48 + 13); // 第一个按钮 y=245,后续每个按钮间隔13px button->setGeometry(x, y, 158, 48); // 设置按钮的可见性,根据 "enabled" 字段显示或隐藏按钮 button->setVisible(isEnabled); // 创建图标标签 QLabel *iconLabel = new QLabel(button); iconLabel->setPixmap(QIcon(buttonIcon).pixmap(16, 16)); iconLabel->setGeometry(10, 16, 16, 16); // 图标距离左边10px,顶部16px iconLabel->setFixedSize(16, 16); iconLabel->setStyleSheet("background-color: transparent;"); iconLabel->setVisible(isEnabled); // 根据按钮的可见性设置图标的可见性 // 创建文本标签 QLabel *textLabel = new QLabel(buttonText, button); textLabel->setGeometry(34, 0, 90, 48); // 文本距离左边34px,占用剩余空间 textLabel->setWordWrap(true); // 允许换行 textLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); //textLabel->setStyleSheet("background-color: transparent;"); textLabel->setStyleSheet(R"( QLabel { background: transparent; font-family: "思源黑体"; font-size: 14px; font-weight: 500; color: #4E51CE; } )"); textLabel->setVisible(isEnabled); // 根据按钮的可见性设置文本的可见性 // 创建 F1-F12 标签 QString fLabelText = QString("F%1").arg(i + 1); // F1, F2, ..., F12 QLabel *fLabel = new QLabel(fLabelText, button); fLabel->setFixedSize(21, 16); // 设置大小为21x16 fLabel->setAlignment(Qt::AlignCenter); fLabel->setStyleSheet(R"( QLabel { background-color: transparent; color: #2A7ED8; font-size: 12px; font-weight: bold; } )"); // 设置标签的位置 int fX = 134; int fY = 2; fLabel->setGeometry(fX, fY, 14, 16); fLabel->setVisible(isEnabled); // 根据按钮的可见性设置标签的可见性 //qDebug() << "创建按钮:" << buttonId << ", 文本:" << buttonText << ", 图标:" << buttonIcon << ", 启用:" << isEnabled; } } //三级目录字段窗口 void TreeViewManager::displayThirdLevelFields(const QJsonObject &fields) { if (fields.isEmpty()) { qWarning() << "字段数据为空,无法显示"; return; } // qDebug() << "显示的字段数据:" << fields; // 检查是否为三级目录 if (!fields.contains("isThirdLevel") || !fields["isThirdLevel"].toBool()) { qWarning() << "不是三级目录,跳过按钮加载"; return; } // 隐藏目录树 treeViewDown->hide(); // 隐藏所有横线 for (auto separator : firstLevelSeparators) { separator->hide(); } // 加载按钮配置 loadButtonConfigForThirdLevel(fields); // 清理之前的控件列表 m_fieldWidgets.clear(); m_currentFieldIndex = -1; // 检查是否已存在字段窗口,防止重复创建 foreach (QObject *child, widget2->children()) { QWidget *childWidget = qobject_cast(child); if (childWidget && childWidget->windowTitle() == "字段展示") { // qDebug() << "字段窗口已存在,关闭旧窗口"; childWidget->close(); } } // 创建一个新的 “字段展示” 窗口 QWidget *fieldWindow = new QWidget(widget2); fieldWindow->setWindowTitle("字段展示"); fieldWindow->setGeometry(treeViewDown->geometry()); //创建一个滚动区域 QScrollArea QScrollArea *scrollArea = new QScrollArea(fieldWindow); scrollArea->setWidgetResizable(true); // 内容自适应大小 scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // 保留水平滚动条 //qDebug() << "QScrollArea 已创建,水平滚动条策略设置为 ScrollBarAsNeeded"; //创建滚动容器 scrollWidget,并设置垂直布局 QWidget *scrollWidget = new QWidget; scrollWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); // 宽度跟随父窗口 QVBoxLayout *scrollLayout = new QVBoxLayout(scrollWidget); scrollLayout->setSpacing(10); // 字段的垂直间距 scrollLayout->setContentsMargins(10, 10, 10, 10); // 减少边距 // 遍历 JSON 中的字段,生成控件 for (auto it = fields.begin(); it != fields.end(); ++it) { QString fieldName = it.key(); if (fieldName == "isThirdLevel") { continue; // 跳过 isThirdLevel 字段 } if (fieldName == "buttons") { continue; // 跳过 buttons 字段 } QJsonObject fieldConfig = it.value().toObject(); QString fieldType = fieldConfig["type"].toString(); // qDebug() << "处理字段:" << fieldName << "类型:" << fieldType; // 每个字段一行:Label在左,控件在右 QHBoxLayout *fieldLayout = new QHBoxLayout; fieldLayout->setSpacing(5); // 减少每行的水平间距 // 左侧:字段名 QLabel *label = new QLabel(fieldName); label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); label->setFixedHeight(24); label->setMinimumWidth(120); fieldLayout->addWidget(label); // 在中间插入弹性伸缩,使后面的控件靠右 fieldLayout->addStretch(1); // 根据 fieldType 创建相应控件 QWidget *rightWidget = new QWidget; QHBoxLayout *rightLayout = new QHBoxLayout(rightWidget); rightLayout->setContentsMargins(0, 0, 20, 0); // 控件右侧边距20 rightLayout->setSpacing(5); // 减少控件之间的间距 QWidget *createdWidget = nullptr; // 用于存储创建的控件 if (fieldType == "input") { // 创建 QLineEdit 输入框 QLineEdit *lineEdit = new QLineEdit(fieldConfig["value"].toString()); lineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // 水平方向扩展 lineEdit->setFixedSize(120, 28); // 减少宽度和高度 lineEdit->setAlignment(Qt::AlignLeft); // 输入框左对齐 lineEdit->setStyleSheet(R"( QLineEdit { background: #FFFFFF; border: 1px solid #BABBDC; border-radius: 6px; padding: 2px 5px; } )"); rightLayout->addWidget(lineEdit); createdWidget = lineEdit; // 记录创建的控件 //qDebug() << "添加输入框:" << fieldName; } else if (fieldType == "radio") { // 创建一组 QRadioButton 单选框 QHBoxLayout *radioLayout = new QHBoxLayout; radioLayout->setSpacing(5); // 减少单选按钮之间的间距 QButtonGroup *radioGroup = new QButtonGroup(rightWidget); QString currentValue = fieldConfig["value"].toString(); QJsonArray options = fieldConfig["options"].toArray(); for (const QJsonValue &option : options) { QRadioButton *radioButton = new QRadioButton(option.toString()); if (option.toString() == currentValue) { radioButton->setChecked(true); } radioGroup->addButton(radioButton); radioLayout->addWidget(radioButton); m_fieldWidgets.append(radioButton); // 添加到控件列表 //qDebug() << "添加单选按钮:" << option.toString(); } rightLayout->addLayout(radioLayout); } else if (fieldType == "checkbox") { // 创建 QCheckBox 复选框不显示文字 QCheckBox *checkBox = new QCheckBox; checkBox->setChecked(fieldConfig["value"].toBool()); // 应用指定的样式 checkBox->setStyleSheet(R"( QCheckBox::indicator { width: 20px; height: 20px; } QCheckBox::indicator:unchecked { background-color: #FFFFFF; border: 1px solid #BABBDC; border-radius: 2px; } QCheckBox::indicator:checked { image: url(:/images/three_Selecte.png); } QCheckBox { /* 取消显示文本 */ spacing: 0px; } )"); rightLayout->addWidget(checkBox); createdWidget = checkBox; // 记录创建的控件 //qDebug() << "添加复选框:" << fieldName; } else if (fieldType == "dropdown") { // 创建 QComboBox 下拉框 QComboBox *comboBox = new QComboBox; comboBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 固定宽度 comboBox->setFixedSize(120, 28); comboBox->setStyleSheet(R"( QComboBox { background: #FFFFFF; border: 1px solid #BABBDC; border-radius: 6px; padding: 2px 5px; } QComboBox::drop-down { width: 20px; } )"); QJsonArray options = fieldConfig["options"].toArray(); for (const QJsonValue &option : options) { comboBox->addItem(option.toString()); } comboBox->setCurrentText(fieldConfig["value"].toString()); rightLayout->addWidget(comboBox); createdWidget = comboBox; // 记录创建的控件 //qDebug() << "添加下拉框:" << fieldName; } else if (fieldType == "time") { // 创建 QTimeEdit 时间选择框 QTimeEdit *timeEdit = new QTimeEdit; timeEdit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 固定宽度 timeEdit->setFixedSize(120, 28); // 减少宽度和高度 timeEdit->setStyleSheet(R"( QTimeEdit { background: #FFFFFF; border: 1px solid #BABBDC; border-radius: 6px; padding: 2px 5px; } )"); timeEdit->setDisplayFormat("HH:mm:ss"); timeEdit->setTime(QTime::fromString(fieldConfig["value"].toString(), "HH:mm:ss")); rightLayout->addWidget(timeEdit); createdWidget = timeEdit; // 记录创建的控件 //qDebug() << "添加时间选择框:" << fieldName; } else if (fieldType == "switch") { // 创建 QCheckBox 开关控件,并添加文字标签“开”和“关” QWidget *switchContainer = new QWidget; QHBoxLayout *switchLayout = new QHBoxLayout(switchContainer); switchLayout->setSpacing(5); // 减少开关和标签之间的间距 switchLayout->setContentsMargins(0, 0, 0, 0); QCheckBox *switchBox = new QCheckBox; switchBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); switchBox->setFixedSize(30, 30); // 调整开关大小 switchBox->setStyleSheet(R"( QCheckBox::indicator { width: 30px; height: 30px; } QCheckBox::indicator:unchecked { background-color: #BABBDC; border-radius: 6px; } QCheckBox::indicator:checked { background-color: #4CAF50; border-radius: 6px; } )"); // 设置初始状态 QString switchValue = fieldConfig["value"].toString(); if (switchValue == "on") { switchBox->setChecked(true); } else { switchBox->setChecked(false); } // 添加文字标签 QLabel *switchLabel = new QLabel(switchBox->isChecked() ? "开" : "关"); switchLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); switchLabel->setStyleSheet("font-size: 14px;"); //调整字体大小 // 连接开关状态改变信号以更新标签文字 connect(switchBox, &QCheckBox::stateChanged, [switchLabel](int state){ if (state == Qt::Checked) { switchLabel->setText("开"); } else { switchLabel->setText("关"); } }); switchLayout->addWidget(switchBox); switchLayout->addWidget(switchLabel); rightLayout->addWidget(switchContainer); createdWidget = switchBox; // 将开关控件添加到控件列表 //qDebug() << "添加开关控件和标签:" << fieldName; } else if (fieldType == "combined") { // 组合控件(包含 QLineEdit 和两个 QToolButton) QJsonObject combinedValue = fieldConfig["value"].toObject(); // 确保必要的字段存在 if (!combinedValue.contains("inputValue") || !combinedValue.contains("moduleButton") || !combinedValue.contains("axisButton")) { //qWarning() << "组合控件的 value 字段不完整:" << fieldName; continue; } QLineEdit *comboInput = new QLineEdit(combinedValue["inputValue"].toString()); comboInput->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // 水平方向扩展 comboInput->setFixedHeight(28); // 减少高度 comboInput->setStyleSheet(R"( QLineEdit { background: #FFFFFF; border: 1px solid #BABBDC; border-radius: 5px; padding: 2px 5px; } )"); m_fieldWidgets.append(comboInput); //qDebug() << "添加组合控件的输入框:" << fieldName; // 创建一个容器 widget 来包含组合控件 QWidget *combinedWidget = new QWidget; QHBoxLayout *combinedLayout = new QHBoxLayout(combinedWidget); combinedLayout->setSpacing(5); // 减少控件之间的间距 combinedLayout->setContentsMargins(0, 0, 0, 0); // “模组” QToolButton QToolButton *moduleButton = new QToolButton(combinedWidget); // 设置父级 moduleButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); moduleButton->setFixedSize(80, 28); // 调整按钮大小 moduleButton->setStyleSheet(R"( QToolButton { background: #FFFFFF; border: 1px solid #BABBDC; border-radius: 5px; padding: 0px; /* 移除默认内边距 */ text-align: center; /* 使文本居中 */ } QToolButton::menu-indicator { image: none; } QToolButton::checked { background: #4CAF50; /* 高亮颜色 */ } /* 强制文本居中 */ QToolButton::hover { qproperty-textAlignment: 'AlignCenter'; } )"); moduleButton->setText("模组"); moduleButton->setCheckable(true); moduleButton->setPopupMode(QToolButton::InstantPopup); m_fieldWidgets.append(moduleButton); // “轴向” QToolButton QToolButton *axisButton = new QToolButton(combinedWidget); // 设置父级 axisButton->setObjectName("axisButton"); // 为轴向按钮设置对象名以便查找 axisButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); axisButton->setFixedSize(80, 28); // 调整按钮大小 axisButton->setStyleSheet(R"( QToolButton { background: #FFFFFF; border: 1px solid #BABBDC; border-radius: 5px; padding: 0px; /* 移除默认内边距 */ text-align: center; /* 使文本居中 */ } QToolButton::menu-indicator { image: none; } QToolButton::checked { background: #4CAF50; /* 高亮颜色 */ } /* 强制文本居中 */ QToolButton::hover { qproperty-textAlignment: 'AlignCenter'; } )"); axisButton->setText("轴向"); axisButton->setCheckable(true); axisButton->setPopupMode(QToolButton::InstantPopup); axisButton->setEnabled(false); // 初始时禁用轴向按钮 m_fieldWidgets.append(axisButton); // 创建 QMenu 用于“轴向”按钮,并设置样式表 QMenu *axisMenu = new QMenu(axisButton); axisMenu->setStyleSheet(R"( QMenu { background-color: white; color: black; border: 1px solid #BABBDC; } QMenu::item:selected { background-color: #4CAF50; /* 选中时背景颜色 */ color: white; /* 选中时字体颜色 */ } )"); QJsonObject axisMap = fieldConfig["axisMap"].toObject(); // 初始化轴向菜单内容为空,等待选择模组后填充 axisButton->setMenu(axisMenu); //qDebug() << "初始化轴向菜单为空:" << fieldName; // 连接“轴向”按钮的菜单触发事件来更新“轴向”菜单 connect(axisMenu, &QMenu::triggered, this, [axisButton](QAction *action){ axisButton->setText(action->text()); axisButton->setChecked(true); //qDebug() << "轴向选择:" << action->text(); }); // “模组” QMenu QMenu *moduleMenu = new QMenu(moduleButton); moduleMenu->setStyleSheet(R"( QMenu { background-color: white; color: black; border: 1px solid #BABBDC; } QMenu::item:selected { background-color: #4CAF50; /* 选中时背景颜色 */ color: white; /* 选中时字体颜色 */ } )"); QJsonArray moduleOptions = fieldConfig["moduleOptions"].toArray(); for (const QJsonValue &mod : moduleOptions) { QAction *action = moduleMenu->addAction(mod.toString()); // 使用按值捕获,避免悬空指针 connect(action, &QAction::triggered, this, [moduleButton, action, fieldConfig, combinedWidget, fieldName, axisButton, axisMenu, axisMap]() { moduleButton->setText(action->text()); moduleButton->setChecked(true); //qDebug() << "模组选择:" << action->text(); // 更改背景颜色为高亮色 moduleButton->setStyleSheet("background: #4CAF50;"); axisButton->setStyleSheet("background: #4CAF50;"); //qDebug() << "组合控件颜色已切换为高亮颜色"; // 启用轴向按钮 axisButton->setEnabled(true); // 动态填充轴向菜单 axisMenu->clear(); // 清空现有菜单项 if (axisMap.contains(action->text())) { QJsonArray axes = axisMap[action->text()].toArray(); for (const QJsonValue &axis : axes) { axisMenu->addAction(axis.toString()); } //qDebug() << "轴向菜单已根据模组选择动态填充"; } else { //qDebug() << "没有找到对应模组的轴向选项"; } // 启动定时器 int timeoutSec = fieldConfig["timeout"].toInt(180); // 默认180秒(3分钟) QTimer *timer = new QTimer(combinedWidget); timer->setSingleShot(true); connect(timer, &QTimer::timeout, combinedWidget, [moduleButton, axisButton]() { // 恢复背景颜色为默认 moduleButton->setStyleSheet("background: #FFFFFF;"); axisButton->setStyleSheet("background: #FFFFFF;"); //qDebug() << "组合控件颜色已切换为默认(超时)"; }); timer->start(timeoutSec * 1000); // 修正为 timeoutSec * 1000 毫秒 (3分钟) //qDebug() << "启动定时器," << timeoutSec << "秒后更改组合控件颜色"; }); } moduleButton->setMenu(moduleMenu); //qDebug() << "添加组合控件的模组按钮:" << fieldName; // 创建一个容器 widget 来包含组合控件 combinedLayout->addWidget(comboInput); combinedLayout->addWidget(moduleButton); combinedLayout->addWidget(axisButton); combinedLayout->addStretch(); // 保证组合控件紧凑排列 rightLayout->addWidget(combinedWidget); //qDebug() << "组合控件已添加到布局:" << fieldName; } else { qWarning() << "未知字段类型:" << fieldType; } // 将创建的控件添加到控件列表 if (createdWidget) { m_fieldWidgets.append(createdWidget); } // 将 rightWidget 加到 fieldLayout 的右侧 fieldLayout->addWidget(rightWidget); // qDebug() << "将 rightWidget 添加到 fieldLayout"; // 将 fieldLayout 添加到 scrollLayout scrollLayout->addLayout(fieldLayout); //qDebug() << "添加字段布局:" << fieldName; if (fieldConfig.contains("separator") && fieldConfig["separator"].toBool()) { QFrame *separator = createUnifiedSeparator(scrollWidget, 2); QHBoxLayout *separatorLayout = new QHBoxLayout; separatorLayout->setContentsMargins(0, 5, 0, 5); // 5px 上下边距 separatorLayout->addWidget(separator); scrollLayout->addLayout(separatorLayout); separator->show(); // 显示分隔线 //qDebug() << "添加统一的动态分割线,并已显示"; } } // 弹性伸缩,让底部也有一定空隙 scrollLayout->addStretch(); // 将 scrollWidget 设置给 scrollArea scrollArea->setWidget(scrollWidget); // scrollArea 放到 fieldWindow 的主布局中 QVBoxLayout *mainLayout = new QVBoxLayout(fieldWindow); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(scrollArea); // 显示窗口 fieldWindow->show(); //qDebug() << "三级目录字段窗口已打开"; } void TreeViewManager::onButtonUpClicked() { if (m_fieldWidgets.isEmpty()) { // 如果当前没有三级菜单打开或没有可导航的控件,执行原有的目录导航逻辑 // 您可以选择忽略按钮点击或恢复原有的目录导航逻辑(如导航至上一个目录项) return; } if (m_currentFieldIndex <= 0) { // 已经在第一个控件,无法向上移动 return; } // 移动到上一个控件 m_currentFieldIndex--; QWidget *widgetToFocus = m_fieldWidgets[m_currentFieldIndex]; if (widgetToFocus) { widgetToFocus->setFocus(); } } // 修改 onButtonDownClicked 方法 void TreeViewManager::onButtonDownClicked() { if (m_fieldWidgets.isEmpty()) { // 如果当前没有三级菜单打开或没有可导航的控件,执行原有的目录导航逻辑 // 您可以选择忽略按钮点击或恢复原有的目录导航逻辑(如导航至下一个目录项) return; } if (m_currentFieldIndex < 0) { // 如果当前没有任何控件被聚焦,默认聚焦第一个控件 m_currentFieldIndex = 0; } else if (m_currentFieldIndex >= m_fieldWidgets.size() - 1) { // 已经在最后一个控件,无法向下移动 return; } else { // 移动到下一个控件 m_currentFieldIndex++; } QWidget *widgetToFocus = m_fieldWidgets[m_currentFieldIndex]; if (widgetToFocus) { widgetToFocus->setFocus(); } } //设置所有按钮 void TreeViewManager::setupButton() { // 创建按钮并设置属性 auto createButton = [&](QPushButton*& button, QWidget* parent, const QString& iconPath, const QRect& geometry) { button = new QPushButton(parent); button->setIcon(QIcon(iconPath)); button->setGeometry(geometry); button->setStyleSheet(R"( QPushButton { position: absolute; border-radius: 6px; opacity: 1; background: #FFFFFF; border: 1px solid #BABBDC; } QPushButton:hover { background-color: #A9B4FF; /* 鼠标悬停效果 */ } )"); button->show(); }; createButton(buttonOpenFile, widget2, ":/images/home_openFile.png", QRect(328, 16, 76, 30)); createButton(buttonUp, widget2, ":/images/home_up.png", QRect(408, 16, 36, 30)); createButton(buttonDown, widget2, ":/images/home_down.png", QRect(408, 50, 36, 30)); createButton(buttonLeft, widget2, ":/images/home_left.png", QRect(328, 50, 36, 30)); createButton(buttonRight, widget2, ":/images/home_right.png", QRect(368, 50, 36, 30)); } void TreeViewManager::onButtonOpenFileClicked() { foreach (QObject *child, widget2->children()) { QWidget *childWidget = qobject_cast(child); if (childWidget && childWidget->windowTitle() == "字段展示") { //qDebug() << "关闭字段窗口:" << childWidget; childWidget->close(); } } treeViewDown->show(); // 显示 treeViewDown treeViewDown->collapseAll(); // 收起所有节点 treeViewDown->clearSelection(); // 清理控件列表和焦点索引 m_fieldWidgets.clear(); m_currentFieldIndex = -1; // 展开一级目录 QStandardItem *rootItem = downModel->invisibleRootItem(); if (rootItem) { for (int i = 0; i < rootItem->rowCount(); ++i) { QStandardItem *childItem = rootItem->child(i); if (childItem) { treeViewDown->expand(downModel->indexFromItem(childItem)); // qDebug() << "展开一级目录:" << childItem->text(); } } } // 重置导航栏 updateNavigationBar(QModelIndex()); // 更新横线的位置和可见性 QTimer::singleShot(0, this, &TreeViewManager::updateSeparatorLine); //qDebug() << "成功返回到一级菜单"; } void TreeViewManager::onButtonLeftClicked() { //qDebug() << "TreeViewManager: 返回上一级目录"; // 检查是否是三级目录字段展示窗口 bool isFieldWindowClosed = false; // 更新横线的位置和可见性 QTimer::singleShot(0, this, &TreeViewManager::updateSeparatorLine); foreach (QObject *child, widget2->children()) { QWidget *childWidget = qobject_cast(child); if (childWidget && childWidget->windowTitle() == "字段展示") { //qDebug() << "关闭三级目录窗口并返回到三级目录"; childWidget->close(); // 关闭字段展示窗口 isFieldWindowClosed = true; } } if (isFieldWindowClosed) { // 重新显示 treeViewDown 并恢复到对应的三级目录索引 treeViewDown->show(); QModelIndex currentIndex = treeViewDown->currentIndex(); if (currentIndex.isValid()) { updateNavigationBar(currentIndex); // 更新导航栏 //qDebug() << "返回到三级目录索引:" << downModel->itemFromIndex(currentIndex)->text(); } else { qWarning() << "当前索引无效,无法更新导航栏"; } // 清理控件列表和焦点索引 m_fieldWidgets.clear(); m_currentFieldIndex = -1; // 继续执行向上遍历的逻辑 QModelIndex parentIndex = currentIndex.parent(); if (parentIndex.isValid()) { treeViewDown->setCurrentIndex(parentIndex); treeViewDown->expand(parentIndex); // 确保父节点展开 updateNavigationBar(parentIndex); // 更新导航栏 QStandardItem *parentItem = downModel->itemFromIndex(parentIndex); if (parentItem) { qDebug() << "返回上一级目录:" << parentItem->text(); } else { qWarning() << "未找到父目录项"; } } else { qWarning() << "当前节点没有父节点"; } return; } // 获取当前选中项的索引 QModelIndex currentIndex = treeViewDown->currentIndex(); if (!currentIndex.isValid()) { qDebug() << "当前无有效索引,无法返回"; return; } // 获取父节点索引 QModelIndex parentIndex = currentIndex.parent(); if (parentIndex.isValid()) { // 如果有父节点,返回到父节点 treeViewDown->setCurrentIndex(parentIndex); treeViewDown->expand(parentIndex); // 展开父节点 updateNavigationBar(parentIndex); // 更新导航栏 QStandardItem *parentItem = downModel->itemFromIndex(parentIndex); if (parentItem) { //qDebug() << "返回上一级目录:" << parentItem->text(); } else { qWarning() << "未找到父目录项"; } } else { // 如果没有父节点,返回到一级目录的第一行 QModelIndex firstIndex = downModel->index(0, 0); if (firstIndex.isValid()) { treeViewDown->setCurrentIndex(firstIndex); treeViewDown->expand(firstIndex); // 展开一级目录 updateNavigationBar(firstIndex); // 更新导航栏 QStandardItem *firstItem = downModel->itemFromIndex(firstIndex); if (firstItem) { qDebug() << "返回到一级目录的第一行:" << firstItem->text(); } else { qWarning() << "未找到一级目录的第一行"; } } else { qWarning() << "无法找到任何一级目录"; } } } void TreeViewManager::onButtonRightClicked() { //qDebug() << "TreeViewManager: 进入下一级目录"; // 获取当前选中项索引 QModelIndex currentIndex = treeViewDown->currentIndex(); if (!currentIndex.isValid()) { //qDebug() << "当前无有效索引,自动从第一个根节点开始"; currentIndex = downModel->index(0, 0); // 从根节点第一个开始 } QStandardItem *currentItem = downModel->itemFromIndex(currentIndex); if (!currentItem) { qWarning() << "无法获取当前目录项"; return; } // 检查当前项是否有子节点 if (currentItem->hasChildren()) { //qDebug() << "当前目录有子目录,展开并进入第一个子目录:" << currentItem->text(); // 展开当前项并进入第一个子项 treeViewDown->expand(currentIndex); QModelIndex childIndex = downModel->index(0, 0, currentIndex); // 使用 QAbstractItemModel::index() if (childIndex.isValid()) { treeViewDown->setCurrentIndex(childIndex); updateNavigationBar(childIndex); // 更新导航栏 // qDebug() << "进入子目录:" << downModel->itemFromIndex(childIndex)->text(); return; } else { qWarning() << "展开失败:未找到子节点"; return; } } // 当前目录没有子节点,寻找同级的下一个节点 QModelIndex nextSiblingIndex = currentIndex.siblingAtRow(currentIndex.row() + 1); while (!nextSiblingIndex.isValid()) { // 如果没有下一个同级节点,向上查找父节点的下一个同级节点 QModelIndex parentIndex = currentIndex.parent(); if (!parentIndex.isValid()) { // qDebug() << "已到达最底层目录,没有更多的下一级节点"; return; // 已经遍历完所有节点 } nextSiblingIndex = parentIndex.siblingAtRow(parentIndex.row() + 1); currentIndex = parentIndex; } // 进入下一个同级节点 treeViewDown->setCurrentIndex(nextSiblingIndex); updateNavigationBar(nextSiblingIndex); // 更新导航栏 //qDebug() << "进入同级目录:" << downModel->itemFromIndex(nextSiblingIndex)->text(); }