zoukankan      html  css  js  c++  java
  • Qt Quick 事件处理之信号与槽

    信号和槽作为 Qt 的核心机制,在 Qt 编程中有着广泛的应用。同样,QML 也继承了这样的特性 - 信号和信号处理程序 ,只不过叫法上略有不同。

    • 信号:来自 QML 对象的通知。

    • 信号:来自 QML 对象的通知。

    • 信号处理程序:由信号触发的表达式(或函数),也被称为 Qt C++ 中的 “槽”。

    信号是事件,信号通过信号处理程序来响应。当一个信号被发射时,相应的信号处理程序就会被调用,在处理程序中放置逻辑(例如:脚本或其他操作)以允许组件响应事件。


    一、使用信号处理程序接收信号

    信号是来自对象的通知,表示发生了某些事件(例如:鼠标已点击、属性已更改、动画已启动/停止)。每当特定信号被发射时,若要接收通知:

    • 对象定义应声明一个名为on<Signal>的信号处理程序,其中<Signal>是信号的名称,首字母大写。
    • 信号处理程序必须在发出信号的对象定义中声明,并且处理程序应包含调用时要执行的 JavaScript 代码块。

    例如, Button 类型有一个clicked信号,无论何时在该按钮上单击鼠标都会发出该信号。由于信号名称是clicked,所以接收该信号的信号处理程序应命名为onClicked

    下面的示例中,每当按钮被点击时,onClicked处理程序就会被调用,退出应用,其代码如下:

    import QtQuick 2.2
    import QtQuick.Controls 1.1
     
    Rectangle {
         320
        height: 240
        color: "gray"
        
        Button {
            test: "Quit"
            anchors.centerIn: parent
            onClicked: {
                Qt.quit()
            }
        }
    }
    

    使用 qmlscene 执行的效果如下所示:

    img


    (2)属性改变信号处理程序

    当 QML 属性值发生改变时,将自动发出信号。这种类型的信号是属性改变信号,对应的处理程序为属性改变信号处理程序。

    • 属性改变信号处理程序以on<Property>Changed的形式写入,<Property>是属性的名称,首字母大写。

    例如,MouseArea 类型具有pressed属性,要在该属性改变时接收通知,需要编写名onPressedChanged的信号处理程序:

    import QtQuick 2.2
    
    Rectangle {
        id: rect
         100
        height: 100
    
        MouseArea {
            anchors.fill: parent
            onPressedChanged: {  // 鼠标按下/释放
                console.log("Mouse area is pressed?", pressed)
            }
        }
    }
    

    尽管 MouseArea 文档中没有记录名为onPressedChanged的信号处理程序,但是因为存在 pressed 属性,所以它也被隐式地提供了。


    (3)使用 Connections 类型

    QtQuick 模块提供了 Connections 类型,用于连接到任意对象的信号。Connections 的优点是:

    • 可以在发射信号的对象外部访问该信号

    例如,上述示例中的onClicked处理程序可以由根 Rectangle 接收,只需要将其放置在一个 Connections 对象中,并指定 target 为 mouseArea:

    import QtQuick 2.2
    
    Rectangle {
        id: rect
         100; height: 100
    
        MouseArea {
            id: mouseArea
            anchors.fill: parent
        }
    
        Connections {
            target: mouseArea
            onClicked: {
                rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
            }
        }
    }
    

    (4)附加信号处理器

    附加信号处理程序所接收的信号来自附加类型,而非声明处理程序的对象。附加,也称为额外。可以简单理解为:对象本身或其基类没有的属性和信号,需要通过外部(附加类型)提供。

    要引用附加属性和处理程序,可以使用以下语法形式:

    • <AttachingType>.<propertyName>
    • <AttachingType>.on<SignalName>

    例如,下面的 Item 可以通过附加类型 Keys 来访问其附加属性和附加信号处理程序:

    import QtQuick 2.2
    
    Item {
         100; height: 100
    
        focus: true
        Keys.enabled: true
        Keys.onReturnPressed: console.log("Return key was pressed")  // 按下回车键,打印 log 信息
    }
    

    enabled 是 Keys 的一个属性,为其赋值为 true(默认值是 true,这里主要用于说明如何使用附加类型的属性),表明启用键盘处理。由于 Keys 提供了returnPressed信号,所以可以通过onReturnPressed来引用相应的附加信号处理程序。


    类似的附加类型还有很多,例如:Component,它有一个onCompleted附加信号处理程序,通常用于在创建完成后执行某些 JavaScript 代码:

    import QtQuick 2.2
    
    Rectangle {
         200; height: 200
        color: Qt.rgba(Qt.random(), Qt.random(), Qt.random(), 1)
    
        Component.onCompleted: {
            console.log("The rectangle's color is", color)
        }
    }
    

    onCompleted处理程序没有响应来自 Rectangle 类型的 completed 信号。相反,Component 对象由 QML 引擎自动附加到 Rectangle 对象,当对象完全创建时,引擎发出completed信号,从而触发Component.onCompleted信号处理程序。


    二、自定义信号

    当现有信号无法满足,这时,最好的方法就是自定义信号。 可以通过 signal 关键字来添加自定义信号,语法如下:

    signal <name>[([<type> <parameter name>[, ...]])]
    

    例如,下面声明了三个自定义信号:

    import QtQuick 2.2
    
    Item {
        signal clicked
        signal hovered()
        signal actionPerformed(string action, var actionResult)
    }
    

    如果信号没有参数,括号 “()” 是可选的;倘若有参数,那么必须声明参数的类型。


    例如,假设下面的代码被定义在一个名为 SquareButton.qml 的文件中,Rectangle 对象有一个 activated 信号。当子 MouseArea 被点击时,它会以鼠标点击的坐标发出 parent 的 activated 信号:

    // SquareButton.qml
    Rectangle {
        id: root
    
        signal activated(real xPosition, real yPosition)
    
        property int side: 100
         side; height: side
    
        MouseArea {
            anchors.fill: parent
            onPressed: root.activated(mouse.x, mouse.y)
        }
    }
    

    然后,SquareButton 的任何对象都可以使用 onActivated 信号处理程序连接到 activated 信号:

    // myapplication.qml
    SquareButton {
        onActivated: console.log("Activated at " + xPosition + "," + yPosition)
    }
    
    

    三、信号到方法/信号的连接

    大部分情况下,通过信号处理程序接收信号就足够了,然而,要将信号连接至多个方法/信号,这对于信号处理程序来说是不可能的(因为信号处理程序的命名必须唯一)。

    在 Qt C++ 中,信号与槽的连接方式使用的是QObject::connect()。相应地,在 QML 中,signal 对象也有一个 connect() 方法,用于将信号连接到一个方法或另一信号。当信号连接到方法时,无论信号何时发出,该方法都将被自动调用。有了这种机制,可以通过方法来接收信号,而无需使用信号处理器。

    所以呢,相对于信号处理程序来说,connect() 更加灵活,可以将信号连接至多个方法/信号 。


    1、信号到方法的连接

    下面,使用 connect() 方法将 messageReceived 信号连接到两个方法:

    import QtQuick 2.2
    
    Rectangle {
        id: relay
    
        signal messageReceived(string message, string qq)
    
        Component.onCompleted: {
            relay.messageReceived.connect(sendToLiLei)  // 连接信号和方法
            relay.messageReceived.connect(sendToHanMeimei)  // 连接信号和方法
            relay.messageReceived("Welcome to join us(QML分享与交流)", "26188347")  // 发射信号
        }
    
        function sendToLiLei(message, qq) {
            console.log("Sending to LiLei: " + message + ", " + qq)
        }
        function sendToHanMeimei(message, qq) {
            console.log("Sending to HanMeimei: " + message + ", " + qq)
        }
    }
    
    

    广播一下,李雷和韩梅梅就可以很快的找到组织了!


    有 connect() 方法,必然也会有相应的 disconnect() 方法,用于删除连接的信号:

    Rectangle {
        id: relay
        //...
    
        function removeLiLeiSignal() {
            relay.messageReceived.disconnect(sendToLiLei)
        }
    }
    
    

    用法很简单,和 connect() 相同。


    2、信号到信号的连接

    通过将信号连接到其他信号,connect() 方法可以形成不同的信号链。

    import QtQuick 2.2
    
    Rectangle {
        id: forwarder
         100; height: 100
    
        signal sendToLiLei()  // 自定义信号
        signal sendToHanMeimei()  // 自定义信号
        onSendToLiLei: console.log("Send to LiLei")  // 信号处理程序
        onSendToHanMeimei: console.log("Send to HanMeimei")  // 信号处理程序
    
        MouseArea {
            id: mousearea
            anchors.fill: parent
            onClicked: console.log("Clicked")
        }
    
        Component.onCompleted: {
            // 连接信号至两个信号
            mousearea.clicked.connect(sendToLiLei)
            mousearea.clicked.connect(sendToHanMeimei)
        }
    }
    
    

    每当 MouseArea 的 clicked 信号被发射,sendToLiLei、sendToHanMeimei 信号也将自动发射,从而执行对应的信号处理程序。 这时,输出如下:

    Clicked
    Send to LiLei
    Send to HanMeimei
    
    

    建议: 在 QML 中,信号和信号处理器程序是一个核心机制,一定要熟练掌握。


    参考:

    《Qt Quick核心编程》第6章

    Qt Quick 事件处理之信号与槽

    QML 信号和信号处理器程序


  • 相关阅读:
    如何快速且深入的学习一门新技术
    为什么说云原生会成为未来企业技术变迁的趋势
    高并发场景下锁的使用技巧
    开箱即用~基于.NET Core的敏捷开发框架规划
    为什么在做微服务设计的时候需要DDD?
    为什么我使用了索引,查询还是慢?
    解读中兴通信在物联网行业如何践行DDD
    服务发现技术是如何演进出来的?
    关于盘点和总结的那点事儿
    文件上传 通过 ServletContext.getRealPath()获取不到路径&war与war exploded的区别
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/11782222.html
Copyright © 2011-2022 走看看