UI handlers
Source: src/app/ui/handlers
Qt widgets only emit signals such as "button clicked" or "box dragged". They do not know what those events mean. A handler is the small object that turns one family of widget signals into application calls and then refreshes the screen. Blurzy has twelve of them, each owning one workflow.
Handlers are the top of the call stack. Everything below them (the Application facade, services, managers, workers) is Qt-free business logic; see the big picture for the whole stack.
Where handlers sit
Arrows are calls. Handlers run on the GUI thread; long work is started through the facade and executes in worker threads that report back with Qt signals.
Who owns whom
Solid arrows show creation: UIController.__init__ builds every handler and
passes itself in. Dashed arrows show that each handler reads widgets from the
Window and calls the Application facade.
| Group | Handlers | Article |
|---|---|---|
| Box editing | AnnotationHandler |
Annotation handler |
| Playback | PlaybackHandler |
Playback handler |
| Rendering, dialogs, preferences | UIHandler, DialogueHandler, PreferencesHandler |
Window, dialogs and preferences |
| Sessions and projects | SessionHandler, ProjectHandler |
Sessions and projects |
| Detection and tracking | DetectionHandler, TrackingHandler, ModelHandler |
Detection and tracking |
| Export and import | ExportHandler, ImportExportHandler |
Export and import |
ModelHandler
ModelHandler (model loading and switching) is grouped with detection
here; see the detection-and-tracking article for its coverage.
The signal-to-refresh loop
Numbers show the usual order for a user action. Dashed arrows are side channels: worker signals (queued onto the GUI thread by Qt) and error display.
- A widget emits a signal; the handler connected it in
_connect_signals(). - The slot calls the Application facade. The facade and services own the rules and mutate session data.
- On success the handler asks the controller to render the current frame.
UIController.render_frame_for_session_id()simply delegates toUIHandler.render_saved_frame(). - On failure the handler shows
window.show_error(title, message). Errors are shown by handlers, never by services.
How handlers are wired
Every handler receives the UIController and stores controller.window and
controller.app. DetectionHandler and TrackingHandler instead receive
controller.ui_controller and controller.ui_app, the narrow
adapters that implement the contracts in
interfaces. The others still use the concrete objects, so
the adapter migration is partial.
Most handlers are QObjects parented to the controller. UIHandler is a
plain class because it only renders and has no signals of its own.
Current behaviour: handlers do call each other
UIController's comments say handlers "should NOT talk to each other".
Today three direct calls exist (red dashed arrows):
ProjectHandler calls SessionHandler (refresh views, initial render),
and SessionHandler calls PreferencesHandler.apply_defaults_to_session()
and PlaybackHandler.stop_playback(). ModelHandler uses a controller
dialog factory instead, which is the intended pattern.
Worked example: one click, end to end
The detailed examples live in the per-handler articles: a delete with undo in Annotation handler, a Play click in Playback handler and a settings change in Window, dialogs and preferences. All follow the loop above.
Shared state and keyboard routing
Presentation state shared across handlers lives on the window, not in the
handlers: see UI view state. UIController also installs a
FrameTableKeyFilter on the window and both data tables so arrow keys, Delete
and Ctrl shortcuts reach AnnotationHandler.
Concrete dialogs, preview widgets and the main window are the interaction surface. Exact methods are in the API reference.