Qt 图形视图框架3-事件处理与传播

  • [1. Qt图形视图框架下的事件处理和传播](#1. Qt图形视图框架下的事件处理和传播)
    • [1.1. 引言](#1.1. 引言)
    • [1.2. 项目背景](#1.2. 项目背景)
    • [1.3. Qt图形视图框架概述](#1.3. Qt图形视图框架概述)
    • [1.4. 事件处理](#1.4. 事件处理)
      • [1.4.1. 事件类型](#1.4.1. 事件类型)
      • [1.4.2. 事件处理函数](#1.4.2. 事件处理函数)
      • [1.4.3. 示例代码分析](#1.4.3. 示例代码分析)
    • [1.5. 事件传播](#1.5. 事件传播)
      • [1.5.1. 传播顺序](#1.5.1. 传播顺序)
      • [1.5.2. 示例代码分析](#1.5.2. 示例代码分析)
    • [1.6. 焦点管理机制](#1.6. 焦点管理机制)
      • [1.6.1. 实际运用要点](#1.6.1. 实际运用要点)
      • [1.6.2. 代码示例](#1.6.2. 代码示例)
      • [1.6.3. 常见问题及解决办法](#1.6.3. 常见问题及解决办法)
    • [1.7. 总结](#1.7. 总结)

1. Qt图形视图框架下的事件处理和传播

1.1. 引言

在使用Qt进行图形界面开发时,图形视图框架(Graphics View Framework)是一个强大的工具,它允许我们创建复杂的图形场景和交互界面。而事件处理和传播则是实现用户交互的关键部分,理解这些机制可以帮助我们更好地控制和响应各种用户操作。本文将结合一个具体的项目,详细介绍Qt图形视图框架下事件处理和传播的理论知识。

1.2. 项目背景

我们的项目是一个基于Qt图形视图框架的简单图形应用程序,包含以下几个主要部分:

  • MyItem类:继承自QGraphicsItem,表示场景中的图形项,可处理键盘、鼠标等事件。
  • MyView类:继承自QGraphicsView,用于显示图形场景,并处理与视图相关的事件。
  • main.cpp:程序入口,负责创建场景、添加图形项和显示视图。

1.3. Qt图形视图框架概述

Qt图形视图框架由三个主要类组成:

  • QGraphicsScene:提供管理大量图形项的场景,是图形项的容器。
  • QGraphicsItem:表示场景中的图形项,如矩形、椭圆等,是所有图形项的基类。
  • QGraphicsView:提供一个视口,用于显示场景中的图形项,支持缩放、平移等操作。

1.4. 事件处理

1.4.1. 事件类型

在Qt中,事件是由系统或应用程序生成的,用于表示各种用户操作或系统状态的变化。常见的事件类型包括:

  • 键盘事件 :如按键按下、释放等,由QKeyEvent表示。
  • 鼠标事件 :如鼠标点击、移动、悬停等,由QMouseEventQGraphicsSceneMouseEvent等表示。
  • 上下文菜单事件 :如右键点击弹出菜单,由QContextMenuEventQGraphicsSceneContextMenuEvent表示。

1.4.2. 事件处理函数

QGraphicsItemQGraphicsView中,我们可以重写相应的事件处理函数来处理特定类型的事件。例如:

  • keyPressEvent(QKeyEvent *event):处理键盘按键按下事件。
  • mousePressEvent(QGraphicsSceneMouseEvent *event):处理鼠标点击事件。
  • hoverEnterEvent(QGraphicsSceneHoverEvent *event):处理鼠标悬停进入事件。
  • contextMenuEvent(QGraphicsSceneContextMenuEvent *event):处理上下文菜单事件。

1.4.3. 示例代码分析

以下是MyItem类中部分事件处理函数的示例代码:

首先要实现两个必须的纯虚函数:

cpp 复制代码
    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;

然后处理键鼠事件函数:

cpp 复制代码
void  MyItem::keyPressEvent(QKeyEvent *event)
{
    if(event->key()==Qt::Key_Down)
        moveBy(0,10);
}
void  MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    setFocus();
    setCursor(Qt::ClosedHandCursor);
}
void  MyItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    setCursor(Qt::OpenHandCursor);
    setToolTip("I am item");
}
void  MyItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    QMenu menu;
    QAction *moveAction = menu.addAction("move back");
    QAction *selectedAction = menu.exec(event->screenPos());
    if(selectedAction == moveAction){
        setPos(0,0);
    }
}
  • keyPressEvent:当按下向下箭头键时,图形项向下移动10个单位。
  • mousePressEvent:鼠标点击时,设置图形项获得焦点,并改变鼠标光标样式。
  • hoverEnterEvent:鼠标悬停进入时,改变鼠标光标样式并设置工具提示。
  • contextMenuEvent:右键点击时,弹出包含"move back"选项的菜单,选择该选项时将图形项移动到原点。

1.5. 事件传播

1.5.1. 传播顺序

在Qt图形视图框架中,事件的传播顺序是从最内层的图形项开始,逐步向外层传播,直到找到一个能够处理该事件的对象为止。具体顺序如下:

  1. 图形项(QGraphicsItem:事件首先发送到鼠标点击或按键发生的图形项。
  2. 图形场景(QGraphicsScene:如果图形项没有处理该事件,事件将传播到图形场景。
  3. 图形视图(QGraphicsView:如果图形场景没有处理该事件,事件将传播到图形视图。

1.5.2. 示例代码分析

以下是MyView类中keyPressEvent函数的示例代码:

cpp 复制代码
void MyView::keyPressEvent(QKeyEvent *event)
{
    switch(event->key())
    {
    case Qt::Key_Plus   :
        scale(1.2,1.2);
        break;
    case Qt::Key_Minus  :
        scale(1/1.2,1/1.2);
        break;
    case Qt::Key_Right:
        rotate(30);
        break;
    }
    QGraphicsView::keyPressEvent(event);
}

当在视图中按下"+"、"-"或右箭头键时,视图会进行相应的缩放或旋转操作。如果图形项没有处理这些按键事件,事件将传播到视图中进行处理。

1.6. 焦点管理机制

  • 设置焦点的方式
    • 借助场景操作:通过调用 QGraphicsScene.setFocusItem(item) 方法,可直接把焦点赋给指定的图形项。
    • 图形项自主操作:图形项自身调用 setFocus() 函数,能让自己成为焦点图形项。
  • 场景焦点的关联
    • 当场景中的某个图形项获得焦点时,场景会自动获得焦点。
    • 若场景主动调用 setFocus() 函数,它会成为焦点对象,不过键盘事件依然会传递给获得焦点的图形项。
  • 焦点状态的保存
    • 当场景失去焦点(例如调用了 clearFocus() 函数),但它内部的某个图形项仍保留焦点时,场景会记住这个有焦点的图形项。
    • 当场景重新获得焦点后,之前保存的那个焦点图形项会再次获得焦点。

1.6.1. 实际运用要点

  1. 确保事件被处理
    • 若要处理键盘事件,首先要保证场景处于有焦点的状态。
    • 可以通过 scene.setFocus() 或者 item.setFocus() 来实现这一目的。
  2. 焦点的转移操作
    • 可以使用 scene.setFocusItem(nextItem) 来实现焦点在不同图形项之间的切换。
  3. 焦点状态的查询
    • 可以通过 scene.focusItem() 来获取当前获得焦点的图形项。
    • 图形项自身可以通过 hasFocus() 方法来判断自己是否拥有焦点。

1.6.2. 代码示例

下面是一个简单的代码示例,展示了焦点设置和键盘事件处理的基本逻辑:

cpp 复制代码
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QKeyEvent>

class MyRectItem : public QGraphicsRectItem {
public:
    MyRectItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem* parent = nullptr)
        : QGraphicsRectItem(x, y, width, height, parent) {}

protected:
    void keyPressEvent(QKeyEvent* event) override {
        // 处理键盘事件
        if (event->key() == Qt::Key_Space) {
            qDebug() << "空格键被按下";
        }
        QGraphicsRectItem::keyPressEvent(event);
    }
};

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

    // 创建场景和视图
    QGraphicsScene scene;
    QGraphicsView view(&scene);

    // 创建图形项并添加到场景
    MyRectItem* rect1 = new MyRectItem(0, 0, 100, 100);
    MyRectItem* rect2 = new MyRectItem(150, 0, 100, 100);
    scene.addItem(rect1);
    scene.addItem(rect2);

    // 设置焦点到rect1
    rect1->setFocus(); // 或者使用 scene.setFocusItem(rect1)

    // 显示视图
    view.show();

    return a.exec();
}

1.6.3. 常见问题及解决办法

  1. 键盘事件无法响应
    • 检查场景是否获得了焦点。
    • 确认是否有图形项获得了焦点,并且该图形项已经重写了 keyPressEvent 方法。
  2. 焦点意外丢失
    • 检查是否有代码调用了 clearFocus() 或者 setFocus() 函数。
    • 可以在图形项的 focusInEventfocusOutEvent 方法中添加调试信息,以便追踪焦点的变化情况。

1.7. 总结

通过本文的介绍,我们了解了Qt图形视图框架下事件处理和传播,焦点管理的基本理论知识。事件处理允许我们对用户的各种操作做出响应,而事件传播机制确保了事件能够在图形项、场景和视图之间正确传递。在实际开发中,我们可以根据具体需求重写相应的事件处理函数,实现丰富的交互效果。同时,合理利用事件传播机制可以避免代码的重复编写,提高开发效率。

相关推荐
Azxcc021 分钟前
C++异步编程入门
开发语言·c++
吐泡泡_23 分钟前
C++(STL源码刨析/vector)
c++
你的冰西瓜24 分钟前
C++排序算法全解析(加强版)
c++·算法·排序算法
特立独行的猫a1 小时前
11款常用C++在线编译与运行平台推荐与对比
java·开发语言·c++
笑鸿的学习笔记1 小时前
qt-C++笔记之setCentralWidget的使用
c++·笔记·qt
轩情吖2 小时前
Qt的信号与槽(二)
数据库·c++·qt·信号·connect·信号槽·
胖大和尚2 小时前
C++项目学习计划
开发语言·c++·学习
GiraKoo4 小时前
【GiraKoo】 C++20的新特性
c++
无聊的小坏坏4 小时前
力扣 239 题:滑动窗口最大值的两种高效解法
c++·算法·leetcode