赞
踩
前面章节中,通过在 xxx.ui 文件中拖拽 Label 组件,设计出了一个显示 "Hello,World!" 的窗口,如下图所示:
图 1 简单的界面
本节我们完全舍弃 xxx.ui 文件,亲手编写代码实现图 1 所示的界面。
首先,打开 Qt Creator 并创建一个 Qt Widgets Application 项目,创建过程可以参考 《编写第一个Qt程序》一节。需要注意的是,我们要创建一个不带 xxx.ui 文件的项目,如下图所示:
图 2 创建不带 ui 文件的图形界面项目
最终创建的项目结构如下图所示:
图 3 项目结构
Demo.pro 是项目文件,文件中的内容可以手动修改,我们会在《Qt pro文件详解》一节中详细讲解,本节不需要修改此文件。接下来,我们逐一介绍 main.cpp、mainwindow.h 和 mainwindow.cpp 这 3 个文件。
1) main.cpp
main.cpp 是主函数文件,内部主要包含应用程序的入口函数,也就是 main() 函数。
我们知道,C/C++ 程序中 main() 函数的语法格式是固定的:
- int main(int argc, char *argv[]){
- //填充代码
- return 0;
- }
Qt 界面程序中的 main() 函数也有固定的格式:
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- //填充代码
- return a.exec();
- }
对于刚刚学习 Qt 的读者,暂时不用了解第 3 行和第 5 行代码的含义,只要记住:使用 Qt 框架编写带界面的应用程序,main() 函数中必须包含第 3 行和第 5 行代码,否则程序无法正常运行。
双击图 3 所示的 main.cpp 文件,可以看到该文件包含的所有代码:
- #include "mainwindow.h"
- #include <QApplication>
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- MainWindow w;
- w.show();
-
- return a.exec();
- }
除了第 6、8 行代码外,其它代码的含义分别是:
" "
双引号括起来;QApplication 是 Qt 提供给我们的,引入时用<>
括起来。2) mainwindow.h和mainwindow.cpp
创建项目时,我们在图 2 所示的对话框中定义了一个继承自 QMainWindow 的主窗口类,并起名为 MianWindow,该类的定义部分位于 mainwindow.h 头文件中,实现部分位于 mainwindow.cpp 源文件中。
双击图 3 所示的 mainwindow.h 和 mainwindow.cpp 文件,可以看到它们各自包含的代码:
mainwindow.h
- //mainwindow.h
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include <QMainWindow>
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- MainWindow(QWidget *parent = 0);
- ~MainWindow();
- };
mainwindow.cpp
- //mainwindow.cpp
- #endif // MAINWINDOW_H
-
- #include "mainwindow.h"
-
- MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
- {
- }
-
- MainWindow::~MainWindow()
- {
-
- }
初始状态下,MainWindow 类由 Q_OBJECT、构造函数和析构函数组成,这里重点介绍一下 Q_OBJECT 和构造函数:
直接运行程序,会输出下图所示的界面:
图 4 空白主窗口
图 4 看到的就是 main() 函数中创建的 w 主窗口。由于没有往 w 窗口中放置任何组件,所以 w 是一个空白窗口。
我们尝试向 w 主窗口添加一个文本框,需要对 MainWindow 类进行修改。修改后的 MainWindow 类如下:
mainwindow.h
- //mainwindow.h
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include <QMainWindow>
- #include <QLabel> // 引入 QLable 文件框组件的头文件
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private:
- QLabel *lab; // 定义一个私有的 QLabel 指针对象
- };
-
- #endif // MAINWINDOW_H
mainwindow.cpp
- //mainwindow.cpp
- #include "mainwindow.h"
-
- MainWindow::MainWindow(QWidget *parent)
- : QMainWindow(parent)
- {
- // 创建一个 QLable 对象
- this->lab = new QLabel("Hello,World!",this);
- }
-
- MainWindow::~MainWindow()
- {
-
- }
和先前空的 MainWindow 类相比,做了如下修改:
<QLabel>
头文件;有关 QLabel 组件的用法,我们会在《Qt QLabel文本框的使用》一节详细讲解,这里不用深究具体的语法。
再次运行程序,显示的窗口如下图所示:
图 5 带文本框的主窗口
图 1 和图 5 是类似的,区别在于图 5 中的 "Hello, World!" 没有加粗,也没有调整它在主窗口中的位置,这些都可以通过编码实现,后续讲 QLabel 时会做详细介绍。
图 5 中,"Hello,World!" 文本框的父窗口是主窗口,所以文本框位于主窗口中(位置默认在窗口的左上角),主窗口关闭时文本框也会随之关闭。
由此,我们就成功设计了一个包含文本框的窗口,这也是我们编写的第一个 Qt 程序。
Qt 是一个著名的 GUI 框架,用来开发和用户交互的图形界面。作为 GUI 框架,丰富的控件和灵活的事件机制是不可或缺的,Qt 在这一方面做得非常优秀。
Qt 控件又称组件或者部件,指用户看到的所有可视化界面以及界面中的各个元素,比如按钮、文本框、输入框等。
为了方便程序员开发,Qt 提供了很多现成的控件。打开某个带 ui 文件的 Qt Widgets Application 项目,ui 文件的 Widget Box 一栏展示了 Qt 提供的几乎所有控件:
图 1 Qt 提供的控件
Qt 中的每个控件都由特定的类表示,每个控件类都包含一些常用的属性和方法,所有的控件类都直接或者间接继承自 QWidget 类。实际开发中,我们可以使用 Qt 提供的这些控件,也可以通过继承某个控件类的方式自定义一个新的控件。
前面说过,Qt 中所有可视化的元素都称为控件,我们习惯将带有标题栏、关闭按钮的控件称为窗口。例如,下图展示了两种常用的窗口,实现它们的类分别是 QMainWindow 和 QDialog。
图 2 Qt 窗口
除了 QMainWindow 和 QDialog 之外,还可以使用 QWidget 类,它的用法非常灵活,既可以用来制作窗口,也可以作为某个窗口上的控件。
窗口很少单独使用,它的内部往往会包含很多控件。例如图 2 中,我们分别往 MainWindow 和 Dialog 窗口中放置了一个按钮控件,根据需要还可以放置更多的控件。当窗口弹出时,窗口包含的所有控件会一同出现;当窗口关闭时,窗口上的所有控件也会随之消失。
实际开发中,制作应用程序的主窗口可以用 QMainWindow 或者 QWdiget;制作一个提示信息的对话框就用 QDialog 或 QWidget;如果暂时无法决定,后续可能作为窗口,也可能作为控件,就选择 QWidget。
简单地理解,Qt 事件指的是应用程序和用户之间的交互过程,例如用户按下某个按钮,点击某个输入框等等。实际上除了用户会与应用程序进行交互外,操作系统也会与应用程序进行交互,例如当某个定时任务触发时,操作系统会关闭应用程序,这也是一个事件。
《分析第一个Qt程序》一节中提到,Qt 界面程序的 main() 主函数中首先要创建一个 QApplication 类的对象,函数执行结束前还要调用 QApplication 对象的 exec() 函数。一个 Qt 界面程序要想接收事件,main() 函数中就必须调用 exec() 函数,它的功能就是使程序能够持续不断地接收各种事件。
Qt 程序可以接收的事件种类有很多,例如鼠标点击事件、鼠标滚轮事件、键盘输入事件、定时事件等。每接收一个事件,Qt 会分派给相应的事件处理函数来处理。所谓事件处理函数,本质就是一个普通的类成员函数,以用户按下某个 QPushButton 按钮为例,Qt 会分派给 QPushButton 类中的 mousePressEvent() 函数处理。
事件处理函数通常会完成两项任务,分别是:
信号和槽是 Qt 中处理事件最常用的方法,我们会在《Qt信号和槽》一节中做详细地讲解。
创建一个不带 ui 文件的 Qt Widgets Application 项目,项目中只保留一个 main.cpp 源文件,删除其它文件(mainwindows.h 和 mainwindow.cpp)。将下述代码直接拷贝到 main.cpp 文件:
- //main.cpp
- #include <QApplication>
- #include <QWidget>
- #include <QPushButton>
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- //添加窗口
- QWidget widget;
- //定义一个按钮,它位于 widget 窗口中
- QPushButton But("按钮控件",&widget);
- //设置按钮的位置和尺寸
- But.setGeometry(10,10,100,50);
- //信号与槽,实现当用户点击按钮时,widget 窗口关闭
- QObject::connect(&But,&QPushButton::clicked,&widget,&QWidget::close);
- //让 widget 窗口显示
- widget.show();
- return a.exec();
- }
运行结果如下图所示:
图 3 运行结果
整个程序的运行过程如下:
实际开发中,我们用各种 Qt 控件设计出功能丰富的界面,用 Qt 事件完成与用户的交互。学习 Qt 界面编程,本质上就是学习 Qt 各个控件的用法以及对 Qt 事件的处理。本节我们只是对 Qt 控件和事件做了简单的了解,接下来将为您系统地讲解它们的用法。
信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。
举个简单的例子,按钮和窗口本是两个独立的控件,点击按钮并不会对窗口造成任何影响。通过信号和槽机制,我们可以将按钮和窗口关联起来,实现“点击按钮会使窗口关闭”的效果。
在 Qt 中,用户和控件的每次交互过程称为一个事件,比如“用户点击按钮”是一个事件,“用户关闭窗口”也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出“按钮被点击”的信号,用户关闭窗口会发出“窗口被关闭”的信号。
Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到“按钮被点击”的信号后,会做出“关闭自己”的响应动作;再比如输入框自己接收到“输入框被点击”的信号后,会做出“显示闪烁的光标,等待用户输入数据”的响应动作。在 Qt 中,对信号做出的响应动作就称为槽。
图 1 信号和槽
信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。例如,“按钮被按下”这个信号可以用 clicked() 函数表示,“窗口关闭”这个槽可以用 close() 函数表示,信号和槽机制实现“点击按钮会关闭窗口”的功能,其实就是 clicked() 函数调用 close() 函数的效果。
信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:
为了提高程序员的开发效率,Qt 的各个控件类都提供了一些常用的信号函数和槽函数。例如 QPushButton 类提供了 4 个信号函数和 5 个 public slots 属性的槽函数,可以满足大部分场景的需要。
实际开发中,可以使用 Qt 提供的信号函数和槽函数,也可以根据需要自定义信号函数和槽函数,我们会在《Qt自定义信号和槽函数》一节做详细介绍。
Qt Creator 提供了很强大的 Qt GUI 开发手册,很容易就能查到某个控件类中包含哪些信号函数和槽函数。举个例子,查看 QPushButton 类中信号函数和槽函数的过程是:
1) 在程序中引入<QPushButton>
头文件,双击选中“QPushButton”并按 "Fn+F1" 快捷键,就会弹出 QPushButton 类的使用手册,如下图所示。
图 2 QPushButton类的使用说明
2) 在 Contents 部分可以看到,QPushButton 类只提供了一些Public Slots
属性的槽函数,没有提供信号函数。对于 QPushButton 类按钮,除了可以使用自己类提供的槽函数,还可以使用从父类继承过来的信号函数和槽函数。由图 2 可知,QPushButton 的父类是 QAbstractButton,点击 QAbstractButton 就可以直接跳转到此类的使用手册,如下图所示:
图 3 QPushButton父类使用说明
QAbstractButton 类中既有 Signals 信号函数,也有 Public Slots 槽函数,这里不再一一列举,感兴趣的读者可以自行查看。
注意,并非所有的控件之间都能通过信号和槽关联起来,信号和槽机制只适用于满足以下条件的控件:
- 控件类必须直接或者间接继承自 QObject 类。Qt 提供的控件类都满足这一条件,这里提供一张 Qt常用类的继承关系的高清图片,感兴趣的读者可以简单了解一下。
- 控件类中必须包含 private 属性的 Q_OBJECT 宏。
将某个信号函数和某个槽函数关联起来,需要借助 QObject 类提供的 connect() 函数。
connect() 是 QObject 类中的一个静态成员函数,专门用来关联指定的信号函数和槽函数。
关联某个信号函数和槽函数,需要搞清楚以下 4 个问题:
- 信号发送者是谁?
- 哪个是信号函数?
- 信号的接收者是谁?
- 哪个是接收信号的槽函数?
仍以实现“按下按钮后窗口关闭”为例,先创建一个窗口和一个按钮,如下所示:
- QWidget widget;
- //定义一个按钮,它位于 widget 窗口中
- QPushButton But("按钮控件",&widget);
信号发送者是 But 按钮对象,要发送的信号是“按钮被点击”,可以用 QPushButton 类提供的 clicked() 信号函数表示;信号的接收者是 widget 主窗口对象,“窗口关闭”作为信号对应的槽,可以用 QWidget 类提供的 close() 函数表示。
在 Qt5 版本之前,connect() 函数最常用的语法格式是:
QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
各个参数的含义分别是:
信号函数和槽函数的参数必须完全一致。槽函数不一定非要放到slots:下。
用 connect() 函数将 But 按钮的 clicked() 信号函数和 widget 窗口的 close() 槽函数关联起来,实现代码如下:
connect(&But, SIGNAL(clicked()), &widget, SLOT(close()));
如此就实现了“按下按钮会关闭窗口”的功能。
发送者、信号、接收者、方法
Qt5 版本中,connect() 函数引入了新的用法,常用的语法格式是:
QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)
和旧版本相比,新版的 connect() 函数改进了指定信号函数和槽函数的方式,不再使用 SIGNAL() 和 SLOT() 宏。
例如,用新版 connect() 函数关联 But 按钮的 clicked() 信号函数和 widget 窗口的 close() 槽函数,实现代码为:
connect(&But, &QPushButton::clicked, &widget, &QWidget::close);
可以看到,新版 connect() 函数指定信号函数和槽函数的语法格式是&+函数所在类+函数名
。
一个 connect() 函数只能关联一个信号函数和一个槽函数,程序中可以包含多个 connect() 函数,能实现以下几种效果:
此外,connect() 函数的 method 参数还可以指定一个信号函数,也就是说,信号之间也可以相互关联,这样当信号发出时,会随之发出另一个信号。
创建一个不含 ui 文件的 Qt Widgets Application 项目,只保留 main.cpp 源文件,删除 mainwindow.h 和 mainwindow.cpp 文件。在 main.cpp 文件中编写如下代码:
- #include <QApplication>
- #include <QWidget>
- #include <QPushButton>
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- //添加窗口
- QWidget widget;
- //定义一个按钮,它位于 widget 窗口中
- QPushButton But("按钮控件",&widget);
- //设置按钮的位置和尺寸
- But.setGeometry(10,10,100,50);
- //信号与槽,实现当用户点击按钮时,widget 窗口关闭
- QObject::connect(&But,&QPushButton::clicked,&widget,&QWidget::close);
- //让 widget 窗口显示
- widget.show();
- return a.exec();
- }
运行结果为:
如上图所示,由于使用了 conect() 函数将 But 的 clicked() 信号函数和 widget 的 close() 槽函数关联起来,所以生成了“点击按钮后主窗口关闭”的效果。
转自:Qt信号和槽机制详解
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。