Qt备忘录之四:可视化窗体设计原理
- 再创世纪·代码厨房
- 2015-03-16
- 74热度
- 2评论
可视化的窗体设计其实并不陌生,在MFC或者.net框架中都用的到。Qt也提供了类似的功能。不论是在Qt Creator中或是用VS-AddIn新建或打开Qt工程,默认都会使用Qt Designer对打开.ui文件进行编辑。用Qt的可视化功能搭建窗体操作流程与其他工具类似,本文将不同之处列举一下,并探究一下原理。本文中所用的简单示例可点此下载。
Buddy及其设置
Buddy是Qt里面比较特殊的一种控件间关系。从效果上,这种关系可以简单表述为:当按下一个label对象的快捷键之后,焦点会被它的buddy接收。例如在Qt Designer里新建一个如下所示的窗体:
其中,label对象的Text表示为Go to My &Buddy,即定义了Alt+B作为该label的快捷键。现在编译之后生成的窗体如下图所示,其中label的Text依旧是Go to My &Buddy,“&”标识还可见,按下Atl+B也没有任何效果。
Edit(编辑)——Edit Buddies(编辑伙伴)之后,可以编辑label对象的buddies。
编辑之后,可以看见buddy视图中,有一个从label指向PushButton的箭头,即表示这个PushButton作为label的buddy。返回Widget视图后,可以看见label的Text变为“Go to My Buddy”。
检查label对象的属性,可以看见在buddy一栏出现了pushButton。
编译之后,按下Alt+B,确实可以触发pushButton动作。
建立同窗体signal与slot的连接
现在在这个工程中添加一个LCD Number对象,然后通过Edit(编辑)——Edit Signals/Slots(编辑信号/槽)建立pushButton与LCD Number的signal/slot连接。
建立连接后,在signals/slots视图中,可以看见一个由pushButton指向LCD Number的箭头,箭头的两端分别是clicked()和hide(),即表示将pushButton的clicked()与LCD Number的hide()相连接。
也可以通过查看signals & slots关系图,证明上述关系已建立。
编译后运行,单击pushButton或者按Alt+B,LCD Number消失(隐藏)。程序执行如预期。
把.ui炼成.h
关闭Qt Designer,用文本查看工具打开.ui文件,可以发现其实.ui文件是一个XML格式的文件。看看这里面有些什么:
qtdesignertest.ui
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtDesignerTest</class><!--主类--> <widget class="QWidget" name="QtDesignerTest"><!--声明名为QtDesignerTest的QWidget对象--> <property name="geometry"><!--QWidget对象的geometry(几何)属性--> <rect> <x>0</x> <y>0</y> <width>315</width> <height>42</height> </rect> </property> ……
所有在Qt Designer中设计的对象,都会以对象/属性声明的方式在XML中体现,与一般XML的用法差别不大。体现Qt特点的buddy和signals/slots用如下的方法体现:
…… <widget class="QLabel" name="label"><!--声明名为label的Qlabel对象--> <property name="geometry"> <rect> <x>11</x> <y>11</y> <width>84</width> <height>16</height> </rect> </property> <property name="text"><!--label的text属性--> <string>Go to My &Buddy</string><!--&符号被转换为&--> </property> <property name="buddy"><!--buddy也是label的属性--> <cstring>pushButton</cstring><!--这里是指定pushButton作为label的buddy--> </property> </widget> ……
从这里再次印证buddy是QLabel的属性。只是这里将buddy的前后用<cstring>标签包起来,没有理解是什么意思。以后如果想明白了,就在这里补充。
…… <layoutdefault spacing="6" margin="11"/><!--这个似乎是默认的布局--> <resources/><!--这个标签很奇怪,只有尾没有头--> <connections><!--这一节就是定义signal和slot的连接了--> <connection> <sender>pushButton</sender> <signal>clicked()</signal> <receiver>lcdNumber</receiver> <slot>hide()</slot> <hints><!--这两个是Qt Designer中connect箭头的前后位置,其实对于程序没有作用的--> <hint type="sourcelabel"> <x>199</x> <y>30</y> </hint> <hint type="destinationlabel"> <x>263</x> <y>30</y> </hint> </hints> </connection> </connections> </ui>
这一段中,</resource>标签比较奇怪,只有结尾没有开头,不符合XML的规范。按我的理解,应该是指代这个标签之上的所有代码都是“资源”吧。Qt会将这个.ui文件转换为一个以ui_为前缀的.h文件,大概长这个样子:
ui_qtdesignertest.h
/******************************************************************************** ** Form generated from reading UI file 'qtdesignertest.ui' ** ** Created by: Qt User Interface Compiler version 5.4.0 ** ** WARNING! All changes made in this file will be lost when recompiling UI file! ********************************************************************************/ #ifndef UI_QTDESIGNERTEST_H #define UI_QTDESIGNERTEST_H #include <QtCore/QVariant> #include <QtWidgets/QAction> #include <QtWidgets/QApplication> #include <QtWidgets/QButtonGroup> #include <QtWidgets/QHeaderView> #include <QtWidgets/QLCDNumber> #include <QtWidgets/QLabel> #include <QtWidgets/QPushButton> #include <QtWidgets/QWidget> QT_BEGIN_NAMESPACE class Ui_QtDesignerTest { public: QLabel *label; QPushButton *pushButton; QLCDNumber *lcdNumber; void setupUi(QWidget *QtDesignerTest) { if (QtDesignerTest->objectName().isEmpty()) QtDesignerTest->setObjectName(QStringLiteral("QtDesignerTest")); QtDesignerTest->resize(315, 42); label = new QLabel(QtDesignerTest); label->setObjectName(QStringLiteral("label")); label->setGeometry(QRect(11, 11, 84, 16)); pushButton = new QPushButton(QtDesignerTest); pushButton->setObjectName(QStringLiteral("pushButton")); pushButton->setGeometry(QRect(110, 10, 86, 23)); lcdNumber = new QLCDNumber(QtDesignerTest); lcdNumber->setObjectName(QStringLiteral("lcdNumber")); lcdNumber->setGeometry(QRect(208, 11, 64, 23)); lcdNumber->setProperty("value", QVariant(3.14)); #ifndef QT_NO_SHORTCUT label->setBuddy(pushButton); #endif // QT_NO_SHORTCUT retranslateUi(QtDesignerTest); QObject::connect(pushButton, SIGNAL(clicked()), lcdNumber, SLOT(hide())); QMetaObject::connectSlotsByName(QtDesignerTest); } // setupUi void retranslateUi(QWidget *QtDesignerTest) { QtDesignerTest->setWindowTitle(QApplication::translate("QtDesignerTest", "QtDesignerTest", 0)); label->setText(QApplication::translate("QtDesignerTest", "Go to My &Buddy", 0)); pushButton->setText(QApplication::translate("QtDesignerTest", "I'm The BUDDY", 0)); } // retranslateUi }; namespace Ui { class QtDesignerTest: public Ui_QtDesignerTest {}; } // namespace Ui QT_END_NAMESPACE #endif // UI_QTDESIGNERTEST_H
这已经是c++的标准语法了,都看得明白,就是把刚刚在.ui里面实现的东西写成c++的一个类。其中setupUi(QWidget*)用于将各种对象根据给定的规则搭建窗体,retranslateUi(QWidget*)用于将与用户交互的各个对象的文字部分进行国际化设置(使用translate()函数)。
回到cpp文件中,我们看看界面是怎么形成的。
qtdesignertest.cpp
#include "qtdesignertest.h" #include "ui_qtdesignertest.h" QtDesignerTest::QtDesignerTest(QWidget *parent) : QWidget(parent), ui(new Ui::QtDesignerTest) { ui->setupUi(this); } QtDesignerTest::~QtDesignerTest() { delete ui; }
就一句,调用setupUi(QWidget*),也就是上面ui_qtdesignertest.h中的setupUi(QWidget*)。其中ui是UI::QtDesignerTest类的实例对象。这里的QtDesignerTest类和UI::QtDesignerTest类是不同的,ui_qtdesignertest.h中可以看到,UI::QtDesignerTest是由Ui_QtDesignerTest类继承而来。
由此产生一个想法:后面的工程中,直接继承Ui_QtDesignerTest类来实现界面的修改和功能添加应该也是可以的。
二零一五年三月十六日
顾毅 写于厦门
脑公你认真工作的样子好帅呀呀呀口牙(花痴脸)
狐狸爱蜂蜜Y(^o^)Y