事件处-  Event Processingn

事件是视窗系统或者Qt 本身在各种不同的情况下产生的。当用户点击或者释放鼠标,键盘时,一个鼠标事件或者键盘事件就产生了。当窗口第一次显示时,一个绘制事件会产生告诉新可见的窗口绘制自己。很多事件是为了相应用户动作产生的,也有一些事件是由 系统独立产生的。 在用Qt 编程时,我们很少要考虑事件,当一些事件发生时,Qt 控件会发出相应的信号。只有当实现用户控件或者需要修改现有控件的行为时,我们才需要考虑事件。事件不能和信号混淆。一般来讲,在使用控件时需要处理的是信号,在实现一个控件时需要处理事件。例如,我们使用QPushButton 时,我们只要clicked()信号就可以了,而不用管鼠标点击事件。但是如果我们实现一个像 QPushButton 这样的类,我们就需要处理鼠标或者键盘事件 ,发出clicked()信号。

重写事件处理函数(Reimplementing Event Handlers

在 Qt 中,一个事件是 QEvent 的子类的对象。Qt 能够处理上百种类型的事件,每一类型的事件由一个枚举值确定。例如,对鼠标点击事件,QEvent::type()返回的值为 QEvent::MouseButtonPress。

很多情况下,一个 QEvent 对象不能保存有关事件的所有信息,例如,鼠标点击事件需要保存是左键还是右键触发了这个信息,还要知道事件发生时鼠标指针的位置,这些额外的信息储存在 QEvent 的子类QMouseEvent 中。

Qt 的对象通过QObject::event()得到有关事件的信息。QWidget::event()提供了很多普通类型的信息,实现了很多事件处理函数,例如 mousePressEvent(),keyPressEvent(),paintEvent()等等。

在前面的章节中,我们已经在MainWindow 类,IconEditor 类,Plotter 类中看到了很多事件处理函数,在QEvent 参考文档中,还列举了很多类型的事件。

我们还可以定义自己的事件,把事件分派出去。这里,我们讨论一下两种最常用的事件:键盘事件和时间事件。

重写函数 keyPressEvent()和keyReleaseEvent()可以处理键盘事件。 Plotter 控件就重写了 keyPressEvent()函数。通常,我们只需要重写 keyPressEvent(),需要处理键盘释放事件的只有修改键(Ctrl, Shift, Alt),而这些键的信息可以通过 QKeyEvent::modifiers()得到。例如,如果我们重写了控件 CodeEditor 控件的 KeyPressEvent()函数,区分Home 键和 Ctrl+Home 键:

void CodeEditor::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) { case Qt::Key_Home:
if (event->modifiers() & Qt::ControlModifier) { goToBeginningOfDocument();
} else {
goToBeginningOfLine();
}
break;
case Qt::Key_End:
...
default:
QWidget::keyPressEvent(event);
}
}

Tab 键和Backtab(Shift+Tab)键很特殊,它们是在控件调用 keyPressEvent()之前,由 QWidget::event()处理的,这两个键的作用是把输入焦点转到前一控件或者下一个控件上,在 CodeEditor 中,希望 Tab 键的作用是缩进,可以这样重写 event():

bool CodeEditor::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Tab) {
insertAtCurrentPosition('\t'); return true;
}
}
return QWidget::event(event);
}

如果这个事件是一个键盘敲击事件,我们把 QEvent 对象转换成QKeyEvent,然后确定是那个键敲击了,如果是 Tab 键,进行处理后返回 true,通知 Qt 我们已经对事件进行了处理。如果返回 false,Qt 还会把这个事件交给基类控件处理。

响应键盘事件的更好的方法是使用 QAction。例如,goToBeginningOfLine()和 goToBeginningOfDocument()是CodeEditor 的两个公有槽函数, CodeEditor 是MainWindow 的中央控件,下面的代码实现了键盘和槽函数的绑定:

MainWindow::MainWindow()
{
editor = new CodeEditor; setCentralWidget(editor); goToBeginningOfLineAction =
new QAction(tr("Go to Beginning of Line"), this); goToBeginningOfLineAction->setShortcut(tr("Home")); connect(goToBeginningOfLineAction, SIGNAL(activated()),
editor, SLOT(goToBeginningOfLine())); goToBeginningOfDocumentAction =
new QAction(tr("Go to Beginning of Document"), this); goToBeginningOfDocumentAction->setShortcut(tr("Ctrl+Home")); connect(goToBeginningOfDocumentAction, SIGNAL(activated()),
editor, SLOT(goToBeginningOfDocument()));
...
}

这样可以很容易把一个键盘敲击的命令加入到菜单或者工具条中。如果命令没有出现在用户界面中,可用用 QShortcut 对象代替QAction 对象,在QAction内部就是使用这个类实现键盘的绑定。

通常情况下,只要窗口中有激活的控件,控件上用 QAction 和 QShortcut 设置的键盘绑定都是可用的。绑定的键可用 QAction::setShortcutContext()或者 QShortcur::setContext()进行修改。

另一个常用的事件类型是时间事件。其他事件都是由用户的某种活动引发的,而时间事件则使程序按照一定的时间间隔执行特定的任务。时间事件一般用来使光标闪烁,或者播放动画,或者只是绘制显示界面或者控件。

为了介绍时间事件,我们将实现一个 Ticker 控件。这个控件显示一条标语,每隔 30 毫秒钟向左移动一个象素。如果控件比标语要宽,标语的文本重复的显示在控件上,填满整个控件。

#ifndef TICKER_H #define TICKER_H
#include <QWidget>
class Ticker : public QWidget
{Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText) public:
Ticker(QWidget *parent = 0);
void setText(const QString &newText); QString text() const { return myText; } QSize sizeHint() const;
protected:
void paintEvent(QPaintEvent *event); void timerEvent(QTimerEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event);
private:
QString myText; int offset;
int myTimerId;
};
#endif

在头文件中,我们实现了 Ticker 的四个事件处理函数,其中三个 timeEvent(), showEvent()和hideEvent()是我们以前没有见过的。

#include <QtGui> #include "ticker.h"
Ticker::Ticker(QWidget *parent)
: QWidget(parent)
{
offset = 0;
myTimerId = 0;
}

在构造函数中,设置offset 为 0,这个变量是文本要显示的x 坐标值。时间ID 总是非 0 的,这里设置myTimerId 为 0 说明我们还没有启动任何时间

void Ticker::setText(const QString &newText)
{
myText = newText; update(); updateGeometry();
}

函数 setText()设置要显示的文本。调用 update()引发绘制事件重新显示文本, updateGeometry()通知布局管理器改变控件的大小。

QSize Ticker::sizeHint() const
{return fontMetrics().size(0, text());
}

函数 sizeHint()返回的是控件在不同文本时完整显示所需的尺寸。 QWidget::fontMetrics()返回一个 QFontMetrics 对象,得到控件所用的字体的信息。在这里我们需要得到的是文本的大小。(在 QFontMetrics::size()中,第一个参数是一个标识,对字符串来讲并不需要,所有赋了 0 值)。

void Ticker::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
int textWidth = fontMetrics().width(text()); if (textWidth < 1)
return;
int x = -offset;
while (x < width()) {
painter.drawText(x, 0, textWidth, height(), Qt::AlignLeft | Qt::AlignVCenter, text());
x += textWidth;
}
}

函数paintEvent()使用QPainter::drawText()绘制文本。调用fontMetrics()得到文本所需要的水平空间,然后多次绘制文本,直至填满整个控件

void Ticker::showEvent(QShowEvent * /* event */)
{
myTimerId = startTimer(30);
}

showEvent()启动了一个计时器。调用QObject::startTimer()返回一个ID值,这个 ID 值可以帮助我们识别这个计时器。QObject 能够支持多个独立的不同的时间间隔的计时器。调用 startTimer()以后,Qt 大约每 30 毫秒产生一个事件,时间的准确与否取决于不同的操作系统。

我们也可以在 Ticker 的构造函数中调用 startTimer()。但是在控件可见以后再启动,能够节省一些资源。

void Ticker::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
++offset;
if (offset >= fontMetrics().width(text())) offset = 0;
scroll(-1, 0);
} else {
QWidget::timerEvent(event);}
}

函数 timerEvent()由系统以一定间隔进行调用的。把offset 增加 1 来模仿文字的移动,增加到标语的宽度时文字的宽度是重新设置为 0。然后调用scroll()把控件向左滚动一个象素。也可以调用 update(),但是 scroll()更加高效,它对可见的象素进行移动,只是对需要新绘制的地方调用绘制事件(在这个例子中 ,只是一个象素宽的区域)。

如果计时器不是我们需要处理的,则把它传递给基类。

void Ticker::hideEvent(QHideEvent * /* event */)
{
killTimer(myTimerId);
}

在 hideEvent()中,调用QObject::killTimer()停止计时器。

时间事件的优先级很低,如果需要多个计时器,那么跟踪每一个计时器的ID 是很费时的。这种情况下,较好的方法是为每一个计时器创建一个QTimer 对象。在每一个时间间隔内,QTimer 发出一个 timeout()信号。QTimer 还支持一次性计时器(只发出一次 timeout()信号的计时器)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85762.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85762.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/85762.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Dify 案例】【MCP实战】【三】【超级美食家】

接上次的超级助理,我们这一期给出一个超级美食家 首先:我的MCP要申请一个key ` 我们来看看这个MCP服务怎么使用呢。`https://modelscope.cn/mcp/servers/@worryzyy/howtocook-mcp插件里面需要配置 {"mcpServers":{"amap-amap-sse":{"url":&qu…

4.文件管理(文本、日志、Excel表)

目录 1.文本 2.日志 3.Excel表 1.文本 using System.Text;namespace (自己创建的一个类) {/// <summary>/// 配置文件*.ini读写器。/// </summary>public class IniFile{[System.Runtime.InteropServices.DllImport("kernel32")]private static ex…

Java 包装类详解

什么是包装类 Java包装类&#xff08;Wrapper Classes&#xff09;是将8种基本数据类型封装成对象的类&#xff0c;位于java.lang包中。每个基本数据类型都有对应的包装类&#xff1a; byte → Byteshort → Shortint → Integerlong → Longfloat → Floatdouble → Doublec…

阿里云ACP认证-数据仓库

数据仓库 Kappa架构&#xff1a;将实时和离线代码统一&#xff08;优化lambda架构&#xff09;&#xff0c;但是不好修正数据&#xff0c;开发周期长&#xff0c;成本浪费&#xff0c;对于历史数据的高吞吐量力不从心 原一代数据仓库&#xff1a; 离线&#xff1a;hivemaxcom…

WebRTC(五):TURN协议

TURN&#xff08;Traversal Using Relays around NAT&#xff09;协议是一个网络协议&#xff0c;旨在解决 NAT&#xff08;网络地址转换&#xff09;和防火墙 环境下的 UDP/TCP通信问题。它通常与 STUN 和 ICE 协议一起使用&#xff0c;广泛应用于 WebRTC、SIP 和视频会议等实…

Python 的内置函数 hasattr

Python 内建函数列表 > Python 的内置函数 hasattr Python 的内置函数 hasattr() 用于检查一个对象是否具有指定的属性或方法。该函数的语法为&#xff1a; hasattr(object, name)参数说明&#xff1a; object&#xff1a;要检查的对象&#xff0c;可以是任何 Python 对象…

docker使用技巧之把扩展卷命名变成有意义

背景 之前使用别人的镜像之后&#xff0c;启动docker后发出现了一堆看不懂名称的扩展卷 eg&#xff1a;集群查看 扩展卷查看 这个时候如果有很多集群需要清理扩展卷就很麻烦&#xff0c;不知道是哪个集群的 操作步骤 可以实现的分析&#xff1a;这个扩展卷的信息应该是和…

《博物通书》《博物新编》与满清历史篡改

《博物新编》作为近代西方科技输入中国的首部著作&#xff0c;其问世犹如一颗投入平静湖面的巨石&#xff0c;在 19 世纪中期的中国激起层层涟漪&#xff0c;对中国近代科学发展产生了多维度、深层次的影响。它不仅是知识传播的载体&#xff0c;更是推动中国科学从传统走向近代…

【入门】【例18.1】 睡眠

| 时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 64MB&#xff0c;其他语言 128MB 难度&#xff1a;中等 分数&#xff1a;100 OI排行榜得分&#xff1a;12(0.1分数2难度) 出题人&#xff1a;root | 描述 一个人只有每天睡眠时间到达 8…

DAY 38 Dataset和Dataloader类

知识点回顾&#xff1a; Dataset类的__getitem__和__len__方法&#xff08;本质是python的特殊方法&#xff09;Dataloader类minist手写数据集的了解 作业&#xff1a;了解下cifar数据集&#xff0c;尝试获取其中一张图片 import torch import torch.nn as nn import torch.o…

【Kubernetes】以LOL的视角打开K8s

前言 对于大部分后端程序员乃至于非后端程序员来说&#xff0c;在当前的云原生时代&#xff0c;Kubernetes&#xff08;后称K8s&#xff09;都是绕不开的一项技术&#xff1b;同时&#xff0c;对于这个时代的程序员来说&#xff0c;“英雄联盟”&#xff08;后称LOL&#xff0…

UE5 游戏模板 —— FirstShootGame

UE5 游戏模板 —— FirstShootGame 前言一、GameMode二、组件1.ShooterPickUpComponent单播多播 2.ShooterWeaponComponent附着武器开火 3.小结4.ShooterProjectile初始化碰撞受击检测 三、Character初始化输入移动 总结 前言 有了前两个俯视角游戏的基础让我们来看看相对复杂…

国家级与省级(不含港澳台)标准地图服务网站汇总

在先前的文章中&#xff0c;介绍了部分省级的标准地图服务网站可以下载各个区县近几年、不同要素的标准地图&#xff08;链接&#xff1a;国家与省市县 标准地图服务网站 审图号地图下载&#xff09;&#xff0c;但是当时只汇总了部分省级的标准地图服务网站。 这两天看到了一个…

前端开发面试题总结-vue3框架篇(一)

文章目录 Vue3高频问答一、vue2/vue3中常用的构建工具和脚手架分别是什么? 有什么区别?二、请说一说vue2和vue3的区别&#xff1f;三、请说一说vue2和vue3响应式原理的区别&#xff1f;四、vue3 如何定义响应式数据?五、说一说你对vue3中的setup函数?六、说一说vue3中的路由…

【LLM06---相对位置编码】

文章目录 相对位置编码经典式XLNET式T5式DeBERTa式 相对位置编码 上一节我们介绍了绝对位置编码&#xff0c;这一节我们来看相对位置编码&#xff0c;也就是用相对位置信息来表示&#xff0c;之前每一个token的位置式通过一个绝对的位置向量来表示的&#xff0c;现在我们在计算…

纯跟踪算法本质解密:航向角偏差=预瞄角?数学证明与工程实践

定义关键问题 在深入纯跟踪算法核心前&#xff0c;必须澄清一对容易被混淆但至关重要的概念&#xff1a; 概念坐标系物理意义计算方式航向角偏差(α_global)全局坐标系车辆航向与预瞄点方向的夹角预瞄点方位角 - 车辆航向角预瞄角(α_body)车身坐标系预瞄点相对于车辆纵轴的夹…

自动驾驶叉车在仓库环境中是否安全?

随着自动驾驶叉车的兴起&#xff0c;仓库运营持续演进。叉车自动化技术的引入使仓库设施变得更快、更安全且更具成本效益。然而一个关键问题依然存在&#xff1a;它们在繁忙的仓库环境中是否安全&#xff1f; 一 、什么是自动驾驶叉车&#xff1f; 自动驾驶叉车&#xff0c;也…

Neo4j操作指南:修改节点数据与新增节点属性

Neo4j操作指南&#xff1a;修改节点数据与新增节点属性 引言 Neo4j作为领先的图数据库&#xff0c;提供了灵活的数据操作方式。在实际应用中&#xff0c;我们经常需要修改已有节点的数据或为节点添加新属性。本文将详细介绍如何使用Cypher查询语言在Neo4j中完成这些操作&…

AI大模型学习之基础数学:微积分在AI大模型中的核心-梯度与优化(梯度下降)详解

微积分在AI大模型中的核心:梯度与优化(梯度下降) 人工智能(AI)大模型的训练和优化依赖于数学基础,其中微积分、线性代数和概率统计构成了其理论核心。微积分在AI中的核心作用在于提供优化工具,尤其是通过梯度和梯度下降方法,帮助模型在高维参数空间中找到损失函数的最…

记录tweenjs踩坑

初次上手tweenjs&#xff0c;试了很多示例代码都不生效&#xff0c;结果在html中生效&#xff0c;在vue3的项目中怎么都不生效 <!DOCTYPE html> <html lang"en"><head><title>Tween.js / simplest possible example!</title><meta…