Skip to content

Commit

Permalink
Merge pull request #8 from astrofrog/patch-load-ui-pyside
Browse files Browse the repository at this point in the history
Properly patch loadUi for PySide
  • Loading branch information
astrofrog committed Mar 3, 2016
2 parents 6f51456 + 0f55597 commit b437b2e
Showing 1 changed file with 155 additions and 12 deletions.
167 changes: 155 additions & 12 deletions qt_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ def reload_qt():
if is_pyside() or is_pyqt4():
patch_qcombobox()

# For PySide, we need to create a loadUi function
if is_pyside():
patch_loadui()


def load_ui(path, parent=None, custom_widgets=None):
if is_pyside():
Expand All @@ -278,19 +282,10 @@ def load_ui(path, parent=None, custom_widgets=None):


def _load_ui_pyside(path, parent, custom_widgets=None):

from PySide.QtUiTools import QUiLoader

loader = QUiLoader()

# must register custom widgets referenced in .ui files
from PySide import loadUi
if custom_widgets is not None:
for w in custom_widgets:
loader.registerCustomWidget(w)

widget = loader.load(path, parent)

return widget
custom_widgets = dict((widget.__name__, widget) for widget in custom_widgets)
return loadUi(path, parent, customWidgets=custom_widgets)


def _load_ui_pyqt4(path, parent):
Expand Down Expand Up @@ -385,5 +380,153 @@ def findData(self, value):
QtGui.QComboBox.findData = findData


def patch_loadui():

# In PySide, loadUi does not exist, so we define it using QUiLoader, and
# then make sure we expose that function. This is based on the solution at
#
# https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8
#
# which was released under the MIT license:
#
# Copyright (c) 2011 Sebastian Wiesner <lunaryorn@gmail.com>
# Modifications by Charl Botha <cpbotha@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.
#
# This version includes further changes.

from PySide.QtCore import Slot, QMetaObject
from PySide.QtUiTools import QUiLoader
from PySide.QtGui import QApplication, QMainWindow, QMessageBox

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
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
# TypeError, if customWidgets is None
try:
widget = self.customWidgets[class_name](parent)
except (TypeError, KeyError) as e:
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 loadUi(uifile, baseinstance=None, customWidgets=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``.
``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.
: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.
"""

loader = UiLoader(baseinstance, customWidgets)

if workingDirectory is not None:
loader.setWorkingDirectory(workingDirectory)

widget = loader.load(uifile)
QMetaObject.connectSlotsByName(widget)
return widget

import PySide
PySide.loadUi = loadUi



# Now load default Qt
reload_qt()

0 comments on commit b437b2e

Please sign in to comment.