既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
程序调试的方法与一般 IDE 工具类似,不再详述。注意,要在 Qt Creator 里调试 MSVC2015 编译的程序,必须安装 Windows 软件开发工具包 SDK。
分析第一个Qt程序
前面章节中,通过在 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 行代码外,其它代码的含义分别是:
- 1~2 行:由于 main() 函数中分别定义了 QApplication 和 MainWindow 类的对象,因此需要引入 mainwindows.h 和 QApplication 头文件。mainwindow.h 文件是我们自己创建的,引入时用
" "
双引号括起来;QApplication 是 Qt 提供给我们的,引入时用<>
括起来。 - 第 7 行:MainWindow 是自定义的类,继承自 QMainWindow 主窗口类,因此 MainWindow 也是一个主窗口类。w 是 MainWindow 类实例化出的对象,表示一个主窗口。
- 第 8 行:默认情况下,Qt 提供的所有组件(控件、部件)都是隐藏的,不会自动显示。通过调用 MainWindow 类提供的 show() 方法,w 窗口就可以在程序运行后显示出来。
2) mainwindow.h和mainwindow.cpp
创建项目时,我们在图 2 所示的对话框中定义了一个继承自 QMainWindow 的主窗口类,并起名为 MianWindow,该类的定义部分位于 mainwindow.h 头文件中,实现部分位于 mainwindow.cpp 源文件中。
双击图 3 所示的 mainwindow.h 和 mainwindow.cpp 文件,可以看到它们各自包含的代码:
//mainwindow.h
#ifndef MAINWINDOW\_H
#define MAINWINDOW\_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget \*parent = 0);
~MainWindow();
};
//mainwindow.cpp
#endif // MAINWINDOW\_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget \*parent)
: QMainWindow(parent)
{
}
MainWindow::~MainWindow()
{
}
初始状态下,MainWindow 类由 Q_OBJECT、构造函数和析构函数组成,这里重点介绍一下 Q_OBJECT 和构造函数:
- Q_OBJECT:本质是一个已定义好的宏,所有需要“信号和槽”功能的组件都必须将 Q_OBJECT 作为 private 属性成员引入到类中。本节设计的界面程序不会用到“信号和槽”,因此可以删除 Q_OBJECT。有关信号和槽,我们会在《Qt信号和槽机制详解》一节详细介绍。
- 带参的构造函数:QWidget 是所有组件的基类,借助 parent 指针,可以为当前窗口指定父窗口。例如图 1 中,QLabel 文本框位于主窗口中,主窗口就是它的父窗口。当父窗口被删除时,所有子窗口也会随之一起删除。当然也可以不指定父窗口,那么当前窗口就会作为一个独立的窗口,不会受到其它窗口的影响。
直接运行程序,会输出下图所示的界面:
图 4 空白主窗口
图 4 看到的就是 main() 函数中创建的 w 主窗口。由于没有往 w 窗口中放置任何组件,所以 w 是一个空白窗口。
编码实现简易的窗口界面
我们尝试向 w 主窗口添加一个文本框,需要对 MainWindow 类进行修改。修改后的 MainWindow 类如下:
//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
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 创建一个 QLable 对象
this->lab = new QLabel("Hello,World!",this);
}
MainWindow::~MainWindow()
{
}
和先前空的 MainWindow 类相比,做了如下修改:
- 添加了一个 QLabel 类的指针对象,相应地要引入
<QLabel>
头文件; - 在构造函数中定义了一个 QLabel 类的文本框对象,通过调用它的带参构造函数,设置它的父对象为当前类的对象,同时设置 “Hello,World!” 为要显示的文本信息。
有关 QLabel 组件的用法,我们会在《Qt QLabel文本框的使用》一节详细讲解,这里不用深究具体的语法。
再次运行程序,显示的窗口如下图所示:
图 5 带文本框的主窗口
图 1 和图 5 是类似的,区别在于图 5 中的 “Hello, World!” 没有加粗,也没有调整它在主窗口中的位置,这些都可以通过编码实现,后续讲 QLabel 时会做详细介绍。
图 5 中,“Hello,World!” 文本框的父窗口是主窗口,所以文本框位于主窗口中(位置默认在窗口的左上角),主窗口关闭时文本框也会随之关闭。
由此,我们就成功设计了一个包含文本框的窗口,这也是我们编写的第一个 Qt 程序。
Qt控件和事件
Qt 是一个著名的 GUI 框架,用来开发和用户交互的图形界面。作为 GUI 框架,丰富的控件和灵活的事件机制是不可或缺的,Qt 在这一方面做得非常优秀。
什么是 Qt 控件
Qt 控件又称组件或者部件,指用户看到的所有可视化界面以及界面中的各个元素,比如按钮、文本框、输入框等。
为了方便程序员开发,Qt 提供了很多现成的控件。打开某个带 ui 文件的 Qt Widgets Application 项目,ui 文件的 Widget Box 一栏展示了 Qt 提供的几乎所有控件:
图 1 Qt 提供的控件
Qt 中的每个控件都由特定的类表示,每个控件类都包含一些常用的属性和方法,所有的控件类都直接或者间接继承自 QWidget 类。实际开发中,我们可以使用 Qt 提供的这些控件,也可以通过继承某个控件类的方式自定义一个新的控件。
前面说过,Qt 中所有可视化的元素都称为控件,我们习惯将带有标题栏、关闭按钮的控件称为窗口。例如,下图展示了两种常用的窗口,实现它们的类分别是 QMainWindow 和 QDialog。
图 2 Qt 窗口
- QMainWindow 类生成的窗口自带菜单栏、工具栏和状态栏,中央区域还可以添加多个控件,常用来作为应用程序的主窗口;
- QDialog 类生成的窗口非常简单,没有菜单栏、工具栏和状态栏,但可以添加多个控件,常用来制作对话框。
除了 QMainWindow 和 QDialog 之外,还可以使用 QWidget 类,它的用法非常灵活,既可以用来制作窗口,也可以作为某个窗口上的控件。
窗口很少单独使用,它的内部往往会包含很多控件。例如图 2 中,我们分别往 MainWindow 和 Dialog 窗口中放置了一个按钮控件,根据需要还可以放置更多的控件。当窗口弹出时,窗口包含的所有控件会一同出现;当窗口关闭时,窗口上的所有控件也会随之消失。
实际开发中,制作应用程序的主窗口可以用 QMainWindow 或者 QWdiget;制作一个提示信息的对话框就用 QDialog 或 QWidget;如果暂时无法决定,后续可能作为窗口,也可能作为控件,就选择 QWidget。
什么是Qt事件
简单地理解,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 运行结果
整个程序的运行过程如下:
- 先创建了一个 QWidget 窗口,在窗口上添加一个 QPushButton 按钮;
- 当用户点击按钮时,Qt 会将此事件分派给 QPushButton 类的 mousePressEvent() 事件处理函数;
- mousePressEvent()函数内部会改变按钮的属性,提示用户成功按下了按钮,还会调用 clicked() 函数发出“用户点击按钮”的信号。对于发出的信号,程序中调用 connect() 函数指定 QWidget 类的 close() 函数接收 clicked() 信号,close() 函数会关闭 widget 窗口。
总结
实际开发中,我们用各种 Qt 控件设计出功能丰富的界面,用 Qt 事件完成与用户的交互。学习 Qt 界面编程,本质上就是学习 Qt 各个控件的用法以及对 Qt 事件的处理。本节我们只是对 Qt 控件和事件做了简单的了解,接下来将为您系统地讲解它们的用法。
Qt信号和槽机制详解
信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。
举个简单的例子,按钮和窗口本是两个独立的控件,点击按钮并不会对窗口造成任何影响。通过信号和槽机制,我们可以将按钮和窗口关联起来,实现“点击按钮会使窗口关闭”的效果。
信号和槽
在 Qt 中,用户和控件的每次交互过程称为一个事件,比如“用户点击按钮”是一个事件,“用户关闭窗口”也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出“按钮被点击”的信号,用户关闭窗口会发出“窗口被关闭”的信号。
Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到“按钮被点击”的信号后,会做出“关闭自己”的响应动作;再比如输入框自己接收到“输入框被点击”的信号后,会做出“显示闪烁的光标,等待用户输入数据”的响应动作。在 Qt 中,对信号做出的响应动作就称为槽。
图 1 信号和槽
信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。例如,“按钮被按下”这个信号可以用 clicked() 函数表示,“窗口关闭”这个槽可以用 close() 函数表示,信号和槽机制实现“点击按钮会关闭窗口”的功能,其实就是 clicked() 函数调用 close() 函数的效果。
信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:
- 信号函数用 signals 关键字修饰,槽函数用 public slots、protected slots 或者 private slots 修饰。signals 和 slots 是 Qt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数;
- 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。
为了提高程序员的开发效率,Qt 的各个控件类都提供了一些常用的信号函数和槽函数。例如 QPushButton 类提供了 4 个信号函数和 5 个 public slots 属性的槽函数,可以满足大部分场景的需要。
实际开发中,可以使用 Qt 提供的信号函数和槽函数,也可以根据需要自定义信号函数和槽函数,我们会在《Qt自定义信号和槽函数》一节做详细介绍。
Qt Creator 提供了很强大的 Qt GUI 开发手册,很容易就能查到某个控件类中包含哪些信号函数和槽函数。举个例子,查看 QPushButton 类中信号函数和槽函数的过程是:
- 在程序中引入
<QPushButton>
头文件,双击选中“QPushButton”并按 “Fn+F1” 快捷键,就会弹出 QPushButton 类的使用手册,如下图所示。
图 2 QPushButton类的使用说明
- 在 Contents 部分可以看到,QPushButton 类只提供了一些
Public Slots
属性的槽函数,没有提供信号函数。对于 QPushButton 类按钮,除了可以使用自己类提供的槽函数,还可以使用从父类继承过来的信号函数和槽函数。由图 2 可知,QPushButton 的父类是 QAbstractButton,点击 QAbstractButton 就可以直接跳转到此类的使用手册,如下图所示:
图 3 QPushButton父类使用说明
QAbstractButton 类中既有 Signals 信号函数,也有 Public Slots 槽函数,这里不再一一列举,感兴趣的读者可以自行查看。
注意,并非所有的控件之间都能通过信号和槽关联起来,信号和槽机制只适用于满足以下条件的控件:
- 控件类必须直接或者间接继承自 QObject 类。Qt 提供的控件类都满足这一条件,这里提供一张 Qt常用类的继承关系的高清图片,感兴趣的读者可以简单了解一下。
- 控件类中必须包含 private 属性的 Q_OBJECT 宏。
将某个信号函数和某个槽函数关联起来,需要借助 QObject 类提供的 connect() 函数。
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)
各个参数的含义分别是:
- sender:指定信号的发送者;
- signal:指定信号函数,信号函数必须用 SIGNAL() 宏括起来;
- reveiver:指定信号的接收者;
- method:指定接收信号的槽函数,槽函数必须用 SLOT() 宏括起来;
- type 用于指定关联方式,默认的关联方式为 Qt::AutoConnection,通常不需要手动设定。
用 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 QLabel文本框的使用
QLabel 是 Qt 帮我们写好的一个控件类,间接继承自 QWidget 类,它的继承关系如下:
QLabel -> QFrame -> QWidget
从字面上理解,QLabel 可以解释为“Qt 的 Label”,即 Qt 提供给我们的一种文本控件,它的基础功能是显示一串文本。例如,下图就是一个普通的文本框:
图 1 QLabel控件
除了显示一串文本外,QLabel 控件上还可以放置图片、超链接、动画等内容。例如:
图 2 QLabel放置图片和超链接文字
本质上,每个文本框都是 QLabel 类的一个实例对象。QLabel 类提供了两个构造函数,分别是:
QLabel(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())
QLabel(const QString &text, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())
各个参数的含义分别是:
- text 参数用于指定文本框中显示的文字;
- parent 参数用于指定文本框的父窗口;
- WindowFlags 是一种枚举类型,f 参数用来设置文本框的一些系统属性和外观属性,默认值为 Qt::Widget,表示当不指定父窗口时,文本框将作为一个独立窗口(如图 1、2 所示),反之则作为父窗口中的一个控件。f 参数的可选值有很多,比如 Qt::Window 表示文本框将作为一个独立的窗口,它自带边框和标题栏,Qt::ToolTip 表示文本框将作为一个提示窗口,不带边框和标题栏等等,这里不再一一列举。
需要注意的是,第一个构造函数中的 parent 和 f 参数都有默认值,因此 QLabel 类还隐含了一个默认构造函数。也就是说,创建 QLable 对象时可以不传递任何参数,或者只给 txt 参数传递一个字符串,就可以成功创建一个文本框。通常情况下,我们会给 text 和 parent 参数传递相应的值,即在创建文本框的同时指定文本内容和父窗口。
QLabel文本框的使用
QLabel 类本身提供有很多属性和方法,它还从父类继承了很多属性和方法。下表给大家罗列了 QLabel 类常用的一些属性和方法:
属 性 | 含 义 |
---|---|
alignment | 保存 QLabel 控件中内容的对齐方式,默认情况下,QLabel 控件中的内容保持左对齐和垂直居中。 该属性的值可以通过调用 alignment() 方法获得,可以借助 setAlignment() 方法修改。 |
text | 保存 QLabel 控件中的文本,如果 QLabel 控件中没有文本,则 text 的值为空字符串, 该属性的值可以通过 text() 方法获得,可以借助 setText() 方法修改。 |
pixmap | 保存 QLabel 控件内显示的图片,如果控件内没有设置图片,pixmap 的值为 0。 该属性的值可以通过调用 pixmap() 方法获得,可以借助 setPixmap() 方法修改。 |
selectedText | 保存 QLabel 控件中被选择了的文本,当没有文本被选择时,selectedText 的值为空字符串。 该属性的值可以通过调用 selectedText() 方法获得。 |
hasSelectedText | 判断用户是否选择了 QLabel 控件内的部分文本,如果是则返回 true,反之则返回 false。默认情况下,该属性的值为 false。 |
indent | 保存 QLabel 控件内文本的缩进量,文本的缩进方向和 alignment 属性的值有关。 该属性的值可以通过调用 indent() 方法获得,可以借助 setIndent() 方法修改。 |
margin | 保存 QLabel 控件中内容与边框之间的距离(边距),margin 的默认值为 0。 该属性的值可以通过调用 margin() 方法获得,可以借助 setMargin() 方法修改。 |
wordWrap | 保存 QLabel 控件内文本的换行策略。当该属性的值为 true 时,控件内的文本会在必要时自动换行。默认情况下,控件内的文本是禁止自动换行的。 该属性的值可以通过 wordWrap() 方法获得,可以借助 setWordWrap() 方法修改。 |
除了上表中提到了获取和修改属性值得成员方法外,下表给大家罗列了一些常用的操作 QLabel 控件的成员方法,它们有些定义在 QLabel 类内,有些是通过继承父类得到的:
成员方法 | 功 能 |
---|---|
hide() | 隐藏文本框。 |
clear() | 清空 QLabel 控件内所有显示的内容。 |
setToolTip(QString) | 设置信息提示,当用户的鼠标放在QLabel 文本框上时会自动跳出文字。 |
setToolTipDuration(int) | 设置提示信息出现的时间,单位是毫秒。 |
setStyleSheet(QString) | 设置 QLabel 文本框的样式。 |
setGeometry(int x, int y, int w, int h) | 设置 QLabel 文本框的位置 (x, y) 以及尺寸 (w, h)。 |
QLabel文本框的信号和槽
QLabel 控件只用来显示文本、图像等内容,很好与用户交互。但是,当 QLabel 控件内包含超链接内容时,可以使用 QLabel 类提供的两个信号函数:
信号函数 | 功 能 |
---|---|
linkActivated(const QString &link) | 用户点击超链接时触发,link 参数用于向槽函数传输超链接的 URL。 |
linkHovered(const QString &link) | 用户的鼠标悬停到超链接位置时触发,link 参数用于向槽函数传输超链接的 URL。 |
QLabel 控件提供了很多槽函数,如下表所示:
槽函数 | 功 能 |
---|---|
clear() | 清空 QLabel 控件内所有的内容。 |
setMovie(QMovie *movie) | 清空 QLabel 控件内所有的内容,改为显示指定的 movie 动画。 |
setNum(int num) | 清空 QLabel 控件内所有的内容,改为显示 num 整数的值。 |
setNum(double num) | 清空 QLabel 控件内所有的内容,改为显示 num 小数的值。 |
setPicture(const QPicture &picture) | 清空 QLabel 控件内所有的内容,改为显示经 QPicture 类处理的图像。 |
setPixmap(const QPixmap &) | 清空 QLabel 控件内所有的内容,改为显示经 QPixmap 类处理的图像。 |
setText(const QString &) | 清空 QLabel 控件内所有的内容,改为显示指定的文本。 |
除了表 2、3 罗列的这些信号和槽函数外,QLabel 类还从父类处继承了一些信号和槽函数,这里不再一一罗列。
实例演示QLabel文本框的用法
接下来通过一个实例,给大家演示 QLabel 控件中一些属性和方法的用法。
#include <QApplication>
#include <QLabel>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建一个文本框
QLabel lab;
//设置文本框内容居中显示
lab.setAlignment(Qt::AlignCenter);
//设置文本框的坐标和尺寸
lab.setGeometry(100,100,400,400);
//设置文本框的外观,包括字体的大小和颜色、按钮的背景色
lab.setStyleSheet("QLabel{font:30px;color:red;background-color:rgb(f9,f9,f9);}");
//设置文本框要显示超链接内容
lab.setText("<a href=\"http://c.biancheng.net\">C语言中文网");
//当用户鼠标位于文本框上时,显示提示内容
lab.setToolTip("点击超链接显示URL");
//提示内容显示 1 秒
lab.setToolTipDuration(1000);
//为文本框设置信号和槽,当用户点击超链接时,将文本框内容改为超链接的 URL
QObject::connect(&lab,&QLabel::linkActivated,&lab,&QLabel::setText);
//程序运行后,文本框显示
lab.show();
return a.exec();
}
执行结果如下图所示,用户最先看到的是图 3a),当用户鼠标移动到文本框区域内时,会提示“点击超链接显示URL”,提示时间为 1 秒。当用户点击“C语言中文网”时会触发 linkActivated() 信号函数,该函数会调用 setText() 函数,将文本框中显示的“C语言中文网”改为“http://c.biancheng.net”,字体颜色为红色,如图 3b) 所示。
图 3 程序运行结果
有关 QLabel 类提供的更多属性和方法,后续章节用到时会做详细地讲解,您也可以借助 Qt Creator 提供的 Qt 帮助手册自行查看 QLabel 类提供的成员。
Qt QPushButton按钮用法详解
按钮是 GUI 开发中最常用到的一种控件,作为一款著名的 GUI 开发框架,Qt 提供了很多种按钮,比如 QPushButton(普通按钮)、QRadioButton(单选按钮)、QToolButton(工具栏按钮)等。
QPushButton 是实际开发中最常使用的一种按钮,本节就给大家详细讲解它的用法。
QPushButton按钮的创建
QPushButton 类间接继承自 QWidget 类,它的继承关系如下:
QPushButton -> QAbstractButton -> QWidget
QAbstractButton 类是所有按钮控件类的基类,包含很多通用的按钮功能。
QPushButton 类专门用来创建可按压的按钮,如图 1 所示。
图 1 QPushButton 按钮
QPushButton 按钮上除了可以放置一串文本,文本左侧还可以放置图标,必要时还可以在按钮上放置图片。QPushButton 按钮可以作为一个独立的窗口,但实际开发中很少这样用,通常的用法是像图 1 这样将按钮内嵌到某个窗口中,作为一个子控件和其它控件搭配使用。
QPushButton 类提供了 3 个构造函数,分别是:
QPushButton(QWidget *parent = Q_NULLPTR)
QPushButton(const QString &text, QWidget *parent = Q_NULLPTR)
QPushButton(const QIcon &icon, const QString &text, QWidget *parent = Q_NULLPTR)
parent 参数用于指定父窗口;text 参数用于设置按钮上要显示的文字;icon 参数用于设置按钮上要显示的图标。
注意,第一个构造函数的 parent 参数附有默认值,所以 QPushButton 类还隐含着一个默认构造函数。也就是说,实例化 QPushButton 类对象时可以不传递任何参数。
QPushButton按钮的使用
QPushButton 类提供了很多实用的属性和方法,它还从父类继承了很多属性和方法。下表给大家罗列了一些比较常用的属性和方法:
属 性 | 含 义 |
---|---|
text | 保存按钮上要显示的文字。 该属性的值可以通过 text() 方法获取,也可以通过 setText(const QString &text) 方法修改。 |
icon | 保存按钮左侧要显示的图标。 该属性的值可以通过 icon() 方法获取,也可以通过 setIcon(const QIcon &icon) 方法修改。 |
iconsize | 保存按钮左侧图标的尺寸。 该属性的值可以通过 iconSize() 方法获取,也可以通过 setIconSize(const QSize &size) 方法修改。 |
size | 保存按钮的尺寸。 该属性的值可以通过 size() 方法获取,也可以通过 resize(int w, int h) 或者 resize(const QSize &) 方法修改。 |
font | 保存按钮上文字的字体和大小。 该属性的值可以通过 font() 方法获取,也可以通过 setFont(const QFont &) 方法修改。 |
flat | 初始状态下,按钮是否显示边框。flat 属性的默认值为 flase,表示按钮带有边框。 该属性的值可以通过 isFlat() 方法获取,也可以通过 setFlat(bool) 方法修改。 |
enabled | 指定按钮是否可以被按下。 该属性的默认值为 true,表示按钮可以被按下,即按钮处于启用状态。当该属性的值为 false 时,按钮将不能被点击,按钮处于禁用状态。 该属性的值可以通过 isEnabled() 方法获取,也可以通过 setEnabled(bool) 方法进行修改。 |
autoDefault | 当用户按下 Enter 回车键时,是否触发点击按钮的事件。 当按钮的父窗口为 QDialog 窗口时,该属性的值为 true;其它情况下,该属性的默认值为 false。 该属性的值可以通过 autoFault() 方法获取,也可以通过 setAutoFault(bool) 方法修改。 |
除了表 1 中罗列的获取、修改属性值的方法外,QPushButton 类常用的成员方法还有:
方 法 | 功 能 |
---|---|
move(int x, int y) | 手动指定按钮位于父窗口中的位置。 |
setStyleSheet(const QString &styleSheet) | 自定义按钮的样式,包括按钮上文字或图片的显示效果,按钮的形状等等。 |
setGeometry(int x, int y, int w, int h) | 同时指定按钮的尺寸和位置。 |
adjustSize() | 根据按钮上要显示的内容,自动调整按钮的大小。 |
setDisabled(bool disable) | 指定按钮是否可以被按下。当 disable 值为 true 时,表示按钮不能被按下,即禁用按钮的功能。 |
QPushButton按钮的信号和槽
GUI 程序中,按钮的主要任务是完成和用户之间的交互,下表罗列了 QPushButton 类常用的信号函数和槽函数:
信号函数 | 功 能 |
---|---|
clicked() clicked(bool checked = false) | 用户点击按钮并释放(或者按下按钮对应的快捷键)后,触发此信号。 |
pressed() | 用户按下按钮时会触发此信号。 |
released() | 用户松开按钮时会触发此信号。 |
槽函数 | 功 能 |
click() | 单击指定的按钮。 |
setIconSize() | 重新设置按钮上图片的尺寸。 |
hide() | 隐藏按钮控件。 |
setMenu(QMenu *menu) | 弹出与按钮关联的菜单。 |
实例演示QPushButton按钮用法
接下来通过一个实例,给大家演示 QPushButton 按钮的用法:
#include <QApplication>
#include <QWidget>
#include <QPushButton>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
QWidget widget;
//设置 widget 窗口的标题
widget.setWindowTitle("QWidget窗口");
//创建一个按钮,并内嵌到 widget 窗口中
QPushButton but("QPushButton按钮",&widget);
//按钮的位置位于距 widget 窗口左上角 (100,100) 的位置
but.move(100,100);
//设置按钮上文字的大小。
but.setStyleSheet("QPushButton{font:20px;}");
//调整按钮的尺寸
but.resize(200,200);
//建立信息和槽,当用户点击并释放按钮后,该按钮隐藏。
QObject::connect(&but,&QPushButton::clicked,&but,&QPushButton::hide);
widget.show();
return a.exec();
}
将程序复制到 main.cpp 文件中,运行结果为:
图 2 运行结果
Qt QLineEdit单行输入框用法详解
QLineEdit 是 Qt 提供的一个控件类,它直接继承自 QWdiget 类,专门用来创建单行输入框,如下图所示:
图 1 单行文本输入框
实际开发中,我们经常用到 QLineEdit 输入框,比如接收用户输入的个人信息、账户信息、角色名称等,就可以用 QLineEdit 实现。
QLineEdit单行输入框的创建
每个单行输入框都是 QLineEdit 类的一个实例对象,QLineEdit 类提供有两个构造函数,分别是:
QLineEdit(QWidget *parent = Q_NULLPTR)
QLineEdit(const QString &contents, QWidget *parent = Q_NULLPTR)
contents 参数用于指定输入框中的文本内容;parent 参数用于指定新建输入框控件的父窗口,新建输入框将会内嵌到父窗口上,作为父窗口的一个子控件。当然,我们也可以不指定父窗口,那么新建的输入框就会作为独立的窗口。
在 QLineEdit 输入框中,用户可以直接输入一行文本,也可以粘贴一行文本,还可以修改输入框内的文本。某些实际场景中,QLineEdit 输入框还可以对用户输入的内容加以限定,比如:
- 限定文本的长度,例如用户最多可以输入 20 个字符;
- 输入文本的格式,例如用户输入出生日期时,必须按照“yy-mm-dd”的格式输入;
- 输入的文本内容,例如当前输入框仅允许用户输入数字,或者只允许用户输入英文字符。
QLineEdit单行输入框的使用
QLineEdit 类的内部提供了很多实用的属性和方法,同时还从 QWidget 父类处继承了一些属性和方法。
下表列出了 QLineEdit 类对象经常调用的一些属性以及它们各自的含义:
属 性 | 含 义 |
---|---|
text | 保存输入框中的文本。 该属性的值可以通过 text() 方法获取,也可以通过 setText(const QString &) 方法修改。 |
maxLength | 设置输入框中最多可以放置的文本长度。当文本长度超出最大限度后,超出部分将被丢弃。 默认情况下,maxLength 的值为 32767。该属性的值可以通过 maxLength() 函数获得,也可以通过 setMaxLength(int) 方法修改。 |
placeholderText | 设置提示信息,例如当用户未选中输入框时,输入框中显示“请输入…”,而用户选中输入框时,“请输入…” 随之消失。 该属性的值可以通过 placeholderText() 方法获取,也可以通过 setPlaceholderText(const QString &) 方法修改。 |
clearButtonEnabled | 当输入框中有文本时,输入框的右侧可以显示一个“一键清除”按钮。该属性的默认值为 false,即输入框中不会自动显示清除按钮。 该属性的值可以通过 isClearButtonEnabled() 方法获取,也可以通过 setClearButtonEnabled(bool enable) 方法修改。 |
echoMode | 设定输入框中文本的显示样式,该属性的可选值有以下几个:QLineEdit::Normal:正常显示所输入的字符,此为默认选项。QLineEdit::NoEcho:不显示任何输入的字符,常用于密码类型的输入,且长度保密QLineEdit::Password:显示与平台相关的密码掩饰字符,而不是实际输入的字符。当用户重新点击输入框时,可以紧接着之前的文本继续输入。QLineEdit::PasswordEchoOnEdit:编辑时正常显示输入的字符,编辑完成后改为用密码掩饰字符显示。当用户重新点击输入框时,不能紧接着之前的文本继续输入。 该属性的是可以通过 echoMode() 方法获取,也可以通过 setEchoMode(EchoMode) 方法修改。 |
frame | 控制输入框的边框。默认情况下,输入框是带有边框的。 该属性的值可以通过 hasFrame() 方法获取,也可以通过 setFrame(bool) 方法修改。 |
除了上表提到的获取和修改属性值的方法外,QLineEdit 类还提供了一些功能实用的方法,例如:
成员方法 | 功 能 |
---|---|
move(int x, int y) | 指定输入框位于父窗口中的位置。 |
setValidator(const QValidator *v) | 限制输入框中的文本内容,比如输入框只包含整数。 |
setReadOnly(bool) | 设置输入框是否进入只读状态。在只读状态下,用户仍可以采用粘贴、拖拽的方式向输入框中放置文本,但无法进行编辑。 |
setAlignent(Qt::Alignment flag) | 设置输入框中输入文本的位置。 |
QLineEdit单行输入框的信号和槽
QLineEdit 类提供了几个信号函数,分别对应用户的几种输入状态。
信号函数 | 功 能 |
---|---|
textEdited(const QString &text) | 当用户编辑输入框中的文本时,此信号就会触发,text 参数即为用户新编辑的文本。 注意,当程序中试图通过 setText() 方法修改输入框中的文本时,不会触发此信号函数。 |
textChanged(const QString &text) | 只要输入框中的文本内容发生变化,就会触发此信息。 |
returnPressed() | 用户按下回车键时,会触发此信号。 |
editingFinished() | 用户按下回车键,或者鼠标点击输入框外的其它位置时,会触发此信号。 |
QLineEdit 类常用的槽函数有以下几个:
槽函数 | 功 能 |
---|---|
clear() | 清空文本框中的内容。 |
setText(const QString &) | 重新指定文本框中的内容。 |
QLineEdit单行输入框的用法示例
下面的实例给大家演示了 QLineEdit 单行输入框控件的基本用法,同时还演示了几个成员方法的用法。
#include <QApplication>
#include <QWidget>
#include <QLineEdit>
using namespace std;
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建一个窗口,作为输入框的父窗口
QWidget widget;
//设置窗口的标题
widget.setWindowTitle("QWidget窗口");
//接下来,分别创建两个输入框,用于让用户分别输入账号和密码
//创建账号输入框
QLineEdit lineEdit(&widget);
//指定输入框位于父窗口中的位置
lineEdit.move(100,100);
//设置提示信息
lineEdit.setPlaceholderText("请输入账号...");
//让输入框显示“一键清除”按钮
lineEdit.setClearButtonEnabled(true);
//创建密码输入框
QLineEdit lineEditPass(&widget);
lineEditPass.setPlaceholderText("请输入密码...");
lineEditPass.move(100,150);
//指定文本显示方式,保护用户账号安全
lineEditPass.setEchoMode(QLineEdit::Password);
//指定窗口的尺寸和显示文字的大小
widget.resize(500,300);
widget.setFont(QFont("宋体",16));
widget.show();
return a.exec();
}
运行结果为:
图 2 运行结果
Qt QListWidget列表框用法详解
很多应用程序中需要以列表的形式向用户展示数据(资源),比如 Windows 操作系统会以列表的方式展示很多张桌面背景图(如图 1a) 所示),再比如很多音乐播放器中以列表的形式展示音乐资源,用户可以选择自己喜欢的音乐(如图 1b) 所示)。
图 1 常见的列表窗口
使用 Qt 框架开发 GUI 程序,如果需要以列表的方法展示数据,可以优先考虑用 QListWidget 类实现。
QListWidget列表框的创建
QListWidget 是 Qt 提供的控件类,专门用来创建列表。QListWidget 类的继承关系如下:
QListWidget -> QListView -> QAbstractItemView -> QAbstractScrollArea -> QFrame -> QWidget
这里着重介绍一下 QListView 类,它也可以用来创建列表。对于初学者来说,我强烈建议先学习 QListWidget,它是“简易版”的 QListView,创建和使用列表的方式更简单、门槛更低,对初学者更友好。当然,QListWidget 只能创建结构简单的列表,如果要制作复杂的列表,应优先考虑 QListView,因为它的功能更强大,很多 QListWidget 难以实现的功能,QListView 都能实现。
通过实例化 QListWidget 类,可以很轻松地创建一个列表。QListWidget 类只提供了 1 个构造函数:
QListWidget(QWidget *parent = Q_NULLPTR)
parent 参数用来指定新建列表的父窗口,该参数的默认值是 Q_NULLPTR,表示新建控件没有父窗口。
语法层面上分析,可以不为 QListWidget 列表指定父窗口,那么它将作为一个独立的窗口。但实际开发中,通常会为 QListWidget 列表指定一个父窗口(例如 QWidget 窗口),它将作为父窗口中的一个子控件,和窗口中的其它控件一起搭配使用。
QListWidgetItem列表项
QListWidget 列表控件可以显示多份数据,每份数据习惯称为列表项(简称项),每个列表项都是 QListWidgetItem 类的实例对象。也就是说,QListWidget 中有多少个列表项,就有多少个 QListWidgetItem 类对象。
默认情况下,QListWidget 中每个列表项独自占用一行,每个列表项中可以包含文字、图标等内容。实际开发中,我们还可以将指定的窗口或者控件放置到列表项中显示,例如 QWidget 窗口、QLabel 文本框、QPushButton 按钮、QLineEdit 输入框等。
借助 QListWidgetItem 类,可以轻松管理 QListWidget 中的每个列表项,包括:
- 借助 QListWidgetItemo 类提供的 setIcon()、setText() 等方法,可以轻松地指定每个列表项要包含的内容;
- 借助 QListWidgetItemo 类提供的 setFont()、setBackground() 等方法,可以轻松地设置每个列表项的外观(文字大小、列表项背景等)。
当然,QListWidgetItem 类还提供有很多其它的成员方法,这里不再一一罗列。
QListWidget列表框的使用
对于刚刚创建好的 QListWidget 类对象,不包含任何 QListWidgetItem 类对象,就是一个空列表。借助 QListWidget 类以及父类提供的属性和方法,我们可以对新建列表执行多种操作。
下表给大家罗列了一些 QListWidget 类常用的属性和方法:
属性名或方法名 | 功 能(含 义) |
---|---|
count 属性 | 保存当前列表中含有的列表项的总数。 该属性的值可以通过 count() 方法获取。 |
currentRow 属性 | 保存当前选择的列表项所在的行数。 该属性的值可以通过 currentRow() 方法获取,也可以通过 setCurrentRow(int row) 方法修改当前选择的列表项。 |
sortingEnabled 属性 | 决定当前 QlistWidget 列表是否开发排序功能,默认值为 false,即不开启排序功能。 该属性的是可以通过 isSortingEnabled() 方法获取,可以通过 setSortingEnabled(bool enable) 方法进行修改。 |
SelectionMode 属性 | 指明当前列表中是否可以同时选择多个列表项,或者是否只能连续选择多个列表项。 该属性是枚举类型,可选值有 5 个:QAbstractItemView::SingleSelection:用户最多只能选择一个列表项。QAbstractItemView::ContiguousSelection:用户按住 Shift 键,可以连续选择多个列表项。QAbstractItemView::ExtendedSelection:用户按住 Ctrl 键,可以选中多个列表项。当用户按住 Shift 键,也可以连续选择多个列表项。QAbstractItemView::MultiSelection:用户可以选择多个列表项。QAbstractItemView::NoSelection:用户无法选择任何列表项。 该属性的值可以通过 selectionMode() 方法获取,也可以通过 setSelectionMode(QAbstractItemView::SelectionMode mode) 方法进行修改。 |
ViewMode 属性 | 指定 QListWidget 是按行显示数据(如图 1b) ),还是分列显示数据(如图 1a) )。 该属性是枚举类型,可选值有 2 个:QListView::ListMode:一行一行地显示列表项,默认情况下,各个列表项不能拖动。QListView::IconMode:分列显示列表项,默认情况下,各个列表项可以拖动。 该属性的值可以通过 viewMode() 方法获取,也可以通过 setViewMode(ViewMode mode) 方法进行修改。 |
void addItem ( const QString & label ) void addItem ( QListWidgetItem * item ) void addItems ( const QStringList & labels ) | 向 QListWidget 列表的尾部添加指定项,可以是一个文本(label)、一个列表项(item),还可以一次性添加多个文本(labels)。 |
void QListWidget::setItemWidget(QListWidgetItem *item, QWidget *widget) | 将指定的 widget 窗口添加到 item 列表项中。 |
currentItem() | 返回当前选中的列表项。 |
removeItemWidget(QListWidgetItem *item) | 删除指定的 item 列表项。 |
sortItems(Qt::SortOrder order = Qt::AscendingOrder) | 默认将所有列表项按照升序排序,通过指定参数为 Qt::DescendingOrder,可以进行降序排序。 |
takeItem(int row) | 返回位于 row 行的列表项。 |
selectedItems() | 返回当前被选择的所有列表项。 |
QListWidget列表框的信号和槽
对于给定的 QlistWidget 列表,用户可以选中其中的一个或者某些列表项,甚至还可以修改列表项中的内容。QListWidget 类具有很多信号和槽信息,可以捕捉用户的很多动作,还可以针对用户的动作做出适当地响应。
下表给大家罗列了一些常用的信号和槽函数:
信号函数 | 功 能 |
---|---|
itemClicked(QListWidgetItem *item) | 用户点击某个列表项时会触发此信号函数,item 参数指的就是被用户点击的列表项。 |
itemDoubleClicked(QListWidgetItem *item) | 用户双击某个列表项时会触发此信号函数,item 参数指的就是被用户双击的列表项。 |
itemPressed(QListWidgetItem *item) | 鼠标按下某个列表项时会触发此信号函数,item 参数指的就是被鼠标按下的列表项, |
itemSelectionChanged() | 当选择的列表项发生变化时会触发此信号函数。 |
currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) | 当前列表项发生变化时会触发此信号函数,current 参数指的是新选择的列表项;previous 参数指的是先前选择的列表项。 |
槽函数 | 功 能 |
clear() | 删除列表中的所有列表项。 |
scrollToItem(const QListWidgetItem *item, QAbstractItemView::ScrollHint hint = EnsureVisible) | 用 hint 参数指定的滑动方式,让用户看到指定的 item 列表项。 |
selectAll() | 选择所有的列表项。 |
scrollToBottom() scrollToTop() | 分别将列表滑动到底部和顶部。 |
实例演示QListWidget列表框的用法
接下来,我们亲自制作一个 QListWidget 列表:
//main.cpp#include <QApplication>#include <QWidget>#include <QListWidget>#include <QLabel>#include <QListWidgetItem>using //main.cpp
#include <QApplication>
#include <QWidget>
#include <QListWidget>
#include <QLabel>
#include <QListWidgetItem>
using namespace std;
class QMyLabel:public QLabel{
Q_OBJECT
public slots:
void rsetText(QListWidgetItem \*item);
};
void QMyLabel::rsetText(QListWidgetItem \*item){
this->setText(item->text());
}
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建一个窗口,作为输入框的父窗口
QWidget widget;
//设置窗口的标题
widget.setWindowTitle("QWidget窗口");
widget.resize(500,500);
QListWidget listWidget(&widget);
listWidget.resize(500,400);
listWidget.setFont(QFont("宋体",14));
listWidget.addItem("C语言中文网");
listWidget.addItem("http://c.biancheng.net");
listWidget.addItem(new QListWidgetItem("Qt教程"));
QMyLabel print;
print.setText("选中内容");
print.setParent(&widget);
print.resize(500,100);
print.move(0,400);
print.setAlignment(Qt::AlignCenter);
QObject::connect(&listWidget,&QListWidget::itemClicked,&print,&QMyLabel::rsetText);
widget.show();
return a.exec();
}
//QMyLabel类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。
#include "main.moc"
程序中,我们自定义了一个 QMyLabel 类,它继承自 QLabel 文本框类,因此 QMyLabel 也是一个文本框类。在 QMyLabel 类中,我们自定义了一个 rsetText() 槽函数。
程序的运行结果为:
图 2 QListMidget 控件实际应用
通过运行动画可以看到,我们将 QListMidget 和自定义的 QMyLabel 控件合理地分布在 QWidget 窗口上,通过为它们的信号和槽建立连接,使得当点击列表中的某个列表项时,文本框可以显示列表项中的文本内容。
Qt布局管理详解(5种布局控件)
实际开发中,一个界面上可能包含十几个控件,手动调整它们的位置既费时又费力。作为一款成熟的 GUI 框架,Qt 提供了很多摆放控件的辅助工具(又称布局管理器或者布局控件),它们可以完成两件事:
- 自动调整控件的位置,包括控件之间的间距、对齐等;
- 当用户调整窗口大小时,位于布局管理器内的控件也会随之调整大小,从而保持整个界面的美观。
总之借助布局管理器,我们无需再逐个调整控件的位置和大小,可以将更多的精力放在软件功能的实现上。
Qt 共提供了 5 种布局管理器,每种布局管理器对应一个类,分别是 QVBoxLayout(垂直布局)、QHBoxLayout(水平布局)、QGridLayout(网格布局)、QFormLayout(表单布局)和 QStackedLayout(分组布局),它们的继承关系如下图所示:
图 1 各个布局管理类的继承关系
QVBoxLayout垂直布局
垂直布局指的是将所有控件从上到下(或者从下到上)依次摆放,例如:
图 2 QVBoxLayout垂直布局
图 2 展示了 4 个 QPushButton 按钮利用 QVBoxLayout 垂直布局的效果。实际场景中,QVBoxLayout 中还可以放置其它控件,比如 QLabel 文本框、QLineEdit 单行输入框等。
程序中使用 QVBoxLayout 布局控件,需提前引入<QVBoxLayout>
头文件。每个 QVBoxLayout 控件本质都是 QVBoxLayout 类的实例对象,该类提供了两个构造函数,分别是:
QVBoxLayout()
QVBoxLayout(QWidget *parent)
创建 QVBoxLayout 控件的同时可以指定父窗口,那么它将作为父窗口中管理其它控件的工具;也可以暂时不指定父窗口,待全部设置完毕后再将其添加到某个窗口中。
QVBoxLayout 类没有新增任何成员方法,它只能使用从父类继承的成员方法,下表给大家罗列了常用的一些:
成员方法 | 功 能 |
---|---|
void QBoxLayout::addWidget(QWidget *widget, int stretch = 0, Qt::Alignment alignment = Qt::Alignment()) | 向布局管理器中添加指定的 widget 控件。 默认情况下,stretch 拉伸系数为 0,表示 widget 控件的尺寸为默认值;alignment 是一个枚举类型参数,默认值也是 0,表示该控件会填满占用的整个空间。 |
void QBoxLayout::addStretch(int stretch = 0) | 添加一个空白行,整个窗口中除了控件占用的区域外,其它区域可以由多个(≥0)空白行分摊,分摊比例取余于各个空白行设置的 stretch 参数的值。 strech 参数的默认值为 0,表示当窗口很小时,空白行可以不占据窗口空间。当窗口中包含多个 strech 值为 0 的空白行时,它们会平分窗口中的空白区域。 |
void QBoxLayout::addSpacing(int size) | 添加一个 size 大小的固定间距。 |
void QLayout::setMargin(int margin) | 设置布局管理器中所有控件的外边距,上、下、左、右外边距的大小都为 margin。默认情况下,所有方向的外边距为 11 px。 |
void QLayout::setContentsMargins(int left, int top, int right, int bottom) | 设置布局管理器中所有控件的外边距,和 setMargin() 的区别是,此方法可以自定义上、下、左、右外边距的值。 |
void QBoxLayout::setDirection(Direction direction) | 设置布局管理器中控件的布局方向,Direction 是一个枚举类型,对于 QVBoxLayout 布局管理器,direction 参数的值通常选择 QBoxLayout::TopToBottom(从上到下依次摆放)或者 QBoxLayout::BottomToTop(从下到上依次摆放)。 |
bool QBoxLayout::setStretchFactor(QWidget *widget, int stretch) | 设置布局管理器中某个控件的拉伸系数。 |
bool QBoxLayout::setStretchFactor(QLayout *layout, int stretch) | 布局管理器内部可以再放置一个布局管理器,该方法用来设置内部某个布局管理器的拉伸系数。 |
举个简单的例子:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建主窗口
QWidget widget;
widget.setWindowTitle("QVBoxLayout垂直布局");
//创建垂直布局管理器
QVBoxLayout \*layout=new QVBoxLayout;
//设置布局管理器中所有控件从下往上依次排列
layout->setDirection(QBoxLayout::BottomToTop);
//连续创建 3 个文本框,并设置它们的背景和字体大小
QLabel lab1("Label1");
lab1.setStyleSheet("QLabel{background:#dddddd;font:20px;}");
lab1.setAlignment(Qt::AlignCenter);
QLabel lab2("Label2");
lab2.setStyleSheet("QLabel{background:#cccccc;font:20px;}");
lab2.setAlignment(Qt::AlignCenter);
QLabel lab3("Label3");
lab3.setStyleSheet("QLabel{background:#ffffff;font:20px;}");
lab3.setAlignment(Qt::AlignCenter);
//将 3 个文本框和 2 个空白行添加到管理器中,它们的伸缩系数比是 2:1:2:3:3
layout->addStretch(2);
layout->addWidget(&lab1,1);
layout->addWidget(&lab2,2);
layout->addWidget(&lab3,3);
layout->addStretch(3);
//将布局管理器添加到 widget 窗口中
widget.setLayout(layout);
widget.show();
return a.exec();
}
程序中做了以下几个操作:
- 通过调用 setDirection() 方法,将添加到 QVBoxLayout 管理器中的所有控件(包括空白行)按照从下到上的顺序依次摆放。举个例子,由于 lab1 文本框是第二个添加到管理器中的,因此在最终显示的界面中,lab1 位于倒数第二的位置。
- 通过调用 addStrech() 方法,向管理器中先后添加了两个空白行,它们的伸缩系数分别为 2 和 3,因此 widget 窗口中的空白区域会平均分为 5 份,一个空白行占 3 份,另一个占 2 份。
- 通过调用 addWidget() 方法,向管理器中先后添加了 3 个文本框,它们的拉伸系数比为 1:2:3,所以当我们拉伸 widget 窗口时,三个文本框的大小(宽度)呈现 1:2:3 的关系。
- 通过调用 setLayout() 方法,成功地将 layout 布局管理器添加到了 widget 窗口中。当然,也可以在创建 layout 对象时指定 widget 作为它的父窗口,两种方式是完全等价的。
执行结果为:
图 3 QVBoxLayout 实例演示
QHBoxLayout水平布局
水平布局指的是将所有控件从左到右(或者从右到左)依次摆放,例如:
图 4 QHBoxLayout水平布局
使用 QHBoxLayout 水平布局控件,程序中要提前引入<QHBoxLayout>
头文件。QHBoxLayout 和 QVBoxLayout 继承自同一个类,它们的用法非常相似,比如 QHBoxLayout 类也提供了两个构造函数:
QHBoxLayout()
QHBoxLayout(QWidget *parent)
QHBoxLayout 类也没有新添任何成员方法,它只能使用从父类继承的成员方法。因此,表 1 中罗列的所有成员方法也同样适用于 QHBoxLayout 对象。
注意,当 QHBoxLayout 对象调用表 1 中的 addStretch() 方法时,表示添加一个空白列。
举个简单的例子:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QHBoxLayout>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建主窗口
QWidget widget;
widget.setWindowTitle("QHBoxLayout水平布局");
//创建水平布局管理器
QHBoxLayout \*layout=new QHBoxLayout;
//设置布局管理器中所有控件的布局方向为从右往左依次排列
layout->setDirection(QBoxLayout::RightToLeft);
//连续创建 3 个文本框,并设置它们的背景和字体大小
QLabel lab1("Label1");
lab1.setStyleSheet("QLabel{background:#dddddd;font:20px;}");
lab1.setAlignment(Qt::AlignCenter);
QLabel lab2("Label2");
lab2.setStyleSheet("QLabel{background:#cccccc;font:20px;}");
lab2.setAlignment(Qt::AlignCenter);
QLabel lab3("Label3");
lab3.setStyleSheet("QLabel{background:#ffffff;font:20px;}");
lab3.setAlignment(Qt::AlignCenter);
//将 3 个文本框和 2 个空白列添加到管理器中,它们的拉伸系数比是 2:1:2:3:3
layout->addStretch(2);
layout->addWidget(&lab1,1);
layout->addWidget(&lab2,2);
layout->addWidget(&lab3,3);
layout->addStretch(3);
//将布局管理器添加到 widget 窗口中
widget.setLayout(layout);
widget.show();
return a.exec();
}
程序执行结果为:
图 5 QHBoxLayout水平布局实例
图 5 中,最左侧和最右侧各添加了一个空白列,它们的伸缩比例为 3:2,即它们的宽度比为 3:2。
QGridLayout网格布局
网格布局又称格栅布局或者表格布局,指的是将一些控件按照行和列排列在窗口上,例如:
图 6 QGridLayout网格布局
QGridLayout 的行标和列标都从 0 开始,例如图 6 中 one 按钮的位置为 (0, 0),Four 按钮的位置为 (2, 0)。我们可以随意指定 QGridLayout 的行数和列数,各个控件可以随意摆放,必要时某些位置可以空着不用。
使用 QGridLayout 网格控件,程序中需引入<QGridLayout>
头文件。每个 QGridLayout 控件都是 QGridLayout 类的一个实例对象,该类提供了两个构造函数,分别是:
QGridLayout(QWidget *parent)
QGridLayout()
QGridLayout 类提供了很多实用的成员方法,常用的如下表所示:
成员方法 | 功 能 |
---|---|
int QGridLayout::rowCount() const | 获取网格的行数。 |
int QGridLayout::columnCount() const | 获取网格的列数。 |
void QGridLayout::addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = Qt::Alignment()) | 将 widget 控件添加到网格中的 (row,column) 位置处,并且可以自定义该控件的对齐方式。 |
void QGridLayout::addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment = Qt::Alignment()) | 将 widget 控件从 (fromRow, fromColumn) 位置开始,跨 rowSpan 行和 ColumnSpan 列添加到网格中,并且可以自定义该控件的对齐方式。 |
void QGridLayout::addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment = Qt::Alignment()) | 向网格中的 (row, column) 位置处添加 layout 布局管理器。 |
void QGridLayout::addLayout(QLayout *layout, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment = Qt::Alignment()) | 将 layout 布局管理器从 (row, column) 位置开始,跨 rowSpan 行和 ColumnSpan 列添加到网格中,并且可以自定义该布局控件的对齐方式。 |
void QGridLayout::setColumnStretch(int column, int stretch) | 给指定的第 column 列设置伸缩系数。 |
void QGridLayout::setRowStretch(int row, int stretch) | 给指定的第 row 行设置伸缩系数。 |
void QGridLayout::setColumnMinimumWidth(int column, int minSize) | 设置第 column 列的最小宽度。 |
void QGridLayout::setRowMinimumHeight(int row, int minSize) | 设置第 row 行的最小宽度。 |
举个简单的例子:
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QGridLayout>
#include <QPushButton>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建主窗口
QWidget widget;
widget.setWindowTitle("QGridLayout网格布局");
//创建 4 个按钮和 1 个文本框
QPushButton \*but1 = new QPushButton("but1");
QPushButton \*but2 = new QPushButton("but2");
QLabel \*lab3 = new QLabel("lab");
lab3->setStyleSheet("QLabel{background:#dddddd;font:20px;}");
lab3->setAlignment(Qt::AlignCenter);
QPushButton \*but3 = new QPushButton("but3");
QPushButton \*but4 = new QPushButton("but4");
//创建网格布局控件
QGridLayout \*layout = new QGridLayout;
//向 layout 中添加控件,并指定各个控件的位置
layout->addWidget(but1, 0, 0);
layout->addWidget(but2, 0, 2);
layout->addWidget(lab3, 1, 0, 3, 3);
layout->addWidget(but3, 4, 0);
layout->addWidget(but4, 4, 2);
//将 layout 添加到 widget 窗口中
widget.setLayout(layout);
widget.show();
return a.exec();
}
程序运行结果为:
图 7 QGridLayout网格布局实例
图 7 中,文本框控件从 (1,0) 位置开始,占据了 3 行 3 列的表格空间。
QFormLayout表单布局
Qt 提供了很多种输入框控件,包括 QLineEdit 单行输入框、QTextEdit 多行输入框等。通常情况下,每个输入框的旁边都会附带一些文字(又称标签),用来提示用户需要输入的信息。例如,图 8 中第一个输入框的标签为 “Name”,提示用户填写自己的姓名。
图 8 QFromLayout表单布局
生成图 8 这样的界面,实现的方法有很多,例如:
- 分别创建 3 个 QLabel 控件和 3 个 QLineEdit 控件,手动指定它们的位置;
- 在 QHBoxLayout 中嵌套 3 个 QVBoxLayout,又或者在 QVBoxLayout 中嵌套 3 个 QHBoxLayout,然后再添加 3 个 QLabel 控件和 3 个 QLineEdit 控件;
- 使用 QGridLayout 创建一个 3 行 2 列的表格,向表格中添加 3 个 QLabel 控件和 3 个 QLineEdit 控件。
- 使用 QFormLayout 表单布局控件实现。
第 1 种方法最大的弊端在于,各个控件的尺寸都是固定的,不会随着父窗口尺寸的改变而改变。第 2、3、4 种方法都是借助布局控件实现的,各个控件的尺寸可以自动调整,但前两种方法需要手动设置每一列的 strech 拉伸系数,而第 4 种方式不需要。总之对于生成类似图 8 这样的表单窗口,建议大家使用 QFormLayout 控件,因为使用 QFormLayout 编写的代码量最少,开发效率最高。
QFormLayout 可以容纳很多个输入框以及对应的标签,并将它们从上到下依次排列在界面上(如图 8 所示)。大多数情况下,QFormLayout 底层是用 QGridLayout 网格布局管理器实现的,和后者不同的是,QFormLayout 只包含 2 列(不限制行数),且第一列放置标签,第二列放置输入框。
使用 QFormLayout 布局控件之前,程序中应引入<QFormLayout>
头文件。每一个表单布局控件都是 QFormLayout 类的一个实例对象,该类仅提供了一个构造函数:
QFormLayout(QWidget *parent = Q_NULLPTR)
下表给大家罗列了操作 QFormLayout 对象常用的一些成员方法:
成员方法 | 功 能 |
---|---|
void QFormLayout::addRow(QWidget *label, QWidget *field) | 将指定的 field 控件和存储标签的 label 控件添加到表单控件中的末尾。 |
void QFormLayout::addRow(const QString &labelText, QWidget *field) | 将指定的 field 控件和 labelText 标签添加到表单控件的末尾。 |
void QFormLayout::insertRow(int row, const QString &labelText, QWidget *field) | 将指定的 field 控件和 labelText 标签插入到表单控件中指定行的位置。 |
void QFormLayout::removeRow(int row) | 删除表单控件中的指定行。 |
void QFormLayout::removeRow(QWidget *widget) | 删除表单控件中 widget 控件所在的行。 |
void setRowWrapPolicy(RowWrapPolicy policy) | 设置标签的显示格式,默认标签位于控件的左侧。 RowWrapPolicy 是 QFormLayout 中定义的枚举类型,该类型包含 3 个值:QFormLayout::DontWrapRows:标签始终在输入框的左侧;QFormLayout::WrapLongRows:根据输入框的尺寸,标签可能位于输入框的左侧,也可能位于上方;QFormLayout::WrapAllRows:标签始终在输入框的上方; |
void QFormLayout::setSpacing(int spacing) | 将行间距和列间距设置为 spacing。 |
举个简单的例子:
#include <QApplication>
#include <QWidget>
#include <QLineEdit>
#include <QFormLayout>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建主窗口
QWidget widget;
widget.setWindowTitle("QFormLayout表单布局");
//创建 4 个按钮和 1 个文本框
QFormLayout\* layout = new QFormLayout();
//设置表单中的标签都位于控件的上方
layout->setRowWrapPolicy(QFormLayout::WrapAllRows);
//添加 3 行输入框和标签
layout->addRow("Name:",new QLineEdit());
layout->addRow("Email:",new QLineEdit());
layout->addRow("Adress:",new QLineEdit());
//设置行间距和列间距为 10
layout->setSpacing(10);
//将 layout 表单添加到 widget 窗口中
widget.setLayout(layout);
widget.show();
return a.exec();
}
程序运行结果为:
图 9 QFormLayout表单布局实例
QStackedLayout分组布局
QStackedLayout 布局管理器可以容纳多个控件或者窗口,但每次只显示其中的一个。
举个简单的例子,下图中的界面就使用了 QStackedLayout 布局管理器:
图 10 QStackedLayout布局管理器
整个窗口被一分为二,左侧是 QListWidget 列表控件,右侧是 QStackedLayout 布局管理器。QStackedLayout 中包含 QPushButonn、QLabel 和 QLineEdit 这 3 个控件,但每次只能 3 个控件中的一个。
QStackedLayout 自身无法切换当前显示的控件或窗口,实际应用时通常和 QListWidget 或者 QComboBox 搭配使用。
使用 QStackedLayout 布局控件,程序中必须先引入<QStackedLayout>
头文件。 每个 QStackedLayout 控件都是 QStackedLayout 类的一个实例对象,该类提供有 3 个构造函数,分别是:
QStackedLayout()
QStackedLayout(QWidget *parent)
QStackedLayout(QLayout *parentLayout)
借助第二个构造函数,我们可以将 QStackedLayout 添加到指定的 parent 窗口中;借助第三个构造函数,我们可以将 QStackedLayout 嵌入到指定的 parentLayout 布局控件中
本节学习的 5 种布局控件都可以嵌套使用,例如将 QVBoxLayout 放到 QHBoxLayout 内部、将 QGridLayout 放到 QStackedLayout 内部等。
下表罗列了操作 QStackedLayout 对象常用的一些成员方法:
成员方法 | 功 能 |
---|---|
int QStackedLayout::addWidget(QWidget *widget) | 将 widget 控件添加到 QStackedLayout 控件中。 |
int QStackedLayout::insertWidget(int index, QWidget *widget) | 将 widget 控件插入到 QStackedLayout 控件指定的位置处。 |
信号函数 | 功 能 |
void QStackedLayout::currentChanged(int index) | 切换当前显示的控件时,会触发此信号,index 为显示的新控件的索引。 |
void QStackedLayout::widgetRemoved(int index) | 移除某个控件时,会触发此信号,index 为被移除控件的索引。 |
槽函数 | 功 能 |
void setCurrentIndex(int index) | 将第 index 个控件作为要显示的控件。 |
void QStackedLayout::setCurrentWidget(QWidget *widget) | 设置 widget 作为当前要实现的控件。注意,必须保证 widget 存储在 QStackedLayout 控件中。 |
这里我们以图 10 所示的窗口为例,实现代码如下:
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QStackedLayout>
#include <QListWidget>
#include <QHBoxLayout>
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建主窗口
QWidget widget;
widget.setWindowTitle("QStackedLayout分组布局");
widget.resize(600,400);
//向主窗口中添加一个水平布局控件
QHBoxLayout \*layout=new QHBoxLayout;
//创建一个列表
QListWidget listWidget(&widget);
listWidget.setMinimumWidth(150);
listWidget.setFont(QFont("宋体",14));
listWidget.addItem("QPushButton");
listWidget.addItem("QLabel");
listWidget.addItem("QLineEdit");
//新建 3 个窗口,分别放置文本框、按钮和单行输入框
QWidget widget1;
widget1.setMinimumSize(400,400);
QPushButton but1("这是一个按钮",&widget1);
QWidget widget2;
widget2.setMinimumSize(400,400);
QLabel lab1("这是一个文本框",&widget2);
QWidget widget3;
widget3.setMinimumSize(400,400);
QLineEdit edit("这是一个单行输入框",&widget3);
//创建一个分组布局,将 3 个窗口添加到分组控件中
QStackedLayout \*stackedLayout = new QStackedLayout;
stackedLayout->addWidget(&widget1);
stackedLayout->addWidget(&widget2);
stackedLayout->addWidget(&widget3);
//layout 第一列添加 QListWidget 控件,第二列添加分组布局控件,设置它们的伸缩系数比为 1:4
layout->addWidget(&listWidget,1);
layout->addLayout(stackedLayout,4);
//将 layout 水平布局控件添加到 widget 窗口中
widget.setLayout(layout);
widget.show();
//连接信号和槽,实现当点击列表中的某一项,切换分组布局管理器显示的控件
QObject::connect(&listWidget,&QListWidget::currentRowChanged,stackedLayout,&QStackedLayout::setCurrentIndex);
return a.exec();
}
此程序中,我们在 QHBoxLayout 水平布局控件内又放置了一个 QStackedLayout 分组布局控件。感兴趣的读者可以编写程序,测试其它布局控件之间嵌套的效果。
Qt pro文件详解
默认情况下,每个 Qt 项目都包含一个后缀名为.pro
、名称和项目名相同的文件,我们通常称它为项目管理文件或者工程管理文件(简称 pro 文件)。
例如,新建一个 Qt 项目,如下图所示:
图 1 项目结构
该项目的项目名为 Demo,项目中共包含 4 个文件,其中 Demo.pro 就是项目管理文件。
任何一个 Qt 项目都至少包含一个 pro 文件,此文件负责存储与当前项目有关的配置信息,比如:
- 项目中用到了哪些模块?
- 项目中包含哪些源文件,哪些头文件,它们的存储路径是什么?
- 项目使用哪个图片作为应用程序的图标?
- 项目最终生成的可执行文件的名称是什么?
所谓模块,可以简单地理解为文件夹或者压缩包,内部包含多个功能相近的类。作为一款成熟的 GUI 框架,Qt 提供了大量的类,根据这些类的功能,Qt 将它们分成了几个组,每个组称为一个模块。打开 Qt Creator 的帮助界面并搜索“All modules”,可以看到 Qt 的所有模块。
一个项目中可能包含上百个源文件,Qt 编译这些源文件的方法是:先由 qmake 工具根据 pro 文件记录的配置信息生成相应的 makefile 文件,然后执行 make 命令完成对整个项目的编译。也就是说,pro 文件存储的配置信息是用来告知编译器如何编译当前项目的,所以一个 Qt 项目要想完美运行,既要保证各个源文件中程序的正确性,还要保证 pro 文件中配置信息的合理性。
对于一个刚刚创建好的 Qt 项目,pro 文件并不是空的,而是包含一些基本的配置信息。实际开发中,Qt 会自动修改 pro 文件的内容,但有时也需要我们手动修改,例如程序中用到某个第三方库时,就需要我们手动修改 pro 文件。
接下来,我们就为大家详细地讲解 pro 文件中各个配置信息的含义,以及如何手动修改 pro 文件。
pro文件中的配置信息
在图 1 所示的 Demo 项目中,双击打开 Demo.pro 项目管理文件,会看到如下内容:
#-------------------------------------------------
Project created by QtCreator 2021-08-31T16:05:04
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = Demo
TEMPLATE = app
The following define makes your compiler emit warnings if you use
any feature of Qt which as been marked as deprecated (the exact warnings
depend on your compiler). Please consult the documentation of the
deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
You can also make your code fail to compile if you use deprecated APIs.
In order to do so, uncomment the following line.
You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES +=
main.cpp
mainwindow.cpp
HEADERS +=
mainwindow.h
以上是 Demo.pro 配置文件中默认包含的内容。其中,#
号是注释符号,除了以#
号开头的注释内容外,其它内容都是当前项目的配置信息,比如QT += core gui
、TARGET = Demo
等。
pro 文件可以存储上百条配置信息,每条配置信息由三部分构成:
- 前半部分是关键字,也称配置项,用来指明配置信息的含义;
- 中间用 +=、-=、= 等数学符号连接配置项和它对应的值;
- 后半部分是配置项对应的值,一个配置项可以对应多个值,每个值代表不同的含义。
例如在 QT += core gui
中,Qt
是配置项,core
和gui
是该配置项的值,中间用+=
符号连接。下表给大家罗列了一些常用的配置项以及它们各自的含义。
配置项 | 含 义 |
---|---|
QT | 指定项目中用到的所有模块,默认值为 core 和 gui,中间用 += 符号连接。 |
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets | 如果 QT 版本大于 4(Qt5 或更高版本),则需要添加 widgets 模块,该模块包含所有控件类。 |
TARGET | 指定程序成功运行后生成的可执行文件的名称,中间用 = 符号连接。 |
TEMPLATE | 指定如何运行当前程序,默认值为 app,表示当前程序是一个应用程序,可以直接编译、运行。常用的值还有 lib,表示将当前程序编译成库文件。 |
DEFINES | 在程序中新定义一个指定的宏,比如 DEFINES += xxx,如同在程序中添加了 #define xxx 语句。 |
SOURCES | 指定项目中包含的所有 .cpp 源文件。 |
HEADERS | 指定项目中包含的所有 .h 头文件。 |
FORMS | 指定项目中包含的 ui 文件。 |
INCLUDEPATH | 指定头文件的存储路径,例如:INCLUDEPATH += /opt/ros/include |
CONFIG | 经常对应的值有:release:以 release 模式编译程序;debug:以 debug 模式编译程序;warn_on:编译器输出尽可能多的警告;c++11:启动 C++11 标准支持。例如 CONFIG += c++11。 |
根据上表中对各个配置项的讲解,您可以很轻松地搞清楚 Demo.pro 文件中各个配置项的含义,这里不再过多赘述。
上表中,大部分配置项不需要我们手动修改,比如 SOURCES、HEADERS、FORMS 等,当我们添加或者删除项目中的源文件时,Qt 会自动修改这些配置项。有些配置项需要手动修改,比如 QT 配置项,接下来重点给大家讲解 QT 的用法。
QT配置项
前面提到,Qt 根据各个类的功能将它们分到不同的模块,因此程序中要想使用某个类,必须完成两项准备工作:
- 引入包含该类的头文件,通常情况下,Qt 中每个类的类名和包含它的头文件的名称是相同的,比如 QWiget 窗口类位于
<QWidget>
头文件中; - 将该类所属的模块添加到 pro 项目管理文件中。
QT
用来指明当前项目中用到的所有模块,它的默认值是 core 和 gui,分别表示引入 Core 模块和 GUI 模块:
- Core 模块包含了 Qt GUI 界面开发的核心功能,其它所有模块都需要依赖于这个模块,它是所有 Qt GUI 项目必备的模块;
- GUI 模块提供了用于开发 GUI 应用程序的必要的一些类。
每个新创建的 Qt GUI 项目中,都默认包含 Core 模块和 GUI 模块,如果项目中用不到它们,可以使用QT -=
删除。例如,删除项目中包含的 GUI 模块,只需在 pro 文件中添加一条配置信息:
QT -= gui
除了 Core 和 GUI 模块外,Qt 还有 SQL(包含操作数据库相关的类)、Widgets(包含构建界面的所有控件类)、Multimedia(包含提供音频、视频等功能的类)等模块,Qt 项目不会自动包含这些模块。例如,项目中用到 SQL 模块中的一些类时,需要在 pro 文件中添加如下配置信息:
QT += sql
那么,当程序中用到某个类时,如何知道它属于哪个模块呢?很简单,先将该类所在的头文件中引入到程序中,然后鼠标选中头文件并按Fn+F1
组合键,打开该头文件的使用手册后就可以看到它所属的模块。以程序中使用 QWidget 窗口类为例,先在程序中添加如下语句:
#include <QWidget>
紧接着,鼠标选中“QWidget”并按Fn+F1
组合键,打开下图所示的 QWdiget 类使用手册,可以看到该类所属的模块为 widgets。
图 2 查看类所属的模块
Qt自定义信号和槽函数
实际开发中,如果仅使用 Qt 提供的信号函数和槽函数,会经常遇到信号函数的参数类型和个数无法满足实际需求、信号函数和槽函数的参数类型不匹配等问题。解决此类问题,最简单有效的方式就是:自定义场景需要的信号函数和槽函数。
自定义信号函数
信号函数指的是符合以下条件的函数:
- 定义在某个类中,该类直接或间接继承自 QObject 类;
- 用 signals 关键字修饰;
- 函数只需要声明,不需要定义(实现);
- 函数的返回值类型为 void,参数的类型和个数不限。
举个简单的例子:
class MyWidget:public QWidget{
//Q\_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
//修饰信号函数的关键字
signals:
//自定义的信号函数
void MySignal(QString message);
};
我们自定义了一个继承自 QWidget 的 MyWidget 类,QWidget 是 QObject 的子类,所以 MyWidget 间接继承自 QObject 类。MyWidget 类中自定义了名为 MySignal 的信号函数(可以简称 MySignal 信号),它用 signals 关键字修饰,没有返回值,也没有定义(实现),仅有 1 个参数。
对于 MySignal() 信号函数,程序中不会直接调用它,而是借助 connect() 连接某个槽函数,实现的语法格式是:
MyWidget myWidget;
QObject::connect(&myWidget,&MyWidget::MySignal,信号接收者,槽函数);
一旦确定了信号接收者和槽函数,当 MySignal 信号发出后,与之相连的槽函数就会执行。那么,程序中如何发出 MySignal 信号呢?
对于 Qt 提供给我们的信号函数,其底层已经设置好了信号发出的时机,例如按下鼠标时、点击 Enter 回车键时等等。对于自定义的信号,我们需要自己指定信号发出的时机,这就需要用到 emit 关键字。emit 中文意思为“发出、射出”,是 Qt 在 C++ 基础上扩展的一个关键字,专门用来发射信号。
以定义好的 MySignal 信号为例,修改 MyWidget 类为:
class MyWidget:public QWidget{
//Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
//自定义信号函数
signals:
void MySignal(QString mess);
public:
void emitSignal(){
emit MySignal(message);
}
private:
QString message;
};
我们为 MyWidget 类新增了一个 emitSignal() 方法和一个 message 属性,emitSignal() 方法中的emit MySignal(message);
语句就表示发射 MySignal 信号。当程序中执行 emitSingal() 函数时,就会发出 MySignal 信号,message 属性的值也会随信号一同发出,对应的槽函数可以接收到 message 的值。
对于每一个自定义的信号函数,程序中都应该提供发射该信号的方法(函数),而且这样的方法(函数)可以有多个。
自定义槽函数
Qt5 中,槽函数既可以是普通的全局函数、也可以是类的成员函数、静态成员函数、友元函数、虚函数,还可以用 lambda 表达式表示。
和信号函数不同,槽函数必须手动定义(实现)。槽函数可以在程序中直接调用,但主要用来响应某个信号。自定义一个槽函数时,需要注意以下几点:
- 槽函数的返回值必须和信号函数相同,由于信号函数的返回值一定是 void,所以槽函数的返回值也必须为 void;
- 对于带参的信号函数,槽函数可以选择接收所有参数,则参数的类型、顺序、个数都必须与信号函数相同;也可以选择接收前几个参数,这些参数的类型、顺序都必须与信号函数相同;还可以选择不接受任何参数。
- 槽函数的参数个数只能比信号函数少,不能比信号函数多;
- 槽函数的参数不能有默认值。
举个例子,自定义响应 MySignal 信号的槽函数:
class MyWidget:public QWidget{
//Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
signals:
void MySignal(QString mess1,QString mess2);
public:
void emitSignal(){
emit MySignal(message1,message2);
}
//类的成员函数
void recSlot1(QString mess){
qDebug() << "执行 recSlot1() 成员函数,输出" << mess;
}
//指明定义的是槽函数
public slots:
void recSlot2(QString mess1,QString mess2){
qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2;
}
public:
QString message1;
QString message2;
};
//全局函数
void recSlot3(){
qDebug() << "执行 recSlot3() 全局函数";
}
程序中,重点关注 recSlot1()、recSlot2()、recSlot3() 这 3 个函数:
- recSlot1() 是 MyWidget 类内部的 public 成员函数,可以当做槽函数使用;
- recSlot2() 位于 MyWidget 类的内部,修饰它的关键字是
public slots
。slots 和 emit 一样,是 Qt 扩展的一个关键字,专门用来修饰槽函数。也就是说,recSlot2() 是 MyWidget 类中的槽函数。 - recSlot3() 是全局函数,可以当做槽函数使用。
slots 关键字可以和 public、protected、private 搭配使用,它们的区别是:
- public slots:该关键字修饰的槽函数,既可以在当前类及其子类的成员函数中调用,也可以在类外部的其它函数(比如 main() 函数)中调用;
- protected slots:该关键字修饰的槽函数,仅允许在当前类及其子类的成员函数内调用,不能在类外部的其它函数内调用;
- private slots:该关键字修饰的槽函数,只允许在当前类的成员函数内调用,不能在子类中调用,也不能在类外部的其它函数内调用。
通常情况下,槽函数使用 public slots 修饰。
很多读者会问,既然 public 修饰的成员函数可以当做槽函数,为什么还要提供 slots 关键字呢?笔者认为,“兼容旧的 Qt 版本”是其中的一个原因。Qt4 中的槽函数只能是 slots 修饰的类成员函数,Qt5 中取消了这一限制,但考虑到要兼容旧的 Qt 版本,Qt5 保留了旧版本中 connect() 函数的语法格式,也保留了 slots 关键字。
调用 connect() 函数,将 MySignal() 信号函数分别连接 recSlot1()、recSlot2()、recSlot3() 三个槽函数,实现代码为:
//类的成员函数作为槽函数QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot1);//信号函数和槽函数相连接QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot2);//全局函数作为槽函数QObject::connect(&mywidget,&MyWidget::MySignal,&recSlot3);
自定义信号和槽的完整实例
//main.cpp
#include <QApplication>
#include <QWidget>
#include <QDebug>
class MyWidget:public QWidget{
//Q\_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
//信号函数
signals:
void MySignal(QString mess1,QString mess2);
public:
//发射信号的函数
void emitSignal(){
emit MySignal(message1,message2);
}
//普通类成员函数
void recSlot1(QString mess){
qDebug() << "执行 recSlot1() 成员函数,输出" << mess;
}
//槽函数
public slots:
void recSlot2(QString mess1,QString mess2){
qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2;
}
public:
QString message1;
QString message2;
};
//全局函数
void recSlot3(){
qDebug() << "执行 recSlot3() 全局函数";
}
int main(int argc, char \*argv[])
{
QApplication a(argc, argv);
//创建主窗口
MyWidget mywidget;
mywidget.message1 = "C语言中文网";
mywidget.message2 = "http://c.biancheng.net";
//类的成员函数作为槽函数
QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot1);
//信号函数和槽函数相连接
QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot2);
//全局函数作为槽函数
QObject::connect(&mywidget,&MyWidget::MySignal,&recSlot3);
mywidget.show();
//发射 Signal 信号
mywidget.emitSignal();
return a.exec();
}
//MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。
#include "main.moc"
执行程序,会弹出一个 myWidget 空白窗口,同时输出以下信息:
执行 recSlot1() 成员函数,输出 “C语言中文网”
执行 recSlot2() 槽函数,输出 “C语言中文网” “http://c.biancheng.net”
执行 recSlot3() 全局函数
Qt QFile文件操作详解
很多应用程序都需要具备操作文件的能力,包括对文件内容进行读/写、创建和删除文件等,甚至某些应用程序的诞生纯粹是为了操作文件,比如 WPS Office、PDFedit 等。为此,Qt 框架提供了 QFile 类专门用来操作文件。
QFile文件操作
QFile 类支持对文件进行读取、写入、删除、重命名、拷贝等操作,它既可以操作文件文件,也可以操作二进制文件。
使用 QFile 类操作文件之前,程序中需引入<QFile>
头文件。创建 QFile 类的对象,常用的构造函数有:
QFile::QFile()
QFile::QFile(const QString &name)
参数 name 用来指定要操作的目标文件,包含文件的存储路径和文件名,存储路径可以使用绝对路径(比如 “D:/Demo/test.txt”)或者相对路径(比如"./Demo/test.txt"),路径中的分隔符要用 “/” 表示。
通常情况下,我们会调用第二个构造函数,直接指明要操作的文件。对于第一个构造函数创建的 QFile 对象,需要再调用 setFileName() 方法指明要操作的文件。
与 C++ 读写文件的规则一样,使用 QFile 读写文件之前必须先打开文件,调用 open() 成员方法即可,常用的语法格式为:
bool QFile::open(OpenMode mode)
mode 参数用来指定文件的打开方式,下表罗列了此参数的可选值以及各自的含义:
打开方式 | 含 义 |
---|---|
QIODevice::ReadOnly | 只能对文件进行读操作 |
QIODevice::WriteOnly | 只能对文件进行写操作,如果目标文件不存在,会自行创建一个新文件。 |
QIODevice::ReadWrite | 等价于 ReadOnly |
QIODevice::Append | 以追加模式打开文件,写入的数据会追加到文件的末尾(文件原有的内容保留)。 |
QIODevice::Truncate | 以重写模式打开,写入的数据会将原有数据全部清除。注意,此打开方式不能单独使用,通常会和 ReadOnly 或 WriteOnly 搭配。 |
QIODevice::Text | 读取文件时,会将行尾结束符(Unix 系统中是 “\n”,Windows 系统中是 “\r\n”)转换成‘\n’;将数据写入文件时,会将行尾结束符转换成本地格式,例如 Win32 平台上是‘\r\n’。 |
根据需要,可以为 mode 参数一次性指定多个值,值和值之间用|
分割。比如:
- QIODevice::ReadOnly | QIODevice::Text:表示只允许对文件进行读操作,读取文件时,会将行尾结束符转换为 ‘\n’;
- QIODevice::WriteOnly | QIODevice::Text:表示只允许对文件进行写操作,将数据写入文件时,会将行尾结束符转换为本地格式;
- QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text:表示对文件进行写操作,写入的数据会存放到文件的尾部,同时数据中的行尾结束符转换为本地格式。
注意,传递给 mode 参数的多个值之间不能相互冲突,比如 Append 和 Truncate 不能同时使用。
如果文件成功打开,open() 函数返回 true,否则返回 false。
QFile 类提供了很多功能实用的方法,可以快速完成对文件的操作,下表列举了常用的一些:
普通成员方法 | 功 能 |
---|---|
qint64 QFile::size() const | 获取当前文件的大小。对于打开的文件,该方法返回文件中可以读取的字节数。 |
bool QIODevice::getChar(char *c) | 从文件中读取一个字符,并存储到 c 中。读取成功时,方法返回 true,否则返回 false。 |
bool QIODevice::putChar(char c) | 向文件中写入字符 c,成功时返回 true,否则返回 false。 |
QByteArray QIODevice::read(qint64 maxSize) | 从文件中一次性最多读取 maxSize 个字节,然后返回读取到的字节。 |
qint64 QIODevice::read(char *data, qint64 maxSize) | 从文件中一次性对多读取 maxSize 个字节,读取到的字节存储到 data 指针指定的内存控件中。该方法返回成功读取到的字节数。 |
QByteArray QIODevice::readAll() | 读取文件中所有的数据。 |
qint64 QIODevice::readLine(char *data, qint64 maxSize) | 每次从文件中读取一行数据或者读取最多 maxSize-1 个字节,存储到 data 中。该方法返回实际读取到的字节数。 |
qint64 QIODevice::write(const char *data, qint64 maxSize) | 向 data 数据一次性最多写入 maxSize 个字节,该方法返回实际写入的字节数。 |
qint64 QIODevice::write(const char *data) | 将 data 数据写入文件,该方法返回实际写入的字节数。 |
qint64 QIODevice::write(const QByteArray &byteArray) | 将 byteArray 数组中存储的字节写入文件,返回实际写入的字节数。 |
bool QFile::copy(const QString &newName) | 将当前文件的内容拷贝到名为 newName 的文件中,如果成功,方法返回 true,否则返回 false。 copy 方法在执行复制操作之前,会关闭源文件。 |
bool QFile::rename(const QString &newName) | 对当前文件进行重命名,新名称为 newName,成功返回 true,失败返回 false。 |
bool QFile::remove() | 删除当前文件,成功返回 true,失败返回 false。 |
【实例一】演示了 QFile 类读写文本文件的过程。
#include <QFile>#include <QDebug>int main(int argc, char \*argv[]){ //创建 QFile 对象,同时指定要操作的文件 QFile file("D:/demo.txt"); //对文件进行写操作 if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } //向文件中写入两行字符串 file.write("C语言中文网\n"); file.write("http://c.biancheng.net"); //关闭文件 file.close(); //重新打开文件,对文件进行读操作 if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } //每次都去文件中的一行,然后输出读取到的字符串 char \* str = new char[100]; qint64 readNum = file.readLine(str,100); //当读取出现错误(返回 -1)或者读取到的字符数为 0 时,结束读取 while((readNum !=0) && (readNum != -1)){ qDebug() << str; readNum = file.readLine(str,100); } file.close(); return 0;}#include <QFile>
#include <QDebug>
int main(int argc, char \*argv[])
{
//创建 QFile 对象,同时指定要操作的文件
QFile file("D:/demo.txt");
//对文件进行写操作
if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){
qDebug()<<"文件打开失败";
}
//向文件中写入两行字符串
file.write("C语言中文网\n");
file.write("http://c.biancheng.net");
//关闭文件
file.close();
//重新打开文件,对文件进行读操作
if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){
qDebug()<<"文件打开失败";
}
//每次都去文件中的一行,然后输出读取到的字符串
char \* str = new char[100];
qint64 readNum = file.readLine(str,100);
//当读取出现错误(返回 -1)或者读取到的字符数为 0 时,结束读取
while((readNum !=0) && (readNum != -1)){
qDebug() << str;
readNum = file.readLine(str,100);
}
file.close();
return 0;
}
执行程序,“C语言中文网” 和 “http://c.biancheng.net” 先写入 D 盘的 demo.txt 文件,然后再从文件中将它们读取出来。
【实例二】演示 QFile 读写二进制文件的过程。
#include <QFile>
#include <QDebug>
int main(int argc, char \*argv[])
{
//指定要写入文件的数据
qint32 nums[5]={1,2,3,4,5};
//写入文件之前,要将数据以二进制方式存储到字节数组中
QByteArray byteArr;
byteArr.resize(sizeof(nums));
for(int i=0;i<5;i++){
//借助指针,将每个整数拷贝到字节数组中
memcpy(byteArr.data()+i\*sizeof(qint32),&(nums[i]),sizeof(qint32));
}
//将 byteArr 字节数组存储到文件中
QFile file("D:/demo.dat");
file.open(QIODevice::WriteOnly);
file.write(byteArr);
file.close();
//再次打开文件,读取文件中存储的二进制数据
file.open(QIODevice::ReadOnly);
QByteArray resArr = file.readAll();
//输出读取到的二进制数据
qDebug()<<"resArr: "<<resArr;
//将二进制数据转化为整数
char\* data = resArr.data();
while(\*data){
qDebug() << \*(qint32\*)data;
data += sizeof(qint32);
}
return 0;
}
执行程序,demo.dat 文件中会存储 {1,2,3,4,5} 这 5 个整数的二进制形式,同时输出以下内容:
resArr: “\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00”
1
2
3
4
5
单独使用 QFile 类读写文件的过程既繁琐又复杂,Qt 提供了两个辅助类 QTextStream 和 QDataStream,前者用来读写文件文件,后者用来读写二进制文件,QFile 可以和它们搭配使用,从整体上提高读写文件的开发效率。
QFile+QTextStream
和单独使用 QFile 类读写文本文件相比,QTextStream 类提供了很多读写文件相关的方法,还可以设定写入到文件中的数据格式,比如对齐方式、写入数字是否带前缀等等。
使用 QTextStream 类之前,程序中要先引入<QTextStream>
头文件。QTextStream 类提供了很多种构造函数,常用的是:
QTextStream(QIODevice *device)
QIODevice 是 QFile 的父类,因此在构造 QTextStream 类的对象时,需要传递一个 QFile 类的对象。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){
qDebug()<<“文件打开失败”;
}
//每次都去文件中的一行,然后输出读取到的字符串
char * str = new char[100];
qint64 readNum = file.readLine(str,100);
//当读取出现错误(返回 -1)或者读取到的字符数为 0 时,结束读取
while((readNum !=0) && (readNum != -1)){
qDebug() << str;
readNum = file.readLine(str,100);
}
file.close();
return 0;
}
执行程序,“C语言中文网” 和 “http://c.biancheng.net” 先写入 D 盘的 demo.txt 文件,然后再从文件中将它们读取出来。
【实例二】演示 QFile 读写二进制文件的过程。
#include
#include
int main(int argc, char *argv[])
{
//指定要写入文件的数据
qint32 nums[5]={1,2,3,4,5};
//写入文件之前,要将数据以二进制方式存储到字节数组中
QByteArray byteArr;
byteArr.resize(sizeof(nums));
for(int i=0;i<5;i++){
//借助指针,将每个整数拷贝到字节数组中
memcpy(byteArr.data()+i*sizeof(qint32),&(nums[i]),sizeof(qint32));
}
//将 byteArr 字节数组存储到文件中
QFile file(“D:/demo.dat”);
file.open(QIODevice::WriteOnly);
file.write(byteArr);
file.close();
//再次打开文件,读取文件中存储的二进制数据
file.open(QIODevice::ReadOnly);
QByteArray resArr = file.readAll();
//输出读取到的二进制数据
qDebug()<<"resArr: "<<resArr;
//将二进制数据转化为整数
char* data = resArr.data();
while(*data){
qDebug() << *(qint32*)data;
data += sizeof(qint32);
}
return 0;
}
执行程序,demo.dat 文件中会存储 {1,2,3,4,5} 这 5 个整数的二进制形式,同时输出以下内容:
resArr: “\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00”
1
2
3
4
5
单独使用 QFile 类读写文件的过程既繁琐又复杂,Qt 提供了两个辅助类 QTextStream 和 QDataStream,前者用来读写文件文件,后者用来读写二进制文件,QFile 可以和它们搭配使用,从整体上提高读写文件的开发效率。
### QFile+QTextStream
和单独使用 QFile 类读写文本文件相比,QTextStream 类提供了很多读写文件相关的方法,还可以设定写入到文件中的数据格式,比如对齐方式、写入数字是否带前缀等等。
使用 QTextStream 类之前,程序中要先引入`<QTextStream>`头文件。QTextStream 类提供了很多种构造函数,常用的是:
QTextStream(QIODevice *device)
QIODevice 是 QFile 的父类,因此在构造 QTextStream 类的对象时,需要传递一个 QFile 类的对象。
[外链图片转存中...(img-es2BtmU4-1715507676192)]
[外链图片转存中...(img-NpFInBvG-1715507676193)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618658159)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**