当前位置: 首页 > news >正文

[Qt]基础数据类型和信号槽

文章目录

  • 1. Qt基本结构
    • 1.1 Qt本有项目
      • 1.1.1 项目文件(.pro)
      • 1.1.2 main.cpp
      • 1.1.3 mainwindow.ui
      • 1.1.4 mainwindow.h
      • 1.1.5 mainwindow.cpp
    • 1.2 Qt中的窗口类
      • 1.2.1基础窗口类
      • 1.2.2 窗口的显示
    • 1.3 内存回收
  • 2. Qt中的基础数据类型
    • 2.1 基础类型
    • 2.2 log输出
      • 2.2.1 在调试窗口中输入日志
      • 2.2.2 在终端窗口中输出日志
    • 2.3 字符串类型
      • 2.3.1 QByteArray
      • 2.3.2 QString
    • 2.4 QVariant
      • 2.4.1 标准类型
      • 2.4.2 自定义类型
    • 2.5 位置和尺寸
      • 2.5.1 QPoint
      • 2.5.2 QLine
      • 2.5.3 QSize
      • 2.5.4 QRect
    • 2.6 日期和时间
      • 2.6.1 QDate
      • 2.6.2 QTime
      • 2.6.3 QDateTime
      • 2.6.4 定时器类QTimer
        • 2.6.4.1 public/slot function
        • 2.6.4.2 signals
        • 2.6.4.3 static public function
        • 2.6.4.4 定时器使用举例
  • 3. 信号槽
    • 3.1 信号和槽概述
      • 3.1.1 信号的本质
      • 3.1.2 槽的本质
      • 3.1.3 信号和槽的关系
    • 3.2 标准信号槽使用
      • 3.2.1 标准信号/槽
      • 3.2.2 使用
    • 3.3 自定义信号槽使用
      • 3.3.1 自定义信号
      • 3.3.2 自定义槽
    • 3.4 信号槽拓展
      • 3.4.1 信号槽使用拓展
      • 3.4.2 信号槽的连接方式


1. Qt基本结构

1.1 Qt本有项目

1.1.1 项目文件(.pro)

在创建的Qt项目中自动生成了一个后缀为 .pro 的项目文件,该文件中记录着项目的一些属性信息,具体信息如下:

# 在项目文件中, 注释需要使用 井号(#)
# 项目编译的时候需要加载哪些底层模块
QT       += core gui # 如果当前Qt版本大于4, 会添加一个额外的模块: widgets
# Qt 5中对gui模块进行了拆分, 将 widgets 独立出来了
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets# 使用c++11新特性
CONFIG += c++11	#如果在项目中调用了废弃的函数, 项目编译的时候会有警告的提示    
DEFINES += QT_DEPRECATED_WARNINGS# 项目中的源文件
SOURCES += \main.cpp \mainwindow.cpp# 项目中的头文件
HEADERS += \mainwindow.h# 项目中的窗口界面文件
FORMS += \mainwindow.ui

1.1.2 main.cpp

在这个源文件中有程序的入口函数 main(),介绍下这个文件中自动生成的几行代码:

#include "mainwindow.h"		// 生成的窗口类头文件
#include <QApplication>		// 应用程序类头文件int main(int argc, char *argv[])
{// 创建应用程序对象, 在一个Qt项目中实例对象有且仅有一个// 类的作用: 检测触发的事件, 进行事件循环并处理QApplication a(argc, argv);// 创建窗口类对象MainWindow w;// 显示窗口w.show();// 应用程序对象开始事件循环, 保证应用程序不退出return a.exec();
}

1.1.3 mainwindow.ui

在Qt中每一个窗口都对应一个可编辑的可视化界面(*.ui), 这个界面对应的是一个xml格式的文件,
一般情况下不需要在xml格式下对这个文件进行编辑, 关于这个文件结构了解即可。

<!-- 双击这个文件看到的是一个窗口界面, 如果使用文本编辑器打开看到的是一个XML格式的文件-->
<!-- 看不懂这种格式没关系, 我们不需要在这种模式下操作这个文件。 -->
<!-- 这里只是给大家介绍这个文件的本质 -->
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>MainWindow</class><widget class="QMainWindow" name="MainWindow"><property name="geometry"><rect><x>0</x><y>0</y><width>800</width><height>600</height></rect></property><property name="windowTitle"><string>MainWindow</string></property><widget class="QWidget" name="centralwidget"/><widget class="QMenuBar" name="menubar"/><widget class="QStatusBar" name="statusbar"/></widget><resources/><connections/>
</ui>

1.1.4 mainwindow.h

这个文件是窗口界面对应的类的头文件。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>		// Qt标准窗口类头文件QT_BEGIN_NAMESPACE
// mainwindow.ui 文件中也有一个类叫 MainWindow, 将这个类放到命名空间 Ui 中
namespace Ui { class MainWindow; }	
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECT	// 这个宏是为了能够使用Qt中的信号槽机制public:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;		// 定义指针指向窗口的 UI 对象
};
#endif // MAINWINDOW_H

1.1.5 mainwindow.cpp

这个文件是窗口界面对应的类的源文件。

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)	// 基于mainwindow.ui创建一个实例对象
{// 将 mainwindow.ui 的实例对象和 当前类的对象进行关联// 这样同名的连个类对象就产生了关联, 合二为一了ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}

1.2 Qt中的窗口类

在通过Qt向导窗口基于窗口的应用程序的项目过程中倒数第二步选择跟随项目创建的第一个窗口的基类, 下拉菜单有三个选项, 分别为:
QMainWindowQDialogQWidget如下图:

1.2.1基础窗口类

在这里插入图片描述

  • 常用的窗口类有3个
    • 在创建Qt窗口的时候, 需要让自己的窗口类继承上述三个窗口类的其中一个
  • QWidget
    • 所有窗口类的基类
    • Qt中的控件(按钮, 输入框, 单选框…)也属于窗口, 基类都是QWidget
    • 可以内嵌到其他窗口中: 没有边框
    • 可以不内嵌单独显示: 独立的窗口, 有边框
  • QDialog
    • 对话框类, 后边的章节会具体介绍这个窗口
    • 不能内嵌到其他窗口中
  • QMainWindow
    • 有工具栏, 状态栏, 菜单栏, 后边的章节会具体介绍这个窗口
    • 不能内嵌到其他窗口中

1.2.2 窗口的显示

  • 内嵌窗口
    • 依附于某一个大的窗口, 作为了大窗口的一部分
    • 大窗口就是这个内嵌窗口的父窗口
    • 父窗口显示的时候, 内嵌的窗口也就被显示出来了
  • 不内嵌窗口
    • 这类窗口有边框, 有标题栏
    • 需要调用函数才可以显示
// QWidget是所有窗口类的基类, 调用这个提供的 show() 方法就可以显示将任何窗口显示出来
// 非模态显示
void QWidget::show();	// 显示当前窗口和它的子窗口// 对话框窗口的非模态显示: 还是调用show() 方法
// 对话框窗口的模态显示
[virtual slot] int QDialog::exec();

1.3 内存回收

在Qt中创建对象的时候会提供一个 Parent对象指针(可以查看类的构造函数),parent是干什么的?
.
QObject是以对象树的形式组织起来的。当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是parent,也就是父对象指针。
这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。当父对象析构的时候,这个列表中的所有对象也会被析构。(这里的父对象并不是继承意义上的父类

QWidget是能够在屏幕上显示的一切组件的父类。QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
Qt 引入对象树的概念,在一定程度上解决了内存问题。

  • 当一个QObject对象在堆上创建的时候,Qt会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
  • 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent
    的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete两次,这是由析构顺序决定的。

在这里插入图片描述

综上所述, 我们可以得到一个结论: Qt中有内存回收机制, 但是不是所有被new出的对象被自动回收, 满足条件才可以回收, 如果想要在Qt中实现内存的自动回收, 需要满足以下两个条件:

  • 创建的对象必须是QObject类的子类(间接子类也可以)
    • QObject类是没有父类的, Qt中有很大一部分类都是从这个类派生出去的
      • Qt中使用频率很高的窗口类和控件都是 QObject 的直接或间接的子类
      • 其他的类可以自己查阅Qt帮助文档
  • 创建出的类对象, 必须要指定其父对象是谁, 一般情况下有两种操作方式:
// 方式1: 通过构造函数
// parent: 当前窗口的父对象, 找构造函数中的 parent 参数即可
QWidget::QWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
QTimer::QTimer(QObject *parent = nullptr);// 方式2: 通过setParent()方法
// 假设这个控件没有在构造的时候指定符对象, 可以调用QWidget的api指定父窗口对象
void QWidget::setParent(QWidget *parent);
void QObject::setParent(QObject *parent);

2. Qt中的基础数据类型

主要介绍Qt中常用数据类型, 主要内容包括: 基础数据类型, Log日志输出, 字符串类型, QVariant, 位置和尺寸相关类型, 日期和时间相关类型

2.1 基础类型

因为Qt是一个C++ 框架, 因此C++中所有的语法和数据类型在Qt中都是被支持的, 但是Qt中也定义了一些属于自己的数据类型, 介绍一下这些基础的数类型。

QT基本数据类型定义在#include <QtGlobal> 中,QT基本数据类型有:

类型名称注释备注
qint8signed char有符号8位数据
qint16signed short16位数据类型
qint32signed short32位有符号数据类型
qint64long long int 或 (__int64)64位有符号数据类型,Windows中定义为__int64
qintptrqint32 或 qint64指针类型 根据系统类型不同而不同,32位系统为qint32、64位系统为qint64
qlonglonglong long int 或 (__int64)Windows中定义为__int64
qptrdiffqint32 或 qint64根据系统类型不同而不同,32位系统为qint32、64位系统为qint64
qrealdouble 或 float除非配置了-qreal float选项,否则默认为double
quint8unsigned char无符号8位数据类型
quint16unsigned short无符号16位数据类型
quint32unsigned int无符号32位数据类型
quint64unsigned long long int 或 (unsigned __int64)无符号64比特数据类型,Windows中定义为unsigned __int64
quintptrquint32 或 quint64根据系统类型不同而不同,32位系统为quint32、64位系统为quint64
qulonglongunsigned long long int 或 (unsigned __int64)Windows中定义为__int64
ucharunsigned char无符号字符类型
uintunsigned int无符号整型
ulongunsigned long无符号长整型
ushortunsigned short无符号短整型

虽然在Qt中有属于自己的整形或者浮点型, 但是在变成过程中这些一般不用, 常用的类型关键字还是 C/C++中的 int, float, double 等。


2.2 log输出

2.2.1 在调试窗口中输入日志

在Qt中进行log输出, 一般不使用c中的printf, 也不是使用C++中的cout
Qt框架提供了专门用于日志输出的类, 头文件名为QDebug

// 包含了QDebug头文件, 直接通过全局函数 qDebug() 就可以进行日志输出了
qDebug() << "Date:" << QDate::currentDate();
qDebug() << "Types:" << QString("String") << QChar('x')<<QRect(0, 10, 50, 40);
qDebug() << "Custom coordinate type:" << coordinate;// 和全局函数 qDebug() 类似的日志函数还有: qWarning(), qInfo(), qCritical()
int number = 666;
float i = 11.11;
qWarning() << "Number:" << number << "Other value:" << i;
qInfo() << "Number:" << number << "Other value:" << i;
qCritical() << "Number:" << number << "Other value:" << i;qDebug() << "我是要成为海贼王的男人!!!";
qDebug() << "我是隔壁的二柱子...";
qDebug() << "我是鸣人, 我擅长嘴遁!!!";

2.2.2 在终端窗口中输出日志

使用上面的方法只能在项目调试过程中进行日志输出, 如果不是通过IDE进行程序调试, 而是直接执行可执行程序在这种情况下是没有日志输出窗口的,因此也就看不到任何的日志输出。

默认情况下日志信息是不会打印到终端窗口的, 如果想要实现这样的效果, 必须在项目文件中添加相关的属性信息

打开项目文件(*.pro)找到配置项 config, 添加 console 控制台属性:

CONFIG += c++11 console

2.3 字符串类型

在Qt中不仅支持C, C++中的字符串类型, 而且还在框架中定义了专属的字符串类型,
必须要掌握在Qt中关于这些类型的使用和相互之间的转换。

语言类型字符串类型
Cchar*
C++std::string, char*
QtQByteArray, QString

2.3.1 QByteArray

在Qt中QByteArray可以看做是c语言中char*的升级版本。我们在使用这种类型的时候可通过这个类的构造函数申请一块动态内存,用于存储我们需要处理的字符串数据。
介绍一下这个类中常用的一些API函数

构造函数

// 构造空对象, 里边没有数据
QByteArray::QByteArray();
// 将data中的size个字符进行构造, 得到一个字节数组对象
// 如果 size==-1 函数内部自动计算字符串长度, 计算方式为: strlen(data)
QByteArray::QByteArray(const char *data, int size = -1);
// 构造一个长度为size个字节, 并且每个字节值都为ch的字节数组
QByteArray::QByteArray(int size, char ch);

数据操作

// 在尾部追加数据
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::append(const QByteArray &ba);
void QByteArray::push_back(const QByteArray &other);// 头部添加数据
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::prepend(const QByteArray &ba);
void QByteArray::push_front(const QByteArray &other);// 插入数据, 将ba插入到数组第 i 个字节的位置(从0开始)
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::insert(int i, const QByteArray &ba);// 删除数据
// 从大字符串中删除len个字符, 从第pos个字符的位置开始删除
QByteArray &QByteArray::remove(int pos, int len);
// 从字符数组的尾部删除 n 个字节
void QByteArray::chop(int n);
// 从字节数组的 pos 位置将数组截断 (前边部分留下, 后边部分被删除)
void QByteArray::truncate(int pos);
// 将对象中的数据清空, 使其为null
void QByteArray::clear();// 字符串替换
// 将字节数组中的 子字符串 before 替换为 after
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::replace(const QByteArray &before, const QByteArray &after);

子字符串查找和判断

// 判断字节数组中是否包含子字符串 ba, 包含返回true, 否则返回false
bool QByteArray::contains(const QByteArray &ba) const;
bool QByteArray::contains(const char *ba) const;
// 判断字节数组中是否包含子字符 ch, 包含返回true, 否则返回false
bool QByteArray::contains(char ch) const;// 判断字节数组是否以字符串 ba 开始, 是返回true, 不是返回false
bool QByteArray::startsWith(const QByteArray &ba) const;
bool QByteArray::startsWith(const char *ba) const;
// 判断字节数组是否以字符 ch 开始, 是返回true, 不是返回false
bool QByteArray::startsWith(char ch) const;// 判断字节数组是否以字符串 ba 结尾, 是返回true, 不是返回false
bool QByteArray::endsWith(const QByteArray &ba) const;
bool QByteArray::endsWith(const char *ba) const;
// 判断字节数组是否以字符 ch 结尾, 是返回true, 不是返回false
bool QByteArray::endsWith(char ch) const;

遍历

// 使用迭代器
iterator QByteArray::begin();
iterator QByteArray::end();// 使用数组的方式进行遍历
// i的取值范围 0 <= i < size()
char QByteArray::at(int i) const;
char QByteArray::operator[](int i) const;

查看字节数

// 返回字节数组对象中字符的个数
int QByteArray::length() const;
int QByteArray::size() const;
int QByteArray::count() const;// 返回字节数组对象中 子字符串ba 出现的次数
int QByteArray::count(const QByteArray &ba) const;
int QByteArray::count(const char *ba) const;
// 返回字节数组对象中 字符串ch 出现的次数
int QByteArray::count(char ch) const;

类型转换

// 将QByteArray类型的字符串 转换为 char* 类型
char *QByteArray::data();
const char *QByteArray::data() const;// int, short, long, float, double -> QByteArray
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::setNum(int n, int base = 10);
QByteArray &QByteArray::setNum(short n, int base = 10);
QByteArray &QByteArray::setNum(qlonglong n, int base = 10);
QByteArray &QByteArray::setNum(float n, char f = 'g', int prec = 6);
QByteArray &QByteArray::setNum(double n, char f = 'g', int prec = 6);
[static] QByteArray QByteArray::number(int n, int base = 10);
[static] QByteArray QByteArray::number(qlonglong n, int base = 10);
[static] QByteArray QByteArray::number(double n, char f = 'g', int prec = 6);// QByteArray -> int, short, long, float, double
int QByteArray::toInt(bool *ok = Q_NULLPTR, int base = 10) const;
short QByteArray::toShort(bool *ok = Q_NULLPTR, int base = 10) const;
long QByteArray::toLong(bool *ok = Q_NULLPTR, int base = 10) const;
float QByteArray::toFloat(bool *ok = Q_NULLPTR) const;
double QByteArray::toDouble(bool *ok = Q_NULLPTR) const;// std::string -> QByteArray
[static] QByteArray QByteArray::fromStdString(const std::string &str);
// QByteArray -> std::string
std::string QByteArray::toStdString() const;// 所有字符转换为大写
QByteArray QByteArray::toUpper() const;
// 所有字符转换为小写
QByteArray QByteArray::toLower() const;

2.3.2 QString

QString也是封装了字符串, 但是内部的编码为utf8, UTF-8属于Unicode字符集,它固定使用多个字节(window为2字节, linux为3字节)来表示一个字符,这样可以将世界上几乎所有语言的常用字符收录其中。
介绍一下这个类中常用的一些API函数。

构造函数

// 构造一个空字符串对象
QString::QString();
// 将 char* 字符串 转换为 QString 类型
QString::QString(const char *str);
// 将 QByteArray 转换为 QString 类型
QString::QString(const QByteArray &ba);
// 其他重载的同名构造函数可参考Qt帮助文档, 此处略

数据操作

// 尾部追加数据
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QString &QString::append(const QString &str);
QString &QString::append(const char *str);
QString &QString::append(const QByteArray &ba);
void QString::push_back(const QString &other);// 头部添加数据
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QString &QString::prepend(const QString &str);
QString &QString::prepend(const char *str);
QString &QString::prepend(const QByteArray &ba);
void QString::push_front(const QString &other);// 插入数据, 将 str 插入到字符串第 position 个字符的位置(从0开始)
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QString &QString::insert(int position, const QString &str);
QString &QString::insert(int position, const char *str);
QString &QString::insert(int position, const QByteArray &str);// 删除数据
// 从大字符串中删除len个字符, 从第pos个字符的位置开始删除
QString &QString::remove(int position, int n);// 从字符串的尾部删除 n 个字符
void QString::chop(int n);
// 从字节串的 position 位置将字符串截断 (前边部分留下, 后边部分被删除)
void QString::truncate(int position);
// 将对象中的数据清空, 使其为null
void QString::clear();// 字符串替换
// 将字节数组中的 子字符串 before 替换为 after
// 参数 cs 为是否区分大小写, 默认区分大小写
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QString &QString::replace(const QString &before, const QString &after, Qt::CaseSensitivity cs = Qt::CaseSensitive);

子字符串查找和判断

// 参数 cs 为是否区分大小写, 默认区分大小写
// 其他重载的同名函数可参考Qt帮助文档, 此处略// 判断字符串中是否包含子字符串 str, 包含返回true, 否则返回false
bool QString::contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;// 判断字符串是否以字符串 ba 开始, 是返回true, 不是返回false
bool QString::startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;// 判断字符串是否以字符串 ba 结尾, 是返回true, 不是返回false
bool QString::endsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;

遍历

// 使用迭代器
iterator QString::begin();
iterator QString::end();// 使用数组的方式进行遍历
// i的取值范围 0 <= position < size()
const QChar QString::at(int position) const
const QChar QString::operator[](int position) const;

查看字节数

// 返回字节数组对象中字符的个数 (字符个数和字节个数是不同的概念)
int QString::length() const;
int QString::size() const;
int QString::count() const;// 返回字节串对象中 子字符串 str 出现的次数
// 参数 cs 为是否区分大小写, 默认区分大小写
int QString::count(const QStringRef &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;

类型转换

// 将int, short, long, float, double 转换为 QString 类型
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QString &QString::setNum(int n, int base = 10);
QString &QString::setNum(short n, int base = 10);
QString &QString::setNum(long n, int base = 10);
QString &QString::setNum(float n, char format = 'g', int precision = 6);
QString &QString::setNum(double n, char format = 'g', int precision = 6);
[static] QString QString::number(long n, int base = 10);
[static] QString QString::number(int n, int base = 10);
[static] QString QString::number(double n, char format = 'g', int precision = 6);// 将 QString 转换为 int, short, long, float, double 类型
int QString::toInt(bool *ok = Q_NULLPTR, int base = 10) const;
short QString::toShort(bool *ok = Q_NULLPTR, int base = 10) const;
long QString::toLong(bool *ok = Q_NULLPTR, int base = 10) const
float QString::toFloat(bool *ok = Q_NULLPTR) const;
double QString::toDouble(bool *ok = Q_NULLPTR) const;// 将标准C++中的 std::string 类型 转换为 QString 类型
[static] QString QString::fromStdString(const std::string &str);
// 将 QString 转换为 标准C++中的 std::string 类型
std::string QString::toStdString() const;// QString -> QByteArray
// 转换为本地编码, 跟随操作系统
QByteArray QString::toLocal8Bit() const;
// 转换为 Latin-1 编码的字符串 不支持中文
QByteArray QString::toLatin1() const;
// 转换为 utf8 编码格式的字符串 (常用)
QByteArray QString::toUtf8() const;// 所有字符转换为大写
QString QString::toUpper() const;
// 所有字符转换为小写
QString QString::toLower() const;

字符串格式

// 其他重载的同名函数可参考Qt帮助文档, 此处略
QString QString::arg(const QString &a, int fieldWidth = 0, QChar fillChar = QLatin1Char( ' ' )) const;
QString QString::arg(int a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char( ' ' )) const;// 示例程序
int i;                // 假设该变量表示当前文件的编号
int total;            // 假设该变量表示文件的总个数
QString fileName;     // 假设该变量表示当前文件的名字
// 使用以上三个变量拼接一个动态字符串
QString status = QString("Processing file %1 of %2: %3").arg(i).arg(total).arg(fileName);

2.4 QVariant

QVariant这个类很神奇,或者说方便。很多时候,需要几种不同的数据类型需要传递,如果用结构体,又不大方便,容器保存的也只是一种数据类型,而QVariant则可以统统搞定。
QVariant 这个类型充当着最常见的数据类型的联合。QVariant可以保存很多Qt的数据类型,包括QBrush、QColor、QCursor、QDateTime、QFont、QKeySequence、QPalette、QPen、QPixmap、QPoint、QRect、QRegion、QSize和QString,并且还有C++基本类型,如int、float等。

2.4.1 标准类型

  • 将标准类型转换为QVariant类型
// 这类转换需要使用QVariant类的构造函数, 由于比较多, 大家可自行查阅Qt帮助文档, 在这里简单写几个
QVariant::QVariant(int val);
QVariant::QVariant(bool val);
QVariant::QVariant(double val);
QVariant::QVariant(const char *val);
QVariant::QVariant(const QByteArray &val);
QVariant::QVariant(const QString &val);
......// 使用设置函数也可以将支持的类型的数据设置到QVariant对象中
// 这里的 T 类型, 就是QVariant支持的类型
void QVariant::setValue(const T &value);
// 该函数行为和 setValue() 函数完全相同
[static] QVariant QVariant::fromValue(const T &value);
// 例子:
#if 1
QVariant v;
v.setValue(5);
#else
QVariant v = QVariant::fromValue(5);
#endifint i = v.toInt();          // i is now 5
QString s = v.toString();   // s is now "5"
  • 判断 QVariant中封装的实际数据类型
// 该函数的返回值是一个枚举类型, 可通过这个枚举判断出实际是什么类型的数据
Type QVariant::type() const;

返回值Type的部分枚举定义, 全部信息可查阅Qt帮助文档

在这里插入图片描述

  • 将QVariant对象转换为实际的数据类型
// 如果要实现该操作, 可以使用QVariant类提供的 toxxx() 方法, 全部转换可以参考Qt帮助文档
// 在此举列举几个常用函数:
bool QVariant::toBool() const;
QByteArray QVariant::toByteArray() const;
double QVariant::toDouble(bool *ok = Q_NULLPTR) const;
float QVariant::toFloat(bool *ok = Q_NULLPTR) const;
int QVariant::toInt(bool *ok = Q_NULLPTR) const;
QString QVariant::toString() const;
......

2.4.2 自定义类型

除标准类型, 自定义的类型也可以使用QVariant类进行封装,被QVariant存储的数据类型需有一个默认的构造函数和一个拷贝构造函数。为实现此功能,首先必须使用Q_DECLARE_METATYPE()宏。
通常会将这个宏放在类的声明所在头文件的下面,原型为
Q_DECLARE_METATYPE(Type)

使用步骤如下:

  • 第一步: 在头文件中声明
// *.h
struct MyTest
{int id;QString name;
};
// 自定义类型注册
Q_DECLARE_METATYPE(MyTest)
  • 第二步: 在源文件中定义
MyTest t;
t.name = "张三丰";
t.num = 666;
// 值的封装
QVariant vt = QVariant::fromValue(t);// 值的读取
if(vt.canConvert<MyTest>())
{MyTest t = vt.value<MyTest>();qDebug() << "name: " << t.name << ", num: " << t.num;
}

以上操作用到的QVariant类的API如下:

// 如果当前QVariant对象可用转换为对应的模板类型 T, 返回true, 否则返回false
bool QVariant::canConvert() const;
// 将当前QVariant对象转换为实际的 T 类型
T QVariant::value() const;

2.5 位置和尺寸

2.5.1 QPoint

QPoint类封装了我们常用用到的坐标点 (x, y), 常用的 API如下:

// 构造函数
// 构造一个坐标原点, 即(0, 0)
QPoint::QPoint();
// 参数为 x轴坐标, y轴坐标
QPoint::QPoint(int xpos, int ypos);// 设置x轴坐标
void QPoint::setX(int x);
// 设置y轴坐标
void QPoint::setY(int y);// 得到x轴坐标
int QPoint::x() const;
// 得到x轴坐标的引用
int &QPoint::rx();
// 得到y轴坐标
int QPoint::y() const;
// 得到y轴坐标的引用
int &QPoint::ry();// 直接通过坐标对象进行算术运算: 加减乘除
QPoint &QPoint::operator*=(float factor);
QPoint &QPoint::operator*=(double factor);
QPoint &QPoint::operator*=(int factor);
QPoint &QPoint::operator+=(const QPoint &point);
QPoint &QPoint::operator-=(const QPoint &point);
QPoint &QPoint::operator/=(qreal divisor);

2.5.2 QLine

QLine是一个直线类, 封装了两个坐标点 (两点确定一条直线)

常用API如下:

// 构造函数
// 构造一个空对象
QLine::QLine();
// 构造一条直线, 通过两个坐标点
QLine::QLine(const QPoint &p1, const QPoint &p2);
// 从点 (x1, y1) 到 (x2, y2)
QLine::QLine(int x1, int y1, int x2, int y2);// 给直线对象设置坐标点
void QLine::setPoints(const QPoint &p1, const QPoint &p2);
// 起始点(x1, y1), 终点(x2, y2)
void QLine::setLine(int x1, int y1, int x2, int y2);
// 设置直线的起点坐标
void QLine::setP1(const QPoint &p1);
// 设置直线的终点坐标
void QLine::setP2(const QPoint &p2);// 返回直线的起始点坐标
QPoint QLine::p1() const;
// 返回直线的终点坐标
QPoint QLine::p2() const;
// 返回值直线的中心点坐标, (p1() + p2()) / 2
QPoint QLine::center() const;// 返回值直线起点的 x 坐标
int QLine::x1() const;
// 返回值直线终点的 x 坐标
int QLine::x2() const;
// 返回值直线起点的 y 坐标
int QLine::y1() const;
// 返回值直线终点的 y 坐标
int QLine::y2() const;// 用给定的坐标点平移这条直线
void QLine::translate(const QPoint &offset);
void QLine::translate(int dx, int dy);
// 用给定的坐标点平移这条直线, 返回平移之后的坐标点
QLine QLine::translated(const QPoint &offset) const;
QLine QLine::translated(int dx, int dy) const;// 直线对象进行比较
bool QLine::operator!=(const QLine &line) const;
bool QLine::operator==(const QLine &line) const;

2.5.3 QSize

在QT中QSize类用来形容长度和宽度

// 构造函数
// 构造空对象, 对象中的宽和高都是无效的
QSize::QSize();
// 使用宽和高构造一个有效对象
QSize::QSize(int width, int height);// 设置宽度
void QSize::setWidth(int width)
// 设置高度
void QSize::setHeight(int height);// 得到宽度
int QSize::width() const;
// 得到宽度的引用
int &QSize::rwidth();
// 得到高度
int QSize::height() const;
// 得到高度的引用
int &QSize::rheight();// 交换高度和宽度的值
void QSize::transpose();
// 交换高度和宽度的值, 返回交换之后的尺寸信息
QSize QSize::transposed() const;// 进行算法运算: 加减乘除
QSize &QSize::operator*=(qreal factor);
QSize &QSize::operator+=(const QSize &size);
QSize &QSize::operator-=(const QSize &size);
QSize &QSize::operator/=(qreal divisor);

2.5.4 QRect

在Qt中使用 QRect类来描述一个矩形

// 构造函数
// 构造一个空对象
QRect::QRect();
// 基于左上角坐标, 和右下角坐标构造一个矩形对象
QRect::QRect(const QPoint &topLeft, const QPoint &bottomRight);
// 基于左上角坐标, 和 宽度, 高度构造一个矩形对象
QRect::QRect(const QPoint &topLeft, const QSize &size);
// 通过 左上角坐标(x, y), 和 矩形尺寸(width, height) 构造一个矩形对象
QRect::QRect(int x, int y, int width, int height);// 设置矩形的尺寸信息, 左上角坐标不变
void QRect::setSize(const QSize &size);
// 设置矩形左上角坐标为(x,y), 大小为(width, height)
void QRect::setRect(int x, int y, int width, int height);
// 设置矩形宽度
void QRect::setWidth(int width);
// 设置矩形高度
void QRect::setHeight(int height);// 返回值矩形左上角坐标
QPoint QRect::topLeft() const;
// 返回矩形右上角坐标
// 该坐标点值为: QPoint(left() + width() -1, top())
QPoint QRect::topRight() const;
// 返回矩形左下角坐标
// 该坐标点值为: QPoint(left(), top() + height() - 1)
QPoint QRect::bottomLeft() const;
// 返回矩形右下角坐标
// 该坐标点值为: QPoint(left() + width() -1, top() + height() - 1)
QPoint QRect::bottomRight() const;
// 返回矩形中心点坐标
QPoint QRect::center() const;// 返回矩形上边缘y轴坐标
int QRect::top() const;
int QRect::y() const;
// 返回值矩形下边缘y轴坐标
int QRect::bottom() const;
// 返回矩形左边缘 x轴坐标
int QRect::x() const;
int QRect::left() const;
// 返回矩形右边缘x轴坐标
int QRect::right() const;// 返回矩形的高度
int QRect::width() const;
// 返回矩形的宽度
int QRect::height() const;
// 返回矩形的尺寸信息
QSize QRect::size() const;

2.6 日期和时间

2.6.1 QDate

QDate类可以封装日期信息也可以通过这个类得到日期相关的信息, 包括:年, 月, 日

// 构造函数
QDate::QDate();
QDate::QDate(int y, int m, int d);// 公共成员函数
// 重新设置日期对象中的日期
bool QDate::setDate(int year, int month, int day);
// 给日期对象添加 ndays 天
QDate QDate::addDays(qint64 ndays) const;
// 给日期对象添加 nmonths 月
QDate QDate::addMonths(int nmonths) const;
// 给日期对象添加 nyears 月
QDate QDate::addYears(int nyears) const;// 得到日期对象中的年/月/日
int QDate::year() const;
int QDate::month() const;
int QDate::day() const;
void QDate::getDate(int *year, int *month, int *day) const;// 日期对象格式化
/*d    - The day as a number without a leading zero (1 to 31)dd   - The day as a number with a leading zero (01 to 31)ddd	 - The abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name, i.e. QLocale::system().dddd - The long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name, i.e. QLocale::system().M    - The month as a number without a leading zero (1 to 12)MM   - The month as a number with a leading zero (01 to 12)MMM	 - The abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name, i.e. QLocale::system().MMMM - The long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name, i.e. QLocale::system().yy   - The year as a two digit number (00 to 99)yyyy - The year as a four digit number. If the year is negative, a minus sign is prepended, making five characters.
*/
QString QDate::toString(const QString &format) const;// 操作符重载 ==> 日期比较
bool QDate::operator!=(const QDate &d) const;
bool QDate::operator<(const QDate &d) const;
bool QDate::operator<=(const QDate &d) const;
bool QDate::operator==(const QDate &d) const;
bool QDate::operator>(const QDate &d) const;
bool QDate::operator>=(const QDate &d) const;// 静态函数 -> 得到本地的当前日期
[static] QDate QDate::currentDate();

2.6.2 QTime

QTime类可以封装时间信息也可以通过这个类得到时间相关的信息, 包括:时, 分, 秒, 毫秒

// 构造函数
QTime::QTime();
/*h 		==> 取值范围: 0 ~ 23m and s 	==> 取值范围: 0 ~ 59ms 		==> 取值范围: 0 ~ 999
*/ 
QTime::QTime(int h, int m, int s = 0, int ms = 0);// 公共成员函数
// Returns true if the set time is valid; otherwise returns false.
bool QTime::setHMS(int h, int m, int s, int ms = 0);
QTime QTime::addSecs(int s) const;
QTime QTime::addMSecs(int ms) const;// 示例代码QTime n(14, 0, 0);                // n == 14:00:00QTime t;t = n.addSecs(70);                // t == 14:01:10t = n.addSecs(-70);               // t == 13:58:50t = n.addSecs(10 * 60 * 60 + 5);  // t == 00:00:05t = n.addSecs(-15 * 60 * 60);     // t == 23:00:00// 从时间对象中取出 时/分/秒/毫秒
// Returns the hour part (0 to 23) of the time. Returns -1 if the time is invalid.
int QTime::hour() const;
// Returns the minute part (0 to 59) of the time. Returns -1 if the time is invalid.
int QTime::minute() const;
// Returns the second part (0 to 59) of the time. Returns -1 if the time is invalid.
int QTime::second() const;
// Returns the millisecond part (0 to 999) of the time. Returns -1 if the time is invalid.
int QTime::msec() const;// 时间格式化
/*-- 时 --h	==>	The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)hh	==>	The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)H	==>	The hour without a leading zero (0 to 23, even with AM/PM display)HH	==>	The hour with a leading zero (00 to 23, even with AM/PM display)-- 分 --m	==>	The minute without a leading zero (0 to 59)mm	==>	The minute with a leading zero (00 to 59)-- 秒 --s	==>	The whole second, without any leading zero (0 to 59)ss	==>	The whole second, with a leading zero where applicable (00 to 59)-- 毫秒 --zzz	==>	The fractional part of the second, to millisecond precision, including trailing zeroes where applicable (000 to 999).-- 上午或者下午AP or A	==>	使用AM/PM(大写) 描述上下午, 中文系统显示汉字ap or a	==>	使用am/pm(小写) 描述上下午, 中文系统显示汉字
*/
QString QTime::toString(const QString &format) const;// 阶段性计时
// 过时的API函数
// 开始计时
void QTime::start();
// 计时结束
int QTime::elapsed() const;
// 重新计时
int QTime::restart();// 推荐使用的API函数
// QElapsedTimer 类
void QElapsedTimer::start();
qint64 QElapsedTimer::restart();
qint64 QElapsedTimer::elapsed() const;// 操作符重载 ==> 时间比较
bool QTime::operator!=(const QTime &t) const;
bool QTime::operator<(const QTime &t) const;
bool QTime::operator<=(const QTime &t) const;
bool QTime::operator==(const QTime &t) const;
bool QTime::operator>(const QTime &t) const;
bool QTime::operator>=(const QTime &t) const;// 静态函数 -> 得到当前时间
[static] QTime QTime::currentTime();

2.6.3 QDateTime

QDateTime类可以封装日期和时间信息也可以通过这个类得到日期和时间相关的信息, 包括:年, 月, 日, 时, 分, 秒,毫秒
其实这个类就是QDateQTime 这两个类的结合体。

// 构造函数
QDateTime::QDateTime();
QDateTime::QDateTime(const QDate &date, const QTime &time, Qt::TimeSpec spec = Qt::LocalTime);// 公共成员函数
// 设置日期
void QDateTime::setDate(const QDate &date);
// 设置时间
void QDateTime::setTime(const QTime &time);
// 给当前日期对象追加 年/月/日/秒/毫秒, 参数可以是负数
QDateTime QDateTime::addYears(int nyears) const;
QDateTime QDateTime::addMonths(int nmonths) const;
QDateTime QDateTime::addDays(qint64 ndays) const;
QDateTime QDateTime::addSecs(qint64 s) const;
QDateTime QDateTime::addMSecs(qint64 msecs) const;// 得到对象中的日期
QDate QDateTime::date() const;
// 得到对象中的时间
QTime QDateTime::time() const;// 日期和时间格式, 格式字符参考QDate 和 QTime 类的 toString() 函数
QString QDateTime::toString(const QString &format) const;// 操作符重载 ==> 日期时间对象的比较
bool QDateTime::operator!=(const QDateTime &other) const;
bool QDateTime::operator<(const QDateTime &other) const;
bool QDateTime::operator<=(const QDateTime &other) const;
bool QDateTime::operator==(const QDateTime &other) const;
bool QDateTime::operator>(const QDateTime &other) const;
bool QDateTime::operator>=(const QDateTime &other) const;// 静态函数
// 得到当前时区的日期和时间(本地设置的时区对应的日期和时间)
[static] QDateTime QDateTime::currentDateTime();

2.6.4 定时器类QTimer

在进行窗口程序的处理过程中, 经常要周期性的执行某些操作, 或者制作一些动画效果,看似比较复杂的问题使用定时器就可以完美的解决这些问题
Qt中提供了定时器方式,介绍一下Qt中的定时器类 QTimer 的使用方法。

要使用它,只需创建一个QTimer类对象,然后调用其 start() 函数开启定时器,此后QTimer对象就会周期性的发出 timeout() 信号。我们先来了解一下这个类的相关API。

2.6.4.1 public/slot function

// 构造函数
// 如果指定了父对象, 创建的堆内存可以自动析构
QTimer::QTimer(QObject *parent = nullptr);// 设置定时器时间间隔为 msec 毫秒
// 默认值是0,一旦窗口系统事件队列中的所有事件都已经被处理完,一个时间间隔为0的QTimer就会触发
void QTimer::setInterval(int msec);
// 获取定时器的时间间隔, 返回值单位: 毫秒
int QTimer::interval() const;// 根据指定的时间间隔启动或者重启定时器, 需要调用 setInterval() 设置时间间隔
[slot] void QTimer::start();
// 启动或重新启动定时器,超时间隔为msec毫秒。
[slot] void QTimer::start(int msec);
// 停止定时器。
[slot] void QTimer::stop();// 设置定时器精度
/*
参数: - Qt::PreciseTimer -> 精确的精度, 毫秒级- Qt::CoarseTimer  -> 粗糙的精度, 和1毫秒的误差在5%的范围内, 默认精度- Qt::VeryCoarseTimer -> 非常粗糙的精度, 精度在1秒左右
*/
void QTimer::setTimerType(Qt::TimerType atype);
Qt::TimerType QTimer::timerType() const;	// 获取当前定时器的精度// 如果定时器正在运行,返回true; 否则返回false。
bool QTimer::isActive() const;// 判断定时器是否只触发一次
bool QTimer::isSingleShot() const;
// 设置定时器是否只触发一次, 参数为true定时器只触发一次, 为false定时器重复触发, 默认为false
void QTimer::setSingleShot(bool singleShot);

2.6.4.2 signals

这个类的信号只有一个, 当定时器超时时,该信号就会被发射出来。
给这个信号通过conect()关联一个槽函数, 就可以在槽函数中处理超时事件了。

[signal] void QTimer::timeout();

2.6.4.3 static public function

// 其他同名重载函数可以自己查阅帮助文档
/*
功能: 在msec毫秒后发射一次信号, 并且只发射一次
参数:- msec:     在msec毫秒后发射信号- receiver: 接收信号的对象地址- method:   槽函数地址
*/
[static] void QTimer::singleShot(int msec, const QObject *receiver, PointerToMemberFunction method);

2.6.4.4 定时器使用举例

  • 周期性定时器
// 创建定时器对象
QTimer* timer = new QTimer(this);// 修改定时器对象的精度
timer->setTimerType(Qt::PreciseTimer);// 按钮 loopBtn 的点击事件
// 点击按钮启动或者关闭定时器, 定时器启动, 周期性得到当前时间
connect(ui->loopBtn, &QPushButton::clicked, this, [=]()
{// 启动定时器if(timer->isActive()){timer->stop();  // 关闭定时器ui->loopBtn->setText("开始");}else{ui->loopBtn->setText("关闭");timer->start(1000); // 1000ms == 1s}
});connect(timer, &QTimer::timeout, this, [=]()
{QTime tm = QTime::currentTime();// 格式化当前得到的系统时间QString tmstr = tm.toString("hh:mm:ss.zzz");// 设置要显示的时间ui->curTime->setText(tmstr);
});
  • 一次性定时器
// 点击按钮 onceBtn 只发射一次信号
// 点击按钮一次, 发射一个信号, 得到某一个时间点的时间
connect(ui->onceBtn, &QPushButton::clicked, this, [=]()
{// 获取2s以后的系统时间, 不创建定时器对象, 直接使用类的静态方法QTimer::singleShot(2000, this, [=](){QTime tm = QTime::currentTime();// 格式化当前得到的系统时间QString tmstr = tm.toString("hh:mm:ss.zzz");// 设置要显示的时间ui->onceTime->setText(tmstr);});
});

3. 信号槽

介绍Qt中的信号槽, 主要内容包括: 信号槽的本质, 信号槽的关系, 标准信号槽的使用, 自定义信号槽的使用, 信号槽的拓展

3.1 信号和槽概述

信号槽是 Qt框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式(发布-订阅模式)。
当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。
如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。
也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

3.1.1 信号的本质

信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时候Qt对应的窗口类会发出某个信号,以此对用户的挑选做出反应。

因此根据上述的描述我们得到一个结论 – 信号的本质就是事件,比如:

  • 按钮单击、双击

  • 窗口刷新

  • 鼠标移动、鼠标按下、鼠标释放

  • 键盘输入

那么在Qt中信号是通过什么形式呈现给使用者的呢?

  • 我们对哪个窗口进行操作, 哪个窗口就可以捕捉到这些被触发的事件。
  • 对于使用者来说触发了一个事件我们就可以得到Qt框架给我们发出的某个特定信号。
  • 信号的呈现形式就是函数, 也就是说某个事件产生了, Qt框架就会调用某个对应的信号函数, 通知使用者。

在QT中信号的发出者是某个实例化的类对象,对象内部可以进行相关事件的检测。


3.1.2 槽的本质

在Qt中槽函数是一类特殊的功能的函数,在编码过程中也可以作为类的普通成员函数来使用。
之所以称之为槽函数是因为它们还有一个职责就是对Qt框架中产生的信号进行处理。

在Qt中槽函数的所有者也是某个类的实例对象。


3.1.3 信号和槽的关系

在Qt中信号和槽函数都是独立的个体,本身没有任何联系,但是由于某种特性需求我们可以将二者连接到一起。在Qt中我们需要使用QOjbect类中的connect函数进二者的关联。

连接信号和槽的connect()函数原型如下, 其中PointerToMemberFunction是一个指向函数地址的指针

QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection);
参数:- sender:   发出信号的对象- signal:   属于sender对象, 信号是一个函数, 这个参数的类型是函数指针, 信号函数地址- receiver: 信号接收者- method:   属于receiver对象, 当检测到sender发出了signal信号, receiver对象调用method方法,信号发出之后的处理动作//  参数 signal 和 method 都是函数地址, 因此简化之后的 connect() 如下:
connect(const QObject *sender, &QObject::signal, const QObject *receiver, &QObject::method);

使用connect()进行信号槽连接的注意事项:

  • connect函数相对于做了信号处理动作的注册
  • 调用conenct函数的sender对象的信号并没有产生, 因此receiver对象的method也不会被调用
  • method槽函数本质是一个回调函数, 调用的时机是信号产生之后, 调用是Qt框架来执行的
  • connect中的sender和recever两个指针必须被实例化了, 否则conenct不会成功

3.2 标准信号槽使用

3.2.1 标准信号/槽

在Qt提供的很多标准类中都可以对用户触发的某些特定事件进行检测, 因此当用户做了这些操作之后, 事件被触发类的内部就会产生对应的信号,这些信号都是Qt类内部自带的, 因此称之为标准信号。
同样的,在Qt的很多类内部为我了提供了很多功能函数,并且这些函数也可以作为触发的信号的处理动作,有这类特性的函数在Qt中称之为标准槽函数。
系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档了,比如在帮助文档中查询按钮的点击信号,那么需要在帮助文档中输入QPushButton
首先我们可以在Contents中寻找关键字signals,信号的意思,但是我们发现并没有找到,这时候我们应该看当前类从父类继承下来了哪些信号

在这里插入图片描述

因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个

在这里插入图片描述


3.2.2 使用

掌握标准信号、槽的查找方式之后以及connect()函数的作用之后, 一个简单的例子了解使用方式。

功能实现: 点击窗口上的按钮, 关闭窗口
功能分析:- 按钮: 信号发出者          -> QPushButton 类型- 窗口: 信号的接收者和处理者  -> QWidget 类型

需要使用的标准信号槽函数

// 单击按钮发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close();

对于上边的需求只需要一句代码, 只需要写一句代码就能实现了

// 单击按钮关闭窗口
connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);

connect()操作一般写在窗口的构造函数中, 相当于在事件产生之前在qt框架中先进行注册, 这样在程序运行过程中假设产生了按钮的点击事件, 框架就会调用信号接收者对象对应的槽函数了, 如果信号不产生, 槽函数也就一直不会被调用。


3.3 自定义信号槽使用

Qt框架提供的信号槽在某些特定场景下是无法满足我们的项目需求的,因此我们还设计自己需要的的信号和槽,同样还是使用connect()对自定义的信号槽进行连接。

如果想要在QT类中自定义信号槽, 需要满足一些条件, 并且有些事项也需要注意:

  • 要编写新的类并且让其继承Qt的某些标准类
  • 这个新的子类必须从QObject类或者是QObject子类进行派生
  • 在定义类的头文件中加入 Q_OBJECT 宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{Q_OBJECT......
}

3.3.1 自定义信号

在Qt中信号的本质是事件, 但是在框架中也是以函数的形式存在的, 只不过信号对应的函数只有声明,没有定义。如果Qt中的标准信号不能满足我们的需求,可以在程序中进行信号的自定义,当自定义信号对应的事件产生之后,认为的将这个信号发射出去即可(其实就是调用一下这个信号函数)。

下边给大家阐述一下, 自定义信号的要求和注意事项:

  1. 信号是类的成员函数
  2. 返回值必须是 void 类型
  3. 信号的名字可以根据实际情况进行指定
  4. 参数可以随意指定, 信号也支持重载
  5. 信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字
  6. 信号函数只需要声明, 不需要定义(没有函数体实现)
  7. 在程序中发射自定义信号: 发送信号的本质就是调用信号函数
    • 习惯性在信号函数前加关键字: emit, 但是可以省略不写
    • emit只是显示的声明一下信号要被发射了, 没有特殊含义
    • 底层 emit == #define emit
// 举例: 信号重载
// Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以)
class Test : public QObject
{Q_OBJECT
signals:void testsignal();// 参数的作用是数据传递, 谁调用信号函数谁就指定实参// 实参最终会被传递给槽函数void testsignal(int a);
};

3.3.2 自定义槽

槽函数就是信号的处理动作,在Qt中槽函数可以作为普通的成员函数来使用。如果标准槽函数提供的功能满足不了需求,可以自己定义槽函数进行某些特殊功能的实现。自定义槽函数和自定义的普通函数写法是一样的。

下边给大家阐述一下, 自定义槽的要求和注意事项:

  1. 返回值必须是 void 类型

  2. 槽也是函数, 因此也支持重载

  3. 槽函数需要指定多少个参数, 需要看连接的信号的参数个数

  4. 槽函数的参数是用来接收信号传递的数据的, 信号传递的数据就是信号的参数

    • 举例:
      • 信号函数: void testsig(int a, double b);
      • 槽函数: void testslot(int a, double b);
    • 总结:
      • 槽函数的参数应该和对应的信号的参数个数, 从左到右类型依次对应
      • 信号的参数可以大于等于槽函数的参数个数 == 信号传递的数据被忽略了
        • 信号函数: void testsig(int a, double b);
        • 槽函数: void testslot(int a);
  5. Qt中槽函数的类型是多样的
    Qt中的槽函数可以是类的成员函数、全局函数、静态函数、Lambda表达式(匿名函数)

  6. 槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)

  • public slots:
  • private slots: –> 这样的槽函数不能在类外部被调用
  • protected slots: –> 这样的槽函数不能在类外部被调用
// 槽函数书写格式举例
// 类中的这三个函数都可以作为槽函数来使用
class Test : public QObject
{public:void testSlot();static void testFunc();public slots:void testSlot(int id);
};

根据特定场景自定义信号槽:

// class GirlFriend
class GirlFriend : public QObject
{Q_OBJECT
public:explicit GirlFriend(QObject *parent = nullptr);signals:void hungry();	            // 不能表达出想要吃什么void hungry(QString msg);	// 可以通过参数表达想要吃什么
};// class Me
class Me : public QObject
{Q_OBJECT
public:explicit Me(QObject *parent = nullptr);public slots:// 槽函数void eatMeal();             // 不能知道信号发出者要吃什么void eatMeal(QString msg);  // 可以知道信号发出者要吃什么
};

3.4 信号槽拓展

3.4.1 信号槽使用拓展

  • 一个信号可以连接多个槽函数, 发送一个信号有多个处理动作
    • 需要写多个connect()连接
    • 槽函数的执行顺序是随机的, 和connect函数的调用顺序没有关系
    • 信号的接收者可以是一个对象, 也可以是多个对象
  • 一个槽函数可以连接多个信号, 多个不同的信号, 处理动作是相同的
    • 需要写多个connect()连接
  • 信号可以连接信号

信号接收者可不处理接收的信号, 而是继续发射新的信号,这相当于传递了数据, 并没有对数据进行处理

connect(const QObject *sender, &QObject::signal, const QObject *receiver, &QObject::siganl-new);
  • 信号槽是可以断开的
disconnect(const QObject *sender, &QObject::signal, const QObject *receiver, &QObject::method);

3.4.2 信号槽的连接方式

  • Qt5的连接方式
// 语法:
QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection);// 信号和槽函数也就是第2,4个参数传递的是地址, 编译器在编译过程中会对数据的正确性进行检测
connect(const QObject *sender, &QObject::signal, const QObject *receiver, &QObject::method);
  • Qt4的连接方式

这种旧的信号槽连接方式在Qt5中是支持的, 但是不推荐使用, 因为这种方式在进行信号槽连接的时候,信号槽函数通过宏SIGNALSLOT转换为字符串类型。
因为信号槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测,如果使用者传错了数据,编译器也不会报错,但实际上信号槽的连接已经不对了,只有在程序运行起来之后才能发现问题,而且问题不容易被定位。

// Qt4的信号槽连接方式
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);connect(const QObject *sender,SIGNAL(信号函数名(参数1, 参数2, ...)),const QObject *receiver,SLOT(槽函数名(参数1, 参数2, ...)));

Qt4中声明槽函数必须要使用 slots 关键字, 不能省略。

  • 应用举例

场景描述:

  • 我肚子饿了, 我要吃东西。

分析:

  • 信号的发出者是我自己, 信号的接收者也是我自己

先定义出一个Qt的类。

class Me : public QObject
{Q_OBJECT// Qt4中的槽函数必须这样声明, qt5中的关键字 slots 可以被省略
public slots:void eat();void eat(QString somthing);signals:void hungury();void hungury(QString somthing);
};// 基于上边的类写出解决方案
// 处理如下逻辑: 我饿了, 我要吃东西
// 分析: 信号的发出者是我自己, 信号的接收者也是我自己
Me m;
// Qt4处理方式
connect(&m, SIGNAL(eat()), &m, SLOT(hungury()));
connect(&m, SIGNAL(eat(QString)), &m, SLOT(hungury(QString)));// Qt5处理方式
connect(&m, &Me::eat, &m, &Me::hungury);	// error

Qt5处理方式错误原因分析:

上边的写法之所以错误是因为这个类中信号槽都是重载过的, 信号和槽都是通过函数名去关联函数的地址, 但是这个同名函数对应两块不同的地址, 一个带参, 一个不带参, 因此编译器就不知道去关联哪块地址了, 所以如果我们在这种时候通过以上方式进行信号槽连接, 编译器就会报错。

解决方案:

  • 可以通过定义函数指针的方式指定出函数的具体参数,这样就可以确定函数的具体地址了。
  • 定义函数指针指向重载的某个信号或者槽函数,在connect()函数中将函数指针名字作为实参就可以了。
// 举例:
void (Me::*func1)(QString) = &Me::eat;	// func1指向带参的信号
void (Me::*func2)() = &Me::hungury;	// func2指向不带参的槽函数

Qt正确的处理方式:

// 定义函数指针指向重载的某一个具体的信号地址
void (Me::*mysignal)(QString) = &Me::eat;
// 定义函数指针指向重载的某一个具体的槽函数地址
void (Me::*myslot)(QString) = &Me::hungury;
// 使用定义的函数指针完成信号槽的连接
connect(&m, mysignal, &m, myslot);

总结

  • Qt4的信号槽连接方式因为使用了宏函数, 宏函数对用户传递的信号槽不会做错误检测, 容易出bug
  • Qt5的信号槽连接方式, 传递的是信号槽函数的地址, 编译器会做错误检测, 减少了bug的产生
  • 当信号槽函数被重载之后, Qt4的信号槽连接方式不受影响
  • 当信号槽函数被重载之后, Qt5中需要给被重载的信号或者槽定义函数指针

相关文章:

[Qt]基础数据类型和信号槽

文章目录 1. Qt基本结构1.1 Qt本有项目1.1.1 项目文件&#xff08;.pro&#xff09;1.1.2 main.cpp1.1.3 mainwindow.ui1.1.4 mainwindow.h1.1.5 mainwindow.cpp 1.2 Qt中的窗口类1.2.1基础窗口类1.2.2 窗口的显示 1.3 内存回收 2. Qt中的基础数据类型2.1 基础类型2.2 log输出2…...

UIStackView入门使用两个问题

项目中横向一排元素&#xff0c;竖向一排元素&#xff0c;可以使用UIStackView。UIStackView的原理不做介绍&#xff0c;这里主要讲两个初次使用容易出现的两个问题。 首先创建一个stackview -(UIStackView*)titleStackView{if(_titleStackView nil){_titleStackView [UISta…...

【Sentinel】Sentinel与gateway的限流算法

文章目录 1、Sentinel与Hystrix的区别2、限流算法3、限流算法对比4、Sentinel限流与Gateway限流 1、Sentinel与Hystrix的区别 线程隔离有两种方式实现&#xff1a; 线程池隔离&#xff08;Hystrix默认采用&#xff09;信号量隔离&#xff08;Sentinel默认采用&#xff09; 服…...

python实现对excel表中的某列数据进行排序

如下需要对webCms中的B列数据进行升序排序&#xff0c;且不能影响到其他列、工作表中的数据和格式。 import pandas as pd import openpyxl from openpyxl.utils.dataframe import dataframe_to_rows# 读取 Excel 文件 file_path 1.xlsx sheet_name webCms# 读取 Excel 文件并…...

CMS指纹识别

一.什么是指纹识别 常见cms系统 通过关键特征&#xff0c;识别出目标的CMS系统&#xff0c;服务器&#xff0c;开发语言&#xff0c;操作系统&#xff0c;CDN&#xff0c;WAF的类别版本等等 1.识别对象 1.CMS信息&#xff1a;比如Discuz,织梦&#xff0c;帝国CMS&#xff0…...

STL- 常用算法

概述: 算法主要是由头文件<algorithm> <functional> <numeric>组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 <numeric>体积很小&#xff0c;只包括几个在序列上面进行简…...

苹果铃声怎么设置?3招教你设置个性化铃声!

苹果手机因其颜值、性能与生态吸引了一大批粉丝用户。在拿到新手机后&#xff0c;大家第一时间就是给手机设置好听的铃声。那么&#xff0c;苹果铃声怎么设置呢&#xff1f;手机铃声能设置成自己喜欢的歌曲吗&#xff1f;当然可以了&#xff01;本文将给大家介绍3种轻松设置苹果…...

LRTimelapse 6 for Mac(延时摄影视频制作软件)

LRTimelapse 是一款适用于macOS 系统的延时摄影视频制作软件&#xff0c;可以帮助用户创建高质量的延时摄影视频。该软件提供了直观的界面和丰富的功能&#xff0c;支持多种时间轴摄影工具和文件格式&#xff0c;并具有高度的可定制性和扩展性。 LRTimelapse 的主要特点如下&am…...

数据结构和算法(4):栈与队列

栈 ADT 及实现 栈&#xff08;stack&#xff09;是存放数据对象的一种特殊容器&#xff0c;其中的数据元素按线性的逻辑次序排列&#xff0c;故也可定义首、末元素。 尽管栈结构也支持对象的插入和删除操作&#xff0c;但其操作的范围仅限于栈的某一特定端。 也就是说&#xf…...

pdf怎么转换成dwg格式?简单转换方法分享

当我们需要在CAD中编辑PDF文件中的向量图形时&#xff0c;将PDF转换成DWG格式是一个非常好的选择。因为PDF是一种非常流行的文档格式&#xff0c;很多时候我们会接收到PDF文件&#xff0c;但是PDF文件中的向量图形无法直接在CAD中编辑。而将PDF转换成DWG格式后&#xff0c;就可…...

uniapp使用H5实现预览pdf文件

下载后把压缩包解压到自己的项目的static文件夹下的pdf文件下&#xff0c;如图 新建一个文件名为filePreview.vue <template><view><web-view :src"allUrl"></web-view></view> </template><script>export default {dat…...

Studio 3T for MongoDB的介绍及语法简单介绍

用法介绍 Studio 3T是一款用于MongoDB数据库管理和开发的图形化工具&#xff0c;它提供了许多功能来简化MongoDB的操作和开发过程。以下是一些常见的Studio 3T用法&#xff1a; 连接到MongoDB服务器&#xff1a; 打开Studio 3T并创建一个新连接配置。输入MongoDB服务器的主机名…...

MySQL数据库——存储引擎(1)-MySQL体系结构、存储引擎简介

目录 MySQL体系结构 连接层 服务层 引擎层 存储层 存储引擎简介 概念 语句 演示 下面开始学习进阶篇的第一个内容——存储引擎 分为四点学习&#xff1a; MySQL体系结构存储引擎简介存储引擎特点存储引擎选择 MySQL体系结构 连接层 最上层是一些客户端和链接服务&am…...

211. 添加与搜索单词 - 数据结构设计

211. 添加与搜索单词 - 数据结构设计 class WordDictionary { public:struct Node{Node *node[26];bool is_end;Node(){is_endfalse;for(int i0;i< 26;i){node[i]NULL;}}};Node *root;WordDictionary() {root new Node();}void addWord(string word) {auto p root;for(aut…...

【深度学习】You Only Segment Once: Towards Real-Time Panoptic Segmentation,YOSO全景分割

论文&#xff1a;https://arxiv.org/abs/2303.14651 代码&#xff1a;https://github.com/hujiecpp/YOSO 文章目录 Abstract1. Introduction2. Related Work3. Method3.1. Task Formulation3.2. Feature Pyramid Aggregator3.3. Separable Dynamic Decoder 4. Experiments4.1. …...

ping与Traceroute是如何工作的

ping 是基于 ICMP 协议工作的。ICMP 全称 Internet Control Message Protocol&#xff0c;就是互联网控制报文协议。 ICMP 报文是封装在 IP 包里面的。因为传输指令的时候&#xff0c;肯定需要源地址和目标地址。它本身非常简单。 ICMP 报文有很多的类型&#xff0c;不同的类型…...

CentOS Python环境搭建

安装依赖 yum install -y libffi-devel wget gcc make zlib-devel openssl openssl-devel ncurses-devel openldap-devel gettext bzip2-devel xz-devel下载安装包 wget "https://www.python.org/ftp/python/3.9.10/Python-3.9.10.tar.xz" 编译安装 # 3.1、解压安装包…...

亚马逊云科技与伊克罗德推出AI绘画解决方案——imAgine

在过去的数月中&#xff0c;亚马逊云科技已经推出了多篇介绍如何在亚马逊云科技上部署Stable Diffusion&#xff0c;或是如何结合Amazon SageMaker与Stable Diffusion进行模型训练和推理任务的内容。 为了帮助客户快速、安全地在亚马逊云科技上构建、部署和管理应用程序&#x…...

机器学习课后习题 --- 逻辑回归

&#xff08;一&#xff09;单选题 1.一监狱人脸识别准入系统用来识别待进入人员的身份&#xff0c;此系统一共包括识别4种不同的人员&#xff1a;狱警&#xff0c;小偷&#xff0c;送餐员&#xff0c;其他。下面哪种学习方法最适合此种应用需求&#xff1a; A:二分类问题 …...

软件设计师学习笔记9-进程调度

目录 1. PV操作 1.1进程的同步与互斥 1.1.1互斥 1.1.2同步 1.2 PV操作 1.2.1信号量 1.2.2 PV操作的概念 2.信号量与PV操作 2.1 PV操作与互斥模型 2.2 PV操作与同步模型 2.3 互斥与同步模型结合 3.前趋图与PV操作 1. PV操作 1.1进程的同步与互斥 1.1.1互斥 互斥&…...

09:STM32-------USART串口通信+串口数据包

目录 一:串口协议 1:通信接口 2:串口通信 3:硬件电路 4:电平标准 5:串口参数及其时序 二:USART介绍 1:简历 2:USART框图 3:USART的基本结构 4:数据帧 5: 波特率发生器 6:数据模式 三:案例 A:串口发送--单发送 1:连接图 2:函数介绍 3:代码 B:串口发送接收 1…...

“安全即服务”为网络安全推开一道门

8月30日&#xff0c;三六零&#xff08;下称“360”&#xff09;集团发布了2023年半年报&#xff0c;其中安全业务第二季度收入6.54亿元&#xff0c;同比增长98.76%&#xff0c;环比增长157.16%&#xff0c;安全第二增长曲线已完全成型&#xff01;特别值得一提的是&#xff0c…...

vue3的生命周期

1.vue3生命周期官方流程图 2.vue3中的选项式生命周期 vue3中的选项式生命周期钩子基本与vue2中的大体相同&#xff0c;它们都是定义在 vue实例的对象参数中的函数&#xff0c;它们在vue中实例的生命周期的不同阶段被调用。生命周期函数钩子会在我们的实例挂载&#xff0c;更新…...

[E2E Test] Python Behave Selenium 一文学会自动化测试

前言 本文将使用Python Behave与Selenium&#xff0c;和同学们一起认识自动化测试&#xff0c;并附上完整的实践教程。 项目源码已上传&#xff1a;CSDN 郭麻花 Azure Repo python-behave-selenium 核心概念 1. 什么是E2E Test E2E即End-to-end&#xff0c;意思是从头到尾…...

Knowledge Graph Prompting for Multi-Document Question Answering

本文是LLM系列文章&#xff0c;针对《Knowledge Graph Prompting for Multi-Document Question Answering》的翻译。 多文档问答中的知识图谱提示 摘要1 引言2 符号3 知识图谱构建4 LM引导的图形遍历器5 实验6 相关工作7 结论 摘要 大型语言模型的“预训练、提示、预测”范式…...

ElMessageBox.prompt 点击确认校验成功后关闭

ElMessageBox.prompt(, 验证取货码, {inputPattern: /^.{1,20}$/,inputErrorMessage: 请输入取货码,inputPlaceholder: 请输入取货码,beforeClose: (action, instance, done) > {if (action confirm) {if (instance.inputValue) {let flag false;if (flag) {done()} else …...

调整Windows11桌面图标间隔

调整Windows11桌面图标间隔 WinR 快捷键如何使用 在Windows系统中&#xff0c;通过 WinR 的快捷键可以快速打开Windows系统的“运行”窗口&#xff0c;然后在这里输入相应的命令就可以快速执行指定的任务。 具体的操作方法是&#xff0c;同时按下键盘上的Windows键和R键即可。…...

Spring最佳实践: 构建高效可维护的Java应用程序

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

stable diffusion webui中的sampler

Stable Diffusion-采样器篇 - 知乎采样器&#xff1a;Stable Diffusion的webUI中&#xff0c;提供了大量的采样器供我们选择&#xff0c;例如Eular a&#xff0c; Heum&#xff0c;DDIM等&#xff0c;不同的采样器之间究竟有什么区别&#xff0c;在操作时又该如何进行选择&…...

MySQL表的内连和外连

文章目录 MySQL表的内连和外连1. 内连接(1) 显示SMITH的名字和部门名称 2. 外连接2.1 左外连接(1) 查询所有学生的成绩&#xff0c;如果这个学生没有成绩&#xff0c;也要将学生的个人信息显示出来 2.2 右外连接(1) 对stu表和exam表联合查询&#xff0c;把所有的成绩都显示出来…...

沈阳网站建设发布/网页在线客服免费版

CentOS的安装方式常用的有三个1 光盘安装2 通过引导光盘网络安装3 U盘安装这次我用的是通过引导光盘网络安装环境&#xff1a;1 VirtualBox虚拟出来的Linux平台CentOS官网镜像站点 https://www.centos.org/download/mirrors/&#xff0c;在它的列表中&#xff0c;选择了浙江大学…...

腾讯官方网站建设/网站 推广

资料下载 coding无法使用浏览器打开&#xff0c;必须用git工具下载&#xff1a; git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git视频观看 百问网驱动大全 先学习输入系统应用编程 1. 百问网Linux视频体系 2. 建议 在《Linux系列教程之…...

wordpress主题外贸下载/网店运营推广

Apollo配置中心 Apollo配置中心Apollo配置中心1.什么是配置中心2.配置举例3.说说Apollo3.1背景3.2 介绍3.3基础模型3.4 核心概念3.4.1 NameSpace3.4.1.1 Namespace格式3.4.1.2 Namespace权限3.4.1.3 Namespace 类型3.4.2 核心模块3.4.2.1 ConfigService3.4.2.2 AdminService3.4…...

网站哪家做的比较好/2022年搜索引擎优化指南

最近挺多童鞋问我如何配置Spring Cloud xxx组件的重试。本篇进行一个总结。 Spring Cloud中的重试机制应该说是比较混乱的&#xff0c;不同的版本有一定区别&#xff0c;实现也不大一样&#xff0c;好在Spring Cloud Camden之后已经基本稳定下来&#xff0c;Dalston中又进行了一…...

建站之星模板制作/自媒体平台注册

在命令模式下&#xff0c;输入:.,$d 一回车就全没了。 表示从当前行到末行全部删除掉。 用gg表示移动到首行。...

怎么做网站管理系统/百度竞价客服

...