信号(Signal)和槽(Slot)是PyQt编程对象之间进行通信的机制。每个继承自QWidget的控件都支持信号与槽机制。信号发射时(发送请求),连接的槽函数就会自动执行(针对请求进行处理)
内置信号和槽
所谓内置信号与槽的使用。是指在发射信号时,使用窗口控件的函数,而不是自定义的函数。信号与槽的连接方法是通过QObject.signal.connect将一个QObject的信号连接到另一个QObject的槽函数。
槽函数close为窗口控件函数
信号与槽:self.pushButton.clicked.connect(self.close)
内置信号和自定义槽使用实例
槽函数showMsg为自定义函数。
信号与槽:self.pushButton.clicked.connect(self.showMsg)
自定义信号和槽使用实例
槽函数可以为自定义函数showMsg,也可以为窗口控件函数close。
这里需要注意一下声明信号的位置和父类,否则可能会报错
信号与槽:self.my_signal.connect(self.print_num)
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5 import QtCore
class Demo(QWidget):
my_signal = pyqtSignal()
def __init__(self):
super(Demo, self).__init__()
self.my_signal.connect(self.print_num)
def print_num(self):
for i in range(10000):
print(i)
不知对错
下面是我的一些理解不知对错,信号和槽提供了一种跨线程的UI访问方式,执行顺序仍然是同步执行。
import sys
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5 import QtCore
import threading
class Demo(QWidget):
my_signal = pyqtSignal()
def __init__(self):
super(Demo, self).__init__()
self.resize(400, 300)
self.label = QLabel('Hello World', self)
self.label.setGeometry(QtCore.QRect(50, 30, 89, 25))
self.my_signal.connect(self.print_num)
def mousePressEvent(self, QMouseEvent):
self.print_num()
self.print_num_asterisk()
def print_num(self):
for i in range(10000):
print(i)
def print_num_asterisk(self):
for i in range(10000):
print(str(i) + '**')
if __name__ == '__main__':
app = QApplication(sys.argv)
demo = Demo()
demo.show()
sys.exit(app.exec_())
毫无疑问运行代码,鼠标按下,会先执行print_num,再执行print_num_asterisk。
那我们把函数mousePressEvent改一下
def mousePressEvent(self, QMouseEvent):
self.my_signal.emit()
self.print_num_asterisk()
运行代码,鼠标按下,仍然会先执行print_num,再执行print_num_asterisk。
下面我们把其中一个打印函数放在多线程里
def mousePressEvent(self, QMouseEvent):
self.my_threadHandle = threading.Thread(target=self.print_num)
self.my_threadHandle.setDaemon(True)
self.my_threadHandle.start()
self.print_num_asterisk()
运行代码,鼠标按下,两个函数的打印会交替执行。
我们再做如下更改,按照预想,应该是两个函数的打印会交替执行,但实际上先执行print_num_asterisk,再执行print_num。我认为的原因,主线程也就是UI线程,是相应信号的线程,当主线程正在执行函数mousePressEvent,函数执行之后,才去响应子线程的信号。
子线程函数内不要采用信号和槽,直接调用函数就行,和主线程通讯再采用信号和槽的方式
def mousePressEvent(self, QMouseEvent):
self.my_threadHandle = threading.Thread(target=self.get_signal)
self.my_threadHandle.setDaemon(True)
self.my_threadHandle.start()
self.print_num_asterisk()
def get_signal(self):
self.my_signal.emit()
暂时的结论是信号(Signal)和槽(Slot)主要是用于窗体间通讯,想要执行异步操作还需要多线程。
def mousePressEvent(self, QMouseEvent):
self.my_threadHandle = threading.Thread(target=self.get_signal)
self.my_threadHandle.setDaemon(True)
self.my_threadHandle.start()
# self.print_num_asterisk()
def get_signal(self):
self.my_threadHandle0 = threading.Thread(target=self.get_signal0)
self.my_threadHandle0.setDaemon(True)
self.my_threadHandle0.start()
self.print_num_asterisk()
def get_signal0(self):
self.my_signal.emit()
两个函数的打印会交替执行。
def mousePressEvent(self, QMouseEvent):
self.my_threadHandle = threading.Thread(target=self.get_signal)
self.my_threadHandle.setDaemon(True)
self.my_threadHandle.start()
self.print_num_asterisk()
def get_signal(self):
self.my_threadHandle0 = threading.Thread(target=self.get_signal0)
self.my_threadHandle0.setDaemon(True)
self.my_threadHandle0.start()
#self.print_num_asterisk()
def get_signal0(self):
self.my_signal.emit()
先执行print_num_asterisk,再执行print_num
跨线程访问UI,窗体间通讯,宜采用信号和槽