现在我像这样加载它们:
if __name__ == '__main__':
app = QApplication(sys.argv)
loader = QUiLoader()
file = QFile('main.ui')
file.open(QFile.ReadOnly)
window = loader.load(file)
file.close()
window.show()
# Here:
window.centralwidget.findChild(QListWidget, 'listWidget').addItems(['Item {0}'.format(x) for x in range(100)])
sys.exit(app.exec_())
但我认为这很不舒服,有没有其他方法,可能加载整个命名空间或其他什么?
更新:
下面的原始解决方案是为 PySide (Qt4) 编写的。它仍然适用于 PySide2 (Qt5) 和 PySide6 (Qt6),但有一些附带条件:
loadUi
之前,必须使用 registerCustomWidget显式导入并注册所需的类。
(此外,应该提到的是,PySide2 和 PySide6 现在有一个 loadUiType 函数,乍一看似乎提供了一个更简单的解决方案。但是,当前实现的主要缺点是需要 Qt uic 工具要安装在系统上并可以从用户的 PATH 执行,这当然不能保证总是如此,因此它是否适合在生产环境中使用是有争议的)。
下面是一个更新的演示,说明了上面提到的两个功能:
测试.py:
from PySide2 import QtWidgets, QtCore, QtUiTools
# from PySide6 import QtWidgets, QtCore, QtUiTools
class UiLoader(QtUiTools.QUiLoader):
_baseinstance = None
def createWidget(self, classname, parent=None, name=''):
if parent is None and self._baseinstance is not None:
widget = self._baseinstance
else:
widget = super().createWidget(classname, parent, name)
if self._baseinstance is not None:
setattr(self._baseinstance, name, widget)
return widget
def loadUi(self, uifile, baseinstance=None):
self._baseinstance = baseinstance
widget = self.load(uifile)
QtCore.QMetaObject.connectSlotsByName(baseinstance)
return widget
class MyLabel(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setStyleSheet('background: plum')
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
loader = UiLoader()
loader.registerCustomWidget(MyLabel)
loader.loadUi('main.ui', self)
@QtCore.Slot()
def on_testButton_clicked(self):
self.customLabel.setText(
'' if self.customLabel.text() else 'Hello World')
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
window = MainWindow()
window.show()
try:
app.exec()
except AttributeError:
app.exec_()
main.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>213</width>
<height>153</height>
</rect>
</property>
<property name="windowTitle">
<string>LoadUi Test</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="MyLabel" name="customLabel">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="testButton">
<property name="text">
<string>Click Me</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>MyLabel</class>
<extends>QLabel</extends>
<header>test</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
原始解决方案:
目前,PySide QUiLoader 类 没有像 PyQt uic 模块 那样将小部件加载到顶级类的实例中的便捷方法。
但是,添加等效内容相当容易:
from PySide import QtGui, QtCore, QtUiTools
class UiLoader(QtUiTools.QUiLoader):
_baseinstance = None
def createWidget(self, classname, parent=None, name=''):
if parent is None and self._baseinstance is not None:
widget = self._baseinstance
else:
widget = super(UiLoader, self).createWidget(classname, parent, name)
if self._baseinstance is not None:
setattr(self._baseinstance, name, widget)
return widget
def loadUi(self, uifile, baseinstance=None):
self._baseinstance = baseinstance
widget = self.load(uifile)
QtCore.QMetaObject.connectSlotsByName(widget)
return widget
然后可以像这样使用:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(self, parent)
UiLoader().loadUi('main.ui', self)
self.listWidget.addItems(['Item {0}'.format(x) for x in range(100)])
为了使其正常工作,
baseinstance
的 loadUi
参数必须是 Qt Designer 文件中顶级类的实例。然后,所有其他小部件将作为实例属性添加到其中。
使用上面的解决方案是有效的,但是当我在 MacO 上使用 PySide6 为 QDialog 实现此方法时(我没有在其他平台上测试过它),窗口始终显示在屏幕的左上角,而不是在父级上方居中.
当父级为 None 时,不只是返回
baseinstance
,而是使用 baseinstance
作为父级创建一个小部件,这对我有用。
class UiLoader(QUiLoader):
_baseinstance = None
def createWidget(self, classname, parent=None, name=''):
if parent is None and self._baseinstance is not None:
widget = super().createWidget(classname, self._baseinstance, name)
else:
widget = super().createWidget(classname, parent, name)
if self._baseinstance is not None:
setattr(self._baseinstance, name, widget)
return widget
def loadUi(self, uifile, baseinstance=None):
self._baseinstance = baseinstance
widget = self.load(uifile)
QMetaObject.connectSlotsByName(baseinstance)
return widget