课程实训QT笔记

简介: 链接上一篇没有完的QT笔记

一.QT基础知识

1.控件与事件

1.控件

Qt 控件又称组件或者部件,指用户看到的所有可视化界面以及界面中的各个元素,比如按钮、文本框、输入框等。

为了方便程序员开发,Qt 提供了很多现成的控件。打开某个带 ui 文件的 Qt Widgets Application 项目,ui 文件的 Widget Box 一栏展示了 Qt 提供的几乎所有控件:

在这里插入图片描述

Qt 中的每个控件都由特定的类表示,每个控件类都包含一些常用的属性和方法,所有的控件类都直接或者间接继承自 QWidget 类。实际开发中,我们可以使用 Qt 提供的这些控件,也可以通过继承某个控件类的方式自定义一个新的控件。

Qt 中所有可视化的元素都称为控件,我们习惯将带有标题栏、关闭按钮的控件称为窗口。例如,下图展示了两种常用的窗口,实现它们的类分别是 QMainWindow 和 QDialog。

在这里插入图片描述

  • QMainWindow 类生成的窗口自带菜单栏、工具栏和状态栏,中央区域还可以添加多个控件,常用来作为应用程序的主窗口;
  • QDialog 类生成的窗口非常简单,没有菜单栏、工具栏和状态栏,但可以添加多个控件,常用来制作对话框。

刚开始在这里我还很疑惑,QMainWindow 类生成的窗口自带菜单栏、工具栏和状态栏在哪里?难道就是上面那一行吗,后来我查了资料,QMainWindow 类生成的窗口的菜单栏、工具栏和状态栏默认是不可见的,需要通过代码进行设置才能显示出来。一般情况下,我们在 QMainWindow 的构造函数中对这些控件进行初始化。

例如,在 QMainWindow 的构造函数中可以使用以下代码创建一个菜单栏并将其添加到 QMainWindow 中:

QMenuBar *menuBar = new QMenuBar(this);
setMenuBar(menuBar);

类似地,可以使用以下代码创建一个工具栏并将其添加到 QMainWindow 中:

QToolBar *toolBar = addToolBar("File");

最后,可以使用以下代码创建一个状态栏并将其添加到 QMainWindow 中:

QStatusBar *statusBar = new QStatusBar(this);
setStatusBar(statusBar);

窗口很少单独使用,它的内部往往会包含很多控件。例如图 中,我们分别往 MainWindow 和 Dialog 窗口中放置了一个按钮控件,根据需要还可以放置更多的控件。当窗口弹出时,窗口包含的所有控件会一同出现;当窗口关闭时,窗口上的所有控件也会随之消失。

实际开发中,制作应用程序的主窗口可以用 QMainWindow 或者 QWdiget;制作一个提示信息的对话框就用 QDialog 或 QWidget;如果暂时无法决定,后续可能作为窗口,也可能作为控件,就选择 QWidget。

2.事件

简单地理解,Qt 事件指的是控件和用户之间的交互过程,例如用户按下某个按钮,点击某个输入框等等。实际上除了用户会与应用程序进行交互外,操作系统也会与应用程序进行交互,例如当某个定时任务触发时,操作系统会关闭应用程序,这也是一个事件。

在上一篇博客中,在main.cpp中我没有解释的两行,其实是Qt 界面程序的 main() 主函数中首先要创建一个 QApplication 类的对象,函数执行结束前还要调用 QApplication 对象的 exec() 函数。一个 Qt 界面程序要想接收事件,main() 函数中就必须调用 exec() 函数,它的功能就是使程序能够持续不断地接收各种事件。

Qt 程序可以接收的事件种类有很多,例如鼠标点击事件、鼠标滚轮事件、键盘输入事件、定时事件等。每接收一个事件,Qt 会分派给相应的事件处理函数来处理。所谓事件处理函数,本质就是一个普通的类成员函数,以用户按下某个 QPushButton 按钮为例,Qt 会分派给 QPushButton 类中的 mousePressEvent() 函数处理。

还不能理解,看完下面的信号与槽,会好一些。

2.信号与槽

1.基础认识

信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。

举个简单的例子,按钮和窗口本是两个独立的控件,点击按钮并不会对窗口造成任何影响。通过信号和槽机制,我们可以将按钮和窗口关联起来,实现“点击按钮会使窗口关闭”的效果。

在 Qt 中,用户和控件的每次交互过程称为一个事件,比如“用户点击按钮”是一个事件,“用户关闭窗口”也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出“按钮被点击”的信号,用户关闭窗口会发出“窗口被关闭”的信号。

Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到“按钮被点击”的信号后,会做出“关闭自己”的响应动作。在 Qt 中,对信号做出的响应动作就称为槽。

信号与槽这种机制,一般有四个参数考虑问题,分别是信号的发起方、发出什么信号(信号函数)、信号的执行方(接收信号并作出处理的目标)、执行的槽函数(做出怎么样的处理)。

信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。例如,“按钮被按下”这个信号可以用 clicked() 函数表示,“窗口关闭”这个槽可以用 close() 函数表示,信号和槽机制实现“点击按钮会关闭窗口”的功能,其实就是 clicked() 函数调用 close() 函数的效果。

信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:

  • 信号函数用 signals 关键字修饰,槽函数用 public slots、protected slots 或者 private slots 修饰。signals 和 slots 是 Qt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数;
  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。

为了提高程序员的开发效率,Qt 的各个控件类都提供了一些常用的信号函数和槽函数。例如 QPushButton 类提供了 4 个信号函数和 5 个 public slots 属性的槽函数,可以满足大部分场景的需要。

实际开发中,可以使用 Qt 提供的信号函数和槽函数,也可以根据需要自定义信号函数和槽函数。

2.connect()函数实现信号和槽

connect() 是 QObject 类中的一个静态成员函数,专门用来关联指定的信号函数和槽函数。

关联某个信号函数和槽函数,需要搞清楚以下 4 个问题:

  • 信号发送者是谁?
  • 哪个是信号函数?
  • 信号的接收者是谁?
  • 哪个是接收信号的槽函数?

在 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时我们一般只输入四个参数。

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 参数还可以指定一个信号函数,也就是说,信号之间也可以相互关联,这样当信号发出时,会随之发出另一个信号。

二.QT常用控件

1.QLabel文本框

从字面上理解,QLabel 可以解释为“Qt 的 Label”,即 Qt 提供给我们的一种文本控件,它的基础功能是显示一串文本。除了显示一串文本外,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的默认值为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 类提供的两个信号函数:

信号函数 功 能
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.QPushButton按钮

QPushButton 类间接继承自 QWidget 类,它的继承关系如下:QPushButton -> QAbstractButton -> QWidget。QAbstractButton 类是所有按钮控件类的基类,包含很多通用的按钮功能。

QPushButton 按钮上除了可以放置一串文本,文本左侧还可以放置图标,必要时还可以在按钮上放置图片。

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 参数用于设置按钮上要显示的图标。

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) 方法修改。

UI 程序中,按钮的主要任务是完成和用户之间的交互,下表罗列了 QPushButton 类常用的信号函数和槽函数:

信号函数 功 能
clicked() clicked(bool checked = false) 用户点击按钮并释放(或者按下按钮对应的快捷键)后,触发此信号。
pressed() 用户按下按钮时会触发此信号。
released() 用户松开按钮时会触发此信号。
槽函数 功 能
click() 单击指定的按钮。
setIconSize() 重新设置按钮上图片的尺寸。
hide() 隐藏按钮控件。
setMenu(QMenu *menu) 弹出与按钮关联的菜单。

3.QLineEdit单行输入框

QLineEdit 是 Qt 提供的一个控件类,它直接继承自 QWdiget 类,专门用来创建单行输入框。

下表列出了 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 类提供了几个信号函数,分别对应用户的几种输入状态。

信号函数 功 能
textEdited(const QString &text) 当用户编辑输入框中的文本时,此信号就会触发,text 参数即为用户新编辑的文本。 注意,当程序中试图通过 setText() 方法修改输入框中的文本时,不会触发此信号函数。
textChanged(const QString &text) 只要输入框中的文本内容发生变化,就会触发此信息。
returnPressed() 用户按下回车键时,会触发此信号。
editingFinished() 用户按下回车键,或者鼠标点击输入框外的其它位置时,会触发此信号。

QLineEdit 类常用的槽函数有以下几个:

槽函数 功 能
clear() 清空文本框中的内容。
setText(const QString &) 重新指定文本框中的内容。

这里有很多控件,我们都需要掌握,我就不一一列举,我们要用什么我们就去学什么。

三.其他特殊控件

1.布局管理

实际开发中,一个界面上可能包含十几个控件,手动调整它们的位置既费时又费力。作为一款成熟的 GUI 框架,Qt 提供了很多摆放控件的辅助工具(又称布局管理器或者布局控件),它们可以完成两件事:

  • 自动调整控件的位置,包括控件之间的间距、对齐等;
  • 当用户调整窗口大小时,位于布局管理器内的控件也会随之调整大小,从而保持整个界面的美观。

总之借助布局管理器,我们无需再逐个调整控件的位置和大小,可以将更多的精力放在软件功能的实现上。

Qt 共提供了 5 种布局管理器,每种布局管理器对应一个类,分别是 QVBoxLayout(垂直布局)、QHBoxLayout(水平布局)、QGridLayout(网格布局)、QFormLayout(表单布局)和 QStackedLayout(分组布局),它们的继承关系:

在这里插入图片描述

垂直布局:

程序中使用 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) 布局管理器内部可以再放置一个布局管理器,该方法用来设置内部某个布局管理器的拉伸系数。

2.项目文件

在上一篇的博客中,我只是简单说了一下.prp项目文件,今天在这里详细解释一下每一句代码:

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

打开一个空的QT项目文件,你的.pro项目文件的内容应该是上面这样的

在这里插入图片描述

四.解析上篇(计算器)代码

1.创建按钮

第一种创建方式,不修改.h头文件:

#include "widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->resize(500,800);      //设置窗口属性
    QPushButton *BUTTON = new QPushButton(this);   //定义一个按钮,用指针
    BUTTON->setText("=");      //修改按钮的属性
    BUTTON->move(100,500);
}

Widget::~Widget()
{

}
  • 1.导入按钮的头文件
  • 2.定义一个按钮
  • 3.修改一个按钮的属性

五.初学令人懵逼的bug

1.中文乱码问题

该问题你在其他地方肯定也遇到过,原因你也了解,编码方式不支持中文,所以你应该修改编码方式:
在这里插入图片描述

相关文章
|
Android开发 开发者 Windows
下一篇
无影云桌面