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:

controller.slide_controller.launch_interruptor()[source]

Launch the verse interruptor script as a detached background process.

This starts controller.helper.verse_interruptor using the current Python interpreter (sys.executable) and suppresses stdin/stdout/stderr to 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: QWidget

Main 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.

Type:

SlideControllerDataManager

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:

EmergencySlideFactory

ws_manager

WebSocket manager for overlay communication.

Type:

SlideWebSocketManager

caption_handler

Dialog/handler used to generate emergency slides from user input.

Type:

EmergencyCaptionHandler

__init__(slide_file, ws_uri)[source]

Initialize the controller.slide_controller.SlideController UI 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.index is sent. If not connected, a warning is printed.

When not in emergency mode, also updates self.data.index so 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_next()[source]

Jump to the next 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, clears core.config.paths.SLIDE_FILE, then attempts restoration from backup via controller.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

controller.slide_controller.main()[source]

Entry point for the slide controller application.

This launches the verse interruptor, applies font settings, and starts the GUI event loop.