免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

3.1 Qt事件之事件处理器

3.1 Qt事件之事件处理器 1. 事件众所周知Qt是一个基于C的框架主要用来开发带窗口的应用程序不带窗口的也行但不是主流。我们使用的基于窗口的应用程序都是基于事件其目的主要是用来实现回调因为只有这样程序的效率才是最高的。所以在Qt框架内部为我们提供了一系列的事件处理机制当窗口事件产生之后事件会经过事件派发 - 事件过滤-事件分发-事件处理几个阶段。Qt窗口中对于产生的一系列事件都有默认的处理动作如果我们有特殊需求就需要在合适的阶段重写事件的处理动作。事件event是由系统或者 Qt 本身在不同的场景下发出的。当用户按下/移动鼠标、敲下键盘或者是窗口关闭/大小发生变化/隐藏或显示都会发出一个相应的事件。一些事件在对用户操作做出响应时发出如鼠标/键盘事件等另一些事件则是由系统自动发出如计时器事件。每一个Qt应用程序都对应一个唯一的 QApplication应用程序对象然后调用这个对象的exec()函数这样Qt框架内部的事件检测就开始了程序将进入事件循环来监听应用程序的事件。int main(int argc, char *argv[]) // 程序入口函数 // argc命令行参数个数 // argv命令行参数数组 { QApplication a(argc, argv); // ① 创建 QApplication 应用程序对象 // QApplication 管理整个 GUI 程序的生命周期 // 负责 // - 初始化 Qt 系统 // - 处理命令行参数 // - 管理事件循环 // 注意每个 GUI 程序有且只有一个 QApplication 对象 // 必须在任何其他 Qt 对象之前创建 MainWindow* w new MainWindow; // ② 在堆上创建主窗口对象 // 没有指定父对象new MainWindow 无 parent 参数 // w 是顶层窗口独立窗口 // 注意没有 delete因为 Qt 对象树会管理 // 但 MainWindow 是顶层窗口需要手动 delete 或设置关闭时销毁 w.show(); // ③ 显示主窗口 // show() 使窗口可见 // 调用后窗口才出现在屏幕上 return a.exec(); // ④ 进入 Qt 事件循环 // exec() execute执行 // 程序在这里卡住开始无限循环处理事件 // 直到用户关闭所有窗口或调用 quit() 才返回 // 返回值作为 main 的返回值0 表示正常退出 }int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow* w new MainWindow; w.show(); return a.exec(); }事件在Qt中产生之后的分发过程是这样的当事件产生之后Qt使用应用程序对象调用notify()函数将事件发送到指定的窗口[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);事件在发送过程中可以通过事件过滤器进行过滤默认不对任何产生的事件进行过滤。// 需要先给窗口安装过滤器, 该事件才会触发 [virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)当事件发送到指定窗口之后窗口的事件分发器会对收到的事件进行分类:[override virtual protected] bool QWidget::event(QEvent *event);事件分发器会将分类之后的事件鼠标事件、键盘事件、绘图事件。。。分发给对应的事件处理器函数进行处理每个事件处理器函数都有默认的处理动作我们也可以重写这些事件处理器函数比如鼠标事件// 鼠标按下 [virtual protected] void QWidget::mousePressEvent(QMouseEvent *event); // 鼠标释放 [virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event); // 鼠标移动 [virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);2. 事件处理器函数通过上面的描述可以得知Qt的事件处理器函数处于食物链的最末端每个事件处理器函数都对应一个唯一的事件这为我们重新定义事件的处理动作提供了便利。另外Qt提供的这些事件处理器函数都是回调函数也就是说作为使用者我们只需要指定函数的处理动作关于函数的调用是不需要操心的当某个事件被触发Qt框架会调用对应的事件处理器函数。QWidget类是Qt中所有窗口类的基类在这个类里边定义了很多事件处理器函数它们都是受保护的虚函数。我们可以在Qt的任意一个窗口类中重写这些虚函数来重定义它们的行为。下面介绍一些常用的事件处理器函数2.1 鼠标事件鼠标按下事件当鼠标左键、鼠标右键、鼠标中键被按下该函数被自动调用通过参数可以得到当前按下的是哪个鼠标键[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);鼠标释放事件当鼠标左键、鼠标右键、鼠标中键被释放该函数被自动调用通过参数可以得到当前释放的是哪个鼠标键[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);鼠标双击事件当鼠标双击该函数被调用通过参数可以得到是通过哪个鼠标键进行了双击操作。[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);鼠标进入事件当鼠标进入窗口的一瞬间触发该事件注意只在进入的瞬间触发一次该事件[virtual protected] void QWidget::enterEvent(QEvent *event);鼠标离开事件当鼠标离开窗口的一瞬间触发该事件注意只在离开的瞬间触发一次该事件[virtual protected] void QWidget::leaveEvent(QEvent *event);2.2 键盘事件键盘按下事件当键盘上的按键被按下了该函数被自动调用通过参数可以得知按下的是哪个键。[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);键盘释放事件当键盘上的按键被释放了该函数被自动调用通过参数可以得知释放的是哪个键。[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event);2.3 窗口重绘事件当窗口需要刷新的时候该函数就会自动被调用。窗口需要刷新的情景很多比如窗口大小发生变化窗口显示等另外我们还可以通过该函数给窗口绘制背景图总之这是一个需要经常被重写的一个事件处理器函数。[virtual protected] void QWidget::paintEvent(QPaintEvent *event);2.4 窗口关闭事件当窗口标题栏的关闭按钮被按下并且在窗口关闭之前该函数被调用可以通过该函数控制窗口是否被关闭。[virtual protected] void QWidget::closeEvent(QCloseEvent *event);2.5 重置窗口大小事件当窗口的大小发生变化该函数被调用。[virtual protected] void QWidget::resizeEvent(QResizeEvent *event);除此之外关于Qt窗口提供的其他事件处理器函数还有很多感兴趣的话可以仔细阅读Qt的帮助文档窗口的事件处理器函数非常好找规律是这样的受保护的虚函数函数名分为两部分: 事件描述Event函数带一个事件类型的参数3. 重写事件处理器函数由于事件处理器函数都是虚函数因此我们就可以添加一个标准窗口类的派生类这样不仅使子类继承了父类的属性还可以在这个子类中重写父类的虚函数总起来说整个操作过程还是 so easy 的。下面举个例子创建一个Qt项目添加一个窗口类让其从某个标准窗口类派生在子类中重写从父类继承的虚函数也就是事件处理器函数3.1 头文件#include QMainWindow QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); ~MainWindow(); protected: // 重写事件处理器函数 void closeEvent(QCloseEvent* ev); void resizeEvent(QResizeEvent* ev); private: Ui::MainWindow *ui; };3.2 源文件#include mainwindow.h #include ui_mainwindow.h #include QCloseEvent #include QMessageBox #include QResizeEvent #include QDebug MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::closeEvent(QCloseEvent *ev) { QMessageBox::Button btn QMessageBox::question(this, 关闭窗口, 您确定要关闭窗口吗?); if(btn QMessageBox::Yes) { // 接收并处理这个事件 ev-accept(); } else { // 忽略这个事件 ev-ignore(); } } void MainWindow::resizeEvent(QResizeEvent *ev) { qDebug() oldSize: ev-oldSize() currentSize: ev-size(); }QCloseEvent类是QEvent类的子类程序中使用的accept()或者ignore()的作用请参考QEvent类3.2Qt事件之事件分发器的1. QEvent。3.3 效果在上面重写的closeEvent事件中添加了关闭窗口的判断这样就可以避免误操作导致窗口被关闭了效果如下如果想要时时检测窗口大小就可以重写窗口的resizeEvent事件这样就可以得到窗口的最新尺寸信息了4. 自定义按钮基于Qt提供的事件处理器函数我们可以非常轻松地按照自己的想法制作出一个按钮按钮的要求如下从视觉上看是一个不规则按钮按钮实际上都是矩形的1.按钮上需要显示指定的背景图片2.按钮在鼠标的不同操作阶段无操作、鼠标悬停、鼠标按下能够显示不同的背景图3.按钮效果如下4.1 添加子类新添加的按钮类可以让它继承 QPushButton也可以让它继承其他的窗口类代价是当鼠标点击事件触发之后需要自己发射自定义信号这里让添加的子类从QWidget类派生。自定义类头文件#ifndef MYBUTTON_H #define MYBUTTON_H #include QWidget class MyButton : public QWidget { Q_OBJECT public: explicit MyButton(QWidget *parent nullptr); void setImage(QString normal, QString hover, QString pressed); protected: void mousePressEvent(QMouseEvent* ev); void mouseReleaseEvent(QMouseEvent* ev); void enterEvent(QEvent* ev); void leaveEvent(QEvent* ev); void paintEvent(QPaintEvent* ev); signals: void clicked(); private: QPixmap m_normal; QPixmap m_press; QPixmap m_hover; QPixmap m_current; }; #endif // MYBUTTON_H自定义类源文件#include mybutton.h #include QPainter MyButton::MyButton(QWidget *parent) : QWidget(parent) { } void MyButton::setImage(QString normal, QString hover, QString pressed) { // 加载图片 m_normal.load(normal); m_hover.load(hover); m_press.load(pressed); m_current m_normal; // 设置按钮和图片大小一致 setFixedSize(m_normal.size()); } void MyButton::mousePressEvent(QMouseEvent *ev) { // 鼠标被按下, 发射这个自定义信号 emit clicked(); m_current m_press; update(); } void MyButton::mouseReleaseEvent(QMouseEvent *ev) { m_current m_normal; update(); } void MyButton::enterEvent(QEvent *ev) { m_current m_hover; update(); } void MyButton::leaveEvent(QEvent *ev) { m_current m_normal; update(); } void MyButton::paintEvent(QPaintEvent *ev) { QPainter p(this); p.drawPixmap(rect(), m_current); }4.2 使用自定控件由于Qt的UI工具箱中提供的都是标准控件自定义的控件是不能直接拖拽到UI窗口中的这时我们需要先看一下自定义控件的基类类型上面自定义的 MyButton 的基类是 QWidget 类型因此需要往窗口中拖拽一个QWidget类型的标准控件然后在这个标准控件上鼠标右键这样添加的控件类型就变成了自定义的子类类型4.3 设置图片在主窗口中通过添加的按钮的对象调用子类的成员函数给其添加图片mainwindow.cppMainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); // 给自定义按钮设置图片 ui-button-setImage(:/ghost-1.png, :/ghost-2.png, :/ghost-3.png); // 处理自定义按钮的鼠标点击事件 connect(ui-button, MyButton::clicked, this, []() { QMessageBox::information(this, 按钮, 莫要调戏我...); }); } MainWindow::~MainWindow() { delete ui; }笔记1.Qt窗口中事件的处理过程在qt中产生一个事件之后这个事件的路由主要是分为四个过程第一个过程叫做时间派发事件派发由qt的应用程序对象来发送的这个对象会把事件发送到对应的窗口在发送过程中可以选择对窗口产生的事件进行过滤如果不过滤事件就会被发送到对应的那个窗口每个窗口都有一个事件分发器事件分发器会对事件进行分类再把分好类的对应的事件发送给对应的事件处理器函数事件处理器函数负责处理产生的事件每个事件处理器函数的功能都是非常单一的并且它们都有默认的处理动作。如果我们想要进行事件的派发就必须在当前的应用程序中创建出一个应用程序对象这个应用程序对象的类型是QApplication类型在每个qt应用程序中有且仅有一个应用程序对象这个对象被创建出来后需要调用exec函数exec函数被调用之后应用程序对象会在后台维护一个事件循环这个事件循环是一直去检测我们当前这个窗口有没有产生相关的事件如果产生了相关的事件应用程序对象就会调用notify函数去对事件进行传递把事件传递到对应的某一个窗口。第一个参数receiver是事件的接受者第二个参数QEvent类型的e对象就是产生的事件QEvent类是所有事件类的基类通过notify可以对事件进行发送在发送过程中可以选择对发送的事件进行过滤第一个参数watched表示要过滤哪一个对象的事件第二个参数QEvent类型的event对象就是对应的要被过滤的事件int main(int argc, char *argv[]) // 程序入口函数 // argc命令行参数个数 // argv命令行参数数组 { QApplication a(argc, argv); // ① 创建 QApplication 应用程序对象 // QApplication 管理整个 GUI 程序的生命周期 // 负责 // - 初始化 Qt 系统 // - 处理命令行参数 // - 管理事件循环 // 注意每个 GUI 程序有且只有一个 QApplication 对象 // 必须在任何其他 Qt 对象之前创建 MainWindow* w new MainWindow; // ② 在堆上创建主窗口对象 // 没有指定父对象new MainWindow 无 parent 参数 // w 是顶层窗口独立窗口 // 注意没有 delete因为 Qt 对象树会管理 // 但 MainWindow 是顶层窗口需要手动 delete 或设置关闭时销毁 w.show(); // ③ 显示主窗口 // show() 使窗口可见 // 调用后窗口才出现在屏幕上 return a.exec(); // ④ 进入 Qt 事件循环 // exec() execute执行 // 程序在这里卡住开始无限循环处理事件 // 直到用户关闭所有窗口或调用 quit() 才返回 // 返回值作为 main 的返回值0 表示正常退出 }程序启动 ↓ 创建 QApplication a ← 初始化 Qt 系统 ↓ 创建 MainWindow w ← 分配内存构造窗口 ↓ w.show() ← 窗口显示在屏幕上 ↓ a.exec() ← 进入事件循环程序卡在这里 ↓ 循环处理事件 ├── 鼠标点击 ├── 键盘输入 ├── 窗口重绘 ├── 定时器触发 └── ... ↓ 用户关闭窗口 / 调用 quit() ↓ exec() 返回 ↓ 程序结束回收资源总结main函数的固定流程创建 QApplication 管理程序 → 创建主窗口 → 显示 → 进入事件循环exec。程序在 exec 中一直运行处理用户操作直到窗口关闭。事件Event和信号槽Signal Slot的关系事件是底层输入信号槽是高层封装。事件先发生Qt 处理事件后可能发射信号。对比表维度事件Event信号槽Signal Slot本质底层消息对象间通信机制发出者操作系统/鼠标/键盘QObject 对象接收者event()函数connect连接的槽函数处理方式重写xxxEvent()虚函数连接信号到槽粒度细鼠标移动、按键粗点击、状态变化关系1 个事件 → 0 或多个信号1 个信号 → 多个槽完整流程用户按下鼠标 ↓ 操作系统捕获 ↓ Qt 事件循环接收 → 封装为 QMouseEvent ↓ 发送给目标控件的 event() 函数 ↓ event() 分发到 mousePressEvent() ← 这是事件 ↓ QPushButton 内部处理 mousePressEvent() ↓ 发射 clicked() 信号 ← 这是信号 ↓ connect 连接的槽函数被调用 ← 这是槽代码对比事件处理重写虚函数class MyWidget : public QWidget { protected: // 重写鼠标按下事件 void mousePressEvent(QMouseEvent *event) override { qDebug() 鼠标按下了; QWidget::mousePressEvent(event); // 调用父类继续处理 } };信号槽连接信号connect(btn, QPushButton::clicked, this, []() { qDebug() 按钮被点击了; });联系与区别特性事件信号槽是否需要 connect不需要重写虚函数即可必须 connect能否过滤能用事件过滤器不能直接过滤传播方向向上传播父对象无传播直接到槽适用场景自定义控件、精细控制对象间通信、业务逻辑信号来源操作系统对象主动发射类比事件 敲门声物理动作 信号 主人喊有客人来了通知 槽 你去开门响应 敲门 → 主人判断 → 喊话 → 开门 事件 → Qt处理 → 发射信号 → 执行槽什么时候用哪个// 用事件需要精细控制鼠标/键盘行为 class MyLabel : public QLabel { protected: void mouseDoubleClickEvent(QMouseEvent *e) override { // 双击时做特殊处理 } }; // 用信号槽普通按钮点击、状态变化 connect(btn, QPushButton::clicked, ...); connect(timer, QTimer::timeout, ...);总结事件是底层输入鼠标、键盘等由操作系统产生Qt 处理后可能发射信号。信号槽是对象间高层次的通信方式。比如按钮点击事件mousePressEvent→ 信号clicked→ 槽你的 lambda。如果没有对事件做任何过滤处理那默认就不会过滤任何事件。这个事件没有被过滤就会被发送到对应的窗口每一个窗口都有一个叫做event的函数这个函数叫做事件分发器接收的参数就是发过来的事件事件分发器接收事件后会分发给对应的事件处理器进行处理。有事件过滤器、事件分发器、事件过滤器这些函数不需要程序员去调用但是程序员可以重写这些函数的行为因为这些函数都是虚函数虚函数的特点可以写一个子类在子类里继承父类的虚函数并且重写父类的虚函数子类重写了虚函数后虚函数的行为就被重新定义了就可以在子类里按照我们指定的行为进行对应的事件处理。2.事件处理器函数介绍QObject 是 Qt 中几乎所有类的基类是 Qt 对象模型的核心。核心地位QObject ↑ ├── QWidget所有可视化控件 │ ↑ │ ├── QPushButton │ ├── QLabel │ └── QMainWindow │ ├── QTimer定时器 ├── QThread线程 └── ...几乎所有 Qt 类QObject 提供的核心功能功能说明信号槽机制connect连接信号和槽对象树父子关系管理自动销毁子对象元对象系统metaObject()、qobject_cast动态属性setProperty()/property()事件处理event()虚函数内存管理父子关系自动清理代码示例// 所有继承 QObject 的类必须写 Q_OBJECT class MyClass : public QObject { Q_OBJECT // ← 启用元对象系统信号槽必需 public: explicit MyClass(QObject *parent nullptr) : QObject(parent) {} // ← 传递父对象 };关键 API// 父子关系 QObject *parent obj-parent(); // 获取父对象 obj-setParent(parentWidget); // 设置父对象 QListQObject* children obj-children(); // 获取子对象列表 // 类型判断 obj-metaObject()-className(); // 获取类名 qobject_castQWidget*(obj); // 安全类型转换 // 动态属性 obj-setProperty(age, 25); // 设置属性 int age obj-property(age).toInt(); // 获取属性对象树机制QObject *parent new QObject; QObject *child1 new QObject(parent); // 指定父对象 QObject *child2 new QObject(parent); delete parent; // 自动递归删除 child1 和 child2 // 不需要手动 delete child1 和 child2为什么 QObject 不能拷贝QObject obj1; QObject obj2 obj1; // ❌ 编译错误 // QObject 的拷贝构造函数是私有的 // 因为对象有父子关系拷贝会导致混乱3.事件处理器函数的使用closeEvent resizeEvent快捷键 F1 帮助文档如果在程序中调用了这个事件的ignore方法相当于在当前窗口里边不去处理这个事件这个事件会向上传递给它的父窗口以此类推直到这个事件在最外层这个窗口就被忽略掉了。yes表示同意关闭当前窗口点击关闭窗口弹出消息对话框询问是否关闭如果选择no相当于关闭事件被忽略了如果选择yes则表示关闭事件生效关闭窗口。拖动窗口只要窗口大小发生变化尺寸信息就会实时更新控制台不断打印出当前窗口的尺寸信息4.基于事件处理器函数自定义按钮通过一个窗口去实现一个按钮的功能基于qwidget实现一个按钮类型的控件下为博主的代码void enterEvent(QEvent *ev);需要改为void enterEvent(QEnterEvent *ev);altenter快速切换到源文件QPixmap翻译成中文是“像素图”或“位图”。在 Qt 框架的上下文中更通俗、更准确的理解是“Qt 图像/图片类”。它与QImage的区别非常重要1.QPixmap专为屏幕显示优化。它在底层通常使用与显示硬件显卡更兼容的格式因此绘制在屏幕上速度最快。但是它操作单个像素非常慢。在不同的平台下QPixmap做了不同的优化。2.QImage专为像素级操作优化。如果你需要读取/修改图片的每一个像素比如做滤镜、修图、图像识别一定要用QImage。它的底层是直接访问内存的。QImage主要是进行跨线程的图片传输或者是多线程绘图能够实现像素级别的修改。3.互相转换两者可以无缝转换QImage img pixmap.toImage();或QPixmap pix QPixmap::fromImage(img);。this是绘图设备触发了对应的鼠标事件之后这个窗口并不会被刷新需要手动刷新窗口需要调用update函数想让paintEvent函数执行并不是在函数里去调用paintEvent而是去调用update因此每个鼠标事件函数里都添加update函数表示手动刷新窗口paintEvent就会调用打印出新的图片。如果想让按钮和图片一样大可以在构造函数里进行设定最后我们需要把这个MyButton显示到对应的某一个窗口上因为MyButton的基类是QWidget类型因此想要使用自定义类型的按钮就需要在ui界面拖拽一个widget类型的控件代码头文件mybutton.h#ifndef MYBUTTON_H #define MYBUTTON_H #include QWidget // QWidget 基类 #include QPainter // 绘图类 class MyButton : public QWidget { Q_OBJECT public: explicit MyButton(QWidget *parent nullptr); protected: // ★ 重写的 5 个事件处理函数 void mousePressEvent(QMouseEvent *ev); // 鼠标按下 void mouseReleaseEvent(QMouseEvent *ev); // 鼠标释放 void enterEvent(QEnterEvent *ev); // 鼠标进入 void leaveEvent(QEvent *ev); // 鼠标离开 void paintEvent(QPaintEvent *ev); // 绘制 signals: // 没有定义信号留空 private: QPixmap m_pixmap; // 存储按钮当前显示的图片 }; #endif // MYBUTTON_H源文件mybutton.cpp#include mybutton.h MyButton::MyButton(QWidget *parent) : QWidget{parent} { // 构造函数加载默认图片 m_pixmap.load(:/_20230610180656.jpg); // 从资源文件加载图片 setFixedSize(300, 300); // 固定按钮大小为 300×300 像素 } // // ★ 鼠标按下事件 // void MyButton::mousePressEvent(QMouseEvent *ev) { m_pixmap.load(:/20240615114601.jpg); // 按下时切换为按下状态的图片 update(); // ★ 触发重绘 // update() 会调用 paintEvent() // 不调用 update 的话界面上看不到图片变化 } // // ★ 鼠标释放事件 // void MyButton::mouseReleaseEvent(QMouseEvent *ev) { m_pixmap.load(:/_20230610180656.jpg); // 释放时恢复默认图片 update(); // 触发重绘 } // // ★ 鼠标进入事件 // void MyButton::enterEvent(QEnterEvent *ev) { m_pixmap.load(:/20240615114611.jpg); // 鼠标进入时切换为悬停图片 update(); } // // ★ 鼠标离开事件 // void MyButton::leaveEvent(QEvent *ev) { m_pixmap.load(:/_20230610180656.jpg); // 鼠标离开时恢复默认图片 update(); } // // ★ 绘制事件核心 // void MyButton::paintEvent(QPaintEvent *ev) { QPainter p(this); // 创建画笔绘制到当前控件上 p.drawPixmap(rect(), m_pixmap); // 在控件矩形区域内绘制当前图片 // rect()控件的完整区域300×300 // m_pixmap要绘制的图片 }运行效果初始状态[ 默认图片 ] 鼠标移入[ 悬停图片 ] ← enterEvent ↓ 按下 鼠标按下[ 按下图片 ] ← mousePressEvent ↓ 释放 鼠标释放[ 默认图片 ] ← mouseReleaseEvent ↓ 移出 鼠标移出[ 默认图片 ] ← leaveEvent核心知识点1. 事件处理流程用户操作 → Qt 检测到事件 → 调用对应的事件处理函数 鼠标移入 → enterEvent() 鼠标按下 → mousePressEvent() 鼠标释放 → mouseReleaseEvent() 鼠标移出 → leaveEvent() 需要重绘 → paintEvent()2.update()的作用m_pixmap.load(新图片); update(); // ★ 必须有这行 // 告诉 Qt内容变了需要重新绘制 // Qt 会调用 paintEvent() 重新绘制 // 没有 update() 就不会刷新显示3.paintEvent绘制QPainter p(this); // 画笔 p.drawPixmap(rect(), m_pixmap); // 画图片 // rect() 控件的矩形区域鼠标状态对比状态触发事件显示图片默认构造/释放/离开默认图片悬停enterEvent悬停图片按下mousePressEvent按下图片释放mouseReleaseEvent恢复默认5.自定义按钮添加信号对于标准按钮来说当我们点击之后它就会发射一个叫做clicked的信号但是对自定义按钮来说不论怎么点都不会发射信号因此需要进行设置当点击自定义按钮时也能发射信号。声明一个宏Q_UNUSED里面的参数表示虽然没有用到这个参数但是在编译的时候不要提示相关的警告信息了。还有一个小知识点因为在当前的MyButton里重写了从父类继承过来的一系列虚函数如果子类重写了父类的虚函数父类的虚函数就不能够被执行了假如说我们在子类里还需要执行父类虚函数里的默认的处理操作可以在子类的虚函数里重新再调用父类的虚函数。当前MyButton的父类是QWidget这段代码说明在子类执行完自己的处理动作后又执行了父类里默认定义的一系列的处理动作。要不要加要看具体父类虚函数要看具体情况默认情况下子类重写了父类的虚函数父类的默认处理动作是不需要被再次执行但是如果当前按钮类MyButton继承的是QPushButton或者QToolButton默认的按钮类当它触发了mousePressEvent事件之后会发射clicked信号此时需要在mousePressEvent的最下面调用一下父类的虚函数让父类的默认虚函数再执行一遍信号才能够被发射出去。6.事件的使用-小案例在Qt 中实现“将一个子窗口或弹窗在其父窗口中居中显示”win-move( (width() - win-width()) / 2, (height() - win-height()) / 2 );
返回列表