@window¶
The @window decorator transforms a class into a Qt main window with automatic setup and configuration.
Basic Usage¶
from qtpy.QtWidgets import QMainWindow
from qtpie import window
@window
class MyWindow(QMainWindow):
pass
With parentheses and parameters:
Parameters¶
title¶
Type: str | None
Default: None
Sets the window title.
@window(title="My Application")
class MyWindow(QMainWindow):
pass
# Window title will be "My Application"
Without a title parameter, the window title is empty.
size¶
Type: tuple[int, int] | None
Default: None
Sets the window dimensions as (width, height).
@window(size=(800, 600))
class MyWindow(QMainWindow):
pass
# Window will be 800 pixels wide, 600 pixels tall
icon¶
Type: str | None
Default: None
Sets the window icon from a file path.
Supports any image format that Qt can load (PNG, JPG, SVG, etc.).
center¶
Type: bool
Default: False
Centers the window on the screen. Must be used with size parameter.
@window(title="Centered", size=(400, 300), center=True)
class MyWindow(QMainWindow):
pass
# Window will appear centered on screen
name¶
Type: str | None
Default: None (auto-generated from class name)
Sets the widget's objectName for QSS styling.
Auto-naming¶
If name is not provided, it's derived from the class name:
@window
class SomeWindow(QMainWindow):
pass
# objectName is "Some" (Window suffix stripped)
@window
class Editor(QMainWindow):
pass
# objectName is "Editor" (no suffix to strip)
classes¶
Type: list[str] | None
Default: None
Sets CSS-like classes for styling via QSS.
@window(classes=["dark-theme", "main-window"])
class MyWindow(QMainWindow):
pass
# Can be styled with QSS:
# QMainWindow[class~="dark-theme"] { ... }
The classes are stored as a Qt property named "class":
Central Widget¶
A field named central_widget is automatically set as the window's central widget:
from qtpy.QtWidgets import QTextEdit
from qtpie import window, make
@window
class MyWindow(QMainWindow):
central_widget: QTextEdit = make(QTextEdit)
# Equivalent to calling: self.setCentralWidget(self.central_widget)
The central widget receives all child content:
from qtpy.QtWidgets import QLabel
@window
class MyWindow(QMainWindow):
central_widget: QLabel = make(QLabel, "Hello from central!")
w = MyWindow()
assert w.centralWidget() is w.central_widget
assert w.central_widget.text() == "Hello from central!"
If no central_widget field exists, the window has no central widget (which is valid for QMainWindow).
Menu Bar Integration¶
QMenu fields are automatically added to the window's menu bar:
from dataclasses import field
from qtpy.QtWidgets import QMenu
@window
class MyWindow(QMainWindow):
file_menu: QMenu = field(default_factory=lambda: QMenu("&File"))
edit_menu: QMenu = field(default_factory=lambda: QMenu("&Edit"))
# Both menus appear in the menu bar
Private Menus¶
Fields starting with _ are NOT added to the menu bar:
@window
class MyWindow(QMainWindow):
_hidden_menu: QMenu = field(default_factory=lambda: QMenu("Hidden"))
visible_menu: QMenu = field(default_factory=lambda: QMenu("Visible"))
# Only visible_menu appears in menu bar
Lifecycle Hook¶
Override setup() to customize initialization:
@window
class MyWindow(QMainWindow):
central_widget: QLabel = make(QLabel, "Initial")
def setup(self) -> None:
# Called after fields are initialized
self.central_widget.setText("Modified in setup")
The hook has access to fully initialized child widgets.
Async closeEvent¶
The @window decorator automatically wraps async closeEvent methods with qasync.asyncClose, allowing cleanup operations to complete before the window is destroyed:
import asyncio
@window(title="My App")
class MyWindow(QMainWindow):
async def closeEvent(self, event: QCloseEvent) -> None:
# Async cleanup - runs to completion before window closes
await self.save_session_async()
await self.disconnect_from_server()
event.accept()
This ensures async cleanup completes before the window is destroyed. Without this wrapping, the coroutine would be created but not awaited.
Requirements: Requires qasync to be installed.
Signal Connections¶
Signals are connected via the make() factory:
from qtpy.QtWidgets import QPushButton
@window
class MyWindow(QMainWindow):
central_widget: QPushButton = make(QPushButton, "Click", clicked="on_click")
def on_click(self) -> None:
print("Button clicked!")
Both method names (strings) and lambdas work:
@window
class MyWindow(QMainWindow):
central_widget: QPushButton = make(
QPushButton,
"Click",
clicked=lambda: print("Clicked!"),
)
Field Defaults¶
Fields can have default values:
@window
class MyWindow(QMainWindow):
count: int = 42
name: str = "default"
w = MyWindow()
assert w.count == 42
assert w.name == "default"
Fields can be overridden via constructor kwargs:
Complete Example¶
from dataclasses import field
from qtpy.QtWidgets import QMainWindow, QTextEdit, QMenu
from qtpie import window, make
@window(
title="Text Editor",
size=(1024, 768),
icon="resources/app_icon.png",
center=True,
classes=["main-window"],
)
class EditorWindow(QMainWindow):
# Central widget
central_widget: QTextEdit = make(QTextEdit, textChanged="mark_dirty")
# Menus
file_menu: QMenu = field(default_factory=lambda: QMenu("&File"))
edit_menu: QMenu = field(default_factory=lambda: QMenu("&Edit"))
# Custom fields
unsaved_changes: bool = False
def setup(self) -> None:
# Configure after initialization
self.statusBar().showMessage("Ready")
def mark_dirty(self) -> None:
self.unsaved_changes = True
See Also¶
- @widget - For general widgets
- @menu - For menu configuration
- @action - For menu actions
- make() - Widget factory
- Windows & Menus Guide - Complete guide