第二章 创建对话框 (快速对话框设计)

发布: 2008-05-29 22:20







相对于手写代码来说,Qt 被设计的更人性化,更找人喜欢,它的与众不同在于它不需要程序员完全手写 C++ 代码来开发整个 Qt 应用程序。很多程序员更喜欢用可视化的方法来设计窗体,因为他们发现这样比手写代码更自然更高效,并且他们希望能够更快速更简单地试验和更改他们的设计,而不是手写代码来实现窗体。


Qt 设计器 (Qt Designer) 通过提供一个可视化的设计能力扩展了程序员可用的选择。Qt 设计器 (Qt Designer) 可以被用来开发所有或只是某些应用程序的窗体。用Qt 设计器 (Qt Designer) 创建的窗体最终都将转化为 C++ 代码,所以 Qt 设计器 (Qt Designer) 可以被用作一个方便的工具并且不会给编译器增加任何要求。


在这部分,我们将用 Qt 设计器 (Qt Designer) 来创建图2.4显示的 Go-to-Cell 对话框。不管我们手写代码还是使用 Qt 设计器 (Qt Designer),创建一个对话框总是包含以下相同的基本步骤:




  • 创建并初始化子物件。




  • 将子物件放到布局中。




  • 设置 tab 顺序。




  • 建立信号/槽连接。




  • 实现对话框自定义的槽。




图 2.4. The Go-to-Cell dialog

 


 


要启动 Qt Designer,Windows 系统单击开始菜单中的 Qt by Trolltech v4.x.y|Designer ,在 Unix 中则要在命令行下键入 designer 名令,或者在 Mac OS X 发现器 (Finder) 中双击 designer。当 Qt Designer 启动后,它会弹出一个模版列表。单击 "Widget" 模版,然后单击 OK。( "Dialog with Buttons Bottom" 模版看起来不错,但是对于这个例子我们将手工创建 OK 和 Cancel 按钮来显示它是怎样被完成的。) 现在你应该拥有了一个名为 "Untitled" 的窗口。


默认 Qt Designer 的用户界面是由几个顶级窗口组成。如果喜欢一个 MDI 风格的界面,只有一个顶级窗口和一些子窗口,点击 Edit|User Iterface Mode|Docked Window。


第一步创建子物件并把它们放到窗体中。创建一个标签,一个行编辑器,一个水平空间占位符, 和两个按钮。对于每个,从 Qt Designer 的物件窗口拖拽它的名字或者图标并把它粗略的放在窗体合适的地方。在最终窗体中不可见的空白占位符在 Qt Designer 中显示为一个蓝色的弹簧。


 


Figure 2.5. Qt Designer in docked window mode on Windows

[View full size image]


 


 


现在拖拽窗体的底部边框使它便短。这将创建一个如图 2.6一样的窗体。不要在项目在窗口的放置上花费太多的时间;Qt 的布局管理器会精确的布局它们。


Figure 2.6. The form with some widgets

 


 


用 Qt Designer 属性编辑器设置每一个物件的属性:




  1. 点击 text 标签。确保它的 objectName 属性为 "label" 并设置 text 属性为 "&Cell Location:"。




  2. 点击行编辑器。确保 objectName 的属性为 "lineEdit".




  3. 点击第一个按钮。设置 objectName 属性为 "okButton",enabled 属性为 "false", text 属性为 "OK", default 属性 "true"。




  4. 点击第二个按钮。设置 objectName 属性为 "cancelButton" ,text 属性为 "Cancel".




  5. 点击窗体的背景选择窗体本身。设置 objectName 为 "GoToCellDialog",windowTitle 为"Go to Cell"。




所有的物件现在看起来好多了,除了显示 &Cell Location的 text 标签。点击 Edit|Edit Buddies 进入允许你设置buddies的特殊模式。然后,点击标签并拖拽红色箭头线 ,接下来, 点击此 label 并拖动红线到行编辑框,然后释放。此 label现在应该显示单元格位置并成为了行编辑器的伙伴了。点击 Edit|Edit 物件可以离开伴位模式。


Figure 2.7. The form with properties set

 


 


下一步布局窗体中的物件:


1. 点击 Cell Location 标签,按住 Shift 的同时点击 line editor 这样就可同时选中它们。点击 Form|Lay Out Horizontally,将它们水平对齐。


2. 点击空白占位符,然后按住Shift同时选中OK和Cancel按钮,点击Form|Lay Out Horizontally。


3. 点击窗体的背景选择所有的选择项,然后点击 Form|Lay Out Vertically。


4. 点击 Form|Adjust Size 来调整窗体为完美大小。


窗体中的红线显示了创建的布局。它们在程序运行时不现实。


Figure 2.8. The form with the layouts

 


 


现在点击 Edit|Edit Tab Order。在每个物件旁边将显示一个蓝色矩形中的数字接受焦点。点击依次按你想要接受焦点的每一个物件,然后点击 click Edit|Edit Widgets 退出tab顺序模式。


Figure 2.9. Setting the form's tab order

 


 


要预览对话框点击 Form|Preview 菜单选项。重复按 Tab 键检查 tab 顺序。用标题栏中的关闭按钮关闭对话框。


用 gotocelldialog 保存对话框.ui 文件用 gotocell 命名后存放在一个目录中,并用文本编辑器创建一个相同目录下的 main.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 goto-cell.pro)文件。qmake 工具能够智能的探测到用户界面文件 goto-celldialog.ui并能够生成合适 makefile 规则来调用 uic - Qt 的用户界面编译器。uic 工具将 gotocelldialog.ui转换成C++并把结果放到 ui-_gotocelldialog.h中。


生成的 ui_gotocelldialog.h 文件包含 Ui::GoToCellDialog 类的定义, which is a C++ equivalent of the gotocelldialog.ui file。类声明了存储窗体的子物件和布局的成员变量,和一个用来初始化窗体的 setupUI() 函数。生成的类象这样:


class Ui::GoToCellDialog


{


public:


QLabel *label;


QLineEdit *lineEdit;


QSpacerItem *spacerItem;


QPushButton *okButton;


QPushButton *cancelButton;


...


void setupUi(QWidget *widget) {


...


}


};


 


生成的类没有继承任何Qt类。当我们要将窗体应用到main.cpp时,我们创建一个 QDialog并把它传递给setupUI()。


如果你现在运行程序,对话框将会工作,但不像我们想象的那样完美:




  • OK 按钮总是被禁用。




  • Cancel 按钮也不会做任何事情。




  • 行编辑器接受任何输入文本,而不是今接受合法的 cell locations.




我们可以写一些代码使得对话框更合适。最简洁的方式就是创建一个同时继承了QDialog和UI::GoToCell-Dialog实现缺失的功能()(thus proving the adage that any software problem can be solved simply by adding another layer of indirection).我们的命名约定是给这个新类一个和uic生成的类相同的名字,只是没有了Ui::前缀。


打开一个文本编辑器,创建一个名为 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


 


The implementation belongs in 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_对象名_信号名() 格式的槽与对应的对象名的 singalName() 信息连接起来,


在我们的例子中,这意味着setupUi() 将创建下面的信号/槽连接:


connect(lineEdit, SIGNAL(textChanged(const QString &)),


this, SLOT(on_lineEdit_textChanged()));


 


还是在构造函数中,我们创建一个验证器来严格定义输入范围。Qt 提供了三个內建验证器类:QIntValidator, QDoubleValidator和QRegExpValidator。这里我们用一个"[A-Za-z][1-9][0-9]{0,2}" 正则表达式类QRegExpValidator:意思是允许包含





大小写字母,随后是一个0-9的数字,然后是一个0-3位,每位都在0-9之间的数字。


(关于正则表达式,可以看一下 QRegExp 类的文档)


 








通过把它传递给 QRegExpValidator 类的构造函数,把它变成了GotoCellDialog对象的一个子对象。这样,我们不必担心以后再删除 QRegExpValidator ; 它会在交对象被删除的时候自己删除。


 


Qt 的父子机制在 QObject 中实现。当我们创建一个具有父对象的对象 (a widget, validator, or any other kind) ,父对象将这个对象添加到他的子对象列表中。当父对象被删除时,它浏览它的子对象列表删除每个子对象。子对象本身删除他的子对象,依次递归直到全部删除。


父子机制大大的简化了内存管理,减少了内存泄露的风险。我们唯一需要明确删除的就是我们用new创建的和没有父类的对象。并且如果我们在删除父对象之前删除了子对象,Qt将自动从父对象的子对象列表中将这个对象删除。


对于物件,父亲还有另外一个意义:子物件在父物件中显示。当我们删除父物件时,子物件不仅在内存消失,它还将从屏幕上消失。


在构造函数的末端,我们连接 OK 按钮到 QDialog 的 accept() 槽上,Cancel 按钮到 reject() 槽上。两个按钮都将关闭对话框,但是 accept() 设置对话框的结果值到 QDialog::Accepted (which equals 1), 而 reject() 则为 QDialog::Rejected (which equals 0)设置值。当我们用这个对话框,我们 When we use this dialog, we can use the result value to see if the user clicked OK and act accordingly.


on_lineEdit_textChanged() 槽开启或禁用 OK 按钮,根据行编辑器是否包含合法的 cell location。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();


}


 


重新编译应用程序 (qmake -project; qmake gotocell.pro) 并再次运行它。在行编辑器中键入 "A12" ,你会发现 OK 按钮变为可用。试着键入一些随机的文本看看验证器是如何工作的。点击 Cancel 对话况。


Qt Designer 的一个优点就是它允许程序员自由修改他们的窗体设计而不需要被迫改变源代码。如果你完全靠手写C++代码来创建窗体,更改窗体设计将是非常耗时的。有了 Qt Designer,你将不再浪费时间,因为当窗体改变时uic将重新生成它的源代码。对话框的用户界面被保存在a.ui文件 (一种基于XML的文件格式) 中,而自定义的功能则通过继承uic生成的类来实现。


 



原文: http://qtchina.tk/?q=node/111

Powered by zexport