diff --git a/Qt.py b/Qt.py
index 8ee4e9e4..a9f90637 100644
--- a/Qt.py
+++ b/Qt.py
@@ -874,23 +874,6 @@ def get_arg(index):
return app.translate(*sanitized_args)
-def _headerToModule(header):
- """
- Translate a header file to python module path
- foo/bar.h => foo.bar
- """
-
- # Only manipulate header files, identified by the `.h` ext.
- if header.endswith(".h") is False:
- return header
-
- # Remove header extension
- module = os.path.splitext(header)[0]
-
- # Replace os separator by python module separator
- return module.replace("/", ".").replace("\\", ".")
-
-
def _loadUi(uifile, baseinstance=None):
"""Dynamically load a user interface from the given `uifile`
@@ -942,6 +925,17 @@ def _loadCustomWidgets(self, etree):
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:
@@ -950,7 +944,14 @@ def _loadCustomWidgets(self, etree):
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))
+
+ try:
+ # try to import the module using the header as defined by the user
+ module = importlib.import_module(header)
+ except ImportError:
+ # try again, but use the customized conversion of a path to a module
+ module = importlib.import_module(headerToModule(header))
+
self.custom_widgets[class_name] = getattr(module,
class_name)
diff --git a/tests.py b/tests.py
index d7798039..d1a8fbdb 100644
--- a/tests.py
+++ b/tests.py
@@ -265,7 +265,7 @@ class Widget(QtWidgets.QWidget):
CustomWidget
QWidget
-
+ custom.customwidget.customwidget
@@ -273,6 +273,18 @@ class Widget(QtWidgets.QWidget):
"""
+python_custom_widget = '''
+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)
+'''
def setup():
"""Module-wide initialisation
@@ -297,7 +309,8 @@ def saveUiFile(filename, ui_template):
self.ui_qpycustomwidget = saveUiFile("qpycustomwidget.ui", qpycustomwidget_ui)
def teardown():
- shutil.rmtree(self.tempdir)
+ # shutil.rmtree(self.tempdir)
+ pass
def binding(binding):
@@ -448,38 +461,39 @@ def test_load_ui_customwidget():
app.exit()
+def test_load_ui_pycustomwidget():
+ """Tests to see if loadUi loads a custom widget properly"""
+ import sys
+ from Qt import QtWidgets, QtCompat
-def test_headerToModule():
- """
- Tests to see if headerToModule manipulates the path passed in appropriately.
- - It should only affect `Header` files and paths, marked with an .h extension.
- """
+ # create a python file for the custom widget in a directory relative to the tempdir
+ filename = os.path.join(
+ self.tempdir,
+ self.tempdir,
+ "custom",
+ "customwidget",
+ "customwidget.py"
+ )
+ os.makedirs(os.path.dirname(filename))
+ with io.open(filename, "w", encoding="utf-8") as f:
+ f.write(self.python_custom_widget)
- path_tests = {
- # Input: Expected
-
- # Valid paths; .h paths need to be updated to work with Qt.py
- "path.to.module": "path.to.module", # valid python .path to module
- "path\\to\\module.h": "path.to.module", # valid .h path with backslashes
- "path\\to/module.h": "path.to.module", # mixed slashes
- "path/to/module.h": "path.to.module", # valid .h path with forward slashes
- "module.h": "module", # valid .h file in current path
- "module": "module", # valid python module in current path
-
- # malformed; Should we change QtDesigner input?
- "path.to.module.py": "path.to.module.py",
- "path\\to\\module.py": "path\\to\\module.py",
- "path/to/module.py": "path/to/module.py",
- "path\\to\\module": "path\\to\\module",
- "path/to/module": "path/to/module",
- "module.py": "module.py",
- }
+ # append the path to ensure the future import can be loaded 'relative' to the tempdir
+ sys.path.append(self.tempdir)
- import Qt
+ app = QtWidgets.QApplication([self.tempdir, ])
+ win = QtWidgets.QMainWindow()
- for provided, expected in path_tests.items():
- result = Qt._headerToModule(provided)
- assert result == expected, "Provided: %s expected: %s got: %s" % (provided, expected, result)
+ QtCompat.loadUi(self.ui_qpycustomwidget, 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():