I have a few questions regarding the pyqtProperty class of PyQt5 package, and the standard built-in property class of python:
Why use pyqtProperty over standard property? The codes given below works in both ways. What's the advantage of using pyqtProperty instead of Python's plain old property (used as a decorator on a method)? I know that I must specify the return type of a pyqtProperty, but why would I want to use it?
Does pyqtProperty inherits from property? The attribute pyqtProperty.__mro__ does not show that (the result is PyQt5.QtCore.pyqtProperty, object) and I can't find a source code file of pyqtProperty. PyDoc's command refers to a .pyd file of the module PyQt5.QtCore (which is not a source code plain text file), and there is no stub file for it. But the command help(pyqtProperty) gives me information very similar to the one get with help(property), different from the one found on the official documentaion. So, where that information comes from?
Why the methods decorated with @pyqtProperty or even with @property are executed when the Qobject is set up?
Back to the codes given below: the property methods (in the both cases @pyqtProperty and @property) are executed before creating the attribute _custom_property, which gives a AttributeError. But that should not happen, and really does not happen with @property decorator with regular python classes (I've tried that too, there's a snipet below). When I use simple getter and setter methods (not decorated), that doesn't happen at all (which is normal). The strangest phenomenon is: this AttributeError occurs when I run in debug mode. In normal mode (python main.py) the property methods get executed anyway, but the error is not raised.
I'm using PyQt5 version 5.9.2 installed with Anaconda Distribution, python 3.8.
This is the contents of Ui_MainWindow.py file, generated with pyuic5:
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"))
This is the content of the main.py file using property decorator:
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_()
This is the content of the main.py file using pyqtProperty decorator:
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_()
I won't put the content of the main file with no decorator (just remove the @ lines in the code above).
But this is a simple file with property decorator showing it is not executed in the __init__ method
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()
The difference between pyqtProperty and property, not an advantage, is that these properties will be accessible through the metaObject that Qt has, and this last tool is used for various tasks in Qt such as exposing properties in QML, being able to be used in QtStyleSheet, being used in QPropertyAnimation, etc.
property as pyqtProperty is a descriptor so it should not necessarily inherit from property (here is an example of how to create descriptors without inheriting from property). If you want to know the implementation of pyqtProperty then you should check the qpycore_pyqtproperty.h and qpycore_pyqtproperty.cpp file in the qpy/QtCore folder of the source code.
The custom_property method call is caused by QMetaObject::connectSlotsByName() but I think it is a bug. This method tries to make the semantic connection if the name of the method meets a certain pattern: on_<object name>_<signal name>(<signal parameters>). I point out that it is a bug since if I implement the same logic with PySide2 I do not observe that behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With