[PyQt]如何使用信号槽?
前言信号(singal)与槽(slot)用于对象相互通信,信号:当某个对象的某个事件发生时,触发一个信号,槽:响应指定信号的所做的反应,其实信号槽类似于.NET里面的委托、事件,比如Repeater控件类,当行数据绑定后,触发一个ItemDataBound事件,不管使用者使用会监听该事件并做额外处理,其控件类内部都会触发该事件,这种机制很多程度提高了类的封装性和完整性。
PyQt的窗体控件类已经有很多的内置信号,开发者也可以添加自己的自定义信号,信号槽有如下特点:
- 一个信号可以连接到许多插槽。
- 一个信号也可以连接到另一个信号。
- 信号参数可以是任何Python类型。
- 一个插槽可以连接到许多信号。
- 连接可能会直接(即同步)或排队(即异步)。
- 连接可能会跨线程。
- 信号可能会断开(以上几条特点翻译于官方文档),接下来,我将以若干个实例,来体现以上几个特点。
内置信号槽、自定义信号槽的使用内置信号槽的使用
12345678910111213141516from
PyQt4.QtGui
import
*
from
PyQt4.QtCore
import
*
def
sinTest():
btn.setText(
"按钮文本改变"
)
app
=
QApplication([])
main
=
QWidget()
main.resize(
200
,
100
)
btn
=
QPushButton(
"按钮文本"
,main)
##按钮btn的内置信号连接名为sinTest的槽
btn.clicked.connect(sinTest)
main.show()
app.exec_()
运行结果:
自定义信号槽的使用
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566from
PyQt4.QtGui
import
*
from
PyQt4.QtCore
import
*
class
SinClass(QObject):
##声明一个无参数的信号
sin1
=
pyqtSignal()
##声明带一个int类型参数的信号
sin2
=
pyqtSignal(
int
)
##声明带一个int和str类型参数的信号
sin3
=
pyqtSignal(
int
,
str
)
##声明带一个列表类型参数的信号
sin4
=
pyqtSignal(
list
)
##声明带一个字典类型参数的信号
sin5
=
pyqtSignal(
dict
)
##声明一个多重载版本的信号,包括了一个带int和str类型参数的信号,以及带str参数的信号
sin6
=
pyqtSignal([
int
,
str
], [
str
])
def
__init__(
self
,parent
=
None
):
super
(SinClass,
self
).__init__(parent)
##信号连接到指定槽
self
.sin1.connect(
self
.sin1Call)
self
.sin2.connect(
self
.sin2Call)
self
.sin3.connect(
self
.sin3Call)
self
.sin4.connect(
self
.sin4Call)
self
.sin5.connect(
self
.sin5Call)
self
.sin6[
int
,
str
].connect(
self
.sin6Call)
self
.sin6[
str
].connect(
self
.sin6OverLoad)
##信号发射
self
.sin1.emit()
self
.sin2.emit(
1
)
self
.sin3.emit(
1
,
"text"
)
self
.sin4.emit([
1
,
2
,
3
,
4
])
self
.sin5.emit({
"name"
:
"codeio"
,
"age"
:
"25"
})
self
.sin6[
int
,
str
].emit(
1
,
"text"
)
self
.sin6[
str
].emit(
"text"
)
def
sin1Call(
self
):
(
"sin1 emit"
)
def
sin2Call(
self
,val):
(
"sin2 emit,value:"
,val)
def
sin3Call(
self
,val,text):
(
"sin3 emit,value:"
,val,text)
def
sin4Call(
self
,val):
(
"sin4 emit,value:"
,val)
def
sin5Call(
self
,val):
(
"sin5 emit,value:"
,val)
def
sin6Call(
self
,val,text):
(
"sin6 emit,value:"
,val,text)
def
sin6OverLoad(
self
,val):
(
"sin6 overload emit,value:"
,val)
sin
=
SinClass()
运行结果:
sin1 emit
sin2 emit,value: 1
sin3 emit,value: 1 text
sin4 emit,value: [1, 2, 3, 4]
sin5 emit,value: {'age': '25', 'name': 'codeio'}
sin6 emit,value: 1 text
sin6 overload emit,value: text
信号槽N对N连接、断开连接以及多线程信号槽通信信号槽N对N连接、断开连接
123456789101112131415161718192021222324252627282930313233343536373839404142434445from
PyQt4.QtGui
import
*
from
PyQt4.QtCore
import
*
class
SinClass(QObject):
##声明一个无参数的信号
sin1
=
pyqtSignal()
##声明带一个int类型参数的信号
sin2
=
pyqtSignal(
int
)
def
__init__(
self
,parent
=
None
):
super
(SinClass,
self
).__init__(parent)
##信号sin1连接到sin1Call和sin2Call这两个槽
self
.sin1.connect(
self
.sin1Call)
self
.sin1.connect(
self
.sin2Call)
##信号sin2连接到信号sin1
self
.sin2.connect(
self
.sin1)
##信号发射
self
.sin1.emit()
self
.sin2.emit(
1
)
##断开sin1、sin2信号与各槽的连接
self
.sin1.disconnect(
self
.sin1Call)
self
.sin1.disconnect(
self
.sin2Call)
self
.sin2.disconnect(
self
.sin1)
##信号sin1和sin2连接同一个槽sin1Call
self
.sin1.connect(
self
.sin1Call)
self
.sin2.connect(
self
.sin1Call)
##信号再次发射
self
.sin1.emit()
self
.sin2.emit(
1
)
def
sin1Call(
self
):
(
"sin1 emit"
)
def
sin2Call(
self
):
(
"sin2 emit"
)
sin
=
SinClass()
运行结果:
sin1 emit
sin2 emit
sin1 emit
sin2 emit
sin1 emit
sin1 emit
多线程信号槽通信
12345678910111213141516171819202122232425262728293031323334353637383940414243444546from
PyQt4.QtCore
import
*
from
PyQt4.QtGui
import
*
class
Main(QWidget):
def
__init__(
self
, parent
=
None
):
super
(Main,
self
).__init__(parent)
##创建一个线程实例并设置名称、变量、信号槽
self
.thread
=
MyThread()
self
.thread.setIdentity(
"thread1"
)
self
.thread.sinOut.connect(
self
.outText)
self
.thread.setVal(
6
)
def
outText(
self
,text):
(text)
class
MyThread(QThread):
sinOut
=
pyqtSignal(
str
)
def
__init__(
self
,parent
=
None
):
super
(MyThread,
self
).__init__(parent)
self
.identity
=
None
def
setIdentity(
self
,text):
self
.identity
=
text
def
setVal(
self
,val):
self
.times
=
int
(val)
##执行线程的run方法
self
.start()
def
run(
self
):
while
self
.times >
0
and
self
.identity:
##发射信号
self
.sinOut.emit(
self
.identity
+
" "
+
str
(
self
.times))
self
.times
-
=
1
app
=
QApplication([])
main
=
Main()
main.show()
app.exec_()
运行结果:
thread1 6
thread1 5
thread1 4
thread1 3
thread1 2
thread1 1
小结该篇主要介绍了信号槽多对多连接、连接断开、多线程如何使用信号槽进行通信,Qt除了窗体部件类,还提供了很多其他系统相关操作类,使用起来很方便,多线程使用了Qt自带的QThread类,这里只是做了个简单的测试,多线程还有许多要深入探究的,有几个心得:
- 对于单一信号连接多个槽的情况,发射信号后,槽的执行顺序就是信号槽连接的顺序
- GUI所在的线程是主线程,线程之间可以通过信号槽来通信,这个方法适用于重写PyQt部件类后对外开发接口
- 我使用的环境是:PyQt4.8.4,Qt4.7.2,Python3.2,这个版本的PyQt将使用新的信号槽风格
- 槽的参数个数可以小于等于信号的参数个数, 换句话说,信号并不关心谁使用了它,怎么使用它,槽可以选择性地使用信号的参数
- 用pyqtSignal来声明一个新信号,它的参数可能是Python的类型对象或者C++的类型
- 新的信号只能在QObject的子类中声明