[pyzo] 43/68: Use (part of) qtpy.

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Wed Sep 28 09:47:12 UTC 2016


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/master
in repository pyzo.

commit ec010479c7996cb16157d7857780c259018e381f
Author: Almar Klein <almar.klein at gmail.com>
Date:   Mon Sep 19 13:47:40 2016 +0200

    Use (part of) qtpy.
---
 pyzo/util/qt/QtCore.py             |  47 ++++
 pyzo/util/qt/QtGui.py              | 103 +++++++++
 pyzo/util/qt/QtWidgets.py          | 122 ++++++++++
 pyzo/util/qt/__init__.py           | 463 +++++++++----------------------------
 pyzo/util/qt/_patch/__init__.py    |   0
 pyzo/util/qt/_patch/qcombobox.py   | 101 ++++++++
 pyzo/util/qt/_patch/qheaderview.py |  82 +++++++
 pyzo/util/qt/_version.py           |   2 +
 pyzo/util/qt/uic.py                | 223 ++++++++++++++++++
 9 files changed, 786 insertions(+), 357 deletions(-)

diff --git a/pyzo/util/qt/QtCore.py b/pyzo/util/qt/QtCore.py
new file mode 100644
index 0000000..bde48a7
--- /dev/null
+++ b/pyzo/util/qt/QtCore.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2014-2015 Colin Duquesnoy
+# Copyright © 2009- The Spyder Development Team
+#
+# Licensed under the terms of the MIT License
+# (see LICENSE.txt for details)
+
+"""
+Provides QtCore classes and functions.
+"""
+
+from . import PYQT5, PYQT4, PYSIDE, PythonQtError
+
+
+if PYQT5:
+    from PyQt5.QtCore import *
+    from PyQt5.QtCore import pyqtSignal as Signal
+    from PyQt5.QtCore import pyqtSlot as Slot
+    from PyQt5.QtCore import pyqtProperty as Property
+    from PyQt5.QtCore import QT_VERSION_STR as __version__
+
+    # Those are imported from `import *`
+    del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR
+elif PYQT4:
+    from PyQt4.QtCore import *
+    # Those are things we inherited from Spyder that fix crazy crashes under
+    # some specific situations. (See #34)
+    from PyQt4.QtCore import QCoreApplication
+    from PyQt4.QtCore import Qt
+    from PyQt4.QtCore import pyqtSignal as Signal
+    from PyQt4.QtCore import pyqtSlot as Slot
+    from PyQt4.QtCore import pyqtProperty as Property
+    from PyQt4.QtGui import (QItemSelection, QItemSelectionModel,
+                             QItemSelectionRange, QSortFilterProxyModel)
+    from PyQt4.QtCore import QT_VERSION_STR as __version__
+
+    # Those are imported from `import *`
+    del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR
+elif PYSIDE:
+    from PySide.QtCore import *
+    from PySide.QtGui import (QItemSelection, QItemSelectionModel,
+                              QItemSelectionRange, QSortFilterProxyModel)
+    import PySide.QtCore
+    __version__ = PySide.QtCore.__version__
+else:
+    raise PythonQtError('No Qt bindings could be found')
diff --git a/pyzo/util/qt/QtGui.py b/pyzo/util/qt/QtGui.py
new file mode 100644
index 0000000..99477c0
--- /dev/null
+++ b/pyzo/util/qt/QtGui.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2014-2015 Colin Duquesnoy
+# Copyright © 2009- The Spyder Development Team
+#
+# Licensed under the terms of the MIT License
+# (see LICENSE.txt for details)
+
+"""
+Provides QtGui classes and functions.
+.. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtGui are
+    exposed here. Therefore, you need to treat/use this package as if it were
+    the ``PyQt5.QtGui`` module.
+"""
+
+from . import PYQT5, PYQT4, PYSIDE, PythonQtError
+
+
+if PYQT5:
+    from PyQt5.QtGui import *
+elif PYQT4:
+    from PyQt4.Qt import QKeySequence, QTextCursor
+    from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap,
+                             QBrush, QClipboard, QCloseEvent, QColor,
+                             QConicalGradient, QContextMenuEvent, QCursor,
+                             QDesktopServices, QDoubleValidator, QDrag,
+                             QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent,
+                             QDropEvent, QFileOpenEvent, QFocusEvent, QFont,
+                             QFontDatabase, QFontInfo, QFontMetrics,
+                             QFontMetricsF, QGlyphRun, QGradient, QHelpEvent,
+                             QHideEvent, QHoverEvent, QIcon, QIconDragEvent,
+                             QIconEngine, QImage, QImageIOHandler, QImageReader,
+                             QImageWriter, QInputEvent, QInputMethodEvent,
+                             QKeyEvent, QLinearGradient,
+                             QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2,
+                             QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3,
+                             QMatrix4x4, QMouseEvent, QMoveEvent, QMovie,
+                             QPaintDevice, QPaintEngine, QPaintEngineState,
+                             QPaintEvent, QPainter, QPainterPath,
+                             QPainterPathStroker, QPalette, QPen, QPicture,
+                             QPictureIO, QPixmap, QPixmapCache, QPolygon,
+                             QPolygonF, QQuaternion, QRadialGradient, QRawFont,
+                             QRegExpValidator, QRegion, QResizeEvent,
+                             QSessionManager, QShortcutEvent, QShowEvent,
+                             QStandardItem, QStandardItemModel, QStaticText,
+                             QStatusTipEvent, QSyntaxHighlighter, QTabletEvent,
+                             QTextBlock, QTextBlockFormat, QTextBlockGroup,
+                             QTextBlockUserData, QTextCharFormat,
+                             QTextDocument, QTextDocumentFragment,
+                             QTextDocumentWriter, QTextFormat, QTextFragment,
+                             QTextFrame, QTextFrameFormat, QTextImageFormat,
+                             QTextInlineObject, QTextItem, QTextLayout,
+                             QTextLength, QTextLine, QTextList, QTextListFormat,
+                             QTextObject, QTextObjectInterface, QTextOption,
+                             QTextTable, QTextTableCell, QTextTableCellFormat,
+                             QTextTableFormat, QTouchEvent, QTransform,
+                             QValidator, QVector2D, QVector3D, QVector4D,
+                             QWhatsThisClickedEvent, QWheelEvent,
+                             QWindowStateChangeEvent, qAlpha, qBlue,
+                             qFuzzyCompare, qGray, qGreen, qIsGray, qRed, qRgb,
+                             qRgba, QIntValidator)
+elif PYSIDE:
+    from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap,
+                              QBrush, QClipboard, QCloseEvent, QColor,
+                              QConicalGradient, QContextMenuEvent, QCursor,
+                              QDesktopServices, QDoubleValidator, QDrag,
+                              QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent,
+                              QDropEvent, QFileOpenEvent, QFocusEvent, QFont,
+                              QFontDatabase, QFontInfo, QFontMetrics,
+                              QFontMetricsF, QGradient, QHelpEvent,
+                              QHideEvent, QHoverEvent, QIcon, QIconDragEvent,
+                              QIconEngine, QImage, QImageIOHandler, QImageReader,
+                              QImageWriter, QInputEvent, QInputMethodEvent,
+                              QKeyEvent, QKeySequence, QLinearGradient,
+                              QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2,
+                              QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3,
+                              QMatrix4x4, QMouseEvent, QMoveEvent, QMovie,
+                              QPaintDevice, QPaintEngine, QPaintEngineState,
+                              QPaintEvent, QPainter, QPainterPath,
+                              QPainterPathStroker, QPalette, QPen, QPicture,
+                              QPictureIO, QPixmap, QPixmapCache, QPolygon,
+                              QPolygonF, QQuaternion, QRadialGradient,
+                              QRegExpValidator, QRegion, QResizeEvent,
+                              QSessionManager, QShortcutEvent, QShowEvent,
+                              QStandardItem, QStandardItemModel,
+                              QStatusTipEvent, QSyntaxHighlighter, QTabletEvent,
+                              QTextBlock, QTextBlockFormat, QTextBlockGroup,
+                              QTextBlockUserData, QTextCharFormat, QTextCursor,
+                              QTextDocument, QTextDocumentFragment,
+                              QTextFormat, QTextFragment,
+                              QTextFrame, QTextFrameFormat, QTextImageFormat,
+                              QTextInlineObject, QTextItem, QTextLayout,
+                              QTextLength, QTextLine, QTextList, QTextListFormat,
+                              QTextObject, QTextObjectInterface, QTextOption,
+                              QTextTable, QTextTableCell, QTextTableCellFormat,
+                              QTextTableFormat, QTouchEvent, QTransform,
+                              QValidator, QVector2D, QVector3D, QVector4D,
+                              QWhatsThisClickedEvent, QWheelEvent,
+                              QWindowStateChangeEvent, qAlpha, qBlue,
+                              qGray, qGreen, qIsGray, qRed, qRgb, qRgba,
+                              QIntValidator)
+else:
+    raise PythonQtError('No Qt bindings could be found')
diff --git a/pyzo/util/qt/QtWidgets.py b/pyzo/util/qt/QtWidgets.py
new file mode 100644
index 0000000..9613464
--- /dev/null
+++ b/pyzo/util/qt/QtWidgets.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2014-2015 Colin Duquesnoy
+# Copyright © 2009- The Spyder Developmet Team
+#
+# Licensed under the terms of the MIT License
+# (see LICENSE.txt for details)
+
+"""
+Provides widget classes and functions.
+.. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtWidgets
+    are exposed here. Therefore, you need to treat/use this package as if it
+    were the ``PyQt5.QtWidgets`` module.
+"""
+
+from . import PYQT5, PYQT4, PYSIDE, PythonQtError
+from ._patch.qcombobox import patch_qcombobox
+from ._patch.qheaderview import introduce_renamed_methods_qheaderview
+
+
+if PYQT5:
+    from PyQt5.QtWidgets import *
+elif PYQT4:
+    from PyQt4.QtGui import *
+    QStyleOptionViewItem = QStyleOptionViewItemV4
+    del QStyleOptionViewItemV4
+
+    # These objects belong to QtGui
+    del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard,
+         QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor,
+         QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent,
+         QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent,
+         QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics,
+         QFontMetricsF, QGlyphRun, QGradient, QHelpEvent, QHideEvent,
+         QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage,
+         QImageIOHandler, QImageReader, QImageWriter, QInputEvent,
+         QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient,
+         QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3,
+         QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent,
+         QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState,
+         QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette,
+         QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon,
+         QPolygonF, QQuaternion, QRadialGradient, QRawFont, QRegExpValidator,
+         QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent,
+         QStandardItem, QStandardItemModel, QStaticText, QStatusTipEvent,
+         QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat,
+         QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor,
+         QTextDocument, QTextDocumentFragment, QTextDocumentWriter,
+         QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat,
+         QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout,
+         QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject,
+         QTextObjectInterface, QTextOption, QTextTable, QTextTableCell,
+         QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform,
+         QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent,
+         QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qFuzzyCompare,
+         qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator)
+
+    # These objects belong to QtPrintSupport
+    del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine,
+         QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo)
+
+    # These objects belong to QtCore
+    del (QItemSelection, QItemSelectionModel, QItemSelectionRange,
+         QSortFilterProxyModel)
+
+    # Patch QComboBox to allow Python objects to be passed to userData
+    patch_qcombobox(QComboBox)
+
+    # QHeaderView: renamed methods
+    introduce_renamed_methods_qheaderview(QHeaderView)
+
+elif PYSIDE:
+    from PySide.QtGui import *
+    QStyleOptionViewItem = QStyleOptionViewItemV4
+    del QStyleOptionViewItemV4
+
+    # These objects belong to QtGui
+    del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard,
+         QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor,
+         QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent,
+         QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent,
+         QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics,
+         QFontMetricsF, QGradient, QHelpEvent, QHideEvent,
+         QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage,
+         QImageIOHandler, QImageReader, QImageWriter, QInputEvent,
+         QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient,
+         QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3,
+         QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent,
+         QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState,
+         QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette,
+         QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon,
+         QPolygonF, QQuaternion, QRadialGradient, QRegExpValidator,
+         QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent,
+         QStandardItem, QStandardItemModel, QStatusTipEvent,
+         QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat,
+         QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor,
+         QTextDocument, QTextDocumentFragment,
+         QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat,
+         QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout,
+         QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject,
+         QTextObjectInterface, QTextOption, QTextTable, QTextTableCell,
+         QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform,
+         QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent,
+         QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen,
+         qIsGray, qRed, qRgb, qRgba, QIntValidator)
+
+    # These objects belong to QtPrintSupport
+    del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine,
+         QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo)
+
+    # These objects belong to QtCore
+    del (QItemSelection, QItemSelectionModel, QItemSelectionRange,
+         QSortFilterProxyModel)
+
+    # Patch QComboBox to allow Python objects to be passed to userData
+    patch_qcombobox(QComboBox)
+
+    # QHeaderView: renamed methods
+    introduce_renamed_methods_qheaderview(QHeaderView)
+
+else:
+    raise PythonQtError('No Qt bindings could be found')
diff --git a/pyzo/util/qt/__init__.py b/pyzo/util/qt/__init__.py
index 26f7eb5..273df1d 100644
--- a/pyzo/util/qt/__init__.py
+++ b/pyzo/util/qt/__init__.py
@@ -1,385 +1,134 @@
 # -*- coding: utf-8 -*-
-# Copyright (c) 2016, Almar Klein
 #
-# This file is distributed under the terms of the (new) BSD License.
-
-""" Module that serves as a proxy for loading Qt libraries.
-
-This module has several goals:
-  * Import QtCore, QtGui, etc. from PySide or PyQt4 (whichever is available).
-  * Fix some incompatibilities between the two.
-  * For applications that bring their own Qt libs, avoid clashes.
-  * Allow using the PySide or PyQt4 libraries of the system 
-    (the ones in /usr/lib/...), so that frozen applications can look good.
-
-To use do ``from pyzo.util.qt import QtCore, QtGui``. Note that this proxy
-package is designed to be portable; it should be possible to use it in
-your own application or library.
-
-To use in frozen applications, create a qt.conf next to the executable,
-that has text as specified in DEFAULT_QT_CONF_TEXT. By modifying the 
-text, preferences can be changed.
-
-
-Notes
------
-
-To prevent colliding of Qt libs when an app brings its own libs, in
-particular on KDE, the plugins of Qt should be disabled. This needs
-to be done in two places:
-  * via qt.conf "Plugins = ''"
-  * set the QT_PLUGIN_PATH variable to empty string
-
-The latter is equivalent to QtGui.QApplication.setLibraryPaths([]),
-but has the advantage that it can be set beforehand.
-
-A downside of the plugins being disabled is that the native style
-(GTK+, Oxygen) cannot be used and other features (Unity integrated
-toolbar) are not available. This module allows a work-around by
-loading the native libs.
+# Copyright © 2009- The Spyder Development Team
+# Copyright © 2014-2015 Colin Duquesnoy
+#
+# Licensed under the terms of the MIT License
+# (see LICENSE.txt for details)
 
 """
+**QtPy** is a shim over the various Python Qt bindings. It is used to write
+Qt binding indenpendent libraries or applications.
 
-import sys
-import os
-import imp
-import importlib
+The shim will automatically select the first available API (PyQt5, PyQt4 and
+finally PySide).
 
-VERBOSE = False
+You can force the use of one specific bindings (e.g. if your application is
+using one specific bindings and you need to use library that use QtPy) by
+setting up the ``QT_API`` environment variable.
 
+PyQt5
+=====
 
+For PyQt5, you don't have to set anything as it will be used automatically::
 
-def qt_name():
-    """ Return the name of the Qt lib in use: 'PySide', 'PyQt4' or None.
-    """
-    try:
-        importer_instance._import_qt()
-    except ImportError:
-        pass
-    else:
-        return importer_instance._qtPackage.__name__
+    >>> from qtpy import QtGui, QtWidgets, QtCore
+    >>> print(QtWidgets.QWidget)
 
 
+PyQt4
+=====
 
-def loadWidget(filename, parent=None):
-    """ Load a widget from a .ui file. Returns a QWidget object.
-    """
-    
-    # Note that PyQt4 has PyQt4.uic.loadUi(filename, basewidget)
-    # allowing the newly created widget to inherit from a given widget
-    # instance. This is not supported in PySide and therefore not
-    # suported by this function.
-    
-    # Check
-    if not os.path.isfile(filename):
-        raise ValueError('Filename in loadWidget() is not a valid file.')
-    
-    if qt_name().lower() == 'pyside':
-        # Import (from PySide import QtCore, QtUiTools)
-        QtCore = importer_instance.load_module('QtCore')
-        QtUiTools = importer_instance.load_module('QtUiTools')
-        # Create loader and load widget
-        loader = QtUiTools.QUiLoader()
-        uifile = QtCore.QFile(filename)
-        uifile.open(QtCore.QFile.ReadOnly)
-        w = loader.load(uifile, parent)
-        uifile.close()
-        return w
-    else:
-        # Import (from PyQt4 import QtCore, uic)
-        QtCore = importer_instance.load_module('QtCore')
-        uic = importer_instance.load_module('uic')
-        # Load widget
-        w = uic.loadUi(filename)
-        # We set the parent explicitly
-        if parent is not None:
-            w.setParent(parent)
-        return w
+Set the ``QT_API`` environment variable to 'pyqt' before importing any python
+package::
 
+    >>> import os
+    >>> os.environ['QT_API'] = 'pyqt'
+    >>> from qtpy import QtGui, QtWidgets, QtCore
+    >>> print(QtWidgets.QWidget)
 
+PySide
+======
 
-class QtProxyImporter:
-    """ Importer to import Qt modules, either from PySide or from PyQt,
-    and either from this Python's version, or the system ones (if
-    available and matching).
-    """
-    
-    def __init__(self):
-        self._qtPackage = None
-        self._enabled = True
-        self._import_path = None  # None for 'normal' (non-system) import
-    
-    
-    def find_module(self, fullname, path=None):
-        """ This is called by Python's import mechanism. We return ourself
-        only if this really looks like a Qt import, and when its imported
-        as a submodule from this stub package.
-        """
-        
-        # Only proceed if we are enabled
-        if not self._enabled:
-            return None
-        
-        # Get different parts of the module name
-        nameparts = fullname.split('.') 
-        
-        # sip is required by PyQt4
-        if fullname == 'sip':
-            self._import_qt()
-            return self
-        
-        # If the import is relative to this package, we will try to
-        # import relative to the selected qtPackage
-        if '.'.join(nameparts[:-1]) == __name__:
-            self._import_qt()
-            return self
-    
-    
-    def load_module(self, fullname):
-        """ This method is called by Python's import mechanism after
-        this instance has been returned from find_module. Here we
-        actually import the module and do some furher processing.
-        """
-        
-        # Get different parts of the module name
-        nameparts = fullname.split('.') 
-        modulename = nameparts[-1]
-        
-        # We can only proceed if qtPackage was loaded
-        if self._qtPackage is None:
-            raise ImportError()
-        
-        # Get qt dir or dummy
-        if self._import_path:
-            qtdir = os.path.dirname(self._qtPackage.__file__)
-        else:
-            qtdir = '/nonexisting/dir/with/subdirs/dummy'
-        
-        # Get real name and path to load it from    
-        if fullname == self._qtPackage.__name__:
-            return self._qtPackage
-        elif fullname == 'sip':
-            realmodulename = 'sip'
-            searchdir = os.path.dirname(qtdir)
-        elif modulename.startswith('Qt') or modulename == 'uic':
-            realmodulename = '%s.%s' % (self._qtPackage.__name__, modulename)
-            searchdir = qtdir
-        else:
-            raise ImportError()
-        
-        # Import. We also need to modify sys.path in case this is a system package
-        if os.path.isdir(qtdir):
-            if VERBOSE: print('load_module explicitly: %s' % fullname)
-            sys.path.insert(0, os.path.dirname(qtdir))
-            try:
-                for entry in os.listdir(searchdir):
-                    if entry.startswith(modulename+'.'):
-                        m = imp.load_dynamic(   realmodulename, 
-                                                os.path.join(searchdir, entry))
-                        break
-                else:
-                    raise ImportError('Could not import %s' % realmodulename)
-            finally:
-                sys.path.pop(0)
-        else:
-            # Module can be inside a zip-file when frozen
-            # Import normally, and disable ourselves so we do not recurse
-            if VERBOSE: print('load_module normally: %s' % realmodulename)
-            self._enabled = False
-            try:
-                p = __import__(realmodulename)
-            finally:
-                self._enabled = True
-            # Get the actual modele
-            if '.' in realmodulename:
-                m = getattr(p, modulename)
-            else:
-                m = p
-        
-        # Also register in sys.modules under the name as it was imported
-        sys.modules[realmodulename] = m
-        sys.modules[fullname] = m
-        
-        # Fix some compatibility issues
-        self._fix_compat(m)
-        
-        # Done
-        return m
-    
-    
-    def _determine_preference(self):
-        """ Determine preference by reading from qt.conf.
-        """
-        
-        # Get dirs to look for qt.conf
-        dirs = [os.path.dirname(sys.executable)]
-        script_dir = ''
-        if sys.path:
-            script_dir = sys.path[0]
-            if getattr(sys, 'frozen', None):
-                script_dir = os.path.dirname(script_dir)
-        dirs.append(script_dir)
-        
-        # Read qt.conf
-        for dir in dirs:
-            qt_conf = os.path.join(dir, 'qt.conf')
-            if os.path.isfile(qt_conf):
-                text = open(qt_conf, 'rb').read().decode('utf-8', 'ignore')
-                break
-        else:
-            text = ''
-        
-        # Parse qt.conf
-        prefer_system = False
-        prefer_toolkit = ''
-        #
-        for line in text.splitlines():
-            line = line.split('#',1)[0].strip()
-            if '=' not in line:
-                continue
-            key, val = [i.strip() for i in line.split('=', 1)]
-            if key == 'PreferSystem' and val.lower() in ('yes', 'true', '1'):
-                prefer_system = True
-            if key == 'PreferToolkit':
-                prefer_toolkit = val
-        
-        return prefer_system, prefer_toolkit
-    
-    
-    def _import_qt(self, toolkit=None):
-        """ This is where we import either PySide or PyQt4.
-        This is done only once.
-        """
-        
-        # Make qtPackage global and only proceed if its not set yet
-        if self._qtPackage is not None:
-            return
-        
-        # Establish preference
-        prefer_system, prefer_toolkit = self._determine_preference()
-        
-        # Check toolkit, use pyside by default
-        prefer_toolkit = toolkit or prefer_toolkit or 'pyside'
-        if prefer_toolkit.lower() not in ('pyside', 'pyqt4'):
-            prefer_toolkit = 'pyside'
-            print('Invalid Qt toolit preference given: "%s"' % prefer_toolkit)
-        
-        # Really import
-        self._qtPackage = self._import_qt_for_real(prefer_system, prefer_toolkit)
-        
-        # Disable plugins if necessary
-        if self._qtPackage and sys.platform.startswith('linux'):
-            if not self._qtPackage.__file__.startswith('/usr'):
-                os.environ['QT_PLUGIN_PATH'] = ''
-    
-    
-    def _import_qt_for_real(self, prefer_system, prefer_toolkit):
-        """ The actual importing.
-        """
-        
-        # Perhaps it is already loaded
-        if 'PySide' in sys.modules:
-            return sys.modules['PySide']
-        elif 'PyQt4' in sys.modules:
-            return sys.modules['PyQt4']
-        
-        # Init potential imports
-        pyside_imports = [('PySide', None)]
-        pyqt4_imports = [('PyQt4', None)]
-        pyside_system_imports = []
-        pyqt4_system_imports = []
-        
-        # Get possible paths, but only on Linux
-        if sys.platform.startswith('linux'):
-            # Determine where PySide or PyQt4 can be
-            ver = sys.version[:3]
-            possible_paths = ['/usr/local/lib/python%s/dist-packages' % ver,
-                os.path.expanduser('~/.local/lib/python%s/site-packages' % ver)]
-            if os.path.isdir('/usr/lib/python%s' % ver):
-                possible_paths.append('/usr/lib/python%s/dist-packages' % ver[0])
-            # Trty if it is there
-            for path in possible_paths:
-                if os.path.isdir(os.path.join(path, 'PySide')):
-                    pyside_system_imports.append(('PySide', path))
-                if os.path.isdir(os.path.join(path, 'PyQt4')):
-                    pyqt4_system_imports.append(('PyQt4', path))
-        
-        # Combine imports in right order
-        if prefer_system:
-            if 'pyside' == prefer_toolkit.lower():
-                imports =   pyside_system_imports + pyqt4_system_imports + \
-                            pyside_imports + pyqt4_imports
-            else:
-                imports =   pyqt4_system_imports + pyside_system_imports + \
-                            pyqt4_imports + pyside_imports
-        else:
-            if 'pyside' == prefer_toolkit.lower():
-                imports =   pyside_imports + pyqt4_imports #+ \
-                            #pyside_system_imports + pyqt4_system_imports
-            else:
-                imports =   pyqt4_imports + pyside_imports #+ \
-                            #pyqt4_system_imports + pyside_system_imports
-        
-        # Try importing
-        package = None
-        for package_name, path in imports:
-            if path:
-                sys.path.insert(0, path)
-            if VERBOSE: print('Attempting to import %s (system=%i)' % (package_name, bool(path)))
-            self._import_path = path
-            try:
-                return __import__(package_name, level=0)
-            except ImportError as err:
-                if VERBOSE: print('Import failed')
-            finally:
-                if path:
-                    sys.path.pop(0)
-        else:
-            raise ImportError('Could not import PySide nor PyQt4.')
-    
-    
-    def _fix_compat(self, m):
-        """ Fix incompatibilities between PySide and PyQt4. 
-        """
-        if self._qtPackage.__name__ == 'PySide':
-            pass
-        else:
-            if m.__name__.endswith('QtCore'):
-                m.Signal = m.pyqtSignal
-        
-        # todo: more compat, like uic loading
-
+Set the QT_API environment variable to 'pyside' before importing other
+packages::
 
-importer_instance = QtProxyImporter()
-sys.meta_path.insert(0, importer_instance)
+    >>> import os
+    >>> os.environ['QT_API'] = 'pyside'
+    >>> from qtpy import QtGui, QtWidgets, QtCore
+    >>> print(QtWidgets.QWidget)
 
+"""
 
+import os
 
+# Version of QtPy
+from ._version import __version__
 
-DEFAULT_QT_CONF_TEXT = """## This file contains configuration options for Qt.
-## It disables plugins so that an application that brings its own 
-## Qt libraries do not clashs with the native Qt. It also has options
-## that the pyzo qt proxy uses to allow you to use your system
-## PySide/PyQt4 libraries.
+#: Qt API environment variable name
+QT_API = 'QT_API'
+#: names of the expected PyQt5 api
+PYQT5_API = ['pyqt5']
+#: names of the expected PyQt4 api
+PYQT4_API = [
+    'pyqt',  # name used in IPython.qt
+    'pyqt4'  # pyqode.qt original name
+]
+#: names of the expected PySide api
+PYSIDE_API = ['pyside']
 
-[Py]
+os.environ.setdefault(QT_API, 'pyqt5')
+API = os.environ[QT_API].lower()
+assert API in (PYQT5_API + PYQT4_API + PYSIDE_API)
 
-## Preferred toolkit: PySide or PyQt4
-PreferToolkit = PySide
+is_old_pyqt = is_pyqt46 = False
+PYQT5 = True
+PYQT4 = PYSIDE = False
 
-## Uncomment if pyzo should try to use the system libraries
-## Note that you version of Python must be ABI compatible with the
-## version on your system for this to work
-#PreferSystem = yes
 
+class PythonQtError(Exception):
+    """Error raise if no bindings could be selected"""
+    pass
 
-[Paths]
 
-## This disables plugins, avoiding Qt library clashes
-Plugins = ''
+if API in PYQT5_API:
+    try:
+        from PyQt5.Qt import PYQT_VERSION_STR as PYQT_VERSION  # analysis:ignore
+        from PyQt5.Qt import QT_VERSION_STR as QT_VERSION  # analysis:ignore
+        PYSIDE_VERSION = None
+    except ImportError:
+        API = os.environ['QT_API'] = 'pyqt'
 
-## On Ubuntu Unity, if PreferSystem is enabled, uncomment this to
-## enable the fancy menu bar.
-#Plugins = /usr/lib/x86_64-linux-gnu/qt4/plugins
+if API in PYQT4_API:
+    try:
+        import sip
+        try:
+            sip.setapi('QString', 2)
+            sip.setapi('QVariant', 2)
+            sip.setapi('QDate', 2)
+            sip.setapi('QDateTime', 2)
+            sip.setapi('QTextStream', 2)
+            sip.setapi('QTime', 2)
+            sip.setapi('QUrl', 2)
+        except AttributeError:
+            # PyQt < v4.6
+            pass
+        from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION  # analysis:ignore
+        from PyQt4.Qt import QT_VERSION_STR as QT_VERSION  # analysis:ignore
+        PYSIDE_VERSION = None
+        PYQT5 = False
+        PYQT4 = True
+    except ImportError:
+        API = os.environ['QT_API'] = 'pyside'
+    else:
+        is_old_pyqt = PYQT_VERSION.startswith(('4.4', '4.5', '4.6', '4.7'))
+        is_pyqt46 = PYQT_VERSION.startswith('4.6')
 
-"""
+if API in PYSIDE_API:
+    try:
+        from PySide import __version__ as PYSIDE_VERSION  # analysis:ignore
+        from PySide.QtCore import __version__ as QT_VERSION  # analysis:ignore
+        PYQT_VERSION = None
+        PYQT5 = False
+        PYSIDE = True
+    except ImportError:
+        raise PythonQtError('No Qt bindings could be found')
+
+API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4',
+            'pyside': 'PySide'}[API]
+if PYQT4:
+        import sip
+        try:
+            API_NAME += (" (API v{0})".format(sip.getapi('QString')))
+        except AttributeError:
+            pass
diff --git a/pyzo/util/qt/_patch/__init__.py b/pyzo/util/qt/_patch/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyzo/util/qt/_patch/qcombobox.py b/pyzo/util/qt/_patch/qcombobox.py
new file mode 100644
index 0000000..d3e98be
--- /dev/null
+++ b/pyzo/util/qt/_patch/qcombobox.py
@@ -0,0 +1,101 @@
+# The code below, as well as the associated test were adapted from
+# qt-helpers, which was released under a 3-Clause BSD license:
+#
+# Copyright (c) 2015, Chris Beaumont and Thomas Robitaille
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#  * Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#  * Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the
+#    distribution.
+#  * Neither the name of the Glue project nor the names of its
+#    contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+def patch_qcombobox(QComboBox):
+    """
+    In PySide, using Python objects as userData in QComboBox causes
+    Segmentation faults under certain conditions. Even in cases where it
+    doesn't, findData does not work correctly. Likewise, findData also does not
+    work correctly with Python objects when using PyQt4. On the other hand,
+    PyQt5 deals with this case correctly. We therefore patch QComboBox when
+    using PyQt4 and PySide to avoid issues.
+    """
+
+    from ..QtGui import QIcon
+    from ..QtCore import Qt, QObject
+
+    class userDataWrapper():
+        """
+        This class is used to wrap any userData object. If we don't do this,
+        then certain types of objects can cause segmentation faults or issues
+        depending on whether/how __getitem__ is defined.
+        """
+        def __init__(self, data):
+            self.data = data
+
+    _addItem = QComboBox.addItem
+
+    def addItem(self, *args, **kwargs):
+        if len(args) == 3 or (not isinstance(args[0], QIcon)
+                              and len(args) == 2):
+            args, kwargs['userData'] = args[:-1], args[-1]
+        if 'userData' in kwargs:
+            kwargs['userData'] = userDataWrapper(kwargs['userData'])
+        _addItem(self, *args, **kwargs)
+
+    _insertItem = QComboBox.insertItem
+
+    def insertItem(self, *args, **kwargs):
+        if len(args) == 4 or (not isinstance(args[1], QIcon)
+                              and len(args) == 3):
+            args, kwargs['userData'] = args[:-1], args[-1]
+        if 'userData' in kwargs:
+            kwargs['userData'] = userDataWrapper(kwargs['userData'])
+        _insertItem(self, *args, **kwargs)
+
+    _setItemData = QComboBox.setItemData
+
+    def setItemData(self, index, value, role=Qt.UserRole):
+        value = userDataWrapper(value)
+        _setItemData(self, index, value, role=role)
+
+    _itemData = QComboBox.itemData
+
+    def itemData(self, index, role=Qt.UserRole):
+        userData = _itemData(self, index, role=role)
+        if isinstance(userData, userDataWrapper):
+            userData = userData.data
+        return userData
+
+    def findData(self, value):
+        for i in range(self.count()):
+            if self.itemData(i) == value:
+                return i
+        return -1
+
+    QComboBox.addItem = addItem
+    QComboBox.insertItem = insertItem
+    QComboBox.setItemData = setItemData
+    QComboBox.itemData = itemData
+    QComboBox.findData = findData
\ No newline at end of file
diff --git a/pyzo/util/qt/_patch/qheaderview.py b/pyzo/util/qt/_patch/qheaderview.py
new file mode 100644
index 0000000..8bef839
--- /dev/null
+++ b/pyzo/util/qt/_patch/qheaderview.py
@@ -0,0 +1,82 @@
+def introduce_renamed_methods_qheaderview(QHeaderView):
+
+    _isClickable = QHeaderView.isClickable
+    def sectionsClickable(self):
+        """
+        QHeaderView.sectionsClickable() -> bool
+        """
+        return _isClickable(self)
+    QHeaderView.sectionsClickable = sectionsClickable
+    def isClickable(self):
+        raise Exception('isClickable is only available in Qt4. Use '
+                        'sectionsClickable instead.')
+    QHeaderView.isClickable = isClickable
+
+
+    _isMovable = QHeaderView.isMovable
+    def sectionsMovable(self):
+        """
+        QHeaderView.sectionsMovable() -> bool
+        """
+        return _isMovable(self)
+    QHeaderView.sectionsMovable = sectionsMovable
+    def isMovable(self):
+        raise Exception('isMovable is only available in Qt4. Use '
+                        'sectionsMovable instead.')
+    QHeaderView.isMovable = isMovable
+
+
+    _resizeMode = QHeaderView.resizeMode
+    def sectionResizeMode(self, logicalIndex):
+        """
+        QHeaderView.sectionResizeMode(int) -> QHeaderView.ResizeMode
+        """
+        return _resizeMode(self, logicalIndex)
+    QHeaderView.sectionResizeMode = sectionResizeMode
+    def resizeMode(self, logicalIndex):
+        raise Exception('resizeMode is only available in Qt4. Use '
+                        'sectionResizeMode instead.')
+    QHeaderView.resizeMode = resizeMode
+
+    _setClickable = QHeaderView.setClickable
+    def setSectionsClickable(self, clickable):
+        """
+        QHeaderView.setSectionsClickable(bool)
+        """
+        return _setClickable(self, clickable)
+    QHeaderView.setSectionsClickable = setSectionsClickable
+    def setClickable(self, clickable):
+        raise Exception('setClickable is only available in Qt4. Use '
+                        'setSectionsClickable instead.')
+    QHeaderView.setClickable = setClickable
+
+
+    _setMovable = QHeaderView.setMovable
+    def setSectionsMovable(self, movable):
+        """
+        QHeaderView.setSectionsMovable(bool)
+        """
+        return _setMovable(self, movable)
+    QHeaderView.setSectionsMovable = setSectionsMovable
+    def setMovable(self, movable):
+        raise Exception('setMovable is only available in Qt4. Use '
+                        'setSectionsMovable instead.')
+    QHeaderView.setMovable = setMovable
+
+
+    _setResizeMode = QHeaderView.setResizeMode
+    def setSectionResizeMode(self, *args):
+        """
+        QHeaderView.setSectionResizeMode(QHeaderView.ResizeMode)
+        QHeaderView.setSectionResizeMode(int, QHeaderView.ResizeMode)
+        """
+        _setResizeMode(self, *args)
+    QHeaderView.setSectionResizeMode = setSectionResizeMode
+    def setResizeMode(self, *args):
+        raise Exception('setResizeMode is only available in Qt4. Use '
+                        'setSectionResizeMode instead.')
+    QHeaderView.setResizeMode = setResizeMode
+
+
+
+
diff --git a/pyzo/util/qt/_version.py b/pyzo/util/qt/_version.py
new file mode 100644
index 0000000..a89112b
--- /dev/null
+++ b/pyzo/util/qt/_version.py
@@ -0,0 +1,2 @@
+version_info = (1, 2, 0, 'dev0')
+__version__ = '.'.join(map(str, version_info))
diff --git a/pyzo/util/qt/uic.py b/pyzo/util/qt/uic.py
new file mode 100644
index 0000000..abca491
--- /dev/null
+++ b/pyzo/util/qt/uic.py
@@ -0,0 +1,223 @@
+import os
+
+from . import PYSIDE, PYQT4, PYQT5
+from .QtWidgets import QComboBox
+
+__all__ = ['loadUi']
+
+if PYQT5:
+
+    from PyQt5.uic import loadUi
+
+elif PYQT4:
+
+    from PyQt4.uic import loadUi
+
+elif PYSIDE:
+
+    # In PySide, loadUi does not exist, so we define it using QUiLoader, and
+    # then make sure we expose that function. This is adapted from qt-helpers
+    # which was released under a 3-clause BSD license:
+    # qt-helpers - a common front-end to various Qt modules
+    #
+    # Copyright (c) 2015, Chris Beaumont and Thomas Robitaille
+    #
+    # All rights reserved.
+    #
+    # Redistribution and use in source and binary forms, with or without
+    # modification, are permitted provided that the following conditions are
+    # met:
+    #
+    #  * Redistributions of source code must retain the above copyright
+    #    notice, this list of conditions and the following disclaimer.
+    #  * Redistributions in binary form must reproduce the above copyright
+    #    notice, this list of conditions and the following disclaimer in the
+    #    documentation and/or other materials provided with the
+    #    distribution.
+    #  * Neither the name of the Glue project nor the names of its contributors
+    #    may be used to endorse or promote products derived from this software
+    #    without specific prior written permission.
+    #
+    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+    # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+    # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+    # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+    # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+    # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+    #
+    # Which itself was based on the solution at
+    #
+    # https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8
+    #
+    # which was released under the MIT license:
+    #
+    # Copyright (c) 2011 Sebastian Wiesner <lunaryorn at gmail.com>
+    # Modifications by Charl Botha <cpbotha at vxlabs.com>
+    #
+    # Permission is hereby granted, free of charge, to any person obtaining a
+    # copy of this software and associated documentation files (the "Software"),
+    # to deal in the Software without restriction, including without limitation
+    # the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    # and/or sell copies of the Software, and to permit persons to whom the
+    # Software is furnished to do so, subject to the following conditions:
+    #
+    # The above copyright notice and this permission notice shall be included in
+    # all copies or substantial portions of the Software.
+    #
+    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+    # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    # DEALINGS IN THE SOFTWARE.
+
+    from PySide.QtCore import QMetaObject
+    from PySide.QtUiTools import QUiLoader
+
+    class UiLoader(QUiLoader):
+        """
+        Subclass of :class:`~PySide.QtUiTools.QUiLoader` to create the user
+        interface in a base instance.
+
+        Unlike :class:`~PySide.QtUiTools.QUiLoader` itself this class does not
+        create a new instance of the top-level widget, but creates the user
+        interface in an existing instance of the top-level class if needed.
+
+        This mimics the behaviour of :func:`PyQt4.uic.loadUi`.
+        """
+
+        def __init__(self, baseinstance, 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 custom widgets. 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.
+            """
+
+            QUiLoader.__init__(self, baseinstance)
+
+            self.baseinstance = baseinstance
+
+            if customWidgets is None:
+                self.customWidgets = {}
+            else:
+                self.customWidgets = customWidgets
+
+        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.baseinstance:
+                # supposed to create the top-level widget, return the base
+                # instance instead
+                return self.baseinstance
+
+            else:
+
+                # For some reason, Line is not in the list of available
+                # widgets, but works fine, so we have to special case it here.
+                if class_name in self.availableWidgets() or class_name == 'Line':
+                    # create a new widget for child widgets
+                    widget = QUiLoader.createWidget(self, class_name, parent, name)
+
+                else:
+                    # If not in the list of availableWidgets, must be a custom
+                    # widget. This will raise KeyError if the user has not
+                    # supplied the relevant class_name in the dictionary or if
+                    # customWidgets is empty.
+                    try:
+                        widget = self.customWidgets[class_name](parent)
+                    except KeyError:
+                        raise Exception('No custom widget ' + class_name + ' '
+                                        'found in customWidgets')
+
+                if self.baseinstance:
+                    # set an attribute for the new child widget on the base
+                    # instance, just like PyQt4.uic.loadUi does.
+                    setattr(self.baseinstance, name, widget)
+
+                return widget
+
+    def _get_custom_widgets(ui_file):
+        """
+        This function is used to parse a ui file and look for the <customwidgets>
+        section, then automatically load all the custom widget classes.
+        """
+
+        import sys
+        import importlib
+        from xml.etree.ElementTree import ElementTree
+
+        # Parse the UI file
+        etree = ElementTree()
+        ui = etree.parse(ui_file)
+
+        # Get the customwidgets section
+        custom_widgets = ui.find('customwidgets')
+
+        if custom_widgets is None:
+            return {}
+
+        custom_widget_classes = {}
+
+        for custom_widget in custom_widgets.getchildren():
+
+            cw_class = custom_widget.find('class').text
+            cw_header = custom_widget.find('header').text
+
+            module = importlib.import_module(cw_header)
+
+            custom_widget_classes[cw_class] = getattr(module, cw_class)
+
+        return custom_widget_classes
+
+    def loadUi(uifile, baseinstance=None, workingDirectory=None):
+        """
+        Dynamically load a user interface from the given ``uifile``.
+
+        ``uifile`` is a string containing a file name of the UI file to load.
+
+        If ``baseinstance`` is ``None``, the a new instance of the top-level
+        widget will be created. Otherwise, the user interface is created within
+        the given ``baseinstance``. In this case ``baseinstance`` must be an
+        instance of the top-level widget class in the UI file to load, or a
+        subclass thereof. In other words, if you've created a ``QMainWindow``
+        interface in the designer, ``baseinstance`` must be a ``QMainWindow``
+        or a subclass thereof, too. You cannot load a ``QMainWindow`` UI file
+        with a plain :class:`~PySide.QtGui.QWidget` as ``baseinstance``.
+
+        :method:`~PySide.QtCore.QMetaObject.connectSlotsByName()` is called on
+        the created user interface, so you can implemented your slots according
+        to its conventions in your widget class.
+
+        Return ``baseinstance``, if ``baseinstance`` is not ``None``. Otherwise
+        return the newly created instance of the user interface.
+        """
+
+        # We parse the UI file and import any required custom widgets
+        customWidgets = _get_custom_widgets(uifile)
+
+        loader = UiLoader(baseinstance, customWidgets)
+
+        if workingDirectory is not None:
+            loader.setWorkingDirectory(workingDirectory)
+
+        widget = loader.load(uifile)
+        QMetaObject.connectSlotsByName(widget)
+        return widget

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pyzo.git



More information about the debian-science-commits mailing list