TL; DR :我希望从其PyQt5的loadUiType()
函数直接替换为可与PySide2和Python 3.6+一起使用的uic
模块。
我想将PyQt5应用程序迁移到PySide2。我使用的一种常见模式是,我在Qt Designer中创建用户界面设计,并动态地将生成的.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_()
这意味着我可以在命令行上放弃将
.ui
设计编译为.py
Python模块。更重要的是,使用混合模式,我可以通过导入小部件范围内的self.name
访问设计中定义的所有Qt小部件,其中name
在Qt Designer中进行了分配。为了提供可重现的示例,以下是与上述Python代码一起使用的最小Qt设计文件,在其中将其称为
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>
我想用PySide2来完成相同的工作,并且代码更改最少。问题是,PySide2没有提供与PyQt5的
uic.loadUiType()
函数等效的功能,而returns the design's form class重要地用作混合功能。有一个相关的问题"PyQt5 to PySide2, loading UI-Files in different classes",但前提是加载的对象可以在单独的类中使用,这本身并不是我关心的问题。另外,(当前)唯一的答案不是我正在寻找的解决方案。其他问题及其答案(1,2)确定可以通过
QtUiTools.QUiLoader().load('design.ui')
将设计文件动态加载到PySide2中,但该方法将返回小部件对象,而不是所需的表单类。后一种方法在不混合导入类的情况下,将需要我更改许多代码行以进行迁移,因为这会导致Python实例变量的对象层次结构不同。在上面的示例中,然后在整个代码库中必须将
self.label
重命名为类似self.ui.label
的名称。我想要的是从与PySide2和Python 3.6+一起使用的
loadUiType(design)
模块直接替代PyQt5的uic
函数,其中design
指定.ui
文件的路径。2013年的This answer完美地演示了这一点,但对于PySide(基于Qt4)和(旧版)Python 2来说,如何使该代码适应在(现代)Python上运行的PySide2(基于Qt5)?
最佳答案
以下是对上述引用的较早答案中提供的解决方案的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)
如果将其另存为与主要Python模块一起的uic.py
,则只需更改import
语句即可将问题中的示例从PyQt5迁移到PySide2:from PySide2 import QtWidgets
import uic
在Windows 10(通过pip install pyside2
安装)和Manjaro Linux 18.0.4(通过pacman
-packages pyside2
和pyside2-tools
)上使用Python 3.7.3和PySide2 5.12.3进行了测试。注意:自5.14.0版本(2019年12月)起,以上解决方案中使用的
pyside2uic
模块已从PySide2代码库中删除。但是,然后在PySide2问题跟踪器上向“bring back loadUiType”提交了一个请求。从版本5.14.2.2(到2020年5月)开始,可以从loadUiType
模块导入QtUiTools
,就像在PyQt5中一样工作。这使问题中提出的问题过时了。