Qt - 事件(Event)
Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件,当事件发生时,Qt将创建一个事件对象。Qt中所有事件类都继承自QEvent。在事件对象创建完毕之后,Qt将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给指定的事件处理函数(event handler)进行处理。
事件处理函数一般都是虚函数,都可以在子类中重写
一、事件处理方法
方法一:重写控件的事件处理函数:如重写keyPressEvent(),mousePressEvent()和paintEvent(),这是最常用的事件处理方法,我们已经看到过很多这样的例子了。
方法二:重写QObject::event(),在事件到达事件处理函数时处理它。在需要改变Tab键的惯用法时这样做。也可以处理那些没有特定事件处理函数的比较少见的事件类型(例如,QEvent::HoverEnter)。我们重写event()时,必须要调用基类的event(),由基类处理我们不需要处理的那些情况。
bool Button::event(QEvent *ev)
{
//对鼠标点击和双击进行处理,返回true表示已处理,事件不会往下分发
if(ev->type() == QEvent::Type::MouseButtonPress || ev->type() == QEvent::Type::MouseButtonDblClick)
{
QMouseEvent* mouseEv = static_cast<QMouseEvent*>(ev);
qDebug()<<mouseEv->pos()<<mouseEv->globalPos();
return true;
}
return QPushButton::event(ev); //其他事件交给父类处理
}
方法三:给QObject对象安装事件过滤器:对象用installEventFilter()后,所有达到目标控件的事件都首先到达监视对象的eventFilter()函数。如果一个对象有多个事件过滤器,过滤器按顺序激活,先到达最近安装的监视对象,最后到达最先安装的监视对象。
方法四:给QApplication安装事件过滤器,如果qApp(唯一的QApplication对象)安装了事件过滤器,程序中所有对象的事件都要送到eventFilter()函数中。这个方法在调试的时候非常有用,在处理非活动状态控件的鼠标事件时这个方法也很常用。
方法五:继承QApplication,重写notify()。Qt调用QApplication::nofity()来发送事件。重写这个函数是在其他事件过滤器处理事件前得到所有事件的唯一方法。通常事件过滤器是最有用的,因为在同一时间,可以有任意数量的事件过滤器,但是notify()函数只有一个。
例如点击按钮时,你想滴滴滴的一声,你不可能每一个按钮对应槽函数设置,重写QCoreApplication::notify()即可
class MyApplication:public QApplication
{
public:
using QApplication::QApplication;
bool notify(QObject *obj, QEvent *ev) override
{
if(ev->type() == QEvent::Type::MouseButtonPress)
{
if(obj->inherits("QAbstractButton"))
{
qDebug()<<"IApplication";
QApplication::beep(); //响铃
return true; //返回true 所有继承自QAbstractButton的类都接收不到鼠标按下事件了
}
}
return QApplication::notify(obj,ev);
}
};
在实际编程中,最常用的是方法一,其次是方法三。因为方法五需要继承自 QApplication 类;而方法四要使用一个全局的事件过滤器,这将减缓事件的传递,所以,虽然这两种方法功能很强大,但是却很少被用到。
二、QWidget事件
1. 鼠标事件
QMouseEvent 类用来表示一个鼠标事件,当在窗口部件中按下鼠标、释放鼠标和移动鼠标指针时,都会产生鼠标事件 QMouseEvent。利用 QMouseEvent 类可以获知鼠标是哪个键按下释放了、鼠标指针的当前位置等信息。通常是重定义窗口部件的鼠标事件处理函数来进行一些自定义的操作。
- Qt 中的 QMouseEvent 一般只涉及按下鼠标、释放鼠标和移动鼠标指针等操作,而对鼠标滚轮的响应则通过 QWheeEvent 来处理。
- 鼠标移动事件只会在按下鼠标按键的情况下才会发生,除非通过显式调用。
- QWidget::setMouseTracking() 函数来开启鼠标轨迹,这种情况下只要鼠标指针在移动,就会产生一系列的 Qt 鼠标事件。
QMouseEvent 的传递
多个重叠的窗口在实现里好比一个递归的倒立树,鼠标事件会沿着鼠标指针所在的父窗口的链表向上传递,直到某个窗口调用 accept() 函数进行事件处理,否则该事件将被过滤销毁掉。
- 如果想要鼠标指针所在的父窗口不接收该事件,则可以调用函数 ignore() 予以忽略。
- 如果一个鼠标事件传递给鼠标指针所在的窗口,而该窗口的 QT::WA_NoMousePropagation 位置为TRUE,则该事件不会通过父窗口继续向上传递。
- 可以使用 QWidget::setEnabled() 来开启/关闭对应的窗口是否接受键盘和鼠标事件。
鼠标事件使用的时候,加头文件
#include <QMouseEvent>
1.1 鼠标按下事件
void Widget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
// 处理鼠标左键按下事件
qDebug() << "Left Button Pressed";
}
else if (event->button() == Qt::RightButton)
{
// 处理鼠标右键按下事件
qDebug() << "Right Button Pressed";
}
}
1.2 鼠标移动事件
默认情况下,触发事件需要点击一下,才能触发。可设置为自动触发:setMouseTracking(true);
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
this->setMouseTracking(true);//设置默认追踪鼠标,否则在触发鼠标移动时,必须先点一下才有效
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
//必须使用buttons()判断,鼠标当前所有状态都通过这个函数返回
if(event->buttons() & Qt::MouseButton::LeftButton)
{
qDebug()<<"左键按下并移动";
}
else if(event->buttons() & Qt::MouseButton::RightButton)
{
qDebug() <<"右键按下并移动";
}
// 获取移动过程中的鼠标指针坐标
qDebug()<<"x = "<<event->x()<<"y = "<<event->y();//鼠标在窗口上的坐标
qDebug()<<event->pos();//相对于窗口的坐标
qDebug()<<event->globalPos()<<endl;//相对于屏幕的坐标
}
1.3 鼠标释放事件
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
// 处理鼠标左键释放事件
qDebug() << "Left Button Released";
}
else if (event->button() == Qt::RightButton)
{
// 处理鼠标右键释放事件
qDebug() << "Right Button Released";
}
}
1.4 鼠标双击事件
void Widget::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) // 检查是否是右键被双击
{
// 处理右键双击事件
qDebug("鼠标右键被双击");
}
else if(event->button() == Qt::LeftButton)// 检查是否是左键被双击
{
// 处理左键双击事件
qDebug("鼠标左键被双击");
}
QWidget::mouseDoubleClickEvent(event);
}
注意:mouseMoveEvent不执行有以下几种情况
————————————————————————————————
1.单独一个widgt如果mouseMove不执行需要设置setMouseTracking(true);
2.如果有子窗口,需要子窗口也设置setMouseTracking(true);否则子窗口区域捕获不到鼠标移动
3.子窗口重写了mouseMoveEvent,需要在函数结尾调用父类的mosueMoveEvent,不然会在子窗口被截获
4.对于一些特殊的窗口本身就带好几层窗口,需要把每一层都设置setMouseTracking(true);
例如QScrollWidget
ui->scrollArea->setMouseTracking(true);
ui->scrollArea->widget()->setMouseTracking(true);
————————————————————————————————
1.5 鼠标滚轮事件
void Widget::wheelEvent(QWheelEvent *event)
{
static int x=0;
x += event->delta();//还是±120
if(event->delta()>0)
{
qDebug()<<"滚轮往前"<<x;
}
else
{
qDebug()<<"滚轮往后"<<x;
}
}
1.6 鼠标移入事件
当鼠标进入小部件时产生
void Widget::enterEvent(QEvent *event)
{
static int n=0;
qDebug()<<"鼠标移入了窗口"<<n++;
}
1.7 鼠标移出事件
当鼠标移出小部件时产生
void Widget::leaveEvent(QEvent *event)
{
static int n=0;
qDebug()<<"鼠标移出了窗口"<<n++;
}
Example:无边框窗口拖动
framelessWindow.h
#include <QWidget>
#include <QPoint>
class framelessWindow : public QWidget
{
Q_OBJECT
public:
framelessWindow(QWidget *parent = Q_NULLPTR);
void mousePressEvent(QMouseEvent* event)override;
void mouseMoveEvent(QMouseEvent* event)override;
private:
QPoint pos; //鼠标距离窗口左上角的距离(x y方向一起存储)
};
framelessWindow.cpp
#include "framelessWindow.h"
#include<QEvent>
#include<QMouseEvent>
framelessWindow::framelessWindow(QWidget *parent): QWidget(parent)
{
this->setWindowFlags(Qt::FramelessWindowHint);//窗口去标题栏,并且会去掉窗口拖拽移动功能和窗口自由拉伸功能
this->setMouseTracking(true);//设置鼠标追踪
resize(430, 330);
}
void framelessWindow::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
pos = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
}
void framelessWindow::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
move(event->globalPos() - pos);
event->accept();
}
}
2. 键盘事件
键盘按键事件通过QKeyEvent处理,包含了用于描述键盘事件的参数。
当在一个窗口里按住鼠标按键、或移动、或释放就会产生鼠标事件QMouseEvent。
-
int key() 返回按下或释放的键的代码
-
Qt::KeyboardModifiers modifiers() 返回事件发生后立即存在的键盘修饰符标志
需要对键盘事件进行处理时,通常要重新实现以下两个事件处理函数
2.1 按键按下事件
void Widget::keyPressEvent(QKeyEvent *event)
{
//单个键
if(event->key() == Qt::Key_Up)
{
qDebug()<<"上键按下";
}
else if(event->key() == Qt::Key_Down)
{
qDebug()<<"下键按下";
}
else if(event->key() == Qt::Key_Left)
{
qDebug()<<"左键按下";
}
else if(event->key() == Qt::Key_Right)
{
qDebug()<<"右键按下";
}
//组合键 Ctrl + A
if(event->modifiers() == Qt::KeyboardModifier::ControlModifier &&
event->key() == Qt::Key_A)
{
qDebug()<<"Ctrl + A";
}
}
2.2 按键释放事件
void Widget::keyReleaseEvent(QKeyEvent *event)
{
//单个键
if(event->key() == Qt::Key_Up)
{
qDebug()<<"上键释放";
}
else if(event->key() == Qt::Key_Down)
{
qDebug()<<"下键释放";
}
else if(event->key() == Qt::Key_Left)
{
qDebug()<<"左键释放";
}
else if(event->key() == Qt::Key_Right)
{
qDebug()<<"右键释放";
}
//组合键 Ctrl + A
if(event->modifiers() == Qt::KeyboardModifier::ControlModifier &&
event->key() == Qt::Key_A)
{
qDebug()<<"Ctrl + A 释放";
}
}
3. 窗口事件
3.1 窗口关闭事件
当Qt从窗口系统接收到一个顶级小部件的窗口关闭请求时,该事件处理程序将与给定的事件一起调用。
默认情况下,接受事件并关闭小部件。 您可以重新实现这个函数,以更改小部件响应窗口关闭请求的方式。 例如,您可以通过对所有事件调用ignore()来防止窗口关闭。
主窗口应用程序通常使用此函数的重新实现来检查用户的工作是否已被保存,并在关闭前请求许可。
void Widget::closeEvent(QCloseEvent *event)
{
if (maybeSave()) //如果还有需要保存的数据
{
writeSettings();
event->accept();
}
else //取消关闭窗口
{
event->ignore();
}
}
其中QCloseEvent继承与QEvent,在QEvent中常用成员函数有
void accept (); //接收者处理当前事件
void ignore (); //接收者忽略当前事件,忽略后,事件可能传递给父组件
bool isAccepted(); //判断当前事件是否被处理过
3.2 窗口显示事件
注意:当窗口系统改变窗口的映射状态时,窗口小部件会收到自发的显示和隐藏事件,例如,当用户最小化窗口时,会收到自发的隐藏事件,当窗口再次恢复时,会收到自发的显示事件。
void Widget::showEvent(QShowEvent *event)
{
qDebug()<<"窗口显示啦~";
}
3.3 窗口隐藏事件
void Widget::hideEvent(QHideEvent *event)
{
qDebug()<<"窗口隐藏啦~";
}
3.4 窗口大小改变事件
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug()<<"原来的大小"<<event->oldSize()<<"现在大小"<<event->size();
}
3.5 窗口移动事件
void Widget::moveEvent(QMoveEvent *event)
{
qDebug()<<"原来的pos"<<event->oldPos()<<"现在的pos"<<event->pos();
}
Example:子窗口跟随主窗口进行移动
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void ResizeTestwgt(bool bResize);
void ShowplayingTitlebar(QPoint point, QSize size, bool bResize);
protected:
void resizeEvent(QResizeEvent *event);
void moveEvent(QMoveEvent *event);
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QDialog* Testwgt;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMouseEvent>
#include <QDebug>
#include <QMoveEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//-----------初始化子窗口
Testwgt = new QDialog(this);
Testwgt->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
Testwgt->setStyleSheet("background-color: rgb(55,55,55)");
Testwgt->setFixedSize(200, 600);
Testwgt->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::ResizeTestwgt(bool bResize)
{
QPoint point = mapToGlobal(QPoint(0, 0));//获取主窗口在屏幕上的坐标
int iWidth = width();
int iHight = 25;
int x = point.x()+iWidth+1;
int y = point.y();
ShowplayingTitlebar(QPoint(x, y), QSize(iWidth, iHight), bResize);
}
void MainWindow::ShowplayingTitlebar(QPoint point, QSize size, bool bResize)
{
if (bResize)
{
Testwgt->resize(size);
Testwgt->move(point);
//return;
}
if (Testwgt->isVisible() == false)
{
qDebug()<<"Testwgt不可见了88888888888";
Testwgt->resize(size);
Testwgt->move(point);;
Testwgt->show();
}
}
void MainWindow::moveEvent(QMoveEvent *event)
{
static int n=0;
ResizeTestwgt(true);
qDebug()<<"窗口移动了"<<n++;
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
ResizeTestwgt(true);
qDebug()<<"原来的大小"<<event->oldSize()<<"现在大小"<<event->size();
}
void MainWindow::on_pushButton_clicked()
{
Testwgt->setVisible(false);//设置Testwgt为不可见
}
运行结果:
4. 程序状态改变事件
可以通过提供的事件检索该事件中正在更改的状态。
void Widget::changeEvent(QEvent *event)
{
if(event->type() == QEvent::WindowStateChange)
{
if(this->windowState() == Qt::WindowState::WindowMinimized)
{
qDebug()<<"窗口最小化啦";
}
if(this->windowState() == Qt::WindowState::WindowNoState)
{
qDebug()<<"窗口正常显示啦";
}
if(this->windowState() == Qt::WindowState::WindowMaximized)
{
qDebug()<<"窗口最大化啦";
}
}
}
Qt窗口置顶激活
重载主窗体类的changeEvent函数,然后在这个重载函数内部激活qt窗口。
void Widget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange)
{
if (this->isActiveWindow())
{
// widget is now active
qDebug()<<"小部件现在处于活动状态";
return;
}
else
{
//休眠10毫秒
QThread::msleep(10);
//主窗体显示堆栈置顶
this->raise();
//显示窗体
this->show();
//激活主窗体
this->activateWindow();
// widget is now inactive
qDebug()<<"小部件现在处于非活动状态";
}
}
}
和上面方法类似,只是置顶使用函数不一样,样例如下(头文件不再赘述):
void Widget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange)
{
if (this->isActiveWindow())
{
// widget is now active
return;
}
else
{
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
//SetWindowPos((HWND)winId(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
//休眠10毫秒
QThread::msleep(10);
//主窗体显示堆栈置顶
this->raise();
//显示窗体
this->show();
//激活主窗体
this->activateWindow();
//widget is now inactive
}
}
}
最小化窗口然后重新显示:
#include <QThread>
#include <windows.h>
void Widget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange)
{
if (this->isActiveWindow())
{
// widget is now active
return;
}
else
{
activateWindow();
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
raise();//必须加,不然X11会不起作用
#ifdef Q_OS_WIN32 //windows必须加这个,不然windows10 会不起作用,具体参看activateWindow 函数的文档
HWND hForgroundWnd = GetForegroundWindow();
DWORD dwForeID = ::GetWindowThreadProcessId(hForgroundWnd, NULL);
DWORD dwCurID = ::GetCurrentThreadId();
::AttachThreadInput(dwCurID, dwForeID, TRUE);
::SetForegroundWindow((HWND)winId());
::AttachThreadInput(dwCurID, dwForeID, FALSE);
#endif // MAC_OS
//setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
::SetWindowPos((HWND)winId(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
//休眠10毫秒
QThread::msleep(10);
//主窗体显示堆栈置顶
this->raise();
//显示窗体
this->show();
//激活主窗体
this->activateWindow();
//widget is now inactive
}
}
}
5. 右键菜单事件
当小部件的contextMenuPolicy为Qt::DefaultContextMenu时,该处理程序被调用。
void Widget::contextMenuEvent(QContextMenuEvent *event)
{
menu->popup(event->globalPos());
}
这种方式是使用 Qt 中 QWidget类中的右键菜单函数 QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy) 来实现, 因为这个函数的参数可以指定不同的值, 因此不同参数对应的具体的实现方式也不同。
这个函数的函数原型如下:
void QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy);
//参数:
// - Qt::NoContextMenu --> 不能实现右键菜单
// - Qt::PreventContextMenu --> 不能实现右键菜单
// - Qt::DefaultContextMenu --> 基于事件处理器函数 QWidget::contextMenuEvent() 实现
// - Qt::ActionsContextMenu --> 添加到当前窗口中所有 QAction 都会作为右键菜单项显示出来
// - Qt::CustomContextMenu --> 基于 QWidget::customContextMenuRequested() 信号实现
6. 拖拽事件
当正在进行拖动并鼠标进入此小部件时,将调用此事件处理程序。
-
要想处理拖拽事件,必须调用函数this->setAcceptDrops(true); 接受拖拽
-
在dragEnterEvent中接受事件:event->accept();
6.1 拖拽进入
void DragArea::dragEnterEvent(QDragEnterEvent *event)
{
auto *mdata = event->mimeData();
if(mdata->hasUrls())
{
QString str;
foreach(auto &url, mdata->urls())
{
str.append(url.path());
qDebug()<<url.path();
}
this->setText(str);
}
event->accept();
}
6.2 拖拽移动
void DragArea::dragMoveEvent(QDragMoveEvent *event)
{
qDebug()<<"dragMove"<<event->mimeData()->text();
}
6.3 拖拽离开
void DragArea::dragLeaveEvent(QDragLeaveEvent *event)
{
qDebug()<<"dragLeave";
}
6.4 拖拽放开
void DragArea::dropEvent(QDropEvent *event)
{
qDebug()<<"drop"<<event->mimeData()->text();
}
7. 焦点系统
7.1 焦点窗口
所谓的焦点窗口,指的是当前时刻拥有键盘输入的窗口。
补充两个概念,活动窗口和前景窗口。活动窗口,指的是当前时刻与用户进行交互的窗口;前景窗口,指的是显示器最顶层窗口,前景窗口永远在其他窗口上方,不被遮挡。
不是所有的窗口都可以成为焦点窗口,只有“可获取焦点”的窗口,才有机会成为焦点窗口,Qt提供了如下接口,用于设置窗口是否是”可获取焦点“窗口:
void QWidget::setFocusPolicy(Qt::FocusPolicy policy);
Qt::FocusPolicy值 | 描述 |
---|---|
Qt::TabFocus | 与焦点链相关,详解见下一小节 |
Qt::ClickFocus | 单击该窗口,获得焦点 |
Qt::StrongFocus | TabFocus | ClickFocus | 0x8 |
Qt::WheelFocus | StrongFocus | 0x4 |
Qt::NoFocus | 不可获取焦点 |
setFocusPolicy()
的参数值可以是上述5个值中的任意一种,当参数值为前4个的时候,当前窗口可以获取焦点,当参数值为Qt::Nofocus
的时候,当前窗口不可获取焦点。
7.2 焦点链
Qt除了把窗口对象组织成树状结构外,还把这些窗口组织成了一个双向链表结构,这个链表叫做焦点链。焦点链上每个节点代表一个窗口,默认情况下,窗口在焦点链上的先后位置,与用户把窗口添加到窗口树的先后顺序有关,越早添加到窗口树上的窗口,其在焦点链中的位置越靠前。
通过按Tab
或者Shift+Tab
,可以实现焦点在各个窗口之间循环移动。焦点移动顺序与焦点链相关,它的移动规律如下:
1.点击Tab
键,焦点链指针向后移动,直至碰到第一个FocusPolicy
为TabFocus
的窗口,并设置该窗口为焦点窗口;
2.点击Shift+Tab
,焦点链指针向前移动,直至碰到第一个FocusPolicy
为TabFocus
的窗口,并设置该窗口为焦点窗口;
Qt提供了下列结构,用于获取焦点链信息:
//返回此部件焦点链中的下一个部件
QWidget* QWidget::nextInFocusChain() const;
//返回此部件焦点链中的前一个部件
QWidget* QWidget::previousInFocusChain() const;
此外,我们还可以调整焦点链中节点的先后顺序
// 将焦点顺序中的部件 second 放置在部件 first 之后
static void QWidget::setTabOrder(QWidget* first, QWidget* second);
比如,若默认的焦点链顺序为 a-b-c-d,则:
setTabOrder(d,c); //改变后焦点链的顺序为 a-b-d-c
setTabOrder(b,a); //改变后焦点链的顺序为 b-a-d-c
7.3 切换焦点窗口
下面这些API可以用于切换焦点窗口:
// 等同于focusNextPrevChild(true)
bool QWidget::focusNextChild();
// 等同于focusNextPrevChild(false)
bool QWidget::focusPreviousChild();
// next==true:设置焦点链中下个`FocusPolicy`为`TabFocus`的窗口为焦点窗口
// next==false:设置焦点链中前一个`FocusPolicy`为`TabFocus`的窗口为焦点窗口
bool QWidget::focusNextPrevChild(bool next);
// 设置当前窗口为焦点窗口
void QWidget::setFocus(Qt::FocusReason reason);
// 取消焦点窗口
void QWidget::clearFocus();
点击Tab
键,底层调用了focusNextChild()
,点击Shift+Tab
,底层调用了focusPreviousChild()
。
setFocus(Qt::FocusReason reason)
有两个作用:设置当前窗口为焦点窗口;提供FocusReason
信息。
在Qt中,setFocus()是QWidget类的一个成员函数,用于设置小部件的焦点。焦点是指用户当前正在与之交互的小部件。
调用setFocus()函数可以将焦点设置到特定的小部件上,使其接收键盘输入和处理键盘事件。这对于需要用户输入或者响应键盘操作的小部件非常有用,例如文本框、按钮等。
示例代码:
QPushButton *button = new QPushButton("Click me");
button->setFocus();
上述代码创建了一个按钮,并将焦点设置到该按钮上。这意味着当用户按下键盘时,该按钮将会接收到键盘事件并触发相应的操作。
需要注意的是,在一个窗口中只能有一个小部件拥有焦点,因此当调用setFocus()函数时,如果其他小部件已经拥有焦点,则它会失去焦点。
7.4 焦点事件
与焦点事件处理相关的函数如下:
void QWidget::focusInEvent(QFocusEvent *event);
void QWidget::focusOutEvent(QFocusEvent *event);
当焦点从一个部件移动到另一个部件时,会触发QFocusEvent
事件,这个事件会被发送给原焦点窗口和当前焦点窗口,原焦点窗口执行focusOutEvent()
,新焦点窗口执行focusInEvent()
。
7.5 焦点代理
顾名思义,就是代为接收焦点事件。比如,控件B是控件A的焦点代理,则当控件A获得焦点时,实际获得并处理焦点的是控件B。涉及到的接口:
//返回该窗口的焦点代理
QWidget* QWidget::focusProxy() const;
//设置该窗口的焦点代理为w
void QWidget::setFocusProxy(QWidget* w);
7.6 自定义焦点链
重载实现下列函数,可用于自定义焦点链。
bool QWidget::focusNextPrevChild(bool next);
8. 绘图事件
paintEvent事件
是一个绘图函数,它并不是由我们手动调用的,而是由系统自动调用的。
paintEvent调用时机:
- 窗口大小变化时:比如最大化、最小化、缩放窗口时,系统都会调用该函数。
- 手动调用:使用updata()函数,同样会触发系统调用paintEvent(),从而完成界面的刷新。
画图的基本操作:
需要重写paintEvent()事件,此事件在QPaintDevicewindows类下。
(1). 需要先创建一个画家对象,即使用QPainter的构造函数,构造函数的参数是所画设备的对象。使用画家调用画图或者写字函数,即可完成画图过程。
(2). 可以更换画笔风格。例如创建画笔对象QPen对象的构造函数,设置笔的宽度setWidth,设置画笔风格setStyle。设置完画笔的各种参数,需要让画家使用画笔,即画家调用setPen()函数。
(3). 还可以设置画刷,画刷其实就是对封闭的图形的填充。使用QBrush构造函数构造画刷对象,设置画刷的风格setStyle(),需要让画家使用画刷,即画家调用setBrush()。
(4). 画各种形状。如,画家调用画线函数drawLine(),画家调用画椭圆函数drawEllipse(),画家调用画矩形函数drawRect(),画家调用写字函数drawText()。
示例:
//类内声明
//添加画图事件
//在类QPaintDeviceWindows下
void paintEvent(QPaintEvent *event);
//类外实现
void Widget:: paintEvent(QPaintEvent *event)
{
//*********************画图的基本操作**************************************
//实例化一个画家,this指定的是绘图的设备,利用画家进行一个操作
QPainter painter(this);
//定义画笔对象和设置笔的颜色
QPen pen(QColor(255,0,0));
//设置画笔宽度
pen.setWidth(3);
//设置画笔风格
pen.setStyle(Qt::DashLine);
//让画家使用这个画笔
painter.setPen(pen); //笔要在画的动作之前
//定义画刷对象和设置画刷颜色
QBrush brush(Qt::cyan);
//设置画刷风格
brush.setStyle(Qt::BDiagPattern);
//画家使用画刷
painter.setBrush(brush);
//画线
painter.drawLine(QPoint(0,0),QPoint(100,100));
//画椭圆,长轴等于纵轴时,是圆
painter.drawEllipse(QPoint(100,100),100,50);
//画矩形
painter.drawRect(QRect(20,20,50,50)); //Rect参数分别是左顶点横纵、坐标、矩形长、宽
//写字
painter.drawText(QRect(10,200,150,50),"好好学习,天天向上");
}
三、QObject事件
1. 定时器事件
Widget::Widget(QWidget *parent): QWidget(parent)
{
oneTimer = startTimer(1000); //开启定时器,并保存定时器Id
}
void Widget::timerEvent(QTimerEvent *event)
{
if(event->timerId() == oneTimer)
{
static int i = 0;
qDebug()<<"定时器 oneTimer"<<i++;
if(i == 10)
{
killTimer(oneTimer); //杀死定时器
}
}
}
四、自定义事件
首先要明白的是:“在 Qt 里,一个事件就是一个对象,所有事件的祖先都来自于 QEvent”。意思就是说,只要有一个事件发生(如鼠标单击事件),此时就会有一个 QEvent 对象被创建出来,然后开始各种传送。由于 Qt 事件系统是依托于元对象系统的,所以所有的 QObject 类都可以接收/处理 QEvent 事件。
说起事件,其实无非就是围绕着“产生-发送-处理”这个基本流程来说的。
1. 如何产生一个事件?
我们从QEvent派生一个子类,取名为MyEvent~
注意的是,每个事件类都有一个唯一的类型标识:type值
class MyEvent : public QEvent
{
public:
MyEvent(const QString&name,int age);
~MyEvent();
friend QDebug operator<<(QDebug&debug,const MyEvent& myEvent);
friend QDebug operator<<(QDebug&debug,const MyEvent* myEvent);
QString name;
int age;
//保存自定义事件的类型,所有自定义MyEvent事件对象共享
inline static QEvent::Type myType = (QEvent::Type)QEvent::registerEventType();
};
MyEvent::MyEvent(const QString&name,int age)
:QEvent(myType)
,name(name)
,age(age)
{}
MyEvent::~MyEvent()
{
qDebug()<<"~MyEvent";
}
QDebug operator<<(QDebug &debug, const MyEvent *myEvent)
{
debug<<*myEvent;
}
QDebug operator<<(QDebug &debug, const MyEvent &myEvent)
{
debug<<"("<<myEvent.name<<myEvent.age<<")";
return debug;
}
好了,现在我们已经认识了 Qt 事件类长什么样了。接下来就是怎么把它发送出去了。
2. 如何发送一个事件?
Qt 提供了三个 static 事件发送函数:sendEvent、postEvent、sendPostEvents。函数原型如下:
void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
bool sendEvent(QObject *receiver, QEvent *event)
直接发送:sendEvent
这是最好理解的,两个参数中一个是要发给谁,另一个是发送什么事件。使用notify()函数将事件直接发送给接收方,返回从事件处理程序返回的值(阻塞式),因此事件被发送的时候,event 对象并不会被销毁,因此我们要在栈上创建 event 对象。
MyEvent ev("张三",18);
QApplication::sendEvent(this,&ev);
发到队列:postEvent
我们创建一个 Qt 程序的时候,一般在 main 下面会看到 QCoreApplication a(argc, argv) 以及 return a.exec() 的字样。这其实就是开启了 Qt 事件循环来维护一个事件队列,exec 的本质就是不停的调用 processEvent() 函数从队列中获取事件来处理。而 postEvent() 的作用就是把事件发送到这个队列中去。
这种方式不需要等待处理结果,只要把事件发到队列中就可以了,所以返回值是 void。由于事件队列会持有发送的事件对象,在事件被处理后会自动 delete 掉,所以我们必须在堆上创建 event 对象。
MyEvent *pev = new MyEvent("maye",20);
QApplication::postEvent(this,pev);
3. 如何处理一个事件?
创建了事件,发送了事件,接下来就是怎么接收处理事件了。
-
系统事件通过
virtual void event(QEvent *event)
处理 -
自定义事件通过
virtual void customEvent(QEvent *event)
处理,当然也可以通过event来处理
void Widget::customEvent(QEvent *ev)
{
if(ev->type() == MyEvent::myType)
{
MyEvent *myEvent = static_cast<MyEvent*>(ev);
qDebug()<<"customEvent"<<myEvent;
}
}
bool Widget::event(QEvent *ev)
{
if(ev->type() == MyEvent::myType)
{
MyEvent *myEvent = static_cast<MyEvent*>(ev);
qDebug()<<"event"<<myEvent;
return true;
}
return QWidget::event(ev);
}
需要注意的是,重写事件处理器函数时,如果不实现任何功能,最好调用基类的实现。就像上面的那段代码,Qt 本身就已经写了一大堆的实现了,你要是不写上 QWidget::event(ev)这个代码,那你写的这个继承于 QWidget类的 Widget就不会对鼠标点击产生任何反应。正所谓“你要不会干这事,叫你爸爸来做吧”。
至此,一个完整的事件处理过程已经说完了。此时的你应该不仅了解了 Qt 自带的类是如何处理事件的,而且写个自定义事件也是应该是能下手了。接下来我们对事件处理再说说其他方便的功能:过滤、接收/忽略。
五、事件传播机制
1. 事件分发器
概述: 事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。
event()函数主要用于事件的分发
如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。
1、如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
2、在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播。
3、记得不关心的事件 记得用父类的事件分发器处理。
声明分发器事件:
virtual bool event(QEvent *event);
重写分发器事件:
bool Widget::event(QEvent *event)
{
if(event->type() == QEvent::KeyPress) //键盘按下处理,其他事件让事件处理器自己处理,不能返回false
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);//把QEvent类型转为QKeyEvent
if(keyEvent->key() == Qt::Key_B) //如果是 键盘上的B键 将不会打印键值
{
return QWidget::event(event);
}
qDebug()<<(char)(keyEvent->key());
return true;
}
else if(event->type() == QEvent::MouseButtonPress)//鼠标按下事件
{
qDebug()<<"鼠标按下了";
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);//将event转成鼠标事件
if (mouseEvent->button() == Qt::LeftButton)
{
qDebug()<<"鼠标左键按下了";
}
else if (mouseEvent->button() == Qt::RightButton)
{
qDebug()<<"鼠标右键按下了";
}
}
else if(event->type() == QEvent::MouseMove)//鼠标移动事件 ,需要在构造函数中设置追踪鼠标
{
//鼠标移动处理事件,当返回true时就停止事件处理,所以在返回true前处理事件,打印坐标
QMouseEvent *ev = static_cast<QMouseEvent *>(event);//把QEvent类型转换为QMouseEvent
qDebug()<<QString("Mouse Move:(%1, %2)").arg(ev->x()).arg(ev->y());
return true; //如果返回true,事件停止传播
}
return QWidget::event(event);
}
2. 事件过滤器
概述: 通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器
1.在mylabel.cpp的构造函数中 安装事件过滤器
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QKeyEvent>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
//设置鼠标跟踪功能(用户不需要按下鼠标)
this->setMouseTracking(true);
//1.安装事件过滤器
this->installEventFilter(this);
}
2.在widget.h中声明 事件过滤器函数
protected:
bool eventFilter(QObject *watched, QEvent *event);
3.在widget.cpp中实现 事件过滤器函数
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
//写法一:指定触发的控件
//watched:触发的控件 event:具体的事件
if (watched == this)
{
if (event->type() == QEvent::MouseButtonPress)
{
qDebug()<<"鼠标按下了";
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
qDebug()<<"坐标 :x = "<<mouseEvent->x()<<"y = "<<mouseEvent->y()<<endl;
return true;//自己处理
}
}
//写法二:不指定
//以上代码等同与下面的鼠标按下
if(event->type() == QEvent::MouseButtonPress)
{
qDebug()<<"鼠标按下了";
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);//将event转成鼠标事件
if (mouseEvent->button() == Qt::LeftButton)
{
qDebug()<<"鼠标左键按下了";
}
else if (mouseEvent->button() == Qt::RightButton)
{
qDebug()<<"鼠标右键按下了";
}
}
if(event->type() == QEvent::MouseMove)//鼠标移动事件 ,需要在构造函数中设置追踪鼠标
{
//鼠标移动处理事件,当返回true时就停止事件处理,所以在返回true前处理事件,打印坐标
QMouseEvent *ev = static_cast<QMouseEvent *>(event);//把QEvent类型转换为QMouseEvent
qDebug()<<QString("Mouse Move:(%1, %2)").arg(ev->x()).arg(ev->y());
return true; //如果返回true,事件停止传播
}
//对于其他控件以及事件,统统交给父类处理
return QWidget::eventFilter(watched, event);
}
注意:
1、这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管很强大,但这种行为会严重降低整个应用程序的事件分发效率。
2、事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
再补充一个示例:
以下示例,如果鼠标点击了并且在矩形范围内,就跳转到一个网页链接。实现了按钮的效果。
bool CGUIDialogRecommendWeb::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)//鼠标按下
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << "mouse: x" << mouseEvent->pos().x() << " y" << mouseEvent->pos().y();
qDebug() <<"g_dscale="<< g_dscale ;
//如果鼠标点击free of adv区域内
if (mouseEvent->pos().x() > 158 * g_dscale && mouseEvent->pos().y() > 350 * g_dscale && mouseEvent->pos().x() < 332 * g_dscale && mouseEvent->pos().y() < 367 * g_dscale)
{
close();
m_controller->slotShowRegisterDialog();//显示注册对话框
return true;
}
//如果鼠标点击Get It Now区域内
else if (mouseEvent->pos().x() > 145 * g_dscale && mouseEvent->pos().y() > 300 * g_dscale && mouseEvent->pos().x() < 340 * g_dscale && mouseEvent->pos().y() < 342 * g_dscale)
{
close();
OpenUrl(MyData->g_strAdlinkEn);//打开网站链接
return true;
}
}
return QDialog::eventFilter(obj, event);
}
再补充一个示例:
判断鼠标位置是否在矩形内
在Qt中,判断鼠标位置是否在矩形内可以使用QRect类提供的contains()方法。以下是一个简单的示例代码:
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
m_rect = QRect(50, 50, 100, 35);//初始化按钮
}
Widget::~Widget()
{
delete ui;
}
bool Widget::event(QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress)//鼠标按下
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);//将event转成鼠标事件
QPoint point = mouseEvent->pos();
if(m_rect.contains(point))
{
qDebug()<<"鼠标在矩形内按下";
}
else
{
//qDebug()<<"鼠标不在矩形内";
}
}
return QWidget::event(event);
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawRect(m_rect); // 绘制矩形
int textX = m_rect.x()+40;
int textY = m_rect.y()+20;
painter.drawText(QPoint(textX,textY),"按钮");// 绘制文字
}
运行效果:
在这个例子中,我们创建了一个QWidget的子类RectangleWidget
,并在其paintEvent
中绘制了一个矩形。在mouseMoveEvent
中,我们通过QRect::contains
方法判断鼠标是否移动到了矩形内。
需要注意的是,这里的矩形坐标是相对于这个QWidget的,如果你需要判断的矩形在其他坐标系中,需要做相应的坐标转换。
再补充一个示例:
以下示例实现了标签可点击的效果
protected:
bool eventFilter(QObject *obj, QEvent *event);
private:
Ui::Widget *ui;
QLabel* Label;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
//设置鼠标跟踪功能(用户不需要按下鼠标)
this->setMouseTracking(true);
//1.加载事件过滤器
this->installEventFilter(this);
Label = new QLabel("我是标签",this);
Label->setFixedSize(100,35);
Label->installEventFilter(this);
Label->setCursor(Qt::PointingHandCursor);
}
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
if (obj == Label && event->type()== QEvent::MouseButtonPress)//鼠标点击了标签
{
qDebug()<<"Label 被点击了";
return true;
}
return false;
}
运行效果:
3. 事件传递过程
注意:这里事件的传播是在组件层次上面的,而不是依靠类继承机制(父对象而不是父类)。
示例:事件被组件对象处理后可能传递到其父组件
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void mousePressEvent(QMouseEvent *event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "mybutton.h"
#include <QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
MyButton *btn= new MyButton("MyButton",this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
qDebug() << "this:" << this << ", Widget::mousePressEvent";
}
mybutton.h
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include <QPushButton>
class MyButton : public QPushButton
{
public:
MyButton(QString text,QWidget* parent=nullptr);
void mousePressEvent(QMouseEvent* event);
};
#endif // MYBUTTON_H
mybutton.cpp
#include "mybutton.h"
#include <QDebug>
#include <QMouseEvent>
MyButton::MyButton(QString text, QWidget* parent):QPushButton(parent)
{
this->setText(text);
}
void MyButton::mousePressEvent(QMouseEvent *event)
{
event->ignore(); //accept()和ignore() 表示是否将事件传递出去,是传递给父组件,而不是父对象
qDebug() << "this:" << this << ", MyButton::mousePressEvent";
//QPushButton::mousePressEvent(event); // 调用父类QPushButton的mousePressEvent处理其他事件
}
如果需要执行父组件 事件处理,则 event->ignore()
点击MyButton按钮之后事件会被传递到父组件Widget上
如果忽略父组件事件处理,则注释 event->ignore(),或者注释 event->ignore() 加上 event->accept()也行。
//方式一
void MyButton::mousePressEvent(QMouseEvent *event)
{
//event->ignore(); //accept()和ignore() 表示是否将事件传递出去,是传递给父组件,而不是父对象
qDebug() << "this:" << this << ", MyButton::mousePressEvent";
QPushButton::mousePressEvent(event); // 调用父类QPushButton的mousePressEvent处理其他事件
}
//方式二
void MyButton::mousePressEvent(QMouseEvent *event)
{
//event->ignore(); //accept()和ignore() 表示是否将事件传递出去,是传递给父组件,而不是父对象
event->accept();//期望处理当前事件
qDebug() << "this:" << this << ", MyButton::mousePressEvent";
//QPushButton::mousePressEvent(event); // 调用父类QPushButton的mousePressEvent处理其他事件
}
这样点击MyButton按钮之后事件就不会被传递到父组件Widget上了。
六、事件的类型
QEvent 类是所有事件类的基类,事件对象包含事件参数。
Qt 的主事件循环(QCoreApplication::exec())从事件队列中获取本地窗口系统事件,将它们转化为 QEvents,然后将转换后的事件发送给 QObjects。
一般来说,事件来自底层窗口系统(spontaneous() 返回 true),但也可以使用 QCoreApplication::sendEvent() 和 QCoreApplication::postEvent()(spontaneous() 返回 false)来手动发送事件。
基本的 QEvent 只包含了一个事件类型参数。QEvent 的子类包含了额外的描述特定事件的参数。
1. QEvent::Type
这个枚举类型定义了Qt中有效的事件类型。事件类型和每个类型的专门类如下:
常量 | 值 | 描述 |
---|---|---|
QEvent::None | 0 | 不是一个事件 |
QEvent::ActionAdded | 114 | 一个新 action 被添加(QActionEvent) |
QEvent::ActionChanged | 113 | 一个 action 被改变(QActionEvent) |
QEvent::ActionRemoved | 115 | 一个 action 被移除(QActionEvent) |
QEvent::ActivationChange | 99 | Widget 的顶层窗口激活状态发生了变化 |
QEvent::ApplicationActivate | 121 | 这个枚举已被弃用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationActivated | ApplicationActivate | 这个枚举已被弃用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationDeactivate | 122 | 这个枚举已被弃用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationFontChange | 36 | 应用程序的默认字体发生了变化 |
QEvent::ApplicationLayoutDirectionChange | 37 | 应用程序的默认布局方向发生了变化 |
QEvent::ApplicationPaletteChange | 38 | 应用程序的默认调色板发生了变化 |
QEvent::ApplicationStateChange | 214 | 应用程序的状态发生了变化 |
QEvent::ApplicationWindowIconChange | 35 | 应用程序的图标发生了变化 |
QEvent::ChildAdded | 68 | 一个对象获得孩子(QChildEvent) |
QEvent::ChildPolished | 69 | 一个部件的孩子被抛光(QChildEvent) |
QEvent::ChildRemoved | 71 | 一个对象时区孩子(QChildEvent) |
QEvent::Clipboard | 40 | 剪贴板的内容发生改变 |
QEvent::Close | 19 | Widget 被关闭(QCloseEvent) |
QEvent::CloseSoftwareInputPanel | 200 | 一个部件要关闭软件输入面板(SIP) |
QEvent::ContentsRectChange | 178 | 部件内容区域的外边距发生改变 |
QEvent::ContextMenu | 82 | 上下文弹出菜单(QContextMenuEvent) |
QEvent::CursorChange | 183 | 部件的鼠标发生改变 |
QEvent::DeferredDelete | 52 | 对象被清除后将被删除(QDeferredDeleteEvent) |
QEvent::DragEnter | 60 | 在拖放操作期间鼠标进入窗口部件(QDragEnterEvent) |
QEvent::DragLeave | 62 | 在拖放操作期间鼠标离开窗口部件(QDragLeaveEvent) |
QEvent::DragMove | 61 | 拖放操作正在进行(QDragMoveEvent) |
QEvent::Drop | 63 | 拖放操作完成(QDropEvent) |
QEvent::DynamicPropertyChange | 170 | 动态属性已添加、更改或从对象中删除 |
QEvent::EnabledChange | 98 | 部件的 enabled 状态已更改 |
QEvent::Enter | 10 | 鼠标进入部件的边界(QEnterEvent) |
QEvent::EnterEditFocus | 150 | 编辑部件获得焦点进行编辑,必须定义 QT_KEYPAD_NAVIGATION |
QEvent::EnterWhatsThisMode | 124 | 当应用程序进入“What’s This?”模式,发送到 toplevel 顶层部件 |
QEvent::Expose | 206 | 当其屏幕上的内容无效,发送到窗口,并需要从后台存储刷新 |
QEvent::FileOpen | 116 | 文件打开请求(QFileOpenEvent) |
QEvent::FocusIn | 8 | 部件或窗口获得键盘焦点(QFocusEvent) |
QEvent::FocusOut | 9 | 部件或窗口失去键盘焦点(QFocusEvent) |
QEvent::FocusAboutToChange | 23 | 部件或窗口焦点即将改变(QFocusEvent) |
QEvent::FontChange | 97 | 部件的字体发生改变 |
QEvent::Gesture | 198 | 触发了一个手势(QGestureEvent) |
QEvent::GestureOverride | 202 | 触发了手势覆盖(QGestureEvent) |
QEvent::GrabKeyboard | 188 | Item 获得键盘抓取(仅限 QGraphicsItem) |
QEvent::GrabMouse | 186 | 项目获得鼠标抓取(仅限 QGraphicsItem) |
QEvent::GraphicsSceneContextMenu | 159 | 在图形场景上的上下文弹出菜单(QGraphicsScene ContextMenuEvent) |
QEvent::GraphicsSceneDragEnter | 164 | 在拖放操作期间,鼠标进入图形场景(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneDragLeave | 166 | 在拖放操作期间鼠标离开图形场景(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneDragMove | 165 | 在场景上正在进行拖放操作(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneDrop | 167 | 在场景上完成拖放操作(QGraphicsSceneDragDropEvent) |
QEvent::GraphicsSceneHelp | 163 | 用户请求图形场景的帮助(QHelpEvent) |
QEvent::GraphicsSceneHoverEnter | 160 | 鼠标进入图形场景中的悬停项(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneHoverLeave | 162 | 鼠标离开图形场景中一个悬停项(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneHoverMove | 161 | 鼠标在图形场景中的悬停项内移动(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneMouseDoubleClick | 158 | 鼠标在图形场景中再次按下(双击)(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMouseMove | 155 | 鼠标在图形场景中移动(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMousePress | 156 | 鼠标在图形场景中按下(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMouseRelease | 157 | 鼠标在图形场景中释放(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMove | 182 | 部件被移动(QGraphicsSceneMoveEvent) |
QEvent::GraphicsSceneResize | 181 | 部件已调整大小(QGraphicsSceneResizeEvent) |
QEvent::GraphicsSceneWheel | 168 | 鼠标滚轮在图形场景中滚动(QGraphicsSceneWheelEvent) |
QEvent::Hide | 18 | 部件被隐藏(QHideEvent) |
QEvent::HideToParent | 27 | 子部件被隐藏(QHideEvent) |
QEvent::HoverEnter | 127 | 鼠标进入悬停部件(QHoverEvent) |
QEvent::HoverLeave | 128 | 鼠标留离开悬停部件(QHoverEvent) |
QEvent::HoverMove | 129 | 鼠标在悬停部件内移动(QHoverEvent) |
QEvent::IconDrag | 96 | 窗口的主图标被拖走(QIconDragEvent) |
QEvent::IconTextChange | 101 | 部件的图标文本发生改变(已弃用) |
QEvent::InputMethod | 83 | 正在使用输入法(QInputMethodEvent) |
QEvent::InputMethodQuery | 207 | 输入法查询事件(QInputMethodQueryEvent) |
QEvent::KeyboardLayoutChange | 169 | 键盘布局已更改 |
QEvent::KeyPress | 6 | 键盘按下(QKeyEvent) |
QEvent::KeyRelease | 7 | 键盘释放(QKeyEvent) |
QEvent::LanguageChange | 89 | 应用程序翻译发生改变 |
QEvent::LayoutDirectionChange | 90 | 布局的方向发生改变 |
QEvent::LayoutRequest | 76 | 部件的布局需要重做 |
QEvent::Leave | 11 | 鼠标离开部件的边界 |
QEvent::LeaveEditFocus | 151 | 编辑部件失去编辑的焦点,必须定义 QT_KEYPAD_NAVIGATION |
QEvent::LeaveWhatsThisMode | 125 | 当应用程序离开“What’s This?”模式,发送到顶层部件 |
QEvent::LocaleChange | 88 | 系统区域设置发生改变 |
QEvent::NonClientAreaMouseButtonDblClick | 176 | 鼠标双击发生在客户端区域外 |
QEvent::NonClientAreaMouseButtonPress | 174 | 鼠标按钮按下发生在客户端区域外 |
QEvent::NonClientAreaMouseButtonRelease | 175 | 鼠标按钮释放发生在客户端区域外 |
QEvent::NonClientAreaMouseMove | 173 | 鼠标移动发生在客户区域外 |
QEvent::MacSizeChange | 177 | 用户更改了部件的大小(仅限 OS X) |
QEvent::MetaCall | 43 | 通过 QMetaObject::invokeMethod() 调用异步方法 |
QEvent::ModifiedChange | 102 | 部件修改状态发生改变 |
QEvent::MouseButtonDblClick | 4 | 鼠标再次按下(QMouseEvent) |
QEvent::MouseButtonPress | 2 | 鼠标按下(QMouseEvent) |
QEvent::MouseButtonRelease | 3 | 鼠标释放(QMouseEvent) |
QEvent::MouseMove | 5 | 鼠标移动(QMouseEvent) |
QEvent::MouseTrackingChange | 109 | 鼠标跟踪状态发生改变 |
QEvent::Move | 13 | 部件的位置发生改变(QMoveEvent) |
QEvent::NativeGesture | 197 | 系统检测到手势(QNativeGestureEvent) |
QEvent::OrientationChange | 208 | 屏幕方向发生改变(QScreenOrientationChangeEvent) |
QEvent::Paint | 12 | 需要屏幕更新(QPaintEvent) |
QEvent::PaletteChange | 39 | 部件的调色板发生改变 |
QEvent::ParentAboutToChange | 131 | 部件的 parent 将要更改 |
QEvent::ParentChange | 21 | 部件的 parent 发生改变 |
QEvent::PlatformPanel | 212 | 请求一个特定于平台的面板 |
QEvent::PlatformSurface | 217 | 原生平台表面已创建或即将被销毁(QPlatformSurfaceEvent) |
QEvent::Polish | 75 | 部件被抛光 |
QEvent::PolishRequest | 74 | 部件应该被抛光 |
QEvent::QueryWhatsThis | 123 | 如果部件有“What’s This?”帮助,应该接受事件 |
QEvent::ReadOnlyChange | 106 | 部件的 read-only 状态发生改变 |
QEvent::RequestSoftwareInputPanel | 199 | 部件想要打开软件输入面板(SIP) |
QEvent::Resize | 14 | 部件的大小发生改变(QResizeEvent) |
QEvent::ScrollPrepare | 204 | 对象需要填充它的几何信息(QScrollPrepareEvent) |
QEvent::Scroll | 205 | 对象需要滚动到提供的位置(QScrollEvent) |
QEvent::Shortcut | 117 | 快捷键处理(QShortcutEvent) |
QEvent::ShortcutOverride | 51 | 按下按键,用于覆盖快捷键(QKeyEvent) |
QEvent::Show | 17 | 部件显示在屏幕上(QShowEvent) |
QEvent::ShowToParent | 26 | 子部件被显示 |
QEvent::SockAct | 50 | Socket 激活,用于实现 QSocketNotifier |
QEvent::StateMachineSignal | 192 | 信号被传递到状态机(QStateMachine::SignalEvent) |
QEvent::StateMachineWrapped | 193 | 事件是一个包装器,用于包含另一个事件(QStateMachine::WrappedEvent) |
QEvent::StatusTip | 112 | 状态提示请求(QStatusTipEvent) |
QEvent::StyleChange | 100 | 部件的样式发生改变 |
QEvent::TabletMove | 87 | Wacom 写字板移动(QTabletEvent) |
QEvent::TabletPress | 92 | Wacom 写字板按下(QTabletEvent) |
QEvent::TabletRelease | 93 | Wacom 写字板释放(QTabletEvent) |
QEvent::OkRequest | 94 | Ok 按钮在装饰前被按下,仅支持 Windows CE |
QEvent::TabletEnterProximity | 171 | Wacom 写字板进入接近事件(QTabletEvent),发送到 QApplication |
QEvent::TabletLeaveProximity | 172 | Wacom 写字板离开接近事件(QTabletEvent),发送到 QApplication |
QEvent::ThreadChange | 22 | 对象被移动到另一个线程。这是发送到此对象的最后一个事件在上一个线程中,参见:QObject::moveToThread() |
QEvent::Timer | 1 | 定时器事件(QTimerEvent) |
QEvent::ToolBarChange | 120 | 工具栏按钮在 OS X 上进行切换 |
QEvent::ToolTip | 110 | 一个 tooltip 请求(QHelpEvent) |
QEvent::ToolTipChange | 184 | 部件的 tooltip 发生改变 |
QEvent::TouchBegin | 194 | 触摸屏或轨迹板事件序列的开始(QTouchEvent) |
QEvent::TouchCancel | 209 | 取消触摸事件序列(QTouchEvent) |
QEvent::TouchEnd | 196 | 触摸事件序列结束(QTouchEvent) |
QEvent::TouchUpdate | 195 | 触摸屏事件(QTouchEvent) |
QEvent::UngrabKeyboard | 189 | Item 失去键盘抓取(QGraphicsItem) |
QEvent::UngrabMouse | 187 | Item 失去鼠标抓取(QGraphicsItem、QQuickItem) |
QEvent::UpdateLater | 78 | 部件应该排队在以后重新绘制 |
QEvent::UpdateRequest | 77 | 部件应该被重绘 |
QEvent::WhatsThis | 111 | 部件应该显示“What’s This”帮助(QHelpEvent) |
QEvent::WhatsThisClicked | 118 | 部件的“What’s This”帮助链接被点击 |
QEvent::Wheel | 31 | 鼠标滚轮滚动(QWheelEvent) |
QEvent::WinEventAct | 132 | 发生了 Windows 特定的激活事件 |
QEvent::WindowActivate | 24 | 窗口已激活 |
QEvent::WindowBlocked | 103 | 窗口被模态对话框阻塞 |
QEvent::WindowDeactivate | 25 | 窗户被停用 |
QEvent::WindowIconChange | 34 | 窗口的图标发生改变 |
QEvent::WindowStateChange | 105 | 窗口的状态(最小化、最大化或全屏)发生改变(QWindowStateChangeEvent) |
QEvent::WindowTitleChange | 33 | 窗口的标题发生改变 |
QEvent::WindowUnblocked | 104 | 一个模态对话框退出后,窗口将不被阻塞 |
QEvent::WinIdChange | 203 | 本地窗口的系统标识符发生改变 |
QEvent::ZOrderChange | 126 | 部件的 z 值发生了改变,该事件不会发送给顶层窗口 |
用户事件的值应该介于 QEvent::User 和 QEvent::MaxUser之间。
常量 | 值 | 描述 |
---|---|---|
QEvent::User | 1000 | 用户定义的事件 |
QEvent::MaxUser | 65535 | 最后的用户事件 ID |
为方便起见,可以使用 [static] int QEvent::registerEventType(int *hint* = -1)
函数来注册和存储一个自定义事件类型,这样做会避免意外地重用一个自定义事件类型。
2. 成员属性
-
accepted : bool
设置 accept 标志意味着接收器需要该事件,不需要的事件可能会被传递给它的父窗口。默认情况下,isAccepted() 设置为 true。不能依赖于此,因为子类可能会在子类构造器中清除该标志。
出于简便考虑,accept 标志可以通过 accept() 设置,ignore() 清除。
访问函数:
bool isAccepted() const
void setAccepted(bool accepted)
3. 成员函数
void QEvent::accept()
设置事件对象的 accept 标志,等价于 setAccepted(true)。
设置 accept 标志意味着接收器需要该事件,不需要的事件可能会被传递给它的父窗口。
void QEvent::ignore()
清除事件对象的 accept 标志,等价于 setAccepted(false)。
清除 accept 标志意味着事件接收器不需要该事件,不需要的事件可能会被传递给它的父窗口。
[static] int QEvent::registerEventType(int hint = -1)
注册并返回一个自定义事件类型。如果 hint 是合法的,则会返回这个值;否则,会返回介于 QEvent::User 和 QEvent::MaxUser 之间的一个尚未被注册的值。如果其值不在 QEvent::User 和 QEvent::MaxUser 之间,hint 值将被忽略。
如果可用的值被使用或程序关闭,将返回 -1
bool QEvent::spontaneous() const
如果事件由应用程序之外产生的,比如一个系统事件,返回 true,否则返回 false。
Type QEvent::type() const
返回事件的类型
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了