Reputation: 1310
For now I'm loading them like this:
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_())
But I think it's uncomfortably, is there any other way, probably to load whole namespace or whatever?
Upvotes: 6
Views: 3900
Reputation: 1
Using the solution above works, however, when I implement this for a QDialog with PySide6 on macOS (I haven't tested it on other platforms) the window always shows up on the top-left of my screen, instead of centered above the parent.
Instead of just returning the baseinstance
when the parent is None, creating a widget with the baseinstance
as parent worked for me.
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
Upvotes: 0
Reputation: 120768
UPDATE:
The original solution below was written for PySide (Qt4). It still works with both PySide2 (Qt5) and PySide6 (Qt6), but with a couple of provisos:
loadUi
is called.(In addition, it should be mentioned that PySide2 and PySide6 now have a loadUiType function, which at first glance seems to provide a much simpler solution. However, the current implementation has the major drawback of requiring the Qt uic tool to be installed on the system and executable from the user's PATH. This certainly isn't always guaranteed to be the case, so it's debateable whether it's suitable for use in a production environment).
Below is an updated demo that illustrates the two features noted above:
test.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>
Original Solution:
At the moment, the PySide QUiLoader class doesn't have a convenient way to load widgets into to an instance of the top-level class like the PyQt uic module has.
However, it's fairly easy to add something equivalent:
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
Which could then used like this:
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)])
For this to work properly, the baseinstance
argument of loadUi
has to be an instance of the top-level class from Qt Designer file. All the other widgets will then be added to it as instance attributes.
Upvotes: 11