#include "nmWxDisplaySettings.h" #include #include #include "nmDataAnalyzeManager.h" #include "nmGuiPlot.h" #include "ZxObjBase.h" #include #include "ZxBaseUtil.h" // --- CustomDisPlayTreeWidgetItem 实现 --- CustomDisPlayTreeWidgetItem::CustomDisPlayTreeWidgetItem(QTreeWidget *pParent) : QTreeWidgetItem(pParent) { m_pCheckBox = new QCheckBox(); m_pCheckBox->setChecked(true); // 默认选中 // 注意:这里不立即设置setItemWidget,因为treeWidget()可能返回nullptr // nmWxDisplaySettings的构造函数中会统一设置 } CustomDisPlayTreeWidgetItem::CustomDisPlayTreeWidgetItem(QTreeWidgetItem *pParent) : QTreeWidgetItem(pParent) { m_pCheckBox = new QCheckBox(); m_pCheckBox->setChecked(true); // 默认选中 } // --- CustomDisPlayTreeWidget 实现 --- CustomDisPlayTreeWidget::CustomDisPlayTreeWidget(QWidget *pParent) : QTreeWidget(pParent) { // 禁用交互功能,使树看起来是静态的 setItemsExpandable(false); // 禁止用户展开/折叠节点 // setAttribute(Qt::WA_TransparentForMouseEvents); // 移除此行,否则鼠标事件(如复选框点击)会失效 setMouseTracking(true); // 启用鼠标跟踪,以便正确处理鼠标移动事件 setSelectionMode(QAbstractItemView::NoSelection); // 禁止选择项 setFocusPolicy(Qt::NoFocus); // 禁止获取焦点,避免虚线框 setAllColumnsShowFocus(false); // 禁止显示焦点矩形 // 隐藏滚动条 //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 视觉样式 setStyleSheet( //"QTreeWidget {" //" border: none;" //" background: transparent;" //"}" //"QTreeWidget::item {" //" background: transparent;" //" border: none; /* 确保项没有边框 */" //"}" "QTreeWidget::item:hover {" " background: transparent; /* 确保hover时背景不变 */" "}" "QTreeWidget::branch {" " background: transparent; /* 隐藏默认分支线 */" "}" ); // 初始化连接线缓存 m_connectionLines.clear(); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateConnectionLines())); } // 更新根节点连接线位置 void CustomDisPlayTreeWidget::updateConnectionLines() { m_connectionLines.clear(); int rootCount = topLevelItemCount(); if(rootCount <= 1) return; // 考虑滚动偏移量 int scrollOffset = verticalScrollBar()->value(); // 获取第一个根节点位置 QTreeWidgetItem* firstRoot = topLevelItem(0); if(!firstRoot) return; QRect firstRect = visualItemRect(firstRoot); firstRect.translate(0, -scrollOffset); // 调整滚动偏移 // 获取最后一个根节点位置 QTreeWidgetItem* lastRoot = topLevelItem(rootCount - 1); if(!lastRoot) return; QRect lastRect = visualItemRect(lastRoot); lastRect.translate(0, -scrollOffset); // 调整滚动偏移 const int baseOffset = 10; int lineStartX = lastRect.left() - baseOffset; // 计算连接线中点Y坐标 int firstMidY = firstRect.center().y(); int lastMidY = lastRect.center().y(); // 保存连接线 ConnectionLine line; line.start = QPoint(lineStartX, lastMidY); line.end = QPoint(lineStartX, firstMidY); m_connectionLines.append(line); } void CustomDisPlayTreeWidget::drawBranches(QPainter *pPainter, const QRect &rect, const QModelIndex &index) const { QTreeWidgetItem *item = itemFromIndex(index); if(!item) return; int level = 0; for(QTreeWidgetItem *parent = item->parent(); parent; parent = parent->parent()) { level++; } pPainter->save(); pPainter->setPen(QPen(Qt::black, 1.2, Qt::DotLine)); const int baseOffset = 10; const int lineLength = 10; int lineY = rect.center().y(); int dynamicOffset = baseOffset; if(level == 1) dynamicOffset += 20; else if(level == 2) dynamicOffset += 40; int lineStartX = rect.left() + dynamicOffset; int lineEndX = lineStartX + lineLength; // 1. 绘制水平线 pPainter->drawLine(lineStartX, lineY, lineEndX, lineY); // 2. 绘制垂直线 if(level > 0 && item->parent() != nullptr) { // 仅绘制到当前项范围的垂直线,不要延伸到整个高度 int verticalLength = lineLength; int verticalStartY = lineY - verticalLength; int verticalEndY = lineY + verticalLength; // 当前节点在父节点中的索引 int childIndex = item->parent()->indexOfChild(item); int childCount = item->parent()->childCount(); // 绘制从当前节点到顶部的垂直线 pPainter->drawLine(lineStartX, lineY, lineStartX, verticalStartY); // 如果不是最后一个子节点,则绘制向下的垂直线 if (childIndex < childCount - 1) { pPainter->drawLine(lineStartX, lineY, lineStartX, verticalEndY); } } pPainter->restore(); } // 重写绘制事件以绘制所有内容,包括QTreeWidget默认内容和我们的特殊连接线 void CustomDisPlayTreeWidget::paintEvent(QPaintEvent *pEvent) { QTreeWidget::paintEvent(pEvent); // 先绘制原始树 // 绘制缓存的连接线 QPainter painter(viewport()); painter.setPen(QPen(Qt::black, 1.2, Qt::DotLine)); foreach(const ConnectionLine &line, m_connectionLines) { painter.drawLine(line.start, line.end); } } // --- nmWxDisplaySettings 类实现 --- nmWxDisplaySettings::nmWxDisplaySettings(QWidget *pParent) : QDialog(pParent) { setWindowTitle(tr("Display settings")); // 设置窗口标题 resize(313, 416); // 设置窗口大小 // 初始化UI组件 m_pTreeWidget = new CustomDisPlayTreeWidget(this); m_pTreeWidget->resize(276, 299); m_pTreeWidget->setColumnCount(1); // 只有一列 m_pTreeWidget->setHeaderHidden(true); // 隐藏表头 // 刷新显示哪些图元 nmDataAnalyzeManager::getCurrentInstance()->refreshChildItemsDisplay(); // 获取保存的显示设置 - 现在使用QVector QVector vecDisplaySettings = nmDataAnalyzeManager::getCurrentInstance()->getDisplaySettings(); // 遍历显示设置并构建树 for(int i = 0; i < vecDisplaySettings.size(); ++i) { const DisplaySetting& setting = vecDisplaySettings[i]; const QString& categoryName = setting.key; const CategoryDisplayInfo& info = setting.value; // 创建父节点 CustomDisPlayTreeWidgetItem *pParentItem = new CustomDisPlayTreeWidgetItem(m_pTreeWidget); m_pTreeWidget->setItemWidget(pParentItem, 0, pParentItem->checkBox()); pParentItem->checkBox()->setText(categoryName); connect(pParentItem->checkBox(), SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxStateChanged(int))); pParentItem->checkBox()->setChecked(info.isRootChecked); // 创建子节点 for(int j = 0; j < info.childItems.size(); ++j) { const ChildCategoryDisplayInfo& childInfo = info.childItems[j]; CustomDisPlayTreeWidgetItem *pChildItem = new CustomDisPlayTreeWidgetItem(pParentItem); QCheckBox* pChildCheckBox = pChildItem->checkBox(); m_pTreeWidget->setItemWidget(pChildItem, 0, pChildCheckBox); pChildCheckBox->setText(childInfo.name); connect(pChildCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxStateChanged(int))); pChildCheckBox->setChecked(childInfo.isChecked); pChildCheckBox->setEnabled(childInfo.isEnabled); } // 更新子项的启用状态 updateChildItemsEnableState(pParentItem, info.isRootChecked); } m_pTreeWidget->expandAll(); // 展开所有节点,实现静态树效果 m_pTreeWidget->updateConnectionLines(); // 初始展开后立即更新连接线 // --- 底部按钮 --- m_pImageManagerButton = new QPushButton(tr("Image manager"), this); m_pApplyButton = new QPushButton(tr("Apply"), this); m_pOkButton = new QPushButton(tr("OK"), this); m_pCancelButton = new QPushButton(tr("Cancel"), this); // 连接按钮的信号到槽 connect(m_pImageManagerButton, SIGNAL(clicked()), this, SLOT(onImageManagerClicked())); connect(m_pApplyButton, SIGNAL(clicked()), this, SLOT(onApplyClicked())); connect(m_pOkButton, SIGNAL(clicked()), this, SLOT(onOkClicked())); connect(m_pCancelButton, SIGNAL(clicked()), this, SLOT(onCancelClicked())); // --- 布局设置 --- // 声明pButtonsVerticalLayout变量 QVBoxLayout *pButtonsVerticalLayout = new QVBoxLayout(); pButtonsVerticalLayout->setContentsMargins(0, 0, 0, 0); // 移除额外边距,让按钮紧凑 // 为 Image Manager 按钮创建单独的水平布局 QHBoxLayout *pImageManagerLayout = new QHBoxLayout(); //pImageManagerLayout->addStretch(); // 左侧伸缩空间,将按钮推向右侧 pImageManagerLayout->addWidget(m_pImageManagerButton); // pImageManagerLayout->addStretch(); // 右侧伸缩空间,将按钮推向左侧,实现居中 pImageManagerLayout->setContentsMargins(0, 0, 0, 0); // 移除额外边距 // 为 Apply, OK, Cancel 按钮创建另一个水平布局 QHBoxLayout* pButtonLayout = new QHBoxLayout(); //pButtonLayout->addStretch(); // 左侧留白,推向右侧 pButtonLayout->addWidget(m_pApplyButton); pButtonLayout->addWidget(m_pOkButton); pButtonLayout->addWidget(m_pCancelButton); //pButtonLayout->addStretch(); // 右侧留白,推向左侧 pButtonLayout->setContentsMargins(0, 0, 0, 0); // 移除额外边距 // 将两个水平按钮布局添加到垂直按钮布局中 pButtonsVerticalLayout->addLayout(pImageManagerLayout); pButtonsVerticalLayout->addLayout(pButtonLayout); // 设置主垂直布局 m_pMainLayout = new QVBoxLayout(this); m_pMainLayout->addWidget(m_pTreeWidget); // 在树控件和按钮之间添加伸缩空间,确保树控件占据大部分垂直空间 //m_pMainLayout->addStretch(); // 将包含所有按钮的垂直布局添加到主布局的底部 m_pMainLayout->addLayout(pButtonsVerticalLayout); // 设置对话框的布局 setLayout(m_pMainLayout); } nmWxDisplaySettings::~nmWxDisplaySettings() { } void nmWxDisplaySettings::onCheckBoxStateChanged(int state) { QCheckBox *pCheckBox = qobject_cast(sender()); if(!pCheckBox) return; // 查找与触发信号的复选框关联的QTreeWidgetItem QTreeWidgetItem *pChangedItem = nullptr; // 使用BFS(广度优先搜索)或DFS(深度优先搜索)来查找项是可靠的方法 // 这里使用BFS QQueue itemsToProcess; // 将所有顶层项添加到队列 for(int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i) { itemsToProcess.enqueue(m_pTreeWidget->topLevelItem(i)); } while(!itemsToProcess.isEmpty()) { QTreeWidgetItem *pCurrentItem = itemsToProcess.dequeue(); // 检查当前项是否包含触发信号的复选框 if(m_pTreeWidget->itemWidget(pCurrentItem, 0) == pCheckBox) { pChangedItem = pCurrentItem; break; // 找到对应的项,退出循环 } // 将当前项的所有子项添加到队列 for(int i = 0; i < pCurrentItem->childCount(); ++i) { itemsToProcess.enqueue(pCurrentItem->child(i)); } } if(pChangedItem) { // 更新子项的启用状态 updateChildItemsEnableState(pChangedItem, state != Qt::Unchecked); } } void nmWxDisplaySettings::updateChildItemsEnableState(QTreeWidgetItem *pItem, bool bEnabled) { // 递归遍历子项,设置复选框的启用状态 for(int i = 0; i < pItem->childCount(); ++i) { QTreeWidgetItem *pChild = pItem->child(i); // 动态转换确保是我们的CustomDisPlayTreeWidgetItem类型 if(CustomDisPlayTreeWidgetItem *pCustomDisPlayChild = dynamic_cast(pChild)) { pCustomDisPlayChild->checkBox()->setEnabled(bEnabled); // 递归调用,处理更深层级的子项 updateChildItemsEnableState(pChild, bEnabled); } } } void nmWxDisplaySettings::onImageManagerClicked() { qDebug("Image Manager button clicked!"); // 这里可以打开一个新的对话框或执行其他逻辑 } void nmWxDisplaySettings::onApplyClicked() { // 存储更新后的显示设置 QVector vecUpdatedSettings; // 遍历所有顶层项 for(int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i) { CustomDisPlayTreeWidgetItem* pTopItem = dynamic_cast(m_pTreeWidget->topLevelItem(i)); if(pTopItem) { DisplaySetting setting; setting.key = pTopItem->checkBox()->text(); setting.value.isRootChecked = pTopItem->checkBox()->isChecked(); // 收集子项信息 for(int j = 0; j < pTopItem->childCount(); ++j) { CustomDisPlayTreeWidgetItem* pChildItem = dynamic_cast(pTopItem->child(j)); if(pChildItem) { QCheckBox* pChildCheckBox = pChildItem->checkBox(); ChildCategoryDisplayInfo childInfo; childInfo.name = pChildCheckBox->text(); childInfo.isChecked = pChildCheckBox->isChecked(); childInfo.isEnabled = pChildCheckBox->isEnabled(); setting.value.childItems.append(childInfo); } } vecUpdatedSettings.append(setting); } } // 更新数据中心的显示设置 nmDataAnalyzeManager::getCurrentInstance()->setDisplaySettings(vecUpdatedSettings); // 更新绘图 nmGuiPlot* pPlot = nmDataAnalyzeManager::getCurrentInstance()->getPlot(); if(pPlot) { // 遍历更新后的设置并应用到绘图 for (int i = 0; i < vecUpdatedSettings.size(); ++i) { const DisplaySetting& setting = vecUpdatedSettings.at(i); QString sCategoryKey = setting.key; CategoryDisplayInfo categoryInfo = setting.value; bool isRootChecked = categoryInfo.isRootChecked; // 整体是否选中 if (categoryInfo.childItems.size() == 0 || isRootChecked == false) { // 无子节点,或者父节点为未选中状态 // 判断类别 if (_isSame(sCategoryKey,tr("Contour"))) { pPlot->setContourVisibility(isRootChecked); } else if (_isSame(sCategoryKey,tr("Images"))) { pPlot->setImagesVisibility(isRootChecked); } else if (_isSame(sCategoryKey,tr("Faults"))) { pPlot->setAllFaultsVisible(isRootChecked); } else if (_isSame(sCategoryKey,tr("Wells"))) { pPlot->setAllWellsVisible(isRootChecked); } else if (_isSame(sCategoryKey,tr("Fractures"))) { pPlot->setAllFracturesVisible(isRootChecked); } else if (_isSame(sCategoryKey,tr("Regions"))) { pPlot->setAllRegionsVisible(isRootChecked); } else if (_isSame(sCategoryKey,tr("RegionMarks"))) { pPlot->setAllRegionMarksVisible(isRootChecked); } } else { // 包含子节点,并且父节点处于选中状态 // 遍历子节点(使用QVector的迭代器) QVector::const_iterator childIt; for (childIt = categoryInfo.childItems.constBegin(); childIt != categoryInfo.childItems.constEnd(); ++childIt) { const ChildCategoryDisplayInfo& child = *childIt; // 判断类别 if (_isSame(sCategoryKey,tr("Faults"))) { pPlot->setFaultVisible(child.name, child.isChecked); } else if (_isSame(sCategoryKey,tr("Wells"))) { pPlot->setWellVisible(child.name, child.isChecked); } else if (_isSame(sCategoryKey,tr("Fractures"))) { pPlot->setFractureVisible(child.name, child.isChecked); } else if (_isSame(sCategoryKey,tr("Regions"))) { pPlot->setRegionVisible(child.name, child.isChecked); } else if (_isSame(sCategoryKey,tr("RegionMarks"))) { pPlot->setRegionMarkVisible(child.name, child.isChecked); } } } } pPlot->update(); } } void nmWxDisplaySettings::onOkClicked() { // 应用当前设置并关闭对话框 onApplyClicked(); accept(); } void nmWxDisplaySettings::onCancelClicked() { // 放弃当前设置并关闭对话框 reject(); }