#include "WindowAppPouScene.h" #include "WindowAppPouFrame.h" #include "ToolDepository.h" #include "WindowAppBlockPort.h" #include "WindowAppBlockStandard.h" #include "WindowAppBlockGoto.h" #include "WindowAppItemLink.h" #include "PouManager.h" #include "WindowAppItemInterface.h" #include "WindowAppBlockComment.h" #include "WindowAppBlockParallel.h" #include "WindowAppBlockForloop.h" #include "WindowAppBlockWait.h" #include "WindowAppTaskView.h" #include "TaskManager.h" extern ToolDepository toolDepository; WindowAppPouScene::WindowAppPouScene( const QString& strTitle, QObject* parent, QWidget* parentparent ) : QGraphicsScene(parent) // 2022-1-8 保存PouFrame的指针 , m_pPouFrame((WindowAppPouFrame*)parentparent) { m_sceneMode = SCENE_MODE::NORMAL_MODE; m_tmpLinkLine = nullptr; m_strPouName = strTitle; // 完成Pou的初始化动作 m_Pou.init(strTitle, this); // 如果本Group是硬件Group的话,则绑定硬件的Pou if (strTitle == GROUP_NAME_HARDWARE) { g_pPouManager->registerHdwPou(&m_Pou); } //else //{ // 在全局信息数据中注册本Pou g_pPouManager->registerPou(&m_Pou); // } //// 保存当前的Pou数据结构信息 //Pou.insert(m_strTitle, &m_Pou); // 初始化步长值为 1 m_nMoveBlockSleep = 1; } /// /// 生成新的运行时工具信息,并添加到界面中 /// /// /// WindowAppBlockBase* WindowAppPouScene::addToolItem(const STATIC_TOOL* pNewTool, QPointF pos) { //// 2022-3-13 增加,如果是注释工具的话,则专门进行注释工具的添加流程 //if (pNewTool->Type == TOOL_TYPE::TOOL_TYPE_COMMENT) //{ // this->addNewCommentItem(pNewTool, pos); // return; //} WindowAppBlockBase* pNewBlock = nullptr; // 如果不是注释工具的话,则需要执行如下判断 if (pNewTool->Type != TOOL_TYPE::TOOL_TYPE_COMMENT) { // 2021-8-14 增加,如果本Pou正在Task的执行过程中,则不允许添加 if (m_Pou.isRunningInTask()) { Utility::VPCriticalMessageBox( "Can not add Tool[" + pNewTool->strName + "], Reason: this pou is running in Task[" + m_Pou.m_pParentTask->strName + "]."); return nullptr; } // 2021-8-19 增加,如果本页面是硬件页面,那么只允许硬件的工具拖进来 if (this->m_strPouName == GROUP_NAME_HARDWARE && pNewTool->strCategory != CATEGORY_TOOL_HARDWARE) { Utility::VPCriticalMessageBox("Only hardware tools could be added in this page!"); return nullptr; } } // 检查通过后开始建立工具 TOOL* toolRunning = new TOOL(pNewTool); // 2021-7-4增加,保存组名 toolRunning->strPouName = m_strPouName; // 生成新的运行时工具信息 // 标准工具 if (pNewTool->isStandardTool()) { pNewBlock = this->addStandardItem(toolRunning, pos); } // Port工具 else if (pNewTool->isPortTool()) { pNewBlock = this->addPortItem(toolRunning, pos); } // Goto工具 else if (pNewTool->isGotoTool()) { pNewBlock = this->addGotoItem(toolRunning, pos); } // Comment工具 else if (pNewTool->isCommentTool()) { pNewBlock = this->addCommentItem(toolRunning, pos); } // Parallel工具 else if (pNewTool->isParallelTool()) { pNewBlock = this->addParallelItem(toolRunning, pos); } // ForLoop工具 else if (pNewTool->isForloopTool()) { pNewBlock = this->addForloopItem(toolRunning, pos); } // Wait工具 else if (pNewTool->isWaitTool()) { pNewBlock = this->addWaitItem(toolRunning, pos); } else { // Error Here qDebug() << "[ERROR] WindowAppPouScene::addNewItem - Unknown tool type :" << (int)pNewTool->Type; return nullptr; } vDebug() << "Add Tool[" << toolRunning->strInstanceName << "] in Pou[" << m_strPouName << "]."; return pNewBlock; } /// /// 生成新的运行时工具信息(反序列化方式) /// /// /// /// 此参数的含义是是否通过反序列化添加,如果是反序列化进来的,需要跳过一些变量设置 WindowAppBlockBase* WindowAppPouScene::addToolItem(TOOL* pNewTool, QPointF pos, bool bFromDoc) { //// 2022-3-13 增加,如果是注释工具的话,则专门进行注释工具的添加流程 //if (pNewTool->Type == TOOL_TYPE::TOOL_TYPE_COMMENT) //{ // this->addNewCommentItem(pos); // return; //} // 标准工具信息 if (pNewTool->isStandardTool()) { return this->addStandardItem(pNewTool, pos, bFromDoc); } // Port工具 else if (pNewTool->isPortTool()) { return this->addPortItem(pNewTool, pos, bFromDoc); } // Goto工具 else if (pNewTool->isGotoTool()) { return this->addGotoItem(pNewTool, pos, bFromDoc); } // 注释工具 else if (pNewTool->isCommentTool()) { return this->addCommentItem(pNewTool, pos, bFromDoc); } // Parallel工具 else if (pNewTool->isParallelTool()) { return this->addParallelItem(pNewTool, pos, bFromDoc); } // ForLoop工具 else if (pNewTool->isForloopTool()) { return this->addForloopItem(pNewTool, pos, bFromDoc); } // Wait工具 else if (pNewTool->isWaitTool()) { return this->addWaitItem(pNewTool, pos, bFromDoc); } else { // Error Here qDebug() << "[ERROR] WindowAppPouScene::addNewItem - Unknown tool type :" << (int)pNewTool->Type; return nullptr; } } /// /// 移除一个ToolItem /// /// void WindowAppPouScene::delToolItem(WindowAppBlockBase* pBlock) { // 确保指针有效 if (pBlock == nullptr) { vDebug() << "[Error] BlockBase is nullptr."; return; } // 检查是否可以被删除 if (!pBlock->couldBeDeleted()) { return; } // 2022-10-2,这里只处理去Task和Pou处处理标准工具,跳过其他工具 // NOTICE:之所以这里会出现这个问题,是因为之前只有StandardBase工具会走这里删除, // MEMORY if (pBlock->m_toolInfo->isIndexedTool()) { // 2022-3-5增加,如果此工具的Pou被Task选中,需要通知Task删除此工具 if (m_Pou.isSelByTask()) { WindowAppTaskView* pTaskView = g_pTaskManager->getTaskViewByName(m_Pou.m_pParentTask->strName); pTaskView->onDelPouTool(&m_Pou, pBlock->m_toolInfo); } } // 2022-10-2,除了Comment工具,其他的工具都需要到Pou中删除本工具的数据信息 // 逻辑数据删除 m_Pou.ToolDelete(pBlock); // 从界面中移除此功能块 this->removeItem(pBlock); } /// /// 添加一个标准工具 /// /// /// WindowAppBlockStandard* WindowAppPouScene::addStandardItem(TOOL* pNewTool, QPointF pos, bool bFromDoc) { // 2022-3-31 如果从序列化中恢复,则不需要重新设置索引和实例名称了 if (!bFromDoc) { // 初始化本功能块 this->initBlockItem(pNewTool, true, true, true); // TODO:2022-9-19,如果此处工具从序列化中恢复,要检查工具Event类型的接口是否正常初始化了?一般都会绑定到Task中 } // 加载dll,取得指针 pNewTool->pDllPtr = toolDepository.GetToolPtr(pNewTool->strName); if (pNewTool->pDllPtr == nullptr) { vDebug() << "[Error] [" << pNewTool->strName << "], but pDllPtr is nullptr "; return nullptr; } // 2022-10-4,保存一下本Dll的全路径信息,供后续查询需要 pNewTool->pDllPtr->m_strFullPath = toolDepository.GetDllPathByName(pNewTool->strName); try { // 工具初始化(带值初始化 withValue ) pNewTool->pDllPtr->InitTool( (WindowAppPouFrame*)this->parent(), m_Pou.pouName(), pNewTool->strInstanceName, (QObject*)g_pTaskManager ); //QFuture future = QtConcurrent::run( // pNewTool->pDllPtr, // &DllTool::InitTool, // (WindowAppPouFrame*)this->parent(), // m_Pou.pouName(), // pNewTool->strInstanceName, // (QObject*)g_pTaskManager // ); //future.waitForFinished(); } catch (...) { qWarning() << "[Error] WindowAppPouScene::addStandardItem - " << m_Pou.pouName() << " init Tool" << pNewTool->strInstanceName << " failed."; } //// 2021-8-5 设置,为dll设置event的传递目标(TaskManager) //pNewTool->pDllPtr->setEventTarget((QObject*)g_pTaskManager); // 2022-3-3增加,设置GvlManager指针,用于和DB类型的全局变量做交互 pNewTool->pDllPtr->setGvlTarget((QObject*)g_pGvlManager); // 2022-3-10 增加,设置WindowAppPouScene指针,用于同步dll侧发来的动态端口同步消息 pNewTool->pDllPtr->setPouTarget(this); //// 2021-8-3 增加,此处从dll获取刚生成的变量值 //// (仅限于Hardware类型的Event接口,但是其实也可以不保存在exe中,每次都从dll中拿,两种方案都可以) //for (int i = 0; i < pNewTool->Interfaces.size(); i++) //{ // pNewTool->Interfaces[i]->value.Ptr = pNewTool->pDllPtr->Interface(i).value.Ptr; //} // 根据对应的工具信息绘制对应的功能块 WindowAppBlockStandard* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_STANDARD)); return pNewBlock; } /// /// 添加一个Port工具 /// /// /// WindowAppBlockPort* WindowAppPouScene::addPortItem(TOOL* pNewPort, QPointF pos, bool bFromDoc) { // 2022-3-31 如果从序列化中恢复,则不需要重新设置实例名称和接口名称了 if (!bFromDoc) { //// 需要根据现有工具情况生成新的实例名字 //pNewPort->strInstanceName = m_Pou.genToolInstanceName(pNewPort->strName); // 初始化本功能块 this->initBlockItem(pNewPort, false, true, false); // 2022-3-23,需要为接口补充上全名,用于在Pou中建立对应关系 if (pNewPort->Type == TOOL_TYPE::TOOL_TYPE_PORT_INPUT) { pNewPort->Interfaces[0]->strFullName = pNewPort->strPouName + "." + pNewPort->strInstanceName + "." + DEFAULT_INPUT_PORT_NAME; } else if (pNewPort->Type == TOOL_TYPE::TOOL_TYPE_PORT_OUTPUT) { pNewPort->Interfaces[0]->strFullName = pNewPort->strPouName + "." + pNewPort->strInstanceName + "." + DEFAULT_OUTPUT_PORT_NAME; } else { qDebug() << "WindowAppPouScene::addPortItem - [Error] Invalid toolType of Port[" << pNewPort->strInstanceName << "."; } } // 根据对应的工具信息绘制对应的功能块 WindowAppBlockPort* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewPort, pos, TOOL_TYPE::TOOL_TYPE_PORT_INPUT)); return pNewBlock; } /// /// 添加一个Goto工具 /// /// /// /// WindowAppBlockGoto* WindowAppPouScene::addGotoItem(TOOL* pNewTool, QPointF pos, bool bFromDoc) { // 2022-3-31 如果从序列化中恢复,则不需要重新设置索引和实例名称了 if (!bFromDoc) { // 初始化本功能块 this->initBlockItem(pNewTool, true, true, false); //// 计算分配的Index //pNewTool->nIndex = m_Pou.GetIndexedToolsCount(); //// 需要根据现有工具情况生成新的实例名字 //pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName)); } // 根据对应的工具信息绘制对应的功能块 WindowAppBlockGoto* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_GOTO)); return pNewBlock; } /// /// 添加一个Comment工具 /// /// /// /// /// WindowAppBlockComment* WindowAppPouScene::addCommentItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/) { // 2022-3-31 如果从序列化中恢复,则不需要重新设置实例名称了 if (!bFromDoc) { // 初始化本功能块 this->initBlockItem(pNewTool, false, true, false); //// 需要根据现有工具情况生成新的实例名字 //pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName)); } // 根据对应的工具信息绘制对应的功能块 WindowAppBlockComment* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_COMMENT)); return pNewBlock; } /// /// 添加一个Parallel工具 /// /// /// /// /// WindowAppBlockParallel* WindowAppPouScene::addParallelItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/) { // 如果从序列化中恢复,则不需要重新设置索引和实例名称了 if (!bFromDoc) { // 初始化本功能块 this->initBlockItem(pNewTool, true, true, true); //// 计算分配的Index //pNewTool->nIndex = m_Pou.GetIndexedToolsCount(); //// 需要根据现有工具情况生成新的实例名字 //pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName)); //// 2022-4-27,为Parallel的ToolInterface命名 //pNewTool->startInterface->strName = pNewTool->strInstanceName; //pNewTool->startInterface->strFullName = m_Pou.pouName() + "." + pNewTool->strInstanceName + ".Start"; //pNewTool->endInterface->strName = pNewTool->strInstanceName; //pNewTool->endInterface->strFullName = m_Pou.pouName() + "." + pNewTool->strInstanceName + ".End"; } // 根据对应的工具信息绘制对应的功能块 WindowAppBlockParallel* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_PARALLEL)); return pNewBlock; } /// /// 添加一个ForLoop工具 /// /// /// /// /// WindowAppBlockForloop* WindowAppPouScene::addForloopItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/) { // 如果从序列化中恢复,则不需要重新设置索引和实例名称了 if (!bFromDoc) { // 初始化本功能块 this->initBlockItem(pNewTool, true, true, true); } // 根据对应的工具信息绘制对应的功能块 WindowAppBlockForloop* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_FORLOOP)); return pNewBlock; } /// /// 添加一个Wait工具 /// /// /// /// /// WindowAppBlockWait* WindowAppPouScene::addWaitItem(TOOL* pNewTool, QPointF pos, bool bFromDoc /*= false*/) { // 如果从序列化中恢复,则不需要重新设置索引和实例名称了 if (!bFromDoc) { // 初始化本功能块 this->initBlockItem(pNewTool, true, true, false); } // 根据对应的工具信息绘制对应的功能块 WindowAppBlockWait* pNewBlock = qgraphicsitem_cast(this->createBlock(pNewTool, pos, TOOL_TYPE::TOOL_TYPE_WAIT)); return pNewBlock; } /// /// 创建功能块 /// /// /// /// /// /// WindowAppBlockBase* WindowAppPouScene::createBlock( TOOL* pTool, const QPointF& pos, TOOL_TYPE toolType ) { WindowAppBlockBase* newItem = nullptr; // 根据种类创建对应的控件 switch (toolType) { case TOOL_TYPE::TOOL_TYPE_STANDARD: newItem = new WindowAppBlockStandard(pTool, &m_Pou); break; case TOOL_TYPE::TOOL_TYPE_PORT_INPUT: case TOOL_TYPE::TOOL_TYPE_PORT_OUTPUT: newItem = new WindowAppBlockPort(pTool, &m_Pou); break; case TOOL_TYPE::TOOL_TYPE_GOTO: newItem = new WindowAppBlockGoto(pTool, &m_Pou); break; case TOOL_TYPE::TOOL_TYPE_COMMENT: newItem = new WindowAppBlockComment(pTool, &m_Pou); break; case TOOL_TYPE::TOOL_TYPE_PARALLEL: newItem = new WindowAppBlockParallel(pTool, &m_Pou); break; case TOOL_TYPE::TOOL_TYPE_FORLOOP: newItem = new WindowAppBlockForloop(pTool, &m_Pou); break; case TOOL_TYPE::TOOL_TYPE_WAIT: newItem = new WindowAppBlockWait(pTool, &m_Pou); break; default: { qDebug() << "[Error] WindowAppPouScene::createBlock - Unknown tooltype: " << (short)toolType; return nullptr; } } // 根据控件类型的不同绑定不同的消息 if (toolType != TOOL_TYPE::TOOL_TYPE_COMMENT) { // 绑定移动信号用于移动功能块时同步移动link连线 connect( newItem, &WindowAppBlockBase::blockMoveSignal, this, &WindowAppPouScene::onBlockMove ); } else { // 绑定信号用于注释控件内容为空时,删除控件 connect( qgraphicsitem_cast(newItem), &WindowAppBlockComment::emptyContent, this, &WindowAppPouScene::removeEmptyBlockComment ); } // 置于最顶层 newItem->setZValue(Z_ORDER_BLOCK); // Scene中添加功能块 addItem(newItem); // 设置位置 newItem->setPos(pos); // 添加功能块接口 newItem->addItemInterfaces(); // 将新的工具信息和功能块信息绑定保存 m_Pou.registerTool(newItem, pTool); return newItem; } /// /// 直接增加一个Port,并且连接 /// /// void WindowAppPouScene::addPortAndAutolink(WindowAppItemInterface* pLinkInf, const QLineF infRealLine) { // 从工具库中获取Port工具原型 const STATIC_TOOL* portTool = nullptr; // 根据接口的输入输出获取不同的port工具,并且根据方向计算一下Port应该出现的中心点位置 QPointF posPort; posPort.setY(infRealLine.y1() - PBD_BASIC_HEIGHT / 2); if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT) { portTool = toolDepository.GetInputPort(); posPort.setX(infRealLine.x2() + PBD_BASIC_WIDTH + AUTOPORT_SPACING + TBD_INF_LINE ); } else { portTool = toolDepository.GetOutputPort(); posPort.setX(infRealLine.x1() - PBD_BASIC_WIDTH - AUTOPORT_SPACING - TBD_INF_LINE); } // 确保工具有效 if (portTool == nullptr) { return; } // 确保输入端口没被引用过 if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_IN && pLinkInf->m_infInfo->nRefCount > 0 ) { return; } // 确保输出端口没被引用过 (暂时限制输出多次添加数据端口,以后有必要的时候再放开) if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT && pLinkInf->m_infInfo->nRefCount > 0) { return; } // 添加这个port工具 TOOL* portRunning = new TOOL(portTool); // 2021-7-5添加,需要保存所属的Group portRunning->strPouName = m_strPouName; // 2021-05-26添加,双击添加的Port工具需要根据link的接口指定类型 portRunning->Interfaces[0]->value.type = pLinkInf->m_infInfo->value.type; WindowAppBlockPort* pNewPortItem = this->addPortItem(portRunning, posPort); // 并且将两个接口进行连接(输出->输入) if (pLinkInf->m_infInfo->Direction == INF_DIRECTION::INF_DIR_OUT) { addLink(pLinkInf, pNewPortItem->m_itemInterfaces[0]); } else { addLink(pNewPortItem->m_itemInterfaces[0], pLinkInf); } } /// /// 鼠标按下时 /// /// void WindowAppPouScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) { //qDebug() << "WindowAppDiagramScene::mousePressEvent"; // 如果鼠标按下的位置是接口的话,则切换进入Link模式 QTransform transform; // 2022-1-8 测试点击的是接口还是功能块 WindowAppItemInterface* activeItem = qgraphicsitem_cast(this->itemAt(mouseEvent->scenePos(), transform)); WindowAppBlockStandardBase* activeBlock = qgraphicsitem_cast(this->itemAt(mouseEvent->scenePos(), transform)); // 如果点击的是接口,并且是输出接口的话,那么执行link if ( activeItem!=nullptr && activeItem->m_infInfo->Direction== INF_DIRECTION::INF_DIR_OUT ) { // 如果是硬件组态,则不执行Link相关的所有操作(包括批量选中移动后的特殊处理) if (m_strPouName == GROUP_NAME_HARDWARE) { QGraphicsScene::mousePressEvent(mouseEvent); return; } // 开始执行Link动作 startLink(activeItem, mouseEvent->scenePos()); // 输出Debug数据 QString strValue = activeItem->m_infInfo->getValueString(); DebugData data; data.addLog(activeItem->m_infInfo->strName, strValue); m_pPouFrame->UpdataDebugData(data); } // 2022-6-20,增加了对批量功能块移动的处理 // 只有标准系列的功能块使用 else if (activeBlock !=nullptr && activeBlock->isStandardBasedBlock()) { // 2022-1-8,如果点击的是标准功能块,则调用UpdataDebugData if (activeBlock->type()== ITEM_TYPE_STANDARD) { // 显示DeBug信息 try { DebugData data = m_Pou.GetToolDebugData(activeBlock); m_pPouFrame->UpdataDebugData(data); } catch (...) { } } // 如果是硬件组态,则不执行Link相关的所有操作(包括批量选中移动后的特殊处理) if (m_strPouName == GROUP_NAME_HARDWARE) { QGraphicsScene::mousePressEvent(mouseEvent); return; } // 检查是否正在是多选之后的批量移动操作 this->checkBatchMove(); } QGraphicsScene::mousePressEvent(mouseEvent); } /// /// 控制功能块的移动以及连线 /// /// void WindowAppPouScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) { // 如果处于Link模式,则实时绘制连线 if (m_sceneMode == SCENE_MODE::LINK_MODE && m_tmpLinkLine!=nullptr ) { bool blink = false; WindowAppItemInterface* startItem = nullptr; WindowAppItemInterface* endItem = nullptr; // 执行连接预检查 改变鼠标的形态 int nStrte = preLinkCheck(mouseEvent->scenePos(), startItem, endItem); if (nStrte == 0) { m_pPouFrame->setCursor(Qt::UpArrowCursor);// ok // 自定义图标 //m_pPouView->setCursor(QCursor(QPixmap(":/image/tree_item.png"))); blink = true; } else { m_pPouFrame->setCursor(Qt::ArrowCursor); } moveLink(mouseEvent->scenePos(), blink); } // 批量移动模式 else if(m_sceneMode == SCENE_MODE::BATCHMOVE_MODE) { // 暂时不需要做什么 } // qDebug() << "WindowAppPouScene::mouseMoveEvent"; QGraphicsScene::mouseMoveEvent(mouseEvent); } /// /// 控制功能块的移动以及生成连线 /// /// void WindowAppPouScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) { //qDebug() << "WindowAppDiagramScene::mouseReleaseEvent"; // 如果正处于Link模式 if (m_sceneMode == SCENE_MODE::LINK_MODE) { // 结束link操作 endLink(mouseEvent->scenePos()); } // 如果正处于批量移动模式 else if (m_sceneMode == SCENE_MODE::BATCHMOVE_MODE) { // 取消所有选中Link的Movable状态 for (auto& linkItem : m_batchMoveLinks) { linkItem->setMovable(false); } // 清空链表 m_batchMoveLinks.clear(); } // 切换回正常模式 m_sceneMode = SCENE_MODE::NORMAL_MODE; // 恢复鼠标 m_pPouFrame->setCursor(Qt::ArrowCursor); QGraphicsScene::mouseReleaseEvent(mouseEvent); } /// /// 2022-6-20,检查是否正在是多选之后的批量移动操作 /// bool WindowAppPouScene::checkBatchMove() { // 获取当前选中的Item QList selItems = this->selectedItems(); QList selBlocks; // 如果选中的 if (selItems.size() <= 1) { return false; } // 取出其中所有的Block for (const auto& selItem : selItems) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock() ) { selBlocks.push_back(block); } } // 如果选中Block的数量大于2,则说明在做批量拖动 if (selBlocks.size() >= 2) { m_sceneMode = SCENE_MODE::BATCHMOVE_MODE; qDebug() << "WindowAppPouScene::checkBatchMove() - SCENE_MODE::BATCHMOVE_MODE"; } else { m_sceneMode = SCENE_MODE::NORMAL_MODE; return false; } // 取出其中每一个功能块之间的Link,将Link状态设置为Movable,可随Block随动 for (const auto& selBlock : selBlocks) { for (const auto& selBlock2 : selBlocks) { if (selBlock2 != selBlock) { // 判断两个Block之间是否存在Link QList linkItems = m_Pou.getLinkItemsBetweenBlocks(selBlock, selBlock2); qDebug() << "WindowAppPouScene::checkBatchMove() - " << linkItems.size() << " link items between [" << selBlock->m_toolInfo->strInstanceName << "] [" << selBlock2->m_toolInfo->strInstanceName << "]."; // 将这些Link都设置为Movable,并且保存下来后续使用 for (auto& linkItem : linkItems) { //linkItem->setFlag(QGraphicsItem::ItemIsMovable, true); linkItem->setMovable(true); // 并且设置为选中 linkItem->setSelected(true); m_batchMoveLinks.push_back(linkItem); } } } } return true; } /// /// 2022-8-26,添加完功能块Item后,初始化对应参数(Index、InstanceName、ToolInterface Name等) /// /// 工具引用 /// 是否需要初始化功能块Index /// 是否需要初始化功能块实例名称 /// 是否需要初始化功能块Tool接口名字 void WindowAppPouScene::initBlockItem(TOOL*& pNewTool, bool bInitIndex, bool bInitInstName, bool bInitToolInfName) { if (bInitIndex) { // 计算分配的Index pNewTool->nIndex = m_Pou.GetIndexedToolsCount(); } if (bInitInstName) { // 需要根据现有工具情况生成新的实例名字 pNewTool->updateInstanceName(m_Pou.genToolInstanceName(pNewTool->strName)); } if (bInitToolInfName) { // 2022-8-25,为工具的Tool接口命名 pNewTool->ToolInterfaces[INF_START]->strName = pNewTool->strInstanceName + ".Start"; pNewTool->ToolInterfaces[INF_START]->strFullName = m_Pou.pouName() + "." + pNewTool->ToolInterfaces[INF_START]->strName; pNewTool->ToolInterfaces[INF_END]->strName = pNewTool->strInstanceName + ".End"; pNewTool->ToolInterfaces[INF_END]->strFullName = m_Pou.pouName() + "." + pNewTool->ToolInterfaces[INF_END]->strName; } } /// /// 功能块序号 减 1 /// void WindowAppPouScene::BlockMoveUp() { // 防止功能块被多选 QList selItems = this->selectedItems(); if (selItems.size() > 1) { return; } QGraphicsItem* selItem = this->selectedItems().first(); if (selItem != nullptr) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { block->onBlockMoveUp(); } } } /// /// 功能块序号 加 1 /// void WindowAppPouScene::BlockMoveDown() { // 防止功能块被多选 QList selItems = this->selectedItems(); if (selItems.size() > 1) { return; } QGraphicsItem* selItem = this->selectedItems().first(); if (selItem != nullptr) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { block->onBlockMoveDown(); } } } /// /// 功能块序号 置1 /// void WindowAppPouScene::BlockMoveFirst() { // 防止功能块被多选 QList selItems = this->selectedItems(); if (selItems.size() > 1) { return; } QGraphicsItem* selItem = this->selectedItems().first(); if (selItem != nullptr) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { block->onBlockMoveFirst(); } } } /// /// 功能块序号 置底 /// void WindowAppPouScene::BlockMoveLast() { // 防止功能块被多选 QList selItems = this->selectedItems(); if (selItems.size() > 1) { return; } QGraphicsItem* selItem = this->selectedItems().first(); if (selItem != nullptr) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { block->onBlockMoveLast(); } } } /// /// 功能块左对齐 /// void WindowAppPouScene::BlockAlignLeft() { // 获取当前选中的Item QList selItems = this->selectedItems(); QList selBlocks; // 如果选中的 if (selItems.size() <= 1) { return ; } // 取出其中所有的Block for (const auto& selItem : selItems) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { selBlocks.push_back(block); } } // 确保选中的功能块数量大于 1 if (selBlocks.size() <= 1) { return; } // 获取最左侧的工具坐标 int nLeftValue = INT_MAX; for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); int x = point.x() - rect.width() / 2; if (nLeftValue > x) { nLeftValue = x; } } // 设置选中的功能块坐标 for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); point.setX(nLeftValue + rect.width()/2 ); selBlocks[i]->setPos(point); } } /// /// 功能块顶对齐 /// void WindowAppPouScene::BlockAlignTop() { // 获取当前选中的Item QList selItems = this->selectedItems(); QList selBlocks; // 如果选中的 if (selItems.size() <= 1) { return; } // 取出其中所有的Block for (const auto& selItem : selItems) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { selBlocks.push_back(block); } } // 确保选中的功能块数量大于 1 if (selBlocks.size() <= 1) { return; } // 获取最顶侧的工具坐标 int nTopValue = INT_MAX; for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); int y = point.y() - rect.height() / 2; if (nTopValue > y) { nTopValue = y; } } // 设置选中的功能块坐标 for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); point.setY(nTopValue + rect.height() / 2); selBlocks[i]->setPos(point); } } /// /// 功能块右对齐 /// void WindowAppPouScene::BlockAlignRight() { // 获取当前选中的Item QList selItems = this->selectedItems(); QList selBlocks; // 如果选中的 if (selItems.size() <= 1) { return; } // 取出其中所有的Block for (const auto& selItem : selItems) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { selBlocks.push_back(block); } } // 确保选中的功能块数量大于 1 if (selBlocks.size() <= 1) { return; } // 获取最右侧的工具坐标 int nRightValue = 0; for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); int x = point.x() + rect.width() / 2; if (nRightValue < x) { nRightValue = x; } } // 设置选中的功能块坐标 for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); point.setX(nRightValue - rect.width() / 2); selBlocks[i]->setPos(point); } } /// /// 功能块底对齐 /// void WindowAppPouScene::BlockAlignBottom() { // 获取当前选中的Item QList selItems = this->selectedItems(); QList selBlocks; // 如果选中的 if (selItems.size() <= 1) { return; } // 取出其中所有的Block for (const auto& selItem : selItems) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { selBlocks.push_back(block); } } // 确保选中的功能块数量大于 1 if (selBlocks.size() <= 1) { return; } // 获取最底侧的工具坐标 int nBottomValue = 0; for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); int y = point.y() + rect.height() / 2; if (nBottomValue < y) { nBottomValue = y; } } // 设置选中的功能块坐标 for (int i = 0; i < selBlocks.size(); i++) { QPointF point = selBlocks[i]->scenePos(); QRectF rect = selBlocks[i]->boundingRect(); point.setY(nBottomValue - rect.height() / 2); selBlocks[i]->setPos(point); } } //================================================================= // // Link相关 // //================================================================= /// /// 开始执行Link动作 /// void WindowAppPouScene::startLink(WindowAppItemInterface* pStartInf, QPointF ptMouse) { // 切换模式 this->m_sceneMode = SCENE_MODE::LINK_MODE; // 连线的起点 QPointF ptStart; // 2022-4-24,根据起点接口的类型,分别进行处理 // 普通功能块接口 if (!pStartInf->m_infInfo->isParallelToolEnd()) { ptStart = pStartInf->mapToScene(pStartInf->line().p2()); } // Parallel输出接口 else { ptStart = ptMouse; } // 建立临时的连接线 m_tmpLinkLine = new QGraphicsLineItem(QLineF(ptStart, ptMouse)); m_tmpLinkLine->setPen(QPen(LINK_TMPLINE_COLOR, LINK_LINE_WIDTH)); this->addItem(m_tmpLinkLine); } /// /// 移动Link连接 /// /// void WindowAppPouScene::moveLink(QPointF ptMouse, bool blink) { QLineF newLine(m_tmpLinkLine->line().p1(), ptMouse); m_tmpLinkLine->setLine(newLine); if (blink) { m_tmpLinkLine->setPen(QPen(LINK_LINE_COLOR, LINK_LINE_WIDTH)); } else { QVector dashes; dashes << 5 << 5 << 5 << 5; QPen pen(QPen(LINK_LINE_COLOR, LINK_LINE_WIDTH)); pen.setDashPattern(dashes); m_tmpLinkLine->setPen(pen); } // 增加了重绘,防止线条覆盖住接口的情况 this->update(); } /// /// 结束Link动作 /// /// void WindowAppPouScene::endLink(QPointF ptMouse) { WindowAppItemInterface* startItem = nullptr; WindowAppItemInterface* endItem = nullptr; // 执行连接预检查,如果检查通过了再建立连接 int nState = preLinkCheck(ptMouse, startItem, endItem); if (nState == 0) { Q_ASSERT(startItem != nullptr); Q_ASSERT(endItem != nullptr); // 此处判断一下是否是Goto->Tool的连接 if (startItem->m_infInfo->isGotoToolEnd()) { // 建立Goto到Tool之间的Link this->addGotoLink(startItem->m_infInfo, endItem->m_infInfo->parent()); } // 继续判断一下是否是Parallel->Tool的连接 else if (startItem->m_infInfo->isParallelToolEnd() && endItem->m_infInfo->isStandardToolStart()) { // 建立Parallel到Tool之间的Link this->addParallelLink(startItem->m_infInfo, endItem->m_infInfo->parent()); } // 建立标准接口间的link else { addLink(startItem, endItem, LINK_MODE::LINK_NORMAL); } } // 无论成功与否都清除掉临时连接线 removeItem(m_tmpLinkLine); RELEASE(m_tmpLinkLine); } /// /// 进行连接预检查 /// /// /// int WindowAppPouScene::preLinkCheck(QPointF ptMouse, WindowAppItemInterface*& startItem, WindowAppItemInterface*& endItem) { // 先把多余的起点和终点区域的Item去掉,只留下接口 // MENTION:此处稍微偏移一些,为了更准确的选中接口 QPointF startPoint = QPointF(m_tmpLinkLine->line().p1().x() - 2, m_tmpLinkLine->line().p1().y()); QList startItems = items(startPoint); if (startItems.count() && startItems.first() == m_tmpLinkLine) { startItems.removeFirst(); } QList endItems = items(ptMouse); if (endItems.count() && endItems.first() == m_tmpLinkLine) { endItems.removeFirst(); } // 去除多余Item后,是否还有有效的接口信息 if (startItems.count() <= 0 || endItems.count() <= 0) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem or endItem count <=0."; return -1; } // 从集合的第一个Item中取出需要link的item startItem = qgraphicsitem_cast(startItems.first()); endItem = qgraphicsitem_cast(endItems.first()); if (startItem == nullptr || endItem == nullptr) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem or endItem is nullptr."; return -2; } // 取出其中的接口信息 const _INTERFACE* startInf = startItem->m_infInfo; const _INTERFACE* endInf = endItem->m_infInfo; // 此处增加一个有效性检查 if (startItem == nullptr) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem is not a WindowAppItemInterface."; return -3; } if (endItem == nullptr) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is not a WindowAppItemInterface."; return -4; } // 检查是否是同一个item if (startItem == endItem) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem is same as endItem."; return -5; } if (startItem->m_infInfo->parent() == endItem->m_infInfo->parent()) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem's parent Tool is same as endItem."; return -6; } // 检查起点是否是有效的接口类型 if (startItem->type() != WindowAppItemInterface::Type) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem is not interface item."; return -7; } // 检查起点是否是有效的接口类型 if (endItem->type() != WindowAppItemInterface::Type) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is not interface item."; return -8; } // 检查终点是否是输入接口 if (endInf->Direction != INF_DIRECTION::INF_DIR_IN) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is not input interface."; return -9; } // 检查终点接口是否已经有连接了 if (endInf->pUpLinkInterface != nullptr) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - endItem is already linked."; return -10; } // 检查起点和终点接口的数值类型是否一致(并且所有基础类型算作是相同类型) if (!endInf->isSameTypeTo(startInf, true)) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - startItem and endItem is not the same value type."; return -11; } // 2022-6-7,增加检查项,如果是Tool类型接口,还需要额外检查 // 本接口目前不允许父功能块是同类型的连接,比如Goto->Goto,Tool->Tool,Parallel->Parallel等等 if (startInf->isToolEnd() && endInf->isParentSameTypeTo(startInf)) { // qDebug() << "[Error] WindowAppPouScene::preLinkCheck - Top output can't link to the same tool type."; return -12; } // 通过所有检查项,执行Link qDebug() << "[OK] WindowAppPouScene::preLinkCheck passed. start:" << startItem->m_infInfo->strFullName << " | end:" << endItem->m_infInfo->strFullName; return 0; } /// /// 增加一个Link /// /// /// /// link的模式(普通/并行) /// 本次link各线段的坐标(用于反序列化) void WindowAppPouScene::addLink( WindowAppItemInterface* pStartInf, WindowAppItemInterface* pEndInf, LINK_MODE linkMode, QVector linePoints ) { WindowAppItemLink* newLinkItem = new WindowAppItemLink(pStartInf, pEndInf, linkMode, linePoints); // 置于最底层 newLinkItem->setZValue(Z_ORDER_LINK); this->addItem(newLinkItem); // newLink->updatePosition(); // 保存Block与Link的关联信息,用于link对Block随动 ITEM_LINK_INFO newInfo; newInfo.bStart = true; newInfo.pLink = newLinkItem; m_itemBlockLinks.insertMulti(pStartInf->parentItem(), newInfo); newInfo.bStart = false; m_itemBlockLinks.insertMulti(pEndInf->parentItem(), newInfo); // 保存接口与Link的关联信息 m_itemInfLinks.insertMulti(pStartInf, newLinkItem); m_itemInfLinks.insertMulti(pEndInf, newLinkItem); // 存储link相关数据结构 m_Pou.makeLink(pStartInf, pEndInf, newLinkItem, linkMode); } /// /// 建立Link(根据接口全名) /// /// /// /// link的模式(普通/并行) /// 本次link各线段的坐标(用于反序列化 void WindowAppPouScene::addLink( const QString& strStartInf, const QString& strEndInf, LINK_MODE linkMode, QVector linePoints ) { // 获取到对应的接口 WindowAppItemInterface* startInf = m_Pou.GetInterfaceItemByName(strStartInf); WindowAppItemInterface* endInf = m_Pou.GetInterfaceItemByName(strEndInf); // Error if (startInf == nullptr || endInf == nullptr) { qDebug() << "[Error] WindowAppPouScene::addLink - startInf is nullptr or endInf is nullptr."; return; } // 建立Link this->addLink(startInf, endInf, linkMode, linePoints); } ///// ///// 建立Link(根据Link结构体) ///// ///// //void WindowAppPouScene::addLink(const LINK& linkInfo) //{ // this->addLink( // linkInfo.strSrcInf, // linkInfo.strDesInf, // linkInfo.linkMode, // linkInfo.lines // ); //} /// /// 删除指定link连线 /// /// void WindowAppPouScene::delLink(WindowAppItemLink* pLinkItem) { WindowAppItemInterface* startInfItem = pLinkItem->startItem(); WindowAppItemInterface* endInfItem = pLinkItem->endItem(); // 相关的起点和终点的引用计数 - 1 //m_PouManager.MinusInterfaceRefCount(startInfItem); //m_PouManager.MinusInterfaceRefCount(endInfItem); startInfItem->m_infInfo->nRefCount--; endInfItem->m_infInfo->nRefCount--; // 删除pou中的link信息 m_Pou.removeLink(endInfItem); // 删除itemBlockLinks中的信息 // 2022-10-19 修正,此处需要精细判断一下具体删除哪个BlockLink,否则一个输出接口连接至多个接口时 // 此处的多条信息会被一次性删除导致bug ITEM_LINK_INFO delInfo; delInfo.bStart = true; delInfo.pLink = pLinkItem; m_itemBlockLinks.remove(startInfItem->parentItem(), delInfo); delInfo.bStart = false; delInfo.pLink = pLinkItem; m_itemBlockLinks.remove(endInfItem->parentItem(), delInfo); // 删除itemInfLinks中的信息 m_itemInfLinks.remove(pLinkItem->startItem(), pLinkItem); m_itemInfLinks.remove(pLinkItem->endItem(), pLinkItem); // 2022-5-6,如果是 qDebug() << "[SCENE][LINK] Delete link between [" << startInfItem->m_infInfo->strFullName << "]->[" << endInfItem->m_infInfo->strFullName << "]."; // 最后彻底清空此连线item delete pLinkItem; pLinkItem = nullptr; } /// /// 根据接口删除link连线 /// /// void WindowAppPouScene::delLink(WindowAppItemInterface* pInf) { // 首先通过接口快速查询到对应的link连线(有可能会有多个) QList pLinkItems = m_itemInfLinks.values(pInf); // 如果查询不到,则说明此接口并没有连线 if ( pLinkItems.size()<=0 ) { qDebug() << "[SCENE][LINK] Delete link error: can not find link of interface[" << pInf->m_infInfo->strFullName << "]."; return; } // 然后分别清除每一个对应接口的信息 for (WindowAppItemLink * pLinkItem : pLinkItems) { this->delLink(pLinkItem); } } /// /// 根据接口名字删除link连线 /// /// void WindowAppPouScene::delLink(const QString& strInf) { WindowAppItemInterface* infItem = m_Pou.GetInterfaceItemByName(strInf); this->delLink(infItem); } /// /// 执行SmartLink(找离本接口最近的同类型输出端口,序号小于自己的) /// /// void WindowAppPouScene::smartLink(WindowAppItemInterface* pInf) { // 在pou中找到可以执行smartLink的接口 WindowAppItemInterface* pLinkInfItem = m_Pou.getSmartLinkInterface(pInf); if (pLinkInfItem == nullptr) { Utility::VPCriticalMessageBox("Not find corresponding smartlink interface!"); return; } // 如果成功找到了的话,则连接 addLink(pLinkInfItem, pInf); } /// /// 建立Tool间的Link(Goto到Tool的link) /// /// /// void WindowAppPouScene::addGotoLink(_INTERFACE* pGotoInf, const TOOL* pTool) { //// 首先检查目标Tool是否已经有连接 //if (pTool->isTopLinked()) //{ // qWarning() << "Tool[" + pTool->strInstanceName + "] is already linked!"; // return; //} // 如果本Goto之前绑定过Tool,则首先清除之前的link,准备绑定新Tool if (pGotoInf->pBindInterface != nullptr) { this->delLink(pGotoInf->strFullName); } // 绑定到目标Tool的ToolStart接口中 pGotoInf->pBindInterface = pTool->startInterface; // 设置接口的名称为目标Tool的实例名字 pGotoInf->strName = pTool->strInstanceName; // 绑定完毕之后,需要把输出接口的显示打开,用于显示绑定目标的名字 pGotoInf->bShowName = true; // 界面增加Link信息 QString strSrc = pGotoInf->strFullName; QString strDst = pTool->startInterface->strFullName; this->addLink(strSrc, strDst); } /// /// 建立Parallel到Tool的Link /// /// /// void WindowAppPouScene::addParallelLink(_INTERFACE* pParaInf, const TOOL* pTool) { // 界面增加Link信息 QString strSrc = pParaInf->strFullName; QString strDst = pTool->startInterface->strFullName; this->addLink(strSrc, strDst, LINK_MODE::LINK_PARALLEL); // Parallel Link建立完毕之后需要立即确认一下并行连线的位置,有可能默认位置已经超出了并行连线的长度 WindowAppItemInterface* startInf = m_Pou.GetInterfaceItemByName(strSrc); startInf->updatePostion(); } /// /// block被移动时,需要同时更新link连线的位置 /// /// void WindowAppPouScene::onBlockMove(QGraphicsItem* item) { // 更新本Block关联的Link连线 // qDebug() << "WindowAppDiagramScene::onBlockMove."; // TODO:为了修正一个在ShowOnly的情况下多次点击接口右键会崩溃的问题,临时处理 if (item == nullptr) { qDebug() << "[POU][Error] WindowAppPouScene::onBlockMove - but item is nullptr."; return; } // 2022-6-15, 如果处于批量拖动模式下,则不需要重绘Link线段,直接平移 // 2022-7-2 修正,此处不能分开处理,否则Link连线会出现断裂的情况 //if (sceneMode != SCENE_MODE::BATCHMOVE_MODE) //{ // 查找这个移动的Block是否有对应的link需要重绘 this->updateLinkItemsByBlock(item); // 2022-5-8增加,如果是并行组相关的功能块移动时,还需要检查其所属的并行母线是否需要延长或者缩短 this->updateParallelLineByBlock(item); //} } /// /// 检查所有的接口连线是否需要刷新 /// /// void WindowAppPouScene::updateLinkItemsByBlock(QGraphicsItem* item) { QList linkInfos = m_itemBlockLinks.values(item); if (linkInfos.size() > 0) { // 自动化方式移动 //for (WindowAppItemLink* pLink : links) //{ // 更新对应Link的位置 // pLink->updateLinkLines(); //} // 手动方式移动 for (auto& linkInfo : linkInfos) { // 根据移动的功能块调整对应的Link连线 linkInfo.pLink->updateLinkLinesManual(item, linkInfo.bStart); } // 重绘界面 this->update(); } } /// /// 检查并行母线是否需要刷新 /// /// void WindowAppPouScene::updateParallelLineByBlock(QGraphicsItem* item) { WindowAppBlockStandard* pStardardBlock = qgraphicsitem_cast(item); if (pStardardBlock == nullptr || !pStardardBlock->m_toolInfo->isParallelSubTool()) { return; } // 找到本工具所属的并行工具组 TOOL* pParaTool = pStardardBlock->m_toolInfo->toolStartUpTool(); // 错误,不应该为空 // Reason:(如果此处不加判断的话,反序列化的时候由于此时工具信息尚未恢复完毕,所以执行到此处的时候会崩溃) if (pParaTool == nullptr) { qDebug() << "[Error] WindowAppPouScene::updateParallelLineByBlock - but ParaTool is nullptr."; return; } // 找到并行工具的功能块 WindowAppBlockParallel* blockParallel = qgraphicsitem_cast(m_Pou.GetToolItem(pParaTool)); if (blockParallel != nullptr) { // 更新对应的并行母线(以起始连线线的X坐标为参照) blockParallel->updateParallelLine(); } } /// /// 用于和注释控件做联动使用,如果注释控件内容为空,则删除此控件 /// /// void WindowAppPouScene::removeEmptyBlockComment(WindowAppBlockComment* item) { // 如果注释控件为空,则删除此控件 if (item->content().isEmpty()) { removeItem(item); item->deleteLater(); } } //================================================================= // // 动态端口同步相关 // //================================================================= /// /// 接收来自Dll端的动态端口同步消息 /// /// void WindowAppPouScene::customEvent(QEvent* event) { // 如果是动态端口同步消息 if (event->type() == DLLINF_EVENT_TYPEID) { SyncInterfaceEvent* pDllInfEvent = dynamic_cast (event); // 取出参数 QList syncInfs = pDllInfEvent->getSyncInterfaces(); qDebug() << "WindowAppPouScene::customEvent - DLLINF_EVENT, inf count:" << syncInfs.size() << " add/del:" << pDllInfEvent->m_bAdd; // 根据名字找到所属的功能块(仅标准功能块支持此功能) WindowAppBlockStandard* pBlock = dynamic_cast(m_Pou.GetToolItem(pDllInfEvent->m_strInstanceName)); // 执行结果 bool bRet = false; // 如果出错,保存错误原因 QString strReason; // 动态增加 if (pDllInfEvent->m_bAdd) { bRet = pBlock->addDynamicInterfacesFromDll(syncInfs, strReason); } // 动态删除 else { bRet = pBlock->deleteDynamicInterfacesFromDll(syncInfs, strReason); } pDllInfEvent->m_bSuccess = bRet; pDllInfEvent->m_strReason = strReason; //// 刷新整个UI //this->update(); } } ///// ///// 2022-3-10 为功能块增加动态接口 ///// ///// ///// //void WindowAppPouScene::addDynamicInterfaces(const QString& strToolInstName, const QList& infList) //{ // WindowAppBlockStandard* pBlock = dynamic_cast(m_Pou.GetToolItem(strToolInstName)); // // // 增加动态接口 // pBlock->addDynamicInterfacesFromDll(infList); // //} // //void WindowAppPouScene::delDynamicInterfaces(const QString& strToolInstName, const QList& infList) //{ // //} /// /// 键盘上下键移动功能块 不按 Control 的时候,鼠标上下键为画布移动,按住 Control 的时候,为功能块移动 /// /// void WindowAppPouScene::keyPressEvent(QKeyEvent* event) { if (QApplication::keyboardModifiers() == Qt::ShiftModifier || QApplication::keyboardModifiers() == Qt::ControlModifier ) { qreal dx = 0, dy = 0; switch (event->key()) { case Qt::Key_Up: dx = 0; dy = -m_nMoveBlockSleep; break; case Qt::Key_Down: dx = 0; dy = m_nMoveBlockSleep; break; case Qt::Key_Left: dx = -m_nMoveBlockSleep; dy = 0; break; case Qt::Key_Right: dx = m_nMoveBlockSleep; dy = 0; break; } // 持续移动的时候,移动步长会持续增加。直到步长值为 50 if (m_nMoveBlockSleep < 50) { m_nMoveBlockSleep++; } QList selItems = this->selectedItems(); QList selBlocks; // 取出其中所有的Block for (const auto& selItem : selItems) { WindowAppBlockStandardBase* block = qgraphicsitem_cast(selItem); if (block != nullptr && block->isStandardBasedBlock()) { selBlocks.push_back(block); //onBlockMove(selItem); block->moveBy(dx, dy); } } // TODO: 还需要移动 Link } //else { QGraphicsScene::keyPressEvent(event); } } /// /// 接收键盘释放消息 /// /// void WindowAppPouScene::keyReleaseEvent(QKeyEvent* event) { // if (event->isAutoRepeat()) { event->ignore(); } else { m_nMoveBlockSleep = 1; } QGraphicsScene::keyReleaseEvent(event); }