|
|
#include "nmWxPerforationClosing.h"
|
|
|
#include "iUnitGroup.h"
|
|
|
#include "iUnitHelper.h"
|
|
|
#include <QStyledItemDelegate>
|
|
|
#include <QPainter>
|
|
|
#include "iBase_global.h"
|
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
#include "nmDialogPreviewGuard.h"
|
|
|
#include <QHBoxLayout>
|
|
|
#include <QSplitter>
|
|
|
#include <QTreeWidgetItem>
|
|
|
#include <QHeaderView>
|
|
|
#include <QLabel>
|
|
|
#include <QSpacerItem>
|
|
|
#include <QIcon>
|
|
|
#include <QMessageBox>
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
|
namespace
|
|
|
{
|
|
|
// 树形分组保留原生折叠行为,只为组内两列表格补画网格和当前行标记。
|
|
|
class PerforationClosingTree : public QTreeWidget
|
|
|
{
|
|
|
public:
|
|
|
explicit PerforationClosingTree(QWidget* parent) : QTreeWidget(parent) {}
|
|
|
|
|
|
protected:
|
|
|
void drawRow(QPainter* painter, const QStyleOptionViewItem& option,
|
|
|
const QModelIndex& index) const override
|
|
|
{
|
|
|
QTreeWidget::drawRow(painter, option, index);
|
|
|
painter->save();
|
|
|
painter->setPen(palette().color(QPalette::Mid));
|
|
|
// 分组行跨列;子行从缩进后的表格左边缘开始绘制,包含箭头栏。
|
|
|
const int left = index.parent().isValid()
|
|
|
? visualRect(index).left() - indentation() : option.rect.left();
|
|
|
painter->drawLine(left, option.rect.bottom(), option.rect.right(), option.rect.bottom());
|
|
|
if(index.parent().isValid()) {
|
|
|
if(index.row() == 0)
|
|
|
painter->drawLine(left, option.rect.top(), option.rect.right(), option.rect.top());
|
|
|
painter->drawLine(left, option.rect.top(), left, option.rect.bottom());
|
|
|
const int markerRight = visualRect(index).left() - 1;
|
|
|
painter->drawLine(markerRight, option.rect.top(), markerRight, option.rect.bottom());
|
|
|
const int divider = columnViewportPosition(1) - 1;
|
|
|
painter->drawLine(divider, option.rect.top(), divider, option.rect.bottom());
|
|
|
}
|
|
|
painter->restore();
|
|
|
}
|
|
|
|
|
|
void drawBranches(QPainter* painter, const QRect& rect,
|
|
|
const QModelIndex& index) const override
|
|
|
{
|
|
|
// 射孔分组使用系统展开箭头,数据行的缩进区域显示当前行三角标记。
|
|
|
if(!index.parent().isValid()) {
|
|
|
QTreeWidget::drawBranches(painter, rect, index);
|
|
|
return;
|
|
|
}
|
|
|
if(index.row() == currentIndex().row() && index.parent() == currentIndex().parent()
|
|
|
&& (index.flags() & Qt::ItemIsEditable)) {
|
|
|
painter->save();
|
|
|
const QPoint center(rect.right() - indentation() / 2, rect.center().y());
|
|
|
QPolygon arrow;
|
|
|
arrow << center + QPoint(-2, -4) << center + QPoint(3, 0) << center + QPoint(-2, 4);
|
|
|
painter->setPen(Qt::NoPen);
|
|
|
painter->setBrush(palette().color(QPalette::Text));
|
|
|
painter->drawPolygon(arrow);
|
|
|
painter->restore();
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
class PerforationClosingDelegate : public QStyledItemDelegate
|
|
|
{
|
|
|
public:
|
|
|
explicit PerforationClosingDelegate(QObject* parent) : QStyledItemDelegate(parent) {}
|
|
|
|
|
|
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,
|
|
|
const QModelIndex& index) const override
|
|
|
{
|
|
|
// 射孔状态由时间顺序决定,仅开始时间允许编辑。
|
|
|
if(index.column() != 0) return nullptr;
|
|
|
QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index);
|
|
|
if(QLineEdit* line = qobject_cast<QLineEdit*>(editor)) {
|
|
|
line->setAlignment(Qt::Alignment(index.data(Qt::TextAlignmentRole).toInt()));
|
|
|
}
|
|
|
return editor;
|
|
|
}
|
|
|
|
|
|
protected:
|
|
|
void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override
|
|
|
{
|
|
|
QStyledItemDelegate::initStyleOption(option, index);
|
|
|
// 三位小数仅用于绘制,编辑与保存仍读取模型中的完整数值。
|
|
|
if(index.column() == 0 && index.parent().isValid() && (index.flags() & Qt::ItemIsEditable)) {
|
|
|
bool ok = false;
|
|
|
const double value = index.data(Qt::EditRole).toString().toDouble(&ok);
|
|
|
QStyleOptionViewItemV4* textOption = qstyleoption_cast<QStyleOptionViewItemV4*>(option);
|
|
|
if(ok && textOption) textOption->text = QString::number(value, 'f', 3);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
|
|
|
nmWxPerforationClosing::nmWxPerforationClosing(nmDataWellBase* pWellData, QWidget *parent)
|
|
|
: iDlgBase(parent)
|
|
|
{
|
|
|
setWindowTitle(tr("Perforation closing intervals"));
|
|
|
new nmDialogPreviewGuard(this);
|
|
|
setMinimumSize(500, 625);
|
|
|
|
|
|
// 初始化成员变量
|
|
|
m_currentTimeUnit = "hr";
|
|
|
m_perforationCounter = 1;
|
|
|
m_timeEntryCounter = 0;
|
|
|
m_showDatesMode = false;
|
|
|
m_hasBaseTime = false;
|
|
|
m_pPerforationRoot = 0;
|
|
|
m_pCurrentPerforationItem = 0;
|
|
|
|
|
|
m_currentWell = pWellData;
|
|
|
m_currentPerforationIndex = -1;
|
|
|
// 按射孔保存独立副本,切换目标不会覆盖其他射孔的封堵记录。
|
|
|
if(m_currentWell) {
|
|
|
for(int i = 0; i < m_currentWell->getPerforationCount(); ++i) {
|
|
|
m_perforations.insert(i, m_currentWell->getPerforationCopy(i));
|
|
|
}
|
|
|
m_currentPerforationIndex = m_currentWell->getPerforationCount() - 1;
|
|
|
}
|
|
|
loadDataFromModel();
|
|
|
// 初始化UI
|
|
|
initUI();
|
|
|
|
|
|
// 连接信号
|
|
|
connect(m_pAddBtn, SIGNAL(clicked()), this, SLOT(onAddClicked()));
|
|
|
connect(m_pRemoveBtn, SIGNAL(clicked()), this, SLOT(onRemoveClicked()));
|
|
|
connect(m_pShowDatesCheckBox, SIGNAL(toggled(bool)), this, SLOT(onShowDatesToggled(bool)));
|
|
|
connect(m_pTimeUnitComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(onTimeUnitChanged(QString)));
|
|
|
connect(m_pAllComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(onAllComboChanged(QString)));
|
|
|
connect(m_pOkBtn, SIGNAL(clicked()), this, SLOT(onOkClicked()));
|
|
|
connect(m_pCancelBtn, SIGNAL(clicked()), this, SLOT(onCancelClicked()));
|
|
|
connect(m_pTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(onItemChanged(QTreeWidgetItem*, int)));
|
|
|
connect(m_pTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
|
|
|
this, SLOT(onCurrentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
|
|
|
refreshDisplay();
|
|
|
m_pAddBtn->setEnabled(m_pCurrentPerforationItem != nullptr);
|
|
|
m_pRemoveBtn->setEnabled(m_pCurrentPerforationItem != nullptr);
|
|
|
}
|
|
|
|
|
|
nmWxPerforationClosing::~nmWxPerforationClosing()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::initUI()
|
|
|
{
|
|
|
// 创建主布局
|
|
|
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
|
|
|
|
|
// 创建工具栏容器
|
|
|
QWidget* toolbarContainer = new QWidget(this);
|
|
|
toolbarContainer->setMinimumHeight(70);
|
|
|
toolbarContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
|
QHBoxLayout* toolbarLayout = new QHBoxLayout(toolbarContainer);
|
|
|
toolbarLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
|
// 创建Add按钮组合
|
|
|
QWidget* addButtonContainer = new QWidget(this);
|
|
|
QVBoxLayout* addContainerLayout = new QVBoxLayout(addButtonContainer);
|
|
|
addContainerLayout->setSpacing(0);
|
|
|
addContainerLayout->setContentsMargins(2, 2, 2, 2);
|
|
|
|
|
|
m_pAddBtn = new QPushButton(addButtonContainer);
|
|
|
// 复用框架图标加载及缓存,保持原有工具栏布局。
|
|
|
m_pAddBtn->setIcon(zxLoadIcon("nmTimeDepSkin2"));
|
|
|
m_pAddBtn->setIconSize(QSize(36, 36));
|
|
|
m_pAddBtn->setFixedSize(40, 40);
|
|
|
|
|
|
QLabel* addTextLabel = new QLabel(tr("Add"), addButtonContainer);
|
|
|
addTextLabel->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
addContainerLayout->addWidget(m_pAddBtn, 0, Qt::AlignHCenter);
|
|
|
addContainerLayout->addWidget(addTextLabel);
|
|
|
addButtonContainer->setMinimumWidth(100);
|
|
|
addButtonContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
|
toolbarLayout->addWidget(addButtonContainer);
|
|
|
|
|
|
// 创建Remove按钮组合
|
|
|
QWidget* removeButtonContainer = new QWidget(this);
|
|
|
QVBoxLayout* removeContainerLayout = new QVBoxLayout(removeButtonContainer);
|
|
|
removeContainerLayout->setSpacing(0);
|
|
|
removeContainerLayout->setContentsMargins(2, 2, 2, 2);
|
|
|
|
|
|
m_pRemoveBtn = new QPushButton(removeButtonContainer);
|
|
|
m_pRemoveBtn->setIcon(zxLoadIcon("nmTimeDepSkin3"));
|
|
|
m_pRemoveBtn->setIconSize(QSize(36, 36));
|
|
|
m_pRemoveBtn->setFixedSize(40, 40);
|
|
|
|
|
|
QLabel* removeTextLabel = new QLabel(tr("Remove"), removeButtonContainer);
|
|
|
removeTextLabel->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
removeContainerLayout->addWidget(m_pRemoveBtn, 0, Qt::AlignHCenter);
|
|
|
removeContainerLayout->addWidget(removeTextLabel);
|
|
|
removeButtonContainer->setMinimumWidth(100);
|
|
|
removeButtonContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
|
toolbarLayout->addWidget(removeButtonContainer);
|
|
|
|
|
|
// 添加复选框和时间单位控件组
|
|
|
m_pShowDatesCheckBox = new QCheckBox(tr("Show dates"));
|
|
|
toolbarLayout->addWidget(m_pShowDatesCheckBox);
|
|
|
|
|
|
toolbarLayout->addSpacing(15); // 复选框和时间单位之间的固定间距
|
|
|
|
|
|
// 单位控件单独保留占位,切换日期显示时前面的控件不移动。
|
|
|
QWidget* timeUnitContainer = new QWidget(toolbarContainer);
|
|
|
QHBoxLayout* timeUnitLayout = new QHBoxLayout(timeUnitContainer);
|
|
|
timeUnitLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
timeUnitLayout->setSpacing(toolbarLayout->spacing());
|
|
|
m_pTimeUnitLabel = new QLabel(tr("Time unit:"));
|
|
|
timeUnitLayout->addWidget(m_pTimeUnitLabel);
|
|
|
timeUnitLayout->addSpacing(5); // 标签和下拉框之间的小间距
|
|
|
|
|
|
// 单位和主窗口统一从底层配置读取。
|
|
|
m_pTimeUnitComboBox = new QComboBox();
|
|
|
iUnitGroup* group = iUnitHelper::getUnitGroupByUnit("s");
|
|
|
const QStringList units = group ? group->getAllUnitNames() : (QStringList() << "hr");
|
|
|
m_pTimeUnitComboBox->addItems(units);
|
|
|
if(m_currentTimeUnit == "hr" && units.contains("h")) m_currentTimeUnit = "h";
|
|
|
int unitIndex = m_pTimeUnitComboBox->findText(m_currentTimeUnit);
|
|
|
m_pTimeUnitComboBox->setCurrentIndex(unitIndex >= 0 ? unitIndex : 0);
|
|
|
m_currentTimeUnit = m_pTimeUnitComboBox->currentText();
|
|
|
m_pTimeUnitComboBox->setFixedWidth(60);
|
|
|
timeUnitLayout->addWidget(m_pTimeUnitComboBox);
|
|
|
timeUnitContainer->setFixedSize(timeUnitContainer->sizeHint());
|
|
|
toolbarLayout->addWidget(timeUnitContainer);
|
|
|
|
|
|
// 避免控件贴边
|
|
|
toolbarLayout->addSpacing(10);
|
|
|
|
|
|
// 将工具栏添加到主布局
|
|
|
mainLayout->addWidget(toolbarContainer);
|
|
|
|
|
|
// 创建单个树形表格控件
|
|
|
m_pTreeWidget = new PerforationClosingTree(this);
|
|
|
// 数值时间和日期共用第一列,第二列显示射孔状态。
|
|
|
m_pTreeWidget->setColumnCount(2);
|
|
|
m_pTreeWidget->setItemDelegate(new PerforationClosingDelegate(m_pTreeWidget));
|
|
|
|
|
|
// 设置表头
|
|
|
QStringList headers;
|
|
|
headers << "" << "";
|
|
|
m_pTreeWidget->setHeaderLabels(headers);
|
|
|
m_pTreeWidget->setHeaderHidden(true); // 列标题显示在各射孔分组内部
|
|
|
|
|
|
// 设置列宽
|
|
|
m_pTreeWidget->setColumnWidth(0, 260); // 分组和开始时间,预留日期文字及树缩进宽度
|
|
|
m_pTreeWidget->setColumnWidth(1, 180); // 射孔状态
|
|
|
|
|
|
// 设置表头拉伸策略
|
|
|
m_pTreeWidget->header()->setStretchLastSection(true);
|
|
|
m_pTreeWidget->header()->setHighlightSections(false);
|
|
|
m_pTreeWidget->header()->setDefaultAlignment(Qt::AlignCenter);
|
|
|
|
|
|
// 设置树形控件的其他属性
|
|
|
m_pTreeWidget->setRootIsDecorated(true); // 显示根节点的展开/折叠图标
|
|
|
m_pTreeWidget->setAlternatingRowColors(true); // 交替行颜色
|
|
|
m_pTreeWidget->setSelectionBehavior(QAbstractItemView::SelectRows); // 整行选择
|
|
|
m_pTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
m_pTreeWidget->setIndentation(20); // 设置缩进距离
|
|
|
// 选中行保持浅蓝底色;网格由树控件统一绘制,不依赖单元格代理。
|
|
|
m_pTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
|
|
|
m_pTreeWidget->setStyleSheet(
|
|
|
"QTreeWidget {"
|
|
|
" background-color: palette(base);"
|
|
|
" alternate-background-color: rgb(232, 232, 232);"
|
|
|
" border: 1px solid palette(mid);"
|
|
|
"}"
|
|
|
"QHeaderView {"
|
|
|
" background-color: palette(button);"
|
|
|
" color: palette(button-text);"
|
|
|
"}"
|
|
|
"QHeaderView::section {"
|
|
|
" background-color: palette(button);"
|
|
|
" color: palette(button-text);"
|
|
|
" border: none;"
|
|
|
" border-right: 1px solid palette(mid);"
|
|
|
" border-bottom: 1px solid palette(mid);"
|
|
|
"}"
|
|
|
"QTreeWidget::item:selected {"
|
|
|
" background-color: rgb(225, 235, 252);"
|
|
|
" color: palette(text);"
|
|
|
"}"
|
|
|
);
|
|
|
|
|
|
// 将树形控件添加到主布局
|
|
|
mainLayout->addWidget(m_pTreeWidget);
|
|
|
|
|
|
if(m_currentWell != nullptr) {
|
|
|
int perforationCount = m_currentWell->getPerforationCount();
|
|
|
|
|
|
// 添加表头行
|
|
|
m_pPerforationRoot = new QTreeWidgetItem(m_pTreeWidget);
|
|
|
m_pPerforationRoot->setText(0, tr("Perforation"));
|
|
|
m_pPerforationRoot->setFirstColumnSpanned(true);
|
|
|
m_pPerforationRoot->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
|
|
|
|
|
|
// 为每个射孔段添加树形项目
|
|
|
for(int i = 0; i < perforationCount; ++i) {
|
|
|
nmDataPerforation* perforation = m_currentWell->getPerforation(i);
|
|
|
if(perforation) {
|
|
|
QString perforationName = perforation->getName().getValue().toString();
|
|
|
if(perforationName.isEmpty()) {
|
|
|
perforationName = tr("Perforation #%1").arg(i + 1);
|
|
|
}
|
|
|
|
|
|
QTreeWidgetItem* perforationItem = new QTreeWidgetItem(m_pTreeWidget);
|
|
|
perforationItem->setText(0, perforationName);
|
|
|
perforationItem->setData(0, Qt::UserRole, i);
|
|
|
perforationItem->setFirstColumnSpanned(true);
|
|
|
perforationItem->setExpanded(false); // 默认收起
|
|
|
|
|
|
// 如果是当前选中的射孔段,保存引用
|
|
|
if(i == m_currentPerforationIndex) {
|
|
|
m_pCurrentPerforationItem = perforationItem;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//// 添加第一行"Perforation"
|
|
|
//m_pPerforationRoot = new QTreeWidgetItem(m_pTreeWidget);
|
|
|
//m_pPerforationRoot->setText(0, tr("Perforation"));
|
|
|
//// 设置第一行不可展开(通过设置ChildIndicatorPolicy)
|
|
|
//m_pPerforationRoot->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
|
|
|
|
|
|
//// 添加第二行"Perforation #1"
|
|
|
//m_pCurrentPerforationItem = new QTreeWidgetItem(m_pTreeWidget);
|
|
|
//m_pCurrentPerforationItem->setText(0, tr("Perforation #%1").arg(m_perforationCounter));
|
|
|
//m_pCurrentPerforationItem->setExpanded(false); // 默认收起
|
|
|
////m_pCurrentPerforationItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator); // 隐藏展开箭头
|
|
|
|
|
|
// 创建底部按钮区域
|
|
|
QHBoxLayout* bottomLayout = new QHBoxLayout();
|
|
|
bottomLayout->setSpacing(8);
|
|
|
|
|
|
// All下拉框
|
|
|
m_pAllComboBox = new QComboBox();
|
|
|
m_pAllComboBox->addItem(tr("All"));
|
|
|
|
|
|
if(m_currentWell != nullptr) {
|
|
|
int perforationCount = m_currentWell->getPerforationCount();
|
|
|
for(int i = 0; i < perforationCount; ++i) {
|
|
|
nmDataPerforation* perforation = m_currentWell->getPerforation(i);
|
|
|
if(perforation) {
|
|
|
QString perforationName = perforation->getName().getValue().toString();
|
|
|
if(perforationName.isEmpty()) {
|
|
|
perforationName = tr("Perforation #%1").arg(i + 1);
|
|
|
}
|
|
|
m_pAllComboBox->addItem(perforationName);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_pAllComboBox->setFixedWidth(180); // 加宽下拉框,便于查看射孔段名称
|
|
|
//m_pAllComboBox = new QComboBox();
|
|
|
//m_pAllComboBox->addItem(tr("All"));
|
|
|
//m_pAllComboBox->addItem(tr("Option 1"));
|
|
|
//m_pAllComboBox->addItem(tr("Option 2"));
|
|
|
//m_pAllComboBox->setFixedWidth(80);
|
|
|
|
|
|
bottomLayout->addWidget(m_pAllComboBox);
|
|
|
bottomLayout->addStretch();
|
|
|
|
|
|
// OK和Cancel按钮
|
|
|
m_pOkBtn = new QPushButton(tr("OK"));
|
|
|
m_pCancelBtn = new QPushButton(tr("Cancel"));
|
|
|
|
|
|
m_pOkBtn->setFixedWidth(75);
|
|
|
m_pCancelBtn->setFixedWidth(75);
|
|
|
|
|
|
bottomLayout->addWidget(m_pOkBtn);
|
|
|
bottomLayout->addWidget(m_pCancelBtn);
|
|
|
|
|
|
// 将底部布局添加到主布局
|
|
|
mainLayout->addLayout(bottomLayout);
|
|
|
|
|
|
// 设置主布局的边距和间距
|
|
|
mainLayout->setContentsMargins(10, 10, 10, 10);
|
|
|
mainLayout->setSpacing(5);
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onAddClicked()
|
|
|
{
|
|
|
if(!m_pCurrentPerforationItem)
|
|
|
return;
|
|
|
|
|
|
// 只在第一次Add时记录基准时间
|
|
|
if(!m_hasBaseTime) {
|
|
|
m_baseTime = QDateTime::currentDateTime();
|
|
|
m_hasBaseTime = true;
|
|
|
}
|
|
|
|
|
|
// 子行的第一行是表头,实际时间记录从第二行开始。
|
|
|
QTreeWidgetItem* selected = m_pTreeWidget->currentItem();
|
|
|
int selectedIndex = selected && selected->parent() == m_pCurrentPerforationItem
|
|
|
? m_pCurrentPerforationItem->indexOfChild(selected) - 1 : -1;
|
|
|
QList<double> newValuesInHr;
|
|
|
|
|
|
if(m_baseTimeValues.isEmpty()) {
|
|
|
// 第一次Add:添加100.0小时
|
|
|
newValuesInHr.append(100.0);
|
|
|
} else if(m_baseTimeValues.size() == 1 || selectedIndex == m_baseTimeValues.size() - 1) {
|
|
|
// 最后一行没有后续区间,沿用初始的100小时步长向后追加一条。
|
|
|
newValuesInHr.append(m_baseTimeValues.last() + 100.0);
|
|
|
} else {
|
|
|
// 保留三等分插入两个点的规则,但使用所选行与下一行之间的区间。
|
|
|
// 只选中射孔名称或表头时,沿用原来的第一个区间。
|
|
|
if(selectedIndex < 0) selectedIndex = 0;
|
|
|
const double start = m_baseTimeValues[selectedIndex];
|
|
|
const double end = m_baseTimeValues[selectedIndex + 1];
|
|
|
const double interval = (end - start) / 3.0;
|
|
|
newValuesInHr.append(start + interval);
|
|
|
newValuesInHr.append(start + 2 * interval);
|
|
|
}
|
|
|
|
|
|
// 合并原有数值和新数值,然后排序
|
|
|
m_baseTimeValues.append(newValuesInHr);
|
|
|
std::sort(m_baseTimeValues.begin(), m_baseTimeValues.end());
|
|
|
|
|
|
// 刷新显示
|
|
|
refreshDisplay();
|
|
|
|
|
|
// 展开当前选中的射孔分组以显示新添加的内容
|
|
|
m_pCurrentPerforationItem->setExpanded(true);
|
|
|
|
|
|
// 更新计数器
|
|
|
m_timeEntryCounter = m_baseTimeValues.size();
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onRemoveClicked()
|
|
|
{
|
|
|
if(!m_pCurrentPerforationItem)
|
|
|
return;
|
|
|
|
|
|
QTreeWidgetItem* selectedItem = m_pTreeWidget->currentItem();
|
|
|
bool hasSelection = false;
|
|
|
int selectedIndex = -1;
|
|
|
|
|
|
// 在日期模式和数值模式下都能正确识别有效行
|
|
|
if(selectedItem && selectedItem->parent() == m_pCurrentPerforationItem &&
|
|
|
selectedItem->text(0) != tr("Start time")) {
|
|
|
// 检查是否是有效的数据行
|
|
|
bool isValidDataRow = false;
|
|
|
|
|
|
if(m_showDatesMode) {
|
|
|
// 日期模式下检查第0列是否有时间数据
|
|
|
isValidDataRow = !selectedItem->text(0).isEmpty() &&
|
|
|
QDateTime::fromString(selectedItem->text(0), "yyyy-MM-dd hh:mm:ss").isValid();
|
|
|
} else {
|
|
|
// 数值时间也位于第一列。
|
|
|
isValidDataRow = !selectedItem->text(0).isEmpty();
|
|
|
}
|
|
|
|
|
|
if(isValidDataRow) {
|
|
|
// 计算选中项在数据中的索引
|
|
|
int dataIndex = 0;
|
|
|
|
|
|
for(int i = 0; i < m_pCurrentPerforationItem->childCount(); i++) {
|
|
|
QTreeWidgetItem* child = m_pCurrentPerforationItem->child(i);
|
|
|
if(child == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if(child == selectedItem) {
|
|
|
selectedIndex = dataIndex;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if(child->text(0) != tr("Start time")) {
|
|
|
dataIndex++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
hasSelection = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QList<double> timeValuesInHr = m_baseTimeValues;
|
|
|
|
|
|
if(timeValuesInHr.isEmpty())
|
|
|
return;
|
|
|
|
|
|
qSort(timeValuesInHr.begin(), timeValuesInHr.end());
|
|
|
|
|
|
// 未选中具体事件时,沿用删除末尾事件的入口。
|
|
|
if(!hasSelection || selectedIndex < 0 || selectedIndex >= timeValuesInHr.size()) {
|
|
|
selectedIndex = timeValuesInHr.size() - 1;
|
|
|
}
|
|
|
|
|
|
// 按Saphir的行操作规则删除当前行及下一行,先删下一行以免索引移位。
|
|
|
if(selectedIndex + 1 < timeValuesInHr.size()) {
|
|
|
timeValuesInHr.removeAt(selectedIndex + 1);
|
|
|
}
|
|
|
// 当前行位于末尾时,只删除自身。
|
|
|
timeValuesInHr.removeAt(selectedIndex);
|
|
|
|
|
|
m_baseTimeValues = timeValuesInHr;
|
|
|
|
|
|
if(m_baseTimeValues.isEmpty()) {
|
|
|
m_hasBaseTime = false;
|
|
|
}
|
|
|
|
|
|
refreshDisplay();
|
|
|
m_timeEntryCounter = timeValuesInHr.size();
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::refreshDisplay()
|
|
|
{
|
|
|
// 显示单位和日期模式属于整个窗口,刷新时同时更新所有射孔分组。
|
|
|
storeCurrentPerforation();
|
|
|
const bool blocked = m_pTreeWidget->blockSignals(true);
|
|
|
QTreeWidgetItem* selected = m_pTreeWidget->currentItem();
|
|
|
QTreeWidgetItem* selectedGroup = selected && selected->parent() ? selected->parent() : selected;
|
|
|
const int selectedRow = selected && selected->parent() ? selectedGroup->indexOfChild(selected) : -1;
|
|
|
|
|
|
for(int groupIndex = 0; groupIndex < m_pTreeWidget->topLevelItemCount(); ++groupIndex) {
|
|
|
QTreeWidgetItem* group = m_pTreeWidget->topLevelItem(groupIndex);
|
|
|
if(group == m_pPerforationRoot) continue;
|
|
|
const nmDataPerforation& data = m_perforations[group->data(0, Qt::UserRole).toInt()];
|
|
|
const QList<double> values = data.getTimeValues();
|
|
|
const QDateTime baseTime = data.getBaseTime();
|
|
|
const bool wasExpanded = group->isExpanded();
|
|
|
const bool hadRows = group->childCount() > 0;
|
|
|
qDeleteAll(group->takeChildren());
|
|
|
if(!values.isEmpty()) {
|
|
|
QTreeWidgetItem* heading = new QTreeWidgetItem(group);
|
|
|
heading->setText(0, tr("Start time"));
|
|
|
heading->setText(1, tr("Perforation state"));
|
|
|
heading->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
|
|
|
}
|
|
|
for(int i = 0; i < values.size(); ++i) {
|
|
|
QTreeWidgetItem* row = new QTreeWidgetItem(group);
|
|
|
const double seconds = toSecondsQuantized(values[i], "hr");
|
|
|
if(m_showDatesMode && baseTime.isValid()) {
|
|
|
row->setText(0, baseTime.addSecs(static_cast<qint64>(seconds)).toString("yyyy-MM-dd hh:mm:ss"));
|
|
|
row->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
} else {
|
|
|
row->setText(0, QString::number(fromSeconds(seconds, m_currentTimeUnit), 'g', 15));
|
|
|
row->setTextAlignment(0, Qt::AlignRight | Qt::AlignVCenter);
|
|
|
}
|
|
|
row->setFlags(row->flags() | Qt::ItemIsEditable);
|
|
|
row->setText(1, i % 2 == 0 ? tr("Closed") : tr("Open"));
|
|
|
row->setTextAlignment(1, Qt::AlignLeft | Qt::AlignVCenter);
|
|
|
row->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
|
|
|
}
|
|
|
group->setExpanded(wasExpanded || (!hadRows && !values.isEmpty()));
|
|
|
}
|
|
|
// 重建子行后按分组及行号恢复选择,不能继续使用已释放的行指针。
|
|
|
if(selectedGroup) {
|
|
|
m_pTreeWidget->setCurrentItem(selectedRow >= 0 && selectedGroup->childCount() > 0
|
|
|
? selectedGroup->child(qMin(selectedRow, selectedGroup->childCount() - 1)) : selectedGroup);
|
|
|
} else if(m_pCurrentPerforationItem) {
|
|
|
m_pTreeWidget->setCurrentItem(m_pCurrentPerforationItem);
|
|
|
}
|
|
|
m_pTreeWidget->blockSignals(blocked);
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
|
|
|
{
|
|
|
Q_UNUSED(previous);
|
|
|
// 点击射孔名称、组内表头或数据行,都以其所属射孔作为增删和编辑目标。
|
|
|
QTreeWidgetItem* group = current && current->parent() ? current->parent() : current;
|
|
|
if(group == m_pPerforationRoot) group = nullptr;
|
|
|
if(group == m_pCurrentPerforationItem) return;
|
|
|
storeCurrentPerforation();
|
|
|
m_pCurrentPerforationItem = group;
|
|
|
m_currentPerforationIndex = group ? group->data(0, Qt::UserRole).toInt() : -1;
|
|
|
loadDataFromModel();
|
|
|
m_pAddBtn->setEnabled(group != nullptr);
|
|
|
m_pRemoveBtn->setEnabled(group != nullptr);
|
|
|
}
|
|
|
void nmWxPerforationClosing::onTimeUnitChanged(const QString &unit)
|
|
|
{
|
|
|
// 更新当前时间单位
|
|
|
m_currentTimeUnit = unit;
|
|
|
|
|
|
// 刷新显示
|
|
|
refreshDisplay();
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onShowDatesToggled(bool checked)
|
|
|
{
|
|
|
m_showDatesMode = checked;
|
|
|
|
|
|
// 根据复选框状态隐藏/显示时间单位相关控件
|
|
|
m_pTimeUnitLabel->setVisible(!checked);
|
|
|
m_pTimeUnitComboBox->setVisible(!checked);
|
|
|
|
|
|
// 刷新显示
|
|
|
refreshDisplay();
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onAllComboChanged(const QString &text)
|
|
|
{
|
|
|
if(!m_currentWell) return;
|
|
|
|
|
|
if(text == tr("All")) {
|
|
|
// 显示所有射孔段的树形项目
|
|
|
showAllPerforationItems();
|
|
|
} else {
|
|
|
// 显示特定射孔段的树形项目
|
|
|
int perforationIndex = findPerforationIndexByName(text);
|
|
|
if(perforationIndex >= 0) {
|
|
|
showSpecificPerforationItem(perforationIndex);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::showAllPerforationItems()
|
|
|
{
|
|
|
if(!m_pTreeWidget) return;
|
|
|
|
|
|
// 显示所有射孔段项目
|
|
|
for(int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i) {
|
|
|
QTreeWidgetItem* item = m_pTreeWidget->topLevelItem(i);
|
|
|
if(item && item != m_pPerforationRoot) {
|
|
|
item->setHidden(false);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::showSpecificPerforationItem(int perforationIndex)
|
|
|
{
|
|
|
if(!m_pTreeWidget || !m_currentWell) return;
|
|
|
|
|
|
// 隐藏所有射孔段项目
|
|
|
for(int i = 0; i < m_pTreeWidget->topLevelItemCount(); ++i) {
|
|
|
QTreeWidgetItem* item = m_pTreeWidget->topLevelItem(i);
|
|
|
if(item && item != m_pPerforationRoot) {
|
|
|
item->setHidden(true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 只显示指定的射孔段项目
|
|
|
int itemIndex = perforationIndex + 1; // +1是因为第0项是表头
|
|
|
if(itemIndex < m_pTreeWidget->topLevelItemCount()) {
|
|
|
QTreeWidgetItem* item = m_pTreeWidget->topLevelItem(itemIndex);
|
|
|
if(item) {
|
|
|
item->setHidden(false);
|
|
|
item->setExpanded(true); // 展开显示内容
|
|
|
m_pTreeWidget->setCurrentItem(item); // 筛选射孔时同步切换操作目标
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int nmWxPerforationClosing::findPerforationIndexByName(const QString& name)
|
|
|
{
|
|
|
if(!m_currentWell) return -1;
|
|
|
|
|
|
int perforationCount = m_currentWell->getPerforationCount();
|
|
|
for(int i = 0; i < perforationCount; ++i) {
|
|
|
nmDataPerforation* perforation = m_currentWell->getPerforation(i);
|
|
|
if(perforation) {
|
|
|
QString perforationName = perforation->getName().getValue().toString();
|
|
|
if(perforationName.isEmpty()) {
|
|
|
perforationName = tr("Perforation #%1").arg(i + 1);
|
|
|
}
|
|
|
if(perforationName == name) {
|
|
|
return i;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onOkClicked()
|
|
|
{
|
|
|
saveDataToModel();
|
|
|
// 验证数据并接受对话框
|
|
|
accept();
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onCancelClicked()
|
|
|
{
|
|
|
// 取消对话框
|
|
|
reject();
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::onItemChanged(QTreeWidgetItem* item, int column)
|
|
|
{
|
|
|
// 添加一个静态变量防止递归调用
|
|
|
static bool isProcessingItemChange = false;
|
|
|
|
|
|
if(isProcessingItemChange) return;
|
|
|
|
|
|
// 只处理数值行的编辑(排除Start time行)
|
|
|
if(!item || item->text(0) == tr("Start time")) return;
|
|
|
|
|
|
// 确保是子项
|
|
|
if(item->parent() != m_pCurrentPerforationItem) return;
|
|
|
|
|
|
// 两种模式都编辑开始时间列,按显示模式选择解析方式。
|
|
|
if(m_showDatesMode && column == 0) {
|
|
|
// 日期模式下编辑第0列(时间字符串)
|
|
|
processDateEdit(item);
|
|
|
} else if(!m_showDatesMode && column == 0) {
|
|
|
// 数值模式下编辑第0列(数值)
|
|
|
processValueEdit(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::processDateEdit(QTreeWidgetItem* item)
|
|
|
{
|
|
|
static bool guard = false;
|
|
|
|
|
|
if(guard) return;
|
|
|
|
|
|
guard = true;
|
|
|
|
|
|
const QString s = item->text(0);
|
|
|
const QDateTime dt = QDateTime::fromString(s, "yyyy-MM-dd hh:mm:ss");
|
|
|
|
|
|
if(!dt.isValid()) {
|
|
|
guard = false;
|
|
|
refreshDisplay();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Qt 返回整数秒差(内部是 qint64),我们直接转 double 使用,不声明 qint64 变量
|
|
|
const double newHr = static_cast<double>(m_baseTime.secsTo(dt)) / 3600.0;
|
|
|
|
|
|
// 定位该项索引
|
|
|
int itemIndex = -1, dataIdx = 0;
|
|
|
|
|
|
for(int i = 0; i < m_pCurrentPerforationItem->childCount(); ++i) {
|
|
|
QTreeWidgetItem* ch = m_pCurrentPerforationItem->child(i);
|
|
|
if(ch == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if(ch == item) {
|
|
|
itemIndex = dataIdx;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if(ch->text(0) != tr("Start time")) dataIdx++;
|
|
|
}
|
|
|
|
|
|
if(itemIndex >= 0 && itemIndex < m_baseTimeValues.size()) {
|
|
|
QList<double> sorted = m_baseTimeValues;
|
|
|
std::sort(sorted.begin(), sorted.end());
|
|
|
|
|
|
const double currentHr = m_baseTimeValues[itemIndex];
|
|
|
int sortedIndex = -1;
|
|
|
|
|
|
for(int i = 0; i < sorted.size(); ++i) {
|
|
|
if(qAbs(sorted[i] - currentHr) < 1e-6) {
|
|
|
sortedIndex = i;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(sortedIndex >= 0) {
|
|
|
const double minAllowed = (sortedIndex > 0) ? sorted[sortedIndex - 1] : -std::numeric_limits<double>::max();
|
|
|
const double maxAllowed = (sortedIndex < sorted.size() - 1) ? sorted[sortedIndex + 1] : std::numeric_limits<double>::max();
|
|
|
|
|
|
if(newHr <= minAllowed || newHr >= maxAllowed) {
|
|
|
guard = false; refreshDisplay(); return;
|
|
|
}
|
|
|
|
|
|
m_baseTimeValues[itemIndex] = newHr;
|
|
|
std::sort(m_baseTimeValues.begin(), m_baseTimeValues.end());
|
|
|
refreshDisplay();
|
|
|
} else {
|
|
|
guard = false; refreshDisplay(); return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
guard = false;
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::processValueEdit(QTreeWidgetItem* item)
|
|
|
{
|
|
|
static bool guard = false;
|
|
|
|
|
|
if(guard) return;
|
|
|
|
|
|
guard = true;
|
|
|
|
|
|
bool ok = false;
|
|
|
// 数值时间与日期统一从第一列读取。
|
|
|
const double newDisp = item->text(0).toDouble(&ok);
|
|
|
|
|
|
if(!ok) {
|
|
|
guard = false;
|
|
|
refreshDisplay();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 显示单位 -> “秒(四舍五入为整数秒,double)” -> “小时”
|
|
|
const double secQuant = toSecondsQuantized(newDisp, m_currentTimeUnit);
|
|
|
const double newHr = fromSeconds(secQuant, "hr");
|
|
|
|
|
|
// 找到该行对应的数据索引(跳过 Start time 行)
|
|
|
int itemIndex = -1, dataIdx = 0;
|
|
|
|
|
|
for(int i = 0; i < m_pCurrentPerforationItem->childCount(); ++i) {
|
|
|
QTreeWidgetItem* ch = m_pCurrentPerforationItem->child(i);
|
|
|
if(ch == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
if(ch == item) {
|
|
|
itemIndex = dataIdx;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if(ch->text(0) != tr("Start time")) dataIdx++;
|
|
|
}
|
|
|
|
|
|
if(itemIndex >= 0 && itemIndex < m_baseTimeValues.size()) {
|
|
|
// 相邻不越界检查(基于排序副本)
|
|
|
QList<double> sorted = m_baseTimeValues;
|
|
|
std::sort(sorted.begin(), sorted.end());
|
|
|
|
|
|
const double currentHr = m_baseTimeValues[itemIndex];
|
|
|
int sortedIndex = -1;
|
|
|
|
|
|
for(int i = 0; i < sorted.size(); ++i) {
|
|
|
if(qAbs(sorted[i] - currentHr) < 1e-6) {
|
|
|
sortedIndex = i;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(sortedIndex >= 0) {
|
|
|
const double minAllowed = (sortedIndex > 0) ? sorted[sortedIndex - 1] : -std::numeric_limits<double>::max();
|
|
|
const double maxAllowed = (sortedIndex < sorted.size() - 1) ? sorted[sortedIndex + 1] : std::numeric_limits<double>::max();
|
|
|
|
|
|
if(newHr <= minAllowed || newHr >= maxAllowed) {
|
|
|
guard = false; refreshDisplay(); return;
|
|
|
}
|
|
|
|
|
|
m_baseTimeValues[itemIndex] = newHr;
|
|
|
std::sort(m_baseTimeValues.begin(), m_baseTimeValues.end());
|
|
|
refreshDisplay();
|
|
|
} else {
|
|
|
guard = false; refreshDisplay(); return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
guard = false;
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::storeCurrentPerforation()
|
|
|
{
|
|
|
// 切换分组和刷新前只更新本地副本,取消窗口时可丢弃全部修改。
|
|
|
if(!m_perforations.contains(m_currentPerforationIndex)) return;
|
|
|
nmDataPerforation& data = m_perforations[m_currentPerforationIndex];
|
|
|
data.setTimeValues(m_baseTimeValues);
|
|
|
QStringList states;
|
|
|
for(int i = 0; i < m_baseTimeValues.size(); ++i)
|
|
|
states << (i % 2 == 0 ? tr("Closed") : tr("Open"));
|
|
|
data.setTimeStates(states);
|
|
|
data.setBaseTime(m_hasBaseTime ? m_baseTime : QDateTime());
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::saveDataToModel()
|
|
|
{
|
|
|
// 统一提交各射孔的封堵字段,保留井对象中其他参数的最新值。
|
|
|
storeCurrentPerforation();
|
|
|
if(!m_currentWell) return;
|
|
|
for(QMap<int, nmDataPerforation>::const_iterator it = m_perforations.constBegin();
|
|
|
it != m_perforations.constEnd(); ++it) {
|
|
|
nmDataPerforation data = m_currentWell->getPerforationCopy(it.key());
|
|
|
data.setTimeValues(it.value().getTimeValues());
|
|
|
data.setTimeStates(it.value().getTimeStates());
|
|
|
data.setBaseTime(it.value().getBaseTime());
|
|
|
m_currentWell->updatePerforation(it.key(), data);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxPerforationClosing::loadDataFromModel()
|
|
|
{
|
|
|
// 切换编辑缓冲区时不重建树,避免删除正在点击或编辑的子行。
|
|
|
const nmDataPerforation data = m_perforations.value(m_currentPerforationIndex);
|
|
|
m_baseTimeValues = data.getTimeValues();
|
|
|
m_baseTime = data.getBaseTime();
|
|
|
m_hasBaseTime = m_baseTime.isValid();
|
|
|
m_timeEntryCounter = m_baseTimeValues.size();
|
|
|
}
|
|
|
double nmWxPerforationClosing::convertTime(double value, const QString& fromUnit, const QString& toUnit) const
|
|
|
{
|
|
|
// 直接使用底层单位组,兼容内部 hr/sec 与配置中的 h/s 写法。
|
|
|
iUnitGroup* group = iUnitHelper::getUnitGroupByUnit("s");
|
|
|
if(group) {
|
|
|
QString source = fromUnit == "sec" ? "s" : fromUnit;
|
|
|
QString destination = toUnit == "sec" ? "s" : toUnit;
|
|
|
if(group->indexOf("h") >= 0) {
|
|
|
if(source == "hr") source = "h";
|
|
|
if(destination == "hr") destination = "h";
|
|
|
}
|
|
|
double converted = value;
|
|
|
int digits = 6;
|
|
|
if(group->convert(source, value, destination, converted, digits)) return converted;
|
|
|
}
|
|
|
// 单位配置未加载时,沿用原有的内部时间换算。
|
|
|
nmDataAttribute attribute;
|
|
|
attribute.setAttributeUnitType(UNIT_TYPE_TIME);
|
|
|
return attribute.convertValueByUnitType(value, fromUnit, toUnit);
|
|
|
}
|
|
|
|
|
|
double nmWxPerforationClosing::roundToSecondDouble(double sec) const
|
|
|
{
|
|
|
return (sec >= 0.0) ? std::floor(sec + 0.5) : std::ceil(sec - 0.5);
|
|
|
}
|
|
|
|
|
|
double nmWxPerforationClosing::toSecondsQuantized(double value, const QString& unit) const
|
|
|
{
|
|
|
const double sec = convertTime(value, unit, "sec");
|
|
|
return roundToSecondDouble(sec); // 量化
|
|
|
}
|
|
|
|
|
|
double nmWxPerforationClosing::fromSeconds(double sec, const QString& unit) const
|
|
|
{
|
|
|
return convertTime(sec, "sec", unit);
|
|
|
}
|