#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);
}