Qt 设计器(Qt Designer)内容总结
1、概述
Qt 允许程序员不通过任何设计工具,以纯粹的 C++代码来设计一个程序。但是更多的
程序员更加习惯于在一个可视化的环境中来设计程序,尤其是在界面设计的时候。这是因为
这种设计方式更加符合人类思考的习惯,也比书写代码要快速的多。
Qt 也提供了这样一个可视化的界面设计工具:Qt 设计器(Qt Designer)。其开始界面
如上图所示。Qt 设计器可以用来开发一个应用程序全部或者部分的界面组件。以 Qt 设计器
生成的界面组件最终被变成 C++代码,因此 Qt 设计器可以被用在一个传统的工具链中,并
且它是编译器无关的。
在不同的平台上启动 Qt Designer 的方式有一定差别。在 Windows 环境下你可以在“开
始->程序->Qt”这个组件中找到 Qt Designer 的图标并点击;在 Unix 环境下,在命令行模式
下输入命令: “designer”;在 Mac Os 下,在 X Finder 下双击 Designer 图标。
默认情况下,Qt Designer 的用户界面是由几个顶级的窗口共同组成的。如果你更习惯
于一个 MDI-style 的界面(由一个顶级窗口和几个子窗口组成的界面),可以在菜单 Edit->User
Interface Mode 中选择 Docked Window 来切换界面。上图显示的就是 MDI-style 的界面风格。
2、开始学习
在这个小节中,我们将使用 Qt Designer 来生成一个对话框: Go-to-cell。对话框如下图
所示。
不管我们是使用 Qt Designer 还是编码来实现一个对话框,都包括以下相同的步骤:
1)、创建并初始化子窗口部件。
2)、将子窗口部件放置到布局当中。
3)、对 Tab 的顺序进行设置。
4)、放置信号和槽的连接。
5)、完成对话框的通用槽的功能。
现在开始工作。首先在 Qt Designer 的菜单中选择“File->New Form”。程序将弹出一
个窗口如下:
可以看到在窗口左上方有一个“templates\forms”的菜单,下面有四个可供选择的模板。
第一个和第二个都是对话框,区别在于对话框中按钮的位置不同。第三个是主窗口,第四
个是窗口部件。本例中我们需要选择第四个选项(Widget)。 现在你应该可以看到 Qt Designer
为你生成了一个窗口,标题栏是“Untitled”(也许你觉得第一个模板更加适合我们的例子,
不过,在这里,我们将手动添加“OK”和“Cancel”这两个按钮)。
我们按照上面讲过的顺序来设计这个窗口。首先需要生成子窗口部件并将它们放置
在工作台上。在 Qt Designer 工作界面的左侧,我们可以看到很多程序设计经常用到的窗口
部件。如果你需要它们中的那一个,用鼠标把它拖到工作台上就可以了。我们在菜单“Display
Widgets”中选择一个“Label”,在菜单“Input Widgets”中选择一个“Line Edit”,在菜单
“Spacers”中选择一个“Horizontal Spacer”(这个空白组件在最终形成的窗口中是不可见的,
在 Qt Designer 中,空白组件的样子就像是一个蓝色的弹簧),在菜单“Buttons”中选择两个
“Push Button”。按照下图的位置,将它们摆放起来。
你可以看到,我们的工作界面显的太大了一些,可以用鼠标拉住边框让它改变大小,
直到你满意为止。一个类似下图的组件是不是已经出现了?记住不要花费太多的时间来摆放
这些窗口部件的位置,只要大概类似就可以了,因为他们并不是不可调整的。Qt 的布局管
理器将会对他们的位置和大小自动进行一些优化。
现在我们已经创建了这些子窗口部件,并把他们放置在了合适的位置,接下来要做
的就是初始化他们。这需要设定这些子窗口的属性。在 Qt Designer 工作界面的右侧也同样
有一些窗口,这些就是属性窗口。 可以在这些窗口中找到所有部件需要设置的属性,并更改
它们,就可以达到我们的目的了。
1)、点击 TextLabel,确认它的“objectName”属性是“label”,然后将它的“text”属
性设置为“&Cell Location”。
2)、点击 line editor (窗口中的空白编辑框),确认它的“objectName”属性是“lineEdit”。
3)、点击第一个按钮(左侧),将其“objectName”属性设置为“OKButton”,“enable”
属性设置为“false”,“text”属性设置为“OK”,“default”属性设置为“true”。
4)、点击第二个按钮(右侧),将其“objectName”属性设置为“cancelButton”,“text”
属性设置为“Cancel”。
5)、点击工作平台的背景,这样我们可以选择整个的界面。这也是一个窗口,也拥有
自己的属性。我们把它的“objectName”属性设置为“GoToCellDialog”,“windowtTitle”属
性设置为“Go to Cell”。
完成后的 Form 变成了下图的形式:
接下来我们给 Label 设置一个伙伴(buddy),在这个例子中, Label 的伙伴当然是后面
的字符编辑框 line editor。在 Qt Designer 的菜单中进行选择:Edit->Edit Buddies。这样我们
进入 Buddy 模式,可以设置子窗口的伙伴了。点击 Label,Label 将会变成红色的,同时出
现一条线,将这条线拖拽到后面的 line editor 上,然后松开。这时两个窗口都将变成红色的,
中间有一条红线相连。移动鼠标到别处并点击,窗口将变成蓝色的。这说明我们已经设置成
功了(如果设置错误,则可以用鼠标在连接窗口的线条上点击,这时相连的窗口又会变成红
色的,此时按 Delete 键就可以取消设置)。选择:Edit->Edit Widget,可以退出这个模式,回
到主菜单中。如下图所示:
接下来我们对工作台上的各个子窗口进行布局:
1)、 点击标签“Cell Location”,按住 Shift 键,再点击后面的字符编辑框“line editor”,
这样它们两个窗口被同时选中。选择菜单:Form->Lay Out Horizontally。这样这两个窗口将
被一个红色边框的矩形包围。
2)点击空白子窗口“Spacer”,按住 Shift 键,再点击后面的两个按钮,同时选定这三
个窗口,然后选择菜单:Form->Lay Out Horizontally。这样这三个窗口将被一个红色边框的
矩形包围。
3)、点击工作平台的背景,取消任何已经选择的组件,然后选择:Form->Lay Out
Vertically。这样我们可以将第 1 步和第 2 步所生成的两个水平布局进行垂直布局。
4)、选择菜单:Form->Adjust Size。这样我们可以调整主窗口的大小。最后效果如下
图(图中的红线在程序最终生成的时候不会被显示):
然后我们对 Tab 的顺序进行设置。选择菜单:Edit->Edit Tab Order。一个带有数字的
蓝色矩形会显示在每一个窗口部件上(由于我们将 Label 和 line editor 设置为 buddy,这样
它们在指定 Tab 的时候被视为一个组件)。点击这些带数字的矩形可以调整到你想要的顺序
上,然后选择:Edit->Edit Widgets 离开这个模式。
现在我们的对话框的外形已经完成了。选择菜单:Form->Preview。这样你可以预览我
们设计的窗口。可以反复按下 Tab 键来验证顺序是否和我们设置的一致。点击顶部的关闭按
钮就可以关闭这个预览窗口。
所有的界面设计工作已经完成了,现在我们要做的就是保存下来。选择菜单: File->Save
Form As:。建立一个名为“gotocell”的文件夹,将这个界面保存为名称为“gotocelldialog.ui”
的文件中,放置到新建的文件夹中。
下面我们试着显示这个对话框。在文件夹“gotocell”中建立一个文件 main.cpp。在 cpp
文件中编写代码如下:
#include <QApplication>
#include <QDialog>
#include "ui_gotocelldialog.h"
int main (int argc, char *argv [])
{
QApplication app (argc, argv);
Ui::GoToCellDialog ui;
QDialog *dialog = new QDialog;
ui.setupUi (dialog);
dialog->show ();
return app. exec ();
}
现在我们运行 qmake 工具来生成一个工程文件 (.pro)和一个 makefile (qmake –project;
qmake gotocell.pro)。qmake 是一个足够聪明的工具,它可以侦测到当前文件夹中的用户界
面文件(在本例中就是我们刚才保存的 gotocelldialog.ui),并且自动生成合适的 makefile
规则来调用 uic(uic 就是 Qt 的用户界面编译器)。Uic 工具将把文件 gotocelldialog.ui 的内
容转换成 C++代码,写入自动生成的头文件“ui_gotocelldialog.h”中。
生成的头文件“ui_gotocelldialog.h”包括了对类 Ui::GoToCellDialog 的定义。这是一
个和 gotocelldialog.ui 文件内容相同的定义,只不过它是用 C++表示的。这个类声明了存储
各个子窗口和布局的成员变量,还包括一个 setupUi()的成员函数,用来初始化程序窗口。生
成的类就像下面所显示的一样:
class Ui::GoToCellDialog
{
public:
QLabel *label;
QLineEdit *lineEdit;
QSpacerItem *spacerItem;
QPushButton *okButton;
QPushButton *cancelButton;
...
void setupUi (QWidget *widget) {
...
}
};
这个自动生成的类并没有继承任何 Qt 的类。当我们在 main.cpp 中使用这个界面的时
候,我们创建一个 QDialog 并把它传递给函数 setupUi()。
现在可以编译并运行这个程序了。程序界面已经可以开始工作了,但是你会发现一些
问题——界面的功能并没有完全实现:
1)、按钮“OK”总是不可见的。
2)、按钮“Cancel”虽然可以显示,但它什么也不能做。
3)、字符编辑框接受任何字符,而不是像我们期望的那样只接受合法的数字。
显然我们应该通过编辑一些代码来使对话框函数正确地工作。最干净利落的方法是类
继承。我们创建一个新的类,这个类同时继承 QDialog(程序中创建的,被传递给函数
setupUi())和 Ui::GoToCellDialog,并完成这些未实现的功能(这也验证了一条名言:任何
软件的问题都可以通过添加一个间接的层来解决)。对于这样的一个类,我们的命名习惯是:
把 uic 生成的类去掉前缀“Ui::”作为我们的类名称。本例中,这个类叫 GoToCellDialog。
创建一个头文件 gotocelldialog.h,在里面编辑代码如下:
#ifndef GOTOCELLDIALOG_H
#define GOTOCELLDIALOG_H
#include <QDialog>
#include "ui_gotocelldialog.h"
class GoToCellDialog: public QDialog, public Ui::GoToCellDialog
{
Q_OBJECT
public:
GoToCellDialog (QWidget *parent = 0);
private slots:
void on_lineEdit_textChanged ();
};
#endif
有了头文件,我们还需要源文件。再创建一个 gotocelldialog.cpp,编辑代码如下:
#include <QtGui>
#include "gotocelldialog.h"
GoToCellDialog::GoToCellDialog (QWidget *parent)
: QDialog (parent)
{
setupUi (this);
QRegExp regExp ("[A-Za-z] [1-9] [0-9] {0, 2}");
lineEdit->setValidator (new QRegExpValidator (regExp, this));
connect (okButton, SIGNAL (clicked ()), this, SLOT (accept ()));
connect (cancelButton, SIGNAL (clicked ()), this, SLOT (reject ()));
}
void GoToCellDialog::on_lineEdit_textChanged ()
{
okButton->setEnabled (lineEdit->hasAcceptableInput ());
}
在构造函数中,我们调用了 setupUi()来初始化工作平台。由于采用了多重继承,这使
得我们可以直接访问类 Ui::GoToCellDialog 的成员变量。在用户界面生成之后,函数 setupUi()
将会把任何遵循命名规则:on_objectName_signalName()而命名的槽和“objectName”中的
信号 signalName()连接起来。在这个例子中,这意味着函数 setupUi()将会建立如下的连接:
connect (lineEdit, SIGNAL (textChanged (const QString &)),
this, SLOT (on_lineEdit_textChanged ()));
同样的,在构造函数中,我们设置了一个验证器(validator)来限制输入的范围。Qt
有三个内置的验证器,他们分别是:QIntValidator,QDoubleValidator 和 QRegExpvalidator。
这里我们使用了一个 QRegExpValidator 验证器,其规则表达式为"[A-Za-z] [1-9] [0-9] {0, 2}"。
它表示:允许一个大写或者小写的字母,这个字母后面紧跟着一个取值范围为[1-9]的数字,
再紧跟位数取值范围为[0-2] (即不可超过 100)的数字,这个数字每一位的取值范围是[0-9]。
我们把一个 this 指针传递给验证器 QRegExpValidator 的构造函数,这样我们使它成为
对象 GoToCellDialog 的一个子对象。通过这样,我们就不必在后面花费心思考虑对于这个
QRegExpValidator 的删除——它会在它的父对象被删除时自动销毁。
Qt 的父子机制是在 QObject 中实现的。当我们从父对象中产生一个子对象时(可以是
一个 widget,一个 validator 或者任何形式),父对象就把这个对象加入到它的子对象链表中。
当父对象被删除时,它会遍历这个链表并销毁每一个子对象。这些子对象然后再继续销毁属
于它们的子对象。如此的循环,直到没有对象剩下为止。
这种机制极大地简化了程序的内存管理任务,减少了内存泄露的危险。当我们删除父
窗口时,子窗口不光从内存中消失,从屏幕上也会消失。
在构造函数的最后,我们将按钮 OK 和 QDialog 的槽 accept()相连;将按钮 Cancel 和
槽 reject()相连。这两个槽都会关闭窗口,但是 accept()将对话框的结果传给 QDialog::Accepted
(实际上是整数 1);而 reject()则将对话框的结果传给 QDialog::Rejected (实际上是整数 0) 。
当我们使用这个对话框的时候,我们可以查看它的返回值来确认用户是否按下了按钮 OK;
程序运行是否正确。
槽 on_lineEdit_textChanged()的作用是:根据字符编辑框中的内容是否合法来决定按钮
OK 可见或不可见。函数 QLineEdit::hasAcceptableInput()使用了我们在构造函数中看到过的
验证器。
以上我们已经完成了对话框的所有工作。现在我们重写 main.cpp:
#include <QApplication>
#include "gotocelldialog.h"
int main (int argc, char *argv [])
{
QApplication app (argc, argv);
GoToCellDialog *dialog = new GoToCellDialog;
dialog->show ();
return app.exec ();
}
重新编译并执行程序。我们的程序可以正常工作了。
使用 Qt Designer 的好处之一是把程序员从必须修改他们的代码来适应界面的改变这
个烦恼中解脱出来。如果你使用纯粹的代码来编写界面,改变界面设计将耗费大量的时间。
应用 Qt Designer 将不会有任何时间上的损失: uic 工具会根据界面的改变自动更新代码。对
话框用户界面被保存为 .ui 文件的格式(基于 XML 的文件格式), 通过派生 uic 工具生成的
类,这种普遍的泛函性得到了实现。
|