Skip to main content

QtPie

Tasty way to build Qt apps

from qtpie import entrypoint, make, state, widget
from qtpy.QtWidgets import QLabel, QPushButton, QWidget


@entrypoint
@widget
class Counter(QWidget):
    count: int = state(0)
    label: QLabel = make(QLabel, bind="Count: {count}")
    button: QPushButton = make(QPushButton, "+1", clicked="increment")

    def increment(self) -> None:
        self.count += 1

Click the button. State changes. Label updates. That's it.

Declarative. Reactive. Delightful.

Install

pip install qtpie

Features

  • state() - reactive variables that update the UI
  • bind="{x}" - format expressions with auto-refresh
  • clicked="method" - signal connections by name
  • @widget - dataclass-style components with automatic layouts
  • Widget[T] - type-safe model binding
  • SCSS hot reload - style with CSS classes
  • Async support - async def just works
  • pyright strict - full type safety, no compromises

Why QtPie?

Qt is powerful but verbose:

# Plain Qt - 35 lines of boilerplate
class Counter(QWidget):
    def __init__(self):
        super().__init__()
        self.setObjectName("Counter")
        self.count = 0

        layout = QVBoxLayout(self)

        self.label = QLabel("Count: 0")
        self.label.setObjectName("label")
        layout.addWidget(self.label)

        self.button = QPushButton("Add")
        self.button.setObjectName("button")
        self.button.clicked.connect(self.increment)
        layout.addWidget(self.button)

    def increment(self):
        self.count += 1
        self.label.setText(f"Count: {self.count}")


if __name__ == "__main__":
    app = QApplication([])
    window = Counter()
    window.show()
    app.exec()

vs QtPie - 12 lines, fully reactive:

@entrypoint
@widget
class Counter(QWidget):
    count: int = state(0)
    label: QLabel = make(QLabel, bind="Count: {count}")
    button: QPushButton = make(QPushButton, "+1", clicked="increment")

    def increment(self) -> None:
        self.count += 1

Quick Examples

Reactive State

count: int = state(0)
label: QLabel = make(QLabel, bind="Count: {count}")

self.count += 1  # Label updates instantly

Two-Way Binding

@widget
class Greeter(QWidget):
    name: str = state("")
    name_input: QLineEdit = make(QLineEdit, bind="name")
    greeting: QLabel = make(QLabel, bind="Hello, {name}!")

Type in the input, greeting updates. Change self.name, input updates.

Format Expressions

bind="Count: {count}"
bind="{first} {last}"
bind="{name.upper()}"
bind="Total: ${price * 1.1:.2f}"

Automatic Layouts

@widget
class MyWidget(QWidget):
    top: QLabel = make(QLabel, "Top")
    middle: QLabel = make(QLabel, "Middle")
    bottom: QLabel = make(QLabel, "Bottom")

@widget(layout="form")
class MyForm(QWidget):
    name: QLineEdit = make(QLineEdit, form_label="Name:")
    email: QLineEdit = make(QLineEdit, form_label="Email:")

Model Binding

@dataclass
class Person:
    name: str = ""
    age: int = 0

@widget
class PersonEditor(QWidget, Widget[Person]):
    name: QLineEdit = make(QLineEdit)  # auto-binds to model.name
    age: QSpinBox = make(QSpinBox)      # auto-binds to model.age

Full Qt Access

QtPie is a layer, not a cage. All of Qt is still there:

@widget
class MyWidget(QWidget):
    label: QLabel = make(QLabel, "Hello")

    def setup(self) -> None:
        self.setWindowTitle("My App")
        self.label.setStyleSheet("color: red;")

Documentation

https://mrowrlib.github.io/qtpie

License

0BSD

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qtpie-0.1.10.tar.gz (52.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qtpie-0.1.10-py3-none-any.whl (73.1 kB view details)

Uploaded Python 3

File details

Details for the file qtpie-0.1.10.tar.gz.

File metadata

  • Download URL: qtpie-0.1.10.tar.gz
  • Upload date:
  • Size: 52.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for qtpie-0.1.10.tar.gz
Algorithm Hash digest
SHA256 be0c37ea668e829af4df9faf9cbe9c59ea51909f860d9e07d6fbeab6041b2d1e
MD5 d810794a2b962c69d835300b75f45434
BLAKE2b-256 d46acbcfb62f4d90d916dd147880524683245a44fc1c7d100832aecc9c4e0792

See more details on using hashes here.

File details

Details for the file qtpie-0.1.10-py3-none-any.whl.

File metadata

  • Download URL: qtpie-0.1.10-py3-none-any.whl
  • Upload date:
  • Size: 73.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for qtpie-0.1.10-py3-none-any.whl
Algorithm Hash digest
SHA256 0b02c54931a8f10d811c22967eb0ea890dcc6d993f2c7ad04092e5dbf6aa2e22
MD5 6a7fc886e932904173164a51b0af3ca2
BLAKE2b-256 e84add261cea9d7d92a45672980609ab772efee9d135e2b6a2c856cfa53045d9

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.10 This release

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page