百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

Qt入门阶段之事件 qt进入事件怎么写

liebian365 2024-10-20 09:54 23 浏览 0 评论

本小节内容为《QTCreator快速入门》的事件笔记。包含事件关系图、鼠标、键盘、定时器等事件以及事件过滤器、事件的发送等。

1 基础概念

1.1 事件关系图

1.2 事件

  1. 事件对象:每个事件都会包装成一个QEvent对象,然后传递到对应部件。
  2. 在Qt中,任何QObject子实例都可以接受和处理事件。意思就是,每个QObject部件都可以对事件进行拦截和处理。
  3. 处理事件的5种方法:

上述5种处理事件的方法,实际上对应着在事件传递的不同阶段进行拦截处理的过程。
QApplication类最后执行exec()进入事件循环,而后监听事件的发生。一旦有事件发生,Qt便会构建一个相应QEvent子类对象来表示它,将其传递给相应的QObject对象或者其子对象。
上述处理事件的5种方法中,notify相关的两种方法相当于是顶级处理,事件刚一发生就直接被QApplication捕获,从而使用notify处理(对应方法2和方法3)。如果前面的不拦截,事件会经过父部件达到子部件,父部件可以选择拦截(FilterEvent参数有两个分别是子对象以及事件类型),如果父部件不拦截,则到达子部件,子部件可以使用event()判断是哪种类型的event而后处理,如果不使用event,那么接下来就能够使用对应的某种事件的处理函数如KeyPressEvent(),如果子部件没有处理,那么事件的处理权就传递到了父部件。事件发生后,事件先传递给焦点部件,传递过程中可使用notify或者父部件进行拦截,传递到焦点部件后可选择处理,如果不处理则事件处理权转交到父部件。

Qt资料领取(视频教程+文档+代码+项目实战)

1.3 事件的传递

事件是先传递给指定窗口部件的,就是说先传递给获得焦点的窗口部件,但是如果该部件忽略掉该事件,那么这个事件就会传递给这个部件的父部件。重新实现事件处理函数时,一般要调用父类的相应事件处理函数来实现默认操作。

任何部件都可以用事件处理函数如KeyPressEvent(),此外,子部件使用bool event(QEvent),父部件使用bool eventFilter(QObject, QEvent*),并将在父部件的构造函数内安装子部件的过滤器。不要忘记重新调用父类的操作函数。

2 鼠标总结

mousePressEvent
mouseReleaseEvent
mouseDoubleClickEvent
mouseMoveEvent
wheelEvent

需要总结的是光标的设定、鼠标的移动处理、滑轮的滑动以及事件的判定。

2.1 光标的设定

光标需要初始设定,可以在窗口的构造函数中设定:

QCursor cursor;
//QCursor cursor(QPixmap(":/image/images/logo.png"))// 初始化时使用图片,引用的是资源中的图片,路径开始要用冒号:
cursor.setShap();//设定形状
setCursor(cursor);为当前的窗口设定光标的形状

光标临时设定于恢复函数,要设定为栈上的临时变量,并使用QApplication::setOverrideCursor(cursor)来暂时修改光标的形状,使用QApplication::restoreOverrideCursor()来恢复光标的形状。

2.2 鼠标的移动处理

通过按下左键并移动鼠标来拖动窗口,需要计算窗口移动后的位置来设定move。
移动后的位置=鼠标当前位置-鼠标与窗口的相对位置;此相对位置在鼠标每次点击左键的时候需要不断更新。
offset = event->globalPos()-pos(); 鼠标全局位置-窗口全局位置 = 相对距离
widgetNewPos = event->globalPos() - offset;鼠标新位置-相对距离 = 窗口新位置

2.3 滑轮的滑动

滑轮滑动事件event提供delta属性,event->delta()>0表示往前滑动,否则往后滑动。
textEdit的ZoomIn表示放大,Zoomout表示缩小。

2.4 事件的判定

鼠标点击通过event->button()==Qt::LeftButton判断是否是左键按下
鼠标的移动就需要通过当下鼠标的状态来判断是左键按下鼠标移动还是其他,通过event->buttons() & Qt::LeftButton

3 键盘总结

此处键盘的笔记包含通过事件判断按键(辅助按键、普通按键)、持续按键响应以及避免、两个普通按键组合执行动作。

3.1 键盘函数总结

事件相关函数

功能

void keyPressEvent(QKeyEvent* event)

按键按下事件

void keyReleaseEvent(QKeyEvent* event)

按键释放事件

按键相关函数

功能

event->key()

通过事件获取普通按键

event->modifiers()

通过事件获取辅助按键,Shift、Ctrl等

event->isAutoReapeat()

是否处于持续按下状态

3.2 持续按键响应

在按键事件中(Press、Release),常常使用单个按键或者组合按键,组合按键常常是辅助按键和普通按键的组合,这种情况只需要在判断一个按键的基础上,再判断另外一个,因为各自有各自的检测函数。
但是如果是两个普通按键的组合,常常需要状态变量作为标志,同时还要防止持续按下带来的响应问题。当持续按下时,Press和Release事件都会响应,
event->isAutoRepeat()函数就是检测是否处于持续响应的状态。

  • event->isAutoRepeat()
    在按下时,在Press事件中此函数对第一次按下动作返回false,而对后续的持续按下返回true;在释放时,在Release中此函数对持续按下返回true,而对最后释放的动作返回false。
    因此我们可以利用此函数来捕获第一次按下的动作,以及最后释放的动作,而将持续按下的Press和Release的响应忽略掉。

3.3 实例

此实例是为了实现以下功能:按左键,按钮左移;按上键,按钮上移;按下键,按钮下移;同时按左上键,左上移。
在此实例中,第一点是使用
event->isAutoRepeat()来消除持续按键响应,第二点是使用标志来记录两个普通按键的状态,第三点是时时刻刻想着只有释放的时候才会动作。


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    move = false;
    keyup = false;
    keyleft = false;
    ui->pushButton->move(400, 200);
    setFocus();
}

void Widget::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Up)
    {
        if(event->isAutoRepeat())return;
        keyup = true;
    }
    else if(event->key() == Qt::Key_Left)
    {
        if(event->isAutoRepeat()) return;
        keyleft = true;
    }
}

void Widget::keyReleaseEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Up)
    {
        if(event->isAutoRepeat()) return;
        keyup = false;
        if(move)//已经释放过了,按钮已经移动过了
        {
            move = false;
            return;
        }
        if(keyleft)
        {
            //最后一次释放,还没有移动过,并且左键已经按下了,说明现在需要斜方向移动
            ui->pushButton->move(100, 50);
            move = true;
        }
        else
        {
            ui->pushButton->move(400, 50);
        }

    }else if(event->key() == Qt::Key_Left)
    {
        if(event->isAutoRepeat()) return;//一直在持续按键,但是我需要捕捉释放的最后一次响应
        keyleft = false;
        if(move)
        {
            move = false;
            return;
        }
        if(keyup)
        {
            ui->pushButton->move(100, 50);
            move = true;
        }
        else
        {
            ui->pushButton->move(100, 200);
        }
    }else if(event->key() == Qt::Key_Down)
    {
        ui->pushButton->move(400, 200);
    }
}

/*
1. 按下去、再释放才能够执行动作
2. 释放时要判断是否是单独按下,还是已经有配合按键已经按下
3. 对于配合键:当持续按下一个键Key1的时候,press和release的Key1判断都会响应,因此应该将持续响应屏蔽掉。按下时只对第一次按下有作用,
释放时,应该忽略掉持续按下导致的响应。也就是说,我们应该捕捉按下的第一次动作,后续的都不算,应该捕捉释放的第一个动作,前面的都不算。
4. event->isAutoRepeat()函数能够起到作用。在第一次按下时press为false,在持续按下的时候press和release都是true,而释放时,release为false。
因此可以利用此函数来忽略持续响应导致的问题。
5. 两个配合按键组合成一个动作,考虑先后释放问题。哪个先释放就会响应,那么后释放的就不能再次导致响应了,因此需要加入判别标志move。第一个按键释放后,
move设置为true,第二个按键释放时发现move为true,直接返回,不再响应。

*/

3.4 注意

在构造函数中,必须使用setFocus(),暂时不知道什么用,但是还得必须用。暂时放在这里。

4 定时器事件

可以直接使用QObject的startTimer,与timerEvent关联起来。但是编程时更加常用的是QTimer类,其能够与信号、槽等创建联系,使用更加灵活。

4.1 简单的定时器

  • 开启定时器:int QObject::startTimer(int interval)
    QObject可开启定时器,输入为毫秒,返回定时器编号。定时器溢出可在
    timerEvent()函数中操作,在timerEvent()函数中使用event->timerId()来获取溢出的定时器ID。
    startTimer以interval为周期不断重复触发事件。
  • 定时器事件:void timerEvent(QTimerEvent* event)
  • id1 = startTimer(1000); void Widget::timerEvent(QTimerEvent *event) { if(event->timerId() == id1) { qDebug() << "id1" <<endl; } }

4.2 QTimer类

实现一个定时器,提供了更高层次的接口,如信号和槽,还可以设置只运行一次的定时器。QTimer定时器达到定时时间后,就调用槽函数,执行相关操作。

  • 创建定时器:QTimer* timer = new QTimer(this);
  • 开启定时器:timer->start(1000);
  • 停止定时器:timer->stop();
  • connect槽函数:connect(timer, &QTimer::timeout, this, &Widget::updateTimer);
  • 获取当前时间并转换为字符串:QTime::currentTime().toString("格式")
  • 创建只执行一次的定时器:QTimer::singleShot(5000, this, &Widget::close);,此定时器能够与槽函数连接起来
//将当前时间显示在LCD中,每隔间隔一秒闪烁
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    timer = new QTimer(this);//创建QTimer对象,并建立QTimer定时器溢出与槽函数的关系
    connect(timer, &QTimer::timeout, this, &Widget::updateTimer);
    timer->start(1000);//开启定时器
}

void Widget::updateTimer()
{
    QTime time = QTime::currentTime(); //获取当前时间,并转换为字符串
    QString text = time.toString("hh:mm");
    if(time.second()%2 == 0) text[2] = ' ';//更改中间的:,每隔一秒就闪烁一下
    ui->lcdNumber->display(text);//在lcd上展示

    if(++count>= 5)//count记录已过秒数,超过5秒就停止
    {
        timer->stop();
    }   
}
//只执行一次的定时器,5秒后关闭窗口
QTimer::singleShot(5000, this, &Widget::close);

5 事件过滤器

在刚开始就讨论这这个事件过滤器的主要作用,此处更加详细的记录并使用一下。

事件过滤器实现的是在一个部件中监控其他子部件事件,因此当事件发生触发事件过滤器时,要判断对象以及事件类型。

5.1 事件过滤器的函数形式

  • 先在父部件上安装对子部件的监控:ui->textEdit->installEventFilter(this);
  • 后实现事件过滤器:bool eventFilter(QObject* obj, QEvent*event)

5.2 事件过滤器的实现

步骤:

  1. 判断对象:obj==ui->textEdit
  2. 判断事件:event->type()==QEvent::WheelEvent此处注意的是,事件类型定义在QEvent
  3. 强制类型转换:QWheelEvent* wheelEvent = static_cast<QWheelEvent>(event)
  4. 处理对应的事件并返回bool:若不需要事件继续传递则return true,否则返回false
  5. 父部件对事件处理:如果obj与子部件没有匹配,则返回父部件事件过滤器的处理结果return eventFilter(obj, event)。注意此处实现的事件过滤器是重写了父类的事件过滤器。
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->textEdit->installEventFilter(this);
    ui->spinBox->installEventFilter(this);
}

Widget::~Widget()
{
    delete ui;
}

bool Widget::eventFilter(QObject *obj, QEvent *event)
{
    if(obj == ui->textEdit)//判断是textEdit
    {
        if(event->type() == QEvent::Wheel)//判断事件类型QEvent
        {
            QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);//强制类型转换
            if(wheelEvent->delta()>0) ui->textEdit->zoomIn();
            else ui->textEdit->zoomOut();
            return true;
        }
        else
        {
            return false;
        }
    }
    else if(obj == ui->spinBox)
    {
        if(event->type() == QEvent::KeyRelease)
        {
            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
            if(keyEvent->key() == Qt::Key_Space)
            {
                ui->spinBox->setValue(0);
            }
            return true;
        }
        else
        {
            return false;
        }
    }
    else return QWidget::eventFilter(obj, event);
}

6 发送事件

发送事件可通过QApplication的静态函数postEvent和sendEvent实现,这两个函数能够将自定义的事件发送给指定部件。

6.1 发送事件函数

static void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
static bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)

两个事件函数都能够实现发送事件,但是区别在于:

  1. sendEvnet发送的事件会立即执行,而postEvent发送的事件会暂时放到队列中。从名字可以看出。
  2. sendEvent的事件不会自动删除,因此需要在栈上创建,而postEvent的事件放在队列中,队列会自动将其删除,因此可以在堆上创建。
  3. postEvent由于暂时放在队列中,因此可以设置优先级

6.2sendEvent的使用

指定的事件如QEvent::KeyEvent、指定事件的具体动作Qt::Key_Up

  1. 创建按键事件,并设置为Key_Up动作:QKeyEvent keyevent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
  2. 发送事件:QApplication::sendEvent(ui->spinBox, &keyevent);

下面的代码是向spinBox发送按键事件,按键动作为向上长。另外创建一个1s周期的QTimer定时器,定时器溢出时执行槽函数,槽函数中发送事件。最终达到的效果是spinBox数字每秒都会+1.

	Widget::Widget(QWidget *parent)
		: QWidget(parent)
		, ui(new Ui::Widget)
	{
		ui->setupUi(this);
		QTimer* timer = new QTimer(this);
		connect(timer, &QTimer::timeout, this, &Widget::updateSpinBox);
		timer->start(1000);
	}

	Widget::~Widget()
	{
		delete ui;
	}

	void Widget::updateSpinBox()
	{
		QKeyEvent keyevent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
		QApplication::sendEvent(ui->spinBox, &keyevent);
	}

文章转自博客园(YueLiGo):https://www.cnblogs.com/wsw2022/p/16526912.html

Qt资料领取→「链接」

相关推荐

看黑客是如何获取你电脑最高权限的,一定要看

在渗透过程中,通过各种方式获取到一枚cmdshell,但是这个shell的权限比较低,无法让我们做我们想要做的一些操作,比如说获取系统密码,获取数据库信息,又或者比如说拿到服务器中的另一个站点的权限,...

是50个常用的Visual Basic代码示例:

以下是50个常用的VisualBasic代码示例:1.声明变量```vb...

电脑系统型号怎么看版本(如何看电脑系统型号)

有时候我们会需要进行查看电脑上安装的windows系统版本及系统版本号,但对于不懂电脑知识的小白来说要怎么查看电脑系统版本信息呢?别着急,有小编在接下来,就将查看电脑系统版本的教程来分享给你们,希望对...

dos命令systeminfo,查看系统启动时间。电脑卡慢,win10怎么了?

最近一段时间,有几个反应电脑卡慢的,都是windows10的系统。询问得知每天电脑有关机,打开任务管理器,内存使用量达到百分之九十多,而程序只打开微信、wps、360浏览器。cmd窗口运行命令syst...

systeminfo命令:全面解析系统信息!

你是否曾想过,仅凭一条简单的命令,就能深入了解计算机的"内心世界"?是不是有点不可思议?那么,让我们一起探寻这个神奇的命令,揭开它背后的奥秘吧!它能提供的信息超乎你的想象,从操作系统到硬件配置,再到驱...

电脑序列号怎么查询?只需两行命令一键查询

当我们的电脑出问题需要保修的时候,需要查询到电脑的型号和序列号才更便于进行下一步的操作,有包装盒的朋友还可以在包装盒上查询,笔记本用户可以在电脑底部标签上查询,没有包装盒和标签破损的用户就无从下手了。...

快速显示系统信息:Systeminfo命令详解

Systeminfo命令是windows系统中显示系统信息的命令,此命令可以显示出计算机的操作系统的详细配置信息,包括操作系统配置、安全信息、产品ID和硬件属性(如RAM、磁盘空间和网卡)。使用...

dos命令systeminfo图文教程,显示操作系统配置信息msinfo32

大家好,我是老盖,首先感谢观看本文,本篇文章做的有视频,视频讲述的比较详细,也可以看我发布的视频。今天我们学习systeminfo命令,该工具显示本地或远程机器(包括服务包级别)的操作系统配置的信息,...

基于uniapp+vue3跨端仿制chatgpt实例uniapp-chatgpt

#夏日生活打卡季#...

原创新作uniapp+vue3+pinia2高仿微信App聊天

前段时间有给大家分享一个flutter3.x桌面端os系统。今天再分享一款最新原创之作uniapp-vue3-wechat聊天实例。uni-vue3-wechat采用...

UniApp开发的设备适配(uniapp服务器配置)

UniApp是一个跨平台开发框架,支持多端应用(如H5、小程序、iOS、Android等)。由于不同设备的屏幕尺寸、分辨率、操作系统等存在差异,设备适配是开发过程中需要重点关注的问题。以下是Uni...

如何用服务器搭建自己的个人网站(自己服务器怎么做网站)

这篇教程主要是告诉大家如何利用TCP和HTTP协议来完成网站的搭建。首先你需要有C/C++语言基础,且有服务器、客户端概念,如果你了解TCP或者HTTP协议的话,那么将会帮助你更快的学会如何搭建个人网...

大话C语言:字符数组(c语言字符数组教学视频)

1字符数组概述C语言中没有字符串这种数据类型,可以通过char的数组来替代。数字0(和字符'\0'等价)结尾的char数组就是一个字符串,字符串是一种特殊的char的数组。...

源码分享:在pdf上加盖电子签章(pdf怎么加电子签章)

在pdf上加盖电子签章,并不是只是加个印章图片,。而是要使用一对密钥中的私钥对文件进行签字。为啥要用私钥呢?很简单,因为公钥是公开的,其他人才可以用公钥为你证明,这个文件是你签的。这就是我们常说的:私...

C语言wcstombs函数详解:宽字符字符串到多字节的「翻译官」

核心定位wcstombs是C语言中用于将宽字符字符串转换为多字节字符串的「翻译官」,它能将宽字符(wchar_t)转换为多字节字符(如UTF-8编码的中文)。就像一位翻译官,它能将一种语言(宽字符...

取消回复欢迎 发表评论: