WindowAppBlockBase.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. #include "WindowAppBlockBase.h"
  2. #include "WindowAppItemInterface.h"
  3. #include "VPCommand.h"
  4. #include "Pou.h"
  5. #include "WindowAppPouScene.h"
  6. // extern WindowAppPouManager pouManager;
  7. WindowAppBlockBase::WindowAppBlockBase(
  8. TOOL* pTool,
  9. POU* Pou,
  10. bool bShowOnly,
  11. QGraphicsObject* parent
  12. ) :
  13. QGraphicsObject(parent)
  14. , m_toolInfo(pTool)
  15. , m_pPou(Pou)
  16. , m_bShowOnly(bShowOnly)
  17. , contextMenu(nullptr)
  18. , m_oldPos(0, 0)
  19. {
  20. // 创建右键菜单
  21. createContextMenu();
  22. }
  23. /// <summary>
  24. /// 绘制图形
  25. /// </summary>
  26. /// <param name="painter"></param>
  27. /// <param name="option"></param>
  28. /// <param name="widget"></param>
  29. void WindowAppBlockBase::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
  30. {
  31. Q_UNUSED(painter);
  32. Q_UNUSED(widget);
  33. Q_UNUSED(option);
  34. }
  35. /// <summary>
  36. /// 返回矩形区域
  37. /// </summary>
  38. /// <returns></returns>
  39. QRectF WindowAppBlockBase::boundingRect() const
  40. {
  41. return blockBoundingRect;
  42. }
  43. //////////////////////////////////////////////////////////
  44. // 绘制功能块
  45. void WindowAppBlockBase::DrawBlock(QPainter* painter)
  46. {
  47. // 边框
  48. if (!this->isSelected())
  49. {
  50. painter->setPen(QPen(COLOR_TBD_FRAME1, PEN_LINE_WIDTH));
  51. }
  52. else
  53. {
  54. painter->setPen(QPen(COLOR_TBD_FRAME1, PEN_LINE_WIDTH_SEL));
  55. }
  56. // 填充
  57. painter->setBrush(COLOR_TBD_BG);
  58. // 绘制主体
  59. painter->drawRect(blockRect);
  60. }
  61. ///////////////////////////////////////////////////
  62. // 绘制功能块阴影
  63. void WindowAppBlockBase::DrawBlockShadow(QPainter* painter)
  64. {
  65. // 三重阴影的颜色
  66. QColor colorShadow[TBD_SHADOW_COUNT];
  67. colorShadow[0] = COLOR_TBD_SHADOW1;
  68. colorShadow[1] = COLOR_TBD_SHADOW2;
  69. colorShadow[2] = COLOR_TBD_SHADOW3;
  70. for (int i = 1; i <= TBD_SHADOW_COUNT; i++)
  71. {
  72. // 偏移
  73. int offset = i * PEN_LINE_WIDTH;
  74. painter->setPen(QPen(colorShadow[i - 1], PEN_LINE_WIDTH));
  75. painter->drawLine(
  76. QPoint(
  77. blockRect.right() + offset,
  78. blockRect.y() + offset
  79. ),
  80. QPoint(
  81. blockRect.right() + offset,
  82. blockRect.bottom() + offset
  83. )
  84. );
  85. painter->drawLine(
  86. QPoint(
  87. blockRect.x() + offset,
  88. blockRect.bottom() + offset
  89. ),
  90. QPoint(
  91. blockRect.right() + offset,
  92. blockRect.bottom() + offset
  93. )
  94. );
  95. }
  96. // 三重阴影的顶点颜色
  97. QColor colorCorner[TBD_SHADOW_COUNT];
  98. colorCorner[0] = COLOR_TBD_SHADOW_CORNER1;
  99. colorCorner[1] = COLOR_TBD_SHADOW_CORNER2;
  100. colorCorner[2] = COLOR_TBD_SHADOW_CORNER3;
  101. for (int i = 1; i <= TBD_SHADOW_COUNT; i++)
  102. {
  103. // 偏移
  104. int offset = i * PEN_LINE_WIDTH;
  105. painter->setPen(QPen(colorCorner[i - 1], PEN_LINE_WIDTH));
  106. painter->drawPoint(
  107. QPoint(
  108. blockRect.right() + offset,
  109. blockRect.bottom() + offset
  110. )
  111. );
  112. }
  113. }
  114. /// <summary>
  115. /// 更新ItemInterface的位置
  116. /// </summary>
  117. /// <param name="blockRect"></param>
  118. void WindowAppBlockBase::updateInterfacesPostion()
  119. {
  120. // (注意此处坐标系需要转换一下)
  121. QPointF pt = mapToScene(0, 0);
  122. QRectF rcBlock = blockRect;
  123. rcBlock.translate(pt);
  124. // 根据矩形区域计算对应的Interface的位置
  125. int outCount = 0, inCount = 0;
  126. for (WindowAppItemInterface* pItemInf : qAsConst(m_itemInterfaces))
  127. {
  128. if (!m_bShowOnly && !pItemInf->m_infInfo->bEnable)
  129. {
  130. continue;
  131. }
  132. // 2022-5-23, 跳过ToolStart接口
  133. if (pItemInf->m_infInfo->isToolStart()
  134. || pItemInf->m_infInfo->isToolEnd())
  135. {
  136. continue;
  137. }
  138. // 为接口更新最新的父功能块区域尺寸
  139. pItemInf->updateParentRect(rcBlock);
  140. // 输出接口
  141. if (pItemInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  142. {
  143. pItemInf->updatePostion(outCount);
  144. outCount++;
  145. }
  146. else if (pItemInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_IN)
  147. {
  148. pItemInf->updatePostion(inCount);
  149. inCount++;
  150. }
  151. }
  152. }
  153. ///// <summary>
  154. ///// 更新单个Interface的位置
  155. ///// </summary>
  156. ///// <param name="pInfItem"></param>
  157. //void WindowAppBlockBase::updateInterfacePostion(WindowAppItemInterface* pInfItem)
  158. //{
  159. //
  160. //}
  161. /// <summary>
  162. /// 重绘所有的Interface
  163. /// </summary>
  164. void WindowAppBlockBase::redrawAllInterface()
  165. {
  166. for (WindowAppItemInterface* pItemInf : qAsConst(m_itemInterfaces))
  167. {
  168. pItemInf->update();
  169. }
  170. }
  171. /// <summary>
  172. /// 显示右键菜单
  173. /// </summary>
  174. /// <param name="event"></param>
  175. void WindowAppBlockBase::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
  176. {
  177. if (!isSelected())
  178. {
  179. scene()->clearSelection();
  180. setSelected(true);
  181. }
  182. // 如果不是仅供展示用时,才弹出右键菜单
  183. if (!m_bShowOnly)
  184. {
  185. if (contextMenu != nullptr)
  186. {
  187. contextMenu->exec(event->screenPos());
  188. }
  189. }
  190. }
  191. /// <summary>
  192. /// 功能块有变动时
  193. /// </summary>
  194. /// <param name="change"></param>
  195. /// <param name="value"></param>
  196. /// <returns></returns>
  197. QVariant WindowAppBlockBase::itemChange(GraphicsItemChange change, const QVariant& value)
  198. {
  199. if (change == QGraphicsItem::ItemPositionChange && scene())
  200. {
  201. // 更新Interface的位置(此处Qt自动处理了,不需要额外处理)
  202. // WindowAppBlockBase::updateAllInterfacesPostion(blockRect);
  203. // qDebug() << "WindowAppBlockTool::itemChange";
  204. //// 功能块移动时,通知对应的Interface以及Interface的link同步移动
  205. //emit blockMoveSignal(this);
  206. }
  207. // 移动完毕之后,执行位置检测
  208. else if (change == QGraphicsItem::ItemPositionHasChanged && scene())
  209. {
  210. // 功能块移动完毕后,通知对应的Interface以及Interface的link同步移动
  211. emit blockMoveSignal(this);
  212. // 位置检测
  213. WindowAppBlockBase::checkPosition(value);
  214. }
  215. else if (change == QGraphicsItem::ItemSelectedChange)
  216. {
  217. // 如果选中状态变更的话,就重绘所有的Interface
  218. WindowAppBlockBase::redrawAllInterface();
  219. }
  220. return QGraphicsItem::itemChange(change, value);
  221. }
  222. /// <summary>
  223. /// 初始化功能块的右键菜单Action
  224. /// </summary>
  225. void WindowAppBlockBase::createContextMenu()
  226. {
  227. // 硬件组态不显示右键菜单
  228. if (m_pPou->pouName() != GROUP_NAME_HARDWARE)
  229. {
  230. executeAction = new QAction(QIcon(":/image/Execute.png"), ("&Execute"), this);
  231. executeAction->setShortcut(QStringLiteral("Ctrl+F5"));
  232. executeAction->setStatusTip(("Execute this tool"));
  233. executeSubAction = new QAction(("&Execute Subsequent"), this);
  234. executeSubAction->setStatusTip(("Execute from this tool"));
  235. executeAllAction = new QAction(("&Execute All"), this);
  236. executeAllAction->setStatusTip(("Execute all tools"));
  237. executeBreakPoint = new QAction(("&BreakPoint"), this);
  238. executeBreakPoint->setStatusTip(("BreakPoint"));
  239. moveUpAction = new QAction(QIcon(":/image/MoveUp.png"), ("Move Up"), this);
  240. moveUpAction->setStatusTip(("Move up tool from diagram"));
  241. moveDownAction = new QAction(QIcon(":/image/MoveDown.png"), ("Move Down"), this);
  242. moveDownAction->setStatusTip(("Move down tool from diagram"));
  243. moveFirstAction = new QAction(QIcon(":/image/MoveFirst.png"), ("Move First"), this);
  244. moveFirstAction->setStatusTip(("Move first tool from diagram"));
  245. moveLastAction = new QAction(QIcon(":/image/MoveLast.png"), ("MoveLast"), this);
  246. moveLastAction->setStatusTip(("Move last tool from diagram"));
  247. }
  248. deleteAction = new QAction(QIcon(":/image/Delete.png"), ("&Delete"), this);
  249. deleteAction->setStatusTip(("Delete item from diagram"));
  250. propertyAction = new QAction(("&Property"), this);
  251. propertyAction->setStatusTip(("Property of this item"));
  252. copyBlockAction = new QAction(("Copy block"), this);
  253. copyDataAction = new QAction(("Copy data"), this);
  254. pasteAction = new QAction(("Paste"), this);
  255. }
  256. /// <summary>
  257. /// 返回本功能块的引用计数(所有接口引用计数之和)
  258. /// </summary>
  259. /// <returns></returns>
  260. int WindowAppBlockBase::getRefCount()
  261. {
  262. int nRefCount = 0;
  263. for (WindowAppItemInterface* pItemInf : qAsConst(m_itemInterfaces))
  264. {
  265. nRefCount += pItemInf->m_infInfo->nRefCount;
  266. }
  267. return nRefCount;
  268. }
  269. /// <summary>
  270. /// // 是否可以被删除(引用计数=0,且没有被Task使用)
  271. /// </summary>
  272. /// <returns></returns>
  273. bool WindowAppBlockBase::couldBeDeleted()
  274. {
  275. // 首先需要查询一下引用计数
  276. int nRefCount = this->getRefCount();
  277. if (nRefCount > 0)
  278. {
  279. Utility::VPCriticalMessageBox("Can't delete this block for reference count is " + QString::number(nRefCount));
  280. return false;
  281. }
  282. // 2021-8-12增加,还需要检查本Pou是否被Task选用,如果选用则不允许删除
  283. if (this->m_pPou->isRunningInTask())
  284. {
  285. Utility::VPCriticalMessageBox("Can't delete this block : this block is running in task[" + m_pPou->m_pParentTask->strName + "]");
  286. return false;
  287. }
  288. return true;
  289. }
  290. /// <summary>
  291. /// 2021-8-17 检查功能块位置(碰撞检测,限制移动范围)
  292. /// </summary>
  293. /// <param name="value"></param>
  294. /// <returns></returns>
  295. QVariant WindowAppBlockBase::checkPosition(const QVariant& value)
  296. {
  297. // 最新中心点位置
  298. QPointF curPos = value.toPointF();
  299. // 碰撞检测
  300. bool bFix = checkCollision();
  301. bFix = false; //暂时屏蔽碰撞
  302. if (bFix)
  303. {
  304. // 恢复上次的位置
  305. this->setPos(m_fixPos);
  306. return value;
  307. }
  308. // 碰撞检测如果没有变更功能块位置,然后继续做边界检测
  309. bFix = restrictBlockPos(curPos);
  310. bFix = false; // 暂时屏蔽边界检测
  311. if (bFix)
  312. {
  313. // 恢复上次的位置
  314. this->setPos(m_fixPos);
  315. }
  316. else
  317. {
  318. // 如果不需要调整,则保存最新的位置
  319. m_fixPos = value.toPointF();
  320. }
  321. return value;
  322. }
  323. /// <summary>
  324. /// 2021-8-17 处理碰撞判断
  325. /// </summary>
  326. /// <param name="value"></param>
  327. /// <returns>返回是否做出了变更</returns>
  328. bool WindowAppBlockBase::checkCollision()
  329. {
  330. // 整个场景区域
  331. QRectF rcScene = scene()->sceneRect();
  332. // 获取界面中和本功能块产生碰撞的物体
  333. QList<QGraphicsItem*> listCollision = scene()->collidingItems(this);
  334. // 需要排除自身的interface,因为Interface和功能块本身存在碰撞
  335. for (QGraphicsItem* pItem : listCollision)
  336. {
  337. if (pItem->type() != ITEM_TYPE_STANDARD
  338. && pItem->type() != ITEM_TYPE_PORT)
  339. {
  340. listCollision.removeOne(pItem);
  341. }
  342. }
  343. // 判断是否还有碰撞
  344. if (!listCollision.isEmpty())
  345. {
  346. return true;
  347. }
  348. return false;
  349. }
  350. /// <summary>
  351. /// 2021-8-17 限制功能块在场景内
  352. /// </summary>
  353. /// <param name="value"></param>
  354. /// <returns>返回是否做出了变更</returns>
  355. bool WindowAppBlockBase::restrictBlockPos(QPointF curPos)
  356. {
  357. // 整个场景区域
  358. QRectF rcScene = scene()->sceneRect();
  359. // 重新计算最新的功能块矩形区域(含Interface)
  360. QRectF blockTotalRect;
  361. if (this->type() == ITEM_TYPE_STANDARD)
  362. {
  363. blockTotalRect.setRect(
  364. (double)curPos.x() - (double)(blockRect.width() / 2.0) - (double)TBD_INF_LINE - 8.0,
  365. (double)curPos.y() - (double)(blockRect.height() / 2.0) - (double)TBD_INDEX_HEIGHT,
  366. (double)blockRect.width() + (double)(TBD_INF_LINE * 3.0) + 5.0,
  367. (double)blockRect.height() + (double)TBD_INDEX_HEIGHT + (double)(TBD_SHADOW_COUNT * PEN_LINE_WIDTH) + 15.0
  368. );
  369. }
  370. else if (this->type() == ITEM_TYPE_PORT)
  371. {
  372. blockTotalRect.setRect(
  373. (double)curPos.x() - (double)(blockRect.width() / 2.0) - TBD_INF_LINE - 8,
  374. (double)curPos.y() - (double)blockRect.height() - 5.0,
  375. (double)blockRect.width() + (double)(TBD_INF_LINE * 3.0),
  376. (double)blockRect.height() + (double)TBD_INDEX_HEIGHT
  377. );
  378. }
  379. else
  380. {
  381. return false;
  382. }
  383. // 判断场景是否包含此矩形区域
  384. if (rcScene.contains(blockTotalRect))
  385. {
  386. //// 如果不需要调整,则保存最新的位置
  387. //m_fixPos = value.toPointF();
  388. // qDebug() << blockTotalRect << " pass last:" << m_fixPos;;
  389. return false;
  390. }
  391. else
  392. {
  393. // qDebug() << blockTotalRect << " restrict resotre to " << m_fixPos;
  394. //if (m_fixPos == QPointF(0.0, 0.0))
  395. //{
  396. // return false;
  397. //}
  398. //// 恢复上次的位置
  399. //this->setPos(m_fixPos);
  400. return true;
  401. }
  402. }
  403. /// <summary>
  404. /// 2022-10-4,删除当前功能块(统一功能块的删除入口)
  405. /// </summary>
  406. void WindowAppBlockBase::onDeleteItem()
  407. {
  408. //// 2022-10-2,为了整合进Undo架构,改为由Scene统一删除
  409. //m_pPou->parentScene()->delToolItem(this);
  410. // 生成删除指令,调用Undo体系进行删除
  411. PouToolDelCommand* toolDelCommand = new PouToolDelCommand(
  412. (WindowAppPouScene*)this->scene(),
  413. this->m_toolInfo,
  414. this->scenePos()
  415. );
  416. ((WindowAppPouScene*)scene())->m_CommandManager.executeCommand(toolDelCommand);
  417. }
  418. /// <summary>
  419. /// 鼠标左键按下
  420. /// </summary>
  421. /// <param name="event"></param>
  422. void WindowAppBlockBase::mousePressEvent(QGraphicsSceneMouseEvent* event)
  423. {
  424. //设置鼠标形状
  425. // TODO: 2022-4-22为了方便功能块调试,临时屏蔽
  426. // setCursor(Qt::ClosedHandCursor);
  427. // 图元多选 系统默认Ctrl + 鼠标左键可实现多选
  428. if (event->button() == Qt::LeftButton)
  429. {
  430. // 2022-10-2,按下时记录功能块当前位置,用于比较鼠标抬起后是否移动
  431. m_oldPos = event->scenePos();
  432. //设置shift+鼠标左键单选
  433. if (event->modifiers() == Qt::ShiftModifier)
  434. {
  435. setSelected(true);
  436. }
  437. else
  438. {
  439. QGraphicsItem::mousePressEvent(event);
  440. }
  441. }
  442. }
  443. /// <summary>
  444. /// 鼠标右键抬起
  445. /// </summary>
  446. /// <param name="event"></param>
  447. void WindowAppBlockBase::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
  448. {
  449. if (event->button() == Qt::LeftButton)
  450. {
  451. // 2022-10-2,抬起时记录功能块当前位置,用于比较鼠标抬起后是否移动
  452. QPointF curPos = event->scenePos();
  453. // 如果移动了,则记录这次动作,用于执行Redo
  454. if (curPos != m_oldPos)
  455. {
  456. PouToolMoveCommand* toolMoveCommand = new PouToolMoveCommand(
  457. this->m_pPou,
  458. this->m_toolInfo->strInstanceName,
  459. m_oldPos,
  460. curPos
  461. );
  462. ((WindowAppPouScene*)scene())->m_CommandManager.executeCommand(toolMoveCommand, true);
  463. m_oldPos = curPos;
  464. }
  465. }
  466. QGraphicsItem::mouseReleaseEvent(event);
  467. }
  468. /// <summary>
  469. /// 2022-6-22 查找某个InferfaceItem是否在本Block内
  470. /// </summary>
  471. /// <param name="infItem"></param>
  472. /// <returns></returns>
  473. bool WindowAppBlockBase::isInBlock(WindowAppItemInterface* infItem)
  474. {
  475. int nIndex = m_itemInterfaces.indexOf(infItem);
  476. if (nIndex >= 0)
  477. {
  478. return true;
  479. }
  480. else
  481. {
  482. return false;
  483. }
  484. }