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