WindowAppItemInterface.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. #include "WindowAppItemInterface.h"
  2. #include "WindowAppBlockStandard.h"
  3. #include "WindowAppBlockPort.h"
  4. #include "WindowAppPouScene.h"
  5. #include "WindowAppItemLink.h"
  6. #include "Pou.h"
  7. #include <qmath.h>
  8. WindowAppItemInterface::WindowAppItemInterface(
  9. _INTERFACE* pInf,
  10. POU* pPou,
  11. const TOOL* pTool,
  12. bool bShowOnly,
  13. QGraphicsItem* parent /*= nullptr*/
  14. ):
  15. m_infInfo(pInf)
  16. , m_pPou(pPou)
  17. , m_pParentTool(pTool)
  18. , m_bShowOnly(bShowOnly)
  19. // , m_nParallelLineLen(PARARREL_LINE_BASE_LEN)
  20. {
  21. // 接收悬停事件,显示提示
  22. setAcceptHoverEvents(true);
  23. // 设置风格,可选择但不可拖动
  24. setFlag(QGraphicsItem::ItemIsMovable, false);
  25. setFlag(QGraphicsItem::ItemIsSelectable, true);
  26. // 接收Item移动消息
  27. setFlag(QGraphicsItem::ItemSendsGeometryChanges);
  28. this->setParentItem(parent);
  29. }
  30. /// <summary>
  31. /// 返回接口的矩形边界
  32. /// </summary>
  33. /// <returns></returns>
  34. QRectF WindowAppItemInterface::boundingRect() const
  35. {
  36. QRectF rc;
  37. // Goto输出接口的矩形区域
  38. if ( this->m_infInfo->isGotoToolEnd() )
  39. {
  40. rc = QRectF(
  41. m_parentRect.right() + 1,
  42. -GOTO_OUTPUT_TRIA_SIDE / 2,
  43. GOTO_OUTPUT_TRIA_SIDE * qSin(60 * 3.14 / 180),
  44. GOTO_OUTPUT_TRIA_SIDE
  45. );
  46. }
  47. // Parallel输出接口的矩形区域(并行母线)
  48. else if ( this->m_infInfo->isParallelToolEnd() )
  49. {
  50. //rc = QRectF(
  51. // m_parentRect.x() - PARARREL_LINE_EX_LEN,
  52. // m_parentRect.bottom() + PARARREL_LINE_SPACE,
  53. // m_nParallelLineLen,
  54. // PARARREL_LINE_HEIGHT
  55. //);
  56. rc = QRectF(
  57. line().x1(),
  58. line().y1(),
  59. line().length(),
  60. PARARREL_LINE_HEIGHT
  61. );
  62. }
  63. // 如果是正常接口,则按正常接口区域返回
  64. else
  65. {
  66. qreal extra = (pen().width()) * 4.0;
  67. rc = QRectF(
  68. line().p1(),
  69. QSizeF(line().p2().x() - line().p1().x(),
  70. line().p2().y() - line().p1().y()
  71. ))
  72. .normalized()
  73. .adjusted(-extra, -extra, extra, extra);
  74. }
  75. return rc;
  76. }
  77. /// <summary>
  78. /// 形状边界(为了扩大接口区域的可点击范围)
  79. /// </summary>
  80. /// <returns></returns>
  81. QPainterPath WindowAppItemInterface::shape() const
  82. {
  83. QPainterPath path;
  84. // Goto输出接口的Shape
  85. if ( this->m_infInfo->isGotoToolEnd())
  86. {
  87. path.moveTo(m_parentRect.right() + 1, -GOTO_OUTPUT_TRIA_SIDE / 2);
  88. path.lineTo(m_parentRect.right() + 1, GOTO_OUTPUT_TRIA_SIDE / 2);
  89. path.lineTo(m_parentRect.right() + 1 + GOTO_OUTPUT_TRIA_SIDE * qSin(60 * 3.14 / 180), 0);
  90. }
  91. // Parallel输出接口的Shape
  92. else if (this->m_infInfo->isParallelToolEnd())
  93. {
  94. //path.addRect(
  95. // m_parentRect.x() - PARARREL_LINE_EX_LEN,
  96. // m_parentRect.bottom() + PARARREL_LINE_SPACE,
  97. // m_nParallelLineLen,
  98. // PARARREL_LINE_HEIGHT
  99. //);
  100. path.addRect(
  101. line().x1(),
  102. line().y1(),
  103. line().length(),
  104. PARARREL_LINE_HEIGHT
  105. );
  106. }
  107. // 默认的线条Shape
  108. else
  109. {
  110. qreal extra = (pen().width()) * 4.0;
  111. extra *= 1.414;
  112. path = QGraphicsLineItem::shape();
  113. // 通过Stroker设置精确Shape
  114. QPainterPathStroker stroker;
  115. stroker.setWidth(extra);
  116. path = stroker.createStroke(path);
  117. }
  118. return path;
  119. }
  120. /// <summary>
  121. /// 接口绘制
  122. /// </summary>
  123. /// <param name="painter"></param>
  124. /// <param name="option"></param>
  125. /// <param name="widget"></param>
  126. void WindowAppItemInterface::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget /*= nullptr*/)
  127. {
  128. Q_UNUSED(option);
  129. Q_UNUSED(widget);
  130. // 如果本接口压根都没启用的话,则不进行绘制
  131. if (!m_bShowOnly && !this->m_infInfo->bEnable)
  132. {
  133. return;
  134. }
  135. // 排除掉非标准接口,因为非标准接口无法通过连线的方式进行链接
  136. if (this->m_infInfo->Type == INF_TYPE::INF_TYPE_EVENT
  137. || this->m_infInfo->Type == INF_TYPE::INF_TYPE_CONTROL
  138. )
  139. {
  140. return;
  141. }
  142. painter->setRenderHint(QPainter::Antialiasing, true);
  143. painter->save();
  144. // 硬件组态禁止显示端口
  145. // if ( m_pPou->pouName() == GROUP_NAME_HARDWARE)
  146. // {
  147. // painter->restore();
  148. // return;
  149. // }
  150. // 线条颜色
  151. QColor penColor;
  152. // 根据接口废弃与否,显示不同的颜色
  153. // 废弃线条显示黑色
  154. if (m_infInfo->Discard != INF_DISCARD::INF_DEFAULT)
  155. {
  156. penColor = COLOR_INF_DISCARDLINE;
  157. }
  158. // 正常线条显示正常颜色
  159. else
  160. {
  161. penColor = COLOR_INF_LINE;
  162. }
  163. // 根据接口选中与否,显示不同的颜色
  164. // 尚未选中,默认线条
  165. if (!isSelected() && !IsParentSelected())
  166. {
  167. painter->setPen(QPen(penColor, PEN_LINE_WIDTH, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  168. }
  169. // 被选中,加粗线条
  170. else
  171. {
  172. painter->setPen(QPen(penColor, PEN_LINE_WIDTH_SEL, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  173. }
  174. // 绘制接口线条
  175. this->drawInterfaceLine(painter);
  176. // 绘制接口文字
  177. this->drawInterfaceText(painter);
  178. painter->restore();
  179. }
  180. /// <summary>
  181. /// 绘制接口线条
  182. /// </summary>
  183. /// <param name="painter"></param>
  184. void WindowAppItemInterface::drawInterfaceLine(QPainter* painter)
  185. {
  186. // 如果是Goto工具的输出接口,则画一个特殊的三角形箭头
  187. if ( this->m_infInfo->isGotoToolEnd())
  188. {
  189. this->drawArrowOutput(painter);
  190. }
  191. // 2022-8-28,如果是ForLoop工具的ToolEnd接口,也需要画一个特殊的三角箭头,但是高度有区别
  192. else if (this->m_infInfo->isForLoopToolEnd())
  193. {
  194. // 调整三角形高度之后绘制
  195. this->drawArrowOutput(painter, -10);
  196. }
  197. // 如果是Parallel工具的输出接口,则画一个特殊的并行母线
  198. else if (this->m_infInfo->isParallelToolEnd())
  199. {
  200. this->drawParallelOutput(painter);
  201. }
  202. // 否则绘制默认的接口线条效果
  203. else
  204. {
  205. painter->drawLine(line());
  206. }
  207. }
  208. /// <summary>
  209. /// 绘制接口文字
  210. /// </summary>
  211. /// <param name="painter"></param>
  212. void WindowAppItemInterface::drawInterfaceText(QPainter* painter)
  213. {
  214. if ( this->m_infInfo->bShowName )
  215. {
  216. painter->setPen(COLOR_TEXT);
  217. painter->setFont(FONT_INF);
  218. painter->drawText(m_ptInfTitle, m_infInfo->strName);
  219. }
  220. }
  221. /// <summary>
  222. /// 绘制Goto输出接口的三角形
  223. /// </summary>
  224. /// <param name="painter"></param>
  225. void WindowAppItemInterface::drawArrowOutput(QPainter* painter, int nFixHeight)
  226. {
  227. QPainterPath path;
  228. // 起点
  229. QPointF ptStart(m_parentRect.right() + 1, -GOTO_OUTPUT_TRIA_SIDE / 2 + nFixHeight);
  230. // 终点
  231. QPointF ptEnd(ptStart.rx() + GOTO_OUTPUT_TRIA_SIDE * qSin(60 * 3.14 / 180), nFixHeight);
  232. path.moveTo(ptStart.rx(), -GOTO_OUTPUT_TRIA_SIDE / 2 + nFixHeight);
  233. path.lineTo(ptStart.rx(), GOTO_OUTPUT_TRIA_SIDE / 2 + nFixHeight);
  234. // 按照正三角形的方式绘制
  235. path.lineTo(ptEnd);
  236. // 如果是Goto工具,则需要根据工具的执行状态改变箭头颜色
  237. if (this->m_pParentTool->isGotoTool())
  238. {
  239. if (this->m_pParentTool->execParams.nRetValue != VPEnum::RETURN_VALUE::Goto)
  240. {
  241. painter->fillPath(path, QBrush(COLOR_GOTO_OUTPUT_READY));
  242. }
  243. else
  244. {
  245. painter->fillPath(path, QBrush(COLOR_GOTO_OUTPUT_DONE));
  246. }
  247. }
  248. else
  249. {
  250. painter->fillPath(path, QBrush(COLOR_ARROW_OUTPUT));
  251. }
  252. }
  253. /// <summary>
  254. /// 绘制并行工具的输出接口(并行母线)
  255. /// </summary>
  256. /// <param name="painter"></param>
  257. void WindowAppItemInterface::drawParallelOutput(QPainter* painter)
  258. {
  259. //QPointF start(m_parentRect.x() - PARARREL_LINE_EX_LEN, m_parentRect.bottom() + PARARREL_LINE_SPACE);
  260. //painter->fillRect(
  261. // start.rx(),
  262. // start.ry(),
  263. // m_nParallelLineLen,
  264. // PARARREL_LINE_HEIGHT,
  265. // COLOR_FRAME_PARALLEL
  266. //);
  267. painter->fillRect(
  268. line().x1(),
  269. line().y1(),
  270. line().length(),
  271. PARARREL_LINE_HEIGHT,
  272. COLOR_FRAME_PARALLEL
  273. );
  274. }
  275. /// <summary>
  276. /// 更新常规接口位置
  277. /// </summary>
  278. void WindowAppItemInterface::updateRegularPostion(int nIndex)
  279. {
  280. // 计算线条高度
  281. int lineHeight = 0;
  282. // 根据Tool类型调整一下起始高度
  283. //2022-9-20(所有既带ToolInterface又带普通接口的工具都需要按标准工具的布局处理)
  284. if (m_infInfo->parent()->isStandardTool()
  285. || m_infInfo->parent()->isForloopTool()
  286. || m_infInfo->parent()->isWaitTool()
  287. )
  288. {
  289. lineHeight = m_parentRect.top() + TBD_INF_OFFSET + nIndex * (short)TBD_INF_SPACING;
  290. }
  291. else
  292. {
  293. lineHeight = m_parentRect.top() + PBD_INF_OFFSET;
  294. }
  295. // 设置线条和文字位置(需要区分左右)
  296. QRect textRect = QFontMetrics(FONT_INF).boundingRect(m_infInfo->strName);
  297. // 输出接口文字位置
  298. if (m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  299. {
  300. QLineF lineInf(
  301. QPointF(m_parentRect.right() + 1, lineHeight),
  302. QPointF(m_parentRect.right() + TBD_INF_LINE + 1, lineHeight)
  303. );
  304. setLine(lineInf);
  305. if (m_pParentTool->Type != TOOL_TYPE::TOOL_TYPE_GOTO)
  306. {
  307. int textX = m_parentRect.right() - textRect.width() - 4;
  308. int textY = lineHeight + textRect.height() / 4;
  309. m_ptInfTitle = QPointF(textX, textY);
  310. }
  311. // 2022-4-4增加,如果是Goto工具,则需要调整一下输出接口的显示未知
  312. // 因为Goto工具有序号遮挡,需要把文字的位置左移
  313. else
  314. {
  315. int textX = m_parentRect.right() - textRect.width() - TBD_INDEX_WIDTH - 4;
  316. int textY = lineHeight + textRect.height() / 4;
  317. m_ptInfTitle = QPointF(textX, textY);
  318. }
  319. }
  320. // 输入接口文字位置
  321. else
  322. {
  323. QLineF lineInf(
  324. QPointF(m_parentRect.left() - TBD_INF_LINE, lineHeight),
  325. QPointF(m_parentRect.left(), lineHeight)
  326. );
  327. setLine(lineInf);
  328. int textX = m_parentRect.left() + 4;
  329. int textY = lineHeight + textRect.height() / 4;
  330. m_ptInfTitle = QPointF(textX, textY);
  331. }
  332. }
  333. /// <summary>
  334. /// 更新Goto输出接口位置
  335. /// </summary>
  336. void WindowAppItemInterface::updateGotoPosition()
  337. {
  338. // 起点
  339. QPointF ptStart(m_parentRect.right() + 1, -GOTO_OUTPUT_TRIA_SIDE / 2);
  340. // 终点
  341. QPointF ptEnd(ptStart.rx() + GOTO_OUTPUT_TRIA_SIDE * qSin(60 * 3.14 / 180), 0);
  342. // 2022-4-23增加,此处应该把Line也设置一下,因为Link的时候是以Line为坐标来Link的
  343. this->setLine(ptStart.rx(), 0, ptEnd.rx(), ptEnd.ry());
  344. }
  345. /// <summary>
  346. /// 更新Parallel输出接口位置
  347. /// </summary>
  348. void WindowAppItemInterface::updateParallelPosition()
  349. {
  350. // 获取本接口连接的下联接口
  351. QVector<_INTERFACE*> linkInterfaces = this->m_infInfo->pDownLinkInterfaces;
  352. // 如果尚未下联接口,则使用默认位置
  353. if (linkInterfaces.size() <= 0)
  354. {
  355. // 基准起点
  356. QPointF start(m_parentRect.x() - PARARREL_LINE_EX_LEN, m_parentRect.bottom() + PARARREL_LINE_SPACE);
  357. // 设置并行母线线条,母线矩形上沿
  358. this->setLine(
  359. start.rx(),
  360. start.ry() + PARARREL_LINE_HEIGHT,
  361. start.rx() + PARARREL_LINE_BASE_LEN + PARARREL_LINE_EX_LEN, // 基础长度
  362. start.ry() + PARARREL_LINE_HEIGHT
  363. );
  364. }
  365. // 如果有下联接口,则遍历所有下联接口连线的坐标位置,用于并行母线的坐标参考
  366. else
  367. {
  368. // 取出对应的LinkItem连线,这些都是从并行母线到下面各个Block的ToolStart接口连线
  369. QList<WindowAppItemLink*> linkItems = (qobject_cast<WindowAppPouScene*>(this->scene()))->getLinkItemsByInfItem(this);
  370. // 遍历所有的接口,取出左边界和右边界的最大值
  371. QRectF sceneParentRect = this->mapRectToScene(m_parentRect);
  372. int nBaseLeft = sceneParentRect.x();
  373. int nBaseRight = nBaseLeft + PARARREL_LINE_BASE_LEN;
  374. int nMaxLeft = nBaseLeft;
  375. int nMaxRight = 0;
  376. // 遍历所有下联接口的Item,找到最左Item和最右Item
  377. for (auto linkItem : linkItems)
  378. {
  379. QLineF linkLine = linkItem->startLine();
  380. int ptX = linkLine.p1().x();
  381. // qDebug() << "linkLine.p1().x: " << ptX << " ParallineLeft: " << nBaseLeft << " ParallineRight: " << nBaseRight;
  382. if (ptX < nMaxLeft)
  383. {
  384. nMaxLeft = ptX;
  385. }
  386. else if (ptX > nMaxRight)
  387. {
  388. nMaxRight = ptX;
  389. }
  390. }
  391. // 根据下联接口线的位置,调整母线的长度
  392. // 调整母线左侧
  393. if (nMaxLeft < nBaseLeft)
  394. {
  395. QPointF pt = this->mapToScene(this->line().p1());
  396. pt.setX(nMaxLeft - PARARREL_LINE_EX_LEN);
  397. pt = this->mapFromScene(pt);
  398. // qDebug() << "Before linkLine: " << line() << " pt:" << pt;
  399. this->setLine(QLineF(pt, line().p2()));
  400. // qDebug() << "Ajusted linkLine: " << line();
  401. }
  402. // 调整母线右侧
  403. if (nMaxRight + PARARREL_LINE_EX_LEN > nBaseRight)
  404. {
  405. QPointF pt = this->mapToScene(this->line().p2());
  406. pt.setX(nMaxRight + PARARREL_LINE_EX_LEN);
  407. pt = this->mapFromScene(pt);
  408. // qDebug() << "Before linkLine: " << line() << " pt:" << pt;
  409. this->setLine(QLineF(line().p1(), pt));
  410. // qDebug() << "Ajusted linkLine: " << line();
  411. }
  412. }
  413. }
  414. /// <summary>
  415. /// 鼠标双击时,自动添加port变量并连接
  416. /// </summary>
  417. /// <param name="event"></param>
  418. void WindowAppItemInterface::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
  419. {
  420. Q_UNUSED(event);
  421. // QMessageBox::information(nullptr, "Vision Plus", "mouseDoubleClickEvent");
  422. //qDebug() << this->scenePos();
  423. //qDebug() << this->mapToScene(this->line().p1()) << this->mapToScene(this->line().p2());
  424. // 只有鼠标左键才响应
  425. // if (event->button() == Qt::LeftButton && !m_bShowOnly)
  426. // {
  427. // // 由于Interface本身的图形已经和所属的Block整合到了一起,所以通过Interface的scenePos()获取的是其父block的Pos
  428. // // 此处需要帮助外部函数提供一个本Interface的真实Pos
  429. // QLineF realLine = QLineF(
  430. // this->mapToScene(this->line().p1()),
  431. // this->mapToScene(this->line().p2())
  432. // );
  433. //
  434. // ((WindowAppPouScene*)scene())->addPortAndAutolink(this, realLine);
  435. // }
  436. }
  437. /// <summary>
  438. /// 更新最新的父矩形区域
  439. /// </summary>
  440. /// <param name="parentRect"></param>
  441. void WindowAppItemInterface::updateParentRect(QRectF parentRect)
  442. {
  443. // 首先转换坐标系
  444. QPointF ptFix = mapFromScene(0, 0);
  445. parentRect.translate(ptFix);
  446. m_parentRect = parentRect;
  447. }
  448. /// <summary>
  449. /// 根据Block的位置和自身的序号更新本接口的位置(标准接口)
  450. /// </summary>
  451. /// <param name="parentRect"></param>
  452. void WindowAppItemInterface::updatePostion(int nIndex)
  453. {
  454. // 由于Goto输出接口是一个三角形,所以Goto接口需要专门处理
  455. if (this->m_infInfo->isGotoToolEnd())
  456. {
  457. this->updateGotoPosition();
  458. }
  459. // 由于Parallel输出接口是并行母线的形式,所以Parallel输出接口需要专门处理
  460. else if (this->m_infInfo->isParallelToolEnd())
  461. {
  462. this->updateParallelPosition();
  463. }
  464. // 如果是正常的ToolInterface接口,直接走Tool类型的接口位置更新
  465. else if (this->m_infInfo->isToolInterface())
  466. {
  467. this->updateToolInterfacePostion();
  468. }
  469. // 剩下的都按常规接口更新
  470. else
  471. {
  472. this->updateRegularPostion(nIndex);
  473. }
  474. }
  475. /// <summary>
  476. /// 根据Block的位置计算ToolStart、ToolEnd接口的位置(标准工具、并行工具、For工具都会调用到此处)
  477. /// </summary>
  478. /// <param name="parentRect"></param>
  479. void WindowAppItemInterface::updateToolInterfacePostion()
  480. {
  481. // 输入接口
  482. if (m_infInfo->Direction == INF_DIRECTION::INF_DIR_IN)
  483. {
  484. // 线条高度
  485. int lineHeight = m_parentRect.top() + TBD_TOPINF_SPACING;
  486. // 设置线条位置
  487. QRect textRect = QFontMetrics(FONT_INF).boundingRect(m_infInfo->strName);
  488. QLineF lineTop(
  489. QPointF(m_parentRect.left() - TBD_TOPINF_LINE, lineHeight),
  490. QPointF(m_parentRect.left(), lineHeight)
  491. );
  492. setLine(lineTop);
  493. //// 设置文字位置(目前ToolStart接口暂时不显示名字,为了以后需要,先计算出来)
  494. //int textX = m_parentRect.left() + 4;
  495. //int textY = lineHeight + textRect.height() / 4;
  496. //m_ptInfTitle = QPointF(textX, textY);
  497. }
  498. // 输出接口
  499. else
  500. {
  501. // 线条高度
  502. int lineHeight = m_parentRect.top() + TBD_TOPINF_SPACING;
  503. // 设置线条位置
  504. QRect textRect = QFontMetrics(FONT_INF).boundingRect(m_infInfo->strName);
  505. QLineF lineTop(
  506. QPointF(m_parentRect.right() + TBD_TOPINF_LINE, lineHeight),
  507. QPointF(m_parentRect.right(), lineHeight)
  508. );
  509. setLine(lineTop);
  510. //// 设置文字位置(目前ToolStart接口暂时不显示名字,为了以后需要,先计算出来)
  511. //int textX = m_parentRect.left() + 4;
  512. //int textY = lineHeight + textRect.height() / 4;
  513. //m_ptInfTitle = QPointF(textX, textY);
  514. }
  515. }
  516. /// <summary>
  517. /// 判断父Block是否处于选中状态
  518. /// </summary>
  519. /// <returns></returns>
  520. bool WindowAppItemInterface::IsParentSelected()
  521. {
  522. bool bIsParentSel = false;
  523. WindowAppBlockBase* pParentBlock = qgraphicsitem_cast<WindowAppBlockBase*>(this->parentItem());
  524. bIsParentSel = pParentBlock->isSelected();
  525. return bIsParentSel;
  526. }
  527. /// <summary>
  528. /// 显示动态右键菜单
  529. /// </summary>
  530. /// <param name="event"></param>
  531. void WindowAppItemInterface::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
  532. {
  533. Q_UNUSED(event);
  534. // 设置接口为选中状态
  535. this->setSelected(true);
  536. // 如果只仅供展示用,则不显示右键菜单
  537. if (m_bShowOnly)
  538. {
  539. return;
  540. }
  541. // 动态创建
  542. createDynamicContextMenu();
  543. }
  544. /// <summary>
  545. /// 新建Interface的右键菜单
  546. /// </summary>
  547. void WindowAppItemInterface::createDynamicContextMenu()
  548. {
  549. if (m_infInfo->isGotoToolEnd()
  550. || m_infInfo->isParallelToolStart()
  551. || m_infInfo->isParallelToolEnd()
  552. //|| m_infInfo->isToolStart()
  553. //|| m_infInfo->isToolEnd()
  554. )
  555. {
  556. return;
  557. }
  558. // 硬件组态不显示右键菜单
  559. if (m_pPou->pouName() == GROUP_NAME_HARDWARE)
  560. {
  561. return;
  562. }
  563. QMenu* contextMenu = new QMenu();
  564. QMenu* childMenu = new QMenu();
  565. QAction* unLinkAction = new QAction(("Unlink"), this);
  566. QAction* smartLinkAction = new QAction(("Smart link"), this);
  567. QAction* addWatchAction = new QAction(("Add Watch..."), this);
  568. QAction* addPortAction = new QAction(("Add Port..."), this);
  569. QAction* disableAction = new QAction(("Disable"), this);
  570. QAction* linkAction;
  571. // 根据输入输出显示不同的内容
  572. if (m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  573. {
  574. linkAction = new QAction(("Link To..."), this);
  575. }
  576. else
  577. {
  578. linkAction = new QAction(("Link From..."), this);
  579. }
  580. connect(childMenu, SIGNAL(triggered(QAction*)), this, SLOT(onMenuLink(QAction*)));
  581. connect(unLinkAction, &QAction::triggered, this, &WindowAppItemInterface::onMenuUnlink);
  582. connect(smartLinkAction, &QAction::triggered, this, &WindowAppItemInterface::onMenuSmartlink);
  583. connect(addWatchAction, &QAction::triggered, this, &WindowAppItemInterface::onMenuAddWatch);
  584. connect(addPortAction, &QAction::triggered, this, &WindowAppItemInterface::onMenuAddPort);
  585. connect(disableAction, &QAction::triggered, this, &WindowAppItemInterface::onMenuDisable);
  586. // 根据适配的接口创建对应的菜单项
  587. const QMap<QString, _INTERFACE*> allInfs = m_pPou->GetAllInterfaces();
  588. QMapIterator<QString, _INTERFACE*> i(allInfs);
  589. while (i.hasNext())
  590. {
  591. const _INTERFACE* pInf = i.next().value();
  592. // 方向相反、类型相同、不从属于一个工具、并且父工具不是Port类型
  593. // 目标端口未被引用 , 端口为启用状态
  594. if (pInf->isRevDirectionTo(this->m_infInfo)
  595. && pInf->isSameTypeTo(this->m_infInfo)
  596. && pInf->parent() !=this->m_infInfo->parent()
  597. && pInf->parent()->isStandardTool()
  598. && (pInf->nRefCount == 0 || pInf->Direction == INF_DIRECTION::INF_DIR_OUT)
  599. && pInf->bEnable
  600. )
  601. {
  602. QAction* childAction = new QAction(i.key(), this);
  603. childMenu->addAction(childAction);
  604. }
  605. }
  606. // 设置菜单结构
  607. if (childMenu->actions().count() > 0)
  608. {
  609. linkAction->setMenu(childMenu);
  610. }
  611. // 输入端口,且未链接过数据
  612. if ( m_infInfo->Direction == INF_DIRECTION::INF_DIR_IN
  613. && m_infInfo->nRefCount == 0
  614. )
  615. {
  616. contextMenu->addAction(linkAction);
  617. }
  618. // 输出端口,都可以添加
  619. if (m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  620. {
  621. contextMenu->addAction(linkAction);
  622. }
  623. // 如果引用计数不等于 0 ,该端口允许取消链接(因为取消无意义)
  624. if (m_infInfo->nRefCount != 0)
  625. {
  626. contextMenu->addAction(unLinkAction);
  627. }
  628. contextMenu->addSeparator();
  629. // 只有输入端口有Smartlink选项
  630. if (m_infInfo->Direction == INF_DIRECTION::INF_DIR_IN
  631. && m_infInfo->nRefCount == 0
  632. )
  633. {
  634. contextMenu->addAction(smartLinkAction);
  635. contextMenu->addSeparator();
  636. }
  637. contextMenu->addAction(addWatchAction);
  638. // 输出端口,和输入端口引用计数为 0 的时候,允许添加 Port
  639. if (m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT
  640. && m_infInfo->nRefCount == 0
  641. )
  642. {
  643. contextMenu->addAction(addPortAction);
  644. }
  645. contextMenu->addSeparator();
  646. // 如果引用计数大于 0 ,该端口不添加删除菜单
  647. if (m_infInfo->nRefCount == 0)
  648. {
  649. contextMenu->addAction(disableAction);
  650. }
  651. contextMenu->exec(QCursor::pos());
  652. }
  653. /// <summary>
  654. /// 菜单 - Link
  655. /// </summary>
  656. /// <param name="action"></param>
  657. void WindowAppItemInterface::onMenuLink(QAction* action)
  658. {
  659. QString strInfName = action->text();
  660. // 从Pou中找到相应的接口
  661. WindowAppItemInterface* linktoInfItem = m_pPou->GetInterfaceItemByName(strInfName);
  662. // 此处一定不为nullptr
  663. if (linktoInfItem != nullptr)
  664. {
  665. // 根据输入输出调整link方向
  666. if (this->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  667. {
  668. // 建立Link
  669. ((WindowAppPouScene*)scene())->addLink(this, linktoInfItem);
  670. }
  671. else
  672. {
  673. // 建立Link
  674. ((WindowAppPouScene*)scene())->addLink(linktoInfItem, this);
  675. }
  676. }
  677. }
  678. /// <summary>
  679. /// 菜单 - Unlink
  680. /// 无需检查引用计数,而且一个接口如果有多个link连线,一次性全部删除
  681. /// </summary>
  682. void WindowAppItemInterface::onMenuUnlink()
  683. {
  684. // 直接调用scene来清除连线相关信息
  685. ((WindowAppPouScene*)scene())->delLink(this);
  686. }
  687. /// <summary>
  688. /// 菜单 - Smartlink (只有输入端口可以执行,找离本接口最近的同类型输出端口,序号小于自己的)
  689. /// </summary>
  690. void WindowAppItemInterface::onMenuSmartlink()
  691. {
  692. // 如果本身已经处于link状态,则不处理
  693. if (this->m_infInfo->pUpLinkInterface != nullptr)
  694. {
  695. Utility::VPCriticalMessageBox("This interface is alreaday linked!");
  696. return;
  697. }
  698. // 调用scene()执行自动连接
  699. ((WindowAppPouScene*)scene())->smartLink(this);
  700. }
  701. /// <summary>
  702. /// 菜单 - Add Watch
  703. /// </summary>
  704. void WindowAppItemInterface::onMenuAddWatch()
  705. {
  706. }
  707. /// <summary>
  708. /// 菜单 - Add Port
  709. /// </summary>
  710. void WindowAppItemInterface::onMenuAddPort()
  711. {
  712. if (!m_bShowOnly)
  713. {
  714. // 由于Interface本身的图形已经和所属的Block整合到了一起,所以通过Interface的scenePos()获取的是其父block的Pos
  715. // 此处需要帮助外部函数提供一个本Interface的真实Pos
  716. QLineF realLine = QLineF(
  717. this->mapToScene(this->line().p1()),
  718. this->mapToScene(this->line().p2())
  719. );
  720. ((WindowAppPouScene*)scene())->addPortAndAutolink(this, realLine);
  721. }
  722. }
  723. /// <summary>
  724. /// 菜单 - Disable
  725. /// </summary>
  726. void WindowAppItemInterface::onMenuDisable()
  727. {
  728. // 首先检查引用计数
  729. int nRefCount = this->m_infInfo->nRefCount;
  730. if (nRefCount > 0)
  731. {
  732. Utility::VPCriticalMessageBox("Can't disable this interface for reference count is " + QString::number(nRefCount));
  733. return;
  734. }
  735. // 禁用此接口
  736. bool bRet = m_pPou->setInterfaceEnable(this->m_infInfo, false);
  737. if (!bRet)
  738. {
  739. Utility::VPCriticalMessageBox(
  740. "Can't disable this interface for reference count is " + QString::number(this->m_infInfo->nRefCount));
  741. return;
  742. }
  743. // 重新更新父接口
  744. // 由于只有Standard类型的会有此操作,所以直接通知Standard类型的父功能块重绘
  745. ((WindowAppBlockStandard*)parentItem())->updatePosition();
  746. // 重绘
  747. ((WindowAppPouScene*)scene())->update();
  748. }
  749. //void WindowAppItemInterface::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent)
  750. //{
  751. // // 切换scene的操作模式
  752. // (qobject_cast<WindowAppDiagramScene*>(scene()))->modeScene = MODE::LINK_MODE;
  753. //
  754. // qDebug() << "WindowAppItemInterface::mousePressEvent";
  755. //
  756. // QGraphicsLineItem::mousePressEvent(mouseEvent);
  757. //}
  758. // 悬停事件处理函数,外观效果
  759. void WindowAppItemInterface::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
  760. {
  761. // qDebug("WindowAppItemInterface::hoverEnterEvent");
  762. if (m_infInfo &&
  763. ( m_infInfo->Type == INF_TYPE::INF_TYPE_STANDARD ||
  764. m_infInfo->Type == INF_TYPE::INF_TYPE_VALUE )
  765. )
  766. {
  767. // 2022-9-28,如果接口被废弃了,就不要再显示接口的值了,否则会崩溃
  768. if (m_infInfo->Discard != INF_DISCARD::INF_DEFAULT)
  769. {
  770. return;
  771. }
  772. QString strValue = m_infInfo->getValueString();
  773. QString strType = m_infInfo->strNameWithType;
  774. setToolTip(strType + "\r\nValue: " + strValue);
  775. }
  776. else
  777. {
  778. // 可以设置鼠标效果以及鼠标提示
  779. if (m_infInfo != nullptr)
  780. {
  781. QString strName = m_infInfo->strFullName;
  782. QString strType = m_infInfo->strNameWithType;
  783. setToolTip(strType + "\r\n" + strName);
  784. }
  785. }
  786. // if ((qobject_cast<WindowAppPouScene*>(scene()))->modeScene == MODE::LINK_MODE)
  787. // {
  788. // this->setSelected(true);
  789. // }
  790. QGraphicsLineItem::hoverEnterEvent(event);
  791. }
  792. //void WindowAppItemInterface::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
  793. //{
  794. // qDebug("WindowAppItemInterface::hoverMoveEvent");
  795. //}
  796. //
  797. //void WindowAppItemInterface::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
  798. //{
  799. // qDebug("WindowAppItemInterface::hoverLeaveEvent");
  800. //
  801. // // setCursor(Qt::ArrowCursor);
  802. //
  803. // QGraphicsLineItem::hoverLeaveEvent(event);
  804. //}
  805. //void WindowAppItemInterface::mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent)
  806. //{
  807. // qDebug() << "WindowAppItemInterface::mouseReleaseEvent...";
  808. //
  809. // qDebug("%X", this);
  810. //
  811. // QGraphicsLineItem::mouseReleaseEvent(mouseEvent);
  812. //}
  813. //
  814. //bool WindowAppItemInterface::event(QEvent* e) {
  815. // if (e->type() == QEvent::ToolTip)
  816. // {
  817. // qDebug() << "tool tip show";
  818. // }
  819. // else if (e->type() == QEvent::Leave)
  820. // {
  821. // qDebug() << "tool tip leave";
  822. // }
  823. // return QGraphicsItem::event(e);
  824. //}