【PySide6】信号(signal)和槽函数(slot),以及事件过滤器

发布时间 2023-12-22 22:33:13作者: China Soft

https://blog.csdn.net/qq_25262697/article/details/129374905

说明
在PYQT中,父控件可以通过两种方式响应子控件的事件:

通过信号(signal)和槽函数(slot)机制连接子控件和父控件
父控件可以通过设置eventFilter()方法来监听响应子控件的事件
一、信号(signal)和槽函数(slot)
示例
在PYQT中,每个组件都可以发出信号(signal),表示某个事件发生了。父组件可以通过connect()方法将子组件的信号连接到自己的槽函数(slot)中,从而响应这个事件。
举个例子,在一个界面中,可能有一个按钮(btn),当用户点击按钮时,需要将用户的操作记录到日志中。这时,可以在父组件中定义一个槽函数(log),然后将按钮的clicked信号连接到这个槽函数中:

class MyWindow(QWidget):
def __init__(self):
super().__init__()

# 创建一个按钮
self.btn = QPushButton('Click me', self)

# 将按钮的clicked信号连接到log槽函数中
self.btn.clicked.connect(self.log)

def log(self):
# 记录日志
print('Button clicked')
1
2
3
4
5
6
7
8
9
10
11
12
13
QThread 多线程
在 PyQt 中,可以使用信号和槽机制来实现不同线程之间的通信。在使用 QThread 时,可以将 QThread 的子类化与信号和槽机制结合使用来实现多线程编程。

以下是一个简单的示例,展示了如何在 QThread 中使用信号和槽函数:

from PySide6.QtCore import QThread, Signal

class Worker(QThread):
finished = Signal() # 定义一个信号

def __init__(self):
super().__init__()

def run(self):
# 这里可以执行耗时操作
self.finished.emit() # 发射信号

class MainWindow(QMainWindow):
def __init__(self):
super().__init__()

self.worker = Worker()
self.worker.finished.connect(self.on_finished) # 连接信号和槽函数

def on_finished(self):
# 这里可以进行 UI 更新等操作
pass

def start_work(self):
self.worker.start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在上面的示例中,我们定义了一个 Worker 类,它继承自 QThread 类。Worker 类中定义了一个 finished 信号,用于在耗时操作完成后发射信号。在 MainWindow 类中,我们创建了一个 Worker 对象,并将其 finished 信号连接到 on_finished 槽函数上。在 start_work 函数中,我们启动了 Worker 线程,耗时操作完成后会发射 finished 信号,从而触发 on_finished 槽函数的执行。

以下是另一个简单的示例,展示了如何在 QThread 中使用信号和槽函数:
```python
from PySide6.QtCore import QThread, Signal, Slot

class WorkerThread(QThread):
new_data = Signal(str)

def __init__(self):
super(WorkerThread, self).__init__()

def run(self):
for i in range(20):
data = "Data: " + str(i)
self.new_data.emit(data)
self.msleep(1000)

class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()

self.worker_thread = WorkerThread()
self.worker_thread.new_data.connect(self.on_new_data)

self.start_button = QPushButton("Start")
self.start_button.clicked.connect(self.worker_thread.start)

self.layout = QVBoxLayout()
self.layout.addWidget(self.start_button)

self.central_widget = QWidget()
self.central_widget.setLayout(self.layout)
self.setCentralWidget(self.central_widget)

@Slot(str)
def on_new_data(self, data):
print("Received data:", data)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
在上面的示例代码中,创建了一个WorkerThread类,该类继承自QThread,并包含一个new_data信号。在run方法中,通过emit方法发送new_data信号。在MainWindow类中,创建了一个WorkerThread实例,并将其new_data信号连接到on_new_data槽函数。当用户单击Start按钮时,调用worker_thread.start方法启动新线程。

在on_new_data槽函数中,打印接收到的数据。注意,on_new_data方法带有一个字符串参数,该参数类型必须与new_data信号的参数类型相同。这是通过在on_new_data方法上添加@Slot(str)装饰器来实现的。

总的来说,在pyside6中使用信号和槽函数来使用QThread非常简单,只需将信号连接到槽函数即可。但请注意要在正确的线程中使用信号和槽函数。如果将信号发射到不同的线程中,可能会导致应用程序崩溃或未定义的行为。

二、事件过滤器
在PySide6中,如果在一个widget中创建了另一个widget,那么新widget默认是不会响应父控件的事件的。如果需要让新widget能够响应控件的事件,可以通过设置事件过滤器来实现。

事件过滤器是一种机制,允许我们在任意widget上监视和过滤所有事件,包括父控件的事件。通过设置事件过滤器,可以将父组件接收到的事件传播到子组件中。

示例代码一,在父控件,子控件分别设置事件过滤器
以下是示例代码:


import sys

from PySide6.QtCore import QEvent
from PySide6.QtGui import Qt, QPainter
from PySide6.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel, QHBoxLayout


class ChildWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName('ChildWidget')
self.setFixedSize(100, 100)
self.label = QLabel("Child")
# 子组件布局
self.child_layout = QVBoxLayout(self)
self.child_layout.addWidget(self.label, 1, alignment=Qt.AlignCenter)

def eventFilter(self, obj, event):
if obj == self and event.type() == QEvent.MouseButtonPress:
print('ChildWidget eventFilter', event)
return super().eventFilter(obj, event)

# 设置子控件颜色
def paintEvent(self, event):
painter = QPainter(self)
painter.fillRect(self.rect(), Qt.red)


class ParentWidget(QWidget):
def __init__(self):
super().__init__()
self.setObjectName('ParentWidget')
self.setAutoFillBackground(True)
self.setFixedSize(200, 200)
# 事件过滤器
self.child = ChildWidget(self)
self.child.installEventFilter(self)
self.installEventFilter(self)
# 父组件布局
self.parent_layout = QHBoxLayout(self)
self.parent_label = QLabel("Parent")
self.parent_layout.addWidget(self.child, 1)
self.parent_layout.addWidget(self.parent_label, 1, alignment=Qt.AlignCenter)

def eventFilter(self, obj, event):
if obj == self and event.type() == QEvent.MouseButtonPress:
print('ParentWidget eventFilter', event)
return self.child.eventFilter(obj, event)


if __name__ == '__main__':
app = QApplication([])
parent = ParentWidget()
parent.show()
sys.exit(app.exec())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
示例代码二,在父控件事件中设置所有子控件的事件


from PySide6.QtCore import Signal, QObject, QEvent
from PySide6.QtWidgets import QWidget, QApplication, QTextEdit, QVBoxLayout, QPushButton, QHBoxLayout


class ChildWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName('ChildWidget')
self.layout = QHBoxLayout(self)
self.child_button = QPushButton("Child")
self.layout.addWidget(self.child_button)

class ParentWidget(QWidget):
def __init__(self):
super().__init__()
self.setObjectName('ParentWidget')
self.layout = QHBoxLayout(self)
# parent_button
self.parent_button = QPushButton("Parent")
self.parent_button.installEventFilter(self)
# ChildWidget
self.child = ChildWidget(self)
self.child.child_button.installEventFilter(self)

self.layout.addWidget(self.child)
self.layout.addWidget(self.parent_button)
self.layout.stretch(2)

def eventFilter(self, obj, event):
if obj == self.child.child_button and event.type() == QEvent.MouseButtonPress:
print('ChildWidget eventFilter', event)
elif obj == self.parent_button and event.type() == QEvent.MouseButtonPress:
print('ParentWidget eventFilter', event)
return super().eventFilter(obj, event)


if __name__ == '__main__':
app = QApplication([])
parent = ParentWidget()
parent.show()
app.exec()
————————————————
版权声明:本文为CSDN博主「KmBase」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_25262697/article/details/129374905