用户名 立即注册 找回密码

微雪课堂

搜索
微雪课堂 树莓派 树莓派QT教程 查看内容

树莓派Qt系列教程8: 信号与槽

2020-7-18 13:16| 发布者: dasi| 查看: 8598| 评论: 0|原作者: dasi

摘要: 这一节我们将学习QML中非常重要的机制---信号和槽

前言

前一篇文章中我们写提到了点击button控件可能会触发pressed,released,clicked信号,这个应用就属于信号与槽。信号和槽作为Qt的和新机制,在Qt编程中有着非常广泛的应用。

事实上,我们在Qt开发中,要想妥善的处理各种事件,根本就离不开信号与槽。信号是事件,信号通过信号处理程序来响应。当一个信号被发射时,相应的信号处理程序就会被调用,在处理程序中编写代码来使控件响应事件。

信号处理程序

信号处理器

先来介绍第一种方法,通过信号处理器。通常是链接QML类型的已知信号,分为两类。一类是由用户输入产生,比如说按键、鼠标等;另一类是由对象的状态或者属性变化产生的,比如说鼠标被按下或释放等。先来通过按键关闭程序来看一下。

1Button{
2        text: "quit"
3        anchors.centerIn: parent
4        onClicked: {
5            Qt.quit()
6        }
7    }

每当按钮被点击时,onClicked处理程序就会被调用,可以在此程序中设置退出应用闭或者其他的操作。是不是很简单?运行看一下效果。

总结一下信号处理器的用法,当信号被发射时,对象定义就声明一个名为on<Signal>的信号处理程序,其中,<Signal>是信号的名称,首字母必须大写。我们在处理程序中编写想要实现的效果。

属性改变信号处理程序的用法也非常简单,通常以on<Property>Changed的形式写入,<Property>是属性的名称,首字母大写。我们提到的这两种信号处理器都是放在当前元素的内部,如果要处理的信号不是当前元素发出来的呢?这就引出了我们接下来要说的附加信号处理器。

附加信号处理器

对象本身或者其基类没有的属性和信号,需要通过外部(附加类型)提供。看个官方文档的例程,运行程序,控制台会输出“哈哈哈”的信息
语法结构:

<AttachingType>.on<Signal>
1Item{
2        width: 100
3        height: 100
4 
5        focus: true
6        Keys.enabled: true
7        Keys.onReturnPressed: console.log("哈哈哈")
8    }

之前提过Item不可见,通过附加类型Keys来访问其附加属性和附加的信号处理程序。enable设置为true,表明启用键盘处理。因为Keys提供了returnPressed信号,所以可以通过onReturnPressed来引用附加信号处理程序。

Connections

前面的两种方式都是用on<Signal>的方式,但是当我们遇到下面的情况时:

  • 将多个对象链接到同一个信号上
  • 在发出信号的作用域之外来建立连接
  • 发射信号的对象没有在QML中定义

这个时候,我们就需要使用Connections

01Rectangle{
02        id:rect
03        anchors.fill: parent
04        color: "red"
05 
06        Button{
07            id:button
08            width: 100
09            height: 30
10            text: "改变颜色"
11            anchors.centerIn: rect
12        }
13 
14        Connections{
15            target: button
16            onClicked:{
17                rect.color = "green"
18            }
19        }
20    }

定义一个充满窗口的红色矩形,在其中间设置一个按钮,我们想要实现的效果就是按下按钮,矩形的颜色变成绿色。注意Connections的用法,我们只需要将onClicked处理程序放置其中,并制定target就可以了!效果如下:

自定义信号

当QML现有的信号无法满足时,我们就需要自定义信号了。通过关键字signal来添加自定义的信号。语法结构为:

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

我们还是通过例子来了解自定义信号的使用,还是上一节中定义的矩形,我们在里面定义属性和信号。自定义属性的方法也很简单,语法结构如下:

[default] property <propertyType> <propertyName> : <value>
01Rectangle{
02        id: rect
03        anchors.fill: parent
04        color:"green"
05 
06        property int clickcount: 0   //自定义点击次数
07 
08        signal signal1    //自定义无参信号
09        signal signal2(string str,int value)   //定义有参信号
10    }

在Qt c++中通过emit来发射信号,而在QML中直接将声明的信号当做函数来调用就可以触发了。所以我们继续在Rectangle对象中再定义一个按钮button,在其信号处理程序中直接调用定义的信号函数。

01Button{
02        text: "点击我"
03        anchors.centerIn: rect
04        onClicked: {
05            rect.clickcount++
06            rect.signal2("点击第",rect.clickcount)
07            if(rect.clickcount%5===0)
08            {
09                rect.signal1()
10            }
11        }
12    }

每次点击,点击次数都会累加且调用signal2函数,当点击次数增加到5时,同时调用siagnl1函数。既然定义了信号函数,必然会有信号处理函数。我们想要实现的效果是每次点击,都会输出现在点击的是第几次。当点击到第5和5的倍数次时,矩形的颜色随机改变。还是在Rectangle对象中输入以下代码

1onSignal1: {
2    rect.color = Qt.rgba(Math.random(), Math.random(),Math.random(), 1)
3}
4 
5onSignal2: {
6    console.log(str,value,"次")
7}

最后实现效果如下:

信号与槽的连接

大多数情况下,通过上面两种方式接收信号就足够啦,然而,要将信号连接至多个方法/信号,信号处理程序根本就无法实现。

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

01Rectangle{
02        id: rect
03        anchors.fill: parent
04        //定义信号
05        signal signal1(string str)
06        signal signal2(string str)
07         
08        Row{
09            id: row
10            anchors.centerIn: rect
11            Label{
12                id:text1
13                text: "a"
14                padding: 80
15 
16            }
17            Label{
18                id: text2
19                text: "b"
20                padding: 80
21            }
22            Label{
23                id: text3
24                text: "c"
25                padding: 80
26            }
27        }
28 
29        Button{
30            anchors.top: row.bottom
31            anchors.horizontalCenter: rect.horizontalCenter
32            text: "变成大写"
33            onClicked: {
34                rect.signal1("变为大写了!!!")
35            }
36        }
37 
38        Component.onCompleted: {
39            //连接到方法
40            rect.signal1.connect(method1)
41            rect.signal1.connect(method2)
42            rect.signal1.connect(method3)
43            //连接到信号
44            rect.signal1.connect(rect.signal2)
45        }
46        //信号处理程序
47        onSignal2: {
48            console.log(str)
49        }
50        //定义方法
51        function method1(){
52            text1.text = "A"
53        }
54 
55        function method2(){
56            text2.text = "B"
57        }
58 
59        function method3(){
60            text3.text = "C"
61        }
62    }

我们在Rectangle对象中通过布局管理器定义了三个label和一个按钮。label控件非常简单,它继承于Text,所有的属性都和Text元素一样。当button的click信号被发射时,调用signal1函数,然后在下面将signal1信号连接到signal2信号和方法,这样,新定义的信号被发射,从而执行对应的信号处理函数,同时调用新定义的方法执行程序。运行看一下效果。

总结

源码:8.zip

QML中,信号和信号处理程序是一个非常重要的机制,它有很多种用法,但是万变不离其宗,归根结底还是信号的发射和槽的接收。建议大家一定要熟练掌握。多去down一些例程用来练习。OK,下节再会!

227

顶一下

刚表态过的朋友 (227 人)

相关阅读

微雪官网|产品资料|手机版|小黑屋|微雪课堂. ( 粤ICP备05067009号 )

GMT+8, 2025-4-14 18:32 , Processed in 0.013268 second(s), 13 queries .

返回顶部