diff --git a/Qt.py b/Qt.py index 922838b5..15fc12f5 100644 --- a/Qt.py +++ b/Qt.py @@ -41,9 +41,10 @@ import sys import types import shutil +import importlib -__version__ = "1.2.0.b2" +__version__ = "1.2.0.b3" # Enable support for `from Qt import *` __all__ = [] @@ -851,6 +852,41 @@ class _UiLoader(Qt._QtUiTools.QUiLoader): def __init__(self, baseinstance): super(_UiLoader, self).__init__(baseinstance) self.baseinstance = baseinstance + self.custom_widgets = {} + + def _loadCustomWidgets(self, etree): + """ + Workaround to pyside-77 bug. + + From QUiLoader doc we should use registerCustomWidget method. + But this causes a segfault on some platforms. + + Instead we fetch from customwidgets DOM node the python class + objects. Then we can directly use them in createWidget method. + """ + + def headerToModule(header): + """ + Translate a header file to python module path + foo/bar.h => foo.bar + """ + # Remove header extension + module = os.path.splitext(header)[0] + + # Replace os separator by python module separator + return module.replace("/", ".").replace("\\", ".") + + custom_widgets = etree.find("customwidgets") + + if custom_widgets is None: + return + + for custom_widget in custom_widgets: + class_name = custom_widget.find("class").text + header = custom_widget.find("header").text + module = importlib.import_module(headerToModule(header)) + self.custom_widgets[class_name] = getattr(module, + class_name) def load(self, uifile, *args, **kwargs): from xml.etree.ElementTree import ElementTree @@ -860,6 +896,7 @@ def load(self, uifile, *args, **kwargs): # a RuntimeError. etree = ElementTree() etree.parse(uifile) + self._loadCustomWidgets(etree) widget = Qt._QtUiTools.QUiLoader.load( self, uifile, *args, **kwargs) @@ -889,7 +926,8 @@ def createWidget(self, class_name, parent=None, name=""): class_name, parent, name) - + elif class_name in self.custom_widgets: + widget = self.custom_widgets[class_name](parent) else: raise Exception("Custom widget '%s' not supported" % class_name) diff --git a/tests.py b/tests.py index 8ad68be9..26458645 100644 --- a/tests.py +++ b/tests.py @@ -51,6 +51,18 @@ def captured_output(): sys.stdout, sys.stderr = old_out, old_err +def CustomWidget(parent=None): + """ + Wrap CustomWidget class into a function to avoid global Qt import + """ + from Qt import QtWidgets + + class Widget(QtWidgets.QWidget): + pass + + return Widget(parent) + + self = sys.modules[__name__] @@ -197,6 +209,38 @@ def captured_output(): """ +qcustomwidget_ui = u"""\ + + + MainWindow + + + + 0 + 0 + 238 + 44 + + + + MainWindow + + + + + + + CustomWidget + QWidget +
tests.h
+
+
+ + +
+""" + + def setup(): """Module-wide initialisation @@ -216,6 +260,8 @@ def saveUiFile(filename, ui_template): self.ui_qmainwindow = saveUiFile("qmainwindow.ui", qmainwindow_ui) self.ui_qdialog = saveUiFile("qdialog.ui", qdialog_ui) self.ui_qdockwidget = saveUiFile("qdockwidget.ui", qdockwidget_ui) + self.ui_qcustomwidget = saveUiFile("qcustomwidget.ui", qcustomwidget_ui) + def teardown(): shutil.rmtree(self.tempdir) @@ -350,6 +396,26 @@ def test_load_ui_dockwidget(): app.exit() +def test_load_ui_customwidget(): + """Tests to see if loadUi loads a custom widget properly""" + import sys + from Qt import QtWidgets, QtCompat + + app = QtWidgets.QApplication(sys.argv) + win = QtWidgets.QMainWindow() + + QtCompat.loadUi(self.ui_qcustomwidget, win) + + # Ensure that the derived class was properly created + # and not the base class (in case of failure) + custom_class_name = getattr(win, "customwidget", None).__class__.__name__ + excepted_class_name = CustomWidget(win).__class__.__name__ + assert custom_class_name == excepted_class_name, \ + "loadUi could not load custom widget to main window" + + app.exit() + + def test_load_ui_invalidpath(): """Tests to see if loadUi successfully fails on invalid paths""" import sys