在《Qt Quick 事件處理之信号與槽》中介紹了 QML 中如何使用内建類型的信号以及如何自定義信号,這次我們來看看如何處理滑鼠、鍵盤、定時器等事件。這些時間在處理時,通常是通過信号來完成的。
滑鼠事件處理
桌面開發的話,難免要處理滑鼠事件……
變色矩形示例
看一個簡單的處理滑鼠事件的例子,先看代碼(handle_mouse.qml):
[javascript] view plain copy

- import QtQuick 2.0
- import QtQuick.Controls 1.1
- Rectangle {
- width: 320;
- height: 240;
- MouseArea {
- anchors.fill: parent;
- acceptedButtons: Qt.LeftButton | Qt.RightButton;
- onClicked: {
- if(mouse.button == Qt.RightButton){
- Qt.quit();
- }
- else if(mouse.button == Qt.LeftButton){
- color = Qt.rgba((mouse.x % 255) / 255.0 , (mouse.y % 255) / 255.0, 0.6, 1.0);
- }
- }
- onDoubleClicked: {
- color = "gray";
- }
- }
- }
使用 "qmlscene handle_mouse.qml" 指令,可以看到運作效果。上面的代碼僅僅是繪制一個矩形,當滑鼠左鍵按下時改變矩形區域的顔色,滑鼠右鍵按下時退出應用。圖 1 是剛運作時的效果:
圖1 handle_mouse.qml 初始運作效果
圖 2 是點選滑鼠左鍵後的效果:
圖 2 滑鼠左鍵點選後的效果
如果你點一下滑鼠右鍵,程式會退出。
示例簡陋,但足以說明如何處理滑鼠事件,下面咱們慢慢來看。
MouseArea
MouseArea 對象可以附加到一個 item 上供 item 處理滑鼠事件,它本身是一個不可見的 item 。在其内部,可以直接引用它所附着的對象的屬性和方法。你可以将 MouseArea 了解為它所附着的 item 的代理。
MouseArea 有很多屬性, enabled 用來控制是否處理滑鼠事件,預設值是 true ,如果你設定為 false ,那麼它所代理的 item 就會無視滑鼠事件。 acceptedButtons 屬性設定接收拿些個滑鼠按鍵産生的事件(左鍵、右鍵、中鍵),示例代碼 "acceptedButtons: Qt.LeftButton | Qt.RightButton;" 表示處理滑鼠左鍵和右鍵。
作為一個 item , MouseArea 也擁有 anchors 屬性,你可以使用它來描述有效的滑鼠區域。示例代碼 "anchors.fill: parent;" 表示整個矩形區域都接受滑鼠事件。
MouseArea 還有很多其他屬性,如 hoverEnabled , pressed 等等,請參考 Qt 幫助文檔。
示例代碼中,在 MouseArea 對象内使用了 onClicked 和 onDoubleClicked 兩個信号處理器,他們對應 MouseArea 的 onClicked 和 onDoubleClicked 信号, MouseArea 還有很多其他的信号,如 onPressed / onReleased / onEntered / onExited / onPressAndHold 等等,從名字上就可以看到這些信号的含義。
onClicked 信号的參數是 MouseEvent 類型,名為 mouse ,是以你可以在信号處理器中直接使用 mouse 來查詢滑鼠事件的詳情。比如哪個 button 按下,正如示例代碼中看到的那樣, MouseEvent 的 button 屬性儲存了被按下的滑鼠按鍵标記, x , y 屬性儲存滑鼠指針位置。還有一個比較重要的屬性 accepted ,如果你處理滑鼠事件後不想這個事件再往下傳遞,就置其值為 true 。
onDoubleClicked 信号代表輕按兩下事件,其參數也是 MouseEvent 類型,示例中輕按兩下滑鼠,矩形顔色變為灰色。
簡單的滑鼠事件處理就這些内容,根據你應用的需要,可能你還會處理 onPressed / onReleased / onEntered 等等信号,請參考 Qt 幫助。
鍵盤事件處理
手機上你可能較少處理鍵盤事件(有一個例外,BACK 按鍵),但是電腦上你免不了要響應鍵盤。
會動的文本執行個體
先看示例代碼,handle_key.qml :
[javascript] view plain copy

- import QtQuick 2.0
- import QtQuick.Controls 1.1
- Rectangle {
- width: 320;
- height: 480;
- color: "gray";
- focus: true;
- Keys.enabled: true;
- Keys.onEscapePressed: {
- Qt.quit();
- }
- Keys.forwardTo: [moveText, likeQt];
- Text {
- id: moveText;
- x: 20;
- y: 20;
- width: 200;
- height: 30;
- text: "Moving Text";
- color: "blue";
- //focus: true;
- font { bold: true; pixelSize: 24;}
- Keys.enabled: true;
- Keys.onPressed: {
- switch(event.key){
- case Qt.Key_Left:
- x -= 10;
- break;
- case Qt.Key_Right:
- x += 10;
- break;
- case Qt.Key_Down:
- y += 10;
- break;
- case Qt.Key_Up:
- y -= 10;
- break;
- default:
- return;
- }
- event.accepted = true;
- }
- }
- CheckBox {
- id: likeQt;
- text: "Like Qt Quick";
- anchors.left: parent.left;
- anchors.leftMargin: 10;
- anchors.bottom: parent.bottom;
- anchors.bottomMargin: 10;
- z: 1;
- }
- }
這個示例通過上下左右四個按鍵移動一個文本串,空格鍵選中複選框, Esc 鍵退出應用。圖 3 是初始運作效果圖:
圖 3 handle_key 初始效果
圖 4 是我按了幾次方向鍵,按了空格鍵後的效果:
圖 4 移動文本,選中複選框
下面解釋示例代碼,介紹如何使用 Keys 對象和信号處理器處理按鍵事件
Keys 與 信号處理器
其實在 《Qt Quick 事件處理之信号與槽》、《Qt Quick 簡單教程》和 《QML 語言基礎》三篇文章中我們都有提到 Keys 對象,有的示例中也用到過,這裡呢,我們專門介紹一下,力求使大家對 Keys 及按鍵處理有個較為全面的了解。
Keys 對象是 Qt Quick 提供的,專門供 Item 處理按鍵事件的對象。它定義了很多針對特定按鍵的信号,比如 onReturnPressed / onEscapePressed / onDownPressed / onDigit0Pressed / onBackPressed 等等;它還定義了更為普通的 onPressed 和 onReleased 信号,一般地,你可以使用這兩個信号來處理大部分按鍵(請對照 Qt C++ 中的 keyPressEvent 和 keyReleaseEvent 來了解),它們有一個名字是 event 的 KeyEvent 參數,包含了按鍵的詳細資訊。
KeyEvent 代表一個按鍵事件,如果一個按鍵被處理, event.accepted 應該被設定為 true 以免它被繼續傳遞;要是你不設定它,那它可能會繼續傳遞給其他的 item ,出現一些奇奇怪怪的問題。
Keys 有三個屬性。
enabled 屬性控制是否處理按鍵。
forwardTo 屬性是清單類型,它表示傳遞按鍵事件給清單内的對象,如果某個對象 accept 了某個按鍵,那位列其後的對象就不會收到該按鍵事件。示例代碼 "Keys.forwardTo: [moveText, likeQt];" 表明轉發按鍵給 id 為 moveText 的 Text 對象和 id 為 likeQt 的 CheckBox 對象。 moveText 在前面,如果它消耗掉某個鍵, likeQt 就收不到了。你可以修改 Text 對象的 Keys.onPressed 附加信号處理器,在 case 清單中添加 Qt.Key_Space 看看效果。
priority 屬性允許你設定 Keys 附加屬性的優先級,有兩種,在 Item 之前處理按鍵,這是預設行為,在 Item 之後處理按鍵。你可以對照着 Qt C++ 的 keyPressEvent() 函數來了解,如果你在派生類中重載了 keyPressEvent() 方法,那麼你可以在重載方法的一開始調用父類的 keyPressEvent() ,也可以在你處理完感興趣的事件後再調用父類的 keyPressEvent() 。這期間的邏輯關系也很簡單,假如 Keys 先處理按鍵,如它吃掉了某個鍵,它所依附的 Item 對象就收不到這個按鍵了;反之亦然。
Qt Quick 提供的一些元素本身會處理按鍵,比如示例中的 CheckBox ,它響應空格鍵來選中或取消選中。而我們不需要給它附加 Keys 對象來再次處理按鍵事件。當然,如果你想改變它的按鍵響應邏輯,可以這麼做,在解釋 priority 屬性時已經提到這點。
最後還有一點要說明的是,如果你想某個元素處理按鍵,需要把焦點給它,這通過 Item 的 focus 屬性來控制,置 true 即可。
現在再來解釋下示例代碼。
Rectangle 對象的附加信号處理器 Keys.onEscapePressed 調用 Qt.quit() 退出,小白很,不說了。
Text 對象實作了 Keys.onPressed 附加信号處理器,使用 switch-case 語句,分揀 event 參數的 key 屬性。如果是上下左右四個鍵,就變更 Text 的位置,置 accepted 為 true ,聲明這幾個按鍵已名花有主找到歸宿;否則就直接傳回,給别人機會處理按鍵。你也看到了,正是因為這樣, CheckBox 才能拿到空格鍵來選中或取消複選框。
示例中的 CheckBox 對象定義時,沒有專門處理按鍵,因為 Qt Quick 提供的實作已經處理了按鍵了。
嗯嗯,貌似内容很少?走着,看定時器去。
定時器
定時器的作用還要說嗎?好像有點兒啰嗦了呀。定時器麼,就是周期性觸發的一個事件,和平常用的鬧鐘差不多。你可以利用定時器來完成一些周期性的任務,比如檢查和伺服器的連接配接呆死了沒,比如備份使用者資料……
定時器對象介紹
在 QML 中, Timer 代表定時器,使用起來也很簡單,響應其 onTriggered() 信号即可,它也就這麼一個有用的信号。另外它還有幾個屬性要說明一下, interval 指定定時周期,機關是毫秒,預設值是 1000 毫秒; repeat 設定定時器是周期性觸發還是一次性觸發,預設是一次性的(好像和 QTimer 不一樣嗳);running 屬性,設定為 true 定時器就開始工作,設定為 false 就歇菜,預設是 false ; triggeredOnStart 屬性,怎麼說呢, Qt 總是對我們這麼好都有點兒那啥不好意思了,這個屬性是考慮到有些同志的特殊需求,本來定時器啟動後要等待設定的間隔才觸發,如果你設定這個屬性為 true ,那定時器開始執行時立馬先觸發一次,預設值是 false 。
Timer 還有 start() / stop() / restart() 三個方法可以調用,它們會影響 running 屬性,望文生義吧您。
現在來看一個簡單的示例,倒計時。
倒計時程式
世界杯倒計時按天算,山中一日世上千年,咱們這個示例用1秒頂它一天,倒數十秒,然後就開香槟慶祝下。
看代碼(count_down.qml):
[javascript] view plain copy

- import QtQuick 2.0
- import QtQuick.Controls 1.1
- Rectangle {
- width: 320;
- height: 240;
- color: "gray";
- QtObject{
- id: attrs;
- property int counter;
- Component.onCompleted:{
- attrs.counter = 10;
- }
- }
- Text {
- id: countShow;
- anchors.centerIn: parent;
- color: "blue";
- font.pixelSize: 40;
- }
- Timer {
- id: countDown;
- interval: 1000;
- repeat: true;
- triggeredOnStart: true;
- onTriggered:{
- countShow.text = attrs.counter;
- attrs.counter -= 1;
- if(attrs.counter < 0)
- {
- countDown.stop();
- countShow.text = "Clap Now!";
- }
- }
- }
- Button {
- id: startButton;
- anchors.top: countShow.bottom;
- anchors.topMargin: 20;
- anchors.horizontalCenter: countShow.horizontalCenter;
- text: "Start";
- onClicked: {
- countDown.start();
- }
- }
- }
我在界面上放了個 Text 對象,它下面放一按鈕。 Rectangle 對象内定義了一個 Timer 對象,預設不啟動。當使用者點選 "Start" 按鈕時啟動定時器。我還設定了定時器的 triggeredOnStart 屬性哦,周期是 1 秒。
計數儲存在 QtObject 對象中, id 是 attrs ,在附加信号處理器 Component.onCompleted 中初始化 counter 屬性的值為 10 。而在 Timer 對象的 onTriggered 信号處理器中遞減 counter ,當 counter 為 0 時修改 Text 對象的文本為 "Clap Now!" 。
喏,就這麼簡單。
來看下效果。圖 5 是初始效果:
圖 5 倒計時程式初始效果
圖 6 是計時效果:
圖 6 計時
圖 7 是倒計時結束的效果:
圖 7 倒計時結束
如果你使用 qmlscene 運作 countdown.qml 文檔,可能會發現它有一個 BUG 哦, "Start" 按鈕第一次點選可以正常倒數計時,完了下次就不行了……我已經找到問題所在,不過還是留給你解決吧。
回顧一下,溫故知新:
- Qt Quick 簡介
- QML 語言基礎
- Qt Quick 之 Hello World 圖文詳解
- Qt Quick 簡單教程
- Qt Quick 事件處理之信号與槽