如果要说 Qt 里面的最常听到的词,那么 “信号”(signals)和“槽”(slots)绝对是其中之一了,其功能简单强大,想必每个 Qt 程序员最能理解的啦,那么,想要用PySide2来写 Qt 程序,首先要接触的就是这两货的写法。
先通过一个示例来看看传统的信号和槽的写法:
import sys
from PySide2.QtWidgets import QApplication,QPushButton,QWidget
from PySide2.QtCore import SIGNAL,QObject
def slotBtnClicked():
print("btn clicked..")
app = QApplication(sys.argv)
widget = QWidget()
widget.resize(300,200)
widget.show()
btn = QPushButton("Click me",widget)
btn.move(widget.width()/2. - btn.width()/2.,widget.height()/2. - btn.height()/2.)
QObject.connect(btn,SIGNAL('clicked()'),slotBtnClicked)
btn.show()
sys.exit(app.exec_())
这里定义了一个简单的按钮,通过将按钮的点击信号连接到槽来实现其效果,注意这里的连接方法,是通过QObject.connect来实现的,其写法比较麻烦。而这里的槽,可以不用像在 C++中那样特意声明为 SLOTS 类型。
点击按钮后终端输出:
btn clicked..
btn clicked..
基本效果已达成。
刚刚说到,上面那种连接方法有些复杂,有没有更简单的写法呢,答案是肯定的。
接下来看看新的写法,会比之前的更简单。
同样是上面的示例,修改如下:
import sys
from PySide2.QtWidgets import QApplication,QPushButton,QWidget
from PySide2.QtCore import SIGNAL,QObject
def slotBtnClicked():
print("btn clicked..")
app = QApplication(sys.argv)
widget = QWidget()
widget.resize(300,200)
widget.show()
btn = QPushButton("Click me",widget)
btn.move(widget.width()/2. - btn.width()/2.,widget.height()/2. - btn.height()/2.)
# QObject.connect(btn,SIGNAL('clicked()'),slotBtnClicked)
btn.clicked.connect(slotBtnClicked)
btn.show()
sys.exit(app.exec_())
这里将连接方式改成了
btn.clicked.connect(slotBtnClicked)
这种写法当然更简单明了,其效果是一样的。
上面的两个示例都是介绍如何没有带参数的信号和槽的连接方式和用法,那么,如果信号中带参数又会有什么不同呢,接着看一个示例:
import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QObject, Signal, Slot
app = QApplication(sys.argv)
#定义一个接受字符串类型的槽
@Slot(str)
def say_some_words(words):
print(words)
class Communicate(QObject):
#定义一个信号
speak = Signal(str)
someone = Communicate()
someone.speak.connect(say_some_words) #连接信号和槽
someone.speak.emit("hello world.") #发送信号
运行,输出:
hello world.
接下来在上面示例的基础上,新加载一个装饰器,将槽定义为可以接收 int 类型。
import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QObject, Signal, Slot
app = QApplication(sys.argv)
@Slot(int)
@Slot(str)
def say_some_words(words):
print(words)
class Communicate(QObject):
#定义一个信号
speak_words = Signal(str)
speak_number = Signal(int)
someone = Communicate()
someone.speak_words.connect(say_some_words)
someone.speak_words.emit("hello world.")
someone.speak_number.connect(say_some_words)
someone.speak_number.emit(1234)
这个示例中,定义了两个信号,分别用来发送数字和字符串,并且这两个信号都连接到同一个槽中,并且这个槽有两个装饰器,分别接收 int 类型和字符串类型。
运行结果:
hello world.
1234
对于上面的示例,分别将两个信号连接到同一个槽,两个信号传入的参数类型不一致,其实还有更高级的一种写法来替代这种方式,直接看示例吧。
import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QObject, Signal, Slot
app = QApplication(sys.argv)
@Slot(int)
@Slot(str)
def say_something(stuff):
print(stuff)
class Communicate(QObject):
#定义一个信号,可用来接收字符串类型或者 int 类型参数
speak = Signal((str,),(int,))
someone = Communicate()
someone.speak.connect(say_something)
someone.speak[int].connect(say_something)
someone.speak.emit("hello world.")
someone.speak[int].emit(1234)
上面的示例,定义了一个接收两个类型参数的信号,注意这里的连接方式和发送信号的方式:
someone.speak.connect(say_something)
someone.speak[int].connect(say_something)
someone.speak.emit("hello world.")
someone.speak[int].emit(1234)
只选择了其中一个参数来连接和发送信号,这种写法在 C++中是没有的。
这里的
someone.speak.connect(say_something)
等同于
someone.speak[str].connect(say_something)
someone.speak.emit(“hello world.”)
等同于
someone.speak[str].emit(“hello world.”)
也就是说第一个参数的类型声明可以在连接和发送信号的时候省略。
而信号的定义
speak = Signal((str,),(int,))
这种写法的意思是,既可以接收字符串类型,也可以接收 int类型,二者选其一,千万要注意的是,这个定义类型,并不是说可以同时接收两个参数,而是只能选择其中一种类型。
那么如果要定义能接受两个参数的信号又怎么写呢,很简单,看下面的示例:
import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QObject, Signal, Slot
app = QApplication(sys.argv)
@Slot(int)
@Slot(str)
def say_something(stuff):
print(stuff)
@Slot(int)
@Slot(str)
def say_word_number(word,number):
print(word,number)
class Communicate(QObject):
#定义一个信号
speak = Signal(str,int)
someone = Communicate()
# someone.speak[str].connect(say_something)
# someone.speak[int].connect(say_something)
someone.speak.connect(say_word_number)
# someone.speak[str].emit("hello world.")
# someone.speak[int].emit(1234)
someone.speak.emit("hello world",2345)
如上示例,如果要接收一个以上的参数,那么直接添加参数类型即可,speak = Signal(str,int)。
又或者,如果要像之前的示例一样,做一个可选择类型的参数,并且其中一个参数可以传入两个值,可以像下面这样定义信号和连接信号:
import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QObject, Signal, Slot
app = QApplication(sys.argv)
@Slot(int)
@Slot(str)
def say_something(stuff):
print(stuff)
@Slot(int)
@Slot(str)
def say_word_number(word,number):
print(word,number)
class Communicate(QObject):
#定义一个信号
speak = Signal((str,int),(int,))
someone = Communicate()
someone.speak[str,int].connect(say_word_number)
someone.speak[str,int].emit("hello world",2345)
代码很简单,就不再赘述了。
上面的代码写得有些乱,不方面扩展和管理,其实可以将信号和槽以及连接方式都封装到类中去,这样一来,代码的可阅读性以及代码结构就会更清晰,如下示例。
import sys
from PySide2.QtCore import QObject, Signal
class Communicate(QObject):
speak = Signal()
def __init__(self):
super(Communicate, self).__init__()
self.speak.connect(self.say_hello)
def speaking_method(self):
self.speak.emit()
def say_hello(self):
print("Hello world")
someone = Communicate()
someone.speaking_method()
关于 Qt for Python 中信号和槽的用法就介绍到这里,上述代码都很简单,就不再一一赘述。