如上图所示:
如何定义xml文件
<em>
<default>default>
<editable>Trueeditable>
<group>1group>
<help>类别说明1help>
<index>0index>
<name>type2name>
<scope>
<chosen0>chosen0>
scope>
<type>strtype>
<value>value>
<widget>combo_boxwidget>
em>
type2
combo_box
1
0
str
每一个动态控件都由 em标记
把解析到的信息存到结构体中:
首先这个结构体不能完全写死,不然之后增删改属性很麻烦。
实际上控件的所有属性都是一个键值对,所以可以用 map存储
typedef struct WidgetELEM //控件详细信息
{
string strType;//控件类型
map<string, string> m_simp;//控件元素
map<string, string> m_simpRange;//取值范围
}WidElement;
解释:所有的键值对都存放在 m_simp中。控件类型很重要单独领出来。
如果我们要用下拉列表:
,那么下拉列表的值这么表示:
<scope>
<chosen0>value1chosen0>
<chosen1>value2chosen1>
<chosen2>value3chosen2>
scope>
如果我们要输入浮点数,且限定在某个范围内
<em>
<default>0.001default>
<editable>Trueeditable>
<group>1group>
<help>学习率help>
<index>8index>
<level>highlevel>
<name>ratename>
<scope>
<max>0.1max>
<min>0.00001min>
scope>
<type>floattype>
<value>0.001value>
<widget>line_editwidget>
em>
也就是说scope内的值根据 控件类型的不同有调整,所以我们也把它单独领出来。用m_simpRange表示
最后所有的控件都保存到一个列表里
typedef struct WidEmList //控件信息vector
{
vector<pair<string, WidElement>> m_mapList; // 控件名字 , 控件信息
}WidEleList;
注意:控件的名字必须是独一无二的,用名字表示每个控件
解析xml是指得到 WidEleList结构体 tagList
void AnalyzeParamsXml(string strFile, WidEleList& tagList)
{
tagList.m_mapList.clear();
QFile qFile(strFile.c_str());
if (!qFile.open(QIODevice::ReadOnly))
{
return;
}
QString str = qFile.readAll();
QDomDocument XMLdoc;
bool bLoad = XMLdoc.setContent(str);
if (!bLoad)
{
return;
}
QDomElement root = XMLdoc.documentElement(); //获取根节点元素 args
QDomElement configElement = root.firstChildElement();//根节点的子节点 em
bool bFind = false;
while (!configElement.isNull())
{
QString strParmName = "";
QString strControlType = "";
WidElement tEM;
QDomElement pElementNode = configElement.firstChildElement();
while (!pElementNode.isNull())
{
QString value = pElementNode.text();
QString text = pElementNode.nodeName();
if (text == "name")
{
strParmName = value;
}
if (text == "widget")
{
strControlType = value;
}
if (text == "scope")
{
map<string, string> mapList;
QDomElement pElementNodeChild = pElementNode.firstChildElement(); //ElementMeta
while (!pElementNodeChild.isNull())
{
QString valueChild = pElementNodeChild.text();
QString textChlid = pElementNodeChild.nodeName();
mapList[textChlid.toStdString()] = valueChild.toStdString();
pElementNodeChild = pElementNodeChild.nextSiblingElement();
}
tEM.m_simpRange = mapList;
}
tEM.m_simp[text.toStdString()] = value.toStdString();
pElementNode = pElementNode.nextSiblingElement(); //查找下一个兄弟节点的指针
}
configElement = configElement.nextSiblingElement();
tEM.strType = strControlType.toStdString();
tagList.m_mapList.push_back(make_pair(strParmName.toStdString(), tEM));
}
}
WidEleList 内部是根据加入的顺序排序的,我们在xml的
定义了该控件显示在第几个,所以首先对 WidEleList m_widElement;
进行排序
排序的函数:
bool WidElementCompare(pair<string, WidElement>& ele1, pair<string, WidElement>& ele2)
{
QString index1 = ele1.second.m_simp["index"].c_str();
QString index2 = ele2.second.m_simp["index"].c_str();
int i1 = index1.toInt();
int i2 = index2.toInt();
return i1 < i2;
}
使用stl自带的排序算法:
sort(m_widElement.m_mapList.begin(), m_widElement.m_mapList.end(), WidElementCompare);
在xml中
设置组别:0就是第一组放在最上面:
首先我们获取父窗口,得到m_pParent
//cpp
void SetGUI(QWidget* pWidget)
{
m_pParent = pWidget;
}
//h
QWidget* m_pParent;//父窗口
在父窗口设置栅格布局
mainLayout = new QGridLayout;
m_pParent->setLayout(mainLayout);
首先创建第一个group,即默认group
//第一个局中局,放在总布局的第一行第一列
QGridLayout *pGridLayout = new QGridLayout;
QFrame *pFrame = new QFrame();
//framComTitleBar
pFrame->setObjectName("frame");
pFrame->setStyleSheet("QFrame#frame{ background-color: rgb(255, 255, 255);border-radius: 10px;border:1px solid rgb(33, 150, 243)}");
pFrame->setLayout(pGridLayout);
mainLayout->addWidget(pFrame,0,0);
m_vecGridLayout[0] = pGridLayout;
m_vecFrame[0] = pFrame;
遇到新的group,就创建新的QFrame加入到父窗口的布局中。
然后我们遍历整个结构体
for(所有的控件)
{
获取控件类型
使用具体类型的创建函数
}
void CreateUIFromXml(string strRoot, string strFile)
{
ClearControl();
m_strRoot = strRoot;
m_strFile = strFile;
parameterAnalyze jcStru;
jcStru.AnalyzeParamsXml(strRoot + strFile, m_widElement);
sort(m_widElement.m_mapList.begin(), m_widElement.m_mapList.end(), WidElementCompare);
mainLayout = new QGridLayout;
m_pParent->setLayout(mainLayout);
//第一个局中局,放在总布局的第一行第一列
QGridLayout *pGridLayout = new QGridLayout;
QFrame *pFrame = new QFrame();
//framComTitleBar
pFrame->setObjectName("frame");
pFrame->setStyleSheet("QFrame#frame{ background-color: rgb(255, 255, 255);border-radius: 10px;border:1px solid rgb(33, 150, 243)}");
pFrame->setLayout(pGridLayout);
mainLayout->addWidget(pFrame,0,0);
m_vecGridLayout[0] = pGridLayout;
m_vecFrame[0] = pFrame;
for (auto it = m_widElement.m_mapList.begin(); it != m_widElement.m_mapList.end(); it++)
{
if (it->second.m_simp.find("editable") == it->second.m_simp.end() ||
it->second.m_simp["editable"] == "false")//不需要画的
{
continue;
}
switch (m_mapControl[it->second.strType])
{
case TYPE_LINEEDIT:
CreateLineEdit(it->second);
break;
case TYPE_SPINBOX:
CreateSpinBix(it->second);
break;
case TYPE_DOUBLE_SPINBOX:
CreateDoubleSpinBox(it->second);
break;
case TYPE_DIRVIEW:
CreateDirView(it->second);
break;
case TYPE_FILEVIEW:
CreateFileView(it->second);
break;
case TYPE_COMBOBOX:
CreateCombox(it->second);
break;
case TYPE_CHECKBOX:
CreateCheckBox(it->second);
break;
case TYPE_CHECKBOXGROUP:
CreateCheckBoxGroup(it->second);
break;
default:
CreateName(it->second);
break;
}
}
CreateSaveAndEditBtn();
}
举例:QLineEdit
首先获取控件的名字,和group,得到要把这个控件放在哪个QFrame中的QGridLayout
new一个QLineEdit,设置默认值,设置取值范围,最后把控件加入到布局中
void CreateLineEdit(WidElement& ele)
{
int nLayoutIndex = 0, row = 0;
CreateElementLabel(ele , nLayoutIndex , row);
QGridLayout *pGridLayout = m_vecGridLayout[nLayoutIndex];
pInfo->m_strKey = ele.m_simp["name"].c_str();
QLineEdit* pEdit =new QLineEdit(m_pParent);
//设置默认值
QString strText;
if (ele.m_simp["value"] != "")
strText = ele.m_simp["value"].c_str();
else
strText = ele.m_simp["default"].c_str();
pEdit->setText(strText);
//设置取值范围
QString strInfoText;
if (ele.m_simpRange.find("min") != ele.m_simpRange.end())
{
strInfoText = "(";
strInfoText += ele.m_simpRange["min"].c_str();
}
if (ele.m_simpRange.find("max") != ele.m_simpRange.end())
{
strInfoText += "-";
strInfoText += ele.m_simpRange["max"].c_str();
strInfoText += ")";
}
pEdit->setToolTip(ele.m_simp["help"].c_str());
QLabel* pLabelInfo = new QLabel(m_pParent);
pLabelInfo->setText(strInfoText);
m_vecLineEdit[m_nCtrID++] = pEdit ;
control_info cInfo{ ele.m_simp["name"].c_str() ,TYPE_LINEEDIT , m_nCtrID - 1 };
m_vecControlInfo.push_back(cInfo);
ele.id = m_nCtrID - 1;
pEdit->setProperty("index", m_nCtrID - 1);
pGridLayout->addWidget(pEdit, row, 1);
m_vecLable[m_nCtrID++] = pLabelInfo;
pLabelInfo->setProperty("index", m_nCtrID - 1);
//把控件加入到布局中
pGridLayout->addWidget(pLabelInfo, row, 2);
control_info cInfo1{ ele.m_simp["name"].c_str() ,TYPE_LABEL , m_nCtrID - 1 };
m_vecControlInfo.push_back(cInfo1);
}
我们要怎么得知放在哪个QFrame中的QGridLayout
首先获取 group,查看 该group的QFrame和QGridLayout 是否创建(m_vecGridLayout里面有没有)
没有的话 new一个,并加入到 m_vecGridLayout中,把QFrame加到父界面的布局中
返回QGridLayout 的指针
void CreateElementLabel(WidElement& ele, int & nLayoutIndex, int &row)
{
QLabel* pLabel = new QLabel(m_pParent);
pLabel->setText(ele.m_simp["name"].c_str());
QString help = ele.m_simp["help"].c_str();
pLabel->setToolTip(help);
m_vecLable[m_nCtrID++] = pLabel;
pLabel->setProperty("index", m_nCtrID-1);
control_info cInfo{ ele.m_simp["name"].c_str() ,TYPE_LABEL , m_nCtrID - 1 };
m_vecControlInfo.push_back(cInfo);
QString groupIndex = ele.m_simp["group"].c_str();
bool ok;
nLayoutIndex = groupIndex.toInt(&ok);
if (!ok || nLayoutIndex<0)
{
nLayoutIndex = 0;
}
map<int, QGridLayout *>::iterator iter = m_vecGridLayout.find(nLayoutIndex);
if (iter == m_vecGridLayout.end())
{
QGridLayout *pGridLayout = new QGridLayout;
QFrame *pFrame = new QFrame();
pFrame->setObjectName("frame");
pFrame->setStyleSheet("QFrame#frame{ background-color: rgb(255, 255, 255);border-radius: 10px;border:1px solid rgb(33, 150, 243)}");
pFrame->setLayout(pGridLayout);
mainLayout->addWidget(pFrame, nLayoutIndex, 0);
m_vecGridLayout[nLayoutIndex] = pGridLayout;
m_vecFrame[nLayoutIndex] = pFrame;
}
QGridLayout *pGridLayout = m_vecGridLayout[nLayoutIndex];
row = pGridLayout->rowCount();
pGridLayout->addWidget(pLabel, ++row, 0);
}