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

Qt事件系统 day7

Qt事件系统 day7

事件系统

  • 在Qt中,事件是派生自抽象QEvent类的对象,它表示应用程序内发生的事情或应用程序需要知道的外部活动的结果。事件可以由QObject子类的任何实例接收和处理,但它们与小部件尤其相关。
  • Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件,当事件发生时,Qt将创建一个事件对象
  • 当事件发生时,Qt通过构造适当的QEvent子类的实例来创建一个事件对象来表示它,并通过调用它的event()函数将它交付给QObject的一个特定实例(或它的一个子类)。
  • 这个函数event()不处理事件本身;根据交付的事件类型,它为该特定类型的事件调用事件处理程序,并根据事件是被接受还是被忽略发送响应。
  • 一些事件,如QMouseEvent和QKeyEvent,来自窗口系统;还有一些,比如QTimerEvent,来自其他来源;有些来自应用程序本身。

事件处理

  • 传递事件的通常方式是调用虚函数
    在这里插入图片描述
  • 自定义处理了按钮控件左键单击,想要让父类也能够处理左键消息,可以把父类的mousePressEvent放在最后调用(不放在else中)
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
class Button :public QPushButton
{
public:Button(QWidget* parent = nullptr) :QPushButton(parent){}
protected://重写了父类的虚函数,也就是不再使用父类的实现,由自己来实现//但是按钮的点击信号,是在mousePressEvent函数里面触发的void mousePressEvent(QMouseEvent* ev) override{if (ev->button() == Qt::MouseButton::LeftButton){qDebug() << "按下小瓜";}//所以写完实现后,剩下的要交给父类处理QPushButton::mousePressEvent(ev);}
};
class Widget :public QWidget
{
public:Widget(QWidget* parent = nullptr) :QWidget(parent){auto btn = new Button(this);btn->setText("小瓜");connect(btn, &Button::clicked,this, [](){qDebug() << "小瓜瓜";});}//处理鼠标点击事件,重写虚函数即可void mousePressEvent(QMouseEvent* ev) override{//判断那个键按下if (ev->button() == Qt::MouseButton::LeftButton){//pos是程序的客户区坐标,globalPos是基于整个屏幕所在的坐标位qDebug() << "leftButton Press" << ev->pos() << ev->globalPos();}}};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

鼠标事件

  • 鼠标按下
void CustomButton::mousePressEvent(QMouseEvent* ev)override
{//获取鼠标按键 类型为枚举:Qt::MouseButton::qDebug() << ev->button();//如果同时有多个鼠标按键按下,需要判断左键是否按下qDebug() << (ev->buttons() & Qt::MouseButton::LeftButton);//获取鼠标坐标 position()返回浮点坐标,可以使用pos()获取整型坐标qDebug() << ev->position();			//鼠标在本控件上的坐标qDebug() << ev->scenePosition();		//鼠标在本控件所在的窗口位置qDebug() << ev->globalPosition();	//鼠标相对于屏幕的坐标//将其他按键传递给基类处理QPushButton::mousePressEvent(ev);
}
  • 鼠标释放
void CustomButton::mouseReleaseEvent(QMouseEvent*ev)override
{QPushButton::mouseReleaseEvent(ev);
}
  • 鼠标双击
void CustomButton::mouseDoubleClickEvent(QMouseEvent*ev)override
{qDebug() << "小瓜";
}
  • 鼠标移动
  • 如果关闭了鼠标跟踪,则只有在移动鼠标时按下鼠标按钮时才会发生鼠标移动事件。如果打开了鼠标跟踪,即使没有按下鼠标按钮,也会发生鼠标移动事件。按钮是自动追踪鼠标的,但是QWidget是不会的,如果要给QWidget的子类重写鼠标移动,需要使用 void setMouseTracking(bool enable)启用鼠标追踪。
void CustomButton::mouseMoveEvent(QMouseEvent* ev)override
{qDebug() << "鼠标移动的坐标位" << ev->pos();
}
  • 鼠标滚轮
  • 返回轮子旋转的相对量,单位为八分之一度。正值表示转轮向前旋转,远离用户;负值表示转轮向后向用户旋转。angleDelta().y()提供自上一个事件以来旋转普通垂直鼠标滚轮的角度。如果鼠标有水平滚轮,angleDelta().x()提供水平鼠标滚轮旋转的角度,否则就是0。有些鼠标允许用户倾斜滚轮来进行水平滚动,有些触摸板支持水平滚动手势;它也会出现在angleDelta().x()中。
  • 大多数鼠标类型的工作步长为15度,在这种情况下,delta值是120的倍数;即120单位* 1/8 = 15度。
  • 然而,有些鼠标的滚轮分辨率更高,发送的delta值小于120单位(小于15度)。为了支持这种可能性,可以累计添加来自事件的增量值,直到达到120的值,然后滚动小部件,或者可以部分滚动小部件以响应每个轮事件
void CustomButton::wheelEvent(QWheelEvent* ev)override
{//获取滚轮滚动方向QPoint numDegrees = ev->angleDelta();qDebug() <<"水平:" << numDegrees.x()/8 <<"垂直:" << numDegrees.y()/8;
}
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
class Button :public QPushButton
{
public:Button(QWidget* parent = nullptr) :QPushButton(parent){}
protected://重写了父类的虚函数,也就是不再使用父类的实现,由自己来实现//但是按钮的点击信号,是在mousePressEvent函数里面触发的void mousePressEvent(QMouseEvent* ev) override{if (ev->button() == Qt::MouseButton::LeftButton){qDebug() << "按下小瓜";}//所以写完实现后,剩下的要交给父类处理QPushButton::mousePressEvent(ev);}
};
class Widget :public QWidget
{
public:Widget(QWidget* parent = nullptr) :QWidget(parent){auto btn = new Button(this);btn->setText("小瓜");connect(btn, &Button::clicked,this, [](){qDebug() << "小瓜瓜";});//设置鼠标追踪,不需要的情况就不要用,会降低效率//setMouseTracking(true);}//处理鼠标点击事件,重写虚函数即可void mousePressEvent(QMouseEvent* ev) override{//判断那个键按下if (ev->button() == Qt::MouseButton::LeftButton){//pos是程序的客户区坐标,globalPos是基于整个屏幕所在的坐标位qDebug() << "leftButton Press" << ev->pos() << ev->globalPos();isPress = true;}}//鼠标放开void mouseReleaseEvent(QMouseEvent* ev) override{if (ev->button() == Qt::MouseButton::LeftButton){isPress = false;}}//移动鼠标void mouseMoveEvent(QMouseEvent* ev) override{//鼠标左键按下,并且移动了鼠标,实现这个要开启鼠标追踪if (isPress){qDebug() << "左键按下,也移动的鼠标";}//buttons判断鼠标按下后还做了别的事情if(ev->buttons() & Qt::MouseButton::RightButton){qDebug() << "移动了鼠标并且按了右击";}}//双击void mouseDoubleClickEvent(QMouseEvent* ev) override{qDebug() << "双击";}//滚轮void wheelEvent(QWheelEvent* ev) override{//判断滚轮的方向qDebug() << ev->angleDelta().y() << ev->angleDelta().x();}
private:bool isPress = false;};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

按键事件

在这里插入图片描述

在这里插入图片描述

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>class Widget :public QWidget
{
public:Widget(QWidget* parent = nullptr) :QWidget(parent){//遍历Qt中的所有快捷键for (size_t i = 0; i < 70; i++){qDebug() << i << "-----" << QKeySequence::StandardKey(i) << "------"<< QKeySequence::keyBindings(QKeySequence::StandardKey(i));}}//按键事件void keyPressEvent(QKeyEvent* ev) override{//当前是什么键按下qDebug() << Qt::Key(ev->key());//描述键 ctrl alt shift键等等//ctrl+Aif (ev->modifiers() & Qt::KeyboardModifier::ControlModifier && ev->key() == Qt::Key_A){qDebug() << "全选";}//matches:匹配if (ev->matches(QKeySequence::StandardKey::Save)){qDebug() << "保存";}}//按键放开事件void keyReleaseEvent(QKeyEvent* ev) override{}
private:
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

窗口关闭事件、大小改变、其他事件处理

  • 窗口关闭
    • 当Qt从窗口系统接收到一个顶级小部件的窗口关闭请求时,将用给定的事件调用此事件处理程序。
    • 默认情况下,接受事件并关闭小部件。您可以重新实现此函数,以更改小部件响应窗口关闭请求的方式。例如,您可以通过在所有事件上调用ignore()来防止窗口关闭。
    • 主窗口应用程序通常使用该函数的重新实现来检查用户的工作是否已保存,并在关闭前请求权限。
void Widget::closeEvent(QCloseEvent* ev)override
{auto ret = QMessageBox::question(this, "温馨提示", "你有未保存的操作,是否保存并关闭?");if (ret == QMessageBox::StandardButton::Yes){//保存并关闭ev->accept();//ev->setAccepted(true);}else{//不保存也不关闭ev->ignore();//ev->setAccepted(false);}
}
  • 窗口隐藏、显示
    • 除隐藏和显示窗口外,窗口最小化会发送窗口隐藏事件,正常显示会发送窗口显示事件。
void Widget::showEvent(QShowEvent* ev)override
{qInfo() << "我显示啦~";
}
void Widget::hideEvent(QHideEvent* ev)override
{qInfo() << "我隐藏啦~";
}
  • 窗口移动
void Widget::moveEvent(QMoveEvent* ev)override
{qInfo() << "Widget moved" << "oldPos" << ev->oldPos() << "newPos" << ev->pos();
}
  • 窗口大小改变
void  Widget::resizeEvent(QResizeEvent* ev)override
{qInfo() << "Widget SizeChanged" << "oldSize" << ev->oldSize() << "newSize" << ev->size();
}
  • 程序状态改变
    • 如果需要检测程序中,某些东西是否发生了改变,可以通过void QWidget::changeEvent(QEvent *event)来检测。
  • 以下是常用事件
    • QEvent::FontChange
    • QEvent::WindowTitleChange
    • QEvent::IconTextChange
    • QEvent::ModifiedChange
    • QEvent::MouseTrackingChange
    • QEvent::WindowStateChange
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
#include <QMessageBox>
class Widget :public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr) :QWidget(parent), btn(new QPushButton("更改标题", this)){resize(400, 400);btn->setFixedSize(100, 100);connect(btn, &QPushButton::clicked, this, [=](){setWindowTitle("小瓜瓜");});}//窗口关闭事件void closeEvent(QCloseEvent* ev) override{auto ret = QMessageBox::question(this, "关闭窗口", "是否保存");if (ret == QMessageBox::StandardButton::Yes){//接收事件ev->accept();}else{//忽略事件ev->ignore();}}//当窗口大小改变的时候,会调用void  resizeEvent(QResizeEvent* ev) override{//这样窗口大小改变的时候,按钮也会跟着变btn->move(ev->size().width() - btn->width(), 0);}void changeEvent(QEvent* ev) override{switch (ev->type()){case QEvent::Type::WindowTitleChange:qDebug() << "改变标题" << this->windowTitle(); break;}}
private:QPushButton* btn{};
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

定时器

  • 有两种定时器
  • 定时器:周期性处理,因为在Qt中不能写死循环,不然会导致主程序的阻塞
  • 第一种是QTimer
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
#include <QMessageBox>
#include <QTimer>
class Widget :public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr) :QWidget(parent) {//两种定时器//1.QTimerauto timer = new QTimer(this);//最简单的调用,使用lambda表达式/*timer->callOnTimeout([](){qDebug() << "upData";});*/timer->callOnTimeout(this, &Widget::game_upData);//发送信号到槽函数connect(timer, &QTimer::timeout, this, &Widget::game_upData);//开启定时器,60帧速度timer->start(1000/60);//QTimer的静态成员,这个示例在1000毫秒后,只会触发一次QTimer::singleShot(1000, [](){qDebug() << "只触发一次";});}void game_upData(){qDebug() << __FUNCTION__;}
private:};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述
  • 第二种定时器事件,只要在QOBject的子类里面,就可以重写定时器事件
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
#include <QMessageBox>
#include <QTimer>
class Widget :public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr) :QWidget(parent){//开启定时器timer_idOne = startTimer(500);timer_idTow = startTimer(200);}//定时器事件void timerEvent(QTimerEvent* ev) override{static int i = 0;//当i为6的时候杀死定时器if (i == 6){killTimer(timer_idOne);killTimer(timer_idTow);//killTimer(ev->timerId());}if (ev->timerId() == timer_idOne){qDebug() << timer_idOne;}else if (ev->timerId() == timer_idTow){qDebug() << timer_idTow;}i++;}private://timer的id接收变量int timer_idOne;int timer_idTow;
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述

自定义事件的发送与处理

事件分发函数

  • 传递事件通常方式是调用虚函数,如果在虚函实现中不执行必要的工作,则可能需要调用基类的实现
  • 如果希望替换基类的事件处理函数,则必须自己实现所有的内容,实现自己所需的功能后,可以调用基类来获得不想处理的任何情况的默认行为
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
#include <QMessageBox>
#include <QTimer>
class Widget :public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr) :QWidget(parent){}//所有的事件处理函数都是从event()事件派发函数调用的bool event(QEvent* ev) override{switch (ev->type()){//实现自己需要的事件操作case QEvent::MouseButtonPress:mousePressEvent(dynamic_cast<QMouseEvent*>(ev)); break;default:break;}//最后交给父类去处理return QWidget::event(ev);}void mousePressEvent(QMouseEvent* ev) override{qDebug() << ev->button();}
private:};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述
发送事件
  • 自定义事件创建与发送,通过构造合适的事件对象并进行使用
  • sendEvent():立即处理事件。当它返回时,事件过滤器或对象本身已经处理了该事件,对于许多事件类。都有一个名为isAccept()的函数,它会告诉事件是被最后一个调用的处理程序接受还是拒绝
  • postEvent():将事件发送到队列中,以便进行分派。它会分发所有发布的事件。
  • 用户自定义的类型必须要在这个区间里
    在这里插入图片描述
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>
#include <QMessageBox>
#include <QTimer>
//自定义事件
class CustomEvent :public QEvent
{
public:enum Type { custom = QEvent::User };CustomEvent(const QString& data) :QEvent(static_cast<QEvent::Type>(custom)),m_data(data){}//析构函数~CustomEvent(){qDebug() << __FUNCTION__;}//提供接口QString data() const{return m_data;}
protected:QString m_data;
};
class Widget :public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr) :QWidget(parent){}void mousePressEvent(QMouseEvent* ev) override{if (ev->button() == Qt::RightButton){//向指定的对象发送事件//1.sendEvent 发送栈区的事件,直到事件处理或完成之后,才会返回(表明sendEvent函数是阻塞的)//如果申请内存放在堆区就不会自动释放,需要自己所手动释放//CustomEvent ev("小瓜 sendEvetn customEvent");//QApplication::sendEvent(this, &ev);//2.postEvent 只能发送堆区的对象。一旦发送,直接返回,不需要等事件处理完成,一旦事件处理完成会自动释放CustomEvent* ev = new CustomEvent("小瓜瓜");QApplication::postEvent(this, ev);}}//处理自定义事件函数void customEvent(QEvent* ev) override{if (ev->type() == CustomEvent::custom){auto cev = dynamic_cast<CustomEvent*>(ev);if (cev){qDebug() <<  cev->data();}}}
private:
};int main(int argc, char* argv[])
{QApplication a(argc, argv);Widget w;w.setWindowIcon(QIcon(":/Resource/tubiao.ico"));w.show();return a.exec();
}
#include "main.moc"
  • 运行结果
    在这里插入图片描述
http://www.lryc.cn/news/196618.html

相关文章:

  • 微服务拆分的思考
  • DateUtil工具类记录
  • 可信执行环境简介:ARM 的 TrustZone
  • 【音视频流媒体】 3、ffmpeg、ffplay、ffprobe 超详细介绍
  • 解决kong部署自定义插件报 helloworld plugin is enabled but not installed
  • 动态数据源自定义SqlSessionFactoryBean时mybatis plus配置失效
  • 【Qt控件之QDialogButtonBox】概述及使用
  • IPv6知识概述 - ND协议
  • react-redux的connect函数实现
  • Vue3使用Vite创建项目
  • NCV7724DQBR2G车规级半桥电机驱动芯片-专为汽车,工业自动化应用提供完美解决方案
  • NSS [GWCTF 2019]枯燥的抽奖
  • 微信小程序会议OA系统
  • CICD:Circle CI 实现CICD
  • 竞赛 深度学习YOLO安检管制物品识别与检测 - python opencv
  • 【华为OD机试python】斗地主之顺子【2023 B卷|100分】
  • ant design DatePicker禁用之前的时间
  • C语言---预处理详解
  • 数组和对象有什么区别?
  • 顺序表(第二节)实现和解析
  • Hadoop3教程(二十一):MapReduce中的压缩
  • 04、RocketMQ -- 核心基础使用
  • mysql中date/datetime类型自动转go的时间类型time.Time
  • MATLAB算法实战应用案例精讲-【图像处理】机器视觉(基础篇)
  • LDAP协议工作原理
  • 【Jetpack Compose】BOM是什么?
  • 多域名SSL数字证书是什么呢
  • 杭电oj--求奇数的乘积
  • E053-web安全应用-Brute force暴力破解初级
  • 外汇天眼;VT Markets 赞助玛莎拉蒂MSG Racing电动方程式世界锦标赛