123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683 |
- #include "WindowAppItemLink.h"
- #include "WindowAppPouScene.h"
- #include "WindowAppItemInterface.h"
- // 连接线段数量(正常情况下是五段线)
- #define LINK_LINE_NORMAL_COUNT 5
- // 并行模式(二段线)
- #define LINK_LINE_PARALLEL_COUNT 2
- WindowAppItemLink::WindowAppItemLink(
- WindowAppItemInterface* startItem,
- WindowAppItemInterface* endItem,
- LINK_MODE linkMode,
- QVector<QLineF> linePoints
- )
- : m_startInfItem(startItem)
- , m_endInfItem(endItem)
- , m_mode(linkMode)
- {
- // 接收悬停事件,用于显示鼠标切换效果
- this->setAcceptHoverEvents(true);
- // 设置风格,可选择但不可拖动
- this->setFlag(QGraphicsItem::ItemIsSelectable, true);
- // this->setFlag(QGraphicsItem::ItemIsMovable, true);
- // 初始化连接线段
- this->initLines(linePoints);
- // 2022-6-12,如果初始化时指定了线段坐标,则按照指定的坐标来初始化线段(主要用于反序列化的情况)
- if (linePoints.size() <= 0)
- {
- // 如果没有指定坐标,则还是根据起点和终点自动生成线段信息
- this->updateLinkLinesAuto();
- }
- // 初始化右键菜单
- createContextMenu();
- //// 将消息全部透传到Group中的成员中处理
- //this->setHandlesChildEvents(false);
- }
- WindowAppItemLink::~WindowAppItemLink()
- {
- // 删除动态创建的Link连线
- for (int i = 0; i < m_linkLines.size(); i++)
- {
- delete m_linkLines[i];
- }
- // 清空线段组
- m_linkLines.clear();
- }
- /// <summary>
- /// 初始化连接线段
- /// </summary>
- void WindowAppItemLink::initLines(QVector<QLineF> linePoints)
- {
- if (m_startInfItem == nullptr || m_endInfItem == nullptr)
- {
- qDebug() << "[Error] WindowAppItemLink::initLines - start or end InfItem is nullptr.";
- return;
- }
- // 如果是普通模式,则正常初始化五段线
- if (this->m_mode == LINK_MODE::LINK_NORMAL)
- {
- m_nLineCount = LINK_LINE_NORMAL_COUNT;
- }
- else
- {
- m_nLineCount = LINK_LINE_PARALLEL_COUNT;
- }
- // 初始化需要绘制的线条
- for (int i = 0; i < m_nLineCount; i++)
- {
- WindowAppItemLinkLine* line = nullptr;
- if (linePoints.size() <= 0)
- {
- line = new WindowAppItemLinkLine(QPointF(0,0), QPointF(0,0), this, m_mode);
- }
- else
- {
- line = new WindowAppItemLinkLine(linePoints[i].p1(), linePoints[i].p2(), this, m_mode);
- }
- // 设定索引值,用于线条移动时使用
- line->m_nLineIndex = i;
- m_linkLines.push_back(line);
- //// 加入本控件组
- //this->addToGroup(line);
- // 绑定线段移动消息,用于在拖动时其他线段进行联动
- connect(
- line,
- &WindowAppItemLinkLine::lineMoveSignal,
- this,
- &WindowAppItemLink::onLineMove
- );
- }
- // 设置第一个线段为起点线段
- m_linkLines[0]->setLinkStart(true);
- // 设置最后一个线段为终点线段
- m_linkLines[m_nLineCount - 1]->setLinkEnd(true);
- }
- /// <summary>
- /// 按照正常模式更新线段
- /// </summary>
- void WindowAppItemLink::updateNormalLines()
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 通过五段线的方式来进行连接
- // 五段线起点坐标
- QPointF ptStart = m_startInfItem->line().p2();
- ptStart = mapFromScene(m_startInfItem->mapToScene(ptStart));
- // 五段线终点坐标
- QPointF ptEnd = m_endInfItem->line().p1();
- ptEnd = mapFromScene(m_endInfItem->mapToScene(ptEnd));
- // 每条连接线的起点和终点
- QPointF ptLineStart = ptStart;
- QPointF ptLineEnd;
- // 起点连接线的终点位置坐标
- QPointF ptLinkStart;
- // 终点连接线的终点位置坐标
- QPointF ptLinkEnd;
- // 1. 计算起点连接线(起点一定是输出接口)
- ptLineEnd = QPointF(ptStart.x() + LINK_START_LINE_SIZE, ptStart.y());
- ptLinkStart = ptLineEnd;
- // 线条方向从起点向外
- m_linkLines[0]->updateLine(ptStart, ptLineEnd);
- // 2. 计算终点连接线(终点一定是输入接口)
- ptLinkEnd = QPointF(ptEnd.x() - LINK_START_LINE_SIZE, ptEnd.y());
- // 线条方向从外部向终点
- m_linkLines[4]->updateLine(ptLinkEnd, ptEnd);
- // 3. 起点延伸线
- ptLineStart = ptLineEnd;
- float ptLineEndY = ptLineStart.y() + (ptEnd.y() - ptStart.y()) / 2;
- ptLineEnd = QPointF(ptLineStart.x(), ptLineEndY);
- // 方向为从起点往外
- m_linkLines[1]->updateLine(ptLineStart, ptLineEnd);
- // 4. 中间连接线
- ptLineStart = ptLineEnd;
- float ptLineEndX = ptLineStart.x() + (ptLinkEnd.x() - ptLinkStart.x());
- ptLineEnd = QPointF(ptLineEndX, ptLineStart.y());
- // 方向为从起点方向到终点方向
- m_linkLines[2]->updateLine(ptLineStart, ptLineEnd);
- // 5. 终点延伸线
- ptLineStart = ptLineEnd;
- // 方向为从外部连向终点
- m_linkLines[3]->updateLine(ptLineStart, ptLinkEnd);
- }
- /// <summary>
- /// 按照并行模式更新线段
- /// </summary>
- void WindowAppItemLink::updateParallelLines()
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 通过二段竖线的方式来进行连接
- // 终点坐标(终点接口的外侧点)
- QPointF ptEnd(m_endInfItem->line().p1());
- ptEnd = mapFromScene(m_endInfItem->mapToScene(ptEnd));
- ptEnd.setX(ptEnd.rx() - LINK_START_LINE_SIZE);
- // 起点坐标(并行母线端)
- QPointF ptStart(m_endInfItem->line().p1().rx() - LINK_START_LINE_SIZE, m_startInfItem->line().p2().ry());
- ptStart = mapFromScene(m_startInfItem->mapToScene(ptStart));
- ptStart.setX(ptEnd.rx());
- // 1. 垂直连线(方向为从母线端到接口端,可能是向上,可能是向下)
- m_linkLines[0]->updateLine(ptStart, ptEnd);
- // 2. 终点连接线(终点一定是输入接口,方向为从左到右)
- ptEnd.setX(ptEnd.rx() + LINK_START_LINE_SIZE);
- QPointF ptLinkEnd = QPointF(ptEnd.rx() - LINK_START_LINE_SIZE, ptEnd.ry());
- m_linkLines[1]->updateLine(ptLinkEnd, ptEnd);
- }
- //=============================================================================
- //
- // 菜单相关代码
- //
- //=============================================================================
- /// <summary>
- /// 显示右键菜单
- /// </summary>
- /// <param name="event"></param>
- void WindowAppItemLink::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
- {
- scene()->clearSelection();
- this->setSelected(true);
- contextMenu->exec(event->screenPos());
- }
- ///// <summary>
- ///// 任何一条子线段被选中时,都交给Group统一处理
- ///// </summary>
- ///// <param name="selItem"></param>
- //void WindowAppItemLink::onLineSelected(WindowAppItemLinkLine* selItem)
- //{
- // //for (const auto& lineItem : m_linkLines)
- // //{
- // // lineItem->setSelected(true);
- //
- // // qDebug() << "WindowAppItemLink::onLineSelected.";
- // //}
- //
- // this->setSelected(true);
- //}
- ///// <summary>
- ///// 显示右键菜单
- ///// </summary>
- ///// <param name="event"></param>
- //void WindowAppItemLink::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
- //{
- // scene()->clearSelection();
- // setSelected(true);
- //
- // contextMenu->exec(event->screenPos());
- //}
- /// <summary>
- /// 初始化功能块的右键菜单
- /// </summary>
- void WindowAppItemLink::createContextMenu()
- {
- cutAction = new QAction(("Cut"), this);
- connect(cutAction, &QAction::triggered, this, &WindowAppItemLink::onMenuCut);
- copyAction = new QAction(QIcon(":/image/Copy.png"), ("Copy"), this);
- connect(copyAction, &QAction::triggered, this, &WindowAppItemLink::onMenuCopy);
- pasteAction = new QAction(("Paste"), this);
- connect(pasteAction, &QAction::triggered, this, &WindowAppItemLink::onMenuPaste);
- deleteAction = new QAction(QIcon(":/image/Delete.png"), ("&Delete"), this);
- deleteAction->setStatusTip(("Delete item from diagram"));
- connect(deleteAction, &QAction::triggered, this, &WindowAppItemLink::onMenuDelete);
- contextMenu = new QMenu();
- contextMenu->addAction(cutAction);
- contextMenu->addAction(copyAction);
- contextMenu->addAction(pasteAction);
- contextMenu->addSeparator();
- contextMenu->addAction(deleteAction);
- }
- /// <summary>
- /// 菜单 - Cut
- /// </summary>
- void WindowAppItemLink::onMenuCut()
- {
- QMessageBox::information(nullptr, "Vision Plus", "onMenuCut");
- }
- /// <summary>
- /// 菜单 - Copy
- /// </summary>
- void WindowAppItemLink::onMenuCopy()
- {
- QMessageBox::information(nullptr, "Vision Plus", "onMenuCopy");
- }
- /// <summary>
- /// 菜单 - Paste
- /// </summary>
- void WindowAppItemLink::onMenuPaste()
- {
- QMessageBox::information(nullptr, "Vision Plus", "onMenuPaste");
- }
- /// <summary>
- /// 菜单 - Delete
- /// </summary>
- void WindowAppItemLink::onMenuDelete()
- {
- // 直接调用scene()中的删除方法来统一删除
- ((WindowAppPouScene*)scene())->delLink(this);
- }
- //=============================================================================
- //
- // 线条拖动相关代码
- //
- //=============================================================================
- /// <summary>
- /// Group内的线段准备拖拽时,是否允许其移动(必须在合法的范围内拖动)
- /// </summary>
- /// <param name="nextP1"></param>
- /// <param name="nextP2"></param>
- /// <returns></returns>
- bool WindowAppItemLink::canMove(QPointF nextP1, QPointF nextP2)
- {
- Q_UNUSED(nextP1);
- Q_UNUSED(nextP2);
- return true;
- }
- /// <summary>
- /// 获取所有线段坐标(用于序列化)
- /// </summary>
- /// <returns></returns>
- QVector<QLineF> WindowAppItemLink::getAllLinkLinePoints()
- {
- QVector<QLineF> allLines;
- for (const auto& one_line : m_linkLines)
- {
- allLines.push_back(one_line->line());
- }
- return allLines;
- }
- /// <summary>
- /// 将Link设置为Movable(包括Group和所有的子线段)
- /// </summary>
- /// <param name="bEnable"></param>
- void WindowAppItemLink::setMovable(bool bEnable /*= true*/)
- {
- // 自身设置为 Movable
- this->setFlag(QGraphicsItem::ItemIsMovable, bEnable);
- // 所有子线段设置
- for (auto& subLine : m_linkLines)
- {
- subLine->setFlag(QGraphicsItem::ItemIsMovable, bEnable);
- }
- }
- /// <summary>
- /// 当Link线段移动时,同步调整其他线段的位置
- /// </summary>
- /// <param name="item"></param>
- void WindowAppItemLink::onLineMove(WindowAppItemLinkLine* moveItem)
- {
- // qDebug() << "WindowAppItemLink::onLineMove";
- QPointF moveLinePt1 = moveItem->mapToScene(moveItem->line().p1());
- QPointF moveLinePt2 = moveItem->mapToScene(moveItem->line().p2());
- // qDebug() << "moveLinePt1:" << moveLinePt1 << " moveLinePt2:" << moveLinePt2;
- // 普通模式(普通模式只允许1、2、3号线移动)
- if (m_mode == LINK_MODE::LINK_NORMAL)
- {
- this->moveNormalLines(moveItem, moveLinePt1, moveLinePt2);
- }
- // 并行模式(并行模式仅有0号线允许移动)
- else
- {
- this->moveParallelLines(moveItem, moveLinePt1, moveLinePt2);
- }
- }
- /// <summary>
- /// 线段被拖动时,同步移动其他线段(普通模式)
- /// </summary>
- /// <param name="item"></param>
- /// <param name="movePt1"></param>
- /// <param name="movePt2"></param>
- void WindowAppItemLink::moveNormalLines(WindowAppItemLinkLine* item, QPointF movePt1, QPointF movePt2)
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 如果移动的是1号线(1号线只允许左右移动)
- if (item->m_nLineIndex == 1)
- {
- // 将0号线的pt2以及2号线的pt1设置成与1号线相同
- m_linkLines[0]->setP2(mapFromScene(movePt1));
- m_linkLines[2]->setP1(mapFromScene(movePt2));
- }
- // 如果移动的是2号线(2号线只允许上下移动)
- else if (item->m_nLineIndex == 2)
- {
- // 将1号线的pt2以及3号线的pt1设置成与2号线相同
- m_linkLines[1]->setP2(mapFromScene(movePt1));
- m_linkLines[3]->setP1(mapFromScene(movePt2));
- }
- // 如果移动的是3号线(3号线只允许左右移动)
- else if (item->m_nLineIndex == 3)
- {
- // 将2号线的pt2以及4号线的pt1设置成与2号线相同
- m_linkLines[2]->setP2(mapFromScene(movePt1));
- m_linkLines[4]->setP1(mapFromScene(movePt2));
- }
- // Error
- else
- {
- }
- }
- /// <summary>
- /// 线段被拖动时,同步移动其他线段(并行模式)
- /// </summary>
- /// <param name="item"></param>
- /// <param name="movePt1"></param>
- /// <param name="movePt2"></param>
- void WindowAppItemLink::moveParallelLines(WindowAppItemLinkLine* item, QPointF movePt1, QPointF movePt2)
- {
- Q_UNUSED(item);
- Q_UNUSED(movePt1);
- // 因为只有 0 号线可以拖动,那么直接调整1号线的长度即可
- WindowAppItemLinkLine* ajustLine = m_linkLines.last();
- ajustLine->setP1(mapFromScene(movePt2));
- // 2022-6-8,此时应该一起调整并行母线的长度
- m_startInfItem->updatePostion();
- }
- /// <summary>
- /// 根据起点和终点信息更新连接线段信息(自动方式连接)
- /// </summary>
- void WindowAppItemLink::updateLinkLinesAuto()
- {
- if (m_mode == LINK_MODE::LINK_NORMAL)
- {
- this->updateNormalLines();
- }
- else
- {
- this->updateParallelLines();
- }
- }
- /// <summary>
- /// 根据移动的功能块进行对应的连接线断调整(手动方式连接)
- /// </summary>
- /// <param name="pBlock"></param>
- /// <param name="bStart"></param>
- void WindowAppItemLink::updateLinkLinesManual(QGraphicsItem* pBlock, bool bStart)
- {
- // qDebug() << "WindowAppItemLink::updateLinkLines " << bStart;
- Q_UNUSED(pBlock);
- // 如果是普通模式
- if (m_mode == LINK_MODE::LINK_NORMAL)
- {
- // 如果移动的是起点位置的功能块,那么移动0号线和1号线
- if (bStart)
- {
- this->updateNormalLinesByStart();
- }
- // 如果移动的是终点位置的功能块,那么移动4号线和3号线
- else
- {
- this->updateNormalLinesByEnd();
- }
- }
- // 如果是并行模式
- else if( m_mode==LINK_MODE::LINK_PARALLEL)
- {
- // 如果移动的是起点位置的功能块
- if (bStart)
- {
- this->updateParallelLinesByStart();
- }
- // 如果移动的是终点位置的功能块
- else
- {
- this->updateParallelLinesByEnd();
- }
- }
- }
- ///// <summary>
- ///// 按照并行模式更新线段(手动连接模式,功能块拖动时)
- ///// </summary>
- ///// <param name="pBlock"></param>
- //void WindowAppItemLink::updateParallelLines(QGraphicsItem* pBlock)
- //{
- //
- //}
- /// <summary>
- /// 按照正常模式更新线段(手动连接模式,功能块拖动时)
- /// </summary>
- /// <param name="pBlock"></param>
- void WindowAppItemLink::updateNormalLinesByStart()
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 移动0号线和1号线
-
- // 计算0号线
- QPointF ptLineStart = m_startInfItem->line().p2();
- ptLineStart = mapFromScene(m_startInfItem->mapToScene(ptLineStart));
- QPointF ptLineEnd = m_linkLines[0]->line().p2();
- // 如果0号线的长度少于了最小长度,则整个线条重置
- if (qAbs(ptLineEnd.x() - ptLineStart.x()) < LINK_START_LINE_SIZE)
- {
- updateLinkLinesAuto();
- return;
- }
- // 否则重新计算0号线的y值
- // QPointF ptLineEnd = QPointF(ptLineStart.x() + LINK_START_LINE_SIZE, ptLineStart.y());
- ptLineEnd.setY(ptLineStart.y());
- m_linkLines[0]->updateLine(ptLineStart, ptLineEnd);
- // 然后继续调整1号线
- // 调整规则,p2始终不变,只调整p1
- m_linkLines[1]->setP1(ptLineEnd);
- }
- /// <summary>
- /// 按照正常模式更新线段(手动连接模式,功能块拖动时)
- /// </summary>
- /// <param name="pBlock"></param>
- void WindowAppItemLink::updateNormalLinesByEnd()
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 移动4号线和3号线
-
- // 计算4号线
- QPointF ptLineEnd = m_endInfItem->line().p1();
- ptLineEnd = mapFromScene(m_endInfItem->mapToScene(ptLineEnd));
- QPointF ptLineStart = m_linkLines[4]->line().p1();
- // 如果4号线的长度少于了最小长度,则整个线条重置
- if (qAbs(ptLineEnd.x() - ptLineStart.x()) < LINK_START_LINE_SIZE)
- {
- updateNormalLines();
- return;
- }
- // 否则重新计算4号线的y值
- ptLineStart.setY(ptLineEnd.y());
- m_linkLines[4]->updateLine(ptLineStart, ptLineEnd);
- // 然后继续调整3号线
- // 调整规则,p1始终不变,只调整p2
- m_linkLines[3]->setP2(ptLineStart);
- }
- /// <summary>
- /// 按照并行模式更新线段(手动连接模式,起点功能块拖动时)
- /// </summary>
- void WindowAppItemLink::updateParallelLinesByStart()
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 计算0号线
- // 只是调整一下 p1 的y值即可
- QPointF ptLineStart = m_linkLines[0]->line().p1();
- QPointF ptItem = m_startInfItem->line().p2();
- ptItem = mapFromScene(m_startInfItem->mapToScene(ptItem));
- ptLineStart.setY(ptItem.ry());
- m_linkLines[0]->setP1(ptLineStart);
- }
- /// <summary>
- /// 按照并行模式更新线段(手动连接模式,终点功能块拖动时)
- /// </summary>
- void WindowAppItemLink::updateParallelLinesByEnd()
- {
- if (m_linkLines.size() <= 0)
- {
- return;
- }
- // 计算1号线
- // 需要同时调整1号线和0号线
- //
- // 计算1号线
- QPointF ptLineEnd = m_endInfItem->line().p1();
- ptLineEnd = mapFromScene(m_endInfItem->mapToScene(ptLineEnd));
- QPointF ptLineStart = m_linkLines[1]->line().p1();
- // 如果1号线的长度少于了最小长度,则整个线条重置
- if (qAbs(ptLineEnd.x() - ptLineStart.x()) < LINK_START_LINE_SIZE)
- {
- updateParallelLines();
- return;
- }
- // 否则重新计算1号线的y值
- ptLineStart.setY(ptLineEnd.y());
- m_linkLines[1]->updateLine(ptLineStart, ptLineEnd);
- // 然后继续调整0号线
- // 调整规则,p1始终不变,只调整p2
- m_linkLines[0]->setP2(ptLineStart);
- }
|