You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWxDisplaySettings.cpp

441 lines
16 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmWxDisplaySettings.h"
#include <QDebug>
#include <QQueue>
#include "nmDataAnalyzeManager.h"
#include "nmGuiPlot.h"
#include "ZxObjBase.h"
#include <QString>
#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<DisplaySetting> 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<QCheckBox*>(sender());
if(!pCheckBox) return;
// 查找与触发信号的复选框关联的QTreeWidgetItem
QTreeWidgetItem *pChangedItem = nullptr;
// 使用BFS广度优先搜索或DFS深度优先搜索来查找项是可靠的方法
// 这里使用BFS
QQueue<QTreeWidgetItem*> 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<CustomDisPlayTreeWidgetItem * >(pChild)) {
pCustomDisPlayChild->checkBox()->setEnabled(bEnabled);
// 递归调用,处理更深层级的子项
updateChildItemsEnableState(pChild, bEnabled);
}
}
}
void nmWxDisplaySettings::onImageManagerClicked()
{
qDebug("Image Manager button clicked!");
// 这里可以打开一个新的对话框或执行其他逻辑
}
void nmWxDisplaySettings::onApplyClicked()
{
// 存储更新后的显示设置
QVector<DisplaySetting> vecUpdatedSettings;
// 遍历所有顶层项
for(int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i) {
CustomDisPlayTreeWidgetItem* pTopItem =
dynamic_cast<CustomDisPlayTreeWidgetItem*>(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<CustomDisPlayTreeWidgetItem*>(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<ChildCategoryDisplayInfo>::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();
}