From d286429d291ce1f2e697ff2c20a08f666009eaa0 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 00:59:21 +0200 Subject: [PATCH 01/59] Add tests for improvements in load_ui --- tests.py | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/tests.py b/tests.py index ab390628..d99cc322 100644 --- a/tests.py +++ b/tests.py @@ -181,3 +181,235 @@ def test_sip_api_already_set(): import sip sip.setapi("QString", 1) assert_raises(ImportError, __import__, "Qt") + + +def test_load_ui_into_self_pyside(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(f.name, self) + assert self.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.show() + # app.exec_() + + +def test_load_ui_into_self_pyqt4(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyqt4(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(f.name, self) + assert self.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.show() + # app.exec_() + + +def test_load_ui_into_custom_pyside(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + self.custom = load_ui(f.name) + assert self.custom.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.custom.show() + # app.exec_() + + +def test_load_ui_into_custom_pyqt4(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyqt4(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + self.custom = load_ui(f.name) + assert self.custom.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.custom.show() + # app.exec_() \ No newline at end of file From b9a5d965260174f6395d5afcb3e1a7107d6762f2 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:08:02 +0200 Subject: [PATCH 02/59] Add distinguishable test descriptions --- tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests.py b/tests.py index d99cc322..a8953726 100644 --- a/tests.py +++ b/tests.py @@ -184,7 +184,7 @@ def test_sip_api_already_set(): def test_load_ui_into_self_pyside(): - """load widgets into self""" + """load widgets into self using PySide""" ui = """\ @@ -242,7 +242,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_pyqt4(): - """load widgets into self""" + """load widgets into self using PyQt4""" ui = """\ @@ -300,7 +300,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load widgets into self""" + """load widgets into custom using PySide""" ui = """\ @@ -358,7 +358,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyqt4(): - """load widgets into self""" + """load widgets into custom using PyQt4""" ui = """\ From 1b068a1f759958dc9236b4a8efc66bd942ca0601 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:23:46 +0200 Subject: [PATCH 03/59] Add support for additional load_ui arguments --- Qt.py | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 17 deletions(-) diff --git a/Qt.py b/Qt.py index 5b970da2..23d6bba3 100644 --- a/Qt.py +++ b/Qt.py @@ -118,60 +118,184 @@ def _pyside(): return PySide -def pyside_load_ui(fname): +def pyside_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? Usage: >> from Qt import load_ui >> class MyWindow(QtWidgets.QWidget): .. fname = 'my_ui.ui' - .. self.ui = load_ui(fname) + .. load_ui(fname, self) .. >> window = MyWindow() - """ - - from PySide import QtUiTools - return QtUiTools.QUiLoader().load(fname) + Note: + This function is based on the gist: + https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + """ -def pyside2_load_ui(fname): + from PySide import QtUiTools, QtCore + + class UiLoader(QtUiTools.QUiLoader): + def __init__(self, base_instance, custom_widgets=None): + QtUiTools.QUiLoader.__init__(self, base_instance) + self.base_instance = base_instance + self.custom_widgets = custom_widgets + + def createWidget(self, class_name, parent=None, name=''): + if parent is None and self.base_instance: + return self.base_instance + else: + if class_name in self.availableWidgets(): + widget = QtUiTools.QUiLoader.createWidget(self, + class_name, + parent, name) + else: + try: + widget = self.custom_widgets[class_name](parent) + except (TypeError, KeyError): + raise Exception('No custom widget ' + + class_name + + ' found in custom_widgets' + + ' param of UiLoader __init__.') + if self.base_instance: + setattr(self.base_instance, name, widget) + return widget + + def loadUi(fname, base_instance=None, custom_widgets=None): + loader = UiLoader(base_instance, custom_widgets) + widget = loader.load(fname) + QtCore.QMetaObject.connectSlotsByName(widget) + return widget + + return loadUi(fname, base_instance=base_instance, + custom_widgets=custom_widgets) + + +def pyside2_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? - """ + Usage: + >> from Qt import load_ui + >> class MyWindow(QtWidgets.QWidget): + .. fname = 'my_ui.ui' + .. load_ui(fname, self) + .. + >> window = MyWindow() - from PySide2 import QtUiTools - return QtUiTools.QUiLoader().load(fname) + Note: + This function is based on the gist: + https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + """ -def pyqt4_load_ui(fname): + from PySide2 import QtUiTools, QtCore + + class UiLoader(QtUiTools.QUiLoader): + def __init__(self, base_instance, custom_widgets=None): + QtUiTools.QUiLoader.__init__(self, base_instance) + self.base_instance = base_instance + self.custom_widgets = custom_widgets + + def createWidget(self, class_name, parent=None, name=''): + if parent is None and self.base_instance: + return self.base_instance + else: + if class_name in self.availableWidgets(): + widget = QtUiTools.QUiLoader.createWidget(self, + class_name, + parent, name) + else: + try: + widget = self.custom_widgets[class_name](parent) + except (TypeError, KeyError): + raise Exception('No custom widget ' + + class_name + + ' found in custom_widgets' + + ' param of UiLoader __init__.') + if self.base_instance: + setattr(self.base_instance, name, widget) + return widget + + def loadUi(fname, base_instance=None, custom_widgets=None): + loader = UiLoader(base_instance, custom_widgets) + widget = loader.load(fname) + QtCore.QMetaObject.connectSlotsByName(widget) + return widget + + return loadUi(fname, base_instance=base_instance, + custom_widgets=custom_widgets) + + +def pyqt4_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? + + Usage: + >> from Qt import load_ui + >> class MyWindow(QtWidgets.QWidget): + .. fname = 'my_ui.ui' + .. load_ui(fname, self) + .. + >> window = MyWindow() """ from PyQt4 import uic - return uic.loadUi(fname) + + if isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname) + elif not isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname, base_instance) + else: + return uic.loadUi(fname, base_instance, custom_widgets) -def pyqt5_load_ui(fname): +def pyqt5_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? + + Usage: + >> from Qt import load_ui + >> class MyWindow(QtWidgets.QWidget): + .. fname = 'my_ui.ui' + .. load_ui(fname, self) + .. + >> window = MyWindow() """ from PyQt5 import uic - return uic.loadUi(fname) + + if isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname) + elif not isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname, base_instance) + else: + return uic.loadUi(fname, base_instance, custom_widgets) def _log(text, verbose): @@ -189,18 +313,18 @@ def _init(): this has executed. """ - + preferred = os.getenv("QT_PREFERRED_BINDING") verbose = os.getenv("QT_VERBOSE") is not None bindings = (_pyside2, _pyqt5, _pyside, _pyqt4) if preferred: - + # Internal flag (used in installer) if preferred == "None": sys.modules[__name__].__wrapper_version__ = __version__ return - + preferred = preferred.split(os.pathsep) available = { "PySide2": _pyside2, From dfa8587b14b36fd36e7472bac5537a79d6b9f0af Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:32:58 +0200 Subject: [PATCH 04/59] Specify a non-binary mode --- tests.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests.py b/tests.py index a8953726..9faa8210 100644 --- a/tests.py +++ b/tests.py @@ -184,7 +184,7 @@ def test_sip_api_already_set(): def test_load_ui_into_self_pyside(): - """load widgets into self using PySide""" + """load_ui: Load widgets into self using PySide""" ui = """\ @@ -221,7 +221,7 @@ def test_load_ui_into_self_pyside(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) @@ -242,7 +242,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_pyqt4(): - """load widgets into self using PyQt4""" + """load_ui: Load widgets into self using PyQt4""" ui = """\ @@ -279,7 +279,7 @@ def test_load_ui_into_self_pyqt4(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) @@ -300,7 +300,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load widgets into custom using PySide""" + """load_ui: Load widgets into custom using PySide""" ui = """\ @@ -337,7 +337,7 @@ def test_load_ui_into_custom_pyside(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) @@ -358,7 +358,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyqt4(): - """load widgets into custom using PyQt4""" + """load_ui: Load widgets into custom using PyQt4""" ui = """\ @@ -395,7 +395,7 @@ def test_load_ui_into_custom_pyqt4(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) From 174833e9772a6cda7120c8afb5f845c998f4b00e Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:36:23 +0200 Subject: [PATCH 05/59] Update with load_ui additional arguments info --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cf19e2f2..f6863211 100644 --- a/README.md +++ b/README.md @@ -181,15 +181,15 @@ from Qt import load_ui class Hello(QtWidgets.QWidget): def __init__(self): super(Hello, self).__init__() - self.ui = load_ui('my_ui.ui') + load_ui('my_ui.ui', self) app = QtWidgets.QApplication(sys.argv) window = Hello() -window.ui.show() +window.show() sys.exit(app.exec_()) ``` -Please note, for maximum compatibility, only pass the argument of the filename to the `load_ui` function. +The `QUiLoader` class was modified to accept a base instance argument as well as custom widgets argument to become as compatible with `uic.loadUi` as possible. ##### sip API v2 From c61b33d1f97982d12f7b2a49fc18f21b0fdd4d1b Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:37:36 +0200 Subject: [PATCH 06/59] Remove QMenuBar and QStatusBar from UI --- tests.py | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/tests.py b/tests.py index 9faa8210..8c7869b3 100644 --- a/tests.py +++ b/tests.py @@ -205,18 +205,6 @@ def test_load_ui_into_self_pyside(): - - - - 0 - 0 - 125 - 22 - - - - - """ @@ -263,18 +251,6 @@ def test_load_ui_into_self_pyqt4(): - - - - 0 - 0 - 125 - 22 - - - - - """ @@ -321,18 +297,6 @@ def test_load_ui_into_custom_pyside(): - - - - 0 - 0 - 125 - 22 - - - - - """ @@ -379,18 +343,6 @@ def test_load_ui_into_custom_pyqt4(): - - - - 0 - 0 - 125 - 22 - - - - - """ From 73920a9924f64a952eb4c4024faa240243c3b4b6 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:37:36 +0200 Subject: [PATCH 07/59] Revert "Remove QMenuBar and QStatusBar from UI" This reverts commit c61b33d1f97982d12f7b2a49fc18f21b0fdd4d1b. --- tests.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests.py b/tests.py index 8c7869b3..9faa8210 100644 --- a/tests.py +++ b/tests.py @@ -205,6 +205,18 @@ def test_load_ui_into_self_pyside(): + + + + 0 + 0 + 125 + 22 + + + + + """ @@ -251,6 +263,18 @@ def test_load_ui_into_self_pyqt4(): + + + + 0 + 0 + 125 + 22 + + + + + """ @@ -297,6 +321,18 @@ def test_load_ui_into_custom_pyside(): + + + + 0 + 0 + 125 + 22 + + + + + """ @@ -343,6 +379,18 @@ def test_load_ui_into_custom_pyqt4(): + + + + 0 + 0 + 125 + 22 + + + + + """ From c6dc0431956c3f7cb038a23b763f1b40192dff83 Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Fri, 29 Jul 2016 07:51:06 +0100 Subject: [PATCH 08/59] Simplify tests --- tests.py | 273 ++++++++++++++++--------------------------------------- 1 file changed, 76 insertions(+), 197 deletions(-) diff --git a/tests.py b/tests.py index 9faa8210..6411f52c 100644 --- a/tests.py +++ b/tests.py @@ -34,6 +34,50 @@ def pyside(): os.environ.pop("QT_PREFERRED_BINDING") +@contextlib.contextmanager +def ui(): + source = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: + f.write(source) + f.seek(0) + + yield f.name + + def test_environment(): """Tests require PySide and PyQt4 bindings to be installed""" @@ -172,244 +216,79 @@ def test_vendoring(): shutil.rmtree(tempdir) -if PYTHON == 2: - def test_sip_api_already_set(): - """Raise ImportError if sip API v1 was already set (Python 2.x only)""" - - with pyqt4(): - __import__("PyQt4.QtCore") # Bypass linter warning - import sip - sip.setapi("QString", 1) - assert_raises(ImportError, __import__, "Qt") - - def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) - + with ui() as fname: with pyside(): from Qt import QtWidgets, load_ui class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(f.name, self) - assert self.pushButton + load_ui(fname, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() - # window.show() - # app.exec_() + + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) - + with ui() as fname: with pyqt4(): from Qt import QtWidgets, load_ui class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(f.name, self) - assert self.pushButton + load_ui(fname, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() - # window.show() - # app.exec_() - -def test_load_ui_into_custom_pyside(): - """load_ui: Load widgets into custom using PySide""" + # Inherited from .ui file + assert hasattr(window, "pushButton") - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) +def test_load_ui_into_custom_pyside(): + """load_ui: Load .ui file""" + with ui() as fname: with pyside(): from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - self.custom = load_ui(f.name) - assert self.custom.pushButton - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - # window.custom.show() - # app.exec_() + widget = load_ui(fname) + + # From .ui file + assert hasattr(widget, "pushButton") def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) - + with ui() as fname: with pyqt4(): from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) + + # From .ui file + assert hasattr(widget, "pushButton") - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - self.custom = load_ui(f.name) - assert self.custom.pushButton - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - # window.custom.show() - # app.exec_() \ No newline at end of file +if PYTHON == 2: + def test_sip_api_already_set(): + """Raise ImportError if sip API v1 was already set (Python 2.x only)""" + + with pyqt4(): + __import__("PyQt4.QtCore") # Bypass linter warning + import sip + sip.setapi("QString", 1) + assert_raises(ImportError, __import__, "Qt") + From cae6aff042a102e6b187cf0ed2e25d742eeaf766 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 09:26:28 +0200 Subject: [PATCH 09/59] Remove whitespace --- tests.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests.py b/tests.py index 6411f52c..5c77b39d 100644 --- a/tests.py +++ b/tests.py @@ -263,7 +263,7 @@ def test_load_ui_into_custom_pyside(): app = QtWidgets.QApplication(sys.argv) widget = load_ui(fname) - + # From .ui file assert hasattr(widget, "pushButton") @@ -277,7 +277,7 @@ def test_load_ui_into_custom_pyqt4(): app = QtWidgets.QApplication(sys.argv) widget = load_ui(fname) - + # From .ui file assert hasattr(widget, "pushButton") @@ -291,4 +291,3 @@ def test_sip_api_already_set(): import sip sip.setapi("QString", 1) assert_raises(ImportError, __import__, "Qt") - From 6dad71fcd528af54cae87793d15f0d297b541ec9 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 13:08:38 +0200 Subject: [PATCH 10/59] Add Xvfb --- Dockerfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7b8c470e..5e5d9740 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,8 @@ FROM ubuntu:14.04 RUN apt-get update && apt-get install -y \ python-qt4 \ python-pyside \ - python-pip + python-pip \ + xvfb # Nose is the Python test-runner RUN pip install nose nosepipe @@ -11,9 +12,14 @@ RUN pip install nose nosepipe # Enable additional output from Qt.py ENV QT_VERBOSE true +# Xvfb +ENV DISPLAY :99 + WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ + Xvfb :99 -screen 0 1024x768x16 & \ + sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ From a01dee12a26c641faf3b5171a567eddede1669f5 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:35:55 +0200 Subject: [PATCH 11/59] Add xvfbwrapper --- Dockerfile | 9 ++---- tests.py | 90 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5e5d9740..2be7e6e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,21 +7,16 @@ RUN apt-get update && apt-get install -y \ xvfb # Nose is the Python test-runner -RUN pip install nose nosepipe +RUN pip install nose nosepipe xvfbwrapper # Enable additional output from Qt.py ENV QT_VERBOSE true -# Xvfb -ENV DISPLAY :99 - WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ - Xvfb :99 -screen 0 1024x768x16 & \ - sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ --with-doctest \ - --exe + --exe diff --git a/tests.py b/tests.py index 5c77b39d..b34196b1 100644 --- a/tests.py +++ b/tests.py @@ -34,6 +34,18 @@ def pyside(): os.environ.pop("QT_PREFERRED_BINDING") +@contextlib.contextmanager +def xvfb(): + from xvfbwrapper import Xvfb + + vdisplay = Xvfb() + vdisplay.start() + + yield + + # vdisplay.stop() + + @contextlib.contextmanager def ui(): source = """\ @@ -219,67 +231,71 @@ def test_vendoring(): def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_custom_pyside(): - """load_ui: Load .ui file""" + """load_ui: Load .ui file into custom using PySide""" - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") if PYTHON == 2: From 9636bf22a3fe7e04856542d99ddc342e5d3742b3 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:42:21 +0200 Subject: [PATCH 12/59] Add xvfbwrapper to .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b06e60eb..067f78fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,8 @@ install: - sudo apt-get install -y qt4-qmake libqt4-dev - sudo apt-get install -y python-qt4 python-pyside - sudo apt-get install -y python3-pyqt4 python3-pyside python3-pip - - sudo pip install nosepipe - - sudo pip3 install nosepipe + - sudo pip install nosepipe xvfbwrapper + - sudo pip3 install nosepipe xvfbwrapper # Print version information - python -c "import PySide;print('Path, %s' % PySide)" From cf6b7d76e021ed83d5f6db92c7171c0218930124 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:54:05 +0200 Subject: [PATCH 13/59] Add tests title consistency --- tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.py b/tests.py index b34196b1..ba98101f 100644 --- a/tests.py +++ b/tests.py @@ -269,7 +269,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load_ui: Load .ui file into custom using PySide""" + """load_ui: Load widgets into custom using PySide""" with xvfb(): with ui() as fname: From 0efaa1038632f6b5840ce5c534dfc92b57e1f009 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:42:21 +0200 Subject: [PATCH 14/59] Revert "Add xvfbwrapper to .travis.yml" This reverts commit 9636bf22a3fe7e04856542d99ddc342e5d3742b3. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 067f78fc..b06e60eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,8 @@ install: - sudo apt-get install -y qt4-qmake libqt4-dev - sudo apt-get install -y python-qt4 python-pyside - sudo apt-get install -y python3-pyqt4 python3-pyside python3-pip - - sudo pip install nosepipe xvfbwrapper - - sudo pip3 install nosepipe xvfbwrapper + - sudo pip install nosepipe + - sudo pip3 install nosepipe # Print version information - python -c "import PySide;print('Path, %s' % PySide)" From 1075d0209606913eb488d7b6f3c8bc0fcaf6fada Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 15:31:29 +0200 Subject: [PATCH 15/59] Remove xvfbwrapper --- Dockerfile | 9 ++++-- tests.py | 88 ++++++++++++++++++++++-------------------------------- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2be7e6e6..5e5d9740 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,16 +7,21 @@ RUN apt-get update && apt-get install -y \ xvfb # Nose is the Python test-runner -RUN pip install nose nosepipe xvfbwrapper +RUN pip install nose nosepipe # Enable additional output from Qt.py ENV QT_VERBOSE true +# Xvfb +ENV DISPLAY :99 + WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ + Xvfb :99 -screen 0 1024x768x16 & \ + sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ --with-doctest \ - --exe + --exe diff --git a/tests.py b/tests.py index ba98101f..b5eb52fa 100644 --- a/tests.py +++ b/tests.py @@ -34,18 +34,6 @@ def pyside(): os.environ.pop("QT_PREFERRED_BINDING") -@contextlib.contextmanager -def xvfb(): - from xvfbwrapper import Xvfb - - vdisplay = Xvfb() - vdisplay.start() - - yield - - # vdisplay.stop() - - @contextlib.contextmanager def ui(): source = """\ @@ -231,71 +219,67 @@ def test_vendoring(): def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - with xvfb(): - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - with xvfb(): - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_custom_pyside(): """load_ui: Load widgets into custom using PySide""" - with xvfb(): - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - with xvfb(): - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") if PYTHON == 2: From 211db116ff288e74bb47eb25b53f9705439af9f7 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 00:59:21 +0200 Subject: [PATCH 16/59] Add tests for improvements in load_ui --- tests.py | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/tests.py b/tests.py index 6c3ca8fd..aa199856 100644 --- a/tests.py +++ b/tests.py @@ -188,3 +188,235 @@ def test_sip_api_already_set(): import sip sip.setapi("QString", 1) assert_raises(ImportError, __import__, "Qt") + + +def test_load_ui_into_self_pyside(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(f.name, self) + assert self.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.show() + # app.exec_() + + +def test_load_ui_into_self_pyqt4(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyqt4(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(f.name, self) + assert self.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.show() + # app.exec_() + + +def test_load_ui_into_custom_pyside(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + self.custom = load_ui(f.name) + assert self.custom.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.custom.show() + # app.exec_() + + +def test_load_ui_into_custom_pyqt4(): + """load widgets into self""" + + ui = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(suffix=".ui") as f: + f.write(ui) + f.seek(0) + + with pyqt4(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + self.custom = load_ui(f.name) + assert self.custom.pushButton + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + # window.custom.show() + # app.exec_() \ No newline at end of file From e07eaab3e368372edc0155bdab938b4df3f81811 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:08:02 +0200 Subject: [PATCH 17/59] Add distinguishable test descriptions --- tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests.py b/tests.py index aa199856..dbf9f853 100644 --- a/tests.py +++ b/tests.py @@ -191,7 +191,7 @@ def test_sip_api_already_set(): def test_load_ui_into_self_pyside(): - """load widgets into self""" + """load widgets into self using PySide""" ui = """\ @@ -249,7 +249,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_pyqt4(): - """load widgets into self""" + """load widgets into self using PyQt4""" ui = """\ @@ -307,7 +307,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load widgets into self""" + """load widgets into custom using PySide""" ui = """\ @@ -365,7 +365,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyqt4(): - """load widgets into self""" + """load widgets into custom using PyQt4""" ui = """\ From 1ef25b474d2e766e70b0115d13635ebbbe9de2e1 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:23:46 +0200 Subject: [PATCH 18/59] Add support for additional load_ui arguments --- Qt.py | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 17 deletions(-) diff --git a/Qt.py b/Qt.py index 5b970da2..23d6bba3 100644 --- a/Qt.py +++ b/Qt.py @@ -118,60 +118,184 @@ def _pyside(): return PySide -def pyside_load_ui(fname): +def pyside_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? Usage: >> from Qt import load_ui >> class MyWindow(QtWidgets.QWidget): .. fname = 'my_ui.ui' - .. self.ui = load_ui(fname) + .. load_ui(fname, self) .. >> window = MyWindow() - """ - - from PySide import QtUiTools - return QtUiTools.QUiLoader().load(fname) + Note: + This function is based on the gist: + https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + """ -def pyside2_load_ui(fname): + from PySide import QtUiTools, QtCore + + class UiLoader(QtUiTools.QUiLoader): + def __init__(self, base_instance, custom_widgets=None): + QtUiTools.QUiLoader.__init__(self, base_instance) + self.base_instance = base_instance + self.custom_widgets = custom_widgets + + def createWidget(self, class_name, parent=None, name=''): + if parent is None and self.base_instance: + return self.base_instance + else: + if class_name in self.availableWidgets(): + widget = QtUiTools.QUiLoader.createWidget(self, + class_name, + parent, name) + else: + try: + widget = self.custom_widgets[class_name](parent) + except (TypeError, KeyError): + raise Exception('No custom widget ' + + class_name + + ' found in custom_widgets' + + ' param of UiLoader __init__.') + if self.base_instance: + setattr(self.base_instance, name, widget) + return widget + + def loadUi(fname, base_instance=None, custom_widgets=None): + loader = UiLoader(base_instance, custom_widgets) + widget = loader.load(fname) + QtCore.QMetaObject.connectSlotsByName(widget) + return widget + + return loadUi(fname, base_instance=base_instance, + custom_widgets=custom_widgets) + + +def pyside2_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? - """ + Usage: + >> from Qt import load_ui + >> class MyWindow(QtWidgets.QWidget): + .. fname = 'my_ui.ui' + .. load_ui(fname, self) + .. + >> window = MyWindow() - from PySide2 import QtUiTools - return QtUiTools.QUiLoader().load(fname) + Note: + This function is based on the gist: + https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + """ -def pyqt4_load_ui(fname): + from PySide2 import QtUiTools, QtCore + + class UiLoader(QtUiTools.QUiLoader): + def __init__(self, base_instance, custom_widgets=None): + QtUiTools.QUiLoader.__init__(self, base_instance) + self.base_instance = base_instance + self.custom_widgets = custom_widgets + + def createWidget(self, class_name, parent=None, name=''): + if parent is None and self.base_instance: + return self.base_instance + else: + if class_name in self.availableWidgets(): + widget = QtUiTools.QUiLoader.createWidget(self, + class_name, + parent, name) + else: + try: + widget = self.custom_widgets[class_name](parent) + except (TypeError, KeyError): + raise Exception('No custom widget ' + + class_name + + ' found in custom_widgets' + + ' param of UiLoader __init__.') + if self.base_instance: + setattr(self.base_instance, name, widget) + return widget + + def loadUi(fname, base_instance=None, custom_widgets=None): + loader = UiLoader(base_instance, custom_widgets) + widget = loader.load(fname) + QtCore.QMetaObject.connectSlotsByName(widget) + return widget + + return loadUi(fname, base_instance=base_instance, + custom_widgets=custom_widgets) + + +def pyqt4_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? + + Usage: + >> from Qt import load_ui + >> class MyWindow(QtWidgets.QWidget): + .. fname = 'my_ui.ui' + .. load_ui(fname, self) + .. + >> window = MyWindow() """ from PyQt4 import uic - return uic.loadUi(fname) + + if isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname) + elif not isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname, base_instance) + else: + return uic.loadUi(fname, base_instance, custom_widgets) -def pyqt5_load_ui(fname): +def pyqt5_load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file + base_instance (widget): Optional instance of the Qt base class. + custom_widgets (widget): ? + + Usage: + >> from Qt import load_ui + >> class MyWindow(QtWidgets.QWidget): + .. fname = 'my_ui.ui' + .. load_ui(fname, self) + .. + >> window = MyWindow() """ from PyQt5 import uic - return uic.loadUi(fname) + + if isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname) + elif not isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname, base_instance) + else: + return uic.loadUi(fname, base_instance, custom_widgets) def _log(text, verbose): @@ -189,18 +313,18 @@ def _init(): this has executed. """ - + preferred = os.getenv("QT_PREFERRED_BINDING") verbose = os.getenv("QT_VERBOSE") is not None bindings = (_pyside2, _pyqt5, _pyside, _pyqt4) if preferred: - + # Internal flag (used in installer) if preferred == "None": sys.modules[__name__].__wrapper_version__ = __version__ return - + preferred = preferred.split(os.pathsep) available = { "PySide2": _pyside2, From 57b795668c38a84d0b4f72ee634e3454e3743ed1 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:32:58 +0200 Subject: [PATCH 19/59] Specify a non-binary mode --- tests.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests.py b/tests.py index dbf9f853..1a90b577 100644 --- a/tests.py +++ b/tests.py @@ -191,7 +191,7 @@ def test_sip_api_already_set(): def test_load_ui_into_self_pyside(): - """load widgets into self using PySide""" + """load_ui: Load widgets into self using PySide""" ui = """\ @@ -228,7 +228,7 @@ def test_load_ui_into_self_pyside(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) @@ -249,7 +249,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_pyqt4(): - """load widgets into self using PyQt4""" + """load_ui: Load widgets into self using PyQt4""" ui = """\ @@ -286,7 +286,7 @@ def test_load_ui_into_self_pyqt4(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) @@ -307,7 +307,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load widgets into custom using PySide""" + """load_ui: Load widgets into custom using PySide""" ui = """\ @@ -344,7 +344,7 @@ def test_load_ui_into_custom_pyside(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) @@ -365,7 +365,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyqt4(): - """load widgets into custom using PyQt4""" + """load_ui: Load widgets into custom using PyQt4""" ui = """\ @@ -402,7 +402,7 @@ def test_load_ui_into_custom_pyqt4(): """ - with tempfile.NamedTemporaryFile(suffix=".ui") as f: + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(ui) f.seek(0) From a440883c7b0180023bde6573f483ba32c7178c5f Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:36:23 +0200 Subject: [PATCH 20/59] Update with load_ui additional arguments info --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dfc3dcce..0a6f6aee 100644 --- a/README.md +++ b/README.md @@ -186,15 +186,15 @@ from Qt import load_ui class Hello(QtWidgets.QWidget): def __init__(self): super(Hello, self).__init__() - self.ui = load_ui('my_ui.ui') + load_ui('my_ui.ui', self) app = QtWidgets.QApplication(sys.argv) window = Hello() -window.ui.show() +window.show() sys.exit(app.exec_()) ``` -Please note, for maximum compatibility, only pass the argument of the filename to the `load_ui` function. +The `QUiLoader` class was modified to accept a base instance argument as well as custom widgets argument to become as compatible with `uic.loadUi` as possible. ##### sip API v2 From 231819d7222738cde00a615cc414bc8fd0a374fb Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:37:36 +0200 Subject: [PATCH 21/59] Remove QMenuBar and QStatusBar from UI --- tests.py | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/tests.py b/tests.py index 1a90b577..dc21d3f0 100644 --- a/tests.py +++ b/tests.py @@ -212,18 +212,6 @@ def test_load_ui_into_self_pyside(): - - - - 0 - 0 - 125 - 22 - - - - - """ @@ -270,18 +258,6 @@ def test_load_ui_into_self_pyqt4(): - - - - 0 - 0 - 125 - 22 - - - - - """ @@ -328,18 +304,6 @@ def test_load_ui_into_custom_pyside(): - - - - 0 - 0 - 125 - 22 - - - - - """ @@ -386,18 +350,6 @@ def test_load_ui_into_custom_pyqt4(): - - - - 0 - 0 - 125 - 22 - - - - - """ From b443923c7947bccfd86f527884bd6a57859d633d Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 01:37:36 +0200 Subject: [PATCH 22/59] Revert "Remove QMenuBar and QStatusBar from UI" This reverts commit c61b33d1f97982d12f7b2a49fc18f21b0fdd4d1b. --- tests.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests.py b/tests.py index dc21d3f0..1a90b577 100644 --- a/tests.py +++ b/tests.py @@ -212,6 +212,18 @@ def test_load_ui_into_self_pyside(): + + + + 0 + 0 + 125 + 22 + + + + + """ @@ -258,6 +270,18 @@ def test_load_ui_into_self_pyqt4(): + + + + 0 + 0 + 125 + 22 + + + + + """ @@ -304,6 +328,18 @@ def test_load_ui_into_custom_pyside(): + + + + 0 + 0 + 125 + 22 + + + + + """ @@ -350,6 +386,18 @@ def test_load_ui_into_custom_pyqt4(): + + + + 0 + 0 + 125 + 22 + + + + + """ From 41dbb76ba908b31e6422f8b0ec96aaf56af10dc9 Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Fri, 29 Jul 2016 07:51:06 +0100 Subject: [PATCH 23/59] Simplify tests --- tests.py | 273 ++++++++++++++++--------------------------------------- 1 file changed, 76 insertions(+), 197 deletions(-) diff --git a/tests.py b/tests.py index 1a90b577..65fa5a56 100644 --- a/tests.py +++ b/tests.py @@ -44,6 +44,50 @@ def pyside(): os.environ.pop("QT_PREFERRED_BINDING") +@contextlib.contextmanager +def ui(): + source = """\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + +""" + + with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: + f.write(source) + f.seek(0) + + yield f.name + + def test_environment(): """Tests require PySide and PyQt4 bindings to be installed""" @@ -179,244 +223,79 @@ def test_vendoring(): ) == 0 -if PYTHON == 2: - def test_sip_api_already_set(): - """Raise ImportError if sip API v1 was already set (Python 2.x only)""" - - with pyqt4(): - __import__("PyQt4.QtCore") # Bypass linter warning - import sip - sip.setapi("QString", 1) - assert_raises(ImportError, __import__, "Qt") - - def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) - + with ui() as fname: with pyside(): from Qt import QtWidgets, load_ui class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(f.name, self) - assert self.pushButton + load_ui(fname, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() - # window.show() - # app.exec_() + + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) - + with ui() as fname: with pyqt4(): from Qt import QtWidgets, load_ui class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(f.name, self) - assert self.pushButton + load_ui(fname, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() - # window.show() - # app.exec_() - -def test_load_ui_into_custom_pyside(): - """load_ui: Load widgets into custom using PySide""" + # Inherited from .ui file + assert hasattr(window, "pushButton") - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) +def test_load_ui_into_custom_pyside(): + """load_ui: Load .ui file""" + with ui() as fname: with pyside(): from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - self.custom = load_ui(f.name) - assert self.custom.pushButton - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - # window.custom.show() - # app.exec_() + widget = load_ui(fname) + + # From .ui file + assert hasattr(widget, "pushButton") def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - ui = """\ - - - MainWindow - - - MainWindow - - - - - - - PushButton - - - - - - - - - 0 - 0 - 125 - 22 - - - - - - - -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: - f.write(ui) - f.seek(0) - + with ui() as fname: with pyqt4(): from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) + + # From .ui file + assert hasattr(widget, "pushButton") - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - self.custom = load_ui(f.name) - assert self.custom.pushButton - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - # window.custom.show() - # app.exec_() \ No newline at end of file +if PYTHON == 2: + def test_sip_api_already_set(): + """Raise ImportError if sip API v1 was already set (Python 2.x only)""" + + with pyqt4(): + __import__("PyQt4.QtCore") # Bypass linter warning + import sip + sip.setapi("QString", 1) + assert_raises(ImportError, __import__, "Qt") + From 9f2532371bed155841286c5626ec69b6ccfc3b50 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 29 Jul 2016 09:26:28 +0200 Subject: [PATCH 24/59] Remove whitespace --- tests.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests.py b/tests.py index 65fa5a56..8ec9afd0 100644 --- a/tests.py +++ b/tests.py @@ -270,7 +270,7 @@ def test_load_ui_into_custom_pyside(): app = QtWidgets.QApplication(sys.argv) widget = load_ui(fname) - + # From .ui file assert hasattr(widget, "pushButton") @@ -284,7 +284,7 @@ def test_load_ui_into_custom_pyqt4(): app = QtWidgets.QApplication(sys.argv) widget = load_ui(fname) - + # From .ui file assert hasattr(widget, "pushButton") @@ -298,4 +298,3 @@ def test_sip_api_already_set(): import sip sip.setapi("QString", 1) assert_raises(ImportError, __import__, "Qt") - From 9c4f7d75862dc2daf30f5ce6045c99e149f74fd9 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 13:08:38 +0200 Subject: [PATCH 25/59] Add Xvfb --- Dockerfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7b8c470e..5e5d9740 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,8 @@ FROM ubuntu:14.04 RUN apt-get update && apt-get install -y \ python-qt4 \ python-pyside \ - python-pip + python-pip \ + xvfb # Nose is the Python test-runner RUN pip install nose nosepipe @@ -11,9 +12,14 @@ RUN pip install nose nosepipe # Enable additional output from Qt.py ENV QT_VERBOSE true +# Xvfb +ENV DISPLAY :99 + WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ + Xvfb :99 -screen 0 1024x768x16 & \ + sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ From 189c0ede7c4456287d10589c7f454111127ca574 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:35:55 +0200 Subject: [PATCH 26/59] Add xvfbwrapper --- Dockerfile | 9 ++---- tests.py | 90 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5e5d9740..2be7e6e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,21 +7,16 @@ RUN apt-get update && apt-get install -y \ xvfb # Nose is the Python test-runner -RUN pip install nose nosepipe +RUN pip install nose nosepipe xvfbwrapper # Enable additional output from Qt.py ENV QT_VERBOSE true -# Xvfb -ENV DISPLAY :99 - WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ - Xvfb :99 -screen 0 1024x768x16 & \ - sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ --with-doctest \ - --exe + --exe diff --git a/tests.py b/tests.py index 8ec9afd0..2f695e8e 100644 --- a/tests.py +++ b/tests.py @@ -44,6 +44,18 @@ def pyside(): os.environ.pop("QT_PREFERRED_BINDING") +@contextlib.contextmanager +def xvfb(): + from xvfbwrapper import Xvfb + + vdisplay = Xvfb() + vdisplay.start() + + yield + + # vdisplay.stop() + + @contextlib.contextmanager def ui(): source = """\ @@ -226,67 +238,71 @@ def test_vendoring(): def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_custom_pyside(): - """load_ui: Load .ui file""" + """load_ui: Load .ui file into custom using PySide""" - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with xvfb(): + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") if PYTHON == 2: From 9fb8646f79a9f5c53171630bad8d5b4e256aab21 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:42:21 +0200 Subject: [PATCH 27/59] Add xvfbwrapper to .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b06e60eb..067f78fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,8 @@ install: - sudo apt-get install -y qt4-qmake libqt4-dev - sudo apt-get install -y python-qt4 python-pyside - sudo apt-get install -y python3-pyqt4 python3-pyside python3-pip - - sudo pip install nosepipe - - sudo pip3 install nosepipe + - sudo pip install nosepipe xvfbwrapper + - sudo pip3 install nosepipe xvfbwrapper # Print version information - python -c "import PySide;print('Path, %s' % PySide)" From bfd9cae9a83a266a5117590ad8eb6ce0c40b9946 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:54:05 +0200 Subject: [PATCH 28/59] Add tests title consistency --- tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.py b/tests.py index 2f695e8e..f1ced779 100644 --- a/tests.py +++ b/tests.py @@ -276,7 +276,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load_ui: Load .ui file into custom using PySide""" + """load_ui: Load widgets into custom using PySide""" with xvfb(): with ui() as fname: From adf4c2bf065b800fd6c3bbc3e36da8debb85f0f1 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 14:42:21 +0200 Subject: [PATCH 29/59] Revert "Add xvfbwrapper to .travis.yml" This reverts commit 9636bf22a3fe7e04856542d99ddc342e5d3742b3. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 067f78fc..b06e60eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,8 @@ install: - sudo apt-get install -y qt4-qmake libqt4-dev - sudo apt-get install -y python-qt4 python-pyside - sudo apt-get install -y python3-pyqt4 python3-pyside python3-pip - - sudo pip install nosepipe xvfbwrapper - - sudo pip3 install nosepipe xvfbwrapper + - sudo pip install nosepipe + - sudo pip3 install nosepipe # Print version information - python -c "import PySide;print('Path, %s' % PySide)" From 0f4ee5ad293ac1567bb116a4306557f1b2ef1d77 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Sun, 31 Jul 2016 15:31:29 +0200 Subject: [PATCH 30/59] Remove xvfbwrapper --- Dockerfile | 9 ++++-- tests.py | 88 ++++++++++++++++++++++-------------------------------- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2be7e6e6..5e5d9740 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,16 +7,21 @@ RUN apt-get update && apt-get install -y \ xvfb # Nose is the Python test-runner -RUN pip install nose nosepipe xvfbwrapper +RUN pip install nose nosepipe # Enable additional output from Qt.py ENV QT_VERBOSE true +# Xvfb +ENV DISPLAY :99 + WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ + Xvfb :99 -screen 0 1024x768x16 & \ + sleep 3 && \ nosetests \ --verbose \ --with-process-isolation \ --with-doctest \ - --exe + --exe diff --git a/tests.py b/tests.py index f1ced779..4d217f49 100644 --- a/tests.py +++ b/tests.py @@ -44,18 +44,6 @@ def pyside(): os.environ.pop("QT_PREFERRED_BINDING") -@contextlib.contextmanager -def xvfb(): - from xvfbwrapper import Xvfb - - vdisplay = Xvfb() - vdisplay.start() - - yield - - # vdisplay.stop() - - @contextlib.contextmanager def ui(): source = """\ @@ -238,71 +226,67 @@ def test_vendoring(): def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - with xvfb(): - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - with xvfb(): - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(fname, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") def test_load_ui_into_custom_pyside(): """load_ui: Load widgets into custom using PySide""" - with xvfb(): - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyside(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - with xvfb(): - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with ui() as fname: + with pyqt4(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(fname) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") if PYTHON == 2: From 69bb70f6b50f673e07b5af8d3c6aa5fd44ecee13 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 1 Aug 2016 09:27:16 +0200 Subject: [PATCH 31/59] Change UI template into QWidget --- tests.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests.py b/tests.py index 1c801ff2..68f6a8e5 100644 --- a/tests.py +++ b/tests.py @@ -49,37 +49,37 @@ def ui(): source = """\ - MainWindow - + Form + + + + 0 + 0 + 400 + 300 + + - MainWindow + Form - - - - - - PushButton - - - - - - + - 0 - 0 - 125 - 22 + 140 + 120 + 90 + 27 + + PushButton + - -""" + +""" with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: f.write(source) From fe4889cd1527518cccfdd86849b19c0b23e26ec5 Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Mon, 1 Aug 2016 08:51:29 +0100 Subject: [PATCH 32/59] Squelch xvfb messages --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5e5d9740..a34cbe68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ ENV DISPLAY :99 WORKDIR /workspace/Qt.py ENTRYPOINT cp -r /Qt.py /workspace && \ python build_caveats_tests.py && \ - Xvfb :99 -screen 0 1024x768x16 & \ + Xvfb :99 -screen 0 1024x768x16 2>/dev/null & \ sleep 3 && \ nosetests \ --verbose \ From cd656b7963cff9905c3dd6732e0f2d3d8988dfc7 Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Mon, 1 Aug 2016 08:52:29 +0100 Subject: [PATCH 33/59] - Remove duplicate definition from prior merge - Replace with ui() - Replace .iu file encoding with unicode - Bypass linter warnings on unused variable `app` --- tests.py | 185 +++++++++++++++++-------------------------------------- 1 file changed, 58 insertions(+), 127 deletions(-) diff --git a/tests.py b/tests.py index 68f6a8e5..1e7d50dd 100644 --- a/tests.py +++ b/tests.py @@ -6,6 +6,7 @@ """ import os +import io import sys import imp import shutil @@ -24,29 +25,9 @@ def setup(): self.tempdir = tempfile.mkdtemp() + self.ui = os.path.join(self.tempdir, "temp.ui") - -def teardown(): - shutil.rmtree(self.tempdir) - - -@contextlib.contextmanager -def pyqt4(): - os.environ["QT_PREFERRED_BINDING"] = "PyQt4" - yield - os.environ.pop("QT_PREFERRED_BINDING") - - -@contextlib.contextmanager -def pyside(): - os.environ["QT_PREFERRED_BINDING"] = "PySide" - yield - os.environ.pop("QT_PREFERRED_BINDING") - - -@contextlib.contextmanager -def ui(): - source = """\ + source = u"""\ Form @@ -79,13 +60,29 @@ def ui(): + """ - with tempfile.NamedTemporaryFile(mode='w', suffix='.ui') as f: + with io.open(self.ui, "w", encoding="utf-8") as f: f.write(source) - f.seek(0) - yield f.name + +def teardown(): + shutil.rmtree(self.tempdir) + + +@contextlib.contextmanager +def pyqt4(): + os.environ["QT_PREFERRED_BINDING"] = "PyQt4" + yield + os.environ.pop("QT_PREFERRED_BINDING") + + +@contextlib.contextmanager +def pyside(): + os.environ["QT_PREFERRED_BINDING"] = "PySide" + yield + os.environ.pop("QT_PREFERRED_BINDING") def test_environment(): @@ -226,133 +223,67 @@ def test_vendoring(): def test_load_ui_into_self_pyside(): """load_ui: Load widgets into self using PySide""" - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui - - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) - - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - - # Inherited from .ui file - assert hasattr(window, "pushButton") - - -def test_load_ui_into_self_pyqt4(): - """load_ui: Load widgets into self using PyQt4""" - - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui - - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) - - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - - # Inherited from .ui file - assert hasattr(window, "pushButton") - - -def test_load_ui_into_custom_pyside(): - """load_ui: Load widgets into custom using PySide""" - - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui - - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) - - # From .ui file - assert hasattr(widget, "pushButton") - - -def test_load_ui_into_custom_pyqt4(): - """load_ui: Load widgets into custom using PyQt4""" - - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui - - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) - - # From .ui file - assert hasattr(widget, "pushButton") - - -def test_load_ui_into_self_pyside(): - """load_ui: Load widgets into self using PySide""" - - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with pyside(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(sys.modules[__name__].ui, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") + app.exit() def test_load_ui_into_self_pyqt4(): """load_ui: Load widgets into self using PyQt4""" - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with pyqt4(): + from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(fname, self) + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(sys.modules[__name__].ui, self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() - # Inherited from .ui file - assert hasattr(window, "pushButton") + # Inherited from .ui file + assert hasattr(window, "pushButton") + app.exit() def test_load_ui_into_custom_pyside(): """load_ui: Load widgets into custom using PySide""" - with ui() as fname: - with pyside(): - from Qt import QtWidgets, load_ui + with pyside(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(sys.modules[__name__].ui) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") + app.exit() def test_load_ui_into_custom_pyqt4(): """load_ui: Load widgets into custom using PyQt4""" - with ui() as fname: - with pyqt4(): - from Qt import QtWidgets, load_ui + with pyqt4(): + from Qt import QtWidgets, load_ui - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(fname) + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(sys.modules[__name__].ui) - # From .ui file - assert hasattr(widget, "pushButton") + # From .ui file + assert hasattr(widget, "pushButton") + app.exit() if PYTHON == 2: From b17413ec3fafe6ee5171fa099bf4e0c58dc59394 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 1 Aug 2016 19:32:56 +0200 Subject: [PATCH 34/59] Add docstring fixes --- Qt.py | 60 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/Qt.py b/Qt.py index 23d6bba3..e755114c 100644 --- a/Qt.py +++ b/Qt.py @@ -123,16 +123,17 @@ def pyside_load_ui(fname, base_instance=None, custom_widgets=None): Args: fname (str): Absolute path to .ui file - base_instance (widget): Optional instance of the Qt base class. + base_instance (widget, optional): Instance of the Qt base class. custom_widgets (widget): ? Usage: - >> from Qt import load_ui - >> class MyWindow(QtWidgets.QWidget): - .. fname = 'my_ui.ui' - .. load_ui(fname, self) - .. - >> window = MyWindow() + >>> from Qt import load_ui + >>> class MyWindow(QtWidgets.QWidget): + ... def __init__(self, parent=None): + ... fname = 'my_ui.ui' + ... load_ui(fname, self) + ... + >>> window = MyWindow() Note: This function is based on the gist: @@ -183,16 +184,17 @@ def pyside2_load_ui(fname, base_instance=None, custom_widgets=None): Args: fname (str): Absolute path to .ui file - base_instance (widget): Optional instance of the Qt base class. + base_instance (widget, optional): Instance of the Qt base class. custom_widgets (widget): ? Usage: - >> from Qt import load_ui - >> class MyWindow(QtWidgets.QWidget): - .. fname = 'my_ui.ui' - .. load_ui(fname, self) - .. - >> window = MyWindow() + >>> from Qt import load_ui + >>> class MyWindow(QtWidgets.QWidget): + ... def __init__(self, parent=None): + ... fname = 'my_ui.ui' + ... load_ui(fname, self) + ... + >>> window = MyWindow() Note: This function is based on the gist: @@ -243,16 +245,17 @@ def pyqt4_load_ui(fname, base_instance=None, custom_widgets=None): Args: fname (str): Absolute path to .ui file - base_instance (widget): Optional instance of the Qt base class. + base_instance (widget, optional): Instance of the Qt base class. custom_widgets (widget): ? Usage: - >> from Qt import load_ui - >> class MyWindow(QtWidgets.QWidget): - .. fname = 'my_ui.ui' - .. load_ui(fname, self) - .. - >> window = MyWindow() + >>> from Qt import load_ui + >>> class MyWindow(QtWidgets.QWidget): + ... def __init__(self, parent=None): + ... fname = 'my_ui.ui' + ... load_ui(fname, self) + ... + >>> window = MyWindow() """ @@ -273,16 +276,17 @@ def pyqt5_load_ui(fname, base_instance=None, custom_widgets=None): Args: fname (str): Absolute path to .ui file - base_instance (widget): Optional instance of the Qt base class. + base_instance (widget, optional): Instance of the Qt base class. custom_widgets (widget): ? Usage: - >> from Qt import load_ui - >> class MyWindow(QtWidgets.QWidget): - .. fname = 'my_ui.ui' - .. load_ui(fname, self) - .. - >> window = MyWindow() + >>> from Qt import load_ui + >>> class MyWindow(QtWidgets.QWidget): + ... def __init__(self, parent=None): + ... fname = 'my_ui.ui' + ... load_ui(fname, self) + ... + >>> window = MyWindow() """ From 1d363a69e69dd6a323a636beac6f353678430a5c Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 1 Aug 2016 21:12:25 +0200 Subject: [PATCH 35/59] Avoid defining UiLoader class twice --- Qt.py | 121 ++++++++++++++++------------------------------------------ 1 file changed, 33 insertions(+), 88 deletions(-) diff --git a/Qt.py b/Qt.py index e755114c..40550890 100644 --- a/Qt.py +++ b/Qt.py @@ -92,7 +92,7 @@ def _pyside2(): PySide2.__binding__ = "PySide2" PySide2.__binding_version__ = PySide2.__version__ PySide2.__qt_version__ = PySide2.QtCore.qVersion() - PySide2.load_ui = pyside2_load_ui + PySide2.load_ui = pyside_load_ui_wrap(version=2) return PySide2 @@ -113,35 +113,16 @@ def _pyside(): PySide.__binding__ = "PySide" PySide.__binding_version__ = PySide.__version__ PySide.__qt_version__ = PySide.QtCore.qVersion() - PySide.load_ui = pyside_load_ui + PySide.load_ui = pyside_load_ui_wrap(version=1) return PySide -def pyside_load_ui(fname, base_instance=None, custom_widgets=None): - """Read Qt Designer .ui `fname` - - Args: - fname (str): Absolute path to .ui file - base_instance (widget, optional): Instance of the Qt base class. - custom_widgets (widget): ? - - Usage: - >>> from Qt import load_ui - >>> class MyWindow(QtWidgets.QWidget): - ... def __init__(self, parent=None): - ... fname = 'my_ui.ui' - ... load_ui(fname, self) - ... - >>> window = MyWindow() - - Note: - This function is based on the gist: - https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 - - """ - - from PySide import QtUiTools, QtCore +def pyside_load_ui_wrap(version): + if version == 1: + from PySide import QtUiTools, QtCore + elif version == 2: + from PySide2 import QtUiTools, QtCore class UiLoader(QtUiTools.QUiLoader): def __init__(self, base_instance, custom_widgets=None): @@ -169,75 +150,39 @@ def createWidget(self, class_name, parent=None, name=''): setattr(self.base_instance, name, widget) return widget - def loadUi(fname, base_instance=None, custom_widgets=None): - loader = UiLoader(base_instance, custom_widgets) - widget = loader.load(fname) - QtCore.QMetaObject.connectSlotsByName(widget) - return widget - - return loadUi(fname, base_instance=base_instance, - custom_widgets=custom_widgets) - - -def pyside2_load_ui(fname, base_instance=None, custom_widgets=None): - """Read Qt Designer .ui `fname` - - Args: - fname (str): Absolute path to .ui file - base_instance (widget, optional): Instance of the Qt base class. - custom_widgets (widget): ? - - Usage: - >>> from Qt import load_ui - >>> class MyWindow(QtWidgets.QWidget): - ... def __init__(self, parent=None): - ... fname = 'my_ui.ui' - ... load_ui(fname, self) - ... - >>> window = MyWindow() + def pyside_load_ui(fname, base_instance=None, custom_widgets=None): + """Read Qt Designer .ui `fname` - Note: - This function is based on the gist: - https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + Args: + fname (str): Absolute path to .ui file + base_instance (widget, optional): Instance of the Qt base class. + custom_widgets (widget): ? - """ + Usage: + >>> from Qt import load_ui + >>> class MyWindow(QtWidgets.QWidget): + ... def __init__(self, parent=None): + ... fname = 'my_ui.ui' + ... load_ui(fname, self) + ... + >>> window = MyWindow() - from PySide2 import QtUiTools, QtCore + Note: + This function is based on the gist: + https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 - class UiLoader(QtUiTools.QUiLoader): - def __init__(self, base_instance, custom_widgets=None): - QtUiTools.QUiLoader.__init__(self, base_instance) - self.base_instance = base_instance - self.custom_widgets = custom_widgets + """ - def createWidget(self, class_name, parent=None, name=''): - if parent is None and self.base_instance: - return self.base_instance - else: - if class_name in self.availableWidgets(): - widget = QtUiTools.QUiLoader.createWidget(self, - class_name, - parent, name) - else: - try: - widget = self.custom_widgets[class_name](parent) - except (TypeError, KeyError): - raise Exception('No custom widget ' + - class_name + - ' found in custom_widgets' + - ' param of UiLoader __init__.') - if self.base_instance: - setattr(self.base_instance, name, widget) - return widget + def loadUi(fname, base_instance=None, custom_widgets=None): + loader = UiLoader(base_instance, custom_widgets) + widget = loader.load(fname) + QtCore.QMetaObject.connectSlotsByName(widget) + return widget - def loadUi(fname, base_instance=None, custom_widgets=None): - loader = UiLoader(base_instance, custom_widgets) - widget = loader.load(fname) - QtCore.QMetaObject.connectSlotsByName(widget) - return widget + return loadUi(fname, base_instance=base_instance, + custom_widgets=custom_widgets) - return loadUi(fname, base_instance=base_instance, - custom_widgets=custom_widgets) + return pyside_load_ui def pyqt4_load_ui(fname, base_instance=None, custom_widgets=None): From cb6d835bdb9a0c16d1f9c2fd4019b75d47d3811d Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 1 Aug 2016 21:15:39 +0200 Subject: [PATCH 36/59] Avoid doctest of pyside_load_ui --- Qt.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Qt.py b/Qt.py index 40550890..3ce9ccaa 100644 --- a/Qt.py +++ b/Qt.py @@ -159,13 +159,12 @@ def pyside_load_ui(fname, base_instance=None, custom_widgets=None): custom_widgets (widget): ? Usage: - >>> from Qt import load_ui - >>> class MyWindow(QtWidgets.QWidget): - ... def __init__(self, parent=None): - ... fname = 'my_ui.ui' - ... load_ui(fname, self) - ... - >>> window = MyWindow() + from Qt import load_ui + class MyWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + fname = 'my_ui.ui' + load_ui(fname, self) + window = MyWindow() Note: This function is based on the gist: From ee30a6d759968fdba82e09e63f78496138a74a1d Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 1 Aug 2016 21:22:42 +0200 Subject: [PATCH 37/59] Fix docstrings for load_ui: PyQt4/PyQt5 --- Qt.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Qt.py b/Qt.py index 3ce9ccaa..f55e189b 100644 --- a/Qt.py +++ b/Qt.py @@ -193,13 +193,12 @@ def pyqt4_load_ui(fname, base_instance=None, custom_widgets=None): custom_widgets (widget): ? Usage: - >>> from Qt import load_ui - >>> class MyWindow(QtWidgets.QWidget): - ... def __init__(self, parent=None): - ... fname = 'my_ui.ui' - ... load_ui(fname, self) - ... - >>> window = MyWindow() + from Qt import load_ui + class MyWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + fname = 'my_ui.ui' + load_ui(fname, self) + window = MyWindow() """ @@ -224,13 +223,12 @@ def pyqt5_load_ui(fname, base_instance=None, custom_widgets=None): custom_widgets (widget): ? Usage: - >>> from Qt import load_ui - >>> class MyWindow(QtWidgets.QWidget): - ... def __init__(self, parent=None): - ... fname = 'my_ui.ui' - ... load_ui(fname, self) - ... - >>> window = MyWindow() + from Qt import load_ui + class MyWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + fname = 'my_ui.ui' + load_ui(fname, self) + window = MyWindow() """ From c231100774a4e2797ecfe1f35e527cf2d5e7f94b Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 1 Aug 2016 21:24:50 +0200 Subject: [PATCH 38/59] Add consistency on load_ui wrap functions --- Qt.py | 84 +++++++++++++++++++++-------------------------------------- 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/Qt.py b/Qt.py index f55e189b..d8bdc243 100644 --- a/Qt.py +++ b/Qt.py @@ -37,7 +37,7 @@ def _pyqt5(): PyQt5.__binding__ = "PyQt5" PyQt5.__binding_version__ = PyQt5.QtCore.PYQT_VERSION_STR PyQt5.__qt_version__ = PyQt5.QtCore.QT_VERSION_STR - PyQt5.load_ui = pyqt5_load_ui + PyQt5.load_ui = pyqt_load_ui_wrap(version=5) return PyQt5 @@ -75,7 +75,7 @@ def _pyqt4(): PyQt4.__binding__ = "PyQt4" PyQt4.__binding_version__ = PyQt4.QtCore.PYQT_VERSION_STR PyQt4.__qt_version__ = PyQt4.QtCore.QT_VERSION_STR - PyQt4.load_ui = pyqt4_load_ui + PyQt4.load_ui = pyqt_load_ui_wrap(version=4) return PyQt4 @@ -184,64 +184,40 @@ def loadUi(fname, base_instance=None, custom_widgets=None): return pyside_load_ui -def pyqt4_load_ui(fname, base_instance=None, custom_widgets=None): - """Read Qt Designer .ui `fname` +def pyqt_load_ui_wrap(version): + if version == 4: + from PyQt4 import uic + elif version == 5: + from PyQt5 import uic - Args: - fname (str): Absolute path to .ui file - base_instance (widget, optional): Instance of the Qt base class. - custom_widgets (widget): ? - - Usage: - from Qt import load_ui - class MyWindow(QtWidgets.QWidget): - def __init__(self, parent=None): - fname = 'my_ui.ui' - load_ui(fname, self) - window = MyWindow() - - """ - - from PyQt4 import uic - - if isinstance(base_instance, type(None)) and \ - isinstance(custom_widgets, type(None)): - return uic.loadUi(fname) - elif not isinstance(base_instance, type(None)) and \ - isinstance(custom_widgets, type(None)): - return uic.loadUi(fname, base_instance) - else: - return uic.loadUi(fname, base_instance, custom_widgets) - - -def pyqt5_load_ui(fname, base_instance=None, custom_widgets=None): - """Read Qt Designer .ui `fname` + def pyqt_load_ui(fname, base_instance=None, custom_widgets=None): + """Read Qt Designer .ui `fname` - Args: - fname (str): Absolute path to .ui file - base_instance (widget, optional): Instance of the Qt base class. - custom_widgets (widget): ? + Args: + fname (str): Absolute path to .ui file + base_instance (widget, optional): Instance of the Qt base class. + custom_widgets (widget): ? - Usage: - from Qt import load_ui - class MyWindow(QtWidgets.QWidget): - def __init__(self, parent=None): - fname = 'my_ui.ui' - load_ui(fname, self) - window = MyWindow() + Usage: + from Qt import load_ui + class MyWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + fname = 'my_ui.ui' + load_ui(fname, self) + window = MyWindow() - """ + """ - from PyQt5 import uic + if isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname) + elif not isinstance(base_instance, type(None)) and \ + isinstance(custom_widgets, type(None)): + return uic.loadUi(fname, base_instance) + else: + return uic.loadUi(fname, base_instance, custom_widgets) - if isinstance(base_instance, type(None)) and \ - isinstance(custom_widgets, type(None)): - return uic.loadUi(fname) - elif not isinstance(base_instance, type(None)) and \ - isinstance(custom_widgets, type(None)): - return uic.loadUi(fname, base_instance) - else: - return uic.loadUi(fname, base_instance, custom_widgets) + return pyqt_load_ui def _log(text, verbose): From b4ed32881061fcd28504f5d3c7c4c54f8a86d12c Mon Sep 17 00:00:00 2001 From: Marcus Ottosson Date: Mon, 1 Aug 2016 21:13:00 +0100 Subject: [PATCH 39/59] - Alternative UiLoader implementation - Remove custom_widgets argument from load_ui (not tested or understood enough) --- Qt.py | 115 ++++++++++++++++++++++++++-------------------------------- 1 file changed, 52 insertions(+), 63 deletions(-) diff --git a/Qt.py b/Qt.py index d8bdc243..1fa3d5d7 100644 --- a/Qt.py +++ b/Qt.py @@ -26,6 +26,7 @@ def _pyqt5(): import PyQt5.Qt + from PyQt5 import uic # Remap PyQt5.QtCore.Signal = PyQt5.QtCore.pyqtSignal @@ -37,7 +38,7 @@ def _pyqt5(): PyQt5.__binding__ = "PyQt5" PyQt5.__binding_version__ = PyQt5.QtCore.PYQT_VERSION_STR PyQt5.__qt_version__ = PyQt5.QtCore.QT_VERSION_STR - PyQt5.load_ui = pyqt_load_ui_wrap(version=5) + PyQt5.load_ui = _pyqt_load_ui_factory(uic) return PyQt5 @@ -61,6 +62,7 @@ def _pyqt4(): raise ImportError import PyQt4.Qt + from PyQt4 import uic # Remap PyQt4.QtWidgets = PyQt4.QtGui @@ -75,14 +77,14 @@ def _pyqt4(): PyQt4.__binding__ = "PyQt4" PyQt4.__binding_version__ = PyQt4.QtCore.PYQT_VERSION_STR PyQt4.__qt_version__ = PyQt4.QtCore.QT_VERSION_STR - PyQt4.load_ui = pyqt_load_ui_wrap(version=4) + PyQt4.load_ui = _pyqt_load_ui_factory(uic) return PyQt4 def _pyside2(): import PySide2 - from PySide2 import QtGui, QtCore + from PySide2 import QtGui, QtCore, QtUiTools # Remap QtCore.QStringListModel = QtGui.QStringListModel @@ -92,14 +94,14 @@ def _pyside2(): PySide2.__binding__ = "PySide2" PySide2.__binding_version__ = PySide2.__version__ PySide2.__qt_version__ = PySide2.QtCore.qVersion() - PySide2.load_ui = pyside_load_ui_wrap(version=2) + PySide2.load_ui = _pyside_load_ui_factory(QtUiTools.QUiLoader) return PySide2 def _pyside(): import PySide - from PySide import QtGui, QtCore + from PySide import QtGui, QtCore, QtUiTools # Remap PySide.QtWidgets = QtGui @@ -113,50 +115,50 @@ def _pyside(): PySide.__binding__ = "PySide" PySide.__binding_version__ = PySide.__version__ PySide.__qt_version__ = PySide.QtCore.qVersion() - PySide.load_ui = pyside_load_ui_wrap(version=1) + PySide.load_ui = _pyside_load_ui_factory(QtUiTools.QUiLoader) return PySide -def pyside_load_ui_wrap(version): - if version == 1: - from PySide import QtUiTools, QtCore - elif version == 2: - from PySide2 import QtUiTools, QtCore +def _pyside_load_ui_factory(superclass): + """load_ui factory function for PySide and PySide2 - class UiLoader(QtUiTools.QUiLoader): - def __init__(self, base_instance, custom_widgets=None): - QtUiTools.QUiLoader.__init__(self, base_instance) + Produce a load_ui function using the provided superclass + + """ + + class UiLoader(superclass): + """PyQt port of loadUi + + Based on: https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + + """ + + def __init__(self, base_instance): + super(UiLoader, self).__init__(base_instance) self.base_instance = base_instance - self.custom_widgets = custom_widgets - def createWidget(self, class_name, parent=None, name=''): + def createWidget(self, class_name, parent=None, name=""): if parent is None and self.base_instance: return self.base_instance - else: - if class_name in self.availableWidgets(): - widget = QtUiTools.QUiLoader.createWidget(self, - class_name, - parent, name) - else: - try: - widget = self.custom_widgets[class_name](parent) - except (TypeError, KeyError): - raise Exception('No custom widget ' + - class_name + - ' found in custom_widgets' + - ' param of UiLoader __init__.') - if self.base_instance: - setattr(self.base_instance, name, widget) - return widget - - def pyside_load_ui(fname, base_instance=None, custom_widgets=None): + + if class_name not in self.availableWidgets(): + raise Exception("\"%s\" not available." % class_name) + + widget = super(UiLoader, self).createWidget( + class_name, parent, name) + + if self.base_instance: + setattr(self.base_instance, name, widget) + + return widget + + def load_ui(fname, base_instance=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file base_instance (widget, optional): Instance of the Qt base class. - custom_widgets (widget): ? Usage: from Qt import load_ui @@ -166,37 +168,31 @@ def __init__(self, parent=None): load_ui(fname, self) window = MyWindow() - Note: - This function is based on the gist: - https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 - """ - def loadUi(fname, base_instance=None, custom_widgets=None): - loader = UiLoader(base_instance, custom_widgets) - widget = loader.load(fname) - QtCore.QMetaObject.connectSlotsByName(widget) - return widget + from Qt import QtCore + + loader = UiLoader(base_instance) + widget = loader.load(fname) + QtCore.QMetaObject.connectSlotsByName(widget) + return widget - return loadUi(fname, base_instance=base_instance, - custom_widgets=custom_widgets) + return load_ui - return pyside_load_ui +def _pyqt_load_ui_factory(uic): + """load_ui factory function for PyQt4 and PyQt5 -def pyqt_load_ui_wrap(version): - if version == 4: - from PyQt4 import uic - elif version == 5: - from PyQt5 import uic + Produce a load_ui function using the provided module + + """ - def pyqt_load_ui(fname, base_instance=None, custom_widgets=None): + def load_ui(fname, base_instance=None): """Read Qt Designer .ui `fname` Args: fname (str): Absolute path to .ui file base_instance (widget, optional): Instance of the Qt base class. - custom_widgets (widget): ? Usage: from Qt import load_ui @@ -208,16 +204,9 @@ def __init__(self, parent=None): """ - if isinstance(base_instance, type(None)) and \ - isinstance(custom_widgets, type(None)): - return uic.loadUi(fname) - elif not isinstance(base_instance, type(None)) and \ - isinstance(custom_widgets, type(None)): - return uic.loadUi(fname, base_instance) - else: - return uic.loadUi(fname, base_instance, custom_widgets) + return uic.loadUi(fname, base_instance) - return pyqt_load_ui + return load_ui def _log(text, verbose): From 2de9542c59dbdbdd2e098b377d5b5a570bb3584b Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Tue, 2 Aug 2016 16:44:54 +0200 Subject: [PATCH 40/59] Revert change UI template into QWidget --- tests.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/tests.py b/tests.py index 1e7d50dd..44e295ec 100644 --- a/tests.py +++ b/tests.py @@ -30,32 +30,33 @@ def setup(): source = u"""\ - Form - - - - 0 - 0 - 400 - 300 - - + MainWindow + - Form + MainWindow - + + + + + + PushButton + + + + + + - 140 - 120 - 90 - 27 + 0 + 0 + 125 + 22 - - PushButton - + From 58b7e4e9eba2d51e99f276285146cec9c18e32ec Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Tue, 2 Aug 2016 16:47:21 +0200 Subject: [PATCH 41/59] Add comments --- Qt.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Qt.py b/Qt.py index 1fa3d5d7..1214c13d 100644 --- a/Qt.py +++ b/Qt.py @@ -128,29 +128,53 @@ def _pyside_load_ui_factory(superclass): """ class UiLoader(superclass): - """PyQt port of loadUi + """PyQt port of uic.loadUi Based on: https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 """ def __init__(self, base_instance): + """Create a loader for the given ``baseinstance``. + + The user interface is created in ``baseinstance``, which + must be an instance of the top-level class in the user + interface to load, or a subclass thereof. + + ``parent`` is the parent object of this loader. + + """ + super(UiLoader, self).__init__(base_instance) self.base_instance = base_instance def createWidget(self, class_name, parent=None, name=""): + """Function that is called for each widget defined in ui + file, overridden here to populate baseinstance instead. + + """ + if parent is None and self.base_instance: + # supposed to create the top-level widget, return the + # base instance instead return self.base_instance if class_name not in self.availableWidgets(): raise Exception("\"%s\" not available." % class_name) + # create a new widget for child widgets widget = super(UiLoader, self).createWidget( class_name, parent, name) if self.base_instance: + # set an attribute for the new child widget on the base + # instance, just like PyQt4.uic.loadUi does. setattr(self.base_instance, name, widget) + # this outputs the various widget names, e.g. + # sampleGraphicsView, dockWidget, samplesTableView etc. + # print(name) + return widget def load_ui(fname, base_instance=None): From 049213a864c2d38010d0ad7869a1ab73c7a47856 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Thu, 4 Aug 2016 12:45:06 +0200 Subject: [PATCH 42/59] Add tests for widget parent and class --- tests.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tests.py b/tests.py index 44e295ec..6b001101 100644 --- a/tests.py +++ b/tests.py @@ -25,9 +25,9 @@ def setup(): self.tempdir = tempfile.mkdtemp() - self.ui = os.path.join(self.tempdir, "temp.ui") + self.ui_valid = os.path.join(self.tempdir, "valid.ui") - source = u"""\ + source_valid = u"""\ MainWindow @@ -64,8 +64,8 @@ def setup(): """ - with io.open(self.ui, "w", encoding="utf-8") as f: - f.write(source) + with io.open(self.ui_valid, "w", encoding="utf-8") as f: + f.write(source_valid) def teardown(): @@ -230,13 +230,16 @@ def test_load_ui_into_self_pyside(): class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui, self) + load_ui(sys.modules[__name__].ui_valid, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() # Inherited from .ui file assert hasattr(window, "pushButton") + assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.pushButton.__class__, type(QtWidgets.QWidget)) app.exit() @@ -249,13 +252,16 @@ def test_load_ui_into_self_pyqt4(): class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui, self) + load_ui(sys.modules[__name__].ui_valid, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() # Inherited from .ui file assert hasattr(window, "pushButton") + assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.pushButton.__class__, type(QtWidgets.QWidget)) app.exit() @@ -266,10 +272,13 @@ def test_load_ui_into_custom_pyside(): from Qt import QtWidgets, load_ui app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui) + widget = load_ui(sys.modules[__name__].ui_valid) # From .ui file assert hasattr(widget, "pushButton") + assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(widget.parent(), type(None)) + assert isinstance(widget.pushButton.__class__, type(QtWidgets.QWidget)) app.exit() @@ -280,10 +289,13 @@ def test_load_ui_into_custom_pyqt4(): from Qt import QtWidgets, load_ui app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui) + widget = load_ui(sys.modules[__name__].ui_valid) # From .ui file assert hasattr(widget, "pushButton") + assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(widget.parent(), type(None)) + assert isinstance(widget.pushButton.__class__, type(QtWidgets.QWidget)) app.exit() From 8929b637498f49c3f526af588ecaa62b60eaab9e Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Thu, 4 Aug 2016 12:49:04 +0200 Subject: [PATCH 43/59] Add test for missing/invalid widget class --- tests.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests.py b/tests.py index 6b001101..bb9f3c3d 100644 --- a/tests.py +++ b/tests.py @@ -67,6 +67,48 @@ def setup(): with io.open(self.ui_valid, "w", encoding="utf-8") as f: f.write(source_valid) + self.ui_invalid_class = os.path.join(self.tempdir, "invalid.ui") + + source_invalid_class = u"""\ + + + MainWindow + + + MainWindow + + + + + + + PushButton + + + + + + + + + 0 + 0 + 125 + 22 + + + + + + + + + +""" + + with io.open(self.ui_invalid_class, "w", encoding="utf-8") as f: + f.write(source_invalid_class) + def teardown(): shutil.rmtree(self.tempdir) @@ -299,6 +341,37 @@ def test_load_ui_into_custom_pyqt4(): app.exit() +def test_load_ui_invalid_class_name(): + """load_ui: Invalid class name in .ui + + ('Name:', u'MainWindow', 'Class:', u'QMainWindow', 'Parent:', None) + ('Name:', u'centralwidget', 'Class:', u'QWidget', + 'Parent:', ) + ('Name:', u'pushButton', 'Class:', u'QPushButton', + 'Parent:', ) + ('Name:', u'menubar', 'Class:', u'QMenuBar', + 'Parent:', ) + ('Name:', u'statusbar', 'Class:', u'QStatusBar', + 'Parent:', ) + + """ + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + # load_ui(sys.modules[__name__].ui_invalid_class, self) + assert_raises(Exception, + load_ui, sys.modules[__name__].ui_invalid_class, + self) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + app.exit() + + if PYTHON == 2: def test_sip_api_already_set(): """Raise ImportError if sip API v1 was already set (Python 2.x only)""" From 4cf72945e719586dbb0af1e09a38dbc3691c69a4 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 21:37:21 +0200 Subject: [PATCH 44/59] Remove class --- tests.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/tests.py b/tests.py index bb9f3c3d..a6a0b73f 100644 --- a/tests.py +++ b/tests.py @@ -344,32 +344,15 @@ def test_load_ui_into_custom_pyqt4(): def test_load_ui_invalid_class_name(): """load_ui: Invalid class name in .ui - ('Name:', u'MainWindow', 'Class:', u'QMainWindow', 'Parent:', None) - ('Name:', u'centralwidget', 'Class:', u'QWidget', - 'Parent:', ) - ('Name:', u'pushButton', 'Class:', u'QPushButton', - 'Parent:', ) - ('Name:', u'menubar', 'Class:', u'QMenuBar', - 'Parent:', ) - ('Name:', u'statusbar', 'Class:', u'QStatusBar', - 'Parent:', ) - """ with pyside(): from Qt import QtWidgets, load_ui - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - # load_ui(sys.modules[__name__].ui_invalid_class, self) - assert_raises(Exception, - load_ui, sys.modules[__name__].ui_invalid_class, - self) - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - app.exit() + assert_raises(Exception, + load_ui, + sys.modules[__name__].ui_invalid_class) if PYTHON == 2: From 7dd4d6c56cf2d1dbc708f73b0b67445a2f958988 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 21:56:26 +0200 Subject: [PATCH 45/59] Comment out test with invalid class --- tests.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests.py b/tests.py index a6a0b73f..aa29c039 100644 --- a/tests.py +++ b/tests.py @@ -341,18 +341,18 @@ def test_load_ui_into_custom_pyqt4(): app.exit() -def test_load_ui_invalid_class_name(): - """load_ui: Invalid class name in .ui - - """ - - with pyside(): - from Qt import QtWidgets, load_ui - - app = QtWidgets.QApplication(sys.argv) - assert_raises(Exception, - load_ui, - sys.modules[__name__].ui_invalid_class) +# def test_load_ui_invalid_class_name(): +# """load_ui: Invalid class name in .ui +# +# """ +# +# with pyside(): +# from Qt import QtWidgets, load_ui +# +# app = QtWidgets.QApplication(sys.argv) +# assert_raises(Exception, +# load_ui, +# sys.modules[__name__].ui_invalid_class) if PYTHON == 2: From 99b1e994c3324587e9d61798774a7236b1605bbd Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 22:42:46 +0200 Subject: [PATCH 46/59] Add custom widgets support and tests --- Qt.py | 52 +++++++++++---------- tests.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 144 insertions(+), 45 deletions(-) diff --git a/Qt.py b/Qt.py index 9b3c771d..0065decb 100644 --- a/Qt.py +++ b/Qt.py @@ -135,19 +135,26 @@ class UiLoader(superclass): """ - def __init__(self, base_instance): + def __init__(self, base_instance, customWidgets=None): """Create a loader for the given ``baseinstance``. The user interface is created in ``baseinstance``, which must be an instance of the top-level class in the user interface to load, or a subclass thereof. + ``customWidgets`` is a dictionary mapping from class name to + class object for widgets that you've promoted in the + Qt Designer interface. Usually, this should be done by + calling registerCustomWidget on the QUiLoader, but with + PySide 1.1.2 on Ubuntu 12.04 x86_64 this causes a segfault. + ``parent`` is the parent object of this loader. """ super(UiLoader, self).__init__(base_instance) self.base_instance = base_instance + self.customWidgets = customWidgets def createWidget(self, class_name, parent=None, name=""): """Function that is called for each widget defined in ui @@ -159,26 +166,25 @@ def createWidget(self, class_name, parent=None, name=""): # supposed to create the top-level widget, return the # base instance instead return self.base_instance - - if class_name not in self.availableWidgets(): - raise Exception("\"%s\" not available." % class_name) - - # create a new widget for child widgets - widget = super(UiLoader, self).createWidget( - class_name, parent, name) - - if self.base_instance: - # set an attribute for the new child widget on the base - # instance, just like PyQt4.uic.loadUi does. - setattr(self.base_instance, name, widget) - - # this outputs the various widget names, e.g. - # sampleGraphicsView, dockWidget, samplesTableView etc. - # print(name) - - return widget - - def load_ui(fname, base_instance=None): + else: + if class_name in self.availableWidgets(): + # create a new widget for child widgets + widget = super(UiLoader, self).createWidget( + class_name, parent, name) + else: + try: + widget = self.customWidgets[class_name](parent) + except (TypeError, KeyError): + raise Exception("\"%s\" not available." % class_name) + + if self.base_instance: + # set an attribute for the new child widget on the base + # instance, just like PyQt4.uic.loadUi does. + setattr(self.base_instance, name, widget) + + return widget + + def load_ui(fname, base_instance=None, customWidgets=None): """Read Qt Designer .ui `fname` Args: @@ -197,7 +203,7 @@ def __init__(self, parent=None): from Qt import QtCore - loader = UiLoader(base_instance) + loader = UiLoader(base_instance, customWidgets) widget = loader.load(fname) QtCore.QMetaObject.connectSlotsByName(widget) return widget @@ -212,7 +218,7 @@ def _pyqt_load_ui_factory(uic): """ - def load_ui(fname, base_instance=None): + def load_ui(fname, base_instance=None, *args): """Read Qt Designer .ui `fname` Args: diff --git a/tests.py b/tests.py index aa29c039..db3b2220 100644 --- a/tests.py +++ b/tests.py @@ -25,9 +25,9 @@ def setup(): self.tempdir = tempfile.mkdtemp() - self.ui_valid = os.path.join(self.tempdir, "valid.ui") + self.ui_simple = os.path.join(self.tempdir, "simple.ui") - source_valid = u"""\ + source_simple = u"""\ MainWindow @@ -64,12 +64,12 @@ def setup(): """ - with io.open(self.ui_valid, "w", encoding="utf-8") as f: - f.write(source_valid) + with io.open(self.ui_simple, "w", encoding="utf-8") as f: + f.write(source_simple) - self.ui_invalid_class = os.path.join(self.tempdir, "invalid.ui") + self.ui_custom_pyqt = os.path.join(self.tempdir, "custom_widget.ui") - source_invalid_class = u"""\ + source_custom_pyqt = u"""\ MainWindow @@ -78,15 +78,19 @@ def setup(): MainWindow - - - - - PushButton - - - - + + + + 10 + 10 + 113 + 32 + + + + PushButton + + @@ -100,14 +104,21 @@ def setup(): + + + MyCustomWidget + QPushButton +
MyCustomClasses
+
+
""" - with io.open(self.ui_invalid_class, "w", encoding="utf-8") as f: - f.write(source_invalid_class) + with io.open(self.ui_custom_pyqt, "w", encoding="utf-8") as f: + f.write(source_custom_pyqt) def teardown(): @@ -272,7 +283,7 @@ def test_load_ui_into_self_pyside(): class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui_valid, self) + load_ui(sys.modules[__name__].ui_simple, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() @@ -282,6 +293,7 @@ def __init__(self, parent=None): assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) assert isinstance(window.parent(), type(None)) assert isinstance(window.pushButton.__class__, type(QtWidgets.QWidget)) + app.exit() @@ -294,16 +306,17 @@ def test_load_ui_into_self_pyqt4(): class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui_valid, self) + load_ui(sys.modules[__name__].ui_simple, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() - # Inherited from .ui file + # From .ui file assert hasattr(window, "pushButton") assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) assert isinstance(window.parent(), type(None)) assert isinstance(window.pushButton.__class__, type(QtWidgets.QWidget)) + app.exit() @@ -314,13 +327,14 @@ def test_load_ui_into_custom_pyside(): from Qt import QtWidgets, load_ui app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui_valid) + widget = load_ui(sys.modules[__name__].ui_simple) # From .ui file assert hasattr(widget, "pushButton") assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) assert isinstance(widget.parent(), type(None)) assert isinstance(widget.pushButton.__class__, type(QtWidgets.QWidget)) + app.exit() @@ -331,13 +345,14 @@ def test_load_ui_into_custom_pyqt4(): from Qt import QtWidgets, load_ui app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui_valid) + widget = load_ui(sys.modules[__name__].ui_simple) # From .ui file assert hasattr(widget, "pushButton") assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) assert isinstance(widget.parent(), type(None)) assert isinstance(widget.pushButton.__class__, type(QtWidgets.QWidget)) + app.exit() @@ -355,6 +370,84 @@ def test_load_ui_into_custom_pyqt4(): # sys.modules[__name__].ui_invalid_class) +def test_load_ui_custom_widget_pyside(): + """load_ui: Load custom widget into self using PySide""" + + with pyside(): + from Qt import QtWidgets, load_ui + import sys + + class MyCustomClasses(object): + class MyCustomWidget(QtWidgets.QPushButton): + def __init__(self, *args): + QtWidgets.QPushButton.__init__(self, *args) + custom_attribute = True + + # PySide + custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} + + # PyQt + sys.modules['MyCustomClasses'] = MyCustomClasses + + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(sys.modules[__name__].ui_custom_pyqt, + self, + custom_widget_map) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + + # From .ui file + assert hasattr(window, "customWidget") + assert hasattr(window.customWidget, "custom_attribute") + assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.customWidget.__class__, type(QtWidgets.QWidget)) + + app.exit() + + +def test_load_ui_custom_widget_pyqt(): + """load_ui: Load custom widget into self using PyQt4""" + + with pyqt4(): + from Qt import QtWidgets, load_ui + import sys + + class MyCustomClasses(object): + class MyCustomWidget(QtWidgets.QPushButton): + def __init__(self, *args): + QtWidgets.QPushButton.__init__(self, *args) + custom_attribute = True + + # PySide + custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} + + # PyQt + sys.modules['MyCustomClasses'] = MyCustomClasses + + class MainWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + QtWidgets.QMainWindow.__init__(self, parent) + load_ui(sys.modules[__name__].ui_custom_pyqt, + self, + custom_widget_map) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + + # From .ui file + assert hasattr(window, "customWidget") + assert hasattr(window.customWidget, "custom_attribute") + assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.customWidget.__class__, type(QtWidgets.QWidget)) + + app.exit() + + if PYTHON == 2: def test_sip_api_already_set(): """Raise ImportError if sip API v1 was already set (Python 2.x only)""" From 0ea392ab6701ed6641ed7a72f90f3f54496b8112 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 22:55:33 +0200 Subject: [PATCH 47/59] Add consistency --- Qt.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Qt.py b/Qt.py index 0065decb..4a75b03e 100644 --- a/Qt.py +++ b/Qt.py @@ -135,14 +135,14 @@ class UiLoader(superclass): """ - def __init__(self, base_instance, customWidgets=None): + def __init__(self, base_instance, custom_widgets=None): """Create a loader for the given ``baseinstance``. The user interface is created in ``baseinstance``, which must be an instance of the top-level class in the user interface to load, or a subclass thereof. - ``customWidgets`` is a dictionary mapping from class name to + ``custom_widgets`` is a dictionary mapping from class name to class object for widgets that you've promoted in the Qt Designer interface. Usually, this should be done by calling registerCustomWidget on the QUiLoader, but with @@ -154,7 +154,7 @@ class object for widgets that you've promoted in the super(UiLoader, self).__init__(base_instance) self.base_instance = base_instance - self.customWidgets = customWidgets + self.custom_widgets = custom_widgets def createWidget(self, class_name, parent=None, name=""): """Function that is called for each widget defined in ui @@ -173,7 +173,7 @@ def createWidget(self, class_name, parent=None, name=""): class_name, parent, name) else: try: - widget = self.customWidgets[class_name](parent) + widget = self.custom_widgets[class_name](parent) except (TypeError, KeyError): raise Exception("\"%s\" not available." % class_name) @@ -184,7 +184,7 @@ def createWidget(self, class_name, parent=None, name=""): return widget - def load_ui(fname, base_instance=None, customWidgets=None): + def load_ui(fname, base_instance=None, custom_widgets=None): """Read Qt Designer .ui `fname` Args: @@ -203,7 +203,7 @@ def __init__(self, parent=None): from Qt import QtCore - loader = UiLoader(base_instance, customWidgets) + loader = UiLoader(base_instance, custom_widgets) widget = loader.load(fname) QtCore.QMetaObject.connectSlotsByName(widget) return widget From 87c7dae65ab6f996b46a101739547f56dabb38a5 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 23:01:46 +0200 Subject: [PATCH 48/59] Add more tests for custom widgets --- Qt.py | 2 +- tests.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/Qt.py b/Qt.py index 4a75b03e..46ba9f34 100644 --- a/Qt.py +++ b/Qt.py @@ -218,7 +218,7 @@ def _pyqt_load_ui_factory(uic): """ - def load_ui(fname, base_instance=None, *args): + def load_ui(fname, base_instance=None, *args, **kwargs): """Read Qt Designer .ui `fname` Args: diff --git a/tests.py b/tests.py index db3b2220..0668bcd8 100644 --- a/tests.py +++ b/tests.py @@ -409,7 +409,7 @@ def __init__(self, parent=None): app.exit() -def test_load_ui_custom_widget_pyqt(): +def test_load_ui_custom_widget_pyqt4(): """load_ui: Load custom widget into self using PyQt4""" with pyqt4(): @@ -448,6 +448,73 @@ def __init__(self, parent=None): app.exit() +def test_load_ui_custom_widget_custom_pyside(): + """load_ui: Load custom widget into custom using PySide""" + + with pyside(): + from Qt import QtWidgets, load_ui + import sys + + class MyCustomClasses(object): + class MyCustomWidget(QtWidgets.QPushButton): + def __init__(self, *args): + QtWidgets.QPushButton.__init__(self, *args) + custom_attribute = True + + # PySide + custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} + + # PyQt + sys.modules['MyCustomClasses'] = MyCustomClasses + + + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(sys.modules[__name__].ui_custom_pyqt, + custom_widgets=custom_widget_map) + + # From .ui file + assert hasattr(widget, "customWidget") + assert hasattr(widget.customWidget, "custom_attribute") + assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(widget.parent(), type(None)) + assert isinstance(widget.customWidget.__class__, type(QtWidgets.QWidget)) + + app.exit() + + +def test_load_ui_custom_widget_custom_pyqt4(): + """load_ui: Load custom widget into custom using PyQt4""" + + with pyqt4(): + from Qt import QtWidgets, load_ui + import sys + + class MyCustomClasses(object): + class MyCustomWidget(QtWidgets.QPushButton): + def __init__(self, *args): + QtWidgets.QPushButton.__init__(self, *args) + custom_attribute = True + + # PySide + custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} + + # PyQt + sys.modules['MyCustomClasses'] = MyCustomClasses + + app = QtWidgets.QApplication(sys.argv) + widget = load_ui(sys.modules[__name__].ui_custom_pyqt, + custom_widgets=custom_widget_map) + + # From .ui file + assert hasattr(widget, "customWidget") + assert hasattr(widget.customWidget, "custom_attribute") + assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) + assert isinstance(widget.parent(), type(None)) + assert isinstance(widget.customWidget.__class__, type(QtWidgets.QWidget)) + + app.exit() + + if PYTHON == 2: def test_sip_api_already_set(): """Raise ImportError if sip API v1 was already set (Python 2.x only)""" From 7bfb97e5dd066908bf2571e4f656132760fc4d26 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 23:17:40 +0200 Subject: [PATCH 49/59] Remove whitespace and fix linting issues --- tests.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests.py b/tests.py index 0668bcd8..b5acef5e 100644 --- a/tests.py +++ b/tests.py @@ -404,7 +404,8 @@ def __init__(self, parent=None): assert hasattr(window.customWidget, "custom_attribute") assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) assert isinstance(window.parent(), type(None)) - assert isinstance(window.customWidget.__class__, type(QtWidgets.QWidget)) + assert isinstance(window.customWidget.__class__, + type(QtWidgets.QWidget)) app.exit() @@ -443,7 +444,8 @@ def __init__(self, parent=None): assert hasattr(window.customWidget, "custom_attribute") assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) assert isinstance(window.parent(), type(None)) - assert isinstance(window.customWidget.__class__, type(QtWidgets.QWidget)) + assert isinstance(window.customWidget.__class__, + type(QtWidgets.QWidget)) app.exit() @@ -467,7 +469,6 @@ def __init__(self, *args): # PyQt sys.modules['MyCustomClasses'] = MyCustomClasses - app = QtWidgets.QApplication(sys.argv) widget = load_ui(sys.modules[__name__].ui_custom_pyqt, custom_widgets=custom_widget_map) @@ -477,7 +478,8 @@ def __init__(self, *args): assert hasattr(widget.customWidget, "custom_attribute") assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) assert isinstance(widget.parent(), type(None)) - assert isinstance(widget.customWidget.__class__, type(QtWidgets.QWidget)) + assert isinstance(widget.customWidget.__class__, + type(QtWidgets.QWidget)) app.exit() @@ -510,7 +512,8 @@ def __init__(self, *args): assert hasattr(widget.customWidget, "custom_attribute") assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) assert isinstance(widget.parent(), type(None)) - assert isinstance(widget.customWidget.__class__, type(QtWidgets.QWidget)) + assert isinstance(widget.customWidget.__class__, + type(QtWidgets.QWidget)) app.exit() From 3daabe95bff0e09454595899543a3ab1d75c4e70 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Mon, 8 Aug 2016 23:38:27 +0200 Subject: [PATCH 50/59] Retry appveyor PyQt4 download --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 224a2e9f..454d9968 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -22,7 +22,7 @@ install: # the registry key. - REG ADD HKCU\Software\Python\PythonCore\2.7\InstallPath /f /ve /t REG_SZ /d %PYTHON% - - ps: (new-object net.webclient).DownloadFile($env:PYQT, "C:\install-PyQt4.exe") + - ps: do{sleep 5;(New-Object Net.WebClient).DownloadFile($env:PYQT, "C:\install-PyQt4.exe")}while(!$?) - ps: Start-Process -FilePath C:\install-PyQt4.exe -ArgumentList "/S" -Wait -Passthru # Test install From 55f464fd02efa5e88bbb03a009852574e08691ff Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Tue, 9 Aug 2016 07:07:52 +0200 Subject: [PATCH 51/59] Remove custom widgets support --- Qt.py | 47 +++++++---------- README.md | 2 +- tests.py | 150 +----------------------------------------------------- 3 files changed, 20 insertions(+), 179 deletions(-) diff --git a/Qt.py b/Qt.py index 46ba9f34..ee0d3914 100644 --- a/Qt.py +++ b/Qt.py @@ -135,26 +135,19 @@ class UiLoader(superclass): """ - def __init__(self, base_instance, custom_widgets=None): + def __init__(self, base_instance): """Create a loader for the given ``baseinstance``. The user interface is created in ``baseinstance``, which must be an instance of the top-level class in the user interface to load, or a subclass thereof. - ``custom_widgets`` is a dictionary mapping from class name to - class object for widgets that you've promoted in the - Qt Designer interface. Usually, this should be done by - calling registerCustomWidget on the QUiLoader, but with - PySide 1.1.2 on Ubuntu 12.04 x86_64 this causes a segfault. - ``parent`` is the parent object of this loader. """ super(UiLoader, self).__init__(base_instance) self.base_instance = base_instance - self.custom_widgets = custom_widgets def createWidget(self, class_name, parent=None, name=""): """Function that is called for each widget defined in ui @@ -166,25 +159,21 @@ def createWidget(self, class_name, parent=None, name=""): # supposed to create the top-level widget, return the # base instance instead return self.base_instance - else: - if class_name in self.availableWidgets(): - # create a new widget for child widgets - widget = super(UiLoader, self).createWidget( - class_name, parent, name) - else: - try: - widget = self.custom_widgets[class_name](parent) - except (TypeError, KeyError): - raise Exception("\"%s\" not available." % class_name) - - if self.base_instance: - # set an attribute for the new child widget on the base - # instance, just like PyQt4.uic.loadUi does. - setattr(self.base_instance, name, widget) - - return widget - - def load_ui(fname, base_instance=None, custom_widgets=None): + + if class_name not in self.availableWidgets(): + raise Exception("\"%s\" not available." % class_name) + + widget = super(UiLoader, self).createWidget( + class_name, parent, name) + + if self.base_instance: + # set an attribute for the new child widget on the base + # instance, just like PyQt4.uic.loadUi does. + setattr(self.base_instance, name, widget) + + return widget + + def load_ui(fname, base_instance=None): """Read Qt Designer .ui `fname` Args: @@ -203,7 +192,7 @@ def __init__(self, parent=None): from Qt import QtCore - loader = UiLoader(base_instance, custom_widgets) + loader = UiLoader(base_instance) widget = loader.load(fname) QtCore.QMetaObject.connectSlotsByName(widget) return widget @@ -218,7 +207,7 @@ def _pyqt_load_ui_factory(uic): """ - def load_ui(fname, base_instance=None, *args, **kwargs): + def load_ui(fname, base_instance=None): """Read Qt Designer .ui `fname` Args: diff --git a/README.md b/README.md index cf7620ad..551fc9af 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ ui.show() app.exec_() ``` -The `QUiLoader` class was modified to accept a base instance argument as well as custom widgets argument to become as compatible with `uic.loadUi` as possible. +The `QUiLoader` class was modified to accept a base instance argument as well, to be more compatible with `uic.loadUi` as possible. ##### sip API v2 diff --git a/tests.py b/tests.py index b5acef5e..105519b6 100644 --- a/tests.py +++ b/tests.py @@ -367,155 +367,7 @@ def test_load_ui_into_custom_pyqt4(): # app = QtWidgets.QApplication(sys.argv) # assert_raises(Exception, # load_ui, -# sys.modules[__name__].ui_invalid_class) - - -def test_load_ui_custom_widget_pyside(): - """load_ui: Load custom widget into self using PySide""" - - with pyside(): - from Qt import QtWidgets, load_ui - import sys - - class MyCustomClasses(object): - class MyCustomWidget(QtWidgets.QPushButton): - def __init__(self, *args): - QtWidgets.QPushButton.__init__(self, *args) - custom_attribute = True - - # PySide - custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} - - # PyQt - sys.modules['MyCustomClasses'] = MyCustomClasses - - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui_custom_pyqt, - self, - custom_widget_map) - - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - - # From .ui file - assert hasattr(window, "customWidget") - assert hasattr(window.customWidget, "custom_attribute") - assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) - assert isinstance(window.parent(), type(None)) - assert isinstance(window.customWidget.__class__, - type(QtWidgets.QWidget)) - - app.exit() - - -def test_load_ui_custom_widget_pyqt4(): - """load_ui: Load custom widget into self using PyQt4""" - - with pyqt4(): - from Qt import QtWidgets, load_ui - import sys - - class MyCustomClasses(object): - class MyCustomWidget(QtWidgets.QPushButton): - def __init__(self, *args): - QtWidgets.QPushButton.__init__(self, *args) - custom_attribute = True - - # PySide - custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} - - # PyQt - sys.modules['MyCustomClasses'] = MyCustomClasses - - class MainWindow(QtWidgets.QMainWindow): - def __init__(self, parent=None): - QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui_custom_pyqt, - self, - custom_widget_map) - - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - - # From .ui file - assert hasattr(window, "customWidget") - assert hasattr(window.customWidget, "custom_attribute") - assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) - assert isinstance(window.parent(), type(None)) - assert isinstance(window.customWidget.__class__, - type(QtWidgets.QWidget)) - - app.exit() - - -def test_load_ui_custom_widget_custom_pyside(): - """load_ui: Load custom widget into custom using PySide""" - - with pyside(): - from Qt import QtWidgets, load_ui - import sys - - class MyCustomClasses(object): - class MyCustomWidget(QtWidgets.QPushButton): - def __init__(self, *args): - QtWidgets.QPushButton.__init__(self, *args) - custom_attribute = True - - # PySide - custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} - - # PyQt - sys.modules['MyCustomClasses'] = MyCustomClasses - - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui_custom_pyqt, - custom_widgets=custom_widget_map) - - # From .ui file - assert hasattr(widget, "customWidget") - assert hasattr(widget.customWidget, "custom_attribute") - assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) - assert isinstance(widget.parent(), type(None)) - assert isinstance(widget.customWidget.__class__, - type(QtWidgets.QWidget)) - - app.exit() - - -def test_load_ui_custom_widget_custom_pyqt4(): - """load_ui: Load custom widget into custom using PyQt4""" - - with pyqt4(): - from Qt import QtWidgets, load_ui - import sys - - class MyCustomClasses(object): - class MyCustomWidget(QtWidgets.QPushButton): - def __init__(self, *args): - QtWidgets.QPushButton.__init__(self, *args) - custom_attribute = True - - # PySide - custom_widget_map = {"MyCustomWidget": MyCustomClasses.MyCustomWidget} - - # PyQt - sys.modules['MyCustomClasses'] = MyCustomClasses - - app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui_custom_pyqt, - custom_widgets=custom_widget_map) - - # From .ui file - assert hasattr(widget, "customWidget") - assert hasattr(widget.customWidget, "custom_attribute") - assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) - assert isinstance(widget.parent(), type(None)) - assert isinstance(widget.customWidget.__class__, - type(QtWidgets.QWidget)) - - app.exit() +# sys.modules[__name__].ui_custom_pyqt) if PYTHON == 2: From f9abc92d4674952485c3f0cdedfe52105c98efc9 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Thu, 11 Aug 2016 18:45:49 +0200 Subject: [PATCH 52/59] Remove extension of UiLoader --- Qt.py | 71 +++++++++++++++-------------------------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) diff --git a/Qt.py b/Qt.py index ee0d3914..fb8f04e5 100644 --- a/Qt.py +++ b/Qt.py @@ -128,51 +128,6 @@ def _pyside_load_ui_factory(superclass): """ - class UiLoader(superclass): - """PyQt port of uic.loadUi - - Based on: https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 - - """ - - def __init__(self, base_instance): - """Create a loader for the given ``baseinstance``. - - The user interface is created in ``baseinstance``, which - must be an instance of the top-level class in the user - interface to load, or a subclass thereof. - - ``parent`` is the parent object of this loader. - - """ - - super(UiLoader, self).__init__(base_instance) - self.base_instance = base_instance - - def createWidget(self, class_name, parent=None, name=""): - """Function that is called for each widget defined in ui - file, overridden here to populate baseinstance instead. - - """ - - if parent is None and self.base_instance: - # supposed to create the top-level widget, return the - # base instance instead - return self.base_instance - - if class_name not in self.availableWidgets(): - raise Exception("\"%s\" not available." % class_name) - - widget = super(UiLoader, self).createWidget( - class_name, parent, name) - - if self.base_instance: - # set an attribute for the new child widget on the base - # instance, just like PyQt4.uic.loadUi does. - setattr(self.base_instance, name, widget) - - return widget - def load_ui(fname, base_instance=None): """Read Qt Designer .ui `fname` @@ -181,21 +136,30 @@ def load_ui(fname, base_instance=None): base_instance (widget, optional): Instance of the Qt base class. Usage: - from Qt import load_ui + from Qt import QtWidgets, load_ui class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None): fname = 'my_ui.ui' load_ui(fname, self) window = MyWindow() + window.show() """ - from Qt import QtCore - - loader = UiLoader(base_instance) - widget = loader.load(fname) - QtCore.QMetaObject.connectSlotsByName(widget) - return widget + immutable = ('__class__', '__dict__') + mutable = ('__delattr__', '__doc__', '__format__', '__getattribute__', + '__hash__', '__init__', '__new__', '__reduce__', + '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', + '__str__', '__subclasshook__') # For reference + + if base_instance: + ui = superclass().load(fname) + for member in dir(ui): + if member not in immutable: + setattr(base_instance, member, getattr(ui, member)) + return ui + else: + return superclass().load(fname) return load_ui @@ -215,12 +179,13 @@ def load_ui(fname, base_instance=None): base_instance (widget, optional): Instance of the Qt base class. Usage: - from Qt import load_ui + from Qt import QtWidgets, load_ui class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None): fname = 'my_ui.ui' load_ui(fname, self) window = MyWindow() + window.show() """ From a545edd481def3b1a45571d16cb3009064947bbd Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Thu, 11 Aug 2016 20:15:04 +0200 Subject: [PATCH 53/59] Add tests for QWidget, QDialog --- tests.py | 282 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 252 insertions(+), 30 deletions(-) diff --git a/tests.py b/tests.py index 105519b6..87eefa1c 100644 --- a/tests.py +++ b/tests.py @@ -25,24 +25,28 @@ def setup(): self.tempdir = tempfile.mkdtemp() - self.ui_simple = os.path.join(self.tempdir, "simple.ui") - source_simple = u"""\ + self.ui_qmainwindow = os.path.join(self.tempdir, "qmainwindow.ui") + source_qmainwindow = u"""\ MainWindow + + + 0 + 0 + 216 + 149 + + MainWindow - - - PushButton - - + @@ -51,7 +55,7 @@ def setup(): 0 0 - 125 + 216 22 @@ -63,12 +67,115 @@ def setup(): """ + with io.open(self.ui_qmainwindow, "w", encoding="utf-8") as f: + f.write(source_qmainwindow) + + self.ui_qwidget = os.path.join(self.tempdir, "qwidget.ui") + source_qwidget = u"""\ + + + Form + + + + 0 + 0 + 235 + 149 + + + + Form + + + + + + + + + + - with io.open(self.ui_simple, "w", encoding="utf-8") as f: - f.write(source_simple) +""" + with io.open(self.ui_qwidget, "w", encoding="utf-8") as f: + f.write(source_qwidget) - self.ui_custom_pyqt = os.path.join(self.tempdir, "custom_widget.ui") + self.ui_qdialog = os.path.join(self.tempdir, "qdialog.ui") + source_qdialog = u"""\ + + + Dialog + + + + 0 + 0 + 201 + 176 + + + + Dialog + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + +""" + with io.open(self.ui_qdialog, "w", encoding="utf-8") as f: + f.write(source_qdialog) + + self.ui_custom_pyqt = os.path.join(self.tempdir, "custom_widget.ui") source_custom_pyqt = u"""\ @@ -116,7 +223,6 @@ def setup(): """ - with io.open(self.ui_custom_pyqt, "w", encoding="utf-8") as f: f.write(source_custom_pyqt) @@ -274,8 +380,8 @@ def test_vendoring(): ) == 0 -def test_load_ui_into_self_pyside(): - """load_ui: Load widgets into self using PySide""" +def test_load_ui_into_self_qmainwindow_pyside(): + """load_ui: Load widgets into self (QMainWindow) using PySide""" with pyside(): from Qt import QtWidgets, load_ui @@ -283,22 +389,77 @@ def test_load_ui_into_self_pyside(): class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui_simple, self) + load_ui(sys.modules[__name__].ui_qmainwindow, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() # Inherited from .ui file - assert hasattr(window, "pushButton") + assert hasattr(window, "lineEdit") assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) assert isinstance(window.parent(), type(None)) - assert isinstance(window.pushButton.__class__, type(QtWidgets.QWidget)) + assert isinstance(window.lineEdit.__class__, type(QtWidgets.QWidget)) + assert window.lineEdit.text() == '' + window.lineEdit.setText('Hello') + assert window.lineEdit.text() == 'Hello' + + app.exit() + + +def test_load_ui_into_self_qwidget_pyside(): + """load_ui: Load widgets into self (QWidget) using PySide""" + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + QtWidgets.QWidget.__init__(self, parent) + load_ui(sys.modules[__name__].ui_qwidget, self) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + + # Inherited from .ui file + assert hasattr(window, "lineEdit") + assert isinstance(window.__class__, type(QtWidgets.QWidget)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.lineEdit.__class__, type(QtWidgets.QWidget)) + assert window.lineEdit.text() == '' + window.lineEdit.setText('Hello') + assert window.lineEdit.text() == 'Hello' + + app.exit() + + +def test_load_ui_into_self_qdialog_pyside(): + """load_ui: Load widgets into self (QDialog) using PySide""" + + with pyside(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QDialog): + def __init__(self, parent=None): + QtWidgets.QDialog.__init__(self, parent) + load_ui(sys.modules[__name__].ui_qdialog, self) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + + # Inherited from .ui file + assert hasattr(window, "lineEdit") + assert isinstance(window.__class__, type(QtWidgets.QDialog)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.lineEdit.__class__, type(QtWidgets.QWidget)) + assert window.lineEdit.text() == '' + window.lineEdit.setText('Hello') + assert window.lineEdit.text() == 'Hello' app.exit() -def test_load_ui_into_self_pyqt4(): - """load_ui: Load widgets into self using PyQt4""" +def test_load_ui_into_self_qmainwindow_pyqt4(): + """load_ui: Load widgets into self (QMainWindow) using PyQt4""" with pyqt4(): from Qt import QtWidgets, load_ui @@ -306,52 +467,113 @@ def test_load_ui_into_self_pyqt4(): class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): QtWidgets.QMainWindow.__init__(self, parent) - load_ui(sys.modules[__name__].ui_simple, self) + load_ui(sys.modules[__name__].ui_qmainwindow, self) app = QtWidgets.QApplication(sys.argv) window = MainWindow() # From .ui file - assert hasattr(window, "pushButton") + assert hasattr(window, "lineEdit") assert isinstance(window.__class__, type(QtWidgets.QMainWindow)) assert isinstance(window.parent(), type(None)) - assert isinstance(window.pushButton.__class__, type(QtWidgets.QWidget)) + assert isinstance(window.lineEdit.__class__, type(QtWidgets.QWidget)) + assert window.lineEdit.text() == '' + window.lineEdit.setText('Hello') + assert window.lineEdit.text() == 'Hello' + + app.exit() + + +def test_load_ui_into_self_qwidget_pyqt4(): + """load_ui: Load widgets into self (QWidget) using PyQt4""" + + with pyqt4(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QWidget): + def __init__(self, parent=None): + QtWidgets.QWidget.__init__(self, parent) + load_ui(sys.modules[__name__].ui_qwidget, self) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + + # From .ui file + assert hasattr(window, "lineEdit") + assert isinstance(window.__class__, type(QtWidgets.QWidget)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.lineEdit.__class__, type(QtWidgets.QWidget)) + assert window.lineEdit.text() == '' + window.lineEdit.setText('Hello') + assert window.lineEdit.text() == 'Hello' + + app.exit() + + +def test_load_ui_into_self_qdialog_pyqt4(): + """load_ui: Load widgets into self (QWidget) using PyQt4""" + + with pyqt4(): + from Qt import QtWidgets, load_ui + + class MainWindow(QtWidgets.QDialog): + def __init__(self, parent=None): + QtWidgets.QDialog.__init__(self, parent) + load_ui(sys.modules[__name__].ui_qdialog, self) + + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + + # From .ui file + assert hasattr(window, "lineEdit") + assert isinstance(window.__class__, type(QtWidgets.QDialog)) + assert isinstance(window.parent(), type(None)) + assert isinstance(window.lineEdit.__class__, type(QtWidgets.QWidget)) + assert window.lineEdit.text() == '' + window.lineEdit.setText('Hello') + assert window.lineEdit.text() == 'Hello' app.exit() def test_load_ui_into_custom_pyside(): - """load_ui: Load widgets into custom using PySide""" + """load_ui: Load widgets into widget (QMainWindow) using PySide""" with pyside(): from Qt import QtWidgets, load_ui app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui_simple) + widget = load_ui(sys.modules[__name__].ui_qmainwindow) # From .ui file - assert hasattr(widget, "pushButton") + assert hasattr(widget, "lineEdit") assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) assert isinstance(widget.parent(), type(None)) - assert isinstance(widget.pushButton.__class__, type(QtWidgets.QWidget)) + assert isinstance(widget.lineEdit.__class__, type(QtWidgets.QWidget)) + assert widget.lineEdit.text() == '' + widget.lineEdit.setText('Hello') + assert widget.lineEdit.text() == 'Hello' app.exit() def test_load_ui_into_custom_pyqt4(): - """load_ui: Load widgets into custom using PyQt4""" + """load_ui: Load widgets into widget (QMainWindow) using PyQt4""" with pyqt4(): from Qt import QtWidgets, load_ui app = QtWidgets.QApplication(sys.argv) - widget = load_ui(sys.modules[__name__].ui_simple) + widget = load_ui(sys.modules[__name__].ui_qmainwindow) # From .ui file - assert hasattr(widget, "pushButton") + assert hasattr(widget, "lineEdit") assert isinstance(widget.__class__, type(QtWidgets.QMainWindow)) assert isinstance(widget.parent(), type(None)) - assert isinstance(widget.pushButton.__class__, type(QtWidgets.QWidget)) + assert isinstance(widget.lineEdit.__class__, type(QtWidgets.QWidget)) + assert widget.lineEdit.text() == '' + widget.lineEdit.setText('Hello') + assert widget.lineEdit.text() == 'Hello' app.exit() From 61bcb86f9380ae4a1f6a5f282fea2ba642fd2330 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 12 Aug 2016 12:20:14 +0200 Subject: [PATCH 54/59] Add tests for signals --- tests.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests.py b/tests.py index 87eefa1c..ef61e157 100644 --- a/tests.py +++ b/tests.py @@ -578,6 +578,63 @@ def test_load_ui_into_custom_pyqt4(): app.exit() +def test_load_ui_connection_pyside(): + """load_ui: Signals in PySide""" + + with pyside(): + import sys + from Qt import QtWidgets, load_ui + + def setup_ui(base_instance=None): + return load_ui(sys.modules[__name__].ui_qmainwindow, base_instance) + + app = QtWidgets.QApplication(sys.argv) + Ui = type(setup_ui()) # Get the un-instantiated class + + class MyWidget(Ui): # Inherit from it. + def __init__(self, parent=None): + super(MyWidget, self).__init__(parent) + setup_ui(self) + self.x = False + self.lineEdit.textChanged.connect(self.some_method) + self.lineEdit.setText('hello') + assert self.x is True + + def some_method(self): + self.x = True + + my_widget = MyWidget() + my_widget.show() + + +def test_load_ui_connection_pyqt4(): + """load_ui: Signals in PyQt4""" + + with pyqt4(): + import sys + from Qt import QtWidgets, load_ui + + def setup_ui(base_instance=None): + return load_ui(sys.modules[__name__].ui_qmainwindow, base_instance) + + app = QtWidgets.QApplication(sys.argv) + Ui = type(setup_ui()) # Get the un-instantiated class + + class MyWidget(Ui): # Inherit from it. + def __init__(self, parent=None): + super(MyWidget, self).__init__(parent) + setup_ui(self) + self.x = False + self.lineEdit.textChanged.connect(self.some_method) + self.lineEdit.setText('hello') + assert self.x is True + + def some_method(self): + self.x = True + + my_widget = MyWidget() + my_widget.show() + # def test_load_ui_invalid_class_name(): # """load_ui: Invalid class name in .ui # From 5e5825b02a99a60b6a935bcd32d613d0920c079a Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 12 Aug 2016 12:21:53 +0200 Subject: [PATCH 55/59] Remove untestable test --- tests.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests.py b/tests.py index ef61e157..62f0a2e9 100644 --- a/tests.py +++ b/tests.py @@ -635,19 +635,6 @@ def some_method(self): my_widget = MyWidget() my_widget.show() -# def test_load_ui_invalid_class_name(): -# """load_ui: Invalid class name in .ui -# -# """ -# -# with pyside(): -# from Qt import QtWidgets, load_ui -# -# app = QtWidgets.QApplication(sys.argv) -# assert_raises(Exception, -# load_ui, -# sys.modules[__name__].ui_custom_pyqt) - if PYTHON == 2: def test_sip_api_already_set(): From a44a46680ff14297ab29fb7b153ad3820435321c Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 12 Aug 2016 12:22:56 +0200 Subject: [PATCH 56/59] Add better test descriptions --- tests.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests.py b/tests.py index 62f0a2e9..2c6bf674 100644 --- a/tests.py +++ b/tests.py @@ -381,7 +381,7 @@ def test_vendoring(): def test_load_ui_into_self_qmainwindow_pyside(): - """load_ui: Load widgets into self (QMainWindow) using PySide""" + """load_ui: Load UI into self (QMainWindow) using PySide""" with pyside(): from Qt import QtWidgets, load_ui @@ -407,7 +407,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_qwidget_pyside(): - """load_ui: Load widgets into self (QWidget) using PySide""" + """load_ui: Load UI into self (QWidget) using PySide""" with pyside(): from Qt import QtWidgets, load_ui @@ -433,7 +433,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_qdialog_pyside(): - """load_ui: Load widgets into self (QDialog) using PySide""" + """load_ui: Load UI into self (QDialog) using PySide""" with pyside(): from Qt import QtWidgets, load_ui @@ -459,7 +459,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_qmainwindow_pyqt4(): - """load_ui: Load widgets into self (QMainWindow) using PyQt4""" + """load_ui: Load UI into self (QMainWindow) using PyQt4""" with pyqt4(): from Qt import QtWidgets, load_ui @@ -485,7 +485,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_qwidget_pyqt4(): - """load_ui: Load widgets into self (QWidget) using PyQt4""" + """load_ui: Load UI into self (QWidget) using PyQt4""" with pyqt4(): from Qt import QtWidgets, load_ui @@ -511,7 +511,7 @@ def __init__(self, parent=None): def test_load_ui_into_self_qdialog_pyqt4(): - """load_ui: Load widgets into self (QWidget) using PyQt4""" + """load_ui: Load UI into self (QWidget) using PyQt4""" with pyqt4(): from Qt import QtWidgets, load_ui @@ -537,7 +537,7 @@ def __init__(self, parent=None): def test_load_ui_into_custom_pyside(): - """load_ui: Load widgets into widget (QMainWindow) using PySide""" + """load_ui: Load UI into widget (QMainWindow) using PySide""" with pyside(): from Qt import QtWidgets, load_ui @@ -558,7 +558,7 @@ def test_load_ui_into_custom_pyside(): def test_load_ui_into_custom_pyqt4(): - """load_ui: Load widgets into widget (QMainWindow) using PyQt4""" + """load_ui: Load UI into widget (QMainWindow) using PyQt4""" with pyqt4(): from Qt import QtWidgets, load_ui @@ -579,7 +579,7 @@ def test_load_ui_into_custom_pyqt4(): def test_load_ui_connection_pyside(): - """load_ui: Signals in PySide""" + """load_ui: Load UI into self, set up signals in PySide""" with pyside(): import sys @@ -608,7 +608,7 @@ def some_method(self): def test_load_ui_connection_pyqt4(): - """load_ui: Signals in PyQt4""" + """load_ui: Load UI into self, set up signals in PySide""" with pyqt4(): import sys From cfe625cbd88ff012d364293eb73dbe097f65e128 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 12 Aug 2016 12:36:22 +0200 Subject: [PATCH 57/59] Add complete usage examples --- Qt.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Qt.py b/Qt.py index fb8f04e5..b6ac4310 100644 --- a/Qt.py +++ b/Qt.py @@ -136,13 +136,17 @@ def load_ui(fname, base_instance=None): base_instance (widget, optional): Instance of the Qt base class. Usage: + import sys from Qt import QtWidgets, load_ui - class MyWindow(QtWidgets.QWidget): + class MyWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): + super(MyWindow, self).__init__(parent) fname = 'my_ui.ui' load_ui(fname, self) + app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() + app.exec_() """ @@ -179,13 +183,17 @@ def load_ui(fname, base_instance=None): base_instance (widget, optional): Instance of the Qt base class. Usage: + import sys from Qt import QtWidgets, load_ui - class MyWindow(QtWidgets.QWidget): + class MyWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): + super(MyWindow, self).__init__(parent) fname = 'my_ui.ui' load_ui(fname, self) + app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() + app.exec_() """ From 7c18457d80b8346b989a3e371ff149b4a06ffde5 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 12 Aug 2016 13:05:42 +0200 Subject: [PATCH 58/59] Skip attributes with double leading underscore --- Qt.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Qt.py b/Qt.py index b6ac4310..01adaba8 100644 --- a/Qt.py +++ b/Qt.py @@ -150,16 +150,10 @@ def __init__(self, parent=None): """ - immutable = ('__class__', '__dict__') - mutable = ('__delattr__', '__doc__', '__format__', '__getattribute__', - '__hash__', '__init__', '__new__', '__reduce__', - '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', - '__str__', '__subclasshook__') # For reference - if base_instance: ui = superclass().load(fname) for member in dir(ui): - if member not in immutable: + if not member.startswith('__'): setattr(base_instance, member, getattr(ui, member)) return ui else: From b3a3cf414f4e6f5fb7e124e98462f44599562cd9 Mon Sep 17 00:00:00 2001 From: Fredrik Averpil Date: Fri, 12 Aug 2016 13:08:50 +0200 Subject: [PATCH 59/59] Amend README, add example with second argument --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 551fc9af..de033824 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,22 @@ ui.show() app.exec_() ``` -The `QUiLoader` class was modified to accept a base instance argument as well, to be more compatible with `uic.loadUi` as possible. +This `load_ui` function accepts a second argument; the base instance into which the UI is loaded: + +```python +import sys +from Qt import QtWidgets, load_ui + +class MyWindow(QtWidgets.QMainWindow): + def __init__(self, parent=None): + super(MyWindow, self).__init__(parent) + load_ui("my.ui", self) + +app = QtWidgets.QApplication(sys.argv) +window = MyWindow() +window.show() +app.exec_() +``` ##### sip API v2