多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 10.2 Ui_YourFormName.h 文件的组成 我们了解了 uic 工具的使用方法和公用,现在来看看它为我们生成的 C++头文件的组成 是什么样的,里面的“构件”又有哪些功用,这对我们理解后面几节的内容有着重要的作用 。 我们将使用 Qt Designer 创建一个界面文件,名为 calculatorform.ui,这个程序主要 是完成简单的加法计算功能。该界面的元素组合如图 10-1 所示。 ![](https://box.kancloud.cn/2016-01-22_56a1a1574ce42.png) 图 10-1 使用 Qt Designer 制作的界面 怎样做到在不使用 qmake 的情况下,就可以产生对应的头文件呢,我们遵循上一节介绍 的方法,在 Qt 的命令行下面输入如下命令: ``` uic –o ui_calculatorform.h calculatorform.ui ``` 小贴士:再次提醒,以 Windows XP 操作系统为例,进入 Qt 命令行的方法是:依次点击【开始】->【所有程序】->【Qt SDK by Nokia v2009.03 (open source)】->【Qt Command Prompt】。然后切换到你的.ui 文件所在的目录,执行上述命令即可。 以下是据此生成的 ui_calculatorform.h 文件的完整内容,我将分段向大家讲解。 ``` /******************************************************************************** ** Form generated from reading ui file 'calculatorform.ui' ** ** Created: Sun Sep 6 22:34:26 2009 ** by: Qt User Interface Compiler version 4.5.2 ** ** WARNING! All changes made in this file will be lost when recompiling ui file! ********************************************************************************/ ``` 这一段代码是 uic 工具自动生成的注释内容,它记录了该 C++头文件对应的原始.ui 文件的名字以及产生的时间和使用的 uic 的版本。最后一行提示内容很重要,它明确的告诉我们,不要手动修改该文件内容,因为在下次运行 qmake 或者 uic 时,它们会被覆盖掉。所以,我们如果需要对文件内容做修改,那么到 Qt Designer 中修改界面元素就行了,uic 会把所 有的改动反映出来。 ``` #ifndef UI_CALCULATORFORM_H #define UI_CALCULATORFORM_H #include <QtCore/QVariant> #include <QtGui/QAction> #include <QtGui/QApplication> #include <QtGui/QButtonGroup> #include <QtGui/QGridLayout> #include <QtGui/QHeaderView> #include <QtGui/QLabel> #include <QtGui/QSpacerItem> #include <QtGui/QSpinBox> #include <QtGui/QVBoxLayout> #include <QtGui/QWidget> ``` 上面这段的开头两句加上该文件结尾的 #endif // UI_CALCULATORFORM_H 这句构成了完整的防止头文件重复定义的卫哨,这是很好的编程规范,我们在自己书写代码时,也要遵循 这种严谨的做法。 后面的几句加入了程序中用到的窗口部件以及变量的头文件 。请大家注意它们的用法也 是非常规范的,正是许多 C++大师所提倡的“用到什么就包含什么,不用的就不包含 ”的做 法的应用典范。笔者发现有很多的朋友不论什么情况,都喜欢加入一句 #include <QtGui>或 者#include <QtCore>,这固然省事,因为它们包含了这两个模块下的所有子模块的定义 ,但 是这样一来,就降低了程序的编译速度,使得性能下降。 ``` QT_BEGIN_NAMESPACE class Ui_CalculatorForm { public: QGridLayout *gridLayout; QSpacerItem *spacerItem; QLabel *label_3_2; QVBoxLayout *vboxLayout; QLabel *label_2_2_2; QLabel *outputWidget; QSpacerItem *spacerItem1; QVBoxLayout *vboxLayout1; QLabel *label_2; QSpinBox *inputSpinBox2; QLabel *label_3; QVBoxLayout *vboxLayout2; QLabel *label; QSpinBox *inputSpinBox1; ``` 上面这段代码中,首先用 QT_BEGIN_NAMESPACE 宏表示开始进入名字空间。然后定义了一个名为 Ui_CalculatorForm 的类,它实际上是界面的实体类,界面上的所有元素都被定义 为相应的窗口部件的实例,并且它们一定是被定义为 public,即公有的成员,这将使得后面的 Ui 名字空间内的派生类能够继承它们。 ``` void setupUi(QWidget *CalculatorForm) { if (CalculatorForm->objectName().isEmpty()) CalculatorForm->setObjectName(QString::fromUtf8("CalculatorForm")); CalculatorForm->resize(400, 300); QSizePolicy sizePolicy(static_cast<QSizePolicy::Policy>(5), static_cast<QSizePolicy::Policy>(5)); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(CalculatorForm->sizePolicy().hasHeightForWidth()); CalculatorForm->setSizePolicy(sizePolicy); gridLayout = new QGridLayout(CalculatorForm); #ifndef Q_OS_MAC gridLayout->setSpacing(6); #endif #ifndef Q_OS_MAC gridLayout->setMargin(9); #endif gridLayout->setObjectName(QString::fromUtf8("gridLayout")); gridLayout->setObjectName(QString::fromUtf8("")); spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); gridLayout->addItem(spacerItem, 0, 6, 1, 1); label_3_2 = new QLabel(CalculatorForm); label_3_2->setObjectName(QString::fromUtf8("label_3_2")); label_3_2->setGeometry(QRect(169, 9, 20, 52)); label_3_2->setAlignment(Qt::AlignCenter); gridLayout->addWidget(label_3_2, 0, 4, 1, 1); vboxLayout = new QVBoxLayout(); #ifndef Q_OS_MAC vboxLayout->setSpacing(6); #endif vboxLayout->setMargin(1); vboxLayout->setObjectName(QString::fromUtf8("vboxLayout")); vboxLayout->setObjectName(QString::fromUtf8("")); label_2_2_2 = new QLabel(CalculatorForm); label_2_2_2->setObjectName(QString::fromUtf8("label_2_2_2")); label_2_2_2->setGeometry(QRect(1, 1, 36, 17)); vboxLayout->addWidget(label_2_2_2); outputWidget = new QLabel(CalculatorForm); outputWidget->setObjectName(QString::fromUtf8("outputWidget")); outputWidget->setGeometry(QRect(1, 24, 36, 27)); outputWidget->setFrameShape(QFrame::Box); outputWidget->setFrameShadow(QFrame::Sunken); outputWidget->setAlignment(Qt::AlignAbsolute|Qt::AlignBottom|Qt::AlignCenter|Qt::AlignHCenter |Qt::AlignHorizontal_Mask|Qt::AlignJustify|Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt:: AlignTop|Qt::AlignTrailing|Qt::AlignVCenter|Qt::AlignVertical_Mask); vboxLayout->addWidget(outputWidget); gridLayout->addLayout(vboxLayout, 0, 5, 1, 1); spacerItem1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); gridLayout->addItem(spacerItem1, 1, 2, 1, 1); vboxLayout1 = new QVBoxLayout(); #ifndef Q_OS_MAC vboxLayout1->setSpacing(6); #endif vboxLayout1->setMargin(1); vboxLayout1->setObjectName(QString::fromUtf8("vboxLayout1")); vboxLayout1->setObjectName(QString::fromUtf8("")); label_2 = new QLabel(CalculatorForm); label_2->setObjectName(QString::fromUtf8("label_2")); label_2->setGeometry(QRect(1, 1, 46, 19)); vboxLayout1->addWidget(label_2); inputSpinBox2 = new QSpinBox(CalculatorForm); inputSpinBox2->setObjectName(QString::fromUtf8("inputSpinBox2")); inputSpinBox2->setGeometry(QRect(1, 26, 46, 25)); vboxLayout1->addWidget(inputSpinBox2); gridLayout->addLayout(vboxLayout1, 0, 3, 1, 1); label_3 = new QLabel(CalculatorForm); label_3->setObjectName(QString::fromUtf8("label_3")); label_3->setGeometry(QRect(63, 9, 20, 52)); label_3->setAlignment(Qt::AlignCenter); gridLayout->addWidget(label_3, 0, 1, 1, 1); vboxLayout2 = new QVBoxLayout(); #ifndef Q_OS_MAC vboxLayout2->setSpacing(6); #endif vboxLayout2->setMargin(1); vboxLayout2->setObjectName(QString::fromUtf8("vboxLayout2")); vboxLayout2->setObjectName(QString::fromUtf8("")); label = new QLabel(CalculatorForm); label->setObjectName(QString::fromUtf8("label")); label->setGeometry(QRect(1, 1, 46, 19)); vboxLayout2->addWidget(label); inputSpinBox1 = new QSpinBox(CalculatorForm); inputSpinBox1->setObjectName(QString::fromUtf8("inputSpinBox1")); inputSpinBox1->setGeometry(QRect(1, 26, 46, 25)); vboxLayout2->addWidget(inputSpinBox1); gridLayout->addLayout(vboxLayout2, 0, 0, 1, 1); retranslateUi(CalculatorForm); QMetaObject::connectSlotsByName(CalculatorForm); } // setupUi ``` 上面这段代码是界面实体类的公有方法 setupUi()的内容。这是最为重要的一个成员函数,它完成了整个界面的构造和布局,代码的内容比较简单,遵循了我们前面反复讲到的使 用 Qt 编程的基本步骤,即先声明要使用到的窗口部件,然后实例化它们,再为它们设置属 性和方法。 QMetaObject::connectSlotsByName(CalculatorForm);这一句是需要重点说明的,因为 它实现了 CalculatorForm 界面实体类中的信号与槽机制。这个方法的原型是: ``` void QMetaObject::connectSlotsByName ( QObject * object ) [static] ``` 它是 QMetaObject 类的静态方法,作用是递归搜索 object 对象及子对象,然后将其中相匹配的信号与槽连接起来。所谓相匹配的信号与槽,就是要求槽函数的书写形式如下所示: ``` void on_<object name>_<signal name>(<signal parameters>); ``` 从编程人员的角度来看,只要将槽函数遵循上面的书写形式 ,就能够实现信号和槽的自动关联,而无需手写代码显式的完成。 再举个例子,假设我们的界面实体类中含有一个子对象,它的类型是 QPushButton,它 的 objectName 属性被设置为 btn1。那么为了使槽函数与信号 clicked()能够自动关联,我们 必须将槽函数写成如下形式: ``` void on_button1_clicked(); ``` 小贴士:QMetaObject 类是 Qt 元对象系统(Meta-Object System)的组成部分,是实现信号/槽机制、运行时类型信息以及 Qt 属性系统的基础。在一个应用程序中所有的 QObject 子类 都会创建一个属于自己的单独的 QMetaObject 的实例,并且这个实例存储了该子类的所有元 对象信息。这些通常包括类名(class name)、超类名(superclass name)、属性信息(properties)、信号与槽(signals and slots)等。 关于 Qt 的元对象系统,我们会在后面的第 13 章作详细介绍。 在后面我们会介绍编译时处理.ui 文件的 3 种方法:直接使用法、单继承法和多继承法。 无论使用它们中的哪一种,都要显式的调用 setupUi()这个方法来完成界面元素的构造和布 局,而它的参数就是你自定义的窗体类,这一点请大家在阅读时注意。 ``` void retranslateUi(QWidget *CalculatorForm) { CalculatorForm->setWindowTitle(QApplication::translate("CalculatorForm", "Calculator Form", 0, QApplication::UnicodeUTF8)); label_3_2->setText(QApplication::translate("CalculatorForm", "=", 0, QApplication::UnicodeUTF8)); label_2_2_2->setText(QApplication::translate("CalculatorForm", "Output", 0, QApplication::UnicodeUTF8)); outputWidget->setText(QApplication::translate("CalculatorForm", "0", 0, QApplication::UnicodeUTF8)); label_2->setText(QApplication::translate("CalculatorForm", "Input 2", 0, QApplication::UnicodeUTF8)); label_3->setText(QApplication::translate("CalculatorForm", "+", 0, QApplication::UnicodeUTF8)); label->setText(QApplication::translate("CalculatorForm", "Input 1", 0, QApplication::UnicodeUTF8)); Q_UNUSED(CalculatorForm); } // retranslateUi ``` 以上这段是 retranslateUi 方法的定义,它使得应用程序具备了支持国际化的能力 。其 中调用了 translate()方法,它是在 Qt 4.5 以后新引入的。 ``` namespace Ui { class CalculatorForm: public Ui_CalculatorForm {}; } // namespace Ui QT_END_NAMESPACE #endif // UI_CALCULATORFORM_H ``` 上面这段代码定义了名字空间 Ui,在其内部声明了一个名为 CalculatorForm 的类,它单公有继承自界面实体类 Ui_CalculatorForm,这就使得它继承了界面实体类的所有公有成 员。声明名字空间的用意是防止混淆命名。 注意,这个类的名字是与你的.ui 文件中 Form 的 objectName 的属性值相一致的。在后面的章节里,我们都要与这个类打交道,而不与界面实体类 Ui_CalculatorForm 发生联系,这样做就使得应用程序的界面设计与代码尽可能的分离开来,是现代编程思想的体现。