PyQt的“pyqtProperty”VS python的标准“property”

Dio*_*ogo 2 python properties python-3.x pyqt5

我有几个关于PyQt5 包的类和pythonpyqtProperty标准内置类的问题:property

  1. 为什么要使用pyqtProperty标准属性?下面给出的代码可以两种方式工作。pyqtProperty使用代替 Python 的普通旧式property(用作方法上的装饰器)有什么优势?我知道我必须指定 a 的返回类型pyqtProperty,但为什么我要使用它?

  2. 继承pyqtPropertyproperty? 该属性pyqtProperty.__mro__没有显示(结果是PyQt5.QtCore.pyqtProperty, object)并且我找不到pyqtProperty. PyDoc的命令引用.pyd模块的文件PyQt5.QtCore(不是源代码纯文本文件),并且没有它的存根文件。但该命令help(pyqtProperty)给我的信息与得到的信息非常相似help(property),与官方文档中找到的信息不同。那么,这些信息从何而来?

  3. 为什么在 Qobject 设置时执行@pyqtProperty甚至 用修饰的方法?@property

    回到下面给出的代码:属性方法(在@pyqtProperty和两种情况@property下)在创建属性 之前执行_custom_property,这给出了AttributeError. 但这不应该发生,并且对于@property具有常规 python 类的装饰器来说确实不会发生(我也尝试过,下面有一个片段)。当我使用简单的 getter 和 setter 方法(未修饰)时,这种情况根本不会发生(这是正常的)。最奇怪的现象是:AttributeError当我在调试模式下运行时会发生这种情况。在正常模式 ( python main.py) 下,属性方法无论如何都会执行,但不会引发错误。

我使用的是与 Anaconda Distribution、python 3.8 一起安装的 PyQt5 版本 5.9.2。


这是使用 pyuic5 生成的 Ui_MainWindow.py 文件的内容:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(419, 196)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
        self.lineEdit.setObjectName("lineEdit")
        self.horizontalLayout.addWidget(self.lineEdit)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 419, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.lineEdit.setText(_translate("MainWindow", "test"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))
Run Code Online (Sandbox Code Playgroud)

main.py这是使用装饰器的文件内容property

from Ui_MainWindow import Ui_MainWindow
from PyQt5.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow,Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self._custom_property = 1
    
    @property
    def custom_property(self):
        print("This method is executed")
        return self._custom_property

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()
Run Code Online (Sandbox Code Playgroud)

main.py这是使用装饰器的文件内容pyqtProperty

from Ui_MainWindow import Ui_MainWindow
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import pyqtProperty

class MainWindow(QMainWindow,Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self._custom_property = 1
    
    @pyqtProperty(int)
    def custom_property(self):
        print("This method is executed")
        return self._custom_property

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()
Run Code Online (Sandbox Code Playgroud)

我不会将主文件的内容放在没有装饰器的情况下(只需删除@上面代码中的行)。
但这是一个带有property装饰器的简单文件,显示它没有在__init__方法中执行

class Test:
    
    def __init__(self) -> None:
        pass
    
    @property
    def x(self):
        print("This method is not executed in init")
        return self._x
    
    def setx(self,value):
        self._x = value

if __name__ == "__main__":
    a = Test()
Run Code Online (Sandbox Code Playgroud)

eyl*_*esc 6

  1. pyqtProperty 和 property 之间的区别不是优点)是,这些属性可以通过 Qt 具有的元对象来访问,最后一个工具用于 Qt 中的各种任务,例如在 QML 中公开属性,能够在 QtStyleSheet 中使用,用于 QPropertyAnimation 等。

  2. propertyaspyqtProperty是一个描述符,因此它不一定从属性继承(这里是如何创建描述符而不从属性继承的示例)。如果你想了解 pyqtProperty 的实现,那么你应该检查源代码qpy/QtCore 文件夹中的 qpycore_pyqtproperty.h 和 qpycore_pyqtproperty.cpp 文件。

  3. 方法custom_property调用是由QMetaObject::connectSlotsByName()但我认为这是一个错误引起的。如果方法名称符合特定模式,则此方法尝试建立语义连接:on_<object name>_<signal name>(<signal parameters>)。我指出这是一个错误,因为如果我使用 PySide2 实现相同的逻辑,我不会观察到该行为。