#include "WindowRuntimeView.h" #include "WindowAppUiView.h" #include "WindowAppUiScene.h" #include "WindowRuntime.h" #include "selectwidget.h" #include "PouManager.h" #include "UiManager.h" #include "GvlManager.h" #include "VPControls.h" WindowRuntimeView::WindowRuntimeView(QWidget *parent) : QWidget(parent) { // 初始化Logo贴图 this->initLogoImage(); } WindowRuntimeView::~WindowRuntimeView() { } /// /// 从设计页面中拷贝,并且建立Runtime和UI、Dll的三方消息同步 /// /// /// bool WindowRuntimeView::copyFrom(WindowAppUiView* pDesignView) { m_pControls = pDesignView->uiScene()->getAllControls(); // 枚举该UI页面中的所有的控件,照着复制一份到Runtime窗体中 // 创建控件时,要留出左侧的基础宽度 APP_RM_WIDTH for (SelectWidget* pUiWidget : m_pControls) { QWidget* pNewControl = nullptr; // 获取控件位置和尺寸 QPoint ptPos = pUiWidget->pos(); //ptPos.setX(ptPos.x()); //int nWidth = pUiWidget->width(); //int nHeight = pUiWidget->height(); QSize controlSize = pUiWidget->size(); // 解析扩展属性是否成功(仅用于复杂控件) bool bPasrseOk = true; if (pUiWidget->m_pProperty == nullptr) { qWarning() << "[Error] in WindowRuntimeView::copyFrom() - m_pProperty in selectWidget is nullptr."; continue; } // 根据不同类型创建不同的控件实例 switch (pUiWidget->m_Type) { case VALUE_TYPE::Control_Label: { pNewControl = new VLabel(this, ptPos, controlSize, pUiWidget->m_pProperty); } break; case VALUE_TYPE::Control_Button: { pNewControl = new VButton(this, ptPos, controlSize, pUiWidget->m_pProperty); // 绑定点击信号 // 绑定信号的目的是为了做控件的三方同步 connect(pNewControl, SIGNAL(clicked()), this, SLOT(slotControlClickedDispatcher())); } break; case VALUE_TYPE::Control_CheckBox: { pNewControl = new VCheckBox(this, ptPos, controlSize, pUiWidget->m_pProperty); // 绑定CheckBox变动信号 connect(pNewControl, SIGNAL(stateChanged(int)), this, SLOT(onVCheckChanged(int))); } break; case VALUE_TYPE::Control_RadioBox: { pNewControl = new VRadioBox(this, ptPos, controlSize, pUiWidget->m_pProperty); // 绑定RadioBox变动信号 connect(pNewControl, SIGNAL(toggled(bool)), this, SLOT(onVRadioChanged(bool))); } break; case VALUE_TYPE::Control_Groupbox: { pNewControl = new VGroupBox(this, ptPos, controlSize, pUiWidget->m_pProperty); } break; case VALUE_TYPE::Control_LineEdit: { pNewControl = new VLineEdit(this, ptPos, controlSize, pUiWidget->m_pProperty); // 注册文字变更消息用于反向同步 connect(pNewControl, SIGNAL(textChanged(QString)), this, SLOT(onVEditChanged(QString))); } break; case VALUE_TYPE::Control_Listbox: { pNewControl = new VListBox(this, ptPos, controlSize, pUiWidget->m_pProperty); // 绑定ListBox变动信号 connect(pNewControl, SIGNAL(currentRowChanged(int)), this, SLOT(onVListChanged(int))); } break; case VALUE_TYPE::Control_ComboBox: { pNewControl = new VComboBox(this, ptPos, controlSize, pUiWidget->m_pProperty); // 绑定ComboBox变动信号 connect(pNewControl, SIGNAL(currentIndexChanged(int)), this, SLOT(onVComboChanged(int))); } break; case VALUE_TYPE::Control_Value: { pNewControl = new ValueControl(this, ptPos, controlSize, pUiWidget->m_pProperty); // 注册文字变更消息用于反向同步 connect(pNewControl, SIGNAL(textChanged(QString)), this, SLOT(onValueControlChanged(QString))); } break; case VALUE_TYPE::Control_Result: { pNewControl = new VResult(this, ptPos, controlSize, pUiWidget->m_pProperty); } break; case VALUE_TYPE::Control_PieChart: { if (pUiWidget->m_pPropertyEx == nullptr) { qWarning() << "[Error] in WindowRuntimeView::copyFrom() - Control_PieChart m_pPropertyEx in selectWidget is nullptr."; continue; } pNewControl = new VPieChart( this, ptPos, controlSize, pUiWidget->m_pProperty, pUiWidget->m_pPropertyEx ); // 解析传入的复杂控件属性 bPasrseOk = this->parseComplexPropertyEx(pNewControl, pUiWidget->m_pPropertyEx); } break; case VALUE_TYPE::Control_CustomPlot: { if (pUiWidget->m_pPropertyEx == nullptr) { qWarning() << "[Error] in WindowRuntimeView::copyFrom() - Control_PieChart m_pPropertyEx in selectWidget is nullptr."; continue; } pNewControl = new VCustomPlot( this, ptPos, controlSize, pUiWidget->m_pProperty, pUiWidget->m_pPropertyEx ); // 2021-12-15 对于复杂控件还需要额外解析扩展属性 bPasrseOk = this->parseComplexPropertyEx(pNewControl, pUiWidget->m_pPropertyEx); } break; case VALUE_TYPE::Control_Table: { if (pUiWidget->m_pPropertyEx == nullptr) { qWarning() << "[Error] in WindowRuntimeView::copyFrom() - Control_Table m_pPropertyEx in selectWidget is nullptr."; continue; } pNewControl = new VTableControl( this, ptPos, controlSize, pUiWidget->m_pProperty, pUiWidget->m_pPropertyEx ); // 2021-12-15 对于复杂控件还需要额外解析扩展属性 bPasrseOk = this->parseComplexPropertyEx(pNewControl, pUiWidget->m_pPropertyEx); } break; case VALUE_TYPE::Control_Image: { if (pUiWidget->m_pPropertyEx == nullptr) { qWarning() << "[Error] in WindowRuntimeView::copyFrom() - Control_Image m_pPropertyEx in selectWidget is nullptr."; continue; } pNewControl = new VImageControl( this, ptPos, controlSize, pUiWidget->m_pProperty, pUiWidget->m_pPropertyEx, true ); // 解析传入的复杂控件属性 bPasrseOk = this->parseComplexPropertyEx(pNewControl, pUiWidget->m_pPropertyEx); } break; default: { qWarning() << "[Runtime] WindowRuntime::runPage - invalid control type:" << static_cast(pUiWidget->m_Type); continue; } // return false; } // 创建控件失败 if (pNewControl == nullptr) { qWarning() << "[Error] WindowRuntimeView::copyFrom - New control failed."; continue; } // 解析扩展属性失败(仅用于复杂控件) if (!bPasrseOk) { QString str = pUiWidget->m_pPropertyEx->m_strTitle + "[" + pUiWidget->m_pProperty->m_strText + "]"; qWarning() << "[Error] WindowRuntimeView::copyFrom - " << str << "parseComplexPropertyEx() failed."; continue; } // 绑定UI控件和Runtime控件对应关系用于同步 // MEMO:此处对于复杂控件也进行了绑定,但是此处暂时是不需要的 // 因为复杂控件不需要UI向Runtime设计中实时同步,而是直接从数据源触发更新 g_pUiManager->bindUiControlWithRtControl(pUiWidget->m_pWidget, pNewControl); // 执行控件的首次同步 // 简单控件 if (pUiWidget->m_pPropertyEx == nullptr) { this->doFirstSync(pUiWidget->m_pWidget, pUiWidget->m_Type, nullptr); } // 复杂控件 else { VARIABLE* pVariable = pUiWidget->m_pPropertyEx->m_refreshLink.linkValue; this->doFirstSync( pUiWidget->m_pWidget, pUiWidget->m_Type, pVariable ); } //pNewControl->resize(controlSize); //pNewControl->move(ptPos); } return true; } /// /// 初始化Logo贴图 /// void WindowRuntimeView::initLogoImage() { m_pLogoImage = new QPixmap(64, 64); QString strPath = QCoreApplication::applicationDirPath() + "/Logo.png"; QFileInfo info(strPath); if (info.exists() != true) { strPath = "./image/logo64.png"; } m_pLogoImage->load(strPath); } /// /// 绘制界面背景 /// /// void WindowRuntimeView::paintEvent(QPaintEvent*) { QRect rc = this->geometry(); if (m_pControls.size() == 0) { // 绘制背景图片 QString path = ":/image/background.png"; QString strPath = QCoreApplication::applicationDirPath() + "/Desktop.bmp"; QFileInfo info(strPath); if (info.exists() != true) { strPath = path; } QPixmap pixmap = QPixmap(strPath).scaled(this->size()); QPalette palette(this->palette()); //palette.setBrush(QPalette::Window, QBrush(pixmap)); //this->setPalette(palette); QPainter painter(this); painter.drawImage(QRect(0, 0, this->width(), this->height()), pixmap.toImage()); } // 在当前界面的右上角显示logo QPainter painter(this); painter.drawPixmap(rc.right() - 100, rc.top(), 64, 64, *m_pLogoImage); } /// /// 控件点击事件的总体调度 /// void WindowRuntimeView::slotControlClickedDispatcher() { QObject* pSender = sender(); QString strSenderClassName = pSender->metaObject()->className(); qDebug() << "WindowRuntimeView::slotControlClicked()" << strSenderClassName; // 通过sender的类型来判断具体是哪个控件触发 // 如果是Button类型 if (strSenderClassName == CLASS_NAME_BUTTON) { onButtonClicked(qobject_cast(pSender)); } else { qDebug() << "WindowRuntimeView::slotControlClickedDispatcher - Unknow sender class: " << strSenderClassName; } } /// /// VButton的点击处理 /// /// void WindowRuntimeView::onButtonClicked(VButton* pClickButton) { // 根据按钮的DataLink信息进行相应的处理 const DataLink& dataLink = pClickButton->getDataLink(); // 如果DataLink是空的则说明没设置数据连接 if (dataLink.value.size() <= 0) { qDebug() << "WindowRuntimeView::onButtonClicked - dataLink is nullptr."; return; } // 解析按钮链接信息 QString strCategory = dataLink.value.at(0); QString strCmd = dataLink.value.at(1); QString strDetail = dataLink.value.at(2); // 如果是系统命令 if (strCategory == SYS_CMD_GROUP_NAME) { // 直接执行 g_pRuntime->runCmd(strCmd, strDetail); } // 如果没有选择第三层级的内容,那么可能是选择了TOOL本身 else if (strDetail.trimmed().isEmpty()) { // 根据名字找到对应的TOOL* POU* pPou = g_pPouManager->getPouByName(strCategory); TOOL* pTool = pPou->GetToolByName(strCmd); if (pTool != nullptr) { // 弹出Tool中的对话框 pTool->pDllPtr->ShowDialog(); } else { QString strMsg = strCategory + "." + strCmd; qWarning() << strMsg << " is NULL"; } } // 否则应该是Tool导出的Button接口 else { // 交给父级统一处理 g_pRuntime->runDllInterface( strCategory, strCmd, strDetail, pClickButton->m_Type ); } } //===================================================================== // // 子控件同步相关 // //===================================================================== /// /// 当Edit控件内容变更时 /// /// void WindowRuntimeView::onVEditChanged(const QString& strText) { Q_UNUSED(strText); qDebug() << "WindowRuntimeView::onVEditChanged - " << strText; VLineEdit* pSender = qobject_cast(sender()); // 向UI同步 g_pUiManager->syncFromRuntime(pSender, UI_SYNC_MSG::EDIT_TEXT_CHANGED); } /// /// ComboBox变更消息 /// /// void WindowRuntimeView::onVComboChanged(const int nIndex) { Q_UNUSED(nIndex); qDebug() << "WindowRuntimeView::onVComboChanged - " << nIndex; VComboBox* pSender = qobject_cast(sender()); // 向UI同步 g_pUiManager->syncFromRuntime(pSender, UI_SYNC_MSG::COMBO_SEL_CHANGED); } /// /// ListBox变更消息 /// /// void WindowRuntimeView::onVListChanged(const int nRow) { Q_UNUSED(nRow); qDebug() << "WindowRuntimeView::onVListChanged - " << nRow; VListBox* pSender = qobject_cast(sender()); // 向UI同步 g_pUiManager->syncFromRuntime(pSender, UI_SYNC_MSG::LIST_SEL_CHANGED); } /// /// RadioBox变更消息 /// /// void WindowRuntimeView::onVRadioChanged(bool checked) { Q_UNUSED(checked); } /// /// CheckBox变更消息 /// /// void WindowRuntimeView::onVCheckChanged(int state) { Q_UNUSED(state); } /// /// 当Value控件内容变更时 /// void WindowRuntimeView::onValueControlChanged(const QString& strText) { Q_UNUSED(strText); vDebug() << strText; ValueControl* pEdit = qobject_cast(sender()); // 向UI同步 g_pUiManager->syncFromRuntime(pEdit, UI_SYNC_MSG::VALUE_CHANGED); } /// /// 执行控件的首次同步 /// /// /// void WindowRuntimeView::doFirstSync(QWidget* pUiControl, VALUE_TYPE controlType, const VARIABLE* pVariable) { if (pUiControl == nullptr) { return; } // 根据不同的类型执行首次同步 switch (controlType) { case VALUE_TYPE::Control_ComboBox: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::COMBO_SEL_CHANGED, true); break; case VALUE_TYPE::Control_RadioBox: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::RADIOBOX_CHANGED, true); break; case VALUE_TYPE::Control_CheckBox: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::CHECKBOX_CHANGED, true); break; case VALUE_TYPE::Control_Listbox: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::LIST_SEL_CHANGED, true); break; case VALUE_TYPE::Control_LineEdit: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::EDIT_TEXT_CHANGED, true); break; // 2022-9-14 加入了ValueControl的首次同步 case VALUE_TYPE::Control_Value: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::VALUE_CHANGED, true); break; case VALUE_TYPE::Control_Result: g_pUiManager->syncToRuntime(pUiControl, UI_SYNC_MSG::VALUE_CHANGED, true); break; case VALUE_TYPE::Control_Image: g_pRuntime->syncFromComplexSource(pVariable); break; default: break; } } //===================================================================== // // 复杂控件同步相关 // //===================================================================== /// /// 解析复杂控件传入的扩展属性(用于执行扩展属性中的数值链接同步) /// /// bool WindowRuntimeView::parseComplexPropertyEx(QWidget* pWidget, CONTROL_PROPERTY_EX* propertyEx) { VARIABLE* pValue = nullptr; DataLink dataLink; // 解析刷新索引值(索引值不允许defaultValue,所以此处如果刷新索引解析失败了则报错退出) bool bRet = this->parseDatalink(propertyEx->m_refreshLink, pValue); if (bRet) { g_pRuntime->registerNewIndex(pValue, pWidget); propertyEx->m_refreshLink.linkValue = pValue; vDebug() << "Bind refreshLink to [" << propertyEx->m_refreshLink.toString() << "]."; } else { // TODO:这个默认值指针如果不传送出去的话,在execute的时候就触发不了(暂未启用) // g_pRuntime->registerNewIndex(propertyEx->m_refreshLink.defaultValue, pWidget); QString str = propertyEx->m_strTitle + "." + propertyEx->m_refreshLink.title; qWarning() << "[Error] WindowRuntimeView::parseComplexPropertyEx() -" << str << "Parse refreshLink failed."; return false; } // 解析主索引值(可能会有0个或者多个) for ( DataLink& dataLink : propertyEx->m_mainDataLinks ) { bool bRet = this->parseDatalink(dataLink, pValue); if (bRet) { // 注册新的变量 g_pRuntime->registerNewVariable(pWidget, dataLink.title, pValue); } // 错误 else { qDebug() << "[Error] WindowRuntimeView::parseComplexPropertyEx() - Parse mainLink failed."; return false; } } // 解析其他数据链接数值(如果解析失败,直接用默认值) for (int i = 0; i < propertyEx->m_groups.size(); i++) { PROPERTY_EX_SUBGROUPS& subGroups = propertyEx->m_groups[i].subGroups; for (int j = 0; j < subGroups.size(); j++) { for (DataLink& dataLink : subGroups[j].dataLinks) { bool bRet = this->parseDatalink(dataLink, pValue); // 2022-1-18,如果没有解析成功,那么直接用默认值 if (bRet) { // 在Runtime中保存 控件-接口 对应关系 g_pRuntime->registerNewVariable(pWidget, dataLink.title, pValue); } // 默认值 else { // 在Runtime中保存 控件-接口 对应关系 g_pRuntime->registerNewVariable(pWidget, dataLink.title, dataLink.defaultValue); } } } } return true; } /// /// 解析Datalink信息 /// /// /// /// bool WindowRuntimeView::parseDatalink( DataLink& dataLink, VARIABLE*& pVar ) { // 2022-1-18,首先判断数据连接是否有效,因为有的数据链接不是强制赋值的 // 到了这里可能会有直接录入数字的,或者留空的 if (dataLink.value.size() != 3) { return false; } POU* pPou = nullptr; TOOL* pTool = nullptr; // 从DataLink信息中解析出接口信息 QString strCategory = dataLink.value.at(0); QString strType = dataLink.value.at(1); QString strDetail = dataLink.value.at(2); // 判断变量源是来自于Tool还是来自于全局变量 bool bVariables = false; if (strType.indexOf(GROUP_GLOBAL_VARIABLE) >= 0 || strType.indexOf(GROUP_LOCAL_VARIABLE) >= 0) { bVariables = true; } // Tool接口 if (!bVariables) { // 根据名字找到对应的_INTERFACE* pPou = g_pPouManager->getPouByName(strCategory); pTool = pPou->GetToolByName(strType); if (pTool) { pVar = pTool->getInterfaceByName(strDetail); if (pVar == nullptr) { QString str = strCategory + "." + strType; qWarning() << "[Error] Link To [" << str << "] Interface failed."; return false; } } else { return false; } } // 局部和全局变量 else { strCategory.remove(VAR_SUFFIX); strType = strCategory; pTool = g_pGvlManager->getGvl(strType); pVar = pTool->getInterfaceByName(strDetail); if (pVar == nullptr) { QString str = strCategory + VAR_SUFFIX + "." + strDetail ; qWarning() << "[Error] Link To [" << str << "] Interface failed."; return false; } } //// 2022-1-18,变量在属性表中的属性名字(用于后续更新使用) //pVar->strPropertyName = dataLink.title; // 在dataLink中保存对应的VARIABLE*指针 dataLink.linkValue = pVar; return true; }