qt-GUI 程序设计基础

 目录

 (一). GUI 程序结构与运行机制

一. GUI 项目文件组成

二.项目配置文件

三. UI 文件

四. 主程序文件

五. 窗口相关的文件

1.文件 widget.h

2.文件 widget.cpp

3.文件 widget.ui

4.文件 ui_widget.h

5.窗口的创建与初始化

(二). 可视化 UI 设计

一. 窗口界面可视化设计

1.创建项目和资源文件

2.窗口界面设计

二.界面组件布局管理

1.界面组件的层次关系

2.布局管理

3.伙伴关系与 Tab 顺序

三. 信号与槽简介

四. 信号与槽的使用

1.信号与槽编辑器的使用

2.为组件的信号生成槽函数原型和框架

3.使用自定义槽函数

五. 为应用程序设置图标

六. Qt 项目构建过程基本原理

1.元对象系统和 MOC

2.UI 文件和 UIC

3.资源文件和 RCC

4.标准 C++编译器

(三). 代码化 UI 设计

一. 示例功能概述

二. 示例功能实现

1.Dialog 类的定义

2.界面组件的创建与布局

3.信号与槽的关联


(一). GUI 程序结构与运行机制

一. GUI 项目文件组成

项目包含以下一些文件。

• example1.pro 是 qmake 构建系统的项目配置文件,其中存储了项目的各 种设置内容。

• widget.ui 是 UI 文件,这是用于窗口界面可视化设计的文件。

• main.cpp 是主程序文件,包含 main()函数。

• widget.h 是窗口类定义头文件,它用到了 UI 文件 widget.ui 中的一个可视 化设计的窗口界面。

• widget.cpp 是对应于 widget.h 的源程序文件。


二.项目配置文件

在使用向导创建项目时,如果选择 qmake 构建系统,就会生成一个后缀为“.pro”的项目配置文件,文件名就是项目的名称。文件 example1.pro 是本项目的项目配置文件,其内容如下:

QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
SOURCES += main.cpp \
widget.cpp
HEADERS += widget.h
FORMS += widget.ui
# Default rules for deployment
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

qmake 是构建项目的工具软件,  qmake 的作用是根据项目配置文件中的设置生成 Makefile 文件,然后 C++编译器就可以根据 Makefile 文件进行编译和连接。对于 Qt 项目, qmake 还会自动为元对象编译器( meta-object compiler,  MOC)和用户界面编译器( user interface compiler,  UIC)生成构建规则。
Qt 项目的配置文件是自动生成的,一般不需要手动修改。配置文件中“ #”用于标识注释语句。配置文件中有一些全大写的单词,这是 qmake配置文件中的变量,一些常见变量的含义如下表所示。

qmake 配置文件中常见变量的含义
变量 含义
QT 项目中使用的 Qt 模块列表,在用到某些模块时需要手动添加
CONFIG 项目的通用配置选项
DEFINES 项目中的预处理定义列表,例如可以定义一些用于预处理的宏
TEMPLATE 项目使用的模板,项目模板可以是应用程序(app)或库(lib)。如果不设置就默认为应用程序
HEADERS 项目中的头文件( .h 文件)列表
SOURCES 项目中的源程序文件( .cpp 文件)列表
FORMS 项目中的 UI 文件( .ui 文件)列表
RESOURCES 项目中的资源文件( .qrc 文件)列表
TARGET 项目构建后生成的应用程序的可执行文件名称,默认与项目名称相同
DESTDIR 目标可执行文件的存放路径
INCLUDEPATH 项目用到的其他头文件的搜索路径列表
DEPENDPATH 项目其他依赖文件(如源程序文件)的搜索路径列表

其中一些变量用于对项目中包含的文件和路径进行管理,如 HEADERS、 FORMS、ESOURCES、INCLUDEPATH 等,当我们向项目中添加文件时,  Qt Creator 会自动更新配置文件中的内容。在项目管理目录树中,右键点击项目节点可调出快捷菜单,点击 Add Existing Files 菜单项,可以将已有的文件添加到项目中。

变量 QT 可用于定义项目中用到的 Qt 模块。如果项目中需要用到 Qt 框架中的一些附加模块, 需要在项目配置文件中将模块加入 QT 变量。例如要在项目中使用 Qt SQL 模块,就需要在项目配置文件中加入如下的一行语句:

QT += sql

qmake 中提供替换函数( replace function)用于在配置过程中处理变量或内置函数的值,“$$”
是替换函数的前缀,后面可以是变量名或 qmake 的一些内置函数。例如下面的一行语句:

qnx: target.path = /tmp/$${TARGET}/bin

其中的 "$${TARGET}" 就是替换函数,表示用变量TARGET 的值替换。将 "$${TARGET} "写成 "$$TARGET"也是可以的。

三. UI 文件

后缀为.ui 的文件是用于窗口界面可视化设计的文件,如 widget.ui。双击项目管理目录树中的
文件 widget.ui, Qt Creator 会打开内置的 Qt Designer 对窗口界面进行可视化设计,如左图:

qt-GUI 程序设计基础_第1张图片

Qt Designer 有以下一些功能区域。
• 窗口左侧是组件面板,分为多个组,如 Layouts、Buttons、Display Widgets 等,界面设计的常用组件都可以在组件面板里找到。

• 窗口中间主要区域是待设计的窗体。如果要将某个组件放置到窗体上,从组件面板上拖动 一个组件放置到窗体上即可。例如,放置一个标签(Label)和一个按钮(Push Button)到窗体上。

• 待设计窗体下方有 Action 编辑器(Action Editor)和信号与槽编辑器(Signals and Slots Editor)。Action 编辑器用于可视化设计 Action,信号与槽编辑器用于可视化地进行信号与 槽的关联。 • 窗口上方有一个布局和界面设计工具栏,工具栏上的按钮主要用于实现布局和界面设计。 

• 窗口右侧上方是对象检查器(Object Inspector),它用树状视图显示窗体上各组件的布局和层级关系。视图有两列,显示了每个组件的对象名称(objectName)和类名称。

• 窗口右侧下方是属性编辑器(Property Editor)。属性编辑器显示窗体上选中的组件或窗体的各种属性,可以在属性编辑器里修改这些属性的值。

上右图是选中窗体上放置的标签后属性编辑器的内容。最上方显示的文字“ labDemo:QLabel”表示这个组件是一个 QLabel 类的组件, 对象名称是 labDemo。 属性编辑器的内容分为两列,Property列是属性的名称, Value 列是属性的值。属性又分为多个组,实际上表示类的继承关系,例如在图中,可以看出 QLabel 类的继承关系是 QObject→QWidget→QFrame→QLabel。

objectName 属性表示组件的对象名称,每个组件都需要一个唯一的对象名称,以便在程序中被引用。组件的命名应该遵循一定的规则,具体使用什么样的命名规则根据个人习惯而定,主要是要便于区分和记忆,也要便于与普通变量区分。在属性编辑器里可以修改组件的属性值,例如将 labDemo 的 text 属性修改为“ Hello Qt6”,我们还修改了它的 font 属性。
我们在图所示的窗体上放置了一个标签和一个按钮,修改了它们的一些属性,也修改了窗体的一些属性。窗体和组件修改的主要属性如下表所示。注意,修改窗体的字体后,窗体上所有组件的字体默认与窗体的字体相同。

窗体和组件修改的主要属性
对象名称 类名称 属性设置 备注
Widget QWidget windowTitle="First Demo"
font.pointSize=10
设置窗口的标题,修改窗口的字体大小
btnClose QPushButton text="Close" 设置按钮的文字

labDemo

QLabel

text="Hello Qt6"
font.pointSize=20
font.bold=true

设置标签显示的文字和字体

再为按钮 btnClose 增加一个功能,即点击此按钮时退出程序,可以使用信号与槽编辑器完成这个功能。点击下图左工具栏上的添加按钮 会新增一行,按照图中的内容进行设置。这样的设置表示将按钮 btnClose 的 clicked()信号与窗口 Widget 的公有槽 close()关联起来。
构建项目并运行,图右是程序运行时界面,点击 Close 按钮可以关闭程序。标签的文字和字体被修改了,窗口标题也显示为所设置的标题,而我们并没有手动编写代码。
qt-GUI 程序设计基础_第2张图片

四. 主程序文件

文件 main.cpp 中有 main()函数,下面是文件 main.cpp 的完整代码。

#include "widget.h"
#include 
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //定义并创建应用程序
Widget w; //定义并创建窗口对象
w.show(); //显示窗口
return a.exec(); //运行应用程序,开始应用程序的消息循环和事件处理
}

main()函数是 C++程序的入口。它的主要功能是定义并创建应用程序,定义并创建窗口对象和显示窗口,运行应用程序,开始应用程序的消息循环和事件处理。

QApplication 是 Qt 的标准应用程序类, main()函数里的第一行代码定义了一个 QApplication类型的变量 a,它就是应用程序对象。然后定义了一个 Widget 类型的变量 w, Widget 是本示例设计的窗口的类名称,定义变量 w 就是创建窗口对象,然后用 w.show()显示此窗口。

函数里最后一行用 a.exec()启动应用程序, 开始应用程序的消息循环和事件处理。 GUI 应用程序是事件驱动的,窗口上的各种组件接收鼠标或键盘的操作,并进行相应的处理。

五. 窗口相关的文件

在 Qt Creator 的 Projects 设置界面,取消勾选 Shadow build 复选框,然后以 Release 模式构建
项目。构建项目后,项目的根目录下会生成一个文件 ui_widget.h。这样,与窗口相关的文件有 4
个,各文件的说明如下表所示。

与窗口相关的 4 个文件
文件 说明
widget.h 定义了窗口类 Widget
widget.cpp 实现 Widget 类的功能的源程序文件
widget.ui 窗口 UI 文件,用于在 Qt Designer 中进行 UI 可视化设计。widget.ui 是一个 XML 文件,存储界面上各 个组件的属性和布局内容
ui_widget.h UI 文件经过 UIC 编译后生成的文件,这个文件里定义了一个类,类的名称是 Ui_Widget,用 C++语言 描述 UI 文件中界面组件的属性设置、布局以及信号与槽的关联等内容

1.文件 widget.h

文件 widget.h 是定义窗口类的头文件。在创建项目时,我们选择窗口基类是 QWidget,文件 widget.h 中定义了一个继承自 QWidget 的类 Widget,下面是文件 widget.h 的内容。

#include 
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; } //一个名字空间 Ui,包含一个类 Widget
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr); //构造函数
~Widget(); //析构函数
private:
Ui::Widget *ui; //使用 Ui::Widget 类定义的一个对象指针
};

文件 widget.h 有几个重要的组成部分。
( 1)窗口 UI 类 Ui::Widget 的声明。文件中有如下几行代码:

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; } //一个名字空间 Ui,包含一个类 Widget
QT_END_NAMESPACE

上述代码声明了一个名称为 Ui 的名字空间( namespace),它包含一个类 Widget。注意,这个类 Widget 并不是本文件的代码里定义的类 Widget,而是文件 ui_widget.h 里定义的一个类,是用于描述可视化设计的窗口界面的,所以我们称 Ui::Widget 类是窗口 UI 类。这种声明相当于外部类型声明,需要了解对文件 ui_widget.h 的解释之后才能搞明白。
2)窗口类 Widget 的定义。文件 widget.h 的主体部分是一个继承自 QWidget 的类 Widget 的定义。 Widget 类中包含创建窗口界面,实现窗口上界面组件的交互操作,以及其他业务逻辑。
Widget 类定义的第一行语句插入了一个宏 Q_OBJECT,这是使用 Qt 元对象系统的类时必须插入的一个宏。插入了这个宏之后, Widget 类中就可以使用信号与槽、属性等功能。
Widget 类的 private 部分定义了一个指针,代码如下:

Ui::Widget *ui;

这个指针是用前面声明的名字空间 Ui 里的类 Widget 定义的,所以 ui 是窗口 UI 类的对象指 针,它指向可视化设计的窗口界面。要访问界面上的组件,就需要通过这个指针来实现。

2.文件 widget.cpp

widget.cpp 是对应于文件 widget.h 的源程序文件,其完整代码如下:

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}

在这个文件的开头部分自动加入了如下的一行包含语句:

#include "ui_widget.h"

文件 ui_widget.h 是 UI 文件 widget.ui 被 UIC 编译后生成的文件, 这个文件里定义了窗口 UI 类。
Widget 类目前只有构造函数和析构函数。其中构造函数头部语句如下:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)

这行代码的功能是运行父类 QWidget 的构造函数,创建一个 Ui::Widget 类的对象 ui。这个 ui就是 Widget 类的 private 部分定义的指针变量 ui。
构造函数里只有一行代码:

ui->setupUi(this);

它表示运行了 Ui::Widget 类的 setupUi()函数,并且以 this 作为函数 setupUi()的输入参数,而this 就是 Widget 类对象的实例,也就是一个窗口。 setupUi()函数里会创建窗口上所有的界面组件,并且以 Widget 窗口作为所有组件的父容器,这需要看了后面的代码才能明白。
所以,在文件 ui_widget.h 里有一个名字空间 Ui,里面有一个类 Widget,记作 Ui::Widget,它是窗口 UI 类。文件 widget.h 里的类 Widget 是完整的窗口类。在 Widget 类里访问 Ui::Widget类的成员变量或函数需要通过 Widget 类里的指针 ui 来实现, 例如构造函数里运行的 ui-> setupUi(this)。

3.文件 widget.ui

文件 widget.ui 是窗口界面定义文件,是一个 XML 文件。它存储了界面上所有组件的属性设置、布局、信号与槽函数的关联等内容。用 Qt Designer 打开 UI 文件进行窗口界面可视化设计, 保存修改后会重新生成 UI 文件,所以,我们不用关注文件 widget.ui 是怎么生成的,也不用关注此文件内容的意义。

4.文件 ui_widget.h

在构建项目时,UI 文件 widget.ui 会被 Qt 的 UIC 编译为对应的 C++语言头文件 ui_widget.h。 这个文件并不会出现在 Qt Creator 的项目管理目录树里,它是构建项目时的一个中间文件。文件 ui_widget.h 的完整内容如下。

/********************************************************************************
** Form generated from reading UI file 'widget.ui'
** Created by: Qt User Interface Compiler version 6.2.3
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include 
#include 
#include 
#include 
#include 
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
QLabel *labDemo;
QPushButton *btnClose;
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName(QString::fromUtf8("Widget"));
Widget->resize(271, 162);
QFont font;
font.setPointSize(10);
Widget->setFont(font);
labDemo = new QLabel(Widget);
labDemo->setObjectName(QString::fromUtf8("labDemo"));
labDemo->setGeometry(QRect(65, 40, 156, 35));
QFont font1;
font1.setPointSize(20);
font1.setBold(true);
labDemo->setFont(font1);
btnClose = new QPushButton(Widget);
btnClose->setObjectName(QString::fromUtf8("btnClose"));
btnClose->setGeometry(QRect(160, 105, 81, 31));
retranslateUi(Widget);
QObject::connect(btnClose, &QPushButton::clicked, Widget,
qOverload<>(&QWidget::close));
QMetaObject::connectSlotsByName(Widget);
} // setupUi
void retranslateUi(QWidget *Widget)
{
Widget->setWindowTitle(QCoreApplication::translate("Widget", "First Demo", nullptr));
labDemo->setText(QCoreApplication::translate("Widget", "Hello Qt6", nullptr));
btnClose->setText(QCoreApplication::translate("Widget", "Close", nullptr));
} // retranslateUi
};
namespace Ui {
class Widget: public Ui_Widget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H

查看文件 ui_widget.h 的内容,发现它主要做了以下一些工作。
( 1)定义了一个类 Ui_Widget,用于封装可视化设计的界面。注意, Ui_Widget 类没有父类,
不是从 QWidget 继承而来的,所以 Ui_Widget 不是一个窗口类。
( 2) Ui_Widget 类的 public 部分为界面上每个组件定义了一个指针变量,变量的名称就是 UI 可视化设计时为组件设置的对象名称。例如,为界面上的 QLabel 和 QPushButton 组件自动生成的定义是:

QLabel *labDemo;
QPushButton *btnClose;

( 3) Ui_Widget 类中定义了一个函数 setupUi(),其输入输出参数定义如下:

void setupUi(QWidget *Widget)

它有一个输入参数 Widget,是 QWidget 类型的指针。函数 setupUi()的代码的第一部分是设置
Widget 的一些属性,例如:

if (Widget->objectName().isEmpty())
Widget->setObjectName(QString::fromUtf8("Widget"));
Widget->resize(271, 162);

函数 setupUi()的输入参数 Widget 表示一个空的 QWidget 类型的窗口, 它的对象名称被设置为
“ Widget”,也就是我们在 Qt Designer 中设计的窗体的对象名称。
函数 setupUi()的代码的第二部分是创建界面组件 labDemo 和 btnClose,并设置其属性。在创
建界面组件 labDemo 和 btnClose 时用到了函数 setupUi()的输入参数 Widget,代码如下:

labDemo = new QLabel(Widget);
btnClose = new QPushButton(Widget);

这是将 Widget 作为 labDemo 和 btnClose 的父容器。在创建界面组件时必须为组件设置父容
器, 容器可以嵌套, 没有父容器的界面组件就是独立的窗口。 所以, 这两行代码就是实现将 labDemo和 btnClose 显示在 QWidget 类型的窗口 Widget 上。
接下来, setupUi()里调用了一个函数 retranslateUi(),其功能是设置界面上各组件的文字属性,
如标签的文字、按钮的文字、窗口的标题等。界面上所有与文字设置相关的功能都集中在函数
retranslateUi()里,在设计多语言界面时会用到这个函数。
函数 setupUi()的最后部分是设置信号与槽的关联,有以下两行代码:

QObject::connect(btnClose, &QPushButton::clicked, Widget, qOverload<>(&QWidget::close));
QMetaObject::connectSlotsByName(Widget);

第一行是运行 connect()函数,就是图 2-4 所示的信号与槽编辑器中信号与槽关联的实现代码,也就是将按钮 btnClose 的 clicked()信号与窗口 Widget 的 close()槽函数关联起来。这样,当点击
btnClose 按钮时,就会运行 Widget 的 close()槽函数,而槽函数 close()的功能是关闭窗口。
第二行是设置槽函数的关联方式,用于将窗口上各组件的信号与槽函数自动连接。
( 4)定义名字空间 Ui,并定义一个从 Ui_Widget 继承的类 Widget,代码如下:

namespace Ui {
class Widget: public Ui_Widget {};
}

文件 ui_widget.h 里封装界面的类是 Ui_Widget。 再定义一个类 Widget 从 Ui_Widget 继承而来,
并将其定义在名字空间 Ui 里,这样 Ui::Widget 与文件 widget.h 里的类 Widget 同名,但是用名字
空间区分开。

5.窗口的创建与初始化

再回过头来看文件 widget.h 里 Widget 类的定义,它定义了 Ui::Widget 类型的指针变量 ui,通过这个指针就可以访问界面上的所有组件,因为在 Ui::Widget 类中,界面组件对象都是公有成员。
在文件 widget.cpp 中, Widget 类的构造函数代码如下:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
}

构造函数创建了 Ui::Widget 类对象 ui,运行 ui->setupUi(this)进行窗口界面初始化,包括创建所有界面组件、设置属性、设置信号与槽的关联。 this 是 Widget 实例对象,它是一个 QWidget 窗口,是 setupUi(this)中创建的所有界面组件的父容器。
在 Widget 类中编写程序时,可以通过 ui 指针访问窗口界面上的所有组件。我们稍微修改Widget 类的构造函数,例如修改 labDemo 和 btnClose 的显示文字,代码如下:

Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget)
{
ui->setupUi(this);
ui->labDemo->setText("欢迎使用 Qt6");
ui->btnClose->setText("关闭");
}

(二). 可视化 UI 设计

示例运行时界面如下图所示。这个 示例的设计过程包含 GUI 应用程序设计的完整过程,其中涉及一些关键技术。

qt-GUI 程序设计基础_第3张图片

• UI 可视化设计的布局管理问题。使用布局管理可以使界面上的各个组件合理地分布,并且可随窗口大小变化而自动调整大小和位置。

• 在可视化设计 UI 时,设置组件的内建信号与窗体上其他组件的公有槽函数关联。

• 在可视化设计 UI 时,为组件的内建信号创建槽函数,并且使其与组件的信号自动关联。

• 创建资源文件,将图片文件导入资源文件,为界面上的组件设置图标。

一. 窗口界面可视化设计

1.创建项目和资源文件

通过 New File or Project 对话框创建一个 GUI 项目,在向导中选择 qmake 构建系统, 窗口基类选择 QDialog,创建的窗口类名称会被自动设置为 Dialog,勾选其中的 Generate form 复选框,Qt Creator 会自动创建 UI 文件 dialog.ui。

在上图中几个按钮具有对应的图标。在 Qt 项目中,图标存储在资源文件里。我们先在项目的根目录下创建一个文件夹 images,将需要用到的图标文件复制到此文件夹里,然后创建一个资源文件。

打开 New File or Project 对话框,选择 Qt 分组里的 Qt Resource File,然后按照向导的指引设 置资源文件的文件名,并将其添加到当前项目里。本项目创建的资源文件名为 res.qrc,项目管理 目录树里会自动创建一个 Resources 文件组。在资源文件名节点的快捷菜单中选择 Open in Editor 菜单项可以打开资源文件编辑器,如图左所示。

资源文件的主要功能是存储图标和图片文件,以便在程序里使用。在资源文件里首先需要建 一个前缀(prefix),例如 icons,前缀表示资源的分组。在左图所示窗口右侧下方的功能区点击 Add Prefix 按钮就可以创建一个前缀,然后点击 Add Files 按钮添加图标文件。如果所选的图标文件不在本项目的子目录下,会提示需复制文件到项目的子目录下。所以,最好提前将图标文件放在项目的子目录下。

qt-GUI 程序设计基础_第4张图片

2.窗口界面设计

在图左所示的项目管理目录树上,双击文件 dialog.ui 就可以打开 Qt Designer 对窗口界面进行可视化设计。右图是已经设计好的窗体,我们在界面设计中使用了布局管理功能。窗体中间的文本框是一个 QPlainTextEdit 组件,在组件面板的 Input Widgets 分组里有这种组件。

在 UI 可视化设计时,对于需要在程序中访问的界面组件,例如各个按钮、需要读取输入的编 辑框、需要显示结果的标签等,应该修改其对象名称,以便在程序里区分。对于不需要在程序里 访问的界面组件,例如用于组件分组的分组框、布局等,无须修改其对象名称,由 Qt Designer 自 动命名即可。上右图所设计窗体上各主要组件的名称、属性设置和说明如下表所示。

dialog.ui 中各主要组件的名称、属性设置和说明
对象名称 类名称 属性设置 说明
plainTextEdit QPlainTextEdit text="Qt 6 C++开发指南" font.PointSize=20 简单的多行文本编辑器。设计时双击 组件可设置其显示的文字
chkBoxUnder QCheckBox text="Underline" 设置文字带有下划线
chkBoxItalic QCheckBox text ="Italic" 设置文字为斜体
chkBoxBold QCheckBox text ="Bold" 设置文字为粗体
radioBlack QRadioButton text ="Black" checked=true 设置文字颜色为黑色
radioRed QRadioButton text ="Red" 设置文字颜色为红色
radioBlue QRadioButton text ="Blue" 设置文字颜色为蓝色
btnClear QPushButton text ="清空" 清除编辑器中的文字
btnOK QPushButton text ="确定" 返回确定,并关闭窗口
btnExit QPushButton text ="退出" 退出程序
Dialog Dialog windowTitle="信号与槽的使用 窗体,其对象与窗口类同名

要为一个 QPushButton 按钮设置图标,只需在属性编辑器里修改其 icon 属性。在 icon 属性值输入框的右端下拉列表框里选择 Choose Resource,就可以从项目的资源文件中选择图标。

对于界面组件的属性设置,需要注意以下几点。

(1)对象名称是窗口上的组件的实例名称,界面上的每个组件需要有一个唯一的对象名称,  程序里访问界面组件时都是通过其对象名称进行的,自动生成的槽函数名称里也有对象名称。所 以, 组件的对象名称设置好之后一般不要改动。若需要修改对象名称,涉及的代码需要相应改动。

(2)窗体的对象名称会影响窗口 UI 类的名称。dialog.ui 被 UIC 编译后生成文件 ui_dialog.h, 窗体的对象名称与文件 ui_dialog.h 中定义的窗口 UI 类有关。一般不在 Qt Designer 里修改窗体的对象名称,除非是要重命名一个窗口,那么需要对窗口相关的 4 个文件都重命名。

(3)设置窗体的 font 属性后,界面上其他组件的默认字体就是窗体的字体,无须再单独设置, 除非要为某个组件设置单独的字体,例如组件 plainTextEdit 的字体大小被单独设置为 20。

(4)组件的属性都有默认值,一个组件的某个属性被修改后,属性编辑器里的属性名称会以粗体显示。如果要恢复属性的默认值,点击属性值右端的还原按钮即可。

二.界面组件布局管理

Qt 的 UI 设计具有布局(layout)功能。所谓布局,就是指界面上组件的排列方式。使用布局可以使组件有规则地分布,并且使其随着窗口大小变化自动调整大小和相对位置。

1.界面组件的层次关系

为了将界面上的各个组件的分布设计得更加美观,我们经常使用一些容器类组件。例如,将 3 个复选框(QCheckBox 类)放置在一个分组框(QGroupBox 类)里,这个分组框就是这 3 个复选框的容器,移动这个分组框就会同时移动其中的 3 个复选框。

下是可视化设计 UI 时的界面,右侧对象浏览器里显示了窗体上各组件的层次关系。分组框groupBox_Font是3个复选框的父容器,分组框groupBox_Color是3个单选按钮(QRadioButton 类)的父容器,而窗体 Dialog 是窗体上所有组件的顶层容器。

qt-GUI 程序设计基础_第5张图片

2.布局管理

Qt 为 UI 设计提供了丰富的布局管理功能。在 Qt Designer 里,组件面板里有 Layouts 和 Spacers 两个分组,在上方的工具栏里有布局管理的按钮。上图中的窗体已经设置好了所有布局,在右 侧的对象检查器里可以看到各布局的类型。

组件面板上 Layouts 和 Spacers 两个分组里的布局组件的说明如下表所示。

qt-GUI 程序设计基础_第6张图片

Qt Designer 有一个工具栏,用于使界面进入不同的设计状态,或进行布局设计,工具栏上各按钮的说明如表所示。

qt-GUI 程序设计基础_第7张图片

使用工具栏上的布局按钮时,只需在窗体上选中需要设计布局的组件,然后点击某个布局按钮。在窗体上选择组件的同时按住 Ctrl 键可以实现组件多选,选择某个容器组件相当于选择了其 内部的所有组件。例如,在上图所示的窗体中,选中分组框 groupBox_Font,然后点击 Lay Out Horizontally 工具按钮,就可以使分组框内的 3 个复选框水平布局。

在上图所示的窗体上,分组框 groupBox_Font 里的 3 个复选框为水平布局,分组框 groupBox_Color 里的 3 个单选按钮为水平布局,下方放置的 3 个按钮为水平布局。

还需为窗体设置总的布局。选中窗体(即不选择任何组件),点击工具栏上的 Lay Out Vertically 按钮,使 4 个组件垂直分布。这样布局后,当窗体大小改变时,各个组件都会自动改变大小,且 窗体纵向增大时,只有中间的文本框增大,其他 3 个布局组件不增大。最终设计好的窗体的组件 布局如上图所示,从中可以清楚地看出组件的层次关系以及布局的设置。

在 Qt Designer 里可视化设计布局时,要善于利用水平/垂直分隔器(spacer),善于设置组件的最大、最小宽度/高度属性,善于设置布局的 layoutStretch、layoutSpacing 等属性来达到较好的 布局效果。

3.伙伴关系与 Tab 顺序

点击 Qt Designer 工具栏上的 Edit Buddies 按钮可以进入伙伴关系编辑状 态,例如设计一个窗体时,进入伙伴关系编辑状态之后界面如图 所示。 伙伴关系是指界面上一个标签和一个具有输入焦点的组件相关联。在图所示的伙伴关系编辑状态下,选中一个标签,按住鼠标左键,然后将其拖向一 个编辑框(QLineEdit 类),就可建立标签和编辑框的伙伴关系。

qt-GUI 程序设计基础_第8张图片

利用伙伴关系,可以在程序运行时通过快捷键将输入焦点切换到某个组件上。例如,在图所示的界面上,设置“姓 名”标签的 text 属性为“姓 名(&N)”,其中符号 “&”用来指定快捷字符,界面上并不显示“&”。这里指定快捷字符为 N,那么在程序运行时,用 户按下 Alt+N 快捷键,输入焦点就会快速切换到“姓名”标签关联的编辑框内。

点击 Qt Designer 工具栏上的 Edit Tab Order 按钮进入 Tab 顺序编辑状态,如下图所示。Tab 顺序是指在程序运行时,按下键盘上的 Tab 键时输入焦点的移动顺序。一个好的 UI,在按 Tab 键时焦点应该以合理的顺序在界面组件上移动。

qt-GUI 程序设计基础_第9张图片

进入 Tab 顺序编辑状态后,界面上会显示具有 Tab 顺序的组件的 Tab 顺序 编号,依次按希望的顺序点击组件,就可以重排 Tab 顺序。没有输入焦点的组 件是没有 Tab 顺序的,例如标签。

三. 信号与槽简介

信号与槽是 Qt 编程的基础,也是 Qt 的一大创新。有了信号与槽的编程机制,在 Qt 中处理界面上各个组件的交互操作就变得比较直观和简单。

信号(signal)是在特定情况下被发射的通知,例如 QPushButton 较常见的信号就是点击鼠标时发射的 clicked()信号。GUI 程序设计的主要工作就是对界面上各组件的信号进行响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。

槽(slot)是对信号进行响应的函数。槽就是函数,所以也称为槽函数。槽函数与一般的 C++ 函数一样,可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与信号关联,当信号被发射时,关联的槽函数被自动运行。

信号与槽关联是用函数 QObject::connect()实现的,使用函数 connect()的基本格式如下:

QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot())); 

connect()是 QObject 类的一个静态函数,而 QObject 是大部分 Qt 类的基类,在实际调用时可以忽略前面的限定符部分,所以可以直接写为:

connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

其中,sender 是发射信号的对象的名称;signal()是信号,信号可以看作特殊的函数,需要带有括 号,有参数时还需要指明各参数类型;receiver 是接收信号的对象的名称;slot()是槽函数,需要带 有括号,有参数时还需要指明各参数类型。

SIGNAL 和 SLOT 是 Qt 的宏,分别用于指明信号和槽函数,并将它们的参数转换为相应的字符串。关于信号与槽的使用,有以下一些规则需要注意。

(1)一个信号可以连接多个槽函数,例如:

connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(addFun(int))); 
connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(updateStatus(int))); 

当一个信号与多个槽函数关联时,槽函数按照建立连接时的顺序依次运行。

当信号和槽函数带有参数时,在函数 connect()里要指明各参数的类型,但不用指明参数名称。

(2)多个信号可以连接同一个槽函数。例如在本示例中,3 个选择颜色的单选按钮的 clicked() 信号关联到相同的自定义槽函数 do_setFontColor()。

connect(ui->radioBlack,SIGNAL(clicked()), this, SLOT(do_setFontColor())); 
connect(ui->radioRed, SIGNAL(clicked()), this, SLOT(do_setFontColor())); 
connect(ui->radioBlue, SIGNAL(clicked()), this, SLOT(do_setFontColor())); 

(3)一个信号可以连接另一个信号,例如:

connect(spinNum, SIGNAL(valueChanged(int)), this, SIGNAL(refreshInfo(int))); 

这样,当发射一个信号时,也会发射另一个信号,以实现某些特殊的功能。

(4)严格的情况下,信号与槽的参数个数和类型需要一致,至少信号的参数不能少于槽的参数。如果参数不匹配,会出现编译错误或运行错误。

(5)在使用信号与槽的类中,必须在类的定义中插入宏 Q_OBJECT。

(6)当一个信号被发射时,与其关联的槽函数通常被立即运行,就像正常调用函数一样。只有当信号关联的所有槽函数运行完毕后,才运行发射信号处后面的代码。

函数 connect()有多种参数形式,有一种常用的形式是不使用 SIGNAL 和 SLOT 宏,而是使用函数指针,如:

QObject::connect(btnClose, &QPushButton::clicked, Widget, qOverload<>(&QWidget::close)); 

四. 信号与槽的使用

1.信号与槽编辑器的使用

Qt 的界面组件都是从 QWidget 继承而来的,都支持信号与槽的功能。每个类都有一些内建的信号和槽函数, 例如 QPushButton 类的常用信号是 clicked(), 在按钮被点击时此信号被发射。 QDialog 是对话框类,它有以下几个公有的槽函数。

• accept(),功能是关闭对话框,表示肯定的选择,如对话框上的“确定”按钮。

• reject(),功能是关闭对话框,表示否定的选择,如对话框上的“取消”按钮。

• close(),功能是关闭对话框。

这 3 个槽函数都可以关闭对话框,但是表示的对话框的返回值不同。在本示例中,我们希望将“确定”按钮与对话框的 accept()槽函数关联,将“退出”按钮与对话框的 close()槽函数关联。可以在 Action 编辑器里设置组件的内建信号与其他组件的公有槽函数关联,设计结果如图所示。

qt-GUI 程序设计基础_第10张图片

2.为组件的信号生成槽函数原型和框架

下面介绍为窗体上设置字体的3个复选框的信号编写槽函数。选中窗体上的复选框 chkBoxUnder,在右键快捷菜单中点击菜单项 Go to slot,会出现下图所示的对话框。这个对话框显示了 QCheckBox 类的所有可用信号。复选框被勾选时会发射 clicked()和 clicked(bool) 信号,其中带有参数的 clicked(bool)信号以复选框当前的选择状态作为参数。我们需要以复选框的选择状态作为参数,设置文字是否带有下划线,所以应该使用信号 clicked(bool)。

qt-GUI 程序设计基础_第11张图片

在 Go to slot 对话框中选择 clicked(bool)信号,然后点击 OK 按钮,在 Dialog 类的 private slots 部分会自动增加槽函数声明,函数名是根据发射信号的对象名和信号名称自动命名的。

void on_chkBoxUnder_clicked(bool checked); 

同时,在文件 dialog.cpp 中会自动生成这个函数的代码框架,在此函数中添加如下的代码,实现对文本框文字下划线的控制。

void Dialog::on_chkBoxUnder_clicked(bool checked) 
{//Underline 复选框
 QFont font=ui->plainTextEdit->font(); 
 font.setUnderline(checked); 
 ui->plainTextEdit->setFont(font); 
}

以同样的方法为 Italic 和 Bold 两个复选框设计槽函数,为“清空”按钮的 clicked()信号生成槽函数,为这几个槽函数编写代码。

void Dialog::on_chkBoxItalic_clicked(bool checked) 
{//Italic 复选框
 QFont font=ui->plainTextEdit->font(); 
 font.setItalic(checked); 
 ui->plainTextEdit->setFont(font); 
} 
void Dialog::on_chkBoxBold_clicked(bool checked) 
{//Bold 复选框
 QFont font=ui->plainTextEdit->font(); 
 font.setBold(checked); 
 ui->plainTextEdit->setFont(font); 
} 
void Dialog::on_btnClear_clicked() 
{//“清空”按钮
 ui->plainTextEdit->clear(); 
} 

构建项目后运行,发现几个复选框的功能可用了,点击“清空”按钮可以清空文本框的内容,点击“确定”和“退出”按钮都可以关闭窗口,说明这几个组件的信号与对应的槽函数已经关联了。

但是,查看 Dialog 类的构造函数,它只有简单的一条语句。

Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog) 
{ 
 ui->setupUi(this); 
} 

这里没有发现设置组件信号与槽函数连接的 connect()函数的语句,打开编译生成的文件 ui_dialog.h,查看函数 setupUi()的代码,它的最后有如下几行语句:

QObject::connect(btnOK, &QPushButton::clicked, Dialog, qOverload<>(&QDialog::accept)); 
QObject::connect(btnExit, &QPushButton::clicked, Dialog, qOverload<>(&QDialog::reject)); 
QMetaObject::connectSlotsByName(Dialog); 

前两行 connect()函数的语句就是两个信号与槽连接的实现代码。最后一行语句的功能是搜索 Dialog 界面上的所有组件,将名称匹配的信号和槽关联起来,假设槽函数的名称是:

void on__(); 
 
  

例如,通过 Go to slot 对话框,为复选框 chkBoxUnder 自动生成的槽函数是:

void on_chkBoxUnder_clicked(bool checked);

它正好是 chkBoxUnder 的信号 clicked(bool)的槽函数。那么,函数 connectSlotsByName()就会将此信号和槽函数关联起来,相当于运行了下面的一条语句:

connect(chkBoxUnder, SIGNAL(clicked(bool)), this, SLOT(on_chkBoxUnder_clicked(bool)));

这就是用 Qt Designer 可视化设计某个组件的信号的槽函数,不用手动设置关联,在窗口类的构造函数里调用的 setupUi()函数里自动完成了关联。

3.使用自定义槽函数

设置文字颜色的 3 个单选按钮是互斥选择的,即一次只有一个单选按钮被选中,虽然也可以采用 Go to slot 对话框为它们的 clicked()信号生成槽函数,但是这样就需要生成 3 个槽函数。我们换一种方式,即设计一个自定义槽函数,将 3 个单选按钮的 clicked()信号都与这个自定义槽函数关联。为此,在 Dialog 类的 private slots 部分增加如下的槽函数定义。

void do_setFontColor(); //设置文字颜色的自定义槽函数

将鼠标光标移动到这个函数名上面,点击鼠标右键,在弹出的快捷菜单中选择 Refactor→Add Definition in dialog.cpp,就可以在文件 dialog.cpp 中自动生成该函数的代码框架。为该函数编写代 码,具体如下:

void Dialog::do_setFontColor() 
{//自定义槽函数,设置文字颜色
 QPalette plet=ui->plainTextEdit->palette(); 
 if (ui->radioBlue->isChecked()) 
 plet.setColor(QPalette::Text,Qt::blue); 
 else if (ui->radioRed->isChecked()) 
 plet.setColor(QPalette::Text,Qt::red); 
 else if (ui->radioBlack->isChecked()) 
 plet.setColor(QPalette::Text,Qt::black); 
 else 
 plet.setColor(QPalette::Text,Qt::black); 
 ui->plainTextEdit->setPalette(plet); 
} 

由于这个槽函数是自定义的,因此它不会与界面上 3 个单选按钮的 clicked()信号自动关联。 我们需要在 Dialog 类的构造函数中手动进行关联,代码如下:

Dialog::Dialog(QWidget *parent): QDialog(parent), ui(new Ui::Dialog) 
{ 
 ui->setupUi(this); 
 connect(ui->radioBlack,SIGNAL(clicked()),this,SLOT(do_setFontColor())); 
 connect(ui->radioRed, SIGNAL(clicked()),this,SLOT(do_setFontColor())); 
 connect(ui->radioBlue, SIGNAL(clicked()),this,SLOT(do_setFontColor())); 
} 

构建项目后运行,点击这 3 个单选按钮就可以更改文字的颜色了。

五. 为应用程序设置图标

项目被构建后生成的可执行文件具有默认的图标,如果需要为应用程序设置图标,可按如下步骤操作:

(1)将一个后缀为“.ico”的图标文件复制到项目根目录下,假设图标文件名是 editor.ico。

(2)在项目配置文件(.pro 文件)里用 RC_ICONS 设置图标文件名,即添加如下一行语句:

RC_ICONS = editor.ico

重新构建项目,生成的可执行文件以及窗口的图标就会换成设置的图标。

六. Qt 项目构建过程基本原理

一个使用 qmake 构建系统的 Qt 项目,除了有项目配置文件,还有用 Qt C++编写的头文件和源程序文件,以及窗口 UI 文件和资源文件。在构建项目的过程中,这 3 类文件会被分别编译为标准 C++语言的程序文件,然后被标准 C++编译器(如 GNU C++编译器或 MSVC 编译器)编译成可执行文件或库。

Qt 项目的构建过程 可以用下图所示的基本过程来描述。

qt-GUI 程序设计基础_第12张图片

1.元对象系统和 MOC

Qt 对标准 C++语言进行了扩展,引入了元对象系统(meta-object system,MOS), 所有从 QObject 继承的类都可以利用元对象系统提供的功能。元对象系统支持属性、信号与槽、动态类型转换等特性。

我们在 Qt Creator 中编写程序时使用的 C++语言,实际上是经过 Qt 扩展的 C++语言。例如, 在示例Dialog 类的定义中插入一个宏 Q_OBJECT,这是使用信号与槽机制的类必须插 入的一个宏。Dialog 类中的 private slots 部分用于定义私有槽,这是标准 C++语言中没有的特性。

Qt 提供了元对象编译器(MOC)。在构建项目时,项目中的头文件会先被 MOC 预编译。例如, 以 Release 模式构建项目 samp2_2 后,文件夹 release 里的文件如下图所示。其中,moc_dialog.cpp 是 MOC 读取文件 dialog.h 的内容后生成的一个元对象代码文件,文件 moc_predefs.h 里是一些宏定义。结合 moc_dialog.cpp 和 moc_predefs.h,dialog.cpp 和 ui_dialog.h 就可以被标准 C++编译器编译。

qt-GUI 程序设计基础_第13张图片

2.UI 文件和 UIC

在构建项目时,可视化设计的窗口 UI 文件会被用户界面编译器(UIC)转换为一个 C++源程序文件。例如示例项目中的文件 widget.ui 被转换为文件 ui_widget.h,UIC 编译生成的文件与 UI 文件在同一个文件夹里, 文件 ui_widget.h 之后会被标准 C++编译器编译。

3.资源文件和 RCC

Qt 项目中的资源文件(.qrc 文件)会被资源编译器(RCC)转换为 C++程序文件。例如示例项目中的资源文件是 res.qrc,经过 RCC 编译后生成的文件是 qrc_res.cpp。 文件 qrc_res.cpp 之后会被标准C++编译器编译。

4.标准 C++编译器

标准 C++编译器就是开发套件中的编译器,例如其在 MinGW 套件中是 GNU C++编译器,在 MSVC 套件中是 Microsoft Visual C++编译器。

使用 MOC, UIC 和 RCC 编译各原始文件的过程称为预编译过程,预编译之后生成的是标准 C++语言的程序文件,它们被标准 C++编译器编译和连接,最终生成可执行文件。

(三). 代码化 UI 设计

窗口界面的可视化设计是对用户而言的,UI 文件都会被 UIC 转换为 C++程序文件。如果不使用 Qt Designer 进行 UI 可视化设计,直接编写 C++代码也是可以创建界面的,而且某些界面效果是可视化设计无法实现的。如果习惯了用纯代码的方式设计界面,就可以采用纯代码的方式创建界面。Qt 自带的示例项目基本都是用纯代码方式创建 UI。

一. 示例功能概述

使用 New File or Project 对话框创建一个GUI 项目。在 向导中选择窗口基类为QDialog,但是不勾选 Generate form 复选框。 这样创建的项目里就没有窗口 UI 文件 dialog.ui。 该项目通过代码创建一个对话框,实现与(二)示例类似的界面和功能。本示例创建完成后的运行效果如图所示,其界面和功能与上示例的相似。

qt-GUI 程序设计基础_第14张图片

二. 示例功能实现

1.Dialog 类的定义

实现功能后的文件 dialog.h 中 Dialog 类的完整定义如下:

class Dialog : public QDialog 
{ 
 Q_OBJECT 
private: 
 QCheckBox *chkBoxUnder; //3 个复选框
 QCheckBox *chkBoxItalic; 
 QCheckBox *chkBoxBold; 
 QRadioButton *radioBlack; //3 个单选按钮
 QRadioButton *radioRed; 
 QRadioButton *radioBlue;
 QPushButton *btnOK; //3 个按钮
 QPushButton *btnCancel; 
 QPushButton *btnClose; 
 QPlainTextEdit *txtEdit; //文本框
 void iniUI(); //UI 创建与初始化
 void iniSignalSlots(); //初始化信号与槽的连接
private slots: 
 void do_chkBoxUnder(bool checked); //Underline 复选框
 void do_chkBoxItalic(bool checked); //Italic 复选框
 void do_chkBoxBold(bool checked); //Bold 复选框
 void do_setFontColor(); //设置文字颜色
public: 
 Dialog(QWidget *parent = nullptr); 
 ~Dialog(); 
}; 

Dialog 类的 private 部分声明了界面上的各个组件的指针变量,这些界面组件都需要在 Dialog 类的构造函数里创建并在窗口上布局。private 部分定义了两个函数,函数 iniUI()创建所有界面组件并完成布局和属性设置,函数 iniSignalSlots()完成所有的信号与槽的关联。

private slots 部分声明了4个自定义槽函数,需要在函数 iniSignalSlots()里将其与相应的信号连接。

注意,这个 Dialog 类里没有定义指向窗口界面的指针 ui,因为它没有对应的窗口 UI 文件。

几个自定义槽函数的功能与之前示例中的类似,只是在访问界面组件时无须使用 ui 指针, 而是直接访问 Dialog 类里定义的界面组件的成员变量,例如 do_chkBoxUnder()的代码如下:

void Dialog::do_chkBoxUnder(bool checked) 
{ 
 QFont font=txtEdit->font(); 
 font.setUnderline(checked); 
 txtEdit->setFont(font); 
} 

其他几个自定义槽函数的代码就不展示了。界面的创建,以及信号与槽函数的关联都在 Dialog 类的构造函数里完成,构造函数代码如下:

Dialog::Dialog(QWidget *parent) : QDialog(parent) 
{ 
 iniUI(); //界面创建与布局
 iniSignalSlots(); //信号与槽的关联
 setWindowTitle("手工创建 UI"); //设置窗口标题
} 

2.界面组件的创建与布局

函数 iniUI()实现界面组件的创建与布局,下面是函数 iniUI()的完整代码:

void Dialog::iniUI() 
{ 
//创建 Underline、Italic、Bold 3 个复选框,并水平布局
 chkBoxUnder= new QCheckBox("Underline"); 
 chkBoxItalic= new QCheckBox("Italic"); 
 chkBoxBold= new QCheckBox("Bold"); 
 QHBoxLayout *HLay1= new QHBoxLayout(); 
 HLay1->addWidget(chkBoxUnder); 
 HLay1->addWidget(chkBoxItalic); 
 HLay1->addWidget(chkBoxBold); 
//创建 Black、Red、Blue 3 个单选按钮,并水平布局
 radioBlack= new QRadioButton("Black"); 
 radioBlack->setChecked(true); 
 radioRed= new QRadioButton("Red"); 
 radioBlue= new QRadioButton("Blue"); 
 QHBoxLayout *HLay2= new QHBoxLayout; 
 HLay2->addWidget(radioBlack); 
 HLay2->addWidget(radioRed); 
 HLay2->addWidget(radioBlue); 
//创建“确定”“取消”“退出”3 个按钮,并水平布局
 btnOK= new QPushButton("确定"); 
 btnCancel= new QPushButton("取消"); 
 btnClose= new QPushButton("退出"); 
 QHBoxLayout *HLay3= new QHBoxLayout; 
 HLay3->addStretch(); 
 HLay3->addWidget(btnOK); 
 HLay3->addWidget(btnCancel); 
 HLay3->addStretch(); 
 HLay3->addWidget(btnClose); 
//创建文本框,并设置初始字体
 txtEdit= new QPlainTextEdit; 
 txtEdit->setPlainText("Hello world\n 手工创建"); 
 QFont font=txtEdit->font(); //获取字体
 font.setPointSize(20); //修改字体大小为 20 
 txtEdit->setFont(font); //设置字体
//创建垂直布局,并设置为主布局
 QVBoxLayout *VLay= new QVBoxLayout(this); 
 VLay->addLayout(HLay1); //添加字体类型组
 VLay->addLayout(HLay2); //添加文字颜色组
 VLay->addWidget(txtEdit); //添加 txtEdit 
 VLay->addLayout(HLay3); //添加按键组
 setLayout(VLay); //设置为窗口的主布局
} 

函数 iniUI()按顺序完成了如下的操作:

• 创建 3 个 QCheckBox 组件,这 3 个组件的指针已经定义为 Dialog 类的私有变量,然后创建1个水平布局 HLay1,将这 3 个复选框添加到这个水平布局里。

• 创建 3 个 QRadioButton 组件和水平布局 HLay2,将这 3 个单选按钮添加到这个水平布局里。

• 创建 3 个 QPushButton 组件和水平布局 HLay3,将这 3 个按钮添加到这个水平布局里。

• 创建 1 个 QPlainTextEdit 组件,设置其文字内容和字体。

• 创建 1 个垂直布局 VLay,将前面创建的 3 个水平布局和文本框依次添加到此布局里。设置垂直布局 VLay 为窗口的主布局。

布局对象就是这个布局内的所有组件的父容器,例如 HLay1 是 3 个复选框的父容器。最后创建的垂直布局 VLay 以窗口作为父容器,因为在创建 VLay 时以 this 作为其 parent 参数。而 VLay 是组件txtEdit和其他几个布局的父容器, 所以界面上这些组件都有父容器组件, 且最终的容器就是窗口。

我们在 Dialog 类的析构函数中并没有使用 delete 指令显式地删除 private 部分定义的对象,因为在 Qt 中容器组件被删除时,其内部组件也会自动被删除。因此,窗口被删除时,窗口上的所有组件会自动被删除。

3.信号与槽的关联

Dialog 类中定义的槽函数都是自定义槽函数,不会和组件的信号自动关联。函数 iniSignalSlots() 用于建立信号与槽的关联,其完整代码如下:

void Dialog::iniSignalSlots() 
{ 
//3 个设置颜色的单选按钮
 connect(radioBlue, SIGNAL(clicked()), this, SLOT(do_setFontColor())); 
 connect(radioRed, SIGNAL(clicked()), this, SLOT(do_setFontColor())); 
 connect(radioBlack, SIGNAL(clicked()), this, SLOT(do_setFontColor())); 
//3 个设置字体的复选框
 connect(chkBoxUnder, SIGNAL(clicked(bool)), this, SLOT(do_chkBoxUnder(bool))); 
 connect(chkBoxItalic, SIGNAL(clicked(bool)), this, SLOT(do_chkBoxItalic(bool))); 
 connect(chkBoxBold, SIGNAL(clicked(bool)), this, SLOT(do_chkBoxBold(bool))); 
//3 个按钮与窗口的槽函数关联
 connect(btnOK, SIGNAL(clicked()), this, SLOT(accept())); 
 connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject())); 
 connect(btnClose, SIGNAL(clicked()), this, SLOT(close())); 
} 

从这个示例可以看出,采用纯代码方式创建 UI 比较复杂,需要对组件的布局有完整的规划, 不如可视化设计直观,且编写代码的工作量大。对每组信号与槽都需要用 connect()函数连接,不像可视化设计那样可以自动连接。

但是有些界面效果无法用可视化设计方法实现,例如在基于 QMainWindow 的窗口上无法可视化地在状态栏上添加 QLabel、QSpinBox 等组件,那么需要在窗口的构造函数里用代码创建这些组件, 实现需要的界面效果。

你可能感兴趣的:(qt,qt)