首页 > 其他分享 >PySide和PyQt中Signal的工作原理

PySide和PyQt中Signal的工作原理

时间:2023-01-02 15:45:08浏览次数:70  
标签:__ PySide Signal PyQt class instance print Foo signal

PySide和PyQt中Signal的工作原理

背景

PySide和PyQt能够让Qt在Python中运行。Qt的信号槽机制在PySide和PyQt中是这样使用的:

PySide:

from PySide6.QtCore import Signal, Slot, QObject


class Foo(QObject):
    signal = Signal(str)

    def emit_signal(self, message):
        self.signal.emit(message)


class Bar(QObject):
    @Slot(str)
    def slot(self, message):
        print(message)


f = Foo()
b = Bar()
f.signal.connect(b.slot)
f.emit_signal("Hello World!")

# 输出:
# Hello World!

PyQt:

from PyQt6.QtCore import pyqtSignal, pyqtSlot, QObject


class Foo(QObject):
    signal = pyqtSignal(str)

    def emit_signal(self, message):
        self.signal.emit(message)


class Bar(QObject):
    @pyqtSlot(str)
    def slot(self, message):
        print(message)


f = Foo()
b = Bar()
f.signal.connect(b.slot)
f.emit_signal("Hello World!")

# 输出:
# Hello World!

可以发现,信号定义为类的一个静态成员。但是,信号和槽的连接是在不同的对象之间进行的。所以,这到底是怎么实现的?

原理

查阅PyQt的文档,我们可以发现:

A signal (specifically an unbound signal) is a class attribute. When a signal is referenced as an attribute of an instance of the class then PyQt6 automatically binds the instance to the signal in order to create a bound signal. This is the same mechanism that Python itself uses to create bound methods from class functions.

A bound signal has connect(), disconnect() and emit() methods that implement the associated functionality. It also has a signal attribute that is the signature of the signal that would be returned by Qt’s SIGNAL() macro.

其中提到,信号定义为类的属性(类的静态成员),它是一个未绑定信号(unbound signal)。当我们用类的一个对象去访问信号时,该对象会自动绑定到信号,并创建一个绑定信号(bound signal)。我们可以做一个实验:

from PySide6.QtCore import Signal, QObject


class Foo(QObject):
    signal = Signal()


signal = Signal()
print(type(signal))

f = Foo()
print(type(f.signal))

# 输出:
# <class 'PySide6.QtCore.Signal'>
# <class 'PySide6.QtCore.SignalInstance'>

可以发现,单独创建信号时,它的类型是PySide6.QtCore.Signal,而在类中创建并且通过类的实例访问时,类型却是PySide6.QtCore.SignalInstance。说明后者通过了某种方式创建了一个新的对象。

查阅资料发现,这其实是利用了python语言的一种机制:Descriptors

简单来说,就是可以通过重写__get__, __set__, __delete__函数,来实现getter/setter。举例说明:

class MySignalInstance:
    pass


class MySignal:
    def __get__(self, instance, owner=None):
        if (instance == None):
            print('instance is None')
        else:
            print(f'instance is not None. type(instance): {type(instance)}')
        print(owner)
        return MySignalInstance()


class Foo:
    mySignal = MySignal()


mySignal = MySignal()
print(type(mySignal))
f = Foo()
print(type(f.mySignal))
print(type(Foo.mySignal))

# 输出:
# <class '__main__.MySignal'>
# instance is not None. type(instance): <class '__main__.Foo'>
# <class '__main__.Foo'>
# <class '__main__.MySignalInstance'>
# instance is None
# <class '__main__.Foo'>
# <class '__main__.MySignalInstance'>

分析:

  • MySignal中重写了__get__函数,返回MySignalInstance对象
  • 如果直接创建mySignal = MySignal(),返回的就是MySignal的对象
  • 如果在Foo中定义一个MySignal的类成员变量,然后通过两种不同的方式访问:
    • 通过类的对象访问f.mySignal,此时,MySignal__get__函数被调用,instance被设为fowner被设为Foo这意味着,在被调用方得到了调用方的对象。
    • 通过类直接访问Foo.mySignal,此时,MySignal__get__函数同样被调用,因为是通过类直接访问,所以instance被设为Noneowner被设为Foo

正是通过这种机制,使用对象.信号访问信号时,对象被传入信号中,接着将对象信号进行绑定,返回一个绑定信号。然后就可以调用connect和其他对象的槽进行连接。

参考

https://doc.qt.io/qtforpython/tutorials/basictutorial/signals_and_slots.html
https://www.riverbankcomputing.com/static/Docs/PyQt6/signals_slots.html
https://stackoverflow.com/questions/3798835/understanding-get-and-set-and-python-descriptors
https://docs.python.org/3/reference/datamodel.html#implementing-descriptors
https://docs.python.org/3/reference/datamodel.html#invoking-descriptors

标签:__,PySide,Signal,PyQt,class,instance,print,Foo,signal
From: https://www.cnblogs.com/cong-wang/p/17019992.html

相关文章

  • MAC之PyQt5环境
    一,安装模块1,pyqt5pip3installpyqt52,pyqt5-toolspip3installpyqt5-tools二,pyqt5designer的位置安装了pyqt5后就有了:/Users/hz/WorkSpace/Python/Qt/venv/lib/python3.......
  • 使用Python3+PyQT5+Pyserial实现简单的串口工具方法
    练手项目,先上图先实现一个简单的串口工具,为之后的上位机做准备代码如下:github下载地址pyserial_demo.pyimportsysimportserialimportserial.tools......
  • pyqt5-python交互
    安装pyqt5,算是框架,直接在pycharm库里面搜pyqt5就行了安装可视化设计器QtDesigner,这个去官网下载就可以了,不过最好找个中文版的下载。在pycharm上配置插件,好让pych......
  • ReentrantLock Condition await signal 专题
     Condition的执行方式,是当在线程T1中调用await方法后,线程T1将释放锁,并且将自己阻塞,等待唤醒,线程T2获取到锁后,开始做事,完毕后,调用Condition的signal方法,唤醒线程T1,在t2执行......
  • cscctf_2019_qual_signal
    cscctf_2019_qual_signal总结没开pie,got表可写的时候,用magicgadget会有很好的效果多次调用ret2csu的时候注意利用重叠部分缩减payload可以通过read的返回值存储在rax......
  • How to perform signal valve pressure test for New Holland T6030
     WanttoknowHowtoperformsignalvalvepressuretestforNewHollandT6030,T6050,T6070,T6080,T6090tractors.RelatedContents:2022NewHollandElectron......
  • pyqt5图书管理系统--9(最终篇)主函数main和修改密码
    本节为最后两个部分:主函数main和修改密码页面。主要流程:1、主函数默认为登录页面,可以通过主函数的登录页面根据账号特性来登录管理员页面和学生用户主界面。     2......
  • PySide6、PyQt6、ui文件转py文件、Qt Designer 使用
    QT官网:​​https://www.qt.io/zh-cn/develop​​1、PySide6、PyQt6、PyQt5PySide6、PySide2、PyQt5都是基于Qt库,Qt是一组C++库和开发工具,包括图形用户界面、网络、线......
  • Python PyQt5 教程
     PyQt5教程 :http://code.py40.com/face 教程翻译自:​​http://zetcode.com/gui/pyqt5/​​ 【第一节】PyQt5简介:​​http://code.py40.com/1948.html​​​【第二节】P......
  • pyqt5图书管理系统--8、学生页面设计、借阅状态页面设计和所有书籍页面设计
    本节分为三个部分:学生页面、借阅状态页面和所有书籍查询页面。主要流程:1、学生用户主界面,可以使用借阅书籍、归还书籍、查看借阅状态、查看所有书籍信息。     2、......