is_assignments/a2/code/.venv/lib/python3.10/site-packages/qtpy/uic.py

286 lines
11 KiB
Python

import os
from . import PYSIDE6, PYSIDE2, PYQT5, PYQT6
from .QtWidgets import QComboBox
if PYQT6:
from PyQt6.uic import *
elif PYQT5:
from PyQt5.uic import *
else:
__all__ = ['loadUi', 'loadUiType']
# 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@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.
if PYSIDE6:
from PySide6.QtCore import QMetaObject
from PySide6.QtUiTools import QUiLoader
elif PYSIDE2:
from PySide2.QtCore import QMetaObject
from PySide2.QtUiTools import QUiLoader
try:
from pyside2uic import compileUi
# Patch UIParser as xml.etree.Elementree.Element.getiterator
# was deprecated since Python 3.2 and removed in Python 3.9
# https://docs.python.org/3.9/whatsnew/3.9.html#removed
from pyside2uic.uiparser import UIParser
from xml.etree.ElementTree import Element
class ElemPatched(Element):
def getiterator(self, *args, **kwargs):
return self.iter(*args, **kwargs)
def readResources(self, elem):
return self._readResources(ElemPatched(elem))
UIParser._readResources = UIParser.readResources
UIParser.readResources = readResources
except ImportError:
pass
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 as error:
raise Exception(
f'No custom widget {class_name} '
'found in customWidgets'
) from error
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 list(custom_widgets):
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
def loadUiType(uifile, from_imports=False):
"""Load a .ui file and return the generated form class and
the Qt base class.
The "loadUiType" command convert the ui file to py code
in-memory first and then execute it in a special frame to
retrieve the form_class.
Credit: https://stackoverflow.com/a/14195313/15954282
"""
import sys
from io import StringIO
from xml.etree.ElementTree import ElementTree
from . import QtWidgets
# Parse the UI file
etree = ElementTree()
ui = etree.parse(uifile)
widget_class = ui.find('widget').get('class')
form_class = ui.find('class').text
with open(uifile, encoding="utf-8") as fd:
code_stream = StringIO()
frame = {}
compileUi(fd, code_stream, indent=0, from_imports=from_imports)
pyc = compile(code_stream.getvalue(), '<string>', 'exec')
exec(pyc, frame)
# Fetch the base_class and form class based on their type in the
# xml from designer
form_class = frame['Ui_%s' % form_class]
base_class = getattr(QtWidgets, widget_class)
return form_class, base_class