controller.slide_controller
- File:
EuljiroWorship/controller/slide_controller.py
- Author:
Benjamin Jaedon Choi - https://github.com/saintbenjamin
- Affiliated Church:
The Eulji-ro Presbyterian Church [대한예수교장로회(통합) 을지로교회]
- Address:
The Eulji-ro Presbyterian Church, 24-10, Eulji-ro 20-gil, Jung-gu, Seoul 04549, South Korea
- Telephone:
+82-2-2266-3070
- E-mail:
euljirochurch [at] G.M.A.I.L. (replace [at] with @ and G.M.A.I.L as you understood.)
- License:
MIT License with Attribution Requirement (see LICENSE file for details); Copyright (c) 2025 The Eulji-ro Presbyterian Church.
Main slide controller module for managing and broadcasting slides in real-time.
This module provides the GUI entry point and the main controller widget used by the slide controller application. It wires together the PySide6 UI, file watchers, WebSocket broadcasting, and the emergency verse interruptor.
Key responsibilities in this module:
Ensure the project root is importable (sys.path injection for direct execution)
Define
controller.slide_controller.launch_interruptor()to start the verse interruptor as a detached processDefine
controller.slide_controller.SlideController, the main QWidget that:loads and displays slide data
sends slides via WebSocket
reacts to slide file changes / emergency interruptor clear events
- controller.slide_controller.launch_interruptor()[source]
Launch the verse interruptor script as a detached background process.
This starts
controller.helper.verse_interruptorusing the current Python interpreter (sys.executable) and suppressesstdin/stdout/stderrto avoid blocking or cluttering the controller GUI.The interruptor script is expected to watch the emergency verse output file (e.g.,
core.config.paths.VERSE_FILE) and handle its own logic independently.- Parameters:
None
- Returns:
None
- class controller.slide_controller.SlideController(*args, **kwargs)[source]
Bases:
QWidgetMain controller widget for managing and broadcasting worship slides.
This controller loads slides from a JSON file, displays them in a table UI, and broadcasts the currently selected slide to the overlay via WebSocket. It also manages “emergency mode” slides and restores the previous slide set after the interruptor file is cleared.
- slide_file
Path to the slide JSON file.
- Type:
str
- ws_uri
WebSocket URI used to broadcast slide data.
- Type:
str
- data
Slide data manager instance responsible for loading/restoring slides.
- slides
Current list of slide dictionaries.
- Type:
list[dict]
- index
Current slide index.
- Type:
int
- index_backup
Backup index saved before entering emergency mode.
- Type:
int
- emergency_mode
Whether the controller is currently in emergency mode.
- Type:
bool
- slide_factory
Factory used to build emergency slides.
- Type:
- ws_manager
WebSocket manager for overlay communication.
- Type:
- caption_handler
Dialog/handler used to generate emergency slides from user input.
- Type:
- __init__(slide_file, ws_uri)[source]
Initialize the
controller.slide_controller.SlideControllerUI and subsystems.This sets up the window, loads slides, connects to the WebSocket server, builds the UI, and starts background threads for:
Watching slide file changes
Watching interruptor (emergency verse) clear events
- Parameters:
slide_file (str) – Path to the slide JSON file to load.
ws_uri (str) – WebSocket URI for broadcasting slide data (e.g.,
ws://127.0.0.1:8765/ws).
- Returns:
None
- insert_blank_if_needed()[source]
Ensure the slide file begins with a blank slide.
If the file exists and the first slide is not style “blank”, this inserts a blank slide at index 0 and writes the updated list back to disk.
- Returns:
None
- load_slides()[source]
Load slide data from the controller’s slide JSON file.
- Returns:
Parsed list of slide dictionaries loaded from disk.
- Return type:
list
- keyPressEvent(event)[source]
Handle keyboard navigation for slide movement.
Right/Down/Space moves forward, Left/Up moves backward.
- Parameters:
event (QKeyEvent) – Key press event object.
- Returns:
None
- update_label()[source]
Update the UI label and table selection for the current slide.
The label shows:
Current page (1-based)
Total pages
A short preview of the first line of the headline
- Returns:
None
- send_current_slide()[source]
Send the current slide to the overlay via WebSocket.
If the WebSocket is connected, the slide dict at
self.indexis sent. If not connected, a warning is printed.When not in emergency mode, also updates
self.data.indexso the current position can be persisted by the data manager.- Returns:
None
- next_slide()[source]
Move to the next slide if one exists.
Increments
self.index, updates the UI label/table highlight, and broadcasts the slide.- Returns:
None
- prev_slide()[source]
Move to the previous slide if available.
Decrements
self.index, updates the UI label/table highlight, and broadcasts the slide.- Returns:
None
- on_cell_clicked(row, _column)[source]
Jump to a slide when a table row is clicked.
- Parameters:
row (int) – The clicked row index (0-based), used as the slide index.
_column (int) – Unused column index (Qt signal provides it).
- Returns:
None
- on_slide_changed(slides, index)
Handle slide file modification events from
controller.utils.slide_file_watcher.SlideFileWatcher.Replaces the internal slide list and index with the new values, updates the UI, and broadcasts the current slide.
- Parameters:
slides (list) – Newly loaded slides from the watcher.
index (int) – Index to set as the current slide.
- Returns:
None
- on_slide_cleared()
Handle slide file cleared events from
controller.utils.slide_file_watcher.SlideFileWatcher.Attempts to restore slides from backup via the data manager. If restoration fails, falls back to a single blank slide.
- Returns:
None
- on_interruptor_cleared()
Handle interruptor-cleared events from
controller.utils.interruptor_watcher.InterruptorWatcher.When the emergency verse file is cleared, this restores the previous slides (via
on_slide_cleared()) and exits emergency mode.- Returns:
None
- eventFilter(source, event)[source]
Capture key events globally to allow slide navigation without focus.
This enables arrow/space navigation even when focus is on other widgets.
- Parameters:
source (QObject) – Event source object.
event (QEvent) – Incoming event.
- Returns:
True if the event was handled here, otherwise delegates to parent.
- Return type:
bool
- launch_emergency_caption()[source]
Enter emergency mode and generate emergency slides from user input.
Saves the current index to
index_backup, invokes the emergency caption handler, and if slides are returned, replaces the current slide list with the emergency slides starting from index 0.- Returns:
None
- jump_to_index(idx)[source]
Jump directly to a given slide index.
If the index is valid, sets
self.index, broadcasts the slide, updates the table selection, scrolls it into view, and updates the label.- Parameters:
idx (int) – Target slide index (0-based).
- Returns:
None
- jump_to_previous()[source]
Jump to the previous slide and center it in the table view.
- Returns:
None
- jump_to_page()[source]
Jump to a slide page based on the number typed in the page input box.
The UI value is interpreted as 1-based; internally converted to 0-based. If invalid, prints an error message.
- Returns:
None
- clear_emergency_caption()[source]
Clear the emergency verse output file and restore normal slides.
This writes an empty string to
core.config.paths.VERSE_FILE, clearscore.config.paths.SLIDE_FILE, then attempts restoration from backup viacontroller.utils.slide_controller_data_manager.SlideControllerDataManager.After restoration, rebuilds the table, updates the label, and scrolls the restored index into view.
- Returns:
None
- rebuild_table()[source]
Rebuild the slide table widget using the current
self.slides.Each row displays:
Page number (1-based)
Caption
Full headline text
- Returns:
None
- closeEvent(event)[source]
Gracefully stop background threads and disconnect WebSocket before exit.
Stops file watchers, quits threads, waits for them to finish, disconnects the WebSocket manager, then delegates to
QWidget.closeEvent().- Parameters:
event (QCloseEvent) – Close event object.
- Returns:
None