App & Entry Points¶
QtPie provides three ways to launch your application:
@entrypointdecorator - The simplest approach for single-file appsAppclass - Full control with lifecycle hooksrun_app()function - Use any QApplication with qasync support
The @entrypoint Decorator¶
The @entrypoint decorator is the easiest way to create a runnable app. When you run the file directly (as __main__), it automatically creates a QApplication and starts the event loop.
Function Entry Point¶
The simplest form - just return a widget:
from qtpie import entrypoint
from qtpy.QtWidgets import QLabel
@entrypoint
def main():
return QLabel("Hello, World!")
Run it: python my_app.py
That's it! No manual app creation, no event loop boilerplate.
Widget Class Entry Point¶
Combine with @widget for declarative UI:
from qtpie import entrypoint, make, widget
from qtpy.QtWidgets import QLabel, QPushButton, QWidget
@entrypoint
@widget
class MyApp(QWidget):
label: QLabel = make(QLabel, "Count: 0")
button: QPushButton = make(QPushButton, "+1", clicked="increment")
count: int = 0
def increment(self):
self.count += 1
self.label.setText(f"Count: {self.count}")
Configuration Options¶
Customize the app with parameters:
@entrypoint(
dark_mode=True,
title="My Application",
size=(800, 600)
)
@widget
class MyApp(QWidget):
...
Available options:
dark_mode: bool- Enable dark mode color schemelight_mode: bool- Enable light mode color schemetitle: str- Set window titlesize: tuple[int, int]- Set window size (width, height)stylesheet: str- Path to QSS/SCSS file to loadwatch_stylesheet: bool- Enable hot-reload for the stylesheetscss_search_paths: Sequence[str]- Directories for SCSS@importresolutionwindow: type[QWidget]- Widget class to instantiate as main window
Stylesheet Options¶
For development with hot-reload:
@entrypoint(
stylesheet="styles/main.scss",
watch_stylesheet=True,
scss_search_paths=["styles/partials"]
)
@widget
class MyApp(QWidget):
pass
Changes to main.scss or any file in partials/ will be detected and applied instantly.
For component-scoped styles, see the @stylesheet decorator.
How It Works¶
The @entrypoint decorator is smart about when to run:
- When file is run directly (
__main__): Creates app, shows window, starts event loop - When imported: Does nothing - just stores configuration
This means you can use the same class in tests or as a library:
# my_app.py
@entrypoint
@widget
class Counter(QWidget):
...
# test_app.py
from my_app import Counter # Doesn't auto-run
def test_counter():
widget = Counter() # Just creates the widget
assert widget.count == 0
Async Entry Points¶
Entry points can be async functions:
The decorator handles qasync setup automatically.
The App Class¶
For more control, subclass App (which extends QApplication):
from typing import override
from qtpie import App, entrypoint
from qtpy.QtWidgets import QLabel
@entrypoint
class MyApp(App):
@override
def create_window(self):
return QLabel("Hello from App!")
Lifecycle Hooks¶
The App class provides hooks for custom initialization:
from typing import override
from qtpie import App
class MyApp(App):
@override
def setup(self):
"""Called after App initialization."""
print("App is ready!")
# Set up services, load config, load stylesheets, etc.
self.load_stylesheet("styles.qss")
@override
def create_window(self):
"""Return the main window widget."""
return MyMainWindow()
Hook order:
1. __init__ - App initialization
2. setup() - General setup
When using @entrypoint, it also calls:
3. create_window() - Creates and shows the window
4. Event loop starts
App Constructor¶
app = App(
name="My Application", # App name (default: "Application")
version="1.0.0", # App version (default: "1.0.0")
dark_mode=True, # Enable dark mode
light_mode=False, # Enable light mode
argv=sys.argv # Command-line args
)
Dark/Light Mode¶
Enable color schemes during initialization or at runtime:
# At initialization
app = App(dark_mode=True)
# At runtime
app.enable_dark_mode()
app.enable_light_mode()
Loading Stylesheets¶
With QRC resources:
Running the App¶
The App class has two run methods:
# Blocking - runs until app quits
app = MyApp()
window = app.create_window()
window.show()
exit_code = app.run() # Blocks here
# Async - for use in existing event loop
app = MyApp()
window = app.create_window()
window.show()
exit_code = await app.run_async()
The run_app() Function¶
A standalone helper that works with any QApplication:
from qtpie import run_app
from qtpy.QtWidgets import QApplication, QLabel
app = QApplication([])
label = QLabel("Hello!")
label.show()
run_app(app) # Sets up qasync and runs event loop
Why run_app()?¶
Standard Qt applications use app.exec(), but that doesn't support async/await. The run_app() function sets up qasync automatically, giving you:
- Full async/await support in signals/slots
- Compatibility with asyncio libraries
- Better integration with modern Python async code
Using with Standard QApplication¶
from qtpy.QtWidgets import QApplication
from qtpie import run_app
app = QApplication([])
# Set up your UI
window = MyWindow()
window.show()
# Run with qasync support
exit_code = run_app(app)
Using with Custom QApplication¶
Works with any QApplication subclass:
class CustomApp(QApplication):
def __init__(self, argv):
super().__init__(argv)
# Custom initialization
app = CustomApp([])
# ... set up UI ...
run_app(app)
Comparison: Which Should I Use?¶
Use @entrypoint when:¶
- Building single-file apps
- Want minimal boilerplate
- Don't need app lifecycle hooks
- Prototyping or creating examples
Use App class when:¶
- Need the
setup()lifecycle hook - Want to manage app state centrally
- Building larger applications with services/config
- Need custom QApplication subclass behavior
Use run_app() when:¶
- Integrating QtPie into existing codebases
- Already have QApplication instance
- Need maximum control over app creation
- Using custom QApplication subclass
Complete Examples¶
Simple Counter (Function Entry Point)¶
from qtpie import entrypoint, make, widget, state
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):
self.count += 1
App with Configuration (App Class)¶
from typing import override
from qtpie import App, entrypoint, make, widget
from qtpy.QtWidgets import QLabel, QWidget
@widget
class MainWindow(QWidget):
label: QLabel = make(QLabel, "Welcome!")
@entrypoint(dark_mode=True, title="My App", size=(1024, 768))
class MyApp(App):
@override
def setup(self):
self.load_stylesheet("assets/styles.scss")
@override
def create_window(self):
return MainWindow()
Manual Control (run_app Function)¶
from qtpie import run_app, App, make, widget
from qtpy.QtWidgets import QLabel, QWidget
@widget
class MyWidget(QWidget):
label: QLabel = make(QLabel, "Hello!")
# Manual setup
app = App("My Application", dark_mode=True)
app.load_stylesheet("styles.qss")
widget = MyWidget()
widget.show()
# Run
exit_code = run_app(app)
Advanced: Async Applications¶
Full async example with background tasks:
import asyncio
from qtpie import entrypoint, make, widget, state
from qtpy.QtWidgets import QLabel, QPushButton, QWidget
@entrypoint
@widget
class AsyncCounter(QWidget):
count: int = state(0)
label: QLabel = make(QLabel, bind="Count: {count}")
start_btn: QPushButton = make(QPushButton, "Start", clicked="start_counting")
async def start_counting(self):
self.start_btn.setEnabled(False)
for i in range(10):
await asyncio.sleep(1)
self.count += 1
self.start_btn.setEnabled(True)
The run_app() function (used internally by @entrypoint) sets up qasync automatically, so async/await just works.
Testing Entry Points¶
When writing tests, the @entrypoint decorator won't auto-run because:
- Tests import modules (not
__main__) - pytest-qt creates QApplication already
from qtpie.testing import QtDriver
from my_app import MyApp # Has @entrypoint, won't auto-run
def test_my_app(qt: QtDriver):
app = MyApp() # Just creates the widget
qt.track(app)
assert app.label.text() == "Hello!"
See Testing Guide for more details.
See Also¶
- Testing - Testing QtPie applications
- @entrypoint Reference - Full decorator API
- App Class Reference - Complete App class documentation
- run_app() Reference - Function details