三十、鼠标及键盘事件

三十、鼠标及键盘事件

30.1 鼠标事件QMouseEvent

发生鼠标事件,当在 Widget 内按下 (或释放) 鼠标按钮 (或移动鼠标光标) 时。

才发生鼠标移动事件当按下鼠标按钮时,除非启用鼠标跟踪采用 QWidget::setMouseTracking().

Qt 自动抓取鼠标当在 Widget 内按下鼠标按钮时;Widget 将继续接收鼠标事件,直到最后释放鼠标按钮。

鼠标事件包含特殊接受标志 (指示接收者是否想要事件)。应该调用 ignore() 若鼠标事件未被 Widget 所处理。鼠标事件会沿父级 Widget 链向上传播,直到小部件接受它采用 accept(),或由事件过滤器消耗掉它。

注意 : 若鼠标事件被传播给 widget其中 Qt::WA_NoMousePropagation有设置,该鼠标事件将不会沿父级 Widget 链向上进一步传播。

可以找到键盘修饰符键的状态通过调用 modifiers()函数,继承自 QInputEvent.

函数 pos(), x(),和 y() 给出光标相对于接收鼠标事件的小部件的位置.如果由于鼠标事件而移动小部件,请使用 globalPos() 返回的全局位置以避免晃动。

QWidget::setEnabled() 函数可用于启用 (或禁用) 小部件的鼠标事件和键盘事件。

重实现 QWidget事件处理程序, QWidget::mousePressEvent (), QWidget::mouseReleaseEvent (), QWidget::mouseDoubleClickEvent (),和 QWidget::mouseMoveEvent() 以在自己的 Widget 中接收鼠标事件。

实现以下功能

mainwindow.h

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include<QMouseEvent>
#include<QMessageBox>
#include<QLabel>
#include<QStatusBar>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    QLabel *statusLabel;
    QLabel *mouseLabel;

    void dispPicture();

protected:
    void mouseMoveEvent(QMouseEvent *e);
    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *e);
};
#endif // MAINWINDOW_H

mainwindow.cpp

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

// MainWindow构造函数
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent) // 继承了QWidget类,创建一个主窗口
    , ui(new Ui::MainWindow) // 初始化用户界面
{
    ui->setupUi(this); // 配置用户界面

    // 创建两个标签,用于显示鼠标的坐标
    statusLabel=new QLabel("鼠标在当前窗口坐标为:"); // 鼠标当前位置的标签
    statusLabel->setFixedWidth(200); // 设置标签宽度为200像素

    mouseLabel=new QLabel(); // 鼠标位置的显示标签
    mouseLabel->setFixedWidth(200); // 设置标签宽度为200像素

    // 将这两个标签添加到状态栏
    statusBar()->addPermanentWidget(statusLabel);
    statusBar()->addPermanentWidget(mouseLabel);

    // 开启鼠标追踪,即可以获取鼠标的移动事件
    this->setMouseTracking(true);
    resize(800,600); // 设置窗口大小为800x600像素

    // 加载并显示图片
    dispPicture();
}

// MainWindow析构函数
MainWindow::~MainWindow()
{
    delete ui; // 删除用户界面对象,释放内存
}

// 鼠标移动事件处理函数
void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
    // 更新鼠标位置的显示标签内容
    mouseLabel->setText("("+QString::number(e->x())+","+QString::number(e->y())+")");
}

// 鼠标按下事件处理函数
void MainWindow::mousePressEvent(QMouseEvent *e)
{
    QString str="("+QString::number(e->x())+","+QString::number(e->y())+")"; // 鼠标坐标字符串
    if(e->button()==Qt::LeftButton) // 如果按下的是左键
    {
        statusBar()->showMessage("按下左键"+str); // 在状态栏显示消息
    }
    else if(e->button()==Qt::RightButton) // 如果按下的是右键
    {
        statusBar()->showMessage("按下右键"+str); // 在状态栏显示消息
    }
    else if(e->button()==Qt::MidButton) // 如果按下的是中键
    {
        statusBar()->showMessage("按下中键"+str); // 在状态栏显示消息
    }
}

// 鼠标释放事件处理函数
void MainWindow::mouseReleaseEvent(QMouseEvent *e)
{
    QString str="("+QString::number(e->x())+","+QString::number(e->y())+")"; // 鼠标坐标字符串
    statusBar()->showMessage("用户已经释放鼠标坐标"+str); // 在状态栏显示消息
}

// 显示图片函数
void MainWindow::dispPicture()
{
    QString str("D:/wallhaven-9d5x5k.png"); // 图片路径字符串

    QImage *image=new QImage; // 创建QImage对象用于加载图片
    QLabel *imageLabel=new QLabel(this); // 创建QLabel对象用于显示图片

    imageLabel->move(20,20); // 设置图片标签的位置,距离窗口左上角20,20像素的位置开始显示图片
    imageLabel->setFixedSize(700,400); // 设置图片标签的大小为700x400像素,即图片的大小为700x400像素

    if(!(image->load(str))) // 如果图片加载失败(例如文件不存在、文件格式不正确等)
    {
        QMessageBox::information(this,"失败","图片加载失败"); // 弹出信息框提示用户图片加载失败
        delete image; // 删除QImage对象,释放内存
        return; // 结束函数执行,不再继续执行后续代码,因为已经无法加载图片了。
    }

    imageLabel->setPixmap(QPixmap::fromImage(*image)); // 将加载的图片转换为QPixmap对象,然后设置到QLabel对象上,显示图片。这里使用*image是因为QImage是const的,不能直接赋值给非const对象。需要先解引用一下。
}

代码提供了一个名为MainWindow的类,它是QMainWindow的子类。这个类主要处理了鼠标的移动、点击事件,以及显示图片的功能。

  1. 构造函数

    • MainWindow的构造函数中,首先初始化了用户界面,然后设置了鼠标追踪,并设置了窗口的大小。
    • 接着,加载并显示了一个图片。
  2. 鼠标移动事件处理函数

    • 当鼠标在窗口内移动时,会更新鼠标位置的显示标签内容。
  3. 鼠标按下事件处理函数

    • 当鼠标被按下时,会根据按下的按钮类型(左键、右键、中键)在状态栏显示相应的消息。
  4. 鼠标释放事件处理函数

    • 当鼠标被释放时,会在状态栏显示消息,告知用户已经释放了鼠标。
  5. 显示图片函数

    • 这个函数用于加载并显示图片。如果图片加载失败,会弹出信息框提示用户。

从代码中可以看出,这个MainWindow类主要实现了鼠标事件的响应和图片的加载与显示功能。这些功能都是通过重写QMainWindow中的事件处理函数来实现的。同时,代码中也使用了Qt的一些常用组件,如QLabelQImage等来进行界面和图片的操作。

void QStatusBar::addPermanentWidget(QWidget *widget, int stretch = 0):

将给定的小部件永久地添加到这个状态栏,如果小部件还不是这个QStatusBar对象的子部件,则重新定位它。随着状态栏的增长和收缩,stretch参数用于计算给定小部件的合适大小。默认的拉伸因子是0,即给小部件一个最小的空间。

永久意味着小部件可能不会被临时消息所掩盖。它位于状态栏的最右边。

void setMouseTracking(bool enable):

此属性保存是否为小部件启用鼠标跟踪

如果禁用了鼠标跟踪(默认值),则当移动鼠标时至少按下一个鼠标按钮时,小部件仅接收鼠标移动事件。

如果启用了鼠标跟踪,即使没有按下按钮,小部件也会接收鼠标移动事件。

void QStatusBar::showMessage(const QString &message, int timeout = 0):

隐藏正常状态指示,并在指定的毫秒数(超时)内显示给定消息。如果timeout为0(默认值),则消息保持显示,直到clearMessage()槽被调用,或者直到showMessage()槽被再次调用以更改消息。

请注意,调用showMessage()是为了显示工具提示文本的临时解释,因此传递超时0不足以显示永久消息。

bool QImage::load(const QString &fileName, const char *format = nullptr):

从具有给定文件名的文件中加载图像。如果图像已成功加载,则返回true;否则将使图像无效并返回false。

加载程序尝试使用指定的格式读取图像,例如PNG或JPG。如果没有指定格式(这是默认的),它会根据文件的后缀和头自动检测。具体操作请参见QImageReader::setAutoDetectImageFormat()。

30.2 键盘事件QKeyEvent

当按下或释放按键时,按键事件将发送到具有键盘输入焦点的小部件。

密钥事件包含一个特殊的接受标志,该标志指示接收方是否将处理密钥事件。默认情况下,QEvent::KeyPressQEvent::KeyRelease设置了此标志,因此在对键事件执行操作时无需调用 accept()。对于 QEvent::ShortcutOverride,接收方需要显式接受事件以触发覆盖。对关键事件调用 ignore() 会将其传播到父小部件。该事件在父小部件链上向上传播,直到小部件接受它或事件过滤器使用它。

QWidget::setEnabled() 函数可用于启用 (或禁用) 小部件的鼠标事件和键盘事件。

事件处理程序 QWidget::keyPressEvent(), QWidget::keyReleaseEvent (), QGraphicsItem:: keyPressEvent () 和 QGraphicsItem:keyReleaseEvent() 接收键事件。

实现以下功能,可以上下左右移动

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QKeyEvent>
#include<QPaintEvent>
#include<QPainter>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void drawPixFunc();
    void paintEvent(QPaintEvent *);
    void keyPressEvent(QKeyEvent *);

private:
    Ui::Widget *ui;

    QPixmap *pix;
    QImage image;
    int startX;
    int startY;
    int width;
    int height;
    int step;
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

// Widget类的构造函数
Widget::Widget(QWidget *parent)
    : QWidget(parent)   // 调用基类QWidget的构造函数
    , ui(new Ui::Widget)  // 初始化用户界面
{
    ui->setupUi(this);    // 设置用户界面

    // 自动填充背景色为白色
    setAutoFillBackground(true);

    // 设置窗口的背景颜色为白色
    QPalette p=this->palette();
    p.setColor(QPalette::Window,Qt::white);
    setPalette(p);

    // 设置窗口的最小和最大尺寸为800x600
    setMinimumSize(800,600);
    setMaximumSize(800,600);

    // 获取窗口的宽度和高度
    width=size().width();
    height=size().height();

    // 创建一个与窗口大小相同的白色位图
    pix=new QPixmap(width,height);
    pix->fill(Qt::white);

    // 从指定路径加载一张图片
    image.load("D:/file.jpg");

    // 初始化图片显示位置的起始点为(30,30)
    startX=30;
    startY=30;
    step=30;

    // 调用drawPixFunc函数,开始在位图上绘制图片的边框和对角线网格
    drawPixFunc();
}

// Widget类的析构函数,负责释放创建的资源
Widget::~Widget()
{
    delete ui;  // 删除用户界面对象,释放内存
}

// drawPixFunc函数,绘制位图上的网格和图片
void Widget::drawPixFunc()
{
    pix->fill(Qt::green);       // 将位图填充为绿色,作为背景色

    QPainter *painter=new QPainter;  // 创建一个QPainter对象,用于绘图
    QPen pen(Qt::DashDotLine);       // 创建一个画笔,用于绘制网格线

    // 绘制水平网格线,间距为step像素,从左到右绘制,直到达到窗口宽度为止
    for(int i=step;i<width;i=i+step)
    {
        painter->begin(pix);           // 开始绘图到指定的位图上
        painter->setPen(pen);          // 设置画笔颜色为网格线颜色
        painter->drawLine(QPoint(i,0),QPoint(i,height));  // 绘制一条从顶部到底部的水平线
        painter->end();                 // 结束绘图操作
    }
    // 绘制垂直网格线,间距为step像素,从上到下绘制,直到达到窗口高度为止
    for(int i=step;i<height;i=i+step)
    {
        painter->begin(pix);           // 开始绘图到指定的位图上
        painter->setPen(pen);          // 设置画笔颜色为网格线颜色
        painter->drawLine(QPoint(0,i),QPoint(width,i));  // 绘制一条从左到右的垂直线
        painter->end();                 // 结束绘图操作
    }

    // 在网格中心位置绘制图片,起始点为(startX, startY)位置,大小为图片的实际大小(width和height已定义为窗口大小)
    painter->begin(pix);             // 开始绘图到指定的位图上
    painter->drawImage(QPoint(startX,startY),image);  // 绘制图片到指定位置和大小
    painter->end();                  // 结束绘图操作
}

// Widget 类的 paintEvent 函数,用于在画布上绘制图片
void Widget::paintEvent(QPaintEvent *)
{
    // 创建一个 QPainter 对象,用于在画布上绘制
    QPainter pt;
    // 在当前对象上开始绘制
    pt.begin(this);
    // 在画布上绘制 pixmap 图片,起始位置为 (0,0)
    pt.drawPixmap(QPoint(0,0),*pix);
    // 结束绘制
    pt.end();
}

// Widget 类的 keyPressEvent 函数,用于处理键盘按键事件
void Widget::keyPressEvent(QKeyEvent *e)
{
    // 检查按键是否为左键、右键、上键或下键
    if(e->key() == Qt::Key_Left) {
        // 处理左键事件
        startX = (startX - step < 0) ? startX : startX - step;
    } else if(e->key() == Qt::Key_Right) {
        // 处理右键事件
        startX = (startX + step > width) ? startX : startX + step;
    } else if(e->key() == Qt::Key_Up) {
        // 处理上键事件
        startY = (startY - step < 0) ? startY : startY - step;
    } else if(e->key() == Qt::Key_Down) {
        // 处理下键事件
        startY = (startY + step > height) ? startY : startY + step;
    }

    // 调用 drawPixFunc 函数进行绘制操作
    drawPixFunc();
    // 更新界面,使更改可见
    update();
}

代码分析:

  1. 构造函数:Widget类的构造函数中,初始化了用户界面、设置了背景色、设置了窗口的大小,并创建了一个与窗口大小相同的白色位图。然后从指定路径加载了一张图片,并初始化了图片显示位置的起始点为(30,30)。最后,调用了drawPixFunc函数,开始在位图上绘制图片的边框和对角线网格。
  2. 析构函数:Widget类的析构函数负责释放创建的资源,例如删除用户界面对象。
  3. drawPixFunc函数:这个函数用于在位图上绘制网格和图片。首先将位图填充为绿色作为背景色,然后绘制了水平和垂直网格线,最后在网格中心位置绘制了图片。
  4. paintEvent函数:这个函数用于在画布上绘制图片。它创建了一个QPainter对象,并在当前对象上开始绘制,然后在画布上绘制了pixmap图片。
  5. keyPressEvent函数:这个函数用于处理键盘按键事件。当按下左键、右键、上键或下键时,它会更新图片的起始位置,并调用drawPixFunc函数进行绘制操作。最后,它更新了界面,使更改可见。
相关推荐
百事老饼干8 分钟前
Java[面试题]-真实面试
java·开发语言·面试
码农客栈15 分钟前
qt QWebSocketServer详解
qt
可均可可29 分钟前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite
杨荧1 小时前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰1 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_011 小时前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
gkdpjj1 小时前
C++优选算法十 哈希表
c++·算法·散列表
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。1 小时前
c++多线程
java·开发语言
-Even-1 小时前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus