diff --git a/Dockerfile-py2.7 b/Dockerfile-py2.7
index a8e59fda..cba92e49 100644
--- a/Dockerfile-py2.7
+++ b/Dockerfile-py2.7
@@ -25,11 +25,16 @@ ENV DISPLAY :99
WORKDIR /workspace/Qt.py
ENTRYPOINT cp -r /Qt.py /workspace && \
- python build_caveats_tests.py && \
- Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \
- sleep 3 && \
+ python build_caveats.py && \
+ python build_membership.py && \
+ Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \
+ while ! ps aux | grep -q '[0]:00 Xvfb :99 -screen 0 1024x768x16'; \
+ do echo "Waiting for Xvfb..."; sleep 1; done && \
+ ps aux | grep Xvfb && \
nosetests \
--verbose \
--with-process-isolation \
--with-doctest \
- --exe
+ --exe \
+ test*.py \
+ examples/*/*.py
diff --git a/Dockerfile-py3.5 b/Dockerfile-py3.5
index 89bedfa4..07e0ec98 100644
--- a/Dockerfile-py3.5
+++ b/Dockerfile-py3.5
@@ -25,11 +25,14 @@ ENV DISPLAY :99
WORKDIR /workspace/Qt.py
ENTRYPOINT cp -r /Qt.py /workspace && \
- python3 build_caveats_tests.py && \
- Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \
- sleep 3 && \
+ python3 build_caveats.py && \
+ Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \
+ while ! ps aux | grep -q '[0]:00 Xvfb :99 -screen 0 1024x768x16'; \
+ do echo "Waiting for Xvfb..."; sleep 1; done && \
nosetests \
--verbose \
--with-process-isolation \
--with-doctest \
- --exe
+ --exe \
+ test*.py \
+ examples/*/*.py
diff --git a/Qt.py b/Qt.py
index 8cc6a569..4ee34d3e 100644
--- a/Qt.py
+++ b/Qt.py
@@ -71,6 +71,7 @@ def _pyqt4():
PyQt4.QtCore.QStringListModel = PyQt4.QtGui.QStringListModel
PyQt4.QtCore.QItemSelectionModel = PyQt4.QtGui.QItemSelectionModel
PyQt4.QtCore.QSortFilterProxyModel = PyQt4.QtGui.QSortFilterProxyModel
+ PyQt4.QtCore.QAbstractProxyModel = PyQt4.QtGui.QAbstractProxyModel
try:
from PyQt4 import QtWebKit
@@ -117,6 +118,7 @@ def _pyside():
PySide.QtCore.QStringListModel = PySide.QtGui.QStringListModel
PySide.QtCore.QItemSelection = PySide.QtGui.QItemSelection
PySide.QtCore.QItemSelectionModel = PySide.QtGui.QItemSelectionModel
+ PySide.QtCore.QAbstractProxyModel = PySide.QtGui.QAbstractProxyModel
try:
from PySide import QtWebKit
diff --git a/README.md b/README.md
index 3b9d8604..d60244d6 100644
--- a/README.md
+++ b/README.md
@@ -79,13 +79,13 @@ app.exec_()
All members of `Qt` stem directly from those available via PySide2, along with these additional members.
-| Attribute | Type | Value
-|:------------------------|:-------|:------------
-| `__binding__` | `str` | A string reference to binding currently in use
-| `__qt_version__` | `str` | Reference to version of Qt, such as Qt 5.6.1
-| `__binding_version__` | `str` | Reference to version of binding, such as PySide 1.2.6
-| `__wrapper_version__` | `str` | Version of this project
-| `load_ui()` | `func` | Minimal wrapper of PyQt4.loadUi and PySide equivalent
+| Attribute | Returns | Description
+|:------------------------|:----------|:------------
+| `__binding__` | `str` | A string reference to binding currently in use
+| `__qt_version__` | `str` | Reference to version of Qt, such as Qt 5.6.1
+| `__binding_version__` | `str` | Reference to version of binding, such as PySide 1.2.6
+| `__wrapper_version__` | `str` | Version of this project
+| `load_ui(fname=str)` | `QObject` | Minimal wrapper of PyQt4.loadUi and PySide equivalent
@@ -136,12 +136,12 @@ import sys
import Qt
app = QtWidgets.QApplication(sys.argv)
-ui = Qt.load_ui("my.ui")
+ui = Qt.load_ui(fname="my.ui")
ui.show()
app.exec_()
```
-Please note, for maximum compatibility, only pass the argument of the filename to the `load_ui` function.
+Please note, `load_ui` has only one argument, whereas the PyQt and PySide equivalent has more. See [here](https://github.com/mottosso/Qt.py/pull/81) for details - in a nutshell, those arguments differ between PyQt and PySide in incompatible ways.
diff --git a/build_caveats_tests.py b/build_caveats.py
old mode 100755
new mode 100644
similarity index 100%
rename from build_caveats_tests.py
rename to build_caveats.py
diff --git a/build_examples.py b/build_examples.py
new file mode 100644
index 00000000..234df7d7
--- /dev/null
+++ b/build_examples.py
@@ -0,0 +1,11 @@
+import os
+import glob
+import shutil
+
+# Copy example files into current working directory
+for filepath in glob.glob('examples/*/*'):
+ filename = os.path.basename(filepath)
+ if filepath.endswith('.py'):
+ shutil.copyfile(filepath, 'test_'+filename) # Prepend 'test' to *.py
+ else:
+ shutil.copyfile(filepath, filename)
diff --git a/build_membership.py b/build_membership.py
new file mode 100644
index 00000000..b10b81d2
--- /dev/null
+++ b/build_membership.py
@@ -0,0 +1,333 @@
+import json
+
+
+def build_membership():
+ """Generate a .json file with all members of PySide2"""
+
+ # NOTE: PySide2, as of this writing, is incomplete.
+ # In it's __all__ module is a module, `QtOpenGL`
+ # that does no exists. This causes `import *` to fail.
+
+ from PySide2 import __all__
+ __all__.remove("QtOpenGL")
+
+ # These modules do not exist pre-Qt 5,
+ # so do not bother testing for them.
+ __all__.remove("QtSql")
+ __all__.remove("QtSvg")
+
+ # These should be present in PySide2,
+ # but are not as of this writing.
+ for missing in ("QtWidgets",
+ "QtXml",
+ "QtHelp",
+ "QtPrintSupport"):
+ __all__.append(missing)
+
+ # Why `import *`?
+ #
+ # PySide, and PyQt, perform magic that triggers via Python's
+ # import mechanism. If we try and sidestep it in any way, say
+ # by using `imp.load_module` or `__import__`, the mechanism
+ # will not trigger and the compiled libraries will not get loaded.
+ #
+ # Wildcard was the only way I could think of to import everything,
+ # without hardcoding the members, such as QtCore into the function.
+ from PySide2 import *
+
+ # Serialise members
+ members = {}
+ for name, module in locals().copy().items():
+ if name.startswith("_"):
+ continue
+
+ if name in ("json", "members", "missing"):
+ continue
+
+ members[name] = list(member for member in dir(module)
+ if not member.startswith("_"))
+
+ # Write to disk
+ with open("reference_members.json", "w") as f:
+ json.dump(members, f, indent=4)
+
+
+def build_tests():
+ """Build membership tests
+
+ Members only available in Qt 5 are excluded, along with member
+ exclusive to a paricular binding.
+
+ """
+
+ header = """\
+#
+# AUTOMATICALLY GENERATED MEMBERSHIP TEST, DO NOT MODIFY
+#
+
+import os
+import json
+
+with open("reference_members.json") as f:
+ reference_members = json.load(f)
+
+excluded = {excluded}
+
+""".format(excluded=json.dumps(excluded, indent=4))
+
+ test = """\
+def test_{binding}_members():
+ os.environ["QT_PREFERRED_BINDING"] = "{Binding}"
+
+ if "PyQt" in "{Binding}":
+ # PyQt4 and 5 performs some magic here
+ # that must take place before attempting
+ # to import with wildcard.
+ from Qt import Qt as _
+
+ if "PySide2" == "{Binding}":
+ # PySide2, as of this writing, doesn't include
+ # these modules in it's __all__ list; leaving
+ # the wildcard import below untrue.
+ from Qt import __all__
+ for missing in ("QtWidgets",
+ "QtXml",
+ "QtHelp",
+ "QtPrintSupport"):
+ __all__.append(missing)
+
+ from Qt import *
+
+ if "PySide" == "{Binding}":
+ # Qt 4 bindings do not include QtWidgets
+ # in their __all__ list. And who knows what else.
+ #
+ # TODO: This needs a more robust implementation.
+ from Qt import QtWidgets
+
+ target_members = dict()
+ for name, module in locals().copy().items():
+ if name.startswith("_"):
+ continue
+
+ target_members[name] = dir(module)
+
+ missing = dict()
+ for module, members in reference_members.items():
+ for member in members:
+
+ # Ignore those that have no Qt 4-equivalent.
+ if member in excluded.get(module, []):
+ continue
+
+ if member not in target_members.get(module, []):
+ if module not in missing:
+ missing[module] = []
+ missing[module].append(member)
+
+ message = ""
+ for module, members in missing.items():
+ message += "\\n%s: \\n - %s" % (module, "\\n - ".join(members))
+
+ assert not missing, "{Binding} is missing members: %s" % message
+
+"""
+
+ tests = list(test.format(Binding=binding,
+ binding=binding.lower())
+ for binding in ["PyQt5",
+ "PyQt4",
+ "PySide"])
+
+ with open("test_membership.py", "w") as f:
+ contents = header + "\n".join(tests)
+ print(contents) # Preview content during tests
+ f.write(contents)
+
+
+# Do not consider these members.
+#
+# Some of these are either:
+# 1. Unique to a particular binding
+# 2. Unique to Qt 5
+# 3. Not yet included in PySide2
+#
+# TODO: Clearly mark which are which. (3) should
+# eventually be removed from this dictionary.
+excluded = {
+ "QtCore": [
+ # missing from PySide
+ "Connection",
+ "QBasicMutex",
+ "QFileDevice",
+ "QItemSelectionRange",
+ "QJsonArray",
+ "QJsonDocument",
+ "QJsonParseError",
+ "QJsonValue",
+ "QMessageLogContext",
+ "QtInfoMsg",
+ "qInstallMessageHandler",
+
+ # missing from PyQt4
+ "ClassInfo",
+ "MetaFunction",
+ "QFactoryInterface",
+ "QSortFilterProxyModel",
+ "QStringListModel",
+ "QT_TRANSLATE_NOOP3",
+ "QT_TRANSLATE_NOOP_UTF8",
+ "__moduleShutdown",
+ "__version__", # (2) unique to PyQt
+ "__version_info__", # (2) unique to PyQt
+ "qAcos",
+ "qAsin",
+ "qAtan",
+ "qAtan2",
+ "qExp",
+ "qFabs",
+ "qFastCos",
+ "qFastSin",
+ "qFuzzyIsNull",
+ "qTan",
+ "qtTrId",
+
+ # missing from PyQt5
+ "SIGNAL",
+ "SLOT",
+ ],
+
+ "QtGui": [
+ # missing from PySide
+ "QGuiApplication", # (2) unique to Qt 5
+ "QPagedPaintDevice",
+ "QSurface",
+ "QSurfaceFormat",
+ "QTouchDevice",
+ "QWindow", # (2) unique to Qt 5
+
+ # missing from PyQt4
+ "QAccessibleEvent",
+ "QToolBarChangeEvent",
+
+ # missing from PyQt5
+ "QMatrix",
+ "QPyTextObject",
+ "QStringListModel",
+ ],
+
+ "QtWebKit": [
+ # missing from PyQt4
+ "WebCore",
+
+ # missing from PyQt5
+ "__doc__",
+ "__file__",
+ "__name__",
+ "__package__",
+ ],
+
+ "QtScript": [
+ # missing from PyQt4
+ "QScriptExtensionInterface",
+ "QScriptExtensionPlugin",
+ "QScriptProgram",
+ "QScriptable",
+
+ # missing from PyQt5
+ "QScriptClass",
+ "QScriptClassPropertyIterator",
+ "QScriptContext",
+ "QScriptContextInfo",
+ "QScriptEngine",
+ "QScriptEngineAgent",
+ "QScriptString",
+ "QScriptValue",
+ "QScriptValueIterator",
+ "__doc__",
+ "__file__",
+ "__name__",
+ "__package__",
+ ],
+
+ "QtNetwork": [
+ # missing from PyQt4
+ "QIPv6Address",
+ ],
+
+ "QtPrintSupport": [
+ # PyQt4
+ "QAbstractPrintDialog",
+ "QPageSetupDialog",
+ "QPrintDialog",
+ "QPrintEngine",
+ "QPrintPreviewDialog",
+ "QPrintPreviewWidget",
+ "QPrinter",
+ "QPrinterInfo",
+ ],
+
+ "QtWidgets": [
+ # PyQt4
+ "QTileRules",
+
+ # PyQt5
+ "QGraphicsItemAnimation",
+ "QTileRules",
+ ],
+
+ "QtHelp": [
+ # PySide
+ "QHelpContentItem",
+ "QHelpContentModel",
+ "QHelpContentWidget",
+ "QHelpEngine",
+ "QHelpEngineCore",
+ "QHelpIndexModel",
+ "QHelpIndexWidget",
+ "QHelpSearchEngine",
+ "QHelpSearchQuery",
+ "QHelpSearchQueryWidget",
+ "QHelpSearchResultWidget",
+ ],
+
+ "QtXml": [
+ # PySide
+ "QDomAttr",
+ "QDomCDATASection",
+ "QDomCharacterData",
+ "QDomComment",
+ "QDomDocument",
+ "QDomDocumentFragment",
+ "QDomDocumentType",
+ "QDomElement",
+ "QDomEntity",
+ "QDomEntityReference",
+ "QDomImplementation",
+ "QDomNamedNodeMap",
+ "QDomNode",
+ "QDomNodeList",
+ "QDomNotation",
+ "QDomProcessingInstruction",
+ "QDomText",
+ "QXmlAttributes",
+ "QXmlContentHandler",
+ "QXmlDTDHandler",
+ "QXmlDeclHandler",
+ "QXmlDefaultHandler",
+ "QXmlEntityResolver",
+ "QXmlErrorHandler",
+ "QXmlInputSource",
+ "QXmlLexicalHandler",
+ "QXmlLocator",
+ "QXmlNamespaceSupport",
+ "QXmlParseException",
+ "QXmlReader",
+ "QXmlSimpleReader",
+ ],
+
+}
+
+if __name__ == '__main__':
+ build_membership()
+ build_tests()
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 00000000..1087732e
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,15 @@
+## Examples
+
+#### Purpose
+
+Sometimes a pull request or a feature request doesn't make it into Qt.py because it doesn't fit [the contribution guidelines](https://github.com/mottosso/Qt.py/blob/master/CONTRIBUTING.md). This is hopefully a good thing for the end product in the long term perspective, but we're always sad to see good code drift into oblivion and disappear when a pull request is turned down and closed.
+
+This part of the Qt.py project is a more loosely maintained (although tested) space, where we welcome example use of Qt.py to be shown off especially if it solves a problem Qt.py by itself doesn't solve out of the box.
+
+If you wish to contribute, make a pull request. Please put your example files in a sub-folder of `/examples`. If you also wish to have your example included in testing, make sure the function you wish to be executed during testing is named in such a way that it starts with `test`. For a working example of examples :wink:, see the `/examples/load_ui` folder.
+
+
+
+#### List of examples
+
+* [`load_ui`](https://github.com/mottosso/Qt.py/blob/master/examples/load_ui/README.md) - add base instance argument
diff --git a/examples/__init__.py b/examples/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/load_ui/README.md b/examples/load_ui/README.md
new file mode 100644
index 00000000..e7046cdd
--- /dev/null
+++ b/examples/load_ui/README.md
@@ -0,0 +1,17 @@
+## `load_ui` examples
+
+#### Base instance as argument
+
+The `uic.loadUi` function of PyQt4 and PyQt5 as well as the `QtUiTools.QUiLoader().load` function of PySide/PySide2 are mapped to a convenience function in Qt.py called `load_ui`. This function only accepts the filename argument of the .ui file.
+
+A popular approach is to provide a base instance argument to PyQt's `uic.loadUi`, into which all widgets are loaded:
+
+```python
+# PyQt4, PyQt5
+class MainWindow(QtWidgets.QWidget):
+ def __init__(self, parent=None):
+ QtWidgets.QWidget.__init__(self, parent)
+ uic.loadUi('uifile.ui', self) # Loads all widgets of uifile.ui into self
+```
+
+PySide does not support this out of the box, but it can be implemented in various ways. In the example in `baseinstance1.py`, a support function `setup_ui` is defined which wraps `load_ui` and provides this second base instance argument. In `baseinstance2.py`, another approach is used where `pysideuic` is required for PySide/PySide2 and `uic` is required for PyQt4/PyQt5.
diff --git a/examples/load_ui/__init__.py b/examples/load_ui/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/load_ui/baseinstance1.py b/examples/load_ui/baseinstance1.py
new file mode 100644
index 00000000..29e4edb9
--- /dev/null
+++ b/examples/load_ui/baseinstance1.py
@@ -0,0 +1,56 @@
+import sys
+import os
+
+# Set preferred binding
+os.environ['QT_PREFERRED_BINDING'] = os.pathsep.join(['PySide', 'PyQt4'])
+
+from Qt import QtWidgets, load_ui
+
+
+def setup_ui(uifile, base_instance=None):
+ """Load a Qt Designer .ui file and returns an instance of the user interface
+
+ Args:
+ uifile (str): Absolute path to .ui file
+ base_instance (QWidget): The widget into which UI widgets are loaded
+
+ Returns:
+ QWidget: the base instance
+
+ """
+ ui = load_ui(uifile) # Qt.py mapped function
+ if not base_instance:
+ return ui
+ else:
+ for member in dir(ui):
+ if not member.startswith('__') and \
+ member is not 'staticMetaObject':
+ setattr(base_instance, member, getattr(ui, member))
+ return ui
+
+
+class MainWindow(QtWidgets.QWidget):
+ """Load .ui file example, using setattr/getattr approach"""
+ def __init__(self, parent=None):
+ QtWidgets.QWidget.__init__(self, parent)
+ self.base_instance = setup_ui('qwidget.ui', self)
+
+
+def test():
+ """Example: load_ui with setup_ui wrapper"""
+ working_directory = os.path.dirname(__file__)
+ os.chdir(working_directory)
+
+ app = QtWidgets.QApplication(sys.argv)
+ window = MainWindow()
+
+ # Tests
+ assert isinstance(window, QtWidgets.QWidget)
+ assert isinstance(window.parent(), type(None))
+ assert isinstance(window.base_instance, QtWidgets.QWidget)
+ assert isinstance(window.lineEdit, QtWidgets.QWidget)
+ assert window.lineEdit.text() == ''
+ window.lineEdit.setText('Hello')
+ assert window.lineEdit.text() == 'Hello'
+
+ app.exit()
diff --git a/examples/load_ui/baseinstance2.py b/examples/load_ui/baseinstance2.py
new file mode 100644
index 00000000..b4e556c9
--- /dev/null
+++ b/examples/load_ui/baseinstance2.py
@@ -0,0 +1,135 @@
+import sys
+import os
+
+# Set preferred binding, or Qt.py tests will fail which doesn't have pysideuic
+os.environ['QT_PREFERRED_BINDING'] = 'PyQt4'
+
+from Qt import QtWidgets
+from Qt import __binding__
+
+
+def load_ui_type(uifile):
+ """Pyside equivalent for the loadUiType function in PyQt.
+
+ From the PyQt4 documentation:
+ Load a Qt Designer .ui file and return a tuple of the generated form
+ class and the Qt base class. These can then be used to create any
+ number of instances of the user interface without having to parse the
+ .ui file more than once.
+
+ Note:
+ Pyside lacks the "loadUiType" command, so we have to convert the ui
+ file to py code in-memory first and then execute it in a special frame
+ to retrieve the form_class.
+
+ Args:
+ uifile (str): Absolute path to .ui file
+
+
+ Returns:
+ tuple: the generated form class, the Qt base class
+ """
+ import pysideuic
+ import xml.etree.ElementTree as ElementTree
+ from cStringIO import StringIO
+
+ parsed = ElementTree.parse(uifile)
+ widget_class = parsed.find('widget').get('class')
+ form_class = parsed.find('class').text
+
+ with open(uifile, 'r') as f:
+ o = StringIO()
+ frame = {}
+
+ pysideuic.compileUi(f, o, indent=0)
+ pyc = compile(o.getvalue(), '', 'exec')
+ exec(pyc) in frame
+
+ # Fetch the base_class and form class based on their type in
+ # the xml from designer
+ form_class = frame['Ui_%s' % form_class]
+ base_class = eval('QtWidgets.%s' % widget_class)
+ return form_class, base_class
+
+
+def pyside_load_ui(uifile, base_instance=None):
+ """Provide PyQt4.uic.loadUi functionality to PySide
+
+ Args:
+ uifile (str): Absolute path to .ui file
+ base_instance (QWidget): The widget into which UI widgets are loaded
+
+
+ Note:
+ pysideuic is required for this to work with PySide.
+
+ This seems to work correctly in Maya as well as outside of it as
+ opposed to other implementations which involve overriding QUiLoader.
+
+ Returns:
+ QWidget: the base instance
+
+ """
+ form_class, base_class = load_ui_type(uifile)
+ if not base_instance:
+ typeName = form_class.__name__
+ finalType = type(typeName,
+ (form_class, base_class),
+ {})
+ base_instance = finalType()
+ else:
+ if not isinstance(base_instance, base_class):
+ raise RuntimeError(
+ 'The base_instance passed to loadUi does not inherit from'
+ ' needed base type (%s)' % type(base_class))
+ typeName = type(base_instance).__name__
+ base_instance.__class__ = type(typeName,
+ (form_class, type(base_instance)),
+ {})
+ base_instance.setupUi(base_instance)
+ return base_instance
+
+
+def load_ui_wrapper(uifile, base_instance=None):
+ """Load a Qt Designer .ui file and returns an instance of the user interface
+
+ Args:
+ uifile (str): Absolute path to .ui file
+ base_instance (QWidget): The widget into which UI widgets are loaded
+
+ Returns:
+ function: pyside_load_ui or uic.loadUi
+
+ """
+ if 'PySide' in __binding__:
+ return pyside_load_ui(uifile, base_instance)
+ elif 'PyQt' in __binding__:
+ from Qt import uic
+ return uic.loadUi(uifile, base_instance)
+
+
+class MainWindow(QtWidgets.QWidget):
+ """Load .ui file example, utilizing pysideuic and/or PyQt4.uic.loadUi"""
+ def __init__(self, parent=None):
+ QtWidgets.QWidget.__init__(self, parent)
+ self.base_instance = load_ui_wrapper('qwidget.ui', self)
+
+
+def test():
+ """Example: load_ui with custom uic.loadUi-like wrapper"""
+ working_directory = os.path.dirname(__file__)
+ os.chdir(working_directory)
+
+ app = QtWidgets.QApplication(sys.argv)
+ window = MainWindow()
+
+ # Tests
+ assert isinstance(window, QtWidgets.QWidget)
+ assert isinstance(window.parent(), type(None))
+ assert isinstance(window.base_instance, QtWidgets.QWidget)
+ assert isinstance(window.lineEdit, QtWidgets.QWidget)
+ assert window.lineEdit.text() == ''
+ window.lineEdit.setText('Hello')
+ assert window.lineEdit.text() == 'Hello'
+
+ app.exit()
diff --git a/examples/load_ui/qwidget.ui b/examples/load_ui/qwidget.ui
new file mode 100644
index 00000000..71a5d099
--- /dev/null
+++ b/examples/load_ui/qwidget.ui
@@ -0,0 +1,24 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 235
+ 149
+
+
+
+ Form
+
+
+ -
+
+
+
+
+
+
+
diff --git a/tests.py b/tests.py
index 5aa45a1d..7903e2f4 100644
--- a/tests.py
+++ b/tests.py
@@ -6,6 +6,7 @@
"""
import os
+import io
import sys
import imp
import shutil
@@ -24,6 +25,36 @@
def setup():
self.tempdir = tempfile.mkdtemp()
+ self.ui_qwidget = os.path.join(self.tempdir, "qwidget.ui")
+
+ with io.open(self.ui_qwidget, "w", encoding="utf-8") as f:
+ f.write(u"""\
+
+
+ Form
+
+
+
+ 0
+ 0
+ 235
+ 149
+
+
+
+ Form
+
+
+ -
+
+
+
+
+
+
+
+"""
+)
def teardown():
@@ -143,6 +174,50 @@ def test_sip_api_qtpy():
% sip.getapi("QString"))
+def test_pyside_load_ui_returntype():
+ """load_ui returns an instance of QObject with PySide"""
+
+ with pyside():
+ import sys
+ from Qt import QtWidgets, QtCore, load_ui
+ app = QtWidgets.QApplication(sys.argv)
+ obj = load_ui(self.ui_qwidget)
+ assert isinstance(obj, QtCore.QObject)
+
+
+def test_pyqt4_load_ui_returntype():
+ """load_ui returns an instance of QObject with PyQt4"""
+
+ with pyqt4():
+ import sys
+ from Qt import QtWidgets, QtCore, load_ui
+ app = QtWidgets.QApplication(sys.argv)
+ obj = load_ui(self.ui_qwidget)
+ assert isinstance(obj, QtCore.QObject)
+
+
+def test_pyside2_load_ui_returntype():
+ """load_ui returns an instance of QObject with PySide2"""
+
+ with pyside2():
+ import sys
+ from Qt import QtWidgets, QtCore, load_ui
+ app = QtWidgets.QApplication(sys.argv)
+ obj = load_ui(self.ui_qwidget)
+ assert isinstance(obj, QtCore.QObject)
+
+
+def test_pyqt5_load_ui_returntype():
+ """load_ui returns an instance of QObject with PyQt5"""
+
+ with pyqt5():
+ import sys
+ from Qt import QtWidgets, QtCore, load_ui
+ app = QtWidgets.QApplication(sys.argv)
+ obj = load_ui(self.ui_qwidget)
+ assert isinstance(obj, QtCore.QObject)
+
+
def test_vendoring():
"""Qt.py may be bundled along with another library/project