binding_helper.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # Copyright (c) 2011, Dirk Thomas, Dorian Scholz, TU Darmstadt
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #   * Redistributions of source code must retain the above copyright
00011 #     notice, this list of conditions and the following disclaimer.
00012 #   * Redistributions in binary form must reproduce the above
00013 #     copyright notice, this list of conditions and the following
00014 #     disclaimer in the documentation and/or other materials provided
00015 #     with the distribution.
00016 #   * Neither the name of the TU Darmstadt nor the names of its
00017 #     contributors may be used to endorse or promote products derived
00018 #     from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 try:
00034     import __builtin__ as builtins
00035 except ImportError:
00036     # since the 'future' package provides a 'builtins' module in Python 2
00037     # this must not be checked second
00038     import builtins
00039 import os
00040 import sys
00041 import traceback
00042 
00043 
00044 QT_BINDING = None
00045 QT_BINDING_MODULES = {}
00046 QT_BINDING_VERSION = None
00047 
00048 
00049 def _select_qt_binding(binding_name=None, binding_order=None):
00050     global QT_BINDING, QT_BINDING_VERSION
00051 
00052     # order of default bindings can be changed here
00053     DEFAULT_BINDING_ORDER = ['pyqt', 'pyside']
00054     binding_order = binding_order or DEFAULT_BINDING_ORDER
00055 
00056     # determine binding preference
00057     if binding_name:
00058         if binding_name not in binding_order:
00059             raise ImportError("Qt binding '%s' is unknown" % binding_name)
00060         binding_order = [binding_name]
00061 
00062     required_modules = [
00063         'QtCore',
00064         'QtGui'
00065     ]
00066     optional_modules = [
00067         'QtDeclarative',
00068         'QtMultimedia',
00069         'QtNetwork',
00070         'QtOpenGL',
00071         'QtOpenVG',
00072         'QtScript',
00073         'QtScriptTools'
00074         'QtSql',
00075         'QtSvg',
00076         'QtWebKit',
00077         'QtXml',
00078         'QtXmlPatterns',
00079     ]
00080 
00081     # try to load preferred bindings
00082     error_msgs = []
00083     for binding_name in binding_order:
00084         try:
00085             binding_loader = getattr(sys.modules[__name__], '_load_%s' % binding_name, None)
00086             if binding_loader:
00087                 QT_BINDING_VERSION = binding_loader(required_modules, optional_modules)
00088                 QT_BINDING = binding_name
00089                 # provide QtWidgets module for forward compatibility with Qt5
00090                 QT_BINDING_MODULES['QtWidgets'] = QT_BINDING_MODULES['QtGui']
00091                 break
00092             else:
00093                 error_msgs.append("  Binding loader '_load_%s' not found." % binding_name)
00094         except ImportError as e:
00095             error_msgs.append("  ImportError for '%s': %s\n%s" % (binding_name, e, traceback.format_exc()))
00096 
00097     if not QT_BINDING:
00098         raise ImportError("Could not find Qt binding (looked for: %s):\n%s" % (', '.join(["'%s'" % b for b in binding_order]), '\n'.join(error_msgs)))
00099 
00100 
00101 def _register_binding_module(module_name, module):
00102     # register module using only its own name (TODO: legacy compatibility, remove when possible)
00103     sys.modules[module_name] = module
00104     # add module to the binding modules
00105     QT_BINDING_MODULES[module_name] = module
00106 
00107 
00108 def _named_import(name):
00109     parts = name.split('.')
00110     assert(len(parts) >= 2)
00111     module = builtins.__import__(name)
00112     for m in parts[1:]:
00113         module = module.__dict__[m]
00114     module_name = parts[-1]
00115     _register_binding_module(module_name, module)
00116 
00117 
00118 def _named_optional_import(name):
00119     try:
00120         _named_import(name)
00121     except ImportError:
00122         pass
00123 
00124 
00125 def _load_pyqt(required_modules, optional_modules):
00126     # set environment variable QT_API for matplotlib
00127     os.environ['QT_API'] = 'pyqt'
00128 
00129     # select PyQt4 API, see http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html
00130     import sip
00131     try:
00132         sip.setapi('QDate', 2)
00133         sip.setapi('QDateTime', 2)
00134         sip.setapi('QString', 2)
00135         sip.setapi('QTextStream', 2)
00136         sip.setapi('QTime', 2)
00137         sip.setapi('QUrl', 2)
00138         sip.setapi('QVariant', 2)
00139     except ValueError as e:
00140         raise RuntimeError('Could not set API version (%s): did you import PyQt4 directly?' % e)
00141 
00142     # register required and optional PyQt4 modules
00143     for module_name in required_modules:
00144         _named_import('PyQt4.%s' % module_name)
00145     for module_name in optional_modules:
00146         _named_optional_import('PyQt4.%s' % module_name)
00147 
00148     # set some names for compatibility with PySide
00149     sys.modules['QtCore'].Signal = sys.modules['QtCore'].pyqtSignal
00150     sys.modules['QtCore'].Slot = sys.modules['QtCore'].pyqtSlot
00151     sys.modules['QtCore'].Property = sys.modules['QtCore'].pyqtProperty
00152 
00153     # try to register PyQt4.Qwt5 module
00154     try:
00155         import PyQt4.Qwt5
00156         _register_binding_module('Qwt', PyQt4.Qwt5)
00157     except ImportError:
00158         pass
00159 
00160     global _loadUi
00161 
00162     def _loadUi(uifile, baseinstance=None, custom_widgets_=None):
00163         from PyQt4 import uic
00164         return uic.loadUi(uifile, baseinstance=baseinstance)
00165 
00166     # override specific function to improve compatibility between different bindings
00167     from QtGui import QFileDialog
00168     QFileDialog.getOpenFileName = QFileDialog.getOpenFileNameAndFilter
00169     QFileDialog.getSaveFileName = QFileDialog.getSaveFileNameAndFilter
00170 
00171     import PyQt4.QtCore
00172     return PyQt4.QtCore.PYQT_VERSION_STR
00173 
00174 
00175 def _load_pyside(required_modules, optional_modules):
00176     # set environment variable QT_API for matplotlib
00177     os.environ['QT_API'] = 'pyside'
00178 
00179     # register required and optional PySide modules
00180     for module_name in required_modules:
00181         _named_import('PySide.%s' % module_name)
00182     for module_name in optional_modules:
00183         _named_optional_import('PySide.%s' % module_name)
00184 
00185     # set some names for compatibility with PyQt4
00186     sys.modules['QtCore'].pyqtSignal = sys.modules['QtCore'].Signal
00187     sys.modules['QtCore'].pyqtSlot = sys.modules['QtCore'].Slot
00188     sys.modules['QtCore'].pyqtProperty = sys.modules['QtCore'].Property
00189 
00190     # try to register PySideQwt module
00191     try:
00192         import PySideQwt
00193         _register_binding_module('Qwt', PySideQwt)
00194     except ImportError:
00195         pass
00196 
00197     global _loadUi
00198 
00199     def _loadUi(uifile, baseinstance=None, custom_widgets=None):
00200         from PySide.QtUiTools import QUiLoader
00201         from PySide.QtCore import QMetaObject
00202 
00203         class CustomUiLoader(QUiLoader):
00204             class_aliases = {
00205                 'Line': 'QFrame',
00206             }
00207 
00208             def __init__(self, baseinstance=None, custom_widgets=None):
00209                 super(CustomUiLoader, self).__init__(baseinstance)
00210                 self._base_instance = baseinstance
00211                 self._custom_widgets = custom_widgets or {}
00212 
00213             def createWidget(self, class_name, parent=None, name=''):
00214                 # don't create the top-level widget, if a base instance is set
00215                 if self._base_instance and not parent:
00216                     return self._base_instance
00217 
00218                 if class_name in self._custom_widgets:
00219                     widget = self._custom_widgets[class_name](parent)
00220                 else:
00221                     widget = QUiLoader.createWidget(self, class_name, parent, name)
00222 
00223                 if str(type(widget)).find(self.class_aliases.get(class_name, class_name)) < 0:
00224                     sys.modules['QtCore'].qDebug(str('PySide.loadUi(): could not find widget class "%s", defaulting to "%s"' % (class_name, type(widget))))
00225 
00226                 if self._base_instance:
00227                     setattr(self._base_instance, name, widget)
00228 
00229                 return widget
00230 
00231         loader = CustomUiLoader(baseinstance, custom_widgets)
00232 
00233         # instead of passing the custom widgets, they should be registered using QUiLoader.registerCustomWidget(),
00234         # but this does not work in PySide 1.0.6: it simply segfaults...
00235         #loader = CustomUiLoader(baseinstance)
00236         #custom_widgets = custom_widgets or {}
00237         #for custom_widget in custom_widgets.values():
00238         #    loader.registerCustomWidget(custom_widget)
00239 
00240         ui = loader.load(uifile)
00241         QMetaObject.connectSlotsByName(ui)
00242         return ui
00243 
00244     import PySide
00245     return PySide.__version__
00246 
00247 
00248 def loadUi(uifile, baseinstance=None, custom_widgets=None):
00249     """
00250     @type uifile: str
00251     @param uifile: Absolute path of .ui file
00252     @type baseinstance: QWidget
00253     @param baseinstance: the optional instance of the Qt base class.
00254                          If specified then the user interface is created in
00255                          it. Otherwise a new instance of the base class is
00256                          automatically created.
00257     @type custom_widgets: dict of {str:QWidget}
00258     @param custom_widgets: Class name and type of the custom classes used
00259                            in uifile if any. This can be None if no custom
00260                            class is in use. (Note: this is only necessary
00261                            for PySide, see
00262                            http://answers.ros.org/question/56382/what-does-python_qt_bindingloaduis-3rd-arg-do-in-pyqt-binding/
00263                            for more information)
00264     """
00265     return _loadUi(uifile, baseinstance, custom_widgets)
00266 
00267 
00268 _select_qt_binding(
00269     getattr(sys, 'SELECT_QT_BINDING', None),
00270     getattr(sys, 'SELECT_QT_BINDING_ORDER', None),
00271 )


python_qt_binding
Author(s): Dave Hershberger, Dorian Scholz, Dirk Thomas
autogenerated on Thu Jun 6 2019 21:52:19