濟南網(wǎng)站優(yōu)化技術(shù)廠家全球疫情最新數(shù)據(jù)
目錄
事件分發(fā)函數(shù)
無邊框窗口拖動
自定義事件
發(fā)送事件的函數(shù)
自定義事件
系統(tǒng)定義的事件號
自定義事件號
自定義事件類
發(fā)送和處理事件
sendEvent與postEvent的區(qū)別
棧區(qū)對象
堆區(qū)對象
事件傳播機制
事件傳播的過程
事件傳播到父組件
鼠標單擊事件與按鈕單擊信號的關(guān)聯(lián)
事件過濾
無邊框窗口拖動(事件過濾器實現(xiàn))
事件跟信號的區(qū)別
事件分發(fā)函數(shù)
傳遞事件的通常方式是調(diào)用虛函數(shù)。例如,QMouseEvent通過調(diào)用QWidget::mousePressEvent()來傳遞。這個虛函數(shù)負責進行適當?shù)捻憫?yīng),通常是處理鼠標點擊,并發(fā)出對應(yīng)的信號。如果在虛函數(shù)的實現(xiàn)中不執(zhí)行所有必要的工作,則可能需要調(diào)用基類的實現(xiàn)。
如果希望替換基類的事件處理函數(shù),則必須自己實現(xiàn)所有內(nèi)容。但是,如果您只想擴展基類的功能,那么您可以實現(xiàn)您想要的功能,并調(diào)用基類來獲得您不想處理的任何情況的默認行為。
有時,沒有這樣一個特定于事件的函數(shù),或者特定于事件的函數(shù)是不夠的。最常見的例子是按Tab鍵。通常情況下,QWidget會攔截這些來移動鍵盤焦點,但也有一些小部件本身需要Tab鍵。
這些對象可以重新實現(xiàn)QObject::event()(通用事件處理程序),并在通常處理之前或之后執(zhí)行它們的事件處理,或者它們可以完全替換函數(shù)。一個非常不尋常的小部件既解釋Tab又具有特定于應(yīng)用程序的自定義事件,它可能包含以下event()函數(shù):
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表示此事件已經(jīng)處理了,不會繼續(xù)傳播}}// 調(diào)用父類實現(xiàn)處理其它的事件return QWidget::event(ev);
}
注意:對于所有未處理的情況,仍然調(diào)用QWidget::event(),并且返回值指示是否處理了事件;true值防止事件被發(fā)送到其他對象。返回true表示此事件已經(jīng)處理了,不需要再處理了。
無邊框窗口拖動
使用事件分發(fā)函數(shù)實現(xiàn)無邊框窗口的拖動
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);
}
自定義事件
發(fā)送事件的函數(shù)
許多應(yīng)用程序都希望創(chuàng)建和發(fā)送它們自己的事件。通過構(gòu)造合適的事件對象并使用QCoreApplication::sendEvent()和QCoreApplication::postEvent()發(fā)送事件,您可以以與Qt自己的事件循環(huán)完全相同的方式發(fā)送事件。
sendEvent()立即處理事件。當它返回時,事件過濾器和/或?qū)ο蟊旧硪呀?jīng)處理了該事件。對于許多事件類,都有一個名為isAccepted()的函數(shù),它告訴您事件是被最后一個調(diào)用的處理程序接受還是拒絕的。
postEvent()將事件發(fā)送到隊列中,以便稍后進行分派。下次Qt的主事件循環(huán)運行時,它會分發(fā)所有發(fā)布的事件,并進行一些優(yōu)化。例如,如果有幾個調(diào)整大小事件,它們將被壓縮為一個。同樣適用于繪制事件:QWidget::update()調(diào)用postEvent(),它通過避免多次重繪來消除閃爍并提高速度。
自定義事件
要創(chuàng)建自定義類型的事件,需要定義一個事件號,該事件號必須大于等于QEvent::User,并且可能需要繼承QEvent的子類,以便傳遞關(guān)于自定義事件的特定信息。有關(guān)更多詳細信息,請參閱QEvent文檔。
系統(tǒng)定義的事件號
QEvent::Type?這個枚舉類型定義了Qt中有效的事件類型。事件類型和每個類型的專門類如下:
常量 | 值(value) | 描述 |
QEvent::None | 0 | 不是一個事件 |
QEvent::ActionAdded | 114 | 一個新 action 被添加(QActionEvent) |
QEvent::ActionChanged | 113 | 一個 action 被改變(QActionEvent) |
QEvent::ActionRemoved | 115 | 一個 action 被移除(QActionEvent) |
QEvent::ActivationChange | 99 | Widget 的頂層窗口激活狀態(tài)發(fā)生了變化 |
QEvent::ApplicationActivate | 121 | 這個枚舉已被棄用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationActivated | ApplicationActivate | 這個枚舉已被棄用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationDeactivate | 122 | 這個枚舉已被棄用,使用 ApplicationStateChange 代替 |
QEvent::ApplicationFontChange | 36 | 應(yīng)用程序的默認字體發(fā)生了變化 |
QEvent::ApplicationLayoutDirectionChange | 37 | 應(yīng)用程序的默認布局方向發(fā)生了變化 |
QEvent::ApplicationPaletteChange | 38 | 應(yīng)用程序的默認調(diào)色板發(fā)生了變化 |
QEvent::ApplicationStateChange | 214 | 應(yīng)用程序的狀態(tài)發(fā)生了變化 |
QEvent::ApplicationWindowIconChange | 35 | 應(yīng)用程序的圖標發(fā)生了變化 |
QEvent::ChildAdded | 68 | 一個對象獲得孩子(QChildEvent) |
QEvent::ChildPolished | 69 | 一個部件的孩子被拋光(QChildEvent) |
QEvent::ChildRemoved | 71 | 一個對象時區(qū)孩子(QChildEvent) |
QEvent::Clipboard | 40 | 剪貼板的內(nèi)容發(fā)生改變 |
QEvent::Close | 19 | Widget 被關(guān)閉(QCloseEvent) |
QEvent::CloseSoftwareInputPanel | 200 | 一個部件要關(guān)閉軟件輸入面板(SIP) |
QEvent::ContentsRectChange | 178 | 部件內(nèi)容區(qū)域的外邊距發(fā)生改變 |
QEvent::ContextMenu | 82 | 上下文彈出菜單(QContextMenuEvent) |
QEvent::CursorChange | 183 | 部件的鼠標發(fā)生改變 |
QEvent::DeferredDelete | 52 | 對象被清除后將被刪除(QDeferredDeleteEvent) |
QEvent::DragEnter | 60 | 在拖放操作期間鼠標進入窗口部件(QDragEnterEvent) |
QEvent::DragLeave | 62 | 在拖放操作期間鼠標離開窗口部件(QDragLeaveEvent) |
QEvent::DragMove | 61 | 拖放操作正在進行(QDragMoveEvent) |
QEvent::Drop | 63 | 拖放操作完成(QDropEvent) |
QEvent::DynamicPropertyChange | 170 | 動態(tài)屬性已添加、更改或從對象中刪除 |
QEvent::EnabledChange | 98 | 部件的 enabled 狀態(tài)已更改 |
QEvent::Enter | 10 | 鼠標進入部件的邊界(QEnterEvent) |
QEvent::EnterEditFocus | 150 | 編輯部件獲得焦點進行編輯,必須定義 QT_KEYPAD_NAVIGATION |
QEvent::EnterWhatsThisMode | 124 | 當應(yīng)用程序進入“What’s This?”模式,發(fā)送到 toplevel 頂層部件 |
QEvent::Expose | 206 | 當其屏幕上的內(nèi)容無效,發(fā)送到窗口,并需要從后臺存儲刷新 |
QEvent::FileOpen | 116 | 文件打開請求(QFileOpenEvent) |
QEvent::FocusIn | 8 | 部件或窗口獲得鍵盤焦點(QFocusEvent) |
QEvent::FocusOut | 9 | 部件或窗口失去鍵盤焦點(QFocusEvent) |
QEvent::FocusAboutToChange | 23 | 部件或窗口焦點即將改變(QFocusEvent) |
QEvent::FontChange | 97 | 部件的字體發(fā)生改變 |
QEvent::Gesture | 198 | 觸發(fā)了一個手勢(QGestureEvent) |
QEvent::GestureOverride | 202 | 觸發(fā)了手勢覆蓋(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 | 鼠標在圖形場景中的懸停項內(nèi)移動(QGraphicsSceneHoverEvent) |
QEvent::GraphicsSceneMouseDoubleClick | 158 | 鼠標在圖形場景中再次按下(雙擊)(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMouseMove | 155 | 鼠標在圖形場景中移動(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMousePress | 156 | 鼠標在圖形場景中按下(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMouseRelease | 157 | 鼠標在圖形場景中釋放(QGraphicsSceneMouseEvent) |
QEvent::GraphicsSceneMove | 182 | 部件被移動(QGraphicsSceneMoveEvent) |
QEvent::GraphicsSceneResize | 181 | 部件已調(diào)整大小(QGraphicsSceneResizeEvent) |
QEvent::GraphicsSceneWheel | 168 | 鼠標滾輪在圖形場景中滾動(QGraphicsSceneWheelEvent) |
QEvent::Hide | 18 | 部件被隱藏(QHideEvent) |
QEvent::HideToParent | 27 | 子部件被隱藏(QHideEvent) |
QEvent::HoverEnter | 127 | 鼠標進入懸停部件(QHoverEvent) |
QEvent::HoverLeave | 128 | 鼠標留離開懸停部件(QHoverEvent) |
QEvent::HoverMove | 129 | 鼠標在懸停部件內(nèi)移動(QHoverEvent) |
QEvent::IconDrag | 96 | 窗口的主圖標被拖走(QIconDragEvent) |
QEvent::IconTextChange | 101 | 部件的圖標文本發(fā)生改變(已棄用) |
QEvent::InputMethod | 83 | 正在使用輸入法(QInputMethodEvent) |
QEvent::InputMethodQuery | 207 | 輸入法查詢事件(QInputMethodQueryEvent) |
QEvent::KeyboardLayoutChange | 169 | 鍵盤布局已更改 |
QEvent::KeyPress | 6 | 鍵盤按下(QKeyEvent) |
QEvent::KeyRelease | 7 | 鍵盤釋放(QKeyEvent) |
QEvent::LanguageChange | 89 | 應(yīng)用程序翻譯發(fā)生改變 |
QEvent::LayoutDirectionChange | 90 | 布局的方向發(fā)生改變 |
QEvent::LayoutRequest | 76 | 部件的布局需要重做 |
QEvent::Leave | 11 | 鼠標離開部件的邊界 |
QEvent::LeaveEditFocus | 151 | 編輯部件失去編輯的焦點,必須定義 QT_KEYPAD_NAVIGATION |
QEvent::LeaveWhatsThisMode | 125 | 當應(yīng)用程序離開“What’s This?”模式,發(fā)送到頂層部件 |
QEvent::LocaleChange | 88 | 系統(tǒng)區(qū)域設(shè)置發(fā)生改變 |
QEvent::NonClientAreaMouseButtonDblClick | 176 | 鼠標雙擊發(fā)生在客戶端區(qū)域外 |
QEvent::NonClientAreaMouseButtonPress | 174 | 鼠標按鈕按下發(fā)生在客戶端區(qū)域外 |
QEvent::NonClientAreaMouseButtonRelease | 175 | 鼠標按鈕釋放發(fā)生在客戶端區(qū)域外 |
QEvent::NonClientAreaMouseMove | 173 | 鼠標移動發(fā)生在客戶區(qū)域外 |
QEvent::MacSizeChange | 177 | 用戶更改了部件的大小(僅限 OS X) |
QEvent::MetaCall | 43 | 通過 QMetaObject::invokeMethod() 調(diào)用異步方法 |
QEvent::ModifiedChange | 102 | 部件修改狀態(tài)發(fā)生改變 |
QEvent::MouseButtonDblClick | 4 | 鼠標再次按下(QMouseEvent) |
QEvent::MouseButtonPress | 2 | 鼠標按下(QMouseEvent) |
QEvent::MouseButtonRelease | 3 | 鼠標釋放(QMouseEvent) |
QEvent::MouseMove | 5 | 鼠標移動(QMouseEvent) |
QEvent::MouseTrackingChange | 109 | 鼠標跟蹤狀態(tài)發(fā)生改變 |
QEvent::Move | 13 | 部件的位置發(fā)生改變(QMoveEvent) |
QEvent::NativeGesture | 197 | 系統(tǒng)檢測到手勢(QNativeGestureEvent) |
QEvent::OrientationChange | 208 | 屏幕方向發(fā)生改變(QScreenOrientationChangeEvent) |
QEvent::Paint | 12 | 需要屏幕更新(QPaintEvent) |
QEvent::PaletteChange | 39 | 部件的調(diào)色板發(fā)生改變 |
QEvent::ParentAboutToChange | 131 | 部件的 parent 將要更改 |
QEvent::ParentChange | 21 | 部件的 parent 發(fā)生改變 |
QEvent::PlatformPanel | 212 | 請求一個特定于平臺的面板 |
QEvent::PlatformSurface | 217 | 原生平臺表面已創(chuàng)建或即將被銷毀(QPlatformSurfaceEvent) |
QEvent::Polish | 75 | 部件被拋光 |
QEvent::PolishRequest | 74 | 部件應(yīng)該被拋光 |
QEvent::QueryWhatsThis | 123 | 如果部件有“What’s This?”幫助,應(yīng)該接受事件 |
QEvent::ReadOnlyChange | 106 | 部件的 read-only 狀態(tài)發(fā)生改變 |
QEvent::RequestSoftwareInputPanel | 199 | 部件想要打開軟件輸入面板(SIP) |
QEvent::Resize | 14 | 部件的大小發(fā)生改變(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 激活,用于實現(xiàn) QSocketNotifier |
QEvent::StateMachineSignal | 192 | 信號被傳遞到狀態(tài)機(QStateMachine::SignalEvent) |
QEvent::StateMachineWrapped | 193 | 事件是一個包裝器,用于包含另一個事件(QStateMachine::WrappedEvent) |
QEvent::StatusTip | 112 | 狀態(tài)提示請求(QStatusTipEvent) |
QEvent::StyleChange | 100 | 部件的樣式發(fā)生改變 |
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),發(fā)送到 QApplication |
QEvent::TabletLeaveProximity | 172 | Wacom 寫字板離開接近事件(QTabletEvent),發(fā)送到 QApplication |
QEvent::ThreadChange | 22 | 對象被移動到另一個線程。這是發(fā)送到此對象的最后一個事件在上一個線程中,參見:QObject::moveToThread() |
QEvent::Timer | 1 | 定時器事件(QTimerEvent) |
QEvent::ToolBarChange | 120 | 工具欄按鈕在 OS X 上進行切換 |
QEvent::ToolTip | 110 | 一個 tooltip 請求(QHelpEvent) |
QEvent::ToolTipChange | 184 | 部件的 tooltip 發(fā)生改變 |
QEvent::TouchBegin | 194 | 觸摸屏或軌跡板事件序列的開始(QTouchEvent) |
QEvent::TouchCancel | 209 | 取消觸摸事件序列(QTouchEvent) |
QEvent::TouchEnd | 196 | 觸摸事件序列結(jié)束(QTouchEvent) |
QEvent::TouchUpdate | 195 | 觸摸屏事件(QTouchEvent) |
QEvent::UngrabKeyboard | 189 | Item 失去鍵盤抓取(QGraphicsItem) |
QEvent::UngrabMouse | 187 | Item 失去鼠標抓取(QGraphicsItem、QQuickItem) |
QEvent::UpdateLater | 78 | 部件應(yīng)該排隊在以后重新繪制 |
QEvent::UpdateRequest | 77 | 部件應(yīng)該被重繪 |
QEvent::WhatsThis | 111 | 部件應(yīng)該顯示“What’s This”幫助(QHelpEvent) |
QEvent::WhatsThisClicked | 118 | 部件的“What’s This”幫助鏈接被點擊 |
QEvent::Wheel | 31 | 鼠標滾輪滾動(QWheelEvent) |
QEvent::WinEventAct | 132 | 發(fā)生了 Windows 特定的激活事件 |
QEvent::WindowActivate | 24 | 窗口已激活 |
QEvent::WindowBlocked | 103 | 窗口被模態(tài)對話框阻塞 |
QEvent::WindowDeactivate | 25 | 窗戶被停用 |
QEvent::WindowIconChange | 34 | 窗口的圖標發(fā)生改變 |
QEvent::WindowStateChange | 105 | 窗口的狀態(tài)(最小化、最大化或全屏)發(fā)生改變(QWindowStateChangeEvent) |
QEvent::WindowTitleChange | 33 | 窗口的標題發(fā)生改變 |
QEvent::WindowUnblocked | 104 | 一個模態(tài)對話框退出后,窗口將不被阻塞 |
QEvent::WinIdChange | 203 | 本地窗口的系統(tǒng)標識符發(fā)生改變 |
QEvent::ZOrderChange | 126 | 部件的 z 值發(fā)生了改變,該事件不會發(fā)送給頂層窗口 |
用戶事件的值應(yīng)該介于 QEvent::User 和 QEvent::MaxUser之間。
常量 | 值 | 描述 |
---|---|---|
QEvent::User | 1000 | 用戶定義的事件 |
QEvent::MaxUser | 65535 | 最后的用戶事件 ID |
為方便起見,可以使用 [static] int QEvent::registerEventType(int *hint* = -1)
函數(shù)來注冊和存儲一個自定義事件類型,這樣做會避免意外地重用一個自定義事件類型。
自定義事件號
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;
};
發(fā)送和處理事件
#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("發(fā)送自定義事件", this);// 點擊按鈕發(fā)送事件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"
除了使用事件分發(fā)函數(shù)event處理自定義事件外,也可以使用customEvent處理自定義事件,它是專門用來接收自定義事件。
void customEvent(QEvent* ev) override
{if(ev->type() == EventType::NumberEvent){auto nev = static_cast<NumberChangeEvent*>(ev);qInfo() << "Number: " << nev->getNumber();}
}
sendEvent與postEvent的區(qū)別
使用棧區(qū)對象與堆區(qū)對象分別對兩種發(fā)送方式進行測試
棧區(qū)對象
connect(m_btn, &QPushButton::clicked, this, [=](){NumberChangeEvent ne1(1);QApplication::sendEvent(this, &ne1);NumberChangeEvent ne2(1);QApplication::postEvent(this, &ne2);
});
當使用postEvent發(fā)送棧區(qū)事件對象時會直接引發(fā)中斷,因為postEvent是將事件放入事件隊列中稍后處理,棧區(qū)事件對象出作用域后內(nèi)存就會被釋放,我認為這是引發(fā)中斷的原因。而使用sendEvent發(fā)送棧區(qū)事件對象不會中斷,內(nèi)存也會自己釋放(出作用域后釋放)。
堆區(qū)對象
當使用sendEvent發(fā)送堆區(qū)對象后,內(nèi)存不會被自動釋放,需要手動釋放。而使用postEvent發(fā)送堆區(qū)事件對象內(nèi)存會自動釋放,不必手動釋放。
因此,當使用棧區(qū)對象時只能使用sendEvent發(fā)送事件,使用堆區(qū)對象時,使用sendEvent發(fā)送事件之后需要手動釋放事件對象內(nèi)存,使用postEvent發(fā)送事件會自動釋放內(nèi)存。另外,使用postEvent發(fā)送事件時可以設(shè)置事件的優(yōu)先級,讓發(fā)送的事件被優(yōu)先處理。
事件傳播機制
事件傳播的過程
Qt的事件產(chǎn)生之后,不是直接傳遞給了對象的,需要經(jīng)過一系列的過程。
-
事件首先由Qt的ServerApplication去接收來自于外部或內(nèi)部的一些行為,鼠標點擊,鍵盤輸入,時鐘事件等,分析并決定送往對應(yīng)的對象去處理(內(nèi)部管理機制),最后會調(diào)用[virtual] bool QCoreApplication::notify ( QObject * receiver, QEvent * event ) 去處理,當然這個是虛函數(shù),你可以在子類去重新實現(xiàn)它 。
-
在notify(…)中,在發(fā)給對應(yīng)的接收者前,會先把消息送給QApplication。所以如果想在你界面的Widget前先處理那些事 件,那么你可以給QApplication對象installEventFilter,然后在對應(yīng)的eventFilter()里先把這些事件都給過一 遍,然后你可以過濾一些不必要事件。
-
如果QApplication沒有處理那些事件,然后就是交給事件接收對象了。在這個對象接收前,也可以為這對象加一個事件過濾器,同樣是 installEventFilter。
-
如果eventFilter沒有過濾某些事件,那么就會將事件交給接收者的event()函數(shù)(你可以根據(jù)不同類型的事件盡情處理),如果event事件在接受者處理后,也不會上傳給父類的event,否則會上傳進入父類的event。
-
默認event()函數(shù)根據(jù)事件類型會調(diào)用不同的事件處理函數(shù),類似mousePressEvent(),keyPressEvent()去分別處理他們。
事件傳播到父組件
當控件忽略事件時,事件會繼續(xù)往上傳播,這里的傳播是指傳播給父組件。
-
QEvent有
accept()
函數(shù) 和ignore()
函數(shù):-
accept():本組件處理該事件,這個事件就不會被繼續(xù)傳播給其父組件;
-
ignore():本組件不想要處理這個事件,這個事件會被繼續(xù)傳播給其父組件;
-
值得注意的是所有的事件默認都是接受的(即不會傳遞到父組件)
-
-
忽略和接受案例
?當默認接受事件時,在本組件處理之后,事件不會傳遞到父組件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事件分發(fā)函數(shù)返回值的作用
#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,表示我處理了這個事件,不再繼續(xù)傳遞了}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"
事件分發(fā)函數(shù)event返回false,表示本組件沒有處理這個事件,事件會被傳遞到父組件處理,返回true則表示本組件處理了這個事件,不需要再讓父組件處理了。
鼠標單擊事件與按鈕單擊信號的關(guān)聯(lián)
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() << "鼠標單擊";
});
?要想觸發(fā)鼠標點擊的信號與槽就必須處理鼠標按下事件
void mousePressEvent(QMouseEvent* ev) override
{return QPushButton::mousePressEvent(ev);
}
事件過濾
有的時候,對象需要查看(可能還需要攔截)傳遞給另一個對象的事件。例如,對話框通常想要過濾一些小部件的按鍵;例如,修改返回鍵處理。
QObject::installEventFilter()函數(shù)通過設(shè)置一個事件過濾器來實現(xiàn)這一點,使指定的過濾器對象在其QObject::eventFilter()函數(shù)中接收目標對象的事件。事件過濾器在目標對象處理事件之前處理事件,允許它根據(jù)需要檢查和丟棄事件??梢允褂肣Object::removeEventFilter()函數(shù)刪除現(xiàn)有的事件過濾器。
當調(diào)用過濾器對象的eventFilter()實現(xiàn)時,它可以接受或拒絕事件,并允許或拒絕對事件的進一步處理。如果所有事件過濾器都允許對事件進行進一步處理(每個返回false),則將事件發(fā)送到目標對象本身。如果其中一個停止處理(通過返回true),目標和任何后面的事件過濾器都不會看到事件。
無邊框窗口拖動(事件過濾器實現(xiàn))
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);// 已經(jīng)處理了,不需要再處理MouseMoveEvent事件了return true;}return QObject::eventFilter(object, ev);}
private:QPoint pressPos;
};
在自己類里面重寫eventFilter來過濾事件,但是如果我有很多個無邊框窗口,都要實現(xiàn)移動,這個方法就不太方便了??梢韵葘懸粋€拖拽控件的過濾器對象,然后哪兒需要使用,就直接加載事件過濾器對象即可。
// 無邊框窗口拖拽的事件過濾器對象
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));}
}
事件跟信號的區(qū)別
事件(QEvent) | 信號(SIGNAL) | |
與QObject的關(guān)系 | 由具體對象進行處理 | 由具體對象主動產(chǎn)生 |
對程序的影響 | 改寫事件處理函數(shù)可能導(dǎo)致程序行為發(fā)生改變 | 信號是否存在對應(yīng)的槽函數(shù)不會改變程序行為 |
兩者的聯(lián)系 | 一般而言,信號在具體的事件處理函數(shù)中產(chǎn)生 |