(13) Qt事件系统(two)

目录

事件分发函数

无边框窗口拖动

自定义事件

发送事件的函数

自定义事件

系统定义的事件号

自定义事件号

自定义事件类

发送和处理事件

sendEvent与postEvent的区别

栈区对象

堆区对象

事件传播机制

事件传播的过程

事件传播到父组件

鼠标单击事件与按钮单击信号的关联

事件过滤

无边框窗口拖动(事件过滤器实现)

事件跟信号的区别


事件分发函数

传递事件的通常方式是调用虚函数。例如,QMouseEvent通过调用QWidget::mousePressEvent()来传递。这个虚函数负责进行适当的响应,通常是处理鼠标点击,并发出对应的信号。如果在虚函数的实现中不执行所有必要的工作,则可能需要调用基类的实现。

如果希望替换基类的事件处理函数,则必须自己实现所有内容。但是,如果您只想扩展基类的功能,那么您可以实现您想要的功能,并调用基类来获得您不想处理的任何情况的默认行为。

有时,没有这样一个特定于事件的函数,或者特定于事件的函数是不够的。最常见的例子是按Tab键。通常情况下,QWidget会拦截这些来移动键盘焦点,但也有一些小部件本身需要Tab键。

这些对象可以重新实现QObject::event()(通用事件处理程序),并在通常处理之前或之后执行它们的事件处理,或者它们可以完全替换函数。一个非常不寻常的小部件既解释Tab又具有特定于应用程序的自定义事件,它可能包含以下event()函数:

bool Widget::event(QEvent* ev)override
{//如果是按键按下事件if (ev->type() == QEvent::Type::KeyPress){QKeyEvent* ke = static_cast<QKeyEvent*>(ev);if (ke->key() == Qt::Key::Key_Tab){//对tab键按下进行处理qInfo() << "处理 tab 按键...";return true;    //返回true表示此事件已经处理了,不会继续传播}}// 调用父类实现处理其它的事件return QWidget::event(ev);
}

注意:对于所有未处理的情况,仍然调用QWidget::event(),并且返回值指示是否处理了事件;true值防止事件被发送到其他对象。返回true表示此事件已经处理了,不需要再处理了。

无边框窗口拖动

使用事件分发函数实现无边框窗口的拖动

bool event(QEvent* ev)override
{if (ev->type() == QEvent::MouseButtonPress){auto me = static_cast<QMouseEvent*>(ev);pressPos = me->pos();return true;}else if (ev->type() == QEvent::MouseMove){auto me = static_cast<QMouseEvent*>(ev);//如果鼠标左键是按下的if(me->buttons() & Qt::MouseButton::LeftButton)this->move(me->globalPosition().toPoint() - pressPos);return true;}return QWidget::event(ev);
}

自定义事件

发送事件的函数

许多应用程序都希望创建和发送它们自己的事件。通过构造合适的事件对象并使用QCoreApplication::sendEvent()和QCoreApplication::postEvent()发送事件,您可以以与Qt自己的事件循环完全相同的方式发送事件。

sendEvent()立即处理事件。当它返回时,事件过滤器和/或对象本身已经处理了该事件。对于许多事件类,都有一个名为isAccepted()的函数,它告诉您事件是被最后一个调用的处理程序接受还是拒绝的。

postEvent()将事件发送到队列中,以便稍后进行分派。下次Qt的主事件循环运行时,它会分发所有发布的事件,并进行一些优化。例如,如果有几个调整大小事件,它们将被压缩为一个。同样适用于绘制事件:QWidget::update()调用postEvent(),它通过避免多次重绘来消除闪烁并提高速度。

自定义事件

要创建自定义类型的事件,需要定义一个事件号,该事件号必须大于等于QEvent::User,并且可能需要继承QEvent的子类,以便传递关于自定义事件的特定信息。有关更多详细信息,请参阅QEvent文档。

系统定义的事件号

QEvent::Type 这个枚举类型定义了Qt中有效的事件类型。事件类型和每个类型的专门类如下:

常量值(value)描述
QEvent::None0不是一个事件
QEvent::ActionAdded114一个新 action 被添加(QActionEvent)
QEvent::ActionChanged113一个 action 被改变(QActionEvent)
QEvent::ActionRemoved115一个 action 被移除(QActionEvent)
QEvent::ActivationChange99Widget 的顶层窗口激活状态发生了变化
QEvent::ApplicationActivate121这个枚举已被弃用,使用 ApplicationStateChange 代替
QEvent::ApplicationActivatedApplicationActivate这个枚举已被弃用,使用 ApplicationStateChange 代替
QEvent::ApplicationDeactivate122这个枚举已被弃用,使用 ApplicationStateChange 代替
QEvent::ApplicationFontChange36应用程序的默认字体发生了变化
QEvent::ApplicationLayoutDirectionChange37应用程序的默认布局方向发生了变化
QEvent::ApplicationPaletteChange38应用程序的默认调色板发生了变化
QEvent::ApplicationStateChange214应用程序的状态发生了变化
QEvent::ApplicationWindowIconChange35应用程序的图标发生了变化
QEvent::ChildAdded68一个对象获得孩子(QChildEvent)
QEvent::ChildPolished69一个部件的孩子被抛光(QChildEvent)
QEvent::ChildRemoved71一个对象时区孩子(QChildEvent)
QEvent::Clipboard40剪贴板的内容发生改变
QEvent::Close19Widget 被关闭(QCloseEvent)
QEvent::CloseSoftwareInputPanel200一个部件要关闭软件输入面板(SIP)
QEvent::ContentsRectChange178部件内容区域的外边距发生改变
QEvent::ContextMenu82上下文弹出菜单(QContextMenuEvent)
QEvent::CursorChange183部件的鼠标发生改变
QEvent::DeferredDelete52对象被清除后将被删除(QDeferredDeleteEvent)
QEvent::DragEnter60在拖放操作期间鼠标进入窗口部件(QDragEnterEvent)
QEvent::DragLeave62在拖放操作期间鼠标离开窗口部件(QDragLeaveEvent)
QEvent::DragMove61拖放操作正在进行(QDragMoveEvent)
QEvent::Drop63拖放操作完成(QDropEvent)
QEvent::DynamicPropertyChange170动态属性已添加、更改或从对象中删除
QEvent::EnabledChange98部件的 enabled 状态已更改
QEvent::Enter10鼠标进入部件的边界(QEnterEvent)
QEvent::EnterEditFocus150编辑部件获得焦点进行编辑,必须定义 QT_KEYPAD_NAVIGATION
QEvent::EnterWhatsThisMode124当应用程序进入“What’s This?”模式,发送到 toplevel 顶层部件
QEvent::Expose206当其屏幕上的内容无效,发送到窗口,并需要从后台存储刷新
QEvent::FileOpen116文件打开请求(QFileOpenEvent)
QEvent::FocusIn8部件或窗口获得键盘焦点(QFocusEvent)
QEvent::FocusOut9部件或窗口失去键盘焦点(QFocusEvent)
QEvent::FocusAboutToChange23部件或窗口焦点即将改变(QFocusEvent)
QEvent::FontChange97部件的字体发生改变
QEvent::Gesture198触发了一个手势(QGestureEvent)
QEvent::GestureOverride202触发了手势覆盖(QGestureEvent)
QEvent::GrabKeyboard188Item 获得键盘抓取(仅限 QGraphicsItem)
QEvent::GrabMouse186项目获得鼠标抓取(仅限 QGraphicsItem)
QEvent::GraphicsSceneContextMenu159在图形场景上的上下文弹出菜单(QGraphicsScene ContextMenuEvent)
QEvent::GraphicsSceneDragEnter164在拖放操作期间,鼠标进入图形场景(QGraphicsSceneDragDropEvent)
QEvent::GraphicsSceneDragLeave166在拖放操作期间鼠标离开图形场景(QGraphicsSceneDragDropEvent)
QEvent::GraphicsSceneDragMove165在场景上正在进行拖放操作(QGraphicsSceneDragDropEvent)
QEvent::GraphicsSceneDrop167在场景上完成拖放操作(QGraphicsSceneDragDropEvent)
QEvent::GraphicsSceneHelp163用户请求图形场景的帮助(QHelpEvent)
QEvent::GraphicsSceneHoverEnter160鼠标进入图形场景中的悬停项(QGraphicsSceneHoverEvent)
QEvent::GraphicsSceneHoverLeave162鼠标离开图形场景中一个悬停项(QGraphicsSceneHoverEvent)
QEvent::GraphicsSceneHoverMove161鼠标在图形场景中的悬停项内移动(QGraphicsSceneHoverEvent)
QEvent::GraphicsSceneMouseDoubleClick158鼠标在图形场景中再次按下(双击)(QGraphicsSceneMouseEvent)
QEvent::GraphicsSceneMouseMove155鼠标在图形场景中移动(QGraphicsSceneMouseEvent)
QEvent::GraphicsSceneMousePress156鼠标在图形场景中按下(QGraphicsSceneMouseEvent)
QEvent::GraphicsSceneMouseRelease157鼠标在图形场景中释放(QGraphicsSceneMouseEvent)
QEvent::GraphicsSceneMove182部件被移动(QGraphicsSceneMoveEvent)
QEvent::GraphicsSceneResize181部件已调整大小(QGraphicsSceneResizeEvent)
QEvent::GraphicsSceneWheel168鼠标滚轮在图形场景中滚动(QGraphicsSceneWheelEvent)
QEvent::Hide18部件被隐藏(QHideEvent)
QEvent::HideToParent27子部件被隐藏(QHideEvent)
QEvent::HoverEnter127鼠标进入悬停部件(QHoverEvent)
QEvent::HoverLeave128鼠标留离开悬停部件(QHoverEvent)
QEvent::HoverMove129鼠标在悬停部件内移动(QHoverEvent)
QEvent::IconDrag96窗口的主图标被拖走(QIconDragEvent)
QEvent::IconTextChange101部件的图标文本发生改变(已弃用)
QEvent::InputMethod83正在使用输入法(QInputMethodEvent)
QEvent::InputMethodQuery207输入法查询事件(QInputMethodQueryEvent)
QEvent::KeyboardLayoutChange169键盘布局已更改
QEvent::KeyPress6键盘按下(QKeyEvent)
QEvent::KeyRelease7键盘释放(QKeyEvent)
QEvent::LanguageChange89应用程序翻译发生改变
QEvent::LayoutDirectionChange90布局的方向发生改变
QEvent::LayoutRequest76部件的布局需要重做
QEvent::Leave11鼠标离开部件的边界
QEvent::LeaveEditFocus151编辑部件失去编辑的焦点,必须定义 QT_KEYPAD_NAVIGATION
QEvent::LeaveWhatsThisMode125当应用程序离开“What’s This?”模式,发送到顶层部件
QEvent::LocaleChange88系统区域设置发生改变
QEvent::NonClientAreaMouseButtonDblClick176鼠标双击发生在客户端区域外
QEvent::NonClientAreaMouseButtonPress174鼠标按钮按下发生在客户端区域外
QEvent::NonClientAreaMouseButtonRelease175鼠标按钮释放发生在客户端区域外
QEvent::NonClientAreaMouseMove173鼠标移动发生在客户区域外
QEvent::MacSizeChange177用户更改了部件的大小(仅限 OS X)
QEvent::MetaCall43通过 QMetaObject::invokeMethod() 调用异步方法
QEvent::ModifiedChange102部件修改状态发生改变
QEvent::MouseButtonDblClick4鼠标再次按下(QMouseEvent)
QEvent::MouseButtonPress2鼠标按下(QMouseEvent)
QEvent::MouseButtonRelease3鼠标释放(QMouseEvent)
QEvent::MouseMove5鼠标移动(QMouseEvent)
QEvent::MouseTrackingChange109鼠标跟踪状态发生改变
QEvent::Move13部件的位置发生改变(QMoveEvent)
QEvent::NativeGesture197系统检测到手势(QNativeGestureEvent)
QEvent::OrientationChange208屏幕方向发生改变(QScreenOrientationChangeEvent)
QEvent::Paint12需要屏幕更新(QPaintEvent)
QEvent::PaletteChange39部件的调色板发生改变
QEvent::ParentAboutToChange131部件的 parent 将要更改
QEvent::ParentChange21部件的 parent 发生改变
QEvent::PlatformPanel212请求一个特定于平台的面板
QEvent::PlatformSurface217原生平台表面已创建或即将被销毁(QPlatformSurfaceEvent)
QEvent::Polish75部件被抛光
QEvent::PolishRequest74部件应该被抛光
QEvent::QueryWhatsThis123如果部件有“What’s This?”帮助,应该接受事件
QEvent::ReadOnlyChange106部件的 read-only 状态发生改变
QEvent::RequestSoftwareInputPanel199部件想要打开软件输入面板(SIP)
QEvent::Resize14部件的大小发生改变(QResizeEvent)
QEvent::ScrollPrepare204对象需要填充它的几何信息(QScrollPrepareEvent)
QEvent::Scroll205对象需要滚动到提供的位置(QScrollEvent)
QEvent::Shortcut117快捷键处理(QShortcutEvent)
QEvent::ShortcutOverride51按下按键,用于覆盖快捷键(QKeyEvent)
QEvent::Show17部件显示在屏幕上(QShowEvent)
QEvent::ShowToParent26子部件被显示
QEvent::SockAct50Socket 激活,用于实现 QSocketNotifier
QEvent::StateMachineSignal192信号被传递到状态机(QStateMachine::SignalEvent)
QEvent::StateMachineWrapped193事件是一个包装器,用于包含另一个事件(QStateMachine::WrappedEvent)
QEvent::StatusTip112状态提示请求(QStatusTipEvent)
QEvent::StyleChange100部件的样式发生改变
QEvent::TabletMove87Wacom 写字板移动(QTabletEvent)
QEvent::TabletPress92Wacom 写字板按下(QTabletEvent)
QEvent::TabletRelease93Wacom 写字板释放(QTabletEvent)
QEvent::OkRequest94Ok 按钮在装饰前被按下,仅支持 Windows CE
QEvent::TabletEnterProximity171Wacom 写字板进入接近事件(QTabletEvent),发送到 QApplication
QEvent::TabletLeaveProximity172Wacom 写字板离开接近事件(QTabletEvent),发送到 QApplication
QEvent::ThreadChange22对象被移动到另一个线程。这是发送到此对象的最后一个事件在上一个线程中,参见:QObject::moveToThread()
QEvent::Timer1定时器事件(QTimerEvent)
QEvent::ToolBarChange120工具栏按钮在 OS X 上进行切换
QEvent::ToolTip110一个 tooltip 请求(QHelpEvent)
QEvent::ToolTipChange184部件的 tooltip 发生改变
QEvent::TouchBegin194触摸屏或轨迹板事件序列的开始(QTouchEvent)
QEvent::TouchCancel209取消触摸事件序列(QTouchEvent)
QEvent::TouchEnd196触摸事件序列结束(QTouchEvent)
QEvent::TouchUpdate195触摸屏事件(QTouchEvent)
QEvent::UngrabKeyboard189Item 失去键盘抓取(QGraphicsItem)
QEvent::UngrabMouse187Item 失去鼠标抓取(QGraphicsItem、QQuickItem)
QEvent::UpdateLater78部件应该排队在以后重新绘制
QEvent::UpdateRequest77部件应该被重绘
QEvent::WhatsThis111部件应该显示“What’s This”帮助(QHelpEvent)
QEvent::WhatsThisClicked118部件的“What’s This”帮助链接被点击
QEvent::Wheel31鼠标滚轮滚动(QWheelEvent)
QEvent::WinEventAct132发生了 Windows 特定的激活事件
QEvent::WindowActivate24窗口已激活
QEvent::WindowBlocked103窗口被模态对话框阻塞
QEvent::WindowDeactivate25窗户被停用
QEvent::WindowIconChange34窗口的图标发生改变
QEvent::WindowStateChange105窗口的状态(最小化、最大化或全屏)发生改变(QWindowStateChangeEvent)
QEvent::WindowTitleChange33窗口的标题发生改变
QEvent::WindowUnblocked104一个模态对话框退出后,窗口将不被阻塞
QEvent::WinIdChange203本地窗口的系统标识符发生改变
QEvent::ZOrderChange126部件的 z 值发生了改变,该事件不会发送给顶层窗口

用户事件的值应该介于 QEvent::User 和 QEvent::MaxUser之间。

常量描述
QEvent::User1000用户定义的事件
QEvent::MaxUser65535最后的用户事件 ID

为方便起见,可以使用 [static] int QEvent::registerEventType(int *hint* = -1) 函数来注册和存储一个自定义事件类型,这样做会避免意外地重用一个自定义事件类型。

自定义事件号

enum EventType 
{NumberEvent = QEvent::Type::User
};

自定义事件类

// 自定义事件类
class NumberChangeEvent : public QEvent
{
public:NumberChangeEvent(int number): QEvent(QEvent::Type(NumberEvent)), m_number(number){}~NumberChangeEvent(){qInfo() << __FUNCTION__;}qint32 getNumber(){ return m_number; }
private:int m_number;
};

发送和处理事件

#include <QApplication>
#include <QWidget>
#include <QEvent>
#include <QPushButton>// 自定义事件类型
enum EventType
{NumberEvent = QEvent::Type::User
};// 自定义事件类
class NumberChangeEvent : public QEvent
{
public:NumberChangeEvent(int number): QEvent(QEvent::Type(NumberEvent)), m_number(number){}~NumberChangeEvent(){qInfo() << __FUNCTION__;}qint32 getNumber(){ return m_number; }
private:int m_number;
};// 自定义事件处理
class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);m_btn = new QPushButton("发送自定义事件", this);// 点击按钮发送事件connect(m_btn, &QPushButton::clicked, this, [=](){static int i = 0;NumberChangeEvent ne(i++);//qApp->sendEvent(this, &ne);QApplication::sendEvent(this, &ne);});}~Widget(){}
protected:bool event(QEvent* ev) override{if(ev->type() == QEvent::Type(NumberEvent)){auto nev = static_cast<NumberChangeEvent*>(ev);qInfo() << "Number: " << nev->getNumber();}return QWidget::event(ev);}
private:QPushButton* m_btn;
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

除了使用事件分发函数event处理自定义事件外,也可以使用customEvent处理自定义事件,它是专门用来接收自定义事件。

void customEvent(QEvent* ev) override
{if(ev->type() == EventType::NumberEvent){auto nev = static_cast<NumberChangeEvent*>(ev);qInfo() << "Number: " << nev->getNumber();}
}

sendEvent与postEvent的区别

使用栈区对象与堆区对象分别对两种发送方式进行测试

栈区对象

connect(m_btn, &QPushButton::clicked, this, [=](){NumberChangeEvent ne1(1);QApplication::sendEvent(this, &ne1);NumberChangeEvent ne2(1);QApplication::postEvent(this, &ne2);
});

当使用postEvent发送栈区事件对象时会直接引发中断,因为postEvent是将事件放入事件队列中稍后处理,栈区事件对象出作用域后内存就会被释放,我认为这是引发中断的原因。而使用sendEvent发送栈区事件对象不会中断,内存也会自己释放(出作用域后释放)。

堆区对象

当使用sendEvent发送堆区对象后,内存不会被自动释放,需要手动释放。而使用postEvent发送堆区事件对象内存会自动释放,不必手动释放。

因此,当使用栈区对象时只能使用sendEvent发送事件,使用堆区对象时,使用sendEvent发送事件之后需要手动释放事件对象内存,使用postEvent发送事件会自动释放内存。另外,使用postEvent发送事件时可以设置事件的优先级,让发送的事件被优先处理。

事件传播机制

事件传播的过程

Qt的事件产生之后,不是直接传递给了对象的,需要经过一系列的过程。

  1. 事件首先由Qt的ServerApplication去接收来自于外部或内部的一些行为,鼠标点击,键盘输入,时钟事件等,分析并决定送往对应的对象去处理(内部管理机制),最后会调用[virtual] bool QCoreApplication::notify ( QObject * receiver, QEvent * event ) 去处理,当然这个是虚函数,你可以在子类去重新实现它 。

  2. 在notify(…)中,在发给对应的接收者前,会先把消息送给QApplication。所以如果想在你界面的Widget前先处理那些事 件,那么你可以给QApplication对象installEventFilter,然后在对应的eventFilter()里先把这些事件都给过一 遍,然后你可以过滤一些不必要事件。

  3. 如果QApplication没有处理那些事件,然后就是交给事件接收对象了。在这个对象接收前,也可以为这对象加一个事件过滤器,同样是 installEventFilter。

  4. 如果eventFilter没有过滤某些事件,那么就会将事件交给接收者的event()函数(你可以根据不同类型的事件尽情处理),如果event事件在接受者处理后,也不会上传给父类的event,否则会上传进入父类的event。

  5. 默认event()函数根据事件类型会调用不同的事件处理函数,类似mousePressEvent(),keyPressEvent()去分别处理他们。

事件传播到父组件

当控件忽略事件时,事件会继续往上传播,这里的传播是指传播给父组件

  • QEvent有 accept()函数 和ignore()函数:

    • accept():本组件处理该事件,这个事件就不会被继续传播给其父组件;

    • ignore():本组件不想要处理这个事件,这个事件会被继续传播给其父组件;

    • 值得注意的是所有的事件默认都是接受的(即不会传递到父组件)

  • 忽略和接受案例

 当默认接受事件时,在本组件处理之后,事件不会传递到父组件mousePressEvent处理。 

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>class Button : public QPushButton
{
public:Button(const QString& text,QWidget* parent = nullptr):QPushButton(text,parent){}void mousePressEvent(QMouseEvent* ev)override{qInfo() << __FUNCTION__;//默认是接受事件的,父组件不会收到鼠标点击事件ev->accept();//如果忽略事件,父组件就会收到鼠标点击事件//ev->ignore();}
};class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);Button* btn = new Button("text", this);}~Widget(){}
protected:void mousePressEvent(QMouseEvent* ev)override{qInfo() << __FUNCTION__;}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

 当忽略事件时,在本组件处理之后,事件会被传递到父组件mousePressEvent处理。

class Button : public QPushButton
{
public:Button(const QString& text,QWidget* parent = nullptr):QPushButton(text,parent){}void mousePressEvent(QMouseEvent* ev)override{qInfo() << __FUNCTION__;//默认是接受事件的,父组件不会收到鼠标点击事件//ev->accept();//如果忽略事件,父组件就会收到鼠标点击事件ev->ignore();}
};

 

  • event事件分发函数返回值的作用
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QMouseEvent>class Button : public QPushButton
{
public:Button(const QString& text,QWidget* parent = nullptr):QPushButton(text,parent){}bool event(QEvent* ev)override{if (ev->type() == QEvent::Type::MouseButtonPress){qInfo() << __FUNCTION__;return false;	//返回false,表示我没有处理这个事件,可以传递给父组件//return true;	//返回true,表示我处理了这个事件,不再继续传递了}return QPushButton::event(ev);}
};class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640,480);Button* btn = new Button("text", this);Q_UNUSED(btn)}~Widget(){}
protected:bool event(QEvent* ev)override{if (ev->type() == QEvent::Type::MouseButtonPress){qInfo() << __FUNCTION__;}return QWidget::event(ev);}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}
#include "main.moc"

事件分发函数event返回false,表示本组件没有处理这个事件,事件会被传递到父组件处理,返回true则表示本组件处理了这个事件,不需要再让父组件处理了。

鼠标单击事件与按钮单击信号的关联

class Button : public QPushButton
{
public:Button(const QString& text,QWidget* parent = nullptr):QPushButton(text,parent){}void mousePressEvent(QMouseEvent* ev) override{//return QPushButton::mousePressEvent(ev);}
};Button* btn = new Button("text", this);
connect(btn, &Button::clicked, this, [](){qInfo() << "鼠标单击";
});

 要想触发鼠标点击的信号与槽就必须处理鼠标按下事件

void mousePressEvent(QMouseEvent* ev) override
{return QPushButton::mousePressEvent(ev);
}

事件过滤

有的时候,对象需要查看(可能还需要拦截)传递给另一个对象的事件。例如,对话框通常想要过滤一些小部件的按键;例如,修改返回键处理。

QObject::installEventFilter()函数通过设置一个事件过滤器来实现这一点,使指定的过滤器对象在其QObject::eventFilter()函数中接收目标对象的事件。事件过滤器在目标对象处理事件之前处理事件,允许它根据需要检查和丢弃事件。可以使用QObject::removeEventFilter()函数删除现有的事件过滤器。

当调用过滤器对象的eventFilter()实现时,它可以接受或拒绝事件,并允许或拒绝对事件的进一步处理。如果所有事件过滤器都允许对事件进行进一步处理(每个返回false),则将事件发送到目标对象本身。如果其中一个停止处理(通过返回true),目标和任何后面的事件过滤器都不会看到事件。

无边框窗口拖动(事件过滤器实现)

class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640, 480);setWindowFlag(Qt::WindowType::FramelessWindowHint);setMouseTracking(true);// 安装事件过滤器this->installEventFilter(this);}bool eventFilter(QObject* object, QEvent* ev)override{auto w = qobject_cast<QWidget*>(object);if (!w)return false;if (ev->type() == QEvent::MouseButtonPress){auto me = static_cast<QMouseEvent*>(ev);pressPos = me->pos();return true;}else if (ev->type() == QEvent::MouseMove){auto me = static_cast<QMouseEvent*>(ev);if (me->buttons() & Qt::MouseButton::LeftButton)w->move(me->globalPosition().toPoint() - pressPos);// 已经处理了,不需要再处理MouseMoveEvent事件了return true;}return QObject::eventFilter(object, ev);}
private:QPoint pressPos;
};

在自己类里面重写eventFilter来过滤事件,但是如果我有很多个无边框窗口,都要实现移动,这个方法就不太方便了。可以先写一个拖拽控件的过滤器对象,然后哪儿需要使用,就直接加载事件过滤器对象即可。

// 无边框窗口拖拽的事件过滤器对象
class DragWidgetFilter :public QObject
{
public:DragWidgetFilter(QObject* parent = nullptr):QObject(parent){}bool eventFilter(QObject* object,QEvent*ev)override{auto w = qobject_cast<QWidget*>(object);if (!w)return false;if (ev->type() == QEvent::MouseButtonPress){auto me = static_cast<QMouseEvent*>(ev);pressPos = me->pos();}else if (ev->type() == QEvent::MouseMove){auto me = static_cast<QMouseEvent*>(ev);if (me->buttons() & Qt::MouseButton::LeftButton)w->move(me->globalPosition().toPoint() - pressPos);}return QObject::eventFilter(object,ev);}
private:QPoint pressPos;
};class Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget* parent = nullptr):QWidget(parent){resize(640, 480);setWindowFlag(Qt::WindowType::FramelessWindowHint);setMouseTracking(true);// 使用事件过滤器对象加载事件过滤器this->installEventFilter(new DragWidgetFilter(this));}
}

事件跟信号的区别

事件(QEvent)信号(SIGNAL)
与QObject的关系由具体对象进行处理由具体对象主动产生
对程序的影响改写事件处理函数可能导致程序行为发生改变信号是否存在对应的槽函数不会改变程序行为
两者的联系一般而言,信号在具体的事件处理函数中产生

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

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

相关文章

若依框架 - 对二次封装数据分页

LsDistrictController /*** 查询地段列表*/ApiOperation("查询地段列表")GetMapping("/list")public TableDataInfo list(LsDistrict lsDistrict) {startPage();Map<String, List> map lsDistrictService.selectLsDistrictList(lsDistrict);if (Col…

express学习笔记5 - 自定义路由异常处理中间件

修改router/index.js&#xff0c;添加异常处理中间件 *** 自定义路由异常处理中间件* 注意两点&#xff1a;* 第一&#xff0c;方法的参数不能减少* 第二&#xff0c;方法的必须放在路由最后*/ router.use((err, req, res, next) > {console.log(err);const msg (err &…

数字电路(一)

1、例题 1、进行DA数模转换器选型时&#xff0c;一般要选择主要参数有&#xff08; A&#xff09;、转换精度和转换速度。 A、分辨率 B、输出电流 C、输出电阻 D、模拟开关 2、下图所示电路的逻辑功能为&#xff08; B&#xff09; A、与门 B、或门 C、与非门 D、或非门 分析该…

程序员面试IT技术岗的三大技巧

文章目录 技巧一&#xff1a;深入研究意向企业技巧二&#xff1a;准备常见的面试问题技巧三&#xff1a;总结经历的面试题 在找工作时&#xff0c;面试是每位程序员必须经历的一关。面对众多求职者竞争激烈的情况&#xff0c;我们需要结合自己的现状&#xff0c;针对意向企业做…

C语言每日一题

今天分享的是一道牛客网上面的题目&#xff0c;链接在下面 有序序列合并 这道题做法有很多&#xff0c;最简单的是合并一起&#xff0c;然后用排序就行了&#xff0c;今天将一个最高效的办法&#xff0c;思路是两个数组第一项进行比较&#xff0c;小的先输出&#xff0c;输出的…

python3GUI--我的翻译器By:PyQt5(附下载地址)

文章目录 一&#xff0e;前言二&#xff0e;展示1.主界面2.段落翻译3.单词翻译 三&#xff0e;设计1.UI设计2.软件设计3.参考 四&#xff0e;总结 一&#xff0e;前言 很早之前写过一篇python3GUI–翻译器By:PyQt5&#xff08;附源码&#xff09; &#xff0c;但是发现相关引擎…

Java on VS Code 7 月更新|反编译器支持升级、代码补全性能提升、AI 相关更新及更多

作者&#xff1a;Nick Zhu 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到 Visual Studio Code for Java 的7月更新&#xff01;在这篇博客中&#xff0c;我们将为您提供有关反编译器支持的重要更新。此外&#xff0c;我们将分享更多最近代码补全性能提升的进展&#x…

【ChatGPT】基于WSL+Docker的ChatGPT PLUS共享服务部署

最近买了ChatGPT PLUS服务&#xff0c;想通过web服务将它共享给其他人使用&#xff0c;搜了一下目前GitHub上比较热门的服务有 ChatGPT-Next-Webchatgpt-web-share 其中chatgpt-web-share支持API和PLUS账号分享两种方式&#xff0c;且架构为PythonJSDocker&#xff0c;相对比…

【LeetCode】27. 移除元素

题目大概意思是剔除nums数组中出现的所有val值。可以用快慢双指针法来做。 快的指针在前面遍历找值不为val的元素的下标&#xff0c;慢的负责接收值不为val的元素&#xff0c;并及时更新数组。 class Solution {public int removeElement(int[] nums, int val) {//快慢指针法in…

【Liux下6818开发板(ARM)】实现简易相册

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…

介绍如何快速传输100G大文件传输方法

如何快速地将100GB的大文件从一个地方传送到另一个地方&#xff0c;这是许多人都遇到过的一个难题&#xff0c;无论是个人用户还是企业用户&#xff0c;都有这样的需求。在很多场合&#xff0c;我们需要将大文件迅速地发送到远程位置。比如&#xff0c;当我们需要对重要数据进行…

心法利器[93] | 谈校招:技术面

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

DeepVO 论文阅读

论文信息 题目&#xff1a;DeepVO Towards End-to-End Visual Odometry with Deep Recurrent Convolutional Neural Networks 作者&#xff1a;Sen Wang, Ronald Clark, Hongkai Wen and Niki Trigoni 代码地址&#xff1a;http://senwang.gitlab.io/DeepVO/ (原作者并没有开源…

插入排序算法

插入排序 算法说明与代码实现&#xff1a; 以下是使用Go语言实现的插入排序算法示例代码&#xff1a; package mainimport "fmt"func insertionSort(arr []int) {n : len(arr)for i : 1; i < n; i {key : arr[i]j : i - 1for j > 0 && arr[j] > …

Python web实战 | 使用 Flask 实现 Web Socket 聊天室

概要 今天我们学习如何使用 Python 实现 Web Socket&#xff0c;并实现一个实时聊天室的功能。本文的技术栈包括 Python、Flask、Socket.IO 和 HTML/CSS/JavaScript。 什么是 Web Socket&#xff1f; Web Socket 是一种在单个 TCP 连接上进行全双工通信的协议。它是 HTML5 中的…

【编程语言 · C语言 · 学生管理系统】

【编程语言 C语言 学生管理系统】https://mp.weixin.qq.com/s?__bizMzg4NTE5MDAzOA&mid2247491542&idx1&snf9b72a5af62a93bc902c5467056a9343&chksmcfade32ff8da6a3956be7d6a5dceb97de27e25157804abf8a3193272fa8ad68e78640ca33a5c&token1462056111&…

【大数据之Flume】四、Flume进阶之复制和多路复用、负载均衡和故障转移、聚合案例

1 复制和多路复用 &#xff08;1&#xff09;需求&#xff1a;使用 Flume-1 监控文件变动&#xff08;可以用Exec Source或Taildir Source&#xff09;&#xff0c;Flume-1 将变动内容传递给 Flume-2&#xff08;用Avro Sink传&#xff09;&#xff0c;&#xff08;用Avro Sou…

BugKu CTF(杂项篇MISC)—想要种子吗

BugKu CTF(杂项篇MISC)—想要种子吗 提 示: 描 述:flag{} 题目下载后是一张图片&#xff0c;打开如下。 一、工具 十六进制编辑器010 editor kali系统文件分离工具binwalk或者foremost 维吉尼亚密码 STEGHIDE图片隐写工具 文章所需的软件下载地址 ARCHPR压缩包密码破解…

美团2024校招6000人;伯克利博士讲Llama 2技术细节;互联网转行AIGC最全指北;技术进步周期与创客崛起 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 美团 2024 届校园招聘将录用 6000 人&#xff0c;技术类岗位扩招超 50% 美团招聘公众号宣布启动 2024 届校园招聘&#xff01;此次招聘…

PCB制版技术

1、在头脑里形成一个原理图----现在就下载AD9盖版&#xff0c;诞生了一个问题&#xff0c;电路板去哪里买&#xff0c;买了怎么焊接电路和芯片&#xff0c;怎样流程化批量制作电子产品 1.1 形成一个PCB板&#xff0c;形成一个结构 1.2 焊接&#xff0c;嫁接&#xff0c;组装等 …