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

说明

在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')

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()

在上面的示例中,我们定义了一个 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)

在上面的示例代码中,创建了一个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())

示例代码二,在父控件事件中设置所有子控件的事件

在这里插入图片描述

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()
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KmBase

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值