目錄
處理事件
鼠標事件
鍵盤事件
定時器事件
窗口事件
雖然 Qt 是跨平臺的 C++ 開發框架,Qt 的很多能力其實是操作系統提供的
只不過 Qt 封裝了系統的 API
事件
前面學習過信號槽:
用戶進行的各種操作,就可能會產生出信號,可以給某個信號指定槽函數,當信號觸發時,就能夠自動的執行到對應的槽函數
事件和信號槽非常類似:
用戶進行的各種操作,也會產生事件,程序員同樣可以給事件關聯上處理函數(處理的邏輯),當事件觸發的時候,就能夠執行到對應的代碼
事件本身是操作系統提供的機制,Qt 也同樣把操作系統事件機制進行了封裝,拿到了 Qt 中
但是由于事件對應的代碼編寫起來不是很方便,所以 Qt 對于事件機制又進行了進一步的封裝,就得到了信號槽
信號槽就是對于事件的進一步封裝,事件是信號槽的底層機制
實際 Qt 開發程序過程中,絕大部分和用戶之間進行的交互都是通過"信號槽"來完成的
有些特殊情況下,信號槽不一定能搞定(某個用戶的動作行為,Qt 沒有提供對應的信號)
此時就需要通過重寫事件處理函數的形式,來手動處理事件的啊應邏輯
開發事件機制給咱們程序員,咱們就可以根據實際的需要進行更深度的定制化 diy 操作了
用戶進行了很多操作,就會產生很多的事件(當然也會產生很多的信號)
下面就是開發中比較典型的事件:
不同場景下,要關注的點是不一樣,這些事件的子類中就會包含一些對應的不同的屬性
處理事件
讓一段代碼和某個事件關聯起來,當事件觸發的時候,就能指定到這段代碼
之前信號槽這里通過 connect 來完成上述關聯的
對于事件來說,還不太一樣:
讓當前的類,重寫某個事件處理函數,這里用到的是"多態”機制
創建子類, 繼承自 Qt 已有的類,在子類中重寫父類的事件處理函數
后續事件觸發過程中,就會通過多態這樣的機制,執行到咱們自己寫的子類的函數中
鼠標事件
下面都是創建 QWidget 的,當然也可以創建 QMainWindow,因為用不到工具欄,所以選擇的 QWidget
鼠標進入和鼠標離開事件
下面使用上述方式,處理一下鼠標進入(enterEvent)和鼠標離開(leaveEvent)事件:
enterEvent 和?leaveEvent 函數都是虛函數,所以可以被子類重寫:
圖形化界面的方式創建一個 Label,鼠標進入 Label 時提示 enterEvent,離開時提示?leaveEvent:
為了能清楚看到 Label 的邊框,將邊框選為 Box
?效果為:
這里需要創建 QLabel 的子類,重寫 enterEvent 和 leaveEvent:
類名就叫 Label,父類叫 QLabel:
按照以往的習慣,創建的 Label 類需要有一個父控件,所以在 label.h 中添加:
label.cpp 中添加:
接著在 label.h 中聲明兩個需要重寫的函數:
注意:
要想重寫父類的函數,就需要確保你這邊寫的函數名字和函數的參數列表都完全一致 (形參名無所謂),謹防單詞拼寫錯誤
label.cpp 實現:
(void)event 是為了消除警告,因為暫時還沒用到 event 這個形參
此時運行程序,鼠標進入和移出 label 時并沒有執行上述邏輯,因為:
當前在界面上創建的這個 label 其實是 QLabel,不是咱們自己寫的 Label
必須要確保界面上的這個 label 是一個咱們自己定義的 Label 類的實例,才會執行到
右鍵圖形化界面的 label,點擊提升為:
輸入提升的類名 Label,點擊添加,再點擊提升:
一定要確保你的類名以及頭文件的名字,和上述自定義的類名頭文件都匹配
此時右邊對象樹上面就是 Label 了,沒提升前顯示是 QLabel:
通過"提升為"這樣的方式,就可以把 Qt Designer 中拖上去的控件的類型轉換成自定義的控件類型
此時再運行程序,鼠標移入移出 Label 時,就會打印下面的內容了:
此時就說明當前的 enterEvent 和 leaveEvent 這兩個事件就被咱們給捕獲到了
通過事件獲取到鼠標點擊的位置
與上面的操作一樣,創建一個 Label,再創建一個 Label 類,父類定為 QLabel,并對生成的 Label 的構造函數做一個調整,添加一個 QWidget* 的參數,以便于能夠指定父窗口
接著再右鍵 Label 點擊提升為,輸入類名后點擊提升,此時就完成了提升操作:
下面就是 mousePressEvent 函數,當鼠標按下時就會觸發這個函數調用:
左鍵、右鍵、滾輪、側鍵都能觸發
在 label.cpp 中實現 mousePressEvent 函數:
此時鼠標在 Label 范圍內點擊就會打印 鼠標點擊的位置坐標:
上述是以 Label 左上角位置為原點的
下面則是以屏幕左上角為原點獲取坐標:
也可以加上下面的代碼,來判斷按下的是左鍵還是右鍵:
通過事件獲取到鼠標點擊釋放按鍵
與上面的鼠標點擊事件一樣,下面是重寫的 mouseReleaseEvent 函數:
此時就能做到獲取鼠標點擊釋放按鍵:
clicked 這樣的信號,就相當于是一次鼠標按下事件和一次鼠標釋放事件
通過事件獲取到鼠標雙擊按鍵
重寫的 mouseDoubleEvent 函數如下:
鼠標第二次按下的時候,才能夠識別到是"雙擊:
注意:
有的程序,可能是單擊有一些邏輯,雙擊有另一些邏輯,如果我們沒注意,可能雙擊操作就能觸發單擊的邏輯,可能就有 bug
通過事件獲取到鼠標移動
剛才重寫鼠標事件的操作,都是在自定義的 Labe| 中完成的,此時鼠標只有在 Label 范圍內進行動作的時候,才能捕獲到
也可以把這些操作直接放到 Widget (QWidqet 子類) 來完成,這樣的話,鼠標在整個窗口中進行的各種動作都能獲取到了
所以直接在 widget.h widget.cpp 中重寫 mouseMoveEvent 函數:
此時運行程序并沒有效果
鼠標移動不同于鼠標按下
隨便移動一下鼠標,就會產生出大量的鼠標移動事件,當你進行捕獲事件的時候,尤其是在這里再進行一些復雜邏輯的時候,程序負擔就很重,很容易產生卡頓之類的情況
Qt 為了保證程序的流暢性,默認情況下不會對鼠標移動進行追蹤,鼠標移動的時候不會調用mouseMoveEvent,除非顯式告訴 Qt 就要追蹤鼠標位置
所以需要在 Widget 的構造函數中設置:
此時稍微一動鼠標,就會一直打印,如果移動的比較快,就會明顯出現打印卡頓的情況:
通過事件獲取到鼠標滾輪的滾動動作
在 QWheelEvent 中 通過 delta() 獲取到這次事件鼠標滾輪滾動了多遠
同樣在 widget.h widget.cpp 中重寫 wheelEvent 函數:
滾輪往下滾動就打印?-120,往上滾動就打印?120:
我們也可以在 Widget 類中新增 int total,初始化為0,就能在 wheelEvent 函數中實現統計滾輪滾動的距離了:
效果為:
就可以根據滾輪滾動的操作實現特定的功能,比如可以通過滾輪去縮放字體大小,可以把滾輪滾動的距離映射到具體的數值上,就可以實現類似的效果了
鍵盤事件
處理鍵盤按鍵事件
我們前面學習過的 QShortCut,這是信號槽機制封裝過的,獲取鍵盤按鍵的方式
站在更底層的角度,也可以通過事件獲取到當前用戶鍵盤按下的情況
依舊是在?widget.h widget.cpp 中重寫 keyPressEvent 函數:
按下 ABCDEF 的效果為,可以發現每一個按鈕都對應一個數字:
如果想得知是否按下了具體的某一個鍵,以 A 為例,代碼改為:
也有些場景是組合鍵 Ctrl + A :
定時器事件
前面學習了QTimer 實現定時器功能
在 QTimer 背后是 QTimerEvent 定時器事件進行支撐的
QObject 提供了一個 timerEvent 這個函數,可以通過定時器,周期性的觸發一些操作
里面需要搭配?startTimer 啟動定時器,killTimer 關閉定時器 使用
下面通過圖形化的方式,拖動一個 LCD Number,初始值改為 10 :
此處 startTimer 的返回值 timerld 類似于 Linux 中的文件描述符,起到的是身份標識的效果
因為后面程序可能還會用到?timerld,所以在 widget.h 中的構造函數定義為類內成員:
在?widget.h widget.cpp 中重寫 timerEvent 函數:
運行程序,每隔一秒-1,直到0就停止:
使用 timerEvent 比 QTimer 還是要更復雜一點,手動管理 timerld,還需要區分這次函數調用是哪個 timer 引起的
后續實際開發中,使用 QTimer 即可
窗口事件
- moveEvent 窗口移動時觸發的事件
- resizeEvent 窗口大小改變時觸發的事件
?moveEvent
QMoveEvent 中有下面兩個常用的方法:
resizeEvent
QResizeEvent 中有下面兩個常用的方法:
在?widget.h widget.cpp 中重寫 moveEvent 和 resizeEvent函數:
如果移動 widget 窗口或調整 widget 窗口大小,下面就會打印:
Qt:事件相關知識到此結束