Compare commits

...

5 Commits

@ -1,5 +1,8 @@
#pragma once
#include <QCursor>
#include <QPointer>
#include "nmObjToolBase.h"
#include "nmObjDelete.h"
@ -14,11 +17,18 @@ public:
nmObjDeleteTool();
nmObjDeleteTool(ZxPlot* m_pPlot);
virtual ~nmObjDeleteTool();
public:
// 鼠标各种响应
virtual bool onLeftDown(const QPointF& pt) override;
virtual bool onLeftUp(const QPointF& pt) override;
virtual bool onMouseMove(const QPointF& pt) override;
virtual bool onRightDown(const QPointF& pt) override;
// 工具被移除前恢复视口光标。
void resetDeleteCursor();
// 渲染
void onRender(QPainter* painter, const ZxPaintParam& param);
@ -28,23 +38,28 @@ public:
protected:
virtual void onDeactivated() override;
// 记录矩形四个点的坐标
QVector<QPointF> m_vecPoints;
// 获取当前画布
ZxPlot* m_pPlot;
// 可以删除的画布对象
QVector<ZxObjBase*> m_delObj;
//画布中所有的点
//QVector<QPointF> m_vecAllPos;
private:
QPointF m_currentMousePos; // 记录当前鼠标位置
bool m_bShowDeleteIcon; // 显示图标标志
QPixmap m_deleteIcon; // 垃圾桶图标
ZxObjBase* m_hoverObj; // 当前悬停的对象
// 悬停和点击统一选择屏幕命中范围内最近的可删除图元。
ZxObjBase* findNearestDeleteObj(const QPointF& pt);
void initDeleteCursor();
bool updateDeleteCursor(const QPointF& pt);
QCursor m_deleteCursor; // 固定像素尺寸的删除光标
QPointer<QWidget> m_cursorViewport; // 当前使用删除光标的视口
QCursor m_previousCursor; // 恢复原有显式光标
bool m_hadExplicitCursor;
};

@ -26,6 +26,9 @@ public slots:
signals:
// 鼠标在绘图区内的位置,使用模型坐标。
void sigPointerPos(double x, double y);
protected:
// 拖拽支持

@ -90,6 +90,10 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
// 清除上次的工具
foreach (nmObjToolBase* oldTool, m_activeTools.values()) {
// 移除工具不会保证触发停用回调,切换命令前主动恢复删除光标。
if (nmObjDeleteTool* deleteTool = dynamic_cast<nmObjDeleteTool*>(oldTool)) {
deleteTool->resetDeleteCursor();
}
pPlot->removeTools(oldTool);
}
m_activeTools.clear();
@ -316,6 +320,10 @@ void nmGuiPlotCmdHelper::cancelActiveTools()
}
foreach (nmObjToolBase* activeTool, m_activeTools.values()) {
// 取消删除模式时同步清理视口上的自定义光标。
if (nmObjDeleteTool* deleteTool = dynamic_cast<nmObjDeleteTool*>(activeTool)) {
deleteTool->resetDeleteCursor();
}
pWxPlot->m_pPlot->removeTools(activeTool);
}
m_activeTools.clear();

@ -8,7 +8,7 @@
#include "ZxPlot.h"
#include <vector>
#include "zxGmyBase.h"
#include "nmObjLineCrack.h"
#include "nmDataFracture.h"
@ -19,9 +19,7 @@ nmObjDeleteTool::nmObjDeleteTool() : nmObjToolBase()
{
setText(tr("nObjDeleteTool"));
m_oNot = NMOT_Delete;
m_bShowDeleteIcon = false;
m_hoverObj = nullptr;
m_deleteIcon.load(ZxBaseUtil::getDirOf(s_Dir_Res, "Icon") + "nmTrash.png");
initDeleteCursor();
}
nmObjDeleteTool::nmObjDeleteTool(ZxPlot* pPlot)
@ -31,171 +29,192 @@ nmObjDeleteTool::nmObjDeleteTool(ZxPlot* pPlot)
setText(tr("nObjDeleteTool"));
m_oNot = NMOT_Delete;
nmDataLogFile::getInstance()->writeLog(" nmObjDeleteTool ++++++++ " + QString::number(m_oNot));
this->m_delObj.clear();
m_bShowDeleteIcon = false;
m_hoverObj = nullptr;
m_deleteIcon.load(ZxBaseUtil::getDirOf(s_Dir_Res, "Icon") + "nmTrash.png");
initDeleteCursor();
}
nmObjDeleteTool::~nmObjDeleteTool()
{
// 视口可能已销毁,由 QPointer 避免访问失效窗口。
resetDeleteCursor();
}
void nmObjDeleteTool::initDeleteCursor()
{
m_hadExplicitCursor = false;
QPixmap icon(ZxBaseUtil::getDirOf(s_Dir_Res, "Icon") + "nmTrash.png");
QPixmap cursorPixmap(40, 40);
cursorPixmap.fill(Qt::transparent);
QPainter painter(&cursorPixmap);
// 仅显示小垃圾桶,以中心作为点击热点,大小不受画布缩放影响。
painter.drawPixmap(cursorPixmap.rect(), icon, icon.rect());
painter.end();
m_deleteCursor = QCursor(cursorPixmap, 20, 20);
}
bool nmObjDeleteTool::updateDeleteCursor(const QPointF& pt)
{
// 与点击使用同一命中规则,仅在绘图视口内切换光标。
if (m_pObj != nullptr || QApplication::activeModalWidget() != nullptr ||
findNearestDeleteObj(pt) == nullptr) {
resetDeleteCursor();
return false;
}
QWidget* viewport = getView()->viewport();
if (m_cursorViewport != viewport) {
resetDeleteCursor();
m_cursorViewport = viewport;
m_hadExplicitCursor = viewport->testAttribute(Qt::WA_SetCursor);
m_previousCursor = viewport->cursor();
}
viewport->setCursor(m_deleteCursor);
return true;
}
void nmObjDeleteTool::resetDeleteCursor()
{
// 只恢复本工具设置的光标,不覆盖后来由其他工具设置的样式。
if (m_cursorViewport &&
m_cursorViewport->cursor().pixmap().cacheKey() == m_deleteCursor.pixmap().cacheKey()) {
if (m_hadExplicitCursor) {
m_cursorViewport->setCursor(m_previousCursor);
} else {
m_cursorViewport->unsetCursor();
}
}
m_cursorViewport = nullptr;
}
bool nmObjDeleteTool::onMouseMove(const QPointF& pt)
{
if (m_pObj != nullptr) {
return nmObjToolBase::onMouseMove(pt);
}
// 命中时阻止图元自身的移动光标覆盖删除提示。
return updateDeleteCursor(pt);
}
bool nmObjDeleteTool::onRightDown(const QPointF& pt)
{
// 右键退出或打开菜单前恢复普通光标。
resetDeleteCursor();
return nmObjToolBase::onRightDown(pt);
}
void nmObjDeleteTool::onDeactivated()
{
// 切换活动工具时释放自定义光标。
resetDeleteCursor();
nmObjToolBase::onDeactivated();
}
bool nmObjDeleteTool::onLeftDown(const QPointF& pt)
{
if (m_pObj != nullptr) {
return nmObjToolBase::onLeftDown(pt);
}
// 命中时接管按下事件,防止区域标记等图元的移动工具抢先处理抬起事件。
return findNearestDeleteObj(pt) != nullptr;
}
bool nmObjDeleteTool::onLeftUp(const QPointF& pt)
{
if (NULL == m_pObj) //绘图对象尚未创建(鼠标选点ing)
{
//changeCursor(Qt::CrossCursor);
// 判断鼠标是否进入了要删除的对象上
if (!m_delObj.isEmpty())
{
int objCount = this->m_delObj.count();
for(int i = 0; i < objCount; i++) {
ZxObjBase* obj = this->m_delObj[i];
if (obj == NULL)
{
continue;
if (m_pObj != nullptr) {
return nmObjToolBase::onLeftUp(pt);
}
// 按抬起位置重新命中,使用与悬停提示相同的对象筛选和距离规则。
ZxObjBase* obj = findNearestDeleteObj(pt);
if (obj != nullptr) {
// 弹出确认框之前恢复,确认或取消后都不残留删除光标。
resetDeleteCursor();
nmSingalCenter::getInstance()->emitSigDeleteOneObj(obj->getName(), (void*)this);
}
return true;
}
ZxObjBase* nmObjDeleteTool::findNearestDeleteObj(const QPointF& pt)
{
ZxRenderView* view = getView();
if (m_pPlot == nullptr || view == nullptr) {
return nullptr;
}
// 转为视口坐标后按 16 像素判断,保持不同缩放下的点击容差一致。
const QPointF mousePos = view->mapFromScene(pt);
const double hitRadius = 16.0;
double nearestDistanceSquared = hitRadius * hitRadius;
ZxObjBase* nearestObj = nullptr;
zxGmyBase geometry;
for (int i = 0; i < m_pPlot->getObjCount(); ++i) {
ZxObjBase* obj = m_pPlot->getObjByIndex(i);
if (obj == nullptr || !obj->isVisible()) {
continue;
}
const bool isFracture = _isSame("nObjLineFracture", obj->getTagName());
const bool isRegion = _isSame("nObjRegion", obj->getTagName());
if (!isFracture && !isRegion &&
!_isSame("nObjLineFault", obj->getTagName()) &&
!_isSame("nObjPointWell", obj->getTagName()) &&
!_isSame("nObjRegionMark", obj->getTagName())) {
continue;
}
QVector<QPointF> pts = obj->getAllPos();
// 井或区域标记
if (pts.count() == 1)
{
// 计算鼠标点击位置与井的中心点的距离
float distance = QLineF(pt, pts[0]).length();
if (distance <= 1.f * 5) // 如果距离在5像素范围内
{
if (obj != NULL)
{
nmSingalCenter::getInstance()->emitSigDeleteOneObj(obj->getName(), (void*)this);
return true;
// 保留原有规则:未绑定数据及 DFN 生成的裂缝不参与单独删除。
if (isFracture) {
nmObjLineCrack* fracture = dynamic_cast<nmObjLineCrack*>(obj);
nmPlotGraphicBinder* binder = fracture ? fracture->getGraphicBinder() : nullptr;
nmDataFracture* data = binder ? binder->getCrackDataFor(fracture) : nullptr;
if (data == nullptr || data->getFractureType().getValue() == tr("DFN")) {
continue;
}
}
}
// 线
for (int i = 0; i < pts.count(); i++)
{
int n1 = i;
int n2 = (i == pts.count() - 1 ? 0 : i + 1);
if (_isNearLine(pt, pts[n1], pts[n2], 1.f * 3))
{
if (obj != NULL)
{
nmSingalCenter::getInstance()->emitSigDeleteOneObj(obj->getName(),(void*) this);
return true;
QVector<QPointF> points = obj->getAllPos();
if (points.isEmpty()) {
continue;
}
for (int j = 0; j < points.count(); ++j) {
points[j] = view->mapFromScene(points[j]);
}
double distance = QLineF(mousePos, points[0]).length();
double distanceSquared = distance * distance;
// 裂缝、断层按开放折线计算,只有区域包含末点到首点的闭合边。
const int segmentCount = isRegion ? points.count() : points.count() - 1;
for (int j = 0; j < segmentCount; ++j) {
const QPointF& start = points[j];
const QPointF& end = points[(j + 1) % points.count()];
double segmentDistanceSquared;
if (start == end) {
// 重复节点按单点处理,避免零长度线段参与投影计算。
distance = QLineF(mousePos, start).length();
segmentDistanceSquared = distance * distance;
} else {
double t = 0.0;
segmentDistanceSquared = geometry.pointSegmentDistanceSquared2D(mousePos, start, end, t);
}
}
distanceSquared = qMin(distanceSquared, segmentDistanceSquared);
}
}
} else {
return nmObjToolBase::onLeftUp(pt);
}
return true;
}
else //业已绑定绘图对象
{
return nmObjToolBase::onLeftUp(pt);
}
// 范围内优先选择最近图元;距离相同时保留先命中的对象。
if (distanceSquared <= hitRadius * hitRadius &&
(nearestObj == nullptr || distanceSquared < nearestDistanceSquared)) {
nearestDistanceSquared = distanceSquared;
nearestObj = obj;
}
}
return nearestObj;
}
void nmObjDeleteTool::onPaint(QPainter* painter, const ZxPaintParam& param)
{
nmObjDelete* pObj = dynamic_cast<nmObjDelete*>(m_pObj);
if (pObj == NULL) //绘图对象尚未创建(鼠标选点ing)
{
m_currentMousePos = getCurrentPos();
m_bShowDeleteIcon = false;
m_hoverObj = nullptr;
if (this->m_pPlot == NULL) {
return;
}
// 处理可删除对象列表
int objCount = this->m_pPlot->getObjCount();
for(int i = 0; i < objCount; i++) {
ZxObjBase* obj = this->m_pPlot->getObjByIndex(i);
if (obj == NULL) {
continue;
}
if(_isSame("nObjLineFracture", obj->getTagName()) || \
_isSame("nObjLineFault", obj->getTagName()) || \
_isSame("nObjRegion", obj->getTagName()) || \
_isSame("nObjPointWell", obj->getTagName()) || \
_isSame("nObjRegionMark", obj->getTagName()))
{
// 判断裂缝是不是裂缝
if(_isSame("nObjLineFracture", obj->getTagName())) {
nmObjLineCrack* pFractureObj = dynamic_cast<nmObjLineCrack*>(obj);
if (pFractureObj == NULL) {
continue;
}
nmPlotGraphicBinder* pBinder = pFractureObj->getGraphicBinder();
nmDataFracture* pFractureData = pBinder ? pBinder->getCrackDataFor(pFractureObj) : nullptr;
if (pFractureData == NULL) {
continue;
}
// 如果是DFN生成的裂缝,那么不做处理
if (pFractureData->getFractureType().getValue() == tr("DFN")) {
continue;
}
}
bool isContain = false;
// 遍历 m_delObj,检查是否存在相同的指针
for (int j = 0; j < m_delObj.count(); j++) {
if (m_delObj[j] == obj) {
isContain = true;
break;
}
}
if (!isContain)
{
m_delObj.append(obj);
}
// 检查鼠标是否在此对象上
QVector<QPointF> pts = obj->getAllPos();
// 井或区域标记
if (pts.count() == 1)
{
float distance = QLineF(m_currentMousePos, pts[0]).length();
if (distance <= 1.f * 5) // 如果距离在5像素范围内
{
m_bShowDeleteIcon = true;
m_hoverObj = obj;
}
}
// 线
else
{
for (int j = 0; j < pts.count(); j++)
{
int n1 = j;
int n2 = (j == pts.count() - 1 ? 0 : j + 1);
if (_isNearLine(m_currentMousePos, pts[n1], pts[n2], 1.f * 3))
{
m_bShowDeleteIcon = true;
m_hoverObj = obj;
break;
}
}
}
}
}
// 如果鼠标悬停在可删除对象上,绘制删除图标
if (m_bShowDeleteIcon && !m_deleteIcon.isNull()) {
// 在鼠标右侧绘制图标
QPointF iconPos = m_currentMousePos + QPointF(4, 2);
int iconSize = 10; // 图标尺寸
QRectF iconRect(iconPos.x(), iconPos.y(), iconSize, iconSize);
painter->drawPixmap(iconRect, m_deleteIcon, m_deleteIcon.rect());
}
}
else //业已绑定绘图对象
{
// paintTrackingRealObj(painter, param);
}
if (m_pObj == nullptr) {
// 缩放或图元变化后重新判断命中,不再在画布上绘制垃圾桶。
updateDeleteCursor(getCurrentPos());
}
}

@ -974,12 +974,10 @@ void nmPlotGraphicBinder::createMeasureForGraphic(nmObjLineMeasure* pGraphic)
QPointF pStart = pts[0];
QPointF pEnd = pts[1];
double dLength = QLineF(pStart, pEnd).length();
if (pEnd != pStart) {
pData->setStartPoint(pStart);
pData->setEndPoint(pEnd);
} else {
pData->setCurrentPoint(pStart);
}
// 单击测量也保存起终点,避免重绘时被尚未更新的原点坐标覆盖。
pData->setStartPoint(pStart);
pData->setEndPoint(pEnd);
pData->setCurrentPoint(pEnd == pStart ? pStart : QPointF(0, 0));
nmDataAttribute lenAttr = pData->getLength();
lenAttr.setValue(dLength);
pData->setLength(lenAttr);

@ -15,6 +15,8 @@ ZX_DEFINE_DYNAMIC(mPlotView, nmPlotView)
nmPlotView::nmPlotView()
{
// 未按下鼠标时也接收视口移动事件,实时更新模型位置。
viewport()->setMouseTracking(true);
}
void nmPlotView::onPrepareContextMenu(QMenu* pMenu,
@ -140,6 +142,8 @@ void nmPlotView::mouseMoveEvent(QMouseEvent* e)
// 状态栏
if (rt.contains(pos))
{
// 复用坐标轴换算结果,通知模型位置窗口刷新坐标。
emit sigPointerPos(dX, dY);
s = tr(" Current Pos:%1-%2").arg(sValueX).arg(sValueY);
if (!sValueY2.isEmpty())
s += QString("-%1").arg(sValueY2);

@ -1125,8 +1125,7 @@ void nmWxForecast::startForecastCalculation()
m_calculationRunning = true;
m_parametersGroup->setEnabled(false);
m_generateButton->setEnabled(false);
// 与敏感性分析一致,隐藏输入窗口,保留对象接收计算结果。
hide();
// 计算期间保留设置窗口可见,输入和生成按钮保持禁用。
m_task->start();
}

@ -83,6 +83,12 @@ void nmWxMeasureDlg::initComponents() {
void nmWxMeasureDlg::onFitStartClicked()
{
// 使用当前井列表;没有可拟合的井时保留测量点,避免写入默认原点。
m_vecWellList = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
if (m_vecWellList.isEmpty()) {
return;
}
m_pFitStartButton->setEnabled(false);
// 先取出当前点
@ -131,9 +137,9 @@ void nmWxMeasureDlg::onFitStartClicked()
nearest = pt;
}
}
// 更新起点和终点
m_pData->setStartPoint(startPt);
m_pData->setEndPoint(nearest);
// 单点测量先拟合起点时,将起点移到井上,终点保留在点击位置。
m_pData->setStartPoint(nearest);
m_pData->setEndPoint(startPt);
// 重新计算长度
double newLen = QLineF(startPt, nearest).length();
@ -148,6 +154,12 @@ void nmWxMeasureDlg::onFitStartClicked()
void nmWxMeasureDlg::onFitEndClicked()
{
// 使用当前井列表;没有可拟合的井时保留测量点,避免写入默认原点。
m_vecWellList = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
if (m_vecWellList.isEmpty()) {
return;
}
m_pFitEndButton->setEnabled(false);
// 先取出当前点

@ -2789,8 +2789,7 @@ void nmWxSensitive::startSensitivityCalculation()
connect(m_pSensitivityTask, SIGNAL(finished()),
this, SLOT(onSensitivityFinished()), Qt::QueuedConnection);
setCalculationRunning(true);
// 计算期间隐藏输入窗口,任务结束后继续复用。
hide();
// 计算期间保留设置窗口可见,由运行状态控制输入和按钮禁用。
m_pSensitivityTask->start();
}

Loading…
Cancel
Save