WindowAppPouScene.cpp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035
  1. #include "WindowAppPouScene.h"
  2. #include "WindowAppPouFrame.h"
  3. #include "ToolDepository.h"
  4. #include "WindowAppBlockPort.h"
  5. #include "WindowAppBlockStandard.h"
  6. #include "WindowAppBlockGoto.h"
  7. #include "WindowAppItemLink.h"
  8. #include "PouManager.h"
  9. #include "WindowAppItemInterface.h"
  10. #include "WindowAppBlockComment.h"
  11. #include "WindowAppBlockParallel.h"
  12. #include "WindowAppBlockForloop.h"
  13. #include "WindowAppBlockWait.h"
  14. #include "WindowAppTaskView.h"
  15. #include "TaskManager.h"
  16. extern ToolDepository toolDepository;
  17. WindowAppPouScene::WindowAppPouScene(
  18. const QString& strTitle,
  19. QObject* parent,
  20. QWidget* parentparent
  21. )
  22. : QGraphicsScene(parent)
  23. // 2022-1-8 保存PouFrame的指针
  24. , m_pPouFrame((WindowAppPouFrame*)parentparent)
  25. {
  26. m_sceneMode = SCENE_MODE::NORMAL_MODE;
  27. m_tmpLinkLine = nullptr;
  28. m_strPouName = strTitle;
  29. // 完成Pou的初始化动作
  30. m_Pou.init(strTitle, this);
  31. // 如果本Group是硬件Group的话,则绑定硬件的Pou
  32. if (strTitle == GROUP_NAME_HARDWARE)
  33. {
  34. g_pPouManager->registerHdwPou(&m_Pou);
  35. }
  36. //else
  37. //{
  38. // 在全局信息数据中注册本Pou
  39. g_pPouManager->registerPou(&m_Pou);
  40. // }
  41. //// 保存当前的Pou数据结构信息
  42. //Pou.insert(m_strTitle, &m_Pou);
  43. // 初始化步长值为 1
  44. m_nMoveBlockSleep = 1;
  45. }
  46. /// <summary>
  47. /// 生成新的运行时工具信息,并添加到界面中
  48. /// </summary>
  49. /// <param name="pNewTool"></param>
  50. /// <param name="pos"></param>
  51. WindowAppBlockBase* WindowAppPouScene::addToolItem(const STATIC_TOOL* pNewTool, QPointF pos)
  52. {
  53. //// 2022-3-13 增加,如果是注释工具的话,则专门进行注释工具的添加流程
  54. //if (pNewTool->Type == TOOL_TYPE::TOOL_TYPE_COMMENT)
  55. //{
  56. // this->addNewCommentItem(pNewTool, pos);
  57. // return;
  58. //}
  59. WindowAppBlockBase* pNewBlock = nullptr;
  60. // 如果不是注释工具的话,则需要执行如下判断
  61. if (pNewTool->Type != TOOL_TYPE::TOOL_TYPE_COMMENT)
  62. {
  63. // 2021-8-14 增加,如果本Pou正在Task的执行过程中,则不允许添加
  64. if (m_Pou.isRunningInTask())
  65. {
  66. Utility::VPCriticalMessageBox(
  67. "Can not add Tool[" + pNewTool->strName + "], Reason: this pou is running in Task[" + m_Pou.m_pParentTask->strName + "].");
  68. return nullptr;
  69. }
  70. // 2021-8-19 增加,如果本页面是硬件页面,那么只允许硬件的工具拖进来
  71. if (this->m_strPouName == GROUP_NAME_HARDWARE
  72. && pNewTool->strCategory != CATEGORY_TOOL_HARDWARE)
  73. {
  74. Utility::VPCriticalMessageBox("Only hardware tools could be added in this page!");
  75. return nullptr;
  76. }
  77. }
  78. // 检查通过后开始建立工具
  79. TOOL* toolRunning = new TOOL(pNewTool);
  80. // 2021-7-4增加,保存组名
  81. toolRunning->strPouName = m_strPouName;
  82. // 生成新的运行时工具信息
  83. // 标准工具
  84. if (pNewTool->isStandardTool())
  85. {
  86. pNewBlock = this->addStandardItem(toolRunning, pos);
  87. }
  88. // Port工具
  89. else if (pNewTool->isPortTool())
  90. {
  91. pNewBlock = this->addPortItem(toolRunning, pos);
  92. }
  93. // Goto工具
  94. else if (pNewTool->isGotoTool())
  95. {
  96. pNewBlock = this->addGotoItem(toolRunning, pos);
  97. }
  98. // Comment工具
  99. else if (pNewTool->isCommentTool())
  100. {
  101. pNewBlock = this->addCommentItem(toolRunning, pos);
  102. }
  103. // Parallel工具
  104. else if (pNewTool->isParallelTool())
  105. {
  106. pNewBlock = this->addParallelItem(toolRunning, pos);
  107. }
  108. // ForLoop工具
  109. else if (pNewTool->isForloopTool())
  110. {
  111. pNewBlock = this->addForloopItem(toolRunning, pos);
  112. }
  113. // Wait工具
  114. else if (pNewTool->isWaitTool())
  115. {
  116. pNewBlock = this->addWaitItem(toolRunning, pos);
  117. }
  118. else
  119. {
  120. // Error Here
  121. qDebug() << "[ERROR] WindowAppPouScene::addNewItem - Unknown tool type :" << (int)pNewTool->Type;
  122. return nullptr;
  123. }
  124. vDebug() << "Add Tool[" << toolRunning->strInstanceName << "] in Pou[" << m_strPouName << "].";
  125. return pNewBlock;
  126. }
  127. /// <summary>
  128. /// 生成新的运行时工具信息(反序列化方式)
  129. /// </summary>
  130. /// <param name="pNewTool"></param>
  131. /// <param name="pos"></param>
  132. /// <param name="bFromDoc">此参数的含义是是否通过反序列化添加,如果是反序列化进来的,需要跳过一些变量设置</param>
  133. WindowAppBlockBase* WindowAppPouScene::addToolItem(TOOL* pNewTool, QPointF pos, bool bFromDoc)
  134. {
  135. //// 2022-3-13 增加,如果是注释工具的话,则专门进行注释工具的添加流程
  136. //if (pNewTool->Type == TOOL_TYPE::TOOL_TYPE_COMMENT)
  137. //{
  138. // this->addNewCommentItem(pos);
  139. // return;
  140. //}
  141. // 标准工具信息
  142. if (pNewTool->isStandardTool())
  143. {
  144. return this->addStandardItem(pNewTool, pos, bFromDoc);
  145. }
  146. // Port工具
  147. else if (pNewTool->isPortTool())
  148. {
  149. return this->addPortItem(pNewTool, pos, bFromDoc);
  150. }
  151. // Goto工具
  152. else if (pNewTool->isGotoTool())
  153. {
  154. return this->addGotoItem(pNewTool, pos, bFromDoc);
  155. }
  156. // 注释工具
  157. else if (pNewTool->isCommentTool())
  158. {
  159. return this->addCommentItem(pNewTool, pos, bFromDoc);
  160. }
  161. // Parallel工具
  162. else if (pNewTool->isParallelTool())
  163. {
  164. return this->addParallelItem(pNewTool, pos, bFromDoc);
  165. }
  166. // ForLoop工具
  167. else if (pNewTool->isForloopTool())
  168. {
  169. return this->addForloopItem(pNewTool, pos, bFromDoc);
  170. }
  171. // Wait工具
  172. else if (pNewTool->isWaitTool())
  173. {
  174. return this->addWaitItem(pNewTool, pos, bFromDoc);
  175. }
  176. else
  177. {
  178. // Error Here
  179. qDebug() << "[ERROR] WindowAppPouScene::addNewItem - Unknown tool type :" << (int)pNewTool->Type;
  180. return nullptr;
  181. }
  182. }
  183. /// <summary>
  184. /// 移除一个ToolItem
  185. /// </summary>
  186. /// <param name="pBlock"></param>
  187. void WindowAppPouScene::delToolItem(WindowAppBlockBase* pBlock)
  188. {
  189. // 确保指针有效
  190. if (pBlock == nullptr)
  191. {
  192. vDebug() << "[Error] BlockBase is nullptr.";
  193. return;
  194. }
  195. // 检查是否可以被删除
  196. if (!pBlock->couldBeDeleted())
  197. {
  198. return;
  199. }
  200. // 2022-10-2,这里只处理去Task和Pou处处理标准工具,跳过其他工具
  201. // NOTICE:之所以这里会出现这个问题,是因为之前只有StandardBase工具会走这里删除,
  202. // MEMORY
  203. if (pBlock->m_toolInfo->isIndexedTool())
  204. {
  205. // 2022-3-5增加,如果此工具的Pou被Task选中,需要通知Task删除此工具
  206. if (m_Pou.isSelByTask())
  207. {
  208. WindowAppTaskView* pTaskView = g_pTaskManager->getTaskViewByName(m_Pou.m_pParentTask->strName);
  209. pTaskView->onDelPouTool(&m_Pou, pBlock->m_toolInfo);
  210. }
  211. }
  212. // 2022-10-2,除了Comment工具,其他的工具都需要到Pou中删除本工具的数据信息
  213. // 逻辑数据删除
  214. m_Pou.ToolDelete(pBlock);
  215. // 从界面中移除此功能块
  216. this->removeItem(pBlock);
  217. }
  218. /// <summary>
  219. /// 添加一个标准工具
  220. /// </summary>
  221. /// <param name="pNewTool"></param>
  222. /// <param name="pos"></param>
  223. WindowAppBlockStandard* WindowAppPouScene::addStandardItem(TOOL* pNewTool, QPointF pos, bool bFromDoc)
  224. {
  225. // 2022-3-31 如果从序列化中恢复,则不需要重新设置索引和实例名称了
  226. if (!bFromDoc)
  227. {
  228. // 初始化本功能块
  229. this->initBlockItem(pNewTool, true, true, true);
  230. // TODO:2022-9-19,如果此处工具从序列化中恢复,要检查工具Event类型的接口是否正常初始化了?一般都会绑定到Task中
  231. }
  232. // 加载dll,取得指针
  233. pNewTool->pDllPtr = toolDepository.GetToolPtr(pNewTool->strName);
  234. if (pNewTool->pDllPtr == nullptr)
  235. {
  236. vDebug() << "[Error] [" << pNewTool->strName << "], but pDllPtr is nullptr ";
  237. return nullptr;
  238. }
  239. // 2022-10-4,保存一下本Dll的全路径信息,供后续查询需要
  240. pNewTool->pDllPtr->m_strFullPath = toolDepository.GetDllPathByName(pNewTool->strName);
  241. try
  242. {
  243. // 工具初始化(带值初始化 withValue )
  244. pNewTool->pDllPtr->InitTool(
  245. (WindowAppPouFrame*)this->parent(),
  246. m_Pou.pouName(),
  247. pNewTool->strInstanceName,
  248. (QObject*)g_pTaskManager
  249. );
  250. //QFuture<int> future = QtConcurrent::run(
  251. // pNewTool->pDllPtr,
  252. // &DllTool::InitTool,
  253. // (WindowAppPouFrame*)this->parent(),
  254. // m_Pou.pouName(),
  255. // pNewTool->strInstanceName,
  256. // (QObject*)g_pTaskManager
  257. // );
  258. //future.waitForFinished();
  259. }
  260. catch (...)
  261. {
  262. qWarning() << "[Error] WindowAppPouScene::addStandardItem - " << m_Pou.pouName()
  263. << " init Tool" << pNewTool->strInstanceName << " failed.";
  264. }
  265. //// 2021-8-5 设置,为dll设置event的传递目标(TaskManager)
  266. //pNewTool->pDllPtr->setEventTarget((QObject*)g_pTaskManager);
  267. // 2022-3-3增加,设置GvlManager指针,用于和DB类型的全局变量做交互
  268. pNewTool->pDllPtr->setGvlTarget((QObject*)g_pGvlManager);
  269. // 2022-3-10 增加,设置WindowAppPouScene指针,用于同步dll侧发来的动态端口同步消息
  270. pNewTool->pDllPtr->setPouTarget(this);
  271. //// 2021-8-3 增加,此处从dll获取刚生成的变量值
  272. //// (仅限于Hardware类型的Event接口,但是其实也可以不保存在exe中,每次都从dll中拿,两种方案都可以)
  273. //for (int i = 0; i < pNewTool->Interfaces.size(); i++)
  274. //{
  275. // pNewTool->Interfaces[i]->value.Ptr = pNewTool->pDllPtr->Interface(i).value.Ptr;
  276. //}
  277. // 根据对应的工具信息绘制对应的功能块
  278. WindowAppBlockStandard* pNewBlock =
  279. qgraphicsitem_cast<WindowAppBlockStandard*>(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_STANDARD));
  280. return pNewBlock;
  281. }
  282. /// <summary>
  283. /// 添加一个Port工具
  284. /// </summary>
  285. /// <param name="pNewTool"></param>
  286. /// <param name="pos"></param>
  287. WindowAppBlockPort* WindowAppPouScene::addPortItem(TOOL* pNewPort, QPointF pos, bool bFromDoc)
  288. {
  289. // 2022-3-31 如果从序列化中恢复,则不需要重新设置实例名称和接口名称了
  290. if (!bFromDoc)
  291. {
  292. //// 需要根据现有工具情况生成新的实例名字
  293. //pNewPort->strInstanceName = m_Pou.genToolInstanceName(pNewPort->strName);
  294. // 初始化本功能块
  295. this->initBlockItem(pNewPort, false, true, false);
  296. // 2022-3-23,需要为接口补充上全名,用于在Pou中建立对应关系
  297. if (pNewPort->Type == TOOL_TYPE::TOOL_TYPE_PORT_INPUT)
  298. {
  299. pNewPort->Interfaces[0]->strFullName = pNewPort->strPouName + "."
  300. + pNewPort->strInstanceName + "."
  301. + DEFAULT_INPUT_PORT_NAME;
  302. }
  303. else if (pNewPort->Type == TOOL_TYPE::TOOL_TYPE_PORT_OUTPUT)
  304. {
  305. pNewPort->Interfaces[0]->strFullName = pNewPort->strPouName + "."
  306. + pNewPort->strInstanceName + "."
  307. + DEFAULT_OUTPUT_PORT_NAME;
  308. }
  309. else
  310. {
  311. qDebug() << "WindowAppPouScene::addPortItem - [Error] Invalid toolType of Port[" << pNewPort->strInstanceName << ".";
  312. }
  313. }
  314. // 根据对应的工具信息绘制对应的功能块
  315. WindowAppBlockPort* pNewBlock =
  316. qgraphicsitem_cast<WindowAppBlockPort*>(this->createBlock(pNewPort, pos, TOOL_TYPE::TOOL_TYPE_PORT_INPUT));
  317. return pNewBlock;
  318. }
  319. /// <summary>
  320. /// 添加一个Goto工具
  321. /// </summary>
  322. /// <param name="pNewPort"></param>
  323. /// <param name="pos"></param>
  324. /// <returns></returns>
  325. WindowAppBlockGoto* WindowAppPouScene::addGotoItem(TOOL* pNewTool, QPointF pos, bool bFromDoc)
  326. {
  327. // 2022-3-31 如果从序列化中恢复,则不需要重新设置索引和实例名称了
  328. if (!bFromDoc)
  329. {
  330. // 初始化本功能块
  331. this->initBlockItem(pNewTool, true, true, false);
  332. //// 计算分配的Index
  333. //pNewTool->nIndex = m_Pou.GetIndexedToolsCount();
  334. //// 需要根据现有工具情况生成新的实例名字
  335. //pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName));
  336. }
  337. // 根据对应的工具信息绘制对应的功能块
  338. WindowAppBlockGoto* pNewBlock =
  339. qgraphicsitem_cast<WindowAppBlockGoto*>(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_GOTO));
  340. return pNewBlock;
  341. }
  342. /// <summary>
  343. /// 添加一个Comment工具
  344. /// </summary>
  345. /// <param name="pNewTool"></param>
  346. /// <param name="pos"></param>
  347. /// <param name="bFromDoc"></param>
  348. /// <returns></returns>
  349. WindowAppBlockComment* WindowAppPouScene::addCommentItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/)
  350. {
  351. // 2022-3-31 如果从序列化中恢复,则不需要重新设置实例名称了
  352. if (!bFromDoc)
  353. {
  354. // 初始化本功能块
  355. this->initBlockItem(pNewTool, false, true, false);
  356. //// 需要根据现有工具情况生成新的实例名字
  357. //pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName));
  358. }
  359. // 根据对应的工具信息绘制对应的功能块
  360. WindowAppBlockComment* pNewBlock =
  361. qgraphicsitem_cast<WindowAppBlockComment*>(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_COMMENT));
  362. return pNewBlock;
  363. }
  364. /// <summary>
  365. /// 添加一个Parallel工具
  366. /// </summary>
  367. /// <param name="pNewTool"></param>
  368. /// <param name="pos"></param>
  369. /// <param name="bFromDoc"></param>
  370. /// <returns></returns>
  371. WindowAppBlockParallel* WindowAppPouScene::addParallelItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/)
  372. {
  373. // 如果从序列化中恢复,则不需要重新设置索引和实例名称了
  374. if (!bFromDoc)
  375. {
  376. // 初始化本功能块
  377. this->initBlockItem(pNewTool, true, true, true);
  378. //// 计算分配的Index
  379. //pNewTool->nIndex = m_Pou.GetIndexedToolsCount();
  380. //// 需要根据现有工具情况生成新的实例名字
  381. //pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName));
  382. //// 2022-4-27,为Parallel的ToolInterface命名
  383. //pNewTool->startInterface->strName = pNewTool->strInstanceName;
  384. //pNewTool->startInterface->strFullName = m_Pou.pouName() + "." + pNewTool->strInstanceName + ".Start";
  385. //pNewTool->endInterface->strName = pNewTool->strInstanceName;
  386. //pNewTool->endInterface->strFullName = m_Pou.pouName() + "." + pNewTool->strInstanceName + ".End";
  387. }
  388. // 根据对应的工具信息绘制对应的功能块
  389. WindowAppBlockParallel* pNewBlock =
  390. qgraphicsitem_cast<WindowAppBlockParallel*>(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_PARALLEL));
  391. return pNewBlock;
  392. }
  393. /// <summary>
  394. /// 添加一个ForLoop工具
  395. /// </summary>
  396. /// <param name="pNewTool"></param>
  397. /// <param name="pos"></param>
  398. /// <param name="bFromDoc"></param>
  399. /// <returns></returns>
  400. WindowAppBlockForloop* WindowAppPouScene::addForloopItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/)
  401. {
  402. // 如果从序列化中恢复,则不需要重新设置索引和实例名称了
  403. if (!bFromDoc)
  404. {
  405. // 初始化本功能块
  406. this->initBlockItem(pNewTool, true, true, true);
  407. }
  408. // 根据对应的工具信息绘制对应的功能块
  409. WindowAppBlockForloop* pNewBlock =
  410. qgraphicsitem_cast<WindowAppBlockForloop*>(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_FORLOOP));
  411. return pNewBlock;
  412. }
  413. /// <summary>
  414. /// 添加一个Wait工具
  415. /// </summary>
  416. /// <param name="pNewTool"></param>
  417. /// <param name="pos"></param>
  418. /// <param name="bFromDoc"></param>
  419. /// <returns></returns>
  420. WindowAppBlockWait* WindowAppPouScene::addWaitItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/)
  421. {
  422. // 如果从序列化中恢复,则不需要重新设置索引和实例名称了
  423. if (!bFromDoc)
  424. {
  425. // 初始化本功能块
  426. this->initBlockItem(pNewTool, true, true, false);
  427. }
  428. // 根据对应的工具信息绘制对应的功能块
  429. WindowAppBlockWait* pNewBlock =
  430. qgraphicsitem_cast<WindowAppBlockWait*>(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_WAIT));
  431. return pNewBlock;
  432. }
  433. /// <summary>
  434. /// 创建功能块
  435. /// </summary>
  436. /// <param name="pBlock"></param>
  437. /// <param name="pTool"></param>
  438. /// <param name="pos"></param>
  439. /// <param name="tooType"></param>
  440. /// <returns></returns>
  441. WindowAppBlockBase* WindowAppPouScene::createBlock(
  442. TOOL* pTool,
  443. const QPointF& pos,
  444. TOOL_TYPE toolType
  445. )
  446. {
  447. WindowAppBlockBase* newItem = nullptr;
  448. // 根据种类创建对应的控件
  449. switch (toolType)
  450. {
  451. case TOOL_TYPE::TOOL_TYPE_STANDARD:
  452. newItem = new WindowAppBlockStandard(pTool, &m_Pou);
  453. break;
  454. case TOOL_TYPE::TOOL_TYPE_PORT_INPUT:
  455. case TOOL_TYPE::TOOL_TYPE_PORT_OUTPUT:
  456. newItem = new WindowAppBlockPort(pTool, &m_Pou);
  457. break;
  458. case TOOL_TYPE::TOOL_TYPE_GOTO:
  459. newItem = new WindowAppBlockGoto(pTool, &m_Pou);
  460. break;
  461. case TOOL_TYPE::TOOL_TYPE_COMMENT:
  462. newItem = new WindowAppBlockComment(pTool, &m_Pou);
  463. break;
  464. case TOOL_TYPE::TOOL_TYPE_PARALLEL:
  465. newItem = new WindowAppBlockParallel(pTool, &m_Pou);
  466. break;
  467. case TOOL_TYPE::TOOL_TYPE_FORLOOP:
  468. newItem = new WindowAppBlockForloop(pTool, &m_Pou);
  469. break;
  470. case TOOL_TYPE::TOOL_TYPE_WAIT:
  471. newItem = new WindowAppBlockWait(pTool, &m_Pou);
  472. break;
  473. default:
  474. {
  475. qDebug() << "[Error] WindowAppPouScene::createBlock - Unknown tooltype: " << (short)toolType;
  476. return nullptr;
  477. }
  478. }
  479. // 根据控件类型的不同绑定不同的消息
  480. if (toolType != TOOL_TYPE::TOOL_TYPE_COMMENT)
  481. {
  482. // 绑定移动信号用于移动功能块时同步移动link连线
  483. connect(
  484. newItem,
  485. &WindowAppBlockBase::blockMoveSignal,
  486. this,
  487. &WindowAppPouScene::onBlockMove
  488. );
  489. }
  490. else
  491. {
  492. // 绑定信号用于注释控件内容为空时,删除控件
  493. connect(
  494. qgraphicsitem_cast<WindowAppBlockComment*>(newItem),
  495. &WindowAppBlockComment::emptyContent,
  496. this,
  497. &WindowAppPouScene::removeEmptyBlockComment
  498. );
  499. }
  500. // 置于最顶层
  501. newItem->setZValue(Z_ORDER_BLOCK);
  502. // Scene中添加功能块
  503. addItem(newItem);
  504. // 设置位置
  505. newItem->setPos(pos);
  506. // 添加功能块接口
  507. newItem->addItemInterfaces();
  508. // 将新的工具信息和功能块信息绑定保存
  509. m_Pou.registerTool(newItem, pTool);
  510. return newItem;
  511. }
  512. /// <summary>
  513. /// 直接增加一个Port,并且连接
  514. /// </summary>
  515. /// <param name="pLinkInf"></param>
  516. void WindowAppPouScene::addPortAndAutolink(WindowAppItemInterface* pLinkInf, const QLineF infRealLine)
  517. {
  518. // 从工具库中获取Port工具原型
  519. const STATIC_TOOL* portTool = nullptr;
  520. // 根据接口的输入输出获取不同的port工具,并且根据方向计算一下Port应该出现的中心点位置
  521. QPointF posPort;
  522. posPort.setY(infRealLine.y1() - PBD_BASIC_HEIGHT / 2);
  523. if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  524. {
  525. portTool = toolDepository.GetInputPort();
  526. posPort.setX(infRealLine.x2() + PBD_BASIC_WIDTH + AUTOPORT_SPACING + TBD_INF_LINE );
  527. }
  528. else
  529. {
  530. portTool = toolDepository.GetOutputPort();
  531. posPort.setX(infRealLine.x1() - PBD_BASIC_WIDTH - AUTOPORT_SPACING - TBD_INF_LINE);
  532. }
  533. // 确保工具有效
  534. if (portTool == nullptr)
  535. {
  536. return;
  537. }
  538. // 确保输入端口没被引用过
  539. if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_IN &&
  540. pLinkInf->m_infInfo->nRefCount > 0 )
  541. {
  542. return;
  543. }
  544. // 确保输出端口没被引用过 (暂时限制输出多次添加数据端口,以后有必要的时候再放开)
  545. if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT &&
  546. pLinkInf->m_infInfo->nRefCount > 0)
  547. {
  548. return;
  549. }
  550. // 添加这个port工具
  551. TOOL* portRunning = new TOOL(portTool);
  552. // 2021-7-5添加,需要保存所属的Group
  553. portRunning->strPouName = m_strPouName;
  554. // 2021-05-26添加,双击添加的Port工具需要根据link的接口指定类型
  555. portRunning->Interfaces[0]->value.type = pLinkInf->m_infInfo->value.type;
  556. WindowAppBlockPort* pNewPortItem = this->addPortItem(portRunning, posPort);
  557. // 并且将两个接口进行连接(输出->输入)
  558. if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT)
  559. {
  560. addLink(pLinkInf, pNewPortItem->m_itemInterfaces[0]);
  561. }
  562. else
  563. {
  564. addLink(pNewPortItem->m_itemInterfaces[0], pLinkInf);
  565. }
  566. }
  567. /// <summary>
  568. /// 鼠标按下时
  569. /// </summary>
  570. /// <param name="mouseEvent"></param>
  571. void WindowAppPouScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent)
  572. {
  573. //qDebug() << "WindowAppDiagramScene::mousePressEvent";
  574. // 如果鼠标按下的位置是接口的话,则切换进入Link模式
  575. QTransform transform;
  576. // 2022-1-8 测试点击的是接口还是功能块
  577. WindowAppItemInterface* activeItem = qgraphicsitem_cast<WindowAppItemInterface*>(this->itemAt(mouseEvent->scenePos(), transform));
  578. WindowAppBlockStandardBase* activeBlock = qgraphicsitem_cast<WindowAppBlockStandardBase*>(this->itemAt(mouseEvent->scenePos(), transform));
  579. // 如果点击的是接口,并且是输出接口的话,那么执行link
  580. if ( activeItem!=nullptr && activeItem->m_infInfo->Direction== INF_DIRECTION::INF_DIR_OUT )
  581. {
  582. // 如果是硬件组态,则不执行Link相关的所有操作(包括批量选中移动后的特殊处理)
  583. if (m_strPouName == GROUP_NAME_HARDWARE)
  584. {
  585. QGraphicsScene::mousePressEvent(mouseEvent);
  586. return;
  587. }
  588. // 开始执行Link动作
  589. startLink(activeItem, mouseEvent->scenePos());
  590. // 输出Debug数据
  591. QString strValue = activeItem->m_infInfo->getValueString();
  592. DebugData data;
  593. data.addLog(activeItem->m_infInfo->strName, strValue);
  594. m_pPouFrame->UpdataDebugData(data);
  595. }
  596. // 2022-6-20,增加了对批量功能块移动的处理
  597. // 只有标准系列的功能块使用
  598. else if (activeBlock !=nullptr && activeBlock->isStandardBasedBlock())
  599. {
  600. // 2022-1-8,如果点击的是标准功能块,则调用UpdataDebugData
  601. if (activeBlock->type()== ITEM_TYPE_STANDARD)
  602. {
  603. // 显示DeBug信息
  604. try
  605. {
  606. DebugData data = m_Pou.GetToolDebugData(activeBlock);
  607. m_pPouFrame->UpdataDebugData(data);
  608. }
  609. catch (...)
  610. {
  611. }
  612. }
  613. // 如果是硬件组态,则不执行Link相关的所有操作(包括批量选中移动后的特殊处理)
  614. if (m_strPouName == GROUP_NAME_HARDWARE)
  615. {
  616. QGraphicsScene::mousePressEvent(mouseEvent);
  617. return;
  618. }
  619. // 检查是否正在是多选之后的批量移动操作
  620. this->checkBatchMove();
  621. }
  622. QGraphicsScene::mousePressEvent(mouseEvent);
  623. }
  624. /// <summary>
  625. /// 控制功能块的移动以及连线
  626. /// </summary>
  627. /// <param name="mouseEvent"></param>
  628. void WindowAppPouScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent)
  629. {
  630. // 如果处于Link模式,则实时绘制连线
  631. if (m_sceneMode == SCENE_MODE::LINK_MODE && m_tmpLinkLine!=nullptr )
  632. {
  633. bool blink = false;
  634. WindowAppItemInterface* startItem = nullptr;
  635. WindowAppItemInterface* endItem = nullptr;
  636. // 执行连接预检查 改变鼠标的形态
  637. int nStrte = preLinkCheck(mouseEvent->scenePos(), startItem, endItem);
  638. if (nStrte == 0)
  639. {
  640. m_pPouFrame->setCursor(Qt::UpArrowCursor);// ok
  641. // 自定义图标
  642. //m_pPouView->setCursor(QCursor(QPixmap(":/image/tree_item.png")));
  643. blink = true;
  644. }
  645. else
  646. {
  647. m_pPouFrame->setCursor(Qt::ArrowCursor);
  648. }
  649. moveLink(mouseEvent->scenePos(), blink);
  650. }
  651. // 批量移动模式
  652. else if(m_sceneMode == SCENE_MODE::BATCHMOVE_MODE)
  653. {
  654. // 暂时不需要做什么
  655. }
  656. // qDebug() << "WindowAppPouScene::mouseMoveEvent";
  657. QGraphicsScene::mouseMoveEvent(mouseEvent);
  658. }
  659. /// <summary>
  660. /// 控制功能块的移动以及生成连线
  661. /// </summary>
  662. /// <param name="mouseEvent"></param>
  663. void WindowAppPouScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent)
  664. {
  665. //qDebug() << "WindowAppDiagramScene::mouseReleaseEvent";
  666. // 如果正处于Link模式
  667. if (m_sceneMode == SCENE_MODE::LINK_MODE)
  668. {
  669. // 结束link操作
  670. endLink(mouseEvent->scenePos());
  671. }
  672. // 如果正处于批量移动模式
  673. else if (m_sceneMode == SCENE_MODE::BATCHMOVE_MODE)
  674. {
  675. // 取消所有选中Link的Movable状态
  676. for (auto& linkItem : m_batchMoveLinks)
  677. {
  678. linkItem->setMovable(false);
  679. }
  680. // 清空链表
  681. m_batchMoveLinks.clear();
  682. }
  683. // 切换回正常模式
  684. m_sceneMode = SCENE_MODE::NORMAL_MODE;
  685. // 恢复鼠标
  686. m_pPouFrame->setCursor(Qt::ArrowCursor);
  687. QGraphicsScene::mouseReleaseEvent(mouseEvent);
  688. }
  689. /// <summary>
  690. /// 2022-6-20,检查是否正在是多选之后的批量移动操作
  691. /// </summary>
  692. bool WindowAppPouScene::checkBatchMove()
  693. {
  694. // 获取当前选中的Item
  695. QList<QGraphicsItem*> selItems = this->selectedItems();
  696. QList<WindowAppBlockStandardBase*> selBlocks;
  697. // 如果选中的
  698. if (selItems.size() <= 1)
  699. {
  700. return false;
  701. }
  702. // 取出其中所有的Block
  703. for (const auto& selItem : selItems)
  704. {
  705. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  706. if (block != nullptr && block->isStandardBasedBlock() )
  707. {
  708. selBlocks.push_back(block);
  709. }
  710. }
  711. // 如果选中Block的数量大于2,则说明在做批量拖动
  712. if (selBlocks.size() >= 2)
  713. {
  714. m_sceneMode = SCENE_MODE::BATCHMOVE_MODE;
  715. qDebug() << "WindowAppPouScene::checkBatchMove() - SCENE_MODE::BATCHMOVE_MODE";
  716. }
  717. else
  718. {
  719. m_sceneMode = SCENE_MODE::NORMAL_MODE;
  720. return false;
  721. }
  722. // 取出其中每一个功能块之间的Link,将Link状态设置为Movable,可随Block随动
  723. for (const auto& selBlock : selBlocks)
  724. {
  725. for (const auto& selBlock2 : selBlocks)
  726. {
  727. if (selBlock2 != selBlock)
  728. {
  729. // 判断两个Block之间是否存在Link
  730. QList<WindowAppItemLink*> linkItems = m_Pou.getLinkItemsBetweenBlocks(selBlock, selBlock2);
  731. qDebug() << "WindowAppPouScene::checkBatchMove() - " << linkItems.size() << " link items between ["
  732. << selBlock->m_toolInfo->strInstanceName << "] ["
  733. << selBlock2->m_toolInfo->strInstanceName << "].";
  734. // 将这些Link都设置为Movable,并且保存下来后续使用
  735. for (auto& linkItem : linkItems)
  736. {
  737. //linkItem->setFlag(QGraphicsItem::ItemIsMovable, true);
  738. linkItem->setMovable(true);
  739. // 并且设置为选中
  740. linkItem->setSelected(true);
  741. m_batchMoveLinks.push_back(linkItem);
  742. }
  743. }
  744. }
  745. }
  746. return true;
  747. }
  748. /// <summary>
  749. /// 2022-8-26,添加完功能块Item后,初始化对应参数(Index、InstanceName、ToolInterface Name等)
  750. /// </summary>
  751. /// <param name="newTool">工具引用</param>
  752. /// <param name="bInitIndex">是否需要初始化功能块Index</param>
  753. /// <param name="bInitInstName">是否需要初始化功能块实例名称</param>
  754. /// <param name="bInitToolInfName">是否需要初始化功能块Tool接口名字</param>
  755. void WindowAppPouScene::initBlockItem(TOOL*& pNewTool, bool bInitIndex, bool bInitInstName, bool bInitToolInfName)
  756. {
  757. if (bInitIndex)
  758. {
  759. // 计算分配的Index
  760. pNewTool->nIndex = m_Pou.GetIndexedToolsCount();
  761. }
  762. if (bInitInstName)
  763. {
  764. // 需要根据现有工具情况生成新的实例名字
  765. pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName));
  766. }
  767. if (bInitToolInfName)
  768. {
  769. // 2022-8-25,为工具的Tool接口命名
  770. pNewTool->ToolInterfaces[INF_START]->strName = pNewTool->strInstanceName + ".Start";
  771. pNewTool->ToolInterfaces[INF_START]->strFullName = m_Pou.pouName() + "." + pNewTool->ToolInterfaces[INF_START]->strName;
  772. pNewTool->ToolInterfaces[INF_END]->strName = pNewTool->strInstanceName + ".End";
  773. pNewTool->ToolInterfaces[INF_END]->strFullName = m_Pou.pouName() + "." + pNewTool->ToolInterfaces[INF_END]->strName;
  774. }
  775. }
  776. /// <summary>
  777. /// 功能块序号 减 1
  778. /// </summary>
  779. void WindowAppPouScene::BlockMoveUp()
  780. {
  781. // 防止功能块被多选
  782. QList<QGraphicsItem*> selItems = this->selectedItems();
  783. if (selItems.size() > 1)
  784. {
  785. return;
  786. }
  787. QGraphicsItem* selItem = this->selectedItems().first();
  788. if (selItem != nullptr)
  789. {
  790. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  791. if (block != nullptr && block->isStandardBasedBlock())
  792. {
  793. block->onBlockMoveUp();
  794. }
  795. }
  796. }
  797. /// <summary>
  798. /// 功能块序号 加 1
  799. /// </summary>
  800. void WindowAppPouScene::BlockMoveDown()
  801. {
  802. // 防止功能块被多选
  803. QList<QGraphicsItem*> selItems = this->selectedItems();
  804. if (selItems.size() > 1)
  805. {
  806. return;
  807. }
  808. QGraphicsItem* selItem = this->selectedItems().first();
  809. if (selItem != nullptr)
  810. {
  811. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  812. if (block != nullptr && block->isStandardBasedBlock())
  813. {
  814. block->onBlockMoveDown();
  815. }
  816. }
  817. }
  818. /// <summary>
  819. /// 功能块序号 置1
  820. /// </summary>
  821. void WindowAppPouScene::BlockMoveFirst()
  822. {
  823. // 防止功能块被多选
  824. QList<QGraphicsItem*> selItems = this->selectedItems();
  825. if (selItems.size() > 1)
  826. {
  827. return;
  828. }
  829. QGraphicsItem* selItem = this->selectedItems().first();
  830. if (selItem != nullptr)
  831. {
  832. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  833. if (block != nullptr && block->isStandardBasedBlock())
  834. {
  835. block->onBlockMoveFirst();
  836. }
  837. }
  838. }
  839. /// <summary>
  840. /// 功能块序号 置底
  841. /// </summary>
  842. void WindowAppPouScene::BlockMoveLast()
  843. {
  844. // 防止功能块被多选
  845. QList<QGraphicsItem*> selItems = this->selectedItems();
  846. if (selItems.size() > 1)
  847. {
  848. return;
  849. }
  850. QGraphicsItem* selItem = this->selectedItems().first();
  851. if (selItem != nullptr)
  852. {
  853. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  854. if (block != nullptr && block->isStandardBasedBlock())
  855. {
  856. block->onBlockMoveLast();
  857. }
  858. }
  859. }
  860. /// <summary>
  861. /// 功能块左对齐
  862. /// </summary>
  863. void WindowAppPouScene::BlockAlignLeft()
  864. {
  865. // 获取当前选中的Item
  866. QList<QGraphicsItem*> selItems = this->selectedItems();
  867. QList<WindowAppBlockStandardBase*> selBlocks;
  868. // 如果选中的
  869. if (selItems.size() <= 1)
  870. {
  871. return ;
  872. }
  873. // 取出其中所有的Block
  874. for (const auto& selItem : selItems)
  875. {
  876. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  877. if (block != nullptr && block->isStandardBasedBlock())
  878. {
  879. selBlocks.push_back(block);
  880. }
  881. }
  882. // 确保选中的功能块数量大于 1
  883. if (selBlocks.size() <= 1)
  884. {
  885. return;
  886. }
  887. // 获取最左侧的工具坐标
  888. int nLeftValue = INT_MAX;
  889. for (int i = 0; i < selBlocks.size(); i++)
  890. {
  891. QPointF point = selBlocks[i]->scenePos();
  892. QRectF rect = selBlocks[i]->boundingRect();
  893. int x = point.x() - rect.width() / 2;
  894. if (nLeftValue > x)
  895. {
  896. nLeftValue = x;
  897. }
  898. }
  899. // 设置选中的功能块坐标
  900. for (int i = 0; i < selBlocks.size(); i++)
  901. {
  902. QPointF point = selBlocks[i]->scenePos();
  903. QRectF rect = selBlocks[i]->boundingRect();
  904. point.setX(nLeftValue + rect.width()/2 );
  905. selBlocks[i]->setPos(point);
  906. }
  907. }
  908. /// <summary>
  909. /// 功能块顶对齐
  910. /// </summary>
  911. void WindowAppPouScene::BlockAlignTop()
  912. {
  913. // 获取当前选中的Item
  914. QList<QGraphicsItem*> selItems = this->selectedItems();
  915. QList<WindowAppBlockStandardBase*> selBlocks;
  916. // 如果选中的
  917. if (selItems.size() <= 1)
  918. {
  919. return;
  920. }
  921. // 取出其中所有的Block
  922. for (const auto& selItem : selItems)
  923. {
  924. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  925. if (block != nullptr && block->isStandardBasedBlock())
  926. {
  927. selBlocks.push_back(block);
  928. }
  929. }
  930. // 确保选中的功能块数量大于 1
  931. if (selBlocks.size() <= 1)
  932. {
  933. return;
  934. }
  935. // 获取最顶侧的工具坐标
  936. int nTopValue = INT_MAX;
  937. for (int i = 0; i < selBlocks.size(); i++)
  938. {
  939. QPointF point = selBlocks[i]->scenePos();
  940. QRectF rect = selBlocks[i]->boundingRect();
  941. int y = point.y() - rect.height() / 2;
  942. if (nTopValue > y)
  943. {
  944. nTopValue = y;
  945. }
  946. }
  947. // 设置选中的功能块坐标
  948. for (int i = 0; i < selBlocks.size(); i++)
  949. {
  950. QPointF point = selBlocks[i]->scenePos();
  951. QRectF rect = selBlocks[i]->boundingRect();
  952. point.setY(nTopValue + rect.height() / 2);
  953. selBlocks[i]->setPos(point);
  954. }
  955. }
  956. /// <summary>
  957. /// 功能块右对齐
  958. /// </summary>
  959. void WindowAppPouScene::BlockAlignRight()
  960. {
  961. // 获取当前选中的Item
  962. QList<QGraphicsItem*> selItems = this->selectedItems();
  963. QList<WindowAppBlockStandardBase*> selBlocks;
  964. // 如果选中的
  965. if (selItems.size() <= 1)
  966. {
  967. return;
  968. }
  969. // 取出其中所有的Block
  970. for (const auto& selItem : selItems)
  971. {
  972. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  973. if (block != nullptr && block->isStandardBasedBlock())
  974. {
  975. selBlocks.push_back(block);
  976. }
  977. }
  978. // 确保选中的功能块数量大于 1
  979. if (selBlocks.size() <= 1)
  980. {
  981. return;
  982. }
  983. // 获取最右侧的工具坐标
  984. int nRightValue = 0;
  985. for (int i = 0; i < selBlocks.size(); i++)
  986. {
  987. QPointF point = selBlocks[i]->scenePos();
  988. QRectF rect = selBlocks[i]->boundingRect();
  989. int x = point.x() + rect.width() / 2;
  990. if (nRightValue < x)
  991. {
  992. nRightValue = x;
  993. }
  994. }
  995. // 设置选中的功能块坐标
  996. for (int i = 0; i < selBlocks.size(); i++)
  997. {
  998. QPointF point = selBlocks[i]->scenePos();
  999. QRectF rect = selBlocks[i]->boundingRect();
  1000. point.setX(nRightValue - rect.width() / 2);
  1001. selBlocks[i]->setPos(point);
  1002. }
  1003. }
  1004. /// <summary>
  1005. /// 功能块底对齐
  1006. /// </summary>
  1007. void WindowAppPouScene::BlockAlignBottom()
  1008. {
  1009. // 获取当前选中的Item
  1010. QList<QGraphicsItem*> selItems = this->selectedItems();
  1011. QList<WindowAppBlockStandardBase*> selBlocks;
  1012. // 如果选中的
  1013. if (selItems.size() <= 1)
  1014. {
  1015. return;
  1016. }
  1017. // 取出其中所有的Block
  1018. for (const auto& selItem : selItems)
  1019. {
  1020. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  1021. if (block != nullptr && block->isStandardBasedBlock())
  1022. {
  1023. selBlocks.push_back(block);
  1024. }
  1025. }
  1026. // 确保选中的功能块数量大于 1
  1027. if (selBlocks.size() <= 1)
  1028. {
  1029. return;
  1030. }
  1031. // 获取最底侧的工具坐标
  1032. int nBottomValue = 0;
  1033. for (int i = 0; i < selBlocks.size(); i++)
  1034. {
  1035. QPointF point = selBlocks[i]->scenePos();
  1036. QRectF rect = selBlocks[i]->boundingRect();
  1037. int y = point.y() + rect.height() / 2;
  1038. if (nBottomValue < y)
  1039. {
  1040. nBottomValue = y;
  1041. }
  1042. }
  1043. // 设置选中的功能块坐标
  1044. for (int i = 0; i < selBlocks.size(); i++)
  1045. {
  1046. QPointF point = selBlocks[i]->scenePos();
  1047. QRectF rect = selBlocks[i]->boundingRect();
  1048. point.setY(nBottomValue - rect.height() / 2);
  1049. selBlocks[i]->setPos(point);
  1050. }
  1051. }
  1052. //=================================================================
  1053. //
  1054. // Link相关
  1055. //
  1056. //=================================================================
  1057. /// <summary>
  1058. /// 开始执行Link动作
  1059. /// </summary>
  1060. void WindowAppPouScene::startLink(WindowAppItemInterface* pStartInf, QPointF ptMouse)
  1061. {
  1062. // 切换模式
  1063. this->m_sceneMode = SCENE_MODE::LINK_MODE;
  1064. // 连线的起点
  1065. QPointF ptStart;
  1066. // 2022-4-24,根据起点接口的类型,分别进行处理
  1067. // 普通功能块接口
  1068. if (!pStartInf->m_infInfo->isParallelToolEnd())
  1069. {
  1070. ptStart = pStartInf->mapToScene(pStartInf->line().p2());
  1071. }
  1072. // Parallel输出接口
  1073. else
  1074. {
  1075. ptStart = ptMouse;
  1076. }
  1077. // 建立临时的连接线
  1078. m_tmpLinkLine = new QGraphicsLineItem(QLineF(ptStart, ptMouse));
  1079. m_tmpLinkLine->setPen(QPen(LINK_TMPLINE_COLOR, LINK_LINE_WIDTH));
  1080. this->addItem(m_tmpLinkLine);
  1081. }
  1082. /// <summary>
  1083. /// 移动Link连接
  1084. /// </summary>
  1085. /// <param name="ptMouse"></param>
  1086. void WindowAppPouScene::moveLink(QPointF ptMouse, bool blink)
  1087. {
  1088. QLineF newLine(m_tmpLinkLine->line().p1(), ptMouse);
  1089. m_tmpLinkLine->setLine(newLine);
  1090. if (blink)
  1091. {
  1092. m_tmpLinkLine->setPen(QPen(LINK_LINE_COLOR, LINK_LINE_WIDTH));
  1093. }
  1094. else
  1095. {
  1096. QVector<qreal> dashes;
  1097. dashes << 5 << 5 << 5 << 5;
  1098. QPen pen(QPen(LINK_LINE_COLOR, LINK_LINE_WIDTH));
  1099. pen.setDashPattern(dashes);
  1100. m_tmpLinkLine->setPen(pen);
  1101. }
  1102. // 增加了重绘,防止线条覆盖住接口的情况
  1103. this->update();
  1104. }
  1105. /// <summary>
  1106. /// 结束Link动作
  1107. /// </summary>
  1108. /// <param name="ptMouse"></param>
  1109. void WindowAppPouScene::endLink(QPointF ptMouse)
  1110. {
  1111. WindowAppItemInterface* startItem = nullptr;
  1112. WindowAppItemInterface* endItem = nullptr;
  1113. // 执行连接预检查,如果检查通过了再建立连接
  1114. int nState = preLinkCheck(ptMouse, startItem, endItem);
  1115. if (nState == 0)
  1116. {
  1117. Q_ASSERT(startItem != nullptr);
  1118. Q_ASSERT(endItem != nullptr);
  1119. // 此处判断一下是否是Goto->Tool的连接
  1120. if (startItem->m_infInfo->isGotoToolEnd())
  1121. {
  1122. // 建立Goto到Tool之间的Link
  1123. this->addGotoLink(startItem->m_infInfo, endItem->m_infInfo->parent());
  1124. }
  1125. // 继续判断一下是否是Parallel->Tool的连接
  1126. else if (startItem->m_infInfo->isParallelToolEnd()
  1127. && endItem->m_infInfo->isStandardToolStart())
  1128. {
  1129. // 建立Parallel到Tool之间的Link
  1130. this->addParallelLink(startItem->m_infInfo, endItem->m_infInfo->parent());
  1131. }
  1132. // 建立标准接口间的link
  1133. else
  1134. {
  1135. addLink(startItem, endItem, LINK_MODE::LINK_NORMAL);
  1136. }
  1137. }
  1138. // 无论成功与否都清除掉临时连接线
  1139. removeItem(m_tmpLinkLine);
  1140. RELEASE(m_tmpLinkLine);
  1141. }
  1142. /// <summary>
  1143. /// 进行连接预检查
  1144. /// </summary>
  1145. /// <param name="ptMouse"></param>
  1146. /// <returns></returns>
  1147. int WindowAppPouScene::preLinkCheck(QPointF ptMouse, WindowAppItemInterface*& startItem, WindowAppItemInterface*& endItem)
  1148. {
  1149. // 先把多余的起点和终点区域的Item去掉,只留下接口
  1150. // MENTION:此处稍微偏移一些,为了更准确的选中接口
  1151. QPointF startPoint = QPointF(m_tmpLinkLine->line().p1().x() - 2, m_tmpLinkLine->line().p1().y());
  1152. QList<QGraphicsItem*> startItems = items(startPoint);
  1153. if (startItems.count() && startItems.first() == m_tmpLinkLine)
  1154. {
  1155. startItems.removeFirst();
  1156. }
  1157. QList<QGraphicsItem*> endItems = items(ptMouse);
  1158. if (endItems.count() && endItems.first() == m_tmpLinkLine)
  1159. {
  1160. endItems.removeFirst();
  1161. }
  1162. // 去除多余Item后,是否还有有效的接口信息
  1163. if (startItems.count() <= 0 || endItems.count() <= 0)
  1164. {
  1165. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem or endItem count <=0.";
  1166. return -1;
  1167. }
  1168. // 从集合的第一个Item中取出需要link的item
  1169. startItem = qgraphicsitem_cast<WindowAppItemInterface*>(startItems.first());
  1170. endItem = qgraphicsitem_cast<WindowAppItemInterface*>(endItems.first());
  1171. if (startItem == nullptr || endItem == nullptr)
  1172. {
  1173. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem or endItem is nullptr.";
  1174. return -2;
  1175. }
  1176. // 取出其中的接口信息
  1177. const _INTERFACE* startInf = startItem->m_infInfo;
  1178. const _INTERFACE* endInf = endItem->m_infInfo;
  1179. // 此处增加一个有效性检查
  1180. if (startItem == nullptr)
  1181. {
  1182. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem is not a WindowAppItemInterface.";
  1183. return -3;
  1184. }
  1185. if (endItem == nullptr)
  1186. {
  1187. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is not a WindowAppItemInterface.";
  1188. return -4;
  1189. }
  1190. // 检查是否是同一个item
  1191. if (startItem == endItem)
  1192. {
  1193. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem is same as endItem.";
  1194. return -5;
  1195. }
  1196. if (startItem->m_infInfo->parent() == endItem->m_infInfo->parent())
  1197. {
  1198. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem's parent Tool is same as endItem.";
  1199. return -6;
  1200. }
  1201. // 检查起点是否是有效的接口类型
  1202. if (startItem->type() != WindowAppItemInterface::Type)
  1203. {
  1204. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem is not interface item.";
  1205. return -7;
  1206. }
  1207. // 检查起点是否是有效的接口类型
  1208. if (endItem->type() != WindowAppItemInterface::Type)
  1209. {
  1210. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is not interface item.";
  1211. return -8;
  1212. }
  1213. // 检查终点是否是输入接口
  1214. if (endInf->Direction != INF_DIRECTION::INF_DIR_IN)
  1215. {
  1216. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is not input interface.";
  1217. return -9;
  1218. }
  1219. // 检查终点接口是否已经有连接了
  1220. if (endInf->pUpLinkInterface != nullptr)
  1221. {
  1222. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is already linked.";
  1223. return -10;
  1224. }
  1225. // 检查起点和终点接口的数值类型是否一致(并且所有基础类型算作是相同类型)
  1226. if (!endInf->isSameTypeTo(startInf, true))
  1227. {
  1228. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem and endItem is not the same value type.";
  1229. return -11;
  1230. }
  1231. // 2022-6-7,增加检查项,如果是Tool类型接口,还需要额外检查
  1232. // 本接口目前不允许父功能块是同类型的连接,比如Goto->Goto,Tool->Tool,Parallel->Parallel等等
  1233. if (startInf->isToolEnd() && endInf->isParentSameTypeTo(startInf))
  1234. {
  1235. // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - Top output can't link to the same tool type.";
  1236. return -12;
  1237. }
  1238. // 通过所有检查项,执行Link
  1239. qDebug() << "[OK] WindowAppPouScene::preLinkCheck passed. start:" << startItem->m_infInfo->strFullName
  1240. << " | end:" << endItem->m_infInfo->strFullName;
  1241. return 0;
  1242. }
  1243. /// <summary>
  1244. /// 增加一个Link
  1245. /// </summary>
  1246. /// <param name="pStartInf"></param>
  1247. /// <param name="pEndInf"></param>
  1248. /// <param name="linkMode">link的模式(普通/并行)</param>
  1249. /// <param name="linePoints">本次link各线段的坐标(用于反序列化)</param>
  1250. void WindowAppPouScene::addLink(
  1251. WindowAppItemInterface* pStartInf,
  1252. WindowAppItemInterface* pEndInf,
  1253. LINK_MODE linkMode,
  1254. QVector<QLineF> linePoints
  1255. )
  1256. {
  1257. WindowAppItemLink* newLinkItem = new WindowAppItemLink(pStartInf, pEndInf, linkMode, linePoints);
  1258. // 置于最底层
  1259. newLinkItem->setZValue(Z_ORDER_LINK);
  1260. this->addItem(newLinkItem);
  1261. // newLink->updatePosition();
  1262. // 保存Block与Link的关联信息,用于link对Block随动
  1263. ITEM_LINK_INFO newInfo;
  1264. newInfo.bStart = true;
  1265. newInfo.pLink = newLinkItem;
  1266. m_itemBlockLinks.insertMulti(pStartInf->parentItem(), newInfo);
  1267. newInfo.bStart = false;
  1268. m_itemBlockLinks.insertMulti(pEndInf->parentItem(), newInfo);
  1269. // 保存接口与Link的关联信息
  1270. m_itemInfLinks.insertMulti(pStartInf, newLinkItem);
  1271. m_itemInfLinks.insertMulti(pEndInf, newLinkItem);
  1272. // 存储link相关数据结构
  1273. m_Pou.makeLink(pStartInf, pEndInf, newLinkItem, linkMode);
  1274. }
  1275. /// <summary>
  1276. /// 建立Link(根据接口全名)
  1277. /// </summary>
  1278. /// <param name="strStartInf"></param>
  1279. /// <param name="strEndInf"></param>
  1280. /// <param name="linkMode">link的模式(普通/并行)</param>
  1281. /// <param name="linePoints">本次link各线段的坐标(用于反序列化</param>
  1282. void WindowAppPouScene::addLink(
  1283. const QString& strStartInf,
  1284. const QString& strEndInf,
  1285. LINK_MODE linkMode,
  1286. QVector<QLineF> linePoints
  1287. )
  1288. {
  1289. // 获取到对应的接口
  1290. WindowAppItemInterface* startInf = m_Pou.GetInterfaceItemByName(strStartInf);
  1291. WindowAppItemInterface* endInf = m_Pou.GetInterfaceItemByName(strEndInf);
  1292. // Error
  1293. if (startInf == nullptr || endInf == nullptr)
  1294. {
  1295. qDebug() << "[Error] WindowAppPouScene::addLink - startInf is nullptr or endInf is nullptr.";
  1296. return;
  1297. }
  1298. // 建立Link
  1299. this->addLink(startInf, endInf, linkMode, linePoints);
  1300. }
  1301. ///// <summary>
  1302. ///// 建立Link(根据Link结构体)
  1303. ///// </summary>
  1304. ///// <param name="linkInfo"></param>
  1305. //void WindowAppPouScene::addLink(const LINK& linkInfo)
  1306. //{
  1307. // this->addLink(
  1308. // linkInfo.strSrcInf,
  1309. // linkInfo.strDesInf,
  1310. // linkInfo.linkMode,
  1311. // linkInfo.lines
  1312. // );
  1313. //}
  1314. /// <summary>
  1315. /// 删除指定link连线
  1316. /// </summary>
  1317. /// <param name="pLinkItem"></param>
  1318. void WindowAppPouScene::delLink(WindowAppItemLink* pLinkItem)
  1319. {
  1320. WindowAppItemInterface* startInfItem = pLinkItem->startItem();
  1321. WindowAppItemInterface* endInfItem = pLinkItem->endItem();
  1322. // 相关的起点和终点的引用计数 - 1
  1323. //m_PouManager.MinusInterfaceRefCount(startInfItem);
  1324. //m_PouManager.MinusInterfaceRefCount(endInfItem);
  1325. startInfItem->m_infInfo->nRefCount--;
  1326. endInfItem->m_infInfo->nRefCount--;
  1327. // 删除pou中的link信息
  1328. m_Pou.removeLink(endInfItem);
  1329. // 删除itemBlockLinks中的信息
  1330. // 2022-10-19 修正,此处需要精细判断一下具体删除哪个BlockLink,否则一个输出接口连接至多个接口时
  1331. // 此处的多条信息会被一次性删除导致bug
  1332. ITEM_LINK_INFO delInfo;
  1333. delInfo.bStart = true;
  1334. delInfo.pLink = pLinkItem;
  1335. m_itemBlockLinks.remove(startInfItem->parentItem(), delInfo);
  1336. delInfo.bStart = false;
  1337. delInfo.pLink = pLinkItem;
  1338. m_itemBlockLinks.remove(endInfItem->parentItem(), delInfo);
  1339. // 删除itemInfLinks中的信息
  1340. m_itemInfLinks.remove(pLinkItem->startItem(), pLinkItem);
  1341. m_itemInfLinks.remove(pLinkItem->endItem(), pLinkItem);
  1342. // 2022-5-6,如果是
  1343. qDebug() << "[SCENE][LINK] Delete link between [" << startInfItem->m_infInfo->strFullName
  1344. << "]->[" << endInfItem->m_infInfo->strFullName << "].";
  1345. // 最后彻底清空此连线item
  1346. delete pLinkItem;
  1347. pLinkItem = nullptr;
  1348. }
  1349. /// <summary>
  1350. /// 根据接口删除link连线
  1351. /// </summary>
  1352. /// <param name="pInf"></param>
  1353. void WindowAppPouScene::delLink(WindowAppItemInterface* pInf)
  1354. {
  1355. // 首先通过接口快速查询到对应的link连线(有可能会有多个)
  1356. QList<WindowAppItemLink*> pLinkItems = m_itemInfLinks.values(pInf);
  1357. // 如果查询不到,则说明此接口并没有连线
  1358. if ( pLinkItems.size()<=0 )
  1359. {
  1360. qDebug() << "[SCENE][LINK] Delete link error: can not find link of interface[" << pInf->m_infInfo->strFullName << "].";
  1361. return;
  1362. }
  1363. // 然后分别清除每一个对应接口的信息
  1364. for (WindowAppItemLink * pLinkItem : pLinkItems)
  1365. {
  1366. this->delLink(pLinkItem);
  1367. }
  1368. }
  1369. /// <summary>
  1370. /// 根据接口名字删除link连线
  1371. /// </summary>
  1372. /// <param name="strInf"></param>
  1373. void WindowAppPouScene::delLink(const QString& strInf)
  1374. {
  1375. WindowAppItemInterface* infItem = m_Pou.GetInterfaceItemByName(strInf);
  1376. this->delLink(infItem);
  1377. }
  1378. /// <summary>
  1379. /// 执行SmartLink(找离本接口最近的同类型输出端口,序号小于自己的)
  1380. /// </summary>
  1381. /// <param name="pInf"></param>
  1382. void WindowAppPouScene::smartLink(WindowAppItemInterface* pInf)
  1383. {
  1384. // 在pou中找到可以执行smartLink的接口
  1385. WindowAppItemInterface* pLinkInfItem = m_Pou.getSmartLinkInterface(pInf);
  1386. if (pLinkInfItem == nullptr)
  1387. {
  1388. Utility::VPCriticalMessageBox("Not find corresponding smartlink interface!");
  1389. return;
  1390. }
  1391. // 如果成功找到了的话,则连接
  1392. addLink(pLinkInfItem, pInf);
  1393. }
  1394. /// <summary>
  1395. /// 建立Tool间的Link(Goto到Tool的link)
  1396. /// </summary>
  1397. /// <param name="pGotoInf"></param>
  1398. /// <param name="pTool"></param>
  1399. void WindowAppPouScene::addGotoLink(_INTERFACE* pGotoInf, const TOOL* pTool)
  1400. {
  1401. //// 首先检查目标Tool是否已经有连接
  1402. //if (pTool->isTopLinked())
  1403. //{
  1404. // qWarning() << "Tool[" + pTool->strInstanceName + "] is already linked!";
  1405. // return;
  1406. //}
  1407. // 如果本Goto之前绑定过Tool,则首先清除之前的link,准备绑定新Tool
  1408. if (pGotoInf->pBindInterface != nullptr)
  1409. {
  1410. this->delLink(pGotoInf->strFullName);
  1411. }
  1412. // 绑定到目标Tool的ToolStart接口中
  1413. pGotoInf->pBindInterface = pTool->startInterface;
  1414. // 设置接口的名称为目标Tool的实例名字
  1415. pGotoInf->strName = pTool->strInstanceName;
  1416. // 绑定完毕之后,需要把输出接口的显示打开,用于显示绑定目标的名字
  1417. pGotoInf->bShowName = true;
  1418. // 界面增加Link信息
  1419. QString strSrc = pGotoInf->strFullName;
  1420. QString strDst = pTool->startInterface->strFullName;
  1421. this->addLink(strSrc, strDst);
  1422. }
  1423. /// <summary>
  1424. /// 建立Parallel到Tool的Link
  1425. /// </summary>
  1426. /// <param name="pGotoInf"></param>
  1427. /// <param name="pTool"></param>
  1428. void WindowAppPouScene::addParallelLink(_INTERFACE* pParaInf, const TOOL* pTool)
  1429. {
  1430. // 界面增加Link信息
  1431. QString strSrc = pParaInf->strFullName;
  1432. QString strDst = pTool->startInterface->strFullName;
  1433. this->addLink(strSrc, strDst, LINK_MODE::LINK_PARALLEL);
  1434. // Parallel Link建立完毕之后需要立即确认一下并行连线的位置,有可能默认位置已经超出了并行连线的长度
  1435. WindowAppItemInterface* startInf = m_Pou.GetInterfaceItemByName(strSrc);
  1436. startInf->updatePostion();
  1437. }
  1438. /// <summary>
  1439. /// block被移动时,需要同时更新link连线的位置
  1440. /// </summary>
  1441. /// <param name="item"></param>
  1442. void WindowAppPouScene::onBlockMove(QGraphicsItem* item)
  1443. {
  1444. // 更新本Block关联的Link连线
  1445. // qDebug() << "WindowAppDiagramScene::onBlockMove.";
  1446. // TODO:为了修正一个在ShowOnly的情况下多次点击接口右键会崩溃的问题,临时处理
  1447. if (item == nullptr)
  1448. {
  1449. qDebug() << "[POU][Error] WindowAppPouScene::onBlockMove - but item is nullptr.";
  1450. return;
  1451. }
  1452. // 2022-6-15, 如果处于批量拖动模式下,则不需要重绘Link线段,直接平移
  1453. // 2022-7-2 修正,此处不能分开处理,否则Link连线会出现断裂的情况
  1454. //if (sceneMode != SCENE_MODE::BATCHMOVE_MODE)
  1455. //{
  1456. // 查找这个移动的Block是否有对应的link需要重绘
  1457. this->updateLinkItemsByBlock(item);
  1458. // 2022-5-8增加,如果是并行组相关的功能块移动时,还需要检查其所属的并行母线是否需要延长或者缩短
  1459. this->updateParallelLineByBlock(item);
  1460. //}
  1461. }
  1462. /// <summary>
  1463. /// 检查所有的接口连线是否需要刷新
  1464. /// </summary>
  1465. /// <param name="item"></param>
  1466. void WindowAppPouScene::updateLinkItemsByBlock(QGraphicsItem* item)
  1467. {
  1468. QList<ITEM_LINK_INFO> linkInfos = m_itemBlockLinks.values(item);
  1469. if (linkInfos.size() > 0)
  1470. {
  1471. // 自动化方式移动
  1472. //for (WindowAppItemLink* pLink : links)
  1473. //{
  1474. // 更新对应Link的位置
  1475. // pLink->updateLinkLines();
  1476. //}
  1477. // 手动方式移动
  1478. for (auto& linkInfo : linkInfos)
  1479. {
  1480. // 根据移动的功能块调整对应的Link连线
  1481. linkInfo.pLink->updateLinkLinesManual(item, linkInfo.bStart);
  1482. }
  1483. // 重绘界面
  1484. this->update();
  1485. }
  1486. }
  1487. /// <summary>
  1488. /// 检查并行母线是否需要刷新
  1489. /// </summary>
  1490. /// <param name="item"></param>
  1491. void WindowAppPouScene::updateParallelLineByBlock(QGraphicsItem* item)
  1492. {
  1493. WindowAppBlockStandard* pStardardBlock = qgraphicsitem_cast<WindowAppBlockStandard*>(item);
  1494. if (pStardardBlock == nullptr || !pStardardBlock->m_toolInfo->isParallelSubTool())
  1495. {
  1496. return;
  1497. }
  1498. // 找到本工具所属的并行工具组
  1499. TOOL* pParaTool = pStardardBlock->m_toolInfo->toolStartUpTool();
  1500. // 错误,不应该为空
  1501. // Reason:(如果此处不加判断的话,反序列化的时候由于此时工具信息尚未恢复完毕,所以执行到此处的时候会崩溃)
  1502. if (pParaTool == nullptr)
  1503. {
  1504. qDebug() << "[Error] WindowAppPouScene::updateParallelLineByBlock - but ParaTool is nullptr.";
  1505. return;
  1506. }
  1507. // 找到并行工具的功能块
  1508. WindowAppBlockParallel* blockParallel = qgraphicsitem_cast<WindowAppBlockParallel*>(m_Pou.GetToolItem(pParaTool));
  1509. if (blockParallel != nullptr)
  1510. {
  1511. // 更新对应的并行母线(以起始连线线的X坐标为参照)
  1512. blockParallel->updateParallelLine();
  1513. }
  1514. }
  1515. /// <summary>
  1516. /// 用于和注释控件做联动使用,如果注释控件内容为空,则删除此控件
  1517. /// </summary>
  1518. /// <param name="item"></param>
  1519. void WindowAppPouScene::removeEmptyBlockComment(WindowAppBlockComment* item)
  1520. {
  1521. // 如果注释控件为空,则删除此控件
  1522. if (item->content().isEmpty())
  1523. {
  1524. removeItem(item);
  1525. item->deleteLater();
  1526. }
  1527. }
  1528. //=================================================================
  1529. //
  1530. // 动态端口同步相关
  1531. //
  1532. //=================================================================
  1533. /// <summary>
  1534. /// 接收来自Dll端的动态端口同步消息
  1535. /// </summary>
  1536. /// <param name="event"></param>
  1537. void WindowAppPouScene::customEvent(QEvent* event)
  1538. {
  1539. // 如果是动态端口同步消息
  1540. if (event->type() == DLLINF_EVENT_TYPEID)
  1541. {
  1542. SyncInterfaceEvent* pDllInfEvent = dynamic_cast<SyncInterfaceEvent*> (event);
  1543. // 取出参数
  1544. QList<DLL_INF> syncInfs = pDllInfEvent->getSyncInterfaces();
  1545. qDebug() << "WindowAppPouScene::customEvent - DLLINF_EVENT, inf count:" << syncInfs.size()
  1546. << " add/del:" << pDllInfEvent->m_bAdd;
  1547. // 根据名字找到所属的功能块(仅标准功能块支持此功能)
  1548. WindowAppBlockStandard* pBlock =
  1549. dynamic_cast<WindowAppBlockStandard*>(m_Pou.GetToolItem(pDllInfEvent->m_strInstanceName));
  1550. // 执行结果
  1551. bool bRet = false;
  1552. // 如果出错,保存错误原因
  1553. QString strReason;
  1554. // 动态增加
  1555. if (pDllInfEvent->m_bAdd)
  1556. {
  1557. bRet = pBlock->addDynamicInterfacesFromDll(syncInfs, strReason);
  1558. }
  1559. // 动态删除
  1560. else
  1561. {
  1562. bRet = pBlock->deleteDynamicInterfacesFromDll(syncInfs, strReason);
  1563. }
  1564. pDllInfEvent->m_bSuccess = bRet;
  1565. pDllInfEvent->m_strReason = strReason;
  1566. //// 刷新整个UI
  1567. //this->update();
  1568. }
  1569. }
  1570. ///// <summary>
  1571. ///// 2022-3-10 为功能块增加动态接口
  1572. ///// </summary>
  1573. ///// <param name="strToolInstName"></param>
  1574. ///// <param name="infList"></param>
  1575. //void WindowAppPouScene::addDynamicInterfaces(const QString& strToolInstName, const QList<DLL_INF>& infList)
  1576. //{
  1577. // WindowAppBlockStandard* pBlock = dynamic_cast<WindowAppBlockStandard*>(m_Pou.GetToolItem(strToolInstName));
  1578. //
  1579. // // 增加动态接口
  1580. // pBlock->addDynamicInterfacesFromDll(infList);
  1581. //
  1582. //}
  1583. //
  1584. //void WindowAppPouScene::delDynamicInterfaces(const QString& strToolInstName, const QList<DLL_INF>& infList)
  1585. //{
  1586. //
  1587. //}
  1588. /// <summary>
  1589. /// 键盘上下键移动功能块 不按 Control 的时候,鼠标上下键为画布移动,按住 Control 的时候,为功能块移动
  1590. /// </summary>
  1591. /// <param name="e"></param>
  1592. void WindowAppPouScene::keyPressEvent(QKeyEvent* event)
  1593. {
  1594. if (QApplication::keyboardModifiers() == Qt::ShiftModifier
  1595. || QApplication::keyboardModifiers() == Qt::ControlModifier
  1596. )
  1597. {
  1598. qreal dx = 0, dy = 0;
  1599. switch (event->key())
  1600. {
  1601. case Qt::Key_Up:
  1602. dx = 0;
  1603. dy = -m_nMoveBlockSleep;
  1604. break;
  1605. case Qt::Key_Down:
  1606. dx = 0;
  1607. dy = m_nMoveBlockSleep;
  1608. break;
  1609. case Qt::Key_Left:
  1610. dx = -m_nMoveBlockSleep;
  1611. dy = 0;
  1612. break;
  1613. case Qt::Key_Right:
  1614. dx = m_nMoveBlockSleep;
  1615. dy = 0;
  1616. break;
  1617. }
  1618. // 持续移动的时候,移动步长会持续增加。直到步长值为 50
  1619. if (m_nMoveBlockSleep < 50)
  1620. {
  1621. m_nMoveBlockSleep++;
  1622. }
  1623. QList<QGraphicsItem*> selItems = this->selectedItems();
  1624. QList<WindowAppBlockStandardBase*> selBlocks;
  1625. // 取出其中所有的Block
  1626. for (const auto& selItem : selItems)
  1627. {
  1628. WindowAppBlockStandardBase* block = qgraphicsitem_cast<WindowAppBlockStandardBase*>(selItem);
  1629. if (block != nullptr && block->isStandardBasedBlock())
  1630. {
  1631. selBlocks.push_back(block);
  1632. //onBlockMove(selItem);
  1633. block->moveBy(dx, dy);
  1634. }
  1635. }
  1636. // TODO: 还需要移动 Link
  1637. }
  1638. //else
  1639. {
  1640. QGraphicsScene::keyPressEvent(event);
  1641. }
  1642. }
  1643. /// <summary>
  1644. /// 接收键盘释放消息
  1645. /// </summary>
  1646. /// <param name="event"></param>
  1647. void WindowAppPouScene::keyReleaseEvent(QKeyEvent* event)
  1648. {
  1649. //
  1650. if (event->isAutoRepeat())
  1651. {
  1652. event->ignore();
  1653. }
  1654. else
  1655. {
  1656. m_nMoveBlockSleep = 1;
  1657. }
  1658. QGraphicsScene::keyReleaseEvent(event);
  1659. }