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++

#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();
}