Piatus w=10=wt BC w=12=wt 标准 w=11=wt
PyQt's "pyqtProperty" VS python's standard "property"
我有几个关于 PyQt5 包的 pyqtProperty
class 和 python 的标准内置 property
class 的问题:
为什么要使用 pyqtProperty
而不是标准的 属性?下面给出的代码以两种方式工作。使用 pyqtProperty
而不是 Python 的普通旧 property
(用作方法的装饰器)有什么好处?我知道我必须指定 pyqtProperty
的 return 类型,但我为什么要使用它?
pyqtProperty
是否继承自 property
?属性 pyqtProperty.__mro__
没有显示(结果是 PyQt5.QtCore.pyqtProperty, object
),我找不到 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。那么,这些信息从何而来?
为什么@pyqtProperty
甚至@property
修饰的方法在设置Qobject时执行?
回到下面给出的代码:属性 方法(在 @pyqtProperty
和 @property
两种情况下)在创建属性 _custom_property
之前执行,它给出一个AttributeError
。但这不应该发生,并且 @property
装饰器和常规 python classes 确实不会发生(我也试过了,下面有一个片段)。当我使用简单的 getter 和 setter 方法(未修饰)时,根本不会发生这种情况(这是正常的)。最奇怪的现象是:我在调试模式下运行时出现这个AttributeError
。在正常模式下 (python main.py
) 属性 方法无论如何都会执行,但不会引发错误。
我使用的是 PyQt5 版本 5.9.2,安装了 Anaconda Distribution,python 3.8.
这是Ui_MainWindow.py文件的内容,用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"))
这是使用 property
装饰器的 main.py
文件的内容:
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_()
这是使用 pyqtProperty
装饰器的 main.py
文件的内容:
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_()
我不会把没有装饰器的主文件的内容放在上面(只需删除上面代码中的 @
行)。
但这是一个带有 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()
pyqt属性和属性的区别,不是优势,就是这些属性会可以通过 Qt 拥有的元对象访问,最后一个工具用于 Qt 中的各种任务,例如在 QML 中公开属性,能够在 QtStyleSheet 中使用,在 QPropertyAnimation 中使用等。
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.
custom_property
方法调用是由 QMetaObject::connectSlotsByName()
引起的,但我认为这是一个错误。如果方法名称满足特定模式:on_<object name>_<signal name>(<signal parameters>)
,则此方法会尝试建立语义连接。我指出这是一个错误,因为如果我使用 PySide2 实现相同的逻辑,我不会观察到该行为。
eyllanesc 的回答足够完整,但我想通过解释一些实际需要使用 pyqtProperty
而使用标准 python 属性是不够的情况来扩展它:
Qt 样式表
QSS 提供对 Qt 属性的访问:
- 对于selectors,可以定义一个属性是否匹配指定值的规则;例如,您可以更改所有标记为
Ok
或 Cancel
: 的按钮的颜色
QPushButton[text="Ok"] {
color: green;
}
QPushButton[text="Cancel"] {
color: red;
}
请注意,更改定义外观的 属性 的值时小部件已经显示(通过样式“抛光”)需要再次重新计算样式 sheet。在上述情况下,如果将按钮的文本从“确定”更改为“取消”,您将看不到文本颜色有任何变化。为了实现这一点,需要通过obj.setStyleSheet(obj.styleSheet())
重新设置样式sheet,这可以在小部件本身上完成 或 任何设置了样式 sheet 的父项(包括 QApplication)。
最简单的方法是在 属性 的 setter 中执行 self.setStyleSheet(self.styleSheet())
(可能,在检查值是否实际更改之后),即使小部件具有 no 样式 sheet 设置并且规则在其任何祖先上定义。
- 可以设置属性,只要它们的类型被 QSS 支持:
#Window {
qproperty-minimumSize: 300px 300px;
qproperty-geometry: rect(250 250 400 400);
}
请注意,正如 documentation 所解释的那样,“q属性 语法仅被评估一次,即当小部件被样式抛光时”,因此您不能使用伪设置属性-状态(如 :hover
状态),因为它们可能会被忽略或提供意外和不可靠的行为。
项目委托编辑者
默认情况下,项目委托使用 user property.
设置编辑器的值
class MyCustomEditor(QtWidgets.QWidget):
# ...
QtCore.pyqtProperty(object, user=True)
def myCoolProperty(self):
# ...
这意味着如果你有一个自定义编辑器并使用一个声明了 user 标志的 Qt 属性,你只需要实现 createEditor()
return 自定义编辑器(甚至可以在自定义 QItemEditorFactory 中为特定数据类型定义编辑器),而 setEditorData()
将自动设置其值,而无需担心索引、数据类型或属性 姓名。
设计器插件
这更像是一个边缘场景,因为很少需要为设计器创建插件,而且不是一件容易的事(PyQt 文档很少而且大部分已经过时,一些未实现为 C++ 提供的特性和功能)。但是,如果您确实需要它,在使用插件创建的自定义小部件的 属性 编辑器框中显示自定义属性的唯一方法是使用 Qt 属性。
我有几个关于 PyQt5 包的 pyqtProperty
class 和 python 的标准内置 property
class 的问题:
为什么要使用
pyqtProperty
而不是标准的 属性?下面给出的代码以两种方式工作。使用pyqtProperty
而不是 Python 的普通旧property
(用作方法的装饰器)有什么好处?我知道我必须指定pyqtProperty
的 return 类型,但我为什么要使用它?pyqtProperty
是否继承自property
?属性pyqtProperty.__mro__
没有显示(结果是PyQt5.QtCore.pyqtProperty, object
),我找不到pyqtProperty
的源代码文件。 PyDoc's command refers to a.pyd
file of the modulePyQt5.QtCore
(which is not a source code plain text file), and there is no stub file for it. But the commandhelp(pyqtProperty)
gives me information very similar to the one get withhelp(property)
, different from the one found on the official documentaion。那么,这些信息从何而来?为什么
@pyqtProperty
甚至@property
修饰的方法在设置Qobject时执行?回到下面给出的代码:属性 方法(在
@pyqtProperty
和@property
两种情况下)在创建属性_custom_property
之前执行,它给出一个AttributeError
。但这不应该发生,并且@property
装饰器和常规 python classes 确实不会发生(我也试过了,下面有一个片段)。当我使用简单的 getter 和 setter 方法(未修饰)时,根本不会发生这种情况(这是正常的)。最奇怪的现象是:我在调试模式下运行时出现这个AttributeError
。在正常模式下 (python main.py
) 属性 方法无论如何都会执行,但不会引发错误。
我使用的是 PyQt5 版本 5.9.2,安装了 Anaconda Distribution,python 3.8.
这是Ui_MainWindow.py文件的内容,用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"))
这是使用 property
装饰器的 main.py
文件的内容:
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_()
这是使用 pyqtProperty
装饰器的 main.py
文件的内容:
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_()
我不会把没有装饰器的主文件的内容放在上面(只需删除上面代码中的 @
行)。
但这是一个带有 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()
pyqt属性和属性的区别,不是优势,就是这些属性会可以通过 Qt 拥有的元对象访问,最后一个工具用于 Qt 中的各种任务,例如在 QML 中公开属性,能够在 QtStyleSheet 中使用,在 QPropertyAnimation 中使用等。
property
aspyqtProperty
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.custom_property
方法调用是由QMetaObject::connectSlotsByName()
引起的,但我认为这是一个错误。如果方法名称满足特定模式:on_<object name>_<signal name>(<signal parameters>)
,则此方法会尝试建立语义连接。我指出这是一个错误,因为如果我使用 PySide2 实现相同的逻辑,我不会观察到该行为。
eyllanesc 的回答足够完整,但我想通过解释一些实际需要使用 pyqtProperty
而使用标准 python 属性是不够的情况来扩展它:
Qt 样式表
QSS 提供对 Qt 属性的访问:
- 对于selectors,可以定义一个属性是否匹配指定值的规则;例如,您可以更改所有标记为
Ok
或Cancel
: 的按钮的颜色
QPushButton[text="Ok"] {
color: green;
}
QPushButton[text="Cancel"] {
color: red;
}
请注意,更改定义外观的 属性 的值时小部件已经显示(通过样式“抛光”)需要再次重新计算样式 sheet。在上述情况下,如果将按钮的文本从“确定”更改为“取消”,您将看不到文本颜色有任何变化。为了实现这一点,需要通过obj.setStyleSheet(obj.styleSheet())
重新设置样式sheet,这可以在小部件本身上完成 或 任何设置了样式 sheet 的父项(包括 QApplication)。
最简单的方法是在 属性 的 setter 中执行 self.setStyleSheet(self.styleSheet())
(可能,在检查值是否实际更改之后),即使小部件具有 no 样式 sheet 设置并且规则在其任何祖先上定义。
- 可以设置属性,只要它们的类型被 QSS 支持:
#Window {
qproperty-minimumSize: 300px 300px;
qproperty-geometry: rect(250 250 400 400);
}
请注意,正如 documentation 所解释的那样,“q属性 语法仅被评估一次,即当小部件被样式抛光时”,因此您不能使用伪设置属性-状态(如 :hover
状态),因为它们可能会被忽略或提供意外和不可靠的行为。
项目委托编辑者
默认情况下,项目委托使用 user property.
设置编辑器的值class MyCustomEditor(QtWidgets.QWidget):
# ...
QtCore.pyqtProperty(object, user=True)
def myCoolProperty(self):
# ...
这意味着如果你有一个自定义编辑器并使用一个声明了 user 标志的 Qt 属性,你只需要实现 createEditor()
return 自定义编辑器(甚至可以在自定义 QItemEditorFactory 中为特定数据类型定义编辑器),而 setEditorData()
将自动设置其值,而无需担心索引、数据类型或属性 姓名。
设计器插件
这更像是一个边缘场景,因为很少需要为设计器创建插件,而且不是一件容易的事(PyQt 文档很少而且大部分已经过时,一些未实现为 C++ 提供的特性和功能)。但是,如果您确实需要它,在使用插件创建的自定义小部件的 属性 编辑器框中显示自定义属性的唯一方法是使用 Qt 属性。