目录
Qt事件
事件介绍
事件的处理
按键事件
鼠标事件
定时器
事件分发器
事件过滤器
Qt文件
Qt文件概述
输入输出设备类
文件读写类
文件和目录信息类
Qt事件
事件介绍
事件是应⽤程序内部或者外部产⽣的事情或者动作的统称。在 Qt 中使⽤⼀个对象来表⽰⼀个事件。所有的 Qt 事件均继承于抽象类 QEvent。事件是由系统或者 Qt 平台本⾝在不同的时刻发出的。当⽤⼾按下⿏标、敲下键盘,或者是窗⼝需要重新绘制的时候,都会发出⼀个相应的事件。⼀些事件是在⽤⼾操作时发出,如键盘事件、⿏标事件等,另⼀些事件则是由系统本⾝⾃动发出,如定时器事件。
常⻅的 Qt 事件如下:
常⻅事件描述:
事件名称 | 描述 |
⿏标事件 | ⿏标左键、⿏标右键、⿏标滚轮,⿏标的移动,⿏标按键的按下和松开 |
键盘事件 | 按键类型、按键按下、按键松开 |
定时器事件 | 定时时间到达 |
进⼊离开事件 | ⿏标的进⼊和离开 |
滚轮事件 | ⿏标滚轮滚动 |
绘屏事件 | 重绘屏幕的某些部分 |
显⽰隐藏事件 | 窗⼝的显⽰和隐藏 |
移动事件 | 窗⼝位置的变化 |
窗⼝事件 | 是否为当前窗⼝ |
⼤⼩改变事件 | 窗⼝⼤⼩改变 |
焦点事件 | 键盘焦点移动 |
拖拽事件 | ⽤⿏标进⾏拖拽 |
事件的处理
事件处理⼀般常⽤的⽅法为:重写相关的 Event 函数。
在 Qt 中,⼏乎所有的 Event 函数都是虚函数,所以可以重新实现。如:在实现⿏标的进⼊和离开事件时,直接重新实现 enterEvent() 和 leaveEvent() 即可。
enterEvent() 和 leaveEvent() 函数原型如下:
按键事件
Qt 中的按键事件是通过 QKeyEvent 类来实现的。当键盘上的按键被按下或者被释放时,键盘事件便会触发。在帮助⽂档中查找 QKeyEvent 类如下:
查找按键事件中所有的按键类型:在帮助⽂档中输⼊:Qt::Key,如下图:
单个按键的例子
1、单个按键的按下事件:
void MyWidget::keyPressEvent(QKeyEvent *event)
{if (event->key() == Qt::Key_A) {qDebug() << "按下了 A 键";// 执行相应的操作}QWidget::keyPressEvent(event); // 传递事件给父类处理
}
2、单个按键的释放事件:
void MyWidget::keyReleaseEvent(QKeyEvent *event)
{if (event->key() == Qt::Key_A) {qDebug() << "释放了 A 键";// 执行相应的操作}QWidget::keyReleaseEvent(event); // 传递事件给父类处理
}
组合按键的例子
1、同时按下多个键的事件:
void MyWidget::keyPressEvent(QKeyEvent *event)
{if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_C) {qDebug() << "同时按下了 Ctrl + C 键";// 执行相应的操作}QWidget::keyPressEvent(event); // 传递事件给父类处理
}
2、同时释放多个键的事件:
void MyWidget::keyReleaseEvent(QKeyEvent *event)
{if (event->modifiers() & Qt::ShiftModifier && event->key() == Qt::Key_F) {qDebug() << "同时释放了 Shift + F 键";// 执行相应的操作}QWidget::keyReleaseEvent(event); // 传递事件给父类处理
}
在上述示例中:
keyPressEvent()
和keyReleaseEvent()
函数分别处理按键按下和释放事件。- 使用
QKeyEvent
对象的key()
方法获取按下或释放的具体按键。- 使用
modifiers()
方法可以获取同时按下的修饰键(如 Ctrl、Shift 等)。- 在处理完事件后,通常会调用
QWidget::keyPressEvent(event)
或QWidget::keyReleaseEvent(event)
将事件传递给父类处理,以确保其他部分的事件处理逻辑能够正常运行。
鼠标事件
在 Qt 中,⿏标事件是⽤ QMouseEvent 类来实现的。当在窗⼝中按下⿏标或者移动⿏标时,都会产⽣⿏标事件。
利⽤ QMouseEvent 类可以获取⿏标的哪个键被按下了以及⿏标的当前位置等信息。在 Qt 帮助⽂档中查找QMouseEvent类 如下图⽰:
鼠标单击事件的例子
void MyWidget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {qDebug() << "左键被按下";// 执行相应的操作} else if (event->button() == Qt::RightButton) {qDebug() << "右键被按下";// 执行相应的操作}QWidget::mousePressEvent(event); // 传递事件给父类处理
}
鼠标释放事件的例子
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {qDebug() << "左键被释放";// 执行相应的操作} else if (event->button() == Qt::RightButton) {qDebug() << "右键被释放";// 执行相应的操作}QWidget::mouseReleaseEvent(event); // 传递事件给父类处理
}
鼠标双击事件的例子
void MyWidget::mouseDoubleClickEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {qDebug() << "左键双击";// 执行相应的操作} else if (event->button() == Qt::RightButton) {qDebug() << "右键双击";// 执行相应的操作}QWidget::mouseDoubleClickEvent(event); // 传递事件给父类处理
}
鼠标移动事件的例子
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{qDebug() << "鼠标移动到 (" << event->pos().x() << ", " << event->pos().y() << ")";// 执行相应的操作,例如更新鼠标位置的显示等QWidget::mouseMoveEvent(event); // 传递事件给父类处理
}
鼠标滚轮事件的例子
void MyWidget::wheelEvent(QWheelEvent *event)
{if (event->delta() > 0) {qDebug() << "鼠标向上滚动";// 执行相应的操作} else if (event->delta() < 0) {qDebug() << "鼠标向下滚动";// 执行相应的操作}QWidget::wheelEvent(event); // 传递事件给父类处理
}
定时器
Qt 中在进⾏窗⼝程序的处理过程中,经常要周期性的执⾏某些操作,或者制作⼀些动画效果,使⽤定时器就可以实现。所谓定时器就是在间隔⼀定时间后,去执⾏某⼀个任务。定时器在很多场景下都会使⽤到,如弹窗⾃动关闭之类的功能等。
Qt中的定时器分为 QTimerEvent 和 QTimer 这2个类。
• QTimerEvent类 ⽤来描述⼀个定时器事件。在使⽤时需要通过 startTimer() 函数来开启⼀个定时器,这个函数需要输⼊⼀个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进⾏相关操作。
• QTimer类 来实现⼀个定时器,它提供了更⾼层次的编程接⼝,如:可以使⽤信号和槽,还可以设置只运⾏⼀次的定时器。
QTimerEvent 类
QTimerEvent
类是用来描述定时器事件的类。它通常与 QObject
的 timerEvent()
函数结合使用,用于处理定时器事件的回调操作。
使用方法:
启动定时器: 通过
startTimer()
函数启动一个定时器,该函数接受一个毫秒为单位的时间间隔作为参数,并返回一个整型值,代表该定时器的唯一标识符(定时器编号)。定时器事件处理: 当定时器设定的时间间隔到达时,会触发
timerEvent()
函数。在timerEvent()
函数中,可以通过传入的参数QTimerEvent *event
来获取定时器的具体信息,例如定时器的标识符,从而执行相应的操作。代码示例:
void MyWidget::timerEvent(QTimerEvent *event) {if (event->timerId() == timerId) {qDebug() << "定时器事件触发,定时器ID:" << event->timerId();// 执行相应的定时操作}QWidget::timerEvent(event); // 传递事件给父类处理 }
QTimer 类
QTimer
类提供了更高级别的定时器功能,其主要特点是能够通过信号和槽机制来处理定时器事件,以及提供更多的灵活性和控制选项。
主要功能:
启动定时器: 通过
QTimer
的start()
函数启动定时器,该函数接受一个毫秒为单位的时间间隔作为参数,还可以选择性地设置定时器的单次触发或重复触发。定时器信号和槽:
QTimer
可以通过信号timeout()
来定期触发定时器事件。可以通过连接(connect)这个信号到槽函数来处理定时器事件,这使得定时器的使用更加方便和直观。单次运行定时器: 可以使用
setSingleShot(true)
方法设置定时器为只运行一次,适用于需要在一段时间后执行一次任务的场景。代码示例:
// 创建一个 QTimer 对象 QTimer *timer = new QTimer(this);// 设置定时器触发的时间间隔,单位为毫秒 timer->setInterval(1000); // 1秒// 连接定时器的 timeout() 信号到槽函数 connect(timer, &QTimer::timeout, [=]() {qDebug() << "定时器触发";// 执行相应的定时操作 });// 启动定时器 timer->start();
区别和选择:
QTimerEvent 和 timerEvent(): 适合在自定义的 QObject 派生类中处理定时器事件,需要手动管理定时器的标识符和事件处理逻辑。
QTimer 类: 更高级别的接口,通过信号和槽机制处理定时器事件,适合在需要简单设置和操作定时器的场景下使用,无需手动管理定时器事件。
事件分发器
概述
在 Qt 中,事件分发器(Event Dispatcher) 是⼀个核⼼概念,⽤于处理 GUI 应⽤程序中的事件。事件分发器负责将事件从⼀个对象传递到另⼀个对象,直到事件被处理或被取消。每个继承⾃ QObject类 或 QObject类 本⾝都可以在本类中重写 bool event(QEvent *e) 函数,来实现相关事件的捕获和拦截。
事件分发器⼯作原理
在 Qt 中,我们发送的事件都是传给了 QObject 对象,更具体点是传给了 QObject 对象的 event() 函数。所有的事件都会进⼊到这个函数⾥⾯,那么我们处理事件就要重写这个 event() 函数。event() 函数本⾝不会去处理事件,⽽是根据 事件类型(type值)调⽤不同的事件处理函数。事件分发器就是⼯作在应⽤程序向下分发事件的过程中,如下图:
如上图,事件分发器⽤于分发事件。在此过程中,事件分发器也可以做拦截操作。事件分发器主要是通过 bool event(QEvent *e) 函数来实现。其返回值为布尔类型,若为 ture,代表拦截,不向下分发。
Qt 中的事件是封装在 QEvent类 中,在 Qt 助⼿中输⼊ QEvent 可以查看其所包括的事件类型,如下图示:
在Qt中声明和实现鼠标点击事件、事件分发器以及拦截事件时,通常会遵循以下步骤。下面是一个基本的示例,分别在头文件 widget.h
和实现文件 widget.cpp
中展示如何完成这些操作。
在 widget.h
头文件中声明
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QMouseEvent>class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = nullptr);~Widget();protected:// 声明鼠标点击事件处理函数void mousePressEvent(QMouseEvent *event) override;// 声明事件分发器函数bool event(QEvent *event) override;private:// 声明拦截事件处理函数bool eventFilter(QObject *watched, QEvent *event) override;
};#endif // WIDGET_H
在 widget.cpp
实现文件中实现
#include "widget.h"
#include <QDebug>Widget::Widget(QWidget *parent) : QWidget(parent)
{// 安装事件过滤器,用于拦截事件this->installEventFilter(this);
}Widget::~Widget()
{
}// 实现鼠标点击事件处理函数
void Widget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {qDebug() << "左键点击,位置:" << event->pos();// 执行相应的操作} else if (event->button() == Qt::RightButton) {qDebug() << "右键点击,位置:" << event->pos();// 执行相应的操作}// 将事件传递给父类处理QWidget::mousePressEvent(event);
}// 实现事件分发器函数
bool Widget::event(QEvent *event)
{if (event->type() == QEvent::MouseButtonPress) {// 处理鼠标按下事件QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);qDebug() << "事件分发器捕获到鼠标按下事件,位置:" << mouseEvent->pos();// 执行相应的操作return true; // 表示事件已处理}// 其他事件交给父类处理return QWidget::event(event);
}// 实现事件过滤器函数
bool Widget::eventFilter(QObject *watched, QEvent *event)
{if (watched == this && event->type() == QEvent::MouseMove) {// 拦截并处理鼠标移动事件QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);qDebug() << "事件过滤器捕获到鼠标移动事件,位置:" << mouseEvent->pos();// 执行相应的操作return true; // 表示事件已处理}// 其他事件交给父类处理return QWidget::eventFilter(watched, event);
}
代码说明:
mousePressEvent
函数: 在该函数中处理鼠标点击事件。根据QMouseEvent
的button()
方法判断是左键还是右键,并可以获取鼠标点击的位置信息。
event
函数: 这是事件分发器函数,用于捕获所有类型的事件。在示例中,通过判断事件的类型 (QEvent::MouseButtonPress
) 来处理鼠标按下事件。
eventFilter
函数: 这是事件过滤器函数,通过调用installEventFilter()
函数安装到对象上。在示例中,通过判断事件类型 (QEvent::MouseMove
) 来处理鼠标移动事件。
事件过滤器
在 Qt 中,⼀个对象可能经常要查看或拦截另外⼀个对象的事件,如对话框想要拦截按键事件,不让别的组件接收到,或者修改按键的默认值等。通过上⾯的学习,我们已经知道,Qt 创建了 QEvent事件对象之后,会调⽤QObject 的 event()函数 处理事件的分发。显然,我们可以在 event()函数 中实现拦截的操作。由于 event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当⿇烦,更不⽤说重写 event()函数还得⼩⼼⼀堆问题。好在 Qt 提供了另外⼀种机制来达到这⼀⽬的:事件过滤器。
事件过滤器是在应⽤程序分发到 event事件分发器 之前,再做⼀次更⾼级的拦截。如下图⽰:
事件过滤器的⼀般使⽤步骤:
1、安装事件过滤器;
2、重写事件过滤器函数:eventfilter() 。
假设我们有一个 MyWidget
类,它继承自 QWidget
,并希望在该小部件上安装事件过滤器来拦截按键事件。以下是如何在 widget.cpp
中实现这个示例:
#include "widget.h"
#include <QDebug>
#include <QKeyEvent>Widget::Widget(QWidget *parent) : QWidget(parent)
{// 创建一个 QLabel 作为被监视对象QLabel *label = new QLabel("监视对象", this);label->setGeometry(50, 50, 100, 30); // 设置标签的位置和大小// 安装事件过滤器到 label 上label->installEventFilter(this);
}bool Widget::eventFilter(QObject *watched, QEvent *event)
{if (watched->objectName() == "监视对象") {if (event->type() == QEvent::KeyPress) {QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);qDebug() << "按键事件被拦截:按键" << keyEvent->key() << "被按下";// 在此处可以根据需求修改事件或者阻止事件继续传递// 例如,拦截所有按键事件,不让它传递给监视对象return true; // 返回 true 表示事件已处理}}// 其他事件交给父类处理return QWidget::eventFilter(watched, event);
}
示例说明:
安装事件过滤器: 在
Widget
类的构造函数中,我们创建了一个QLabel
对象作为被监视对象,并通过installEventFilter()
函数将当前小部件 (this
) 设置为其事件过滤器。重写事件过滤器函数: 在
eventFilter()
函数中,我们对事件进行检查和处理。在示例中,我们检查了被监视对象的对象名是否为"监视对象"
,如果是,并且事件类型为QEvent::KeyPress
,则我们拦截并处理按键事件。在实际应用中,你可以根据需要修改事件内容、记录日志、阻止事件继续传递等操作。返回值解释: 如果事件被处理了并且不需要传递给被监视对象,则返回
true
;否则,返回QWidget::eventFilter(watched, event)
,以继续将事件传递给父类处理。
Qt文件
Qt文件概述
⽂件操作是应⽤程序必不可少的部分。Qt 作为⼀个通⽤开发库,提供了跨平台的⽂件操作能⼒。 Qt提供了很多关于⽂件的类,通过这些类能够对⽂件系统进⾏操作,如⽂件读写、⽂件信息获取、⽂件复制或重命名等。
输入输出设备类
在 Qt 中,⽂件读写的类为 QFile 。QFile 的⽗类为 QFileDevice ,QFileDevice 提供了⽂件交互操作的底层功能。 QFileDevice 的⽗类是 QIODevice,QIODevice 的⽗类为 QObject 。
QIODevice 是 Qt 中所有输⼊输出设备(input/output device,简称 I/O 设备)的基础类,I/O 设备就是能进⾏数据输⼊和输出的设备,例如⽂件是⼀种 I/O 设备,⽹络通信中的 socket 是 I/O 设备, 串⼝、蓝⽛等通信接⼝也是 I/O 设备,所以它们也是从 QIODevice 继承来的。Qt 中主要的⼀些 I/O 设备类的继承关系如下图所⽰:
上图中各类的说明如下:
• QFile 是⽤于⽂件操作和⽂件数据读写的类,使⽤ QFile 可以读写任意格式的⽂件。
• QSaveFile 是⽤于安全保存⽂件的类。使⽤ QSaveFile 保存⽂件时,它会先把数据写⼊⼀个临时⽂件,成功提交后才将数据写⼊最终的⽂件。如果保存过程中出现错误,临时⽂件⾥的数据不会被写⼊最终⽂件,这样就能确保最终⽂件中不会丢失数据或被写⼊部分数据。 在保存⽐较⼤的⽂件或复杂格式的⽂件时可以使⽤这个类,例如从⽹络上下载⽂件等。
• QTemporaryFile 是⽤于创建临时⽂件的类。使⽤函数 QTemporaryFile::open() 就能创建⼀个⽂件名唯⼀的临时⽂件,在 QTemporaryFile 对象被删除时,临时⽂件被⾃动删除。
• QTcpSocket 和 QUdpSocket 是分别实现了 TCP 和 UDP 的类。
• QSerialPort 是实现了串⼝通信的类,通过这个类可以实现计算机与串⼝设备的通信。
• QBluetoothSocket 是⽤于蓝⽛通信的类。⼿机和平板计算机等移动设备有蓝⽛通信模块,笔记本电脑⼀般也有蓝⽛通信模块。通过QBluetoothSocket类,就可以编写蓝⽛通信程。如编程实现笔记本电脑与⼿机的蓝⽛通信。
• QProcess 类⽤于启动外部程序,并且可以给程序传递参数。
• QBuffer 以⼀个 QByteArray 对象作为数据缓冲区,将 QByteArray 对象当作⼀个 I/O 设备来读写。
文件读写类
在 Qt 中,⽂件的读写主要是通过 QFile 类来实现。在 QFile 类中提供了⼀些⽤来读写⽂件的⽅法。对于⽂件的操作主要有:
• 读数据:QFile 类中提供了多个⽅法⽤于读取⽂件内容;如 read()、readAll()、readLine()等。
• 写数据:QFile 类中提供了多个⽅法⽤于往⽂件中写内容;如 write()、writeData()等。
• 关闭⽂件:⽂件使⽤结束后必须⽤函数 close() 关闭⽂件。
访问⼀个设备之前,需要使⽤ open()函数 打开该设备,⽽且必须指定正确的打开模式,QIODevice 中所有的打开模式由 QIODevice::OpenMode 枚举变量定义,其取值如下:
QIODevice::NotOpen | 没有打开设备 |
QIODevice::ReadOnly | 以只读⽅式打开设备 |
QIODevice::WriteOnly | 以只写⽅式打开设备 |
QIODevice::ReadWrite | 以读写⽅式打开设备 |
QIODevice::Append | 以追加⽅式打开设备,数据将写到⽂件末尾 |
QIODevice::Truncate | 每次打开⽂件后重写⽂件内容,原内容将被删除 |
QIODevice::Text | 在读⽂件时,⾏尾终⽌符会被转换为 '\n';当写⼊⽂件时,⾏尾终⽌符会被转换为 本地编码。如 Win32上为'\r\n'; |
QIODevice::Unbuffered | ⽆缓冲形式打开⽂件,绕过设备中的任何缓冲区 |
QIODevice::NewOnly | ⽂件存在则打开失败,不存在则创建⽂件 |
文件和目录信息类
QFileInfo 是 Qt 提供的⼀个⽤于获取⽂件和⽬录信息的类,如获取⽂件名、⽂件⼤⼩、⽂件修改⽇期等。QFileInfo类中提供了很多的⽅法,常⽤的有:
• isDir() 检查该⽂件是否是⽬录;
• isExecutable() 检查该⽂件是否是可执⾏⽂件;
• fileName() 获得⽂件名;
• completeBaseName() 获取完整的⽂件名;
• suffix() 获取⽂件后缀名;
• completeSuffix() 获取完整的⽂件后缀;
• size() 获取⽂件⼤⼩;
• isFile() 判断是否为⽂件;
• fileTime() 获取⽂件创建时间、修改时间、最近访问时间等;
代码示例:
#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 创建一个 QFileInfo 对象,传入文件路径或者文件名QFileInfo fileInfo("/path/to/your/file.txt");// 获取文件名QString fileName = fileInfo.fileName();qDebug() << "文件名:" << fileName;// 获取文件路径QString filePath = fileInfo.filePath();qDebug() << "文件路径:" << filePath;// 获取文件大小(字节)qint64 fileSize = fileInfo.size(); // 返回 qint64 类型qDebug() << "文件大小:" << fileSize << "bytes";// 获取文件修改日期和时间QDateTime lastModified = fileInfo.lastModified();qDebug() << "最后修改时间:" << lastModified.toString(Qt::ISODate);// 获取文件后缀名QString suffix = fileInfo.suffix();qDebug() << "文件后缀名:" << suffix;// 检查文件是否存在if (fileInfo.exists()) {qDebug() << "文件存在";} else {qDebug() << "文件不存在";}return a.exec();
}
示例说明:
包含头文件: 引入了
QFileInfo
类的头文件<QFileInfo>
,以及用于调试输出的<QDebug>
。创建
QFileInfo
对象: 使用文件路径或文件名创建一个QFileInfo
对象,例如"/path/to/your/file.txt"
。获取文件信息:
- 使用
fileName()
获取文件名。- 使用
filePath()
获取文件路径。- 使用
size()
获取文件大小,返回qint64
类型表示文件大小的字节数。- 使用
lastModified()
获取文件最后修改时间,返回QDateTime
对象。- 使用
suffix()
获取文件后缀名。检查文件存在性: 使用
exists()
函数检查文件是否存在,并根据结果输出相应信息。输出信息: 使用
qDebug()
输出获取到的文件信息。