Sophus hat geschrieben:Was meinst du, das importierte Widget (Label und pushButton) wird nicht eingehängt? Die Widgets, die in der ui-Datei sind? Die werden doch mit geladen, sobald man die ui-Datei lädt?
Das ist das, was ich meinte - QuiLoader funktioniert sehr anders als PyQts `uic.loadUi`. Damit die Widgets aus der ui-Datei auch gerendert werden, ist mehr nötig (Bsp. aus PySide Doku):
Code: Alles auswählen
class MyWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
loader = QUiLoader()
file = QFile(":/forms/myform.ui")
file.open(QFile.ReadOnly)
myWidget = loader.load(file, self)
file.close()
layout = QVBoxLayout()
layout.addWidget(myWidget)
self.setLayout(layout)
Wie Du siehst, können mit QUiLoader nur Subwidgets erstellt werden, welche zusätzlich in das Elternwidget eingehängt werden müssen. Das ist für Dich ein Riesenproblem, wenn Du transparent zwischen PyQt und PySide wechseln können möchtest, da Du alle Methoden (signal und slots) umbiegen müsstest.
Mit dem von mir gezeigten Ladecode kannst Du das Problem umgehen, da dieser recht ähnlich zu PyQt tickt. Ich hab diesen nochmal etwas aufgeräumt und ein paar Tests dazu geschrieben:
Code: Alles auswählen
from PyQt4 import QtGui, QtCore
from PyQt4.uic import loadUi, loadUiType
#from PySide import QtGui, QtCore
#from uic_fix import loadUi, loadUiType
FormClass, BaseClass = loadUiType('./test.ui')
class TestBase(QtGui.QWidget):
"""Base test class with one slot"""
def testSlot(self):
print 'button pressed'
class Test1(TestBase):
def __init__(self, parent=None):
TestBase.__init__(self, parent)
assert(loadUi('./test.ui', self) == self)
def testSlot(self):
print 'button pressed'
def test1(app):
print 'TEST 1: applying form class to initializing class with loadUi'
w = Test1()
w.show()
app.exec_()
class Test2(TestBase, FormClass):
def __init__(self, parent=None):
TestBase.__init__(self, parent)
self.setupUi(self)
def testSlot(self):
print 'button pressed'
def test2(app):
print 'TEST 2: multi inheritance with form class'
w = Test2()
w.show()
app.exec_()
class Test3(TestBase):
def __init__(self, parent=None):
TestBase.__init__(self, parent)
self.ui = FormClass()
self.ui.setupUi(self)
def testSlot(self):
print 'button pressed'
def test3(app):
print 'TEST 3: explicit form class instantiation (C++ default way)'
w = Test3()
w.show()
app.exec_()
def test4(app):
print 'TEST 4: late styling with form class'
w = TestBase()
f = FormClass()
f.setupUi(w)
w.show()
app.exec_()
def test5(app):
print 'TEST 5: late styling with loadUi'
w = TestBase()
assert(loadUi('./test.ui', w) == w)
w.show()
app.exec_()
def test6(app):
print 'TEST 6: loadUi without baseinstance'
try:
loadUi('./test.ui')
except AttributeError as e:
assert(e.message == "'QWidget' object has no attribute 'testSlot'")
if __name__ == '__main__':
app = QtGui.QApplication([])
test1(app)
test2(app)
test3(app)
test4(app)
test5(app)
test6(app)
Wie Du siehst, sind die API-SChnittstellen quasi gleich, für den Wechsel zwischen PySide und PyQt musst Du nur die Importe ändern. PySide braucht hierfür zusätzlich die Datei uic_fix.py:
Code: Alles auswählen
import pysideuic
from PySide import QtGui, QtUiTools
import xml.etree.ElementTree as xml
from cStringIO import StringIO
def loadUiType(filename):
"""Load form class from ui file."""
parsed = xml.parse(filename)
widget_classname = parsed.find('widget').get('class')
form_classname = parsed.find('class').text
with open(filename, 'r') as f:
o = StringIO()
frame = {}
pysideuic.compileUi(f, o, indent=0)
pyc = compile(o.getvalue(), '<string>', 'exec')
exec pyc in frame
form_class = frame['Ui_%s'%form_classname]
base_class = getattr(QtGui, widget_classname)
return form_class, base_class
def loadUi(filename, instance=None):
"""Load ui form class onto a given QWidget object."""
form_cls, base_cls = loadUiType(filename)
if not instance:
instance = type(base_cls.__name__, (base_cls,),{})()
cls = instance.__class__
instance.__class__ = cls.__class__(cls.__name__, (cls, form_cls), {})
instance.setupUi(instance)
return instance
Zur Erklärung: PyQt kennt QUiLoader nicht. Das kommt direkt aus Qt und ist als Lademechanismus für Formularteile gedacht, wird allerdings selten genutzt in C++. Auch unterliegt es bestimmten Beschränkung - so ist es nicht möglich, den Formularstyle auf eine bestehendes Objekt anzuwenden. Genau das aber macht PyQt mit `uic.loadUi`.
Zum Nachladen der ui-Dateien nutzt PyQt die Fähigkeit von Python, zur Laufzeit Klassen erstellen, Code nachladen oder gar existierende Objekte modifizieren zu können. Und genau das machen die beiden Funktionen `loadUiType` und `loadUi`:
- `loadUiType` lädt eine ui-Datei, holt sich die Klassennamen des Formulars und der Basisklasse, generiert Pythoncode daraus und führt diesen aus, um an die Formularklasse als Pythontypen zu kommen (mit compile
)
- `loadUi` erweitert die Elternklassen des übergebenen Objektes zur Laufzeit (!) um die Formularklasse und ruft `setupUi` auf, was das Layout anwendet und Signal-/Slotverbindungen aktiviert. Das geht so nicht in C++, weshalb QUiLoader das nicht kann.