Use decorators to simplify using UI creation flow for PySide
Project description
pyside-callbacks
GitHub repository: https://github.com/schang412/pyside-callbacks
A small library that provides utility decorators for simplifying the creation of PySide6 UI. This package also contains a mypy plugin to assist in the type-checking the signals according to the parameters.
The QT Designer workflow with Python would look like this:
- Use QT Designer to create
main_win.ui
file. - Use
uic
to compilemain_win.ui
intomain_win.py
- Sub-Class the class defined in
main_win.py
. - Connect the signals to their handlers.
import main_win
from PySide6 import QtWidgets
class MyQtApp(main_win.Ui_MainWindow, QtWidgets.QMainWindow):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.line_edit_return_pressed)
self.lineEdit.returnPressed.connect(self.line_edit_return_pressed)
def line_edit_return_pressed(self) -> None:
cmd = self.lineEdit.text()
if not cmd:
return
self.lineEdit.setText("")
self.display.appendPlainText(cmd)
However, this connection method does not inherently offer type-checking and could be improved using decorators:
import main_win
from PySide6 import QtWidgets
import pyside_callbacks
@pyside_callbacks.pyside_callbacks
class MyQtApp(main_win.Ui_MainWindow, QtWidgets.QMainWindow):
def __init__(self) -> None:
super().__init__()
self.setupUi(self)
@pyside_callbacks.widget_event("pushButton", "clicked")
@pyside_callbacks.widget_event("lineEdit", "returnPressed")
def line_edit_return_pressed(self) -> None:
cmd = self.lineEdit.text()
if not cmd:
return
self.lineEdit.setText("")
self.display.appendPlainText(cmd)
Note that we need to decorate both the class and the method because we need to add a hook to the
__init__
method in order to register the callback to the class instance. The way that we keep track of the callbacks requires that thewidget_event
decorator is the outermost decorator. However, currently, themypy
plugin expects onlywidget_event
callbacks on functions that use it.In other words, we cannot mix @widget_event with other decorators (for example, @staticmethod).
We can also include a mypy
plugin to ensure that our signals are correct. We add the pyside_callbacks_mypy
plugin and suppress the errors from the uic
generated file.
[tool.mypy]
plugins = [
"pyside_callbacks_mypy.plugin"
]
[[tool.mypy.overrides]]
module = "main_win"
ignore_errors = true
Adding the following lines to the example application:
@pyside_callbacks.widget_event("lineEdit", "cursorPositionChanged")
def curpos_changed(self, b: str) -> None:
print("changed cursor position!")
Then, running mypy
we will find the errors:
example/my_app/app.py:34: error: Argument 2 to "curpos_changed" has incompatible type "str"; Emitted signal will expect type "int". [arg-type]
example/my_app/app.py:34: error: Too many arguments for "curpos_changed"; Emitted signal will supply ["int", "int"] [call-arg]
Found 2 errors in 1 file (checked 2 source files)
Note that we have to add a type-hint to
main_win.Ui_MainWindow.setupUi
otherwise dynamic types (typing.Any) will be inferred for all the widgets.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for pyside_callbacks-0.1.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8dd5f8ce00b7d2a289675d216c70ebb0500503abff35cff3e9418bd37a56b96a |
|
MD5 | 15f0789b1c5723e594ddcf239493170a |
|
BLAKE2b-256 | 074eb8ead624f68c1c505e7caa5a23c2e4f86b0503aeccfdc7aa7e8c8b21c252 |