[英]PySide2 equivalent of PyQt5's loadUiType() to dynamically mix in UI designs
TL;DR : I want a drop-in replacement for PyQt5's loadUiType()
function from its uic
module that works with PySide2 and Python 3.6+. TL; DR :我想从它的
uic
模块中替换 PyQt5 的loadUiType()
函数,该模块可与 PySide2 和 Python 3.6+ 一起使用。
I want to migrate a PyQt5 application to PySide2.我想将 PyQt5 应用程序迁移到 PySide2。 A common pattern I use is, I create the user-interface design in Qt Designer and dynamically load the resulting
.ui
file as a mix-in class extending a Qt widget in the Python code, such as the main window itself:我使用的一个常见模式是,我在 Qt 设计器中创建用户界面设计并动态加载生成的
.ui
文件作为混合类扩展 Python 代码中的 Qt 小部件,例如主窗口本身:
from PyQt5 import QtWidgets, uic
class Window(QtWidgets.QMainWindow, uic.loadUiType('design.ui')[0]):
def __init__(self):
super().__init__()
self.setupUi(self)
print(self.label.text())
app = QtWidgets.QApplication([])
window = Window()
window.show()
app.exec_()
This means I can forgo compiling the .ui
design to a .py
Python module on the command line.这意味着我可以放弃在命令行上将
.ui
设计编译为.py
Python 模块。 More importantly, the mix-in pattern lets me access all Qt widgets defined in the design via self.name
within the scope of the importing widgets, where name
is assigned as such within Qt Designer.更重要的是,混合模式让我可以在导入小部件的范围内通过
self.name
访问设计中定义的所有 Qt 小部件,其中name
在 Qt 设计器中分配。
For the sake of providing a reproducible example, here is a minimal Qt design file to go along with the above Python code, in which it is referenced as design.ui
:为了提供可重现的示例,这里有一个最小的 Qt 设计文件与上述 Python 代码一起使用,其中引用为
design.ui
:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<widget class="QWidget" name="centralwidget">
<widget class="QLabel" name="label">
<property name="text">
<string>Hi, Qt.</string>
</property>
</widget>
</widget>
</widget>
</ui>
I would like to accomplish the same, but with PySide2, and with the fewest code changes possible.我想完成同样的事情,但使用 PySide2,并且代码更改尽可能少。 The problem is, PySide2 does not provide an equivalent of PyQt5's
uic.loadUiType()
function, which, importantly, returns the design's form class to be used as the mix-in.问题是,PySide2 没有提供 PyQt5 的
uic.loadUiType()
函数的等价物,重要的是,它返回设计的表单类以用作混合。
There is a related question, "PyQt5 to PySide2, loading UI-Files in different classes" , but its premise is that the loaded objects be usable from a separate class, which is not my concern per se.有一个相关的问题, “PyQt5 到 PySide2,在不同的类中加载 UI 文件” ,但它的前提是加载的对象可以从单独的类中使用,这本身不是我关心的问题。 Plus, the (currently) only answer to it is not the solution I am looking for.
另外,(目前)唯一的答案不是我正在寻找的解决方案。 Other questions and their answers ( 1 , 2 ) establish that design files can be dynamically loaded in PySide2, via
QtUiTools.QUiLoader().load('design.ui')
, but that method returns the widget object, not the required form class.其他问题及其答案( 1 , 2 )确定设计文件可以通过
QtUiTools.QUiLoader().load('design.ui')
在 PySide2 中动态加载,但该方法返回小部件对象,而不是所需的表单类.
The latter approach, without mixing in the imported class, would require me to change many lines of code for the migration, as it results in a different object hierarchy of the Python instance variables.后一种方法没有在导入的类中混合,需要我为迁移更改多行代码,因为它会导致 Python 实例变量的对象层次结构不同。 In the above example,
self.label
would then have to be renamed to something like self.ui.label
throughout the code base.在上面的例子中,
self.label
必须在整个代码库中重命名为self.ui.label
之类的东西。
What I want is a drop-in replacement for PyQt5's loadUiType(design)
function from its uic
module that works with PySide2 and Python 3.6+, wherein design
designates the path to a .ui
file.我想要的是 PyQt5 的
loadUiType(design)
函数的替代品,它的uic
模块可与 PySide2 和 Python 3.6+ 一起使用,其中design
指定了.ui
文件的路径。
This answer , from 2013, perfectly demonstrates that, but for PySide (based on Qt4) and (legacy) Python 2. How do I adapt that code to PySide2 (based on Qt5) running on (modern) Python?这个来自 2013 年的答案完美地证明了这一点,但是对于 PySide(基于 Qt4)和(旧版)Python 2。我如何将该代码改编为在(现代)Python 上运行的 PySide2(基于 Qt5)?
The following is an adaptation for Python 3.6 or newer and PySide2 5.13 or older (see note at the end) of the solution presented in the above-cited, earlier answer:以下是对 Python 3.6 或更高版本和 PySide2 5.13 或更低版本(见最后的注释)的上述引用的早期答案中提供的解决方案的改编:
from PySide2 import QtWidgets
from pyside2uic import compileUi
from xml.etree import ElementTree
from io import StringIO
def loadUiType(design):
"""
PySide2 equivalent of PyQt5's `uic.loadUiType()` function.
Compiles the given `.ui` design file in-memory and executes the
resulting Python code. Returns form and base class.
"""
parsed_xml = ElementTree.parse(design)
widget_class = parsed_xml.find('widget').get('class')
form_class = parsed_xml.find('class').text
with open(design) as input:
output = StringIO()
compileUi(input, output, indent=0)
source_code = output.getvalue()
syntax_tree = compile(source_code, filename='<string>', mode='exec')
scope = {}
exec(syntax_tree, scope)
form_class = scope[f'Ui_{form_class}']
base_class = eval(f'QtWidgets.{widget_class}')
return (form_class, base_class)
If saved as uic.py
alongside the main Python module, only the import
statements have to be changed in order to migrate the example in the question from PyQt5 to PySide2:如果与主 Python 模块一起保存为
uic.py
,则只需更改import
语句即可将问题中的示例从 PyQt5 迁移到 PySide2:
from PySide2 import QtWidgets
import uic
Tested with Python 3.7.3 and PySide2 5.12.3 on Windows 10 (installed via pip install pyside2
) and Manjaro Linux 18.0.4 (via the pacman
-packages pyside2
and pyside2-tools
).在 Windows 10(通过
pip install pyside2
)和 Manjaro Linux 18.0.4(通过pacman
pyside2
和pyside2-tools
)上使用 Python 3.7.3 和 PySide2 5.12.3 进行测试。
Note : The pyside2uic
module used in the above solution was removed from the PySide2 code base as of version 5.14.0 (December 2019).注意:从 5.14.0 版(2019 年 12 月)起,上述解决方案中使用的
pyside2uic
模块已从 PySide2 代码库中删除。 However, a request was then filed on the PySide2 issue tracker to " bring back loadUiType ".但是,随后在 PySide2 问题跟踪器上提交了“恢复 loadUiType ”的请求。 As of version 5.14.2.2 (May 2020),
loadUiType
can be imported from the QtUiTools
module and works just like it does in PyQt5.由于5.14.2.2版(2020日),
loadUiType
可以从进口QtUiTools
模块的工作原理就像它在PyQt5。 This renders the problem presented in the question obsolete.这使得问题中提出的问题已过时。
PySide2 brought back loadUiType in May 2020 . PySide2在 2020 年 5 月带回了 loadUiType 。 So if you upgrade, you can get a drop-in replacement.
因此,如果您升级,您可以获得直接替换。 The only difference is the import:
唯一的区别是导入:
from PySide2.QtUiTools import loadUiType
Syntax is the same (you will use loadUiType(<file>)[0]
)语法相同(您将使用
loadUiType(<file>)[0]
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.