首页 > 其他分享 >【QT 学习之路】事件

【QT 学习之路】事件

时间:2023-08-16 11:02:57浏览次数:40  
标签:QString Qt 函数 学习 事件 QT event 鼠标

事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。

事件也就是我们通常说的“事件驱动(event drive)”程序设计的基础概念。事件的出现,使得程序代码不会按照原始的线性顺序执行。想想看,从最初的 C 语言开始,我们的程序就是以一种线性的顺序执行代码:这一条语句执行之后,开始执行下一条语句;这一个函数执行过后,开始执行下一个函数。这种类似“批处理”的程序设计风格显然不适合于处理复杂的用户交互。我们来想象一下用户交互的情景:我们设计了一堆功能放在界面上,用户点击了“打开文件”,于是开始执行打开文件的操作;用户点击了“保存文件”,于是开始执行保存文件的操作。我们不知道用户究竟想进行什么操作,因此也就不能预测接下来将会调用哪一个函数。如果我们设计了一个“文件另存为”的操作,如果用户不点击,这个操作将永远不会被调用。这就是所谓的“事件驱动”,我们的程序的执行顺序不再是线性的,而是由一个个事件驱动着程序继续执行。没有事件,程序将阻塞在那里,不执行任何代码。

在 Qt 中,事件的概念似乎同信号槽类似。的确如此,一般来说,使用 Qt 组件时,我们并不会把主要精力放在事件上。因为在 Qt 中,我们关心的更多的是事件关联的一个信号。比如,对于QPushButton的鼠标点击,我们不需要关心这个鼠标点击事件,而是关心它的clicked()信号的发出。这与其他的一些 GUI 框架不同:在 Swing 中,你所要关心的是JButtonActionListener这个点击事件。由此看出,相比于其他 GUI 框架,Qt 给了我们额外的选择:信号槽。

但是,Qt 中的事件和信号槽却并不是可以相互替代的。信号由具体的对象发出,然后会马上交给由connect()函数连接的槽进行处理;而对于事件,Qt 使用一个事件队列对所有发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部。前一个事件完成后,取出后面的事件进行处理。但是,必要的时候,Qt 的事件也可以不进入事件队列,而是直接处理。信号一旦发出,对应的槽函数一定会被执行。但是,事件则可以使用“事件过滤器”进行过滤,对于有些事件进行额外的处理,另外的事件则不关心。总的来说,如果我们使用组件,我们关心的是信号槽;如果我们自定义组件,我们关心的是事件。因为我们可以通过事件来改变组件的默认操作。比如,如果我们要自定义一个能够响应鼠标事件的EventLabel,我们就需要重写QLabel的鼠标事件,做出我们希望的操作,有可能还得在恰当的时候发出一个类似按钮的clicked()信号(如果我们希望让这个EventLabel能够被其它组件使用)或者其它的信号。

在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QCoreApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObjectevent()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。关于这一点,我们会在以后的章节中详细说明。

在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如keyPressEvent()keyReleaseEvent()mouseDoubleClickEvent()mouseMoveEvent()mousePressEvent()mouseReleaseEvent()等。这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。下面来看一个例子:

class EventLabel : public QLabel
{
protected:
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
};

void EventLabel::mouseMoveEvent(QMouseEvent *event)
{
    this->setText(QString("<center><h1>Move: (%1, %2)</h1></center>")
                  .arg(QString::number(event->x()), QString::number(event->y())));
}

void EventLabel::mousePressEvent(QMouseEvent *event)
{
    this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>")
                  .arg(QString::number(event->x()), QString::number(event->y())));
}

void EventLabel::mouseReleaseEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",
                event->x(), event->y());
    this->setText(msg);
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    EventLabel *label = new EventLabel;
    label->setWindowTitle("MouseEvent Demo");
    label->resize(300, 200);
    label->show();

    return a.exec();
}

我们编译运行上面的代码,就可以理解到有关事件的使用方法。

EventLabel继承了QLabel,覆盖了mousePressEvent()mouseMoveEvent()MouseReleaseEvent()三个函数。我们并没有添加什么功能,只是在鼠标按下(press)、鼠标移动(move)和鼠标释放(release)的时候,把当前鼠标的坐标值显示在这个Label上面。由于QLabel是支持 HTML 代码的,因此我们直接使用了 HTML 代码来格式化文字。

QStringarg()函数可以自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种。

QString("[%1, %2]").arg(x, y);

语句将会使用 x 替换 %1,y 替换 %2,因此,这个语句生成的QString为 [x, y]。

mouseReleaseEvent()函数中,我们使用了另外一种QString的构造方法。我们使用类似 C 风格的格式化函数sprintf()来构造QString

运行上面的代码,当我们点击了一下鼠标之后,label 上将显示鼠标当前坐标值。

为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。知道了这一点,我们就可以在main()函数中直接设置下:

EventLabel *label = new EventLabel;
label->setWindowTitle("MouseEvent Demo");
label->resize(300, 200);
label->setMouseTracking(true);
label->show();

这样子就没有这个问题了。

标签:QString,Qt,函数,学习,事件,QT,event,鼠标
From: https://www.cnblogs.com/RioTian/p/17633421.html

相关文章

  • Markdown学习
    Mardown学习标题:二级标题三级标题字体Hello,World!Hello,World!Hello,World!Hello,World!引用选择java编程,走向人生巅峰分割线图片!截图超链接点击跳转到博客列表A表格名字性别生日代码publice......
  • 自定义MarkupExtension的学习,以及WPF中Combobox绑定枚举类型
    我们上一期讲到ComBobox绑定数据,https://www.cnblogs.com/guchen33/p/17630808.html这次我们简单化一下,正常来讲WPF中绑定不了枚举的像这样//前台代码<ComboBoxItemsSource="{BindingMyEnum}"/>//后台VMpublicenumMyEnum{One,Two,Three,Fo......
  • C#语言学习代码示例
    保留数位namespaceBasicGrammarStudy{classProgram{staticvoidMain(string[]args){Console.WriteLine(string.Format("{0:F3}",13.47483327438));//13.475Console.WriteLine(string.Format("{0......
  • 利用Docker简化机器学习应用程序的部署和可扩展性
    利用Docker的强大功能:简化部署解决方案,确保可扩展性,并简化机器学习模型的持续集成/持续交付(CI/CD)流程。机器学习(ML)近年来取得了爆炸性增长,导致对稳健、可扩展和高效部署方法的需求增加。传统方法往往需要帮助将ML模型操作化,原因包括训练和服务环境之间的差异或扩展的困难。Docker是......
  • Golang 语言学习路线
    学习Go语言是一个很好的选择,它具有高效的编译速度、强大的并发支持和简洁的语法。适用于初学者的Golang学习路线:1.学习基础:安装Go:从官方网站下载并安装Go语言的最新版本。Hello,World!:学习如何编写一个简单的HelloWorld程序,并运行它。基本语法:了解Go的基本语法,包括变量、常......
  • 算法工程师学习运筹学 笔记三 对偶问题
    对偶问题每一个线性规划问题(称为原始问题)都有一个与它对应的对偶线性规划问题(称为对偶问题)。在原始的和对偶的两个线性规划中求解任何一个规划时,会自动地给出另一个规划的最优解;当对偶问题比原始问题有较少约束时,求解对偶规划比求解原始规划要方便得多;对偶规划中的变量就是影......
  • ThingsKit物联网平台设备管理之事件管理
    产品物模型事件功能下发事件命令记录。v1/devices/event/${deviceId}/${identifier}:::info......
  • 【学习笔记】lazytag 重学笔记
    还打算用作TikZ练习文。感觉用TikZ画图很酷炫。下面不分析具体问题,直接对于普遍的lazytag问题解决。可以lazytag的问题见atcoderlibrary。......
  • QT使用QML实现地图绘制虚线
    QML提供了MapPolyline用于在地图上绘制线段,该线段是实线,因此我使用Canvas自定义绘制的方式在地图上绘制线段,如图:鼠标在地图上点击后,在点击位置添加图标,当有多个图标被添加到地图上后,计算各个图标间的距离,并创建一个新的虚线线段组件,连接两个图标点,显示距离数值。如果对自定义图标......
  • Long-term Forecasting with TiDE: Time-series Dense Encoder 学习笔记
    Long-termForecastingwithTiDE:Time-seriesDenseEncoder论文地址:https://arxiv.org/abs/2304.08424代码地址:https://github.com/google-research/google-research/tree/master/tide摘要:这篇论文提出了时间序列密集编码器(TiDE),一种基于MLP的编码器-解码器模型,用于长期时......