事件处理
Posted la_vie_est_belle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事件处理相关的知识,希望对你有一定的参考价值。
目录
事件的背后原理其实是信号和槽。事件被触发,就是某个信号被发射了。在本章,我们会学习以下几个常用事件,这些事件会由专门的元素进行处理。
- 鼠标事件
- 键盘事件
- 触摸事件
4.1 鼠标事件
我们使用MouseArea元素处理鼠标事件,请看以下示例代码。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
color: "green"
MouseArea
anchors.fill: root // 1
acceptedButtons: Qt.LeftButton | Qt.RightButton // 2
onClicked: // 3
if (mouse.button == Qt.LeftButton) // 4
console.log("x:", mouseX) // 5
console.log("y:", mouseY)
else
Qt.quit() // 6
onDoubleClicked: // 7
let red = Math.random()
let green = Math.random()
let blue = Math.random()
root.color = Qt.rgba(red, green, blue, 1)
运行结果:
- 单击窗口后,控制台打印鼠标坐标。
- 双击窗口后,窗口颜色随机改变。
- 右键点击窗口后,窗口退出。
代码解释:
1. 使用锚布局让MouseArea元素充满整个父类元素,也就是充满整个窗口。
2. acceptedButtons属性用来设置MouseArea元素可以接受的鼠标按键。
3. 监听鼠标点击事件(或者说是监听clicked信号发射,MouseArea元素还有鼠标按下、释放、双击、滚轮等信号,读者可以前往MouseArea的文档介绍页面进行查看。
4. 判断用户所点击的鼠标按键。当鼠标点击事件发生时,clicked信号会携带类型为MouseEvent的mouse参数,所以我们可以直接在花括号中使用mouse这个变量。
5. 用console.log()打印鼠标在窗口中的坐标。
6. 调用Qt对象的quit()方法退出窗口。使用这个方法时,我们需要往demo.py文件中添加一行代码,否则会报Signal QQmlEngine::quit() emitted, but no receivers connected to handle it.这个错误。代码如下所示。
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtCore import QUrl
if __name__ == "__main__":
app = QApplication([])
view = QQuickView()
view.setSource(QUrl('./demo.qml'))
view.engine().quit.connect(app.quit) # 新增代码
view.show()
sys.exit(app.exec())
7. 当用户双击鼠标后,我们随机改变窗口的颜色。
注:在QML中我们可以使用Qt对象,非常方便。读者可以查看该页面了解该Qt对象中包含的属性和方法。
我们再来实现下滚轮事件。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
color: "green"
Text // 1
id: txt
anchors.top: root.tops
anchors.horizontalCenter: root.horizontalCenter
text: "滚动事件"
color: "white"
MouseArea
anchors.fill: root
onWheel: // 2
if (wheel.angleDelta.y > 0)
txt.y += 1
if (txt.y >= 200-txt.height)
txt.y = 200-txt.height
else
txt.y -= 1
if (txt.y <= 0)
txt.y = 0
运行结果:
代码解释:
1. 设置文本元素,把它放在窗口顶部中间的位置。
2. 监听滚轮事件,通过滚轮修改文本在窗口上的y坐标。当滚轮事件发生时,wheel信号会携带类型为WheelEvent的wheel参数,所以我们可以直接在花括号中使用这个wheel变量。
4.2 键盘事件
键盘事件会在用户按下或释放某个按键时触发,在QML中我们通过Keys元素来处理键盘事件,请看下方代码。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
Text
id: txt
x: 10
y: 20
text: "键盘事件"
focus: true // 1
Keys.onPressed: // 2
switch(event.key)
case Qt.Key_Left:
txt.x -= 10
break
case Qt.Key_Right:
txt.x += 10
break
case Qt.Key_Up:
txt.y -= 10
break
case Qt.Key_Down:
txt.y += 10
break
default:
txt.text = event.text
return
Keys.onEscapePressed: // 3
Qt.quit()
运行结果:
代码解释:
1. 元素如果需要处理键盘事件,则需要先获取焦点,将focus设置为ture则表示拥有焦点。
2. 监听键盘按下事件(或者说是监听pressed信号发射)。我们在花括号中通过switch语句判断用户按下的按键,键值通过event.key获取。如果是上下左右键,则移动文本;如果是其他按键,则将文本元素的文本设置为按键名称,按键名称通过event.text获取。Keys元素所有信号发射时,都会携带KeyEvent类型的event参数。
3. escapePressed信号会在escape键被按下时发射,当用户按下这个键后,我们调用Qt.quit()方法退出窗口。读者可以前往Keys文档详情页查看该元素的其他信号。
注:Keys元素严格来说只是一种附加属性,需要依附于其他元素,我们不能像下面这样设置Keys。
Keys onPressed: ... onEscapePressed: ...
这样会报Keys is only available via attached properties错误。
Keys元素有三个属性:enabled、forwardTo以及priority。
enabled属性控制Keys元素是否生效,默认是true。设置会false之后就会失效,不再处理键盘事件。forwardTo属性的值是一个列表,它可以把键盘事件传递给列表中的元素。priority属性则控制Keys元素处理键盘事件的优先级。
我们通过下方的三个示例代码来解释下。
首先是enabled属性。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
Text
id: txt
x: 10
y: 20
text: "键盘事件"
focus: true
Keys.enabled: false // 1
Keys.onEscapePressed:
Qt.quit()
代码解释:
1. 设置Keys.enabled属性为false,键盘事件不再被处理。
接下来看下forwardTo属性。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
Text
id: txt1
x: 10
y: 20
text: "文本1"
color: "green"
Keys.onPressed: // 2
if (event.key == Qt.Key_Left)
x -= 10
else if (event.key == Qt.Key_Right)
x += 10
event.accepted = true // 3
Text
id: txt2
x: 100
y: 50
text: "文本2"
color: "blue"
Keys.onPressed: // 2
if (event.key == Qt.Key_Left)
x -= 10
else if (event.key == Qt.Key_Right)
x += 10
focus: true
Keys.forwardTo: [txt1, txt2] // 1
运行结果:
代码解释:
1. 使用forwardTo属性将事件传递给id为txt1和txt2的元素,根据列表顺序,先传递给txt1。
2. 在两个Text元素中监听键盘按下事件,判断是按下了左键还是右键。
3. 当键盘右键被按下后,我们在txt1中设置event.accepted = true,让事件不再继续传递,这样txt2就不会再接收到右键被按下的事件了。如果读者想让某个元素处理一些按键(比如左右键),让其他元素处理另一些按键(比如上下),用forwardTo属性可以很快实现。
最后来看下priority属性。该属性的值可以是Keys.BeforeItem(默认)或者Keys.AfterItem。当设置为Keys.BeforeItem时,事件的处理顺序是这样的:
- forwardTo属性中设置的元素键盘事件
- 具体的键盘事件,比如onReturnPressed
- onPressed, onReleased键盘事件
- 元素本身带有的键盘事件,比如TextInput(单行文本输入框)元素的按键处理。
- 父元素中的键盘事件
当设置为Keys.AfterItem时,事件的处理顺序是这样的:
- 元素本身带有的键盘事件,比如TextInput(单行文本输入框)元素的按键处理。
- forwardTo属性中设置的元素键盘事件
- 具体的键盘事件,比如onReturnPressed
- onPressed, onReleased键盘事件
- 父元素中的键盘事件
请看下方代码:
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
TextInput
id: input
text: "文本输入框"
Text
id: txt
x: 100
y: 150
text: "文本"
Keys.onPressed:
console.log("forwardTo元素")
focus: true
Keys.forwardTo: [txt]
Keys.onPressed:
console.log("当前元素")
Keys.onReturnPressed:
console.log("当前元素Return")
Keys.onPressed:
console.log("父元素")
运行代码后,文本输入框拥有焦点。按下a键后,事件处理顺序如下:
- forwardTo属性中Text元素的onPressed键盘事件,控制台打印"forwardTo元素"。
- 然后是TextInput当前元素的onPressed键盘事件,控制台打印"当前元素"。
- 然后是TextInput自身的按键处理事件。如果输入有效,事件被吞没,所以不再传递,父元素也就不会处理键盘事件。
- 如果我们按下↓键,由于TextInput不处理这个键,所以键盘事件会传递到父元素,控制台会打印出"父元素"。
注:按下Return回车键时,由于onReturnPressed比onPressed更具体,所以onReturnPressed事件优先执行,onPressed事件无需再执行(因为都是处理按下事件的)。
现在我们修改下priority优先级顺序,在代码中加入这句:Keys.priority: Keys.AfterItem。
TextInput
...
focus: true
Keys.priority: Keys.AfterItem // 加入这行代码
Keys.forwardTo: [txt]
...
运行代码后,文本输入框拥有焦点。按下a键后,事件处理顺序如下:
- 首先执行的是TextInput自身的按键处理事件。如果输入有效,事件就会被吞没,不再传递,其他元素也不会再处理键盘事件。
- 如果我们按下↓键,由于TextInput不处理这个键,所以键盘事件会继续传递。首先传递到forwardTo属性指定的元素,再到当前元素,最后到父元素。
4.3 触摸事件
在移动端,我们可以使用MouseArea元素处理触摸事件,不过只能是单点触摸,不能处理多点触摸。我们应该使用MultiPointTouchArea元素,它既可以处理单点,也可以处理多点触摸。请看下方示例代码。
注:该示例代码适用于配有触摸板或触摸屏的电脑。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
MultiPointTouchArea
anchors.fill: root
maximumTouchPoints: 3 // 1
touchPoints: [ // 2
TouchPoint id: point1,
TouchPoint id: point2,
TouchPoint id: point3
]
Rectangle
width: 30
height: 30
color: "green"
x: point1.x // 3
y: point1.y
Rectangle
width: 30
height: 30
color: "blue"
x: point2.x // 3
y: point2.y
Rectangle
width: 30
height: 30
color: "yellow"
x: point3.x // 3
y: point3.y
运行结果:
代码解释:
1. maximumTouchPoints属性用来设置最大的触摸点数,minimumTouchPoints属性用来设置最小的触摸点数。
2. touchPoints是一个列表属性,列表中的元素是触摸点对象,一个触摸点用一个TouchPoint对象来表示。设置了TouchPoint的id后,我们就能在之后的元素中使用这个触摸点对象。
3. 让三个矩形元素的坐标等于相应触摸点对象的坐标。
我们修改下上面的代码,用MultiPointTouchArea的pressed信号和touchUpdated信号实现相同的效果。
import QtQuick 2.0
Rectangle
id: root
width: 300
height: 200
MultiPointTouchArea
anchors.fill: root
maximumTouchPoints: 3
onPressed: // 2
moveRect(touchPoints)
onTouchUpdated: // 2
moveRect(touchPoints)
function moveRect(touchPoints) // 1
for(let i=0; i<touchPoints.length; i++)
if (i==0)
rect1.x = touchPoints[0].x
rect1.y = touchPoints[0].y
else if (i==1)
rect2.x = touchPoints[1].x
rect2.y = touchPoints[1].y
else if (i==2)
rect3.x = touchPoints[2].x
rect3.y = touchPoints[2].y
Rectangle
id: rect1
width: 30
height: 30
color: "green"
Rectangle
id: rect2
width: 30
height: 30
color: "blue"
Rectangle
id: rect3
width: 30
height: 30
color: "yellow"
代码解释:
1. 编写一个moveRect()函数,该函数接收一个touchPoints触摸对象列表参数。在函数中,我们循环这个列表对象,并让各个矩形元素的坐标等于相应触摸点对象的坐标。
2. 调用moveRect()函数,并传入touchPoints参数。pressed和touchUpdated信号发射时,会携带touchPoints参数,所以我们可以在花括号中直接使用这个touchPoints变量。
4.4 本章小结
1. 鼠标事件可以用MouseArea元素来处理,MouseArea元素还可以用来处理移动端的单点触摸事件。
2. 键盘事件通过Keys元素来处理,Keys元素需要作为其他元素的附加属性。Keys元素还有enabled、forwardTo以及priority三个重要属性。第一个属性用来控制是否接收键盘事件,第二个属性用来将键盘事件传递给其他元素,第三个属性用来控件事件处理的优先级顺序。
3. 可以用MultiPointTouchArea元素处理单点和多点触摸事件。
以上是关于事件处理的主要内容,如果未能解决你的问题,请参考以下文章