python - 如何从 PySide 中的 .ui 文件加载子项?

标签 python user-interface pyside qt-designer uic

现在我像这样加载它们:

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),但有几个附带条件:

(此外,应该提到 PySide2 和 PySide6 现在有一个 loadUiType 函数,乍一看似乎提供了一个更简单的解决方案。但是,当前的实现有一个主要缺点,即需要 Qt uic tool安装在系统上并可从用户的 PATH 执行。这当然不能保证总是如此,因此它是否适合在生产环境中使用是有争议的)。

下面是一个更新的演示,说明了上述两个功能:

screenshot

测试.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 class没有方便的方法将小部件加载到顶级类的实例中,例如 PyQt uic module有。

但是,添加等价的东西相当容易:

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)])

为了使其正常工作,loadUibaseinstance 参数必须是 Qt Designer 文件中顶级类的实例。然后所有其他小部件将作为实例属性添加到它。

关于python - 如何从 PySide 中的 .ui 文件加载子项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27603350/

相关文章:

user-interface - 触摸屏系统指南

python - 如何阻止 PaintEvent 绘制子控件?

c++ - PyQt 与 PySide 比较

python - 如何修复这个 OOP 错误?

python - 递归函数名称错误

java - 如何将多个文本字段添加到数组列表 JavaFX

java - 如何让SmartGWT标签的内容不删减空格?

python - 在 PySide 中使用 QtHostinfo.lookupHost

Python 多处理 Numpy 随机

python - pandas DataFrame - 如何对行进行分组和标记