TUiLoader操作手册
1.介绍
TUiLoader可以在程序运行时,通过JavaScript的数据格来动态生成用户界面, 并在QUiLoader的基础上集成了数据有效性验证,快速设置或获取界面数据等功能.
2.入门指南
下例是一个简单的使用TUiLoader的例子,可以生成如下图所示的界面.
#include <QApplication> #include <QtWidgets> #include <QScriptValue> #include <TWidget/tuiloader.h> int main(int argc, char *argv[]) { QApplication a(argc, argv); QString uistr = "{" \ "name : 'hbox1'," \ "type : 'HBoxLayout'," \ "property : { spacing:5, margin:3 }," \ "pack : {stretch:0, alignment:'Qt::AlignLeft|Qt::AlignTop' }," \ "child : [" \ "{" \ "name : 'label1'," \ "type : 'Label'," \ "property : {text:'Label:',buddy:'lineedit1',alignment:'Qt::AlignRight'}," \ "pack : {stretch:0, alignment:'Qt::AlignLeft|Qt::AlignTop' }," \ "}," \ "{" \ "name : 'lineedit1'," \ "type : 'LineEdit'," \ "value : 'Line Edit Value 1'," \ "property : {}," \ "pack : {stretch:1}," \ "}," \ "]" \ "}"; QMainWindow *mw = new QMainWindow; TUiLoader ui(mw); ui.setUiStr(uistr); mw->show(); return a.exec(); }
从上例可以看出UiLoader的关键在于 ui.setUiStr(uistr) 中uistr的定义;
3.格式定义
{ name : 'hbox1', //控件名称 type : 'HBoxLayout', // 控件类型 property : { spacing:5, margin:3 }, //控件属性 pack : {stretch:0, alignment:'Qt::AlignLeft|Qt::AlignTop' }, //控件添加到父控件时参数 ... : //其它参数 child : [ //包含的子控件 {子控件}, {...}, ] }
4.控件参数
4.1 name
name的值会赋给控件的objectName属性,作为该控件在整个UiLoader中的唯一标示符; 便于通过引名称获取控件实例,为控件的赋值等;
name : 'customer_name',
4.2 type
控件类型;
- Label => TLabel
- LineEdit => TLineEdit
- ComboBox => TComboBox
- EditableComboBox => TEditableComboBox
- DateTimeEdit => TDateTimeEdit
- DateEdit => TDateEdit
- TimeEdit => TTimeEdit
- CheckBox => TCheckBox
- PlainTextEdit => TPlainTextEdit
- TextEdit => TTextEdit
- RadioBox => TRadioBox
- MultiComboBox => TMultiComboBox
- MultiCheckBox => TMultiCheckBox
- CodeEdit => TCodeEdit
- Dial => TDial
- LCDNumber => TLCDNumber
- ListView => TListView
- ListWidget => TListWidget
- ProgressBar => TProgressBar
- PushButton => TPushButton
- RadioButton => TRadioButton
- CommandLinkButton => TCommandLinkButton
- ScrollBar => TScrollBar
- Slider => TSlider
- SpinBox => TSpinBox
- DoubleSpinBox => TDoubleSpinBox
- TableView => TTableView
- TableWidget => TTableWidget
- TextBrowser => TTextBrowser
- PlainTextEdit => TPlainTextEdit
- ToolButton => TToolButton
- TreeView => TTreeView
- TreeWidget => TTreeWidget
- FontComboBox => TFontComboBox
- CalendarWidget => TCalendarWidget
- ColumnView => TColumnView
- GraphicsView => TGraphicsView
- Widget => TWidget
- IconSelector => TIconSelector
- TitleExpander => TTitleExpander
- DockWidget => TDockWidget
- Frame => TFrame
- GroupBox => TGroupBox
- ScrollArea => TScrollArea
- MainWindow => TMainWindow
- MdiArea => TMdiArea
- Menu => TMenu
- MenuBar => TMenuBar
- TabWidget => TTabWidget
- ToolBar => TToolBar
- ToolBox => TToolBox
- Splitter => TSplitter
- StackedWidget => TStackedWidget
- StatusBar => TStatusBar
- DialogButtonBox => TDialogButtonBox
- Wizard => TWizard
- WizardPage => TWizardPage
- GridLayout => TGridLayout
- HBoxLayout => THBoxLayout
- StackedLayout => TStackedLayout
- VBoxLayout => TVBoxLayout
- FormLayout => TFormLayout
- Action => TAction
type : 'LineEdit',
4.3 property
控件属性;
property : {text : 'Text Value',icon:RES(':/new.png')},
4.4 pack
添加到父控件时的参数,不同父控件所对应的参数会有不同;
- HBoxLayout,VBoxLayout : {stretch : 1, alignment: 'Top|Left'}
- GridLayout : {row : 0, column:1, rowSpan : 1, columnSpan: 2, alignment: 'Bottom|Right'}
- FormLayout : {label : 'Label'}
- TabWidget : {icon : RES('info'), label: 'Information'}
- Splitter : {stretch : 1, collapsible : false}
- MainWindow : {area:'Left'|'Right'|'Top'|'Bottom', orientation:'Qt::Vertical', tabified:true}
- TitleExpander : {area:'Title'|'Check'|'Body',position:-1, stretch:1, alignment:'Center'}
pack : {stretch:0, alignment:'Left|Top' }
4.5 child
包含的子控件;
child : [ { name : 'label2', type : 'Label', property : {text:'ComboBox:',buddy:'lineedit2',alignment:'Qt::AlignRight'}, pack : {row:1,column:0}, }, { name : 'combobox', type : 'ComboBox', property : {sizePolicy:'Expanding,Fixed',itemList:[{name:'a',text:'Item A'},{name:'b',text:'Item B'}]}, pack : {row:1,column:1}, }, ]
4.6 value
为控件赋值,常用的控件均有默认的赋值函数, 也可通过setter属性自定义赋值函数;
value : 'Text Value',
4.7 title
控件标题,主要用于有效性验证消息显示;
title : 'Age',
4.8 validate
有效性验证回调函数; 如果为'NOTNULL'则表示值不能为空;
- obj: 控件对象实例
- val: 控件的值
- title: 控件title属性
- moment: 验证触发的时机;载入数据时为'LOAD',控件编辑时为'EDIT'
验证后的返回格式为[错误或警告消息, 错误类型{ERROR,WARN,OK,INFO} , 控件StyleSheet]
validate: function(obj,val,title,moment,self){ print(val + title + moment); if (val > 10){ return [title+'必须小于10','ERROR','background:brown'] } else{ return [title+'OK','OK']; } },
4.9 initCallback
控件初始化完成后执行的回调函数;
- obj: 控件对象实例
initCallback:function(obj,self){ obj.setDataKeyList("colnm1","colnm2","colnm3","colnm4","colnm5","colnm6"]); }
4.10 formula
公式回调函数,当触发公式函数时,其返回值赋值给当前控件;
- obj: 控件对象实例
formula:function(obj,self){ val = this.getValue('position'); if (val > 10){ return val * 2; } else{ return val * 1.5; } }
4.11 formulaTrigger
当值列表中的控件发生更改时触发公式函数;列表中的'LOAD'代表载入值时触发;
formulaformulaTrigger:['LOAD','position','name'],
4.12 getter
获取值回调函数;
- obj: 控件对象实例
getter: function(obj,self){return obj.currentText},
4.13 setter
赋值回调函数;
- obj: 控件对象实例
- value: 值
setter: function(obj,value,self){obj.setText(value);},
4.14 state
控件状态回调函数,其返回值用于设置控件状态{Hide,Show,Disable};
- obj: 控件对象实例
state: function(obj,self){ val = this.getValue('age'); if (val < 10 ){ return 'Hide'; } else if (val >= 10 and val < 40) { return 'Disable'; } else{ return 'Show'; } },
4.15 validateSignal
触发有效性验证的信号,常用控件均有设置默认的有效性验证信号;
validateSignal: 'editingFinished()'; validateSignal: ['editingFinished()','currentNameChanged(QString)'];
4.16 changeSignal
触发数据有更改的信号;
changeSignal: 'valueChanged()'; changeSignal: ['valueChanged()','currentNameChanged(QString)'];
4.17 callback
当控件为Button,Action等时的响应函数;
callback : function(obj,checked,self){ try{ var data = this.getAllValues(); allkeys = Object.keys(data); print('----------return data--------'); for(var i = 0; i < allkeys.length; ++i){ print(allkeys[i] + '=' + data[allkeys[i]]); } print('----------------------------'); } catch(e){ print(e); } },
4.18 tabOrder
设置焦点顺序;
tabOrder : 11,
4.19 data
控件其它数据信息;
data : {x : 12, y: 20},
4.20 newParam
某些控件在实例化时必须传入的一些属性值;
data : {text:'Value'},
5. 特殊属性
5.1 buddy
Label拥有buddy属性的值应该为控件的name;
{ name : 'label1', type : 'Label', property : { text : 'Age' , buddy => 'age'}, }
5.2 actionGroup
单选类型的Action可以设置一个actionGroup来设定哪些Action在同一组;
{ name : 'action1', type : 'Action', property : { actionGroup : 'grp1' }, }
5.3 sizePolicy
sizePolicy属性有下几种方式; 字符方式: 'hpolicy,vpolicy,hstretch,vstretch' 后面的可省略; 数组方式: [hpolicy,vpolicy,hstretch,vstretch] 对象方式: {hPolicy:'', vPolicy:'', hStretch:'' vStretch:''} policy的值参考QSizePolicy::Policy, 可只写::后面的部分 stretch为整数
{ property : { sizePolicy : 'Preferred,Preferred' }, } { property : { sizePolicy : ['Preferred','Preferred',1,1] }, } { property : { sizePolicy : {hPolicy:'Perferred',vPolicy:'Expanding'}}, }
5.4 *size*
包含size属性有下几种方式; 字符方式: 'width,height'; 数组方式: [width,heigth] 对象方式: {width:'', height:''}
{ property : { maximumSize : '12,100' }, } { property : { maximumSize : [12,100] }, } { property : { maximumSize : {width:12,height:100} }, }
size
6. 格式实例
下例是一个较完整的UiLoader文本,可以生成如下图所示的界面.
UI = { name : 'mainwindow1', type : 'MainWindow', property : {}, pack : {}, child : [ { name : 'vbox1', type : 'VBoxLayout', property : { spacing : 5, margin : 10, }, newParam : {}, pack : {}, child : [ { name : 'hbox1', type : 'HBoxLayout', property : { spacing:5, margin:3 }, pack : {stretch:0, alignment:'Qt::AlignLeft|Qt::AlignTop' }, child : [ { name : 'btn1', type : 'PushButton', property : {text : 'GetAllValue'}, callback : function(){ try{ var data = this.getAllValues(); allkeys = Object.keys(data); print('----------return data--------'); for(var i = 0; i < allkeys.length; ++i){ print(allkeys[i] + '=' + data[allkeys[i]]); } print('----------------------------'); } catch(e){ print(e); } }, }, { name : 'btn2', type : 'PushButton', property : {text : 'ValidateAll',icon:RES(':/new.png')}, callback: function(){ try{ var errlist = this.validateAll('SAVE',true); print('----------validate message--------'); print(errlist); print('----------------------------------'); } catch(e){ print(e); } }, }, { name : 'btn3', type : 'PushButton', property: {text : 'ToggleButton',checkable:true}, callback : function(){}, } ] }, { name : 'tabwidget1', type : 'TabWidget', property : {}, pack: {stretch:1}, initHook:function(obj){ try{ obj.setCurrentIndex(0); } catch(e){ print(e); } }, child : [ { name : 'scrollarea1', type : 'ScrollArea', property : {widgetResizable:true,frameShape:'QFrame::NoFrame'}, pack : {icon:RES(':/new.png'),label:"Tab One"}, child :{ name : 'tab1', type : 'GridLayout', property : {}, pack : {}, initHook: function(obj){}, child : [ { name : 'label1', type : 'Label', property : {SS:'D',text:'LineEdit:',buddy:'lineedit1',alignment:'Qt::AlignRight'}, pack : {row:0,column:0}, }, { name : 'lineedit1', type : 'LineEdit', title : ' LLLL', value : 'Line Edit 1', property : {}, validate: function(obj,val,title,moment){ print(val + title + moment); if (val > 10){ return [title+'必须小于10','ERROR','background:brown'] } else{ return [title+'OK','OK']; } }, pack : {row:0,column:1}, }, { name : 'label2', type : 'Label', property : {text:'ComboBox:',buddy:'lineedit2',alignment:'Qt::AlignRight'}, pack : {row:1,column:0}, }, { name : 'combobox', type : 'ComboBox', property : {sizePolicy:'Expanding,Fixed',itemList:[{name:'a',text:'Item A'},{name:'b',text:'Item B'}]}, pack : {row:1,column:1}, value : 'b', validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'label3', type : 'Label', property : {text:'EditableComboBox:',buddy:'lineedit2',alignment:'Qt::AlignRight'}, pack : {row:2,column:0}, }, { name : 'editcombobox', type : 'EditableComboBox', property : {sizePolicy:'Expanding,Fixed',itemList:['Item1','Item2','Item3']}, pack : {row:2,column:1}, value : 'Item2', validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'label4', type : 'Label', property : {text:'DateTimeEdit:',alignment:'Qt::AlignRight'}, pack : {row:3,column:0}, }, { name : 'dattimeedit', type : 'DateTimeEdit', value : '2011-12-13 12:32:31', property : {sizePolicy:'Expanding,Fixed'}, pack : {row:3,column:1}, validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'label4', type : 'Label', property : {text:'DateEdit:',alignment:'Qt::AlignRight'}, pack : {row:4,column:0}, }, { name : 'dateedit', type : 'DateEdit', value : '2022-01-13', property : {sizePolicy:'Expanding,Fixed',wheelEnabled:true}, pack : {row:4,column:1}, validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'label5', type : 'Label', property : {text:'TimeEdit:',alignment:'Qt::AlignRight'}, pack : {row:5,column:0}, }, { name : 'timeedit', type : 'TimeEdit', value : '12:32:34', property : {sizePolicy:'Expanding,Fixed'}, pack : {row:5,column:1}, validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'label6', type : 'Label', property : {text:'CheckBox:',alignment:'Qt::AlignRight'}, pack : {row:6,column:0}, }, { name : 'checkbox', type : 'CheckBox', value : -1, property : {text:'CheckBox',tristate:false}, pack : {row:6,column:1}, validate: 'NotNull', }, { name : 'label7', type : 'Label', property : {text:'PlainTextEdit:',alignment:'Qt::AlignTop|Qt::AlignRight'}, pack : {row:7,column:0}, }, { name : 'plaintextedit', type : 'TextEdit', value : 'This is a Plain Text', property : {acceptRichText:false,sizePolicy:'Expanding,Expanding',minimumHeight:20, verticalScrollBarPolicy:'Qt::ScrollBarAlwaysOff' }, pack : {row:7,column:1}, validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'textedit', type : 'TextEdit', value : 'This is a Text', property : {sizePolicy:'Expanding,Expanding',minimumHeight:20, verticalScrollBarPolicy:'Qt::ScrollBarAlwaysOff' }, pack : {row:8,column:1}, validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, { name : 'label2', type : 'Label', property : {text:'RadioBox:',buddy:'lineedit2',alignment:'Qt::AlignRight'}, pack : {row:9,column:0}, }, { name : 'radiobox', type : 'RadioBox', property : {sizePolicy:'Expanding,Fixed',itemList:[{name:'a',text:'Radio A'},{name:'b',text:'Radio B'}]}, pack : {row:9,column:1}, value : 'b', validate: function(obj,val,title,moment){ print(title +' changed to <' + val + '> on ' + moment); } }, ] } }, { name : 'tab2', type : 'VBoxLayout', property : {}, pack : {label:"第二页"}, child : [ { name : 'splitter1', type : 'Splitter', property : {}, pack : {}, child : [ { name : 'treewidget1', type : 'TreeWidget', property : {size:[40,100],sizePolicy:'Expanding'}, pack : {collaplible:true,stretch:0}, }, { name : 'textedit1', type : 'TextEdit', property : {}, pack : {stretch:1}, } ] } ] } ] } ] }, { name : 'dock1', type : 'DockWidget', property : { windowTitle:'工具', allowedAreas:'Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea' }, pack: {area:'left'}, child : { name : 'dock1treeview1', type : 'TreeView', property : {}, pack : {}, initHook:function(obj){ try{ obj.setHeaderItem( [ { "name": "col1","display":"第一列","resizeMode":"ResizeToContents", "displayRole":".colnm1" }, { "name": "col2","display":"第二列","resizeMode":"Fixed", "displayRole":".colnm2" }, { "name": "col3","display":"第三列","resizeMode":"Stretch", "textAlignment":"HCenter|VCenter", "displayRole":".colnm3","typeRole":"html" }, { "name": "col4","display":"第四列","resizeMode":"Interactive", "displayRole":".colnm4","typeRole":"progress" }, { "name": "col5","display":"第五列", "displayRole":".colnm5","typeRole":"button", "propertyRole": {"text":"设置"} } ] ); obj.setDataKeyList(["INDEX","colnm1","colnm2","colnm3","colnm4","colnm5","colnm6"]); } catch(e){ print(e); } }, value : [ { "INDEX":"1","colnm1":"Row1Col1","colnm2":"Row1Col2","colnm3":"<font color=red><b>Row1Col3</b></font><br><i>详细描述信息</i>", "colnm4":"10","colnm5":"Row1Col5","colnm6":"Row1Col6" }, { "INDEX":"2","colnm1":"Row2Col1","colnm2":"Row2Col2","colnm3":"Row2Col3", "colnm4":"20","colnm5":"Row2Col5","colnm6":"Row2Col6" }, { "INDEX":"2.1","colnm1":"Row2.1Col1","colnm2":"Row2.1Col2","colnm3":"Row2.1Col3", "colnm4":"40","colnm5":"Row2.1Col5","colnm6":"Row2.1Col6" }, { "INDEX":"2.2","colnm1":"Row2.2Col1","colnm2":"Row2.2Col2","colnm3":"Row2.2Col3", "colnm4":"100","colnm5":"Row2.2Col5","colnm6":"Row2.2Col6" }, { "INDEX":"2.2.1","colnm1":"Row2.2.1Col1","colnm2":"Row2.2.1Col2","colnm3":"Row2.2.1Col3", "colnm4":"80","colnm5":"Row2.2.1Col5","colnm6":"Row2.2.1Col6" } ], } }, { name : 'dock2', type : 'DockWidget', property : {windowTitle:'图形'}, pack: {area:'left',tabified:'dock1'}, child : { name : 'dock1tableview1', type : 'TableView', property : {}, pack : {}, initHook : function(obj){ obj.setHeaderItem( [ { "displayRole":".index" }, { "name": "col1","display":"第一列","resizeMode":"ResizeToContents", "displayRole":".colnm1" }, { "name": "col2","display":"第二列","resizeMode":"Fixed", "displayRole":".colnm2" }, { "name": "col3","display":"第三列","resizeMode":"Stretch", "textAlignment":"HCenter|VCenter", "displayRole":".colnm3","typeRole":"html" }, { "name": "col4","display":"第四列","resizeMode":"Interactive", "displayRole":".colnm4","typeRole":"progress" }, { "name": "col5","display":"第五列", "displayRole":".colnm5","typeRole":"button", "propertyRole": {"text":"设置"} } ] ); obj.setDataKeyList(["INDEX","colnm1","colnm2","colnm3","colnm4","colnm5","colnm6"]); }, value : [ { "INDEX":"1","colnm1":"Row1Col1","colnm2":"Row1Col2","colnm3":"<font color=red><b>Row1Col3</b></font><br><i>详细描述信息</i>", "colnm4":"10","colnm5":"Row1Col5","colnm6":"Row1Col6" }, { "INDEX":"2","colnm1":"Row2Col1","colnm2":"Row2Col2","colnm3":"Row2Col3", "colnm4":"20","colnm5":"Row2Col5","colnm6":"Row2Col6" }, { "INDEX":"2.1","colnm1":"Row2.1Col1","colnm2":"Row2.1Col2","colnm3":"Row2.1Col3", "colnm4":"40","colnm5":"Row2.1Col5","colnm6":"Row2.1Col6" }, { "INDEX":"2.2","colnm1":"Row2.2Col1","colnm2":"Row2.2Col2","colnm3":"Row2.2Col3", "colnm4":"100","colnm5":"Row2.2Col5","colnm6":"Row2.2Col6" }, { "INDEX":"2.2.1","colnm1":"Row2.2.1Col1","colnm2":"Row2.2.1Col2","colnm3":"Row2.2.1Col3", "colnm4":"80","colnm5":"Row2.2.1Col5","colnm6":"Row2.2.1Col6" } ], } }, { name : 'dock3', type : 'DockWidget', property : {windowTitle:'日志'}, pack: {area:'right'}, child : { name : 'dock1text1', type : 'TextEdit', property : {}, pack : {}, } }, { name : 'toolbar1', type : 'ToolBar', property : {}, pack :{}, child : [ { name : 'action1', type: 'Action' , property : {text:"新建",icon:RES(':/new.png'),shortcut:'Ctrl+N',tooltip:'新建一个文档'}, callback : function() {print("New Files");}, }, { name: 'action2', type: 'Action', property : {text:'编辑'}, callback : function(){}, }, { type : 'Separator' }, { name : 'action3', type : 'Action', property: {text:'Checkable',checkable:true}, callback : function(checked){print(checked)}, }, { type : 'Separator' }, { name : 'action4', type : 'Action', property: {text:'Check1',checkable:true,actionGroup:'a'}, callback : function(checked){print(checked)}, }, { name : 'action5', type : 'Action', property: {text:'Check2',checkable:true,actionGroup:'a'}, callback : function(checked){print(checked)}, }, { type : 'Separator' }, { name : 'menuaction', type : 'Action', property : {text : 'Menu Action'}, child : { name : 'actionsubmenu', type : 'Menu', child : [ { name : 'action1',type: 'Action' }, { name : 'action3',type: 'Action' }, ] } }, { type : 'Separator' }, { name : 'menubtn1', type : 'ToolButton', property : { text:'Menu Button',popupMode:'QToolButton::InstantPopup' }, child : { name : 'btnsubmenu', type : 'Menu', child : [ { name : 'action1',type: 'Action' }, { name : 'action3',type: 'Action' }, ] } }, { type : 'Separator' }, { type : 'Label', property : {text:'查找: '}, }, { name : 'search1', type : 'LineEdit', property : {sizePolicy:'Fixed'}, } ] }, { name : 'toolbar2', type : 'ToolBar', property : {}, pack :{area:'left'}, child : [ { name : 'action1',type: 'Action' }, { name : 'action3',type: 'Action' }, { type : 'Separator' }, { name : 'action4',type: 'Action' }, { name : 'action5',type: 'Action' }, ] }, { name : 'menubar', type : 'MenuBar', property : {}, pack:{}, child : [ { name : 'menu1', type : 'Menu', property : {title:'文件'}, pack : {}, child : [ { name : 'menuaction',type: 'Action' }, { name : 'action1',type: 'Action' }, { type : 'Separator' }, { name : 'action3',type: 'Action' }, { name : 'submenu1', type : 'Menu', property : { title : '方式' }, child : [ { name : 'action4',type: 'Action' }, { name : 'action5',type: 'Action' }, ] } ] } ] } ] };