事件处理

Posted la_vie_est_belle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事件处理相关的知识,希望对你有一定的参考价值。

目录

4.1 鼠标事件

4.2 键盘事件

4.3 触摸事件

4.4 本章小结


事件的背后原理其实是信号和槽。事件被触发,就是某个信号被发射了。在本章,我们会学习以下几个常用事件,这些事件会由专门的元素进行处理。

  1. 鼠标事件
  2. 键盘事件
  3. 触摸事件

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时,事件的处理顺序是这样的:

  1. forwardTo属性中设置的元素键盘事件
  2. 具体的键盘事件,比如onReturnPressed
  3. onPressed, onReleased键盘事件
  4. 元素本身带有的键盘事件,比如TextInput(单行文本输入框)元素的按键处理。
  5. 父元素中的键盘事件

当设置为Keys.AfterItem时,事件的处理顺序是这样的:

  1. 元素本身带有的键盘事件,比如TextInput(单行文本输入框)元素的按键处理。
  2. forwardTo属性中设置的元素键盘事件
  3. 具体的键盘事件,比如onReturnPressed
  4. onPressed, onReleased键盘事件
  5. 父元素中的键盘事件

请看下方代码:

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键后,事件处理顺序如下:

  1. forwardTo属性中Text元素的onPressed键盘事件,控制台打印"forwardTo元素"。
  2. 然后是TextInput当前元素的onPressed键盘事件,控制台打印"当前元素"。
  3. 然后是TextInput自身的按键处理事件。如果输入有效,事件被吞没,所以不再传递,父元素也就不会处理键盘事件。
  4. 如果我们按下↓键,由于TextInput不处理这个键,所以键盘事件会传递到父元素,控制台会打印出"父元素"。

注:按下Return回车键时,由于onReturnPressed比onPressed更具体,所以onReturnPressed事件优先执行,onPressed事件无需再执行(因为都是处理按下事件的)。

现在我们修改下priority优先级顺序,在代码中加入这句:Keys.priority: Keys.AfterItem。

TextInput 
    ...

    focus: true
    Keys.priority: Keys.AfterItem   // 加入这行代码
    Keys.forwardTo: [txt]

    ...

运行代码后,文本输入框拥有焦点。按下a键后,事件处理顺序如下:

  1. 首先执行的是TextInput自身的按键处理事件。如果输入有效,事件就会被吞没,不再传递,其他元素也不会再处理键盘事件。
  2. 如果我们按下↓键,由于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元素处理单点和多点触摸事件。

以上是关于事件处理的主要内容,如果未能解决你的问题,请参考以下文章

事件处理

BigDecimal与double

Android 基于回调的事件处理机制详解

浮点数大于0

浮点数定义到底是啥意思啊?求教!

浮点数精度丢失问题详解