git-cola v2.7
[git-cola.git] / extras / qtpy / qtpy / uic.py
blobff049ff795955d7f33d324473cd1681cb11b2c2f
1 import os
3 from qtpy import PYSIDE, PYQT4, PYQT5
4 from qtpy.QtWidgets import QComboBox
6 __all__ = ['loadUi']
8 if PYQT5:
10 from PyQt5.uic import loadUi
12 elif PYQT4:
14 from PyQt4.uic import loadUi
16 elif PYSIDE:
18 # In PySide, loadUi does not exist, so we define it using QUiLoader, and
19 # then make sure we expose that function. This is adapted from qt-helpers
20 # which was released under a 3-clause BSD license:
21 # qt-helpers - a common front-end to various Qt modules
23 # Copyright (c) 2015, Chris Beaumont and Thomas Robitaille
25 # All rights reserved.
27 # Redistribution and use in source and binary forms, with or without
28 # modification, are permitted provided that the following conditions are
29 # met:
31 # * Redistributions of source code must retain the above copyright
32 # notice, this list of conditions and the following disclaimer.
33 # * Redistributions in binary form must reproduce the above copyright
34 # notice, this list of conditions and the following disclaimer in the
35 # documentation and/or other materials provided with the
36 # distribution.
37 # * Neither the name of the Glue project nor the names of its contributors
38 # may be used to endorse or promote products derived from this software
39 # without specific prior written permission.
41 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
42 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
43 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
44 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
45 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
46 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
47 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
48 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
49 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
50 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
51 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53 # Which itself was based on the solution at
55 # https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8
57 # which was released under the MIT license:
59 # Copyright (c) 2011 Sebastian Wiesner <lunaryorn@gmail.com>
60 # Modifications by Charl Botha <cpbotha@vxlabs.com>
62 # Permission is hereby granted, free of charge, to any person obtaining a
63 # copy of this software and associated documentation files (the "Software"),
64 # to deal in the Software without restriction, including without limitation
65 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
66 # and/or sell copies of the Software, and to permit persons to whom the
67 # Software is furnished to do so, subject to the following conditions:
69 # The above copyright notice and this permission notice shall be included in
70 # all copies or substantial portions of the Software.
72 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
73 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
74 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
75 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
76 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
77 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
78 # DEALINGS IN THE SOFTWARE.
80 from PySide.QtCore import QMetaObject
81 from PySide.QtUiTools import QUiLoader
83 class UiLoader(QUiLoader):
84 """
85 Subclass of :class:`~PySide.QtUiTools.QUiLoader` to create the user
86 interface in a base instance.
88 Unlike :class:`~PySide.QtUiTools.QUiLoader` itself this class does not
89 create a new instance of the top-level widget, but creates the user
90 interface in an existing instance of the top-level class if needed.
92 This mimics the behaviour of :func:`PyQt4.uic.loadUi`.
93 """
95 def __init__(self, baseinstance, customWidgets=None):
96 """
97 Create a loader for the given ``baseinstance``.
99 The user interface is created in ``baseinstance``, which must be an
100 instance of the top-level class in the user interface to load, or a
101 subclass thereof.
103 ``customWidgets`` is a dictionary mapping from class name to class
104 object for custom widgets. Usually, this should be done by calling
105 registerCustomWidget on the QUiLoader, but with PySide 1.1.2 on
106 Ubuntu 12.04 x86_64 this causes a segfault.
108 ``parent`` is the parent object of this loader.
111 QUiLoader.__init__(self, baseinstance)
113 self.baseinstance = baseinstance
115 if customWidgets is None:
116 self.customWidgets = {}
117 else:
118 self.customWidgets = customWidgets
120 def createWidget(self, class_name, parent=None, name=''):
122 Function that is called for each widget defined in ui file,
123 overridden here to populate baseinstance instead.
126 if parent is None and self.baseinstance:
127 # supposed to create the top-level widget, return the base
128 # instance instead
129 return self.baseinstance
131 else:
133 # For some reason, Line is not in the list of available
134 # widgets, but works fine, so we have to special case it here.
135 if class_name in self.availableWidgets() or class_name == 'Line':
136 # create a new widget for child widgets
137 widget = QUiLoader.createWidget(self, class_name, parent, name)
139 else:
140 # If not in the list of availableWidgets, must be a custom
141 # widget. This will raise KeyError if the user has not
142 # supplied the relevant class_name in the dictionary or if
143 # customWidgets is empty.
144 try:
145 widget = self.customWidgets[class_name](parent)
146 except KeyError:
147 raise Exception('No custom widget ' + class_name + ' '
148 'found in customWidgets')
150 if self.baseinstance:
151 # set an attribute for the new child widget on the base
152 # instance, just like PyQt4.uic.loadUi does.
153 setattr(self.baseinstance, name, widget)
155 return widget
157 def _get_custom_widgets(ui_file):
159 This function is used to parse a ui file and look for the <customwidgets>
160 section, then automatically load all the custom widget classes.
163 import sys
164 import importlib
165 from xml.etree.ElementTree import ElementTree
167 # Parse the UI file
168 etree = ElementTree()
169 ui = etree.parse(ui_file)
171 # Get the customwidgets section
172 custom_widgets = ui.find('customwidgets')
174 if custom_widgets is None:
175 return {}
177 custom_widget_classes = {}
179 for custom_widget in custom_widgets.getchildren():
181 cw_class = custom_widget.find('class').text
182 cw_header = custom_widget.find('header').text
184 module = importlib.import_module(cw_header)
186 custom_widget_classes[cw_class] = getattr(module, cw_class)
188 return custom_widget_classes
190 def loadUi(uifile, baseinstance=None, workingDirectory=None):
192 Dynamically load a user interface from the given ``uifile``.
194 ``uifile`` is a string containing a file name of the UI file to load.
196 If ``baseinstance`` is ``None``, the a new instance of the top-level
197 widget will be created. Otherwise, the user interface is created within
198 the given ``baseinstance``. In this case ``baseinstance`` must be an
199 instance of the top-level widget class in the UI file to load, or a
200 subclass thereof. In other words, if you've created a ``QMainWindow``
201 interface in the designer, ``baseinstance`` must be a ``QMainWindow``
202 or a subclass thereof, too. You cannot load a ``QMainWindow`` UI file
203 with a plain :class:`~PySide.QtGui.QWidget` as ``baseinstance``.
205 :method:`~PySide.QtCore.QMetaObject.connectSlotsByName()` is called on
206 the created user interface, so you can implemented your slots according
207 to its conventions in your widget class.
209 Return ``baseinstance``, if ``baseinstance`` is not ``None``. Otherwise
210 return the newly created instance of the user interface.
213 # We parse the UI file and import any required custom widgets
214 customWidgets = _get_custom_widgets(uifile)
216 loader = UiLoader(baseinstance, customWidgets)
218 if workingDirectory is not None:
219 loader.setWorkingDirectory(workingDirectory)
221 widget = loader.load(uifile)
222 QMetaObject.connectSlotsByName(widget)
223 return widget