qts - use the Qt you’ve got

Introduction

Note

qts is presently an exploratory project. It does have test coverage and is significantly documented. It only covers a few Qt modules.

qts is a Qt5/6 and PyQt/PySide compatibility layer for your libraries and applications. It is designed to work with mypy and includes a CLI utility to notify mypy of the needed conditions. To keep the scope reasonable, qts will focus on the variances that all code using Qt will need such as imports and signals. Nuanced detailed differences will not be abstracted away. Helper functions and similar may be provided on a case by case basis.

import qts
import qts.util


def main():
    qts.set_wrapper(qts.available_wrappers()[0])

    from qts import QtWidgets

    application = QtWidgets.QApplication([])
    widget = QtWidgets.QLabel("this is qts")
    widget.show()
    qts.util.exec(application)

main()

qts

The top level package exposes the basic functionality of the qts library.

qts.__version__: str = 'v0.3'

The qts version string.

Selecting a wrapper

A Qt wrapper must be chosen before leveraging the qts compatibility features. qts is notified of the choice by a call to qts.set_wrapper(). qts.autoset_wrapper() automatically chooses and sets an available wrapper. In any case, qts checks for wrappers that have already been imported. The setting of a wrapper will fail if only unsupported wrappers are already imported. The setting also fails if a supported wrapper other than the one requested is already imported.

qts.set_wrapper(wrapper: qts._core.Wrapper) None[source]

Set the wrapper you want to back the Qt modules accessed through qts.

Raises
qts.autoset_wrapper() None[source]

Automatically choose and set the wrapper used to back the Qt modules accessed through qts. If the environment variable QTS_WRAPPER is set to a name of a supported wrapper then that wrapper will be used. The lookup is case insensitive. If a supported wrapper has already been imported then it will be used.

Raises

qts.InvalidWrapperError – When an unsupported wrapper name is specified in the QTS_WRAPPER environment variable.

Supported wrappers

The objects representing the supported wrappers are directly available. Each is an instance of the qts.Wrapper class. The full list of supported wrappers is available as qts._core.supported_wrappers.

qts._core.pyqt_5_wrapper = Wrapper(family='PyQt', name='PyQt5', major_version=5, module_name='PyQt5')

The PyQt/Qt5 wrapper object.

qts._core.pyqt_6_wrapper = Wrapper(family='PyQt', name='PyQt6', major_version=6, module_name='PyQt6')

The PyQt/Qt6 wrapper object.

qts._core.pyside_5_wrapper = Wrapper(family='PySide', name='PySide2', major_version=5, module_name='PySide2')

The PySide/Qt5 wrapper object.

qts._core.pyside_6_wrapper = Wrapper(family='PySide', name='PySide6', major_version=6, module_name='PySide6')

The PySide/Qt6 wrapper object.

qts._core.supported_wrappers = [Wrapper(family='PySide', name='PySide6', major_version=6, module_name='PySide6'), Wrapper(family='PyQt', name='PyQt6', major_version=6, module_name='PyQt6'), Wrapper(family='PySide', name='PySide2', major_version=5, module_name='PySide2'), Wrapper(family='PyQt', name='PyQt5', major_version=5, module_name='PyQt5')]

A list of all the supported wrapper objects.

class qts.Wrapper(family: str, name: str, major_version: int, module_name: str)[source]

Bases: object

A representation of a specific wrapper that can be used to access a specific version of Qt.

family: str

The wrapper family. "PyQt" or "PySide".

name: str

The name of the specific wrapper, including the version number. Such as "PySide6".

major_version: int

The major version of the wrapped Qt library. Such as 6.

module_name: str

The name used to import the module. Such as "PySide6".

Available wrappers

Not all supported wrappers will be available in every case.

qts.available_wrapper(wrappers: Optional[Iterable[qts._core.Wrapper]] = None) qts._core.Wrapper[source]

Get the available wrapper when there is only one.

Returns

The wrapper object for the single available wrapper.

Raises
qts.available_wrappers(wrappers: Optional[Iterable[qts._core.Wrapper]] = None) Sequence[qts._core.Wrapper][source]

Get a sequence of the wrappers that are available for use. If wrappers is passed, only wrappers that are both available and in the passed iterable will be returned. Availability is checked both by installation metadata and any wrappers that have already been imported.

Returns

The wrappers that are installed and available for use.

qts.an_available_wrapper(wrappers: Optional[Iterable[qts._core.Wrapper]] = None) qts._core.Wrapper[source]

Get an available wrapper when there is one or more available.

Parameters

wrappers – The wrappers to consider. All if not specified.

Returns

The wrapper object for the single available wrapper.

Raises

qts.NoWrapperAvailableError – When no wrappers are available.

qts.wrapper_by_name(name: str) qts._core.Wrapper[source]

Get a wrapper object by its name. The name is checked case insensitively.

Returns

The wrapper that goes by the passed name.

Present configuration

You can directly query the present wrapper object multiple ways. The wrapper object can be retrieved directly through qts.wrapper. In some cases it is more useful to simply check if a specific wrapper is selected. The qts.is_* values are helpful for this. In particular, mypy is able to understand booleans via the command line arguments --always-false and --always-true. The Command line interface can be used to help generate the relevant options to pass to mypy.

qts.wrapper: Optional[qts._core.Wrapper]

The presently active wrapper. None if no wrapper is set.

qts.is_pyqt_5_wrapper: bool = False

True if the PyQt/Qt5 wrapper is active.

qts.is_pyqt_6_wrapper: bool = False

True if the PyQt/Qt6 wrapper is active.

qts.is_pyside_5_wrapper: bool = True

True if the PySide/Qt5 wrapper is active.

qts.is_pyside_6_wrapper: bool = False

True if the PySide/Qt6 wrapper is active.

Exceptions

See Exceptions.

Command line interface

This tooling is a bit sparse right now. Perhaps other useful helpers will be identified in the future.

qts

qts [OPTIONS] COMMAND [ARGS]...
mypy
qts mypy [OPTIONS] COMMAND [ARGS]...
args
Generate arguments to be passed to mypy so it can understand which code should

be active. If applications or other libraries use the same conditions in their code then this will work for them as well. The output can be directly injected in some shells such as is done below in bash.

$ mypy $(qts mypy args --wrapper pyside6) my_file.py

The module import selection code in qts itself can act as a reference.

 1import qts
 2
 3
 4if qts.wrapper is None:
 5    qts.autoset_wrapper()
 6
 7if qts.is_pyqt_5_wrapper:
 8    from PyQt5.QtCore import *
 9elif qts.is_pyqt_6_wrapper:
10    from PyQt6.QtCore import *
11elif qts.is_pyside_5_wrapper:
12    from PySide2.QtCore import *
13elif qts.is_pyside_6_wrapper:
14    from PySide6.QtCore import *
15else:
16    raise qts.InvalidWrapperError(wrapper=qts.wrapper)
qts mypy args [OPTIONS]

Options

--wrapper <wrapper_name>
Options

PySide6 | PyQt6 | PySide2 | PyQt5

--delimiter <delimiter>

Defaults to a space for TTYs and a newline otherwise.

Utilities

qts.util.exec(execable: qts.util.ExecProtocol) int[source]

Call the proper execute method on the passed object. Either .exec() or .exec_() is chosen based on the configured wrapper. .exec_() exists as an artifact from supporting Python 2 where exec was a keyword.

class qts.util.ExecProtocol(*args, **kwargs)[source]

Bases: Protocol

A protocol that requires the presence of either .exec() or .exec_() if the selected wrapper requires the trailing _.

exec_() int[source]

Used only in cases where the trailing _ is required.

exec() int[source]

Used in all cases where the trailing _ is not required

Exceptions

class qts.QtsError[source]

Bases: Exception

Base exception class for all qts exceptions.

Do not raise directly.

class qts.InternalError[source]

Bases: qts._errors.QtsError

Raised when things that should not happen do, and they aren’t the user’s fault.

class qts.InvalidWrapperError(wrapper: object)[source]

Bases: qts._errors.QtsError

Raised when an invalid wrapper is specified.

class qts.MultipleWrappersAvailableError(searched: Iterable[qts._core.Wrapper], found: Iterable[qts._core.Wrapper])[source]

Bases: qts._errors.QtsError

Raised when searching for one wrapper but multiple are available.

class qts.NoWrapperAvailableError(wrappers: Iterable[qts._core.Wrapper])[source]

Bases: qts._errors.QtsError

Raised when searching for wrappers and none are available.

class qts.OtherWrapperAlreadyImportedError(requested: qts._core.Wrapper, already_imported: Collection[qts._core.Wrapper])[source]

Bases: qts._errors.QtsError

Raised when wrappers have already been imported but a call is made to set another.

class qts.WrapperAlreadySelectedError(existing_wrapper: qts._core.Wrapper, requested_wrapper: qts._core.Wrapper)[source]

Bases: qts._errors.QtsError

Raised when attempting to set a wrapper but one has already been selected.

Release History

qts 0.3

Features

qts 0.2

Features
  • qts.autoset_wrapper() automatically selects an available wrapper and sets it. (#21)

  • qts.an_available_wrapper() returns a single wrapper that is available as long as there is at least one to choose. (#21)

  • Importing the Qt modules will set the wrapper via qts.autoset_wrapper() if one has not already been set. There is hope to provide a more explicit, yet still easily usable, API but this automation will be available for now. It may be removed or discouaraged at a later time. (#21)

Removals
  • NoWrapperSelectedError is no longer applicable since a wrapper will be automatically selected if needed. (#21)

qts 0.1

  • Initial release

Qt Modules

QtCore

Note

This documentation is meant only to describe the details that qts helps make more common. Complete documentation is left to the individual wrappers and the underlying C++ Qt library.

qts.QtCore.Signal

The class attribute Signal object. It is a descriptor that evaluates to a SignalInstance when accessed on an instance of its owning class.

  • PyQt - pyqtSignal

  • PySide - Signal

qts.QtCore.SignalInstance

The instance attribute Signal object that allows subscription to the signal via .connect() and emission via .emit() and so on.

  • PyQt - pyqtBoundSignal

  • PySide - SignalInstance

QtGui

Note

This documentation is meant only to describe the details that qts helps make more common. Complete documentation is left to the individual wrappers and the underlying C++ Qt library.

No special handling is provided for this module.

QtWidgets

Note

This documentation is meant only to describe the details that qts helps make more common. Complete documentation is left to the individual wrappers and the underlying C++ Qt library.

No special handling is provided for this module.