core.generator.ui.slide_generator
- File:
EuljiroWorship/core/generator/ui/slide_generator.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 window class for the EuljiroWorship slide generator GUI.
This module defines the main Qt window (core.generator.ui.slide_generator.SlideGenerator) that drives the
slide authoring workflow:
Manage a table of slides (add/insert/delete/reorder).
Edit slides via a style-specific modal editor dialog.
Save/load a “session” JSON file for ongoing editing.
Export overlay-ready slide JSON for the live controller/overlay.
Open and apply persistent generator settings (fonts, paths, etc.).
The generator is typically launched from the project entry point
(EuljiroWorship.py) and interacts with a slide controller process
through exported JSON and the WebSocket-based overlay pipeline.
- class core.generator.ui.slide_generator.SlideGenerator(*args, **kwargs)[source]
Bases:
QMainWindowMain window for the EuljiroWorship slide generator.
The generator provides a table-based slide session editor and supports:
Creating, inserting, deleting, and reordering slide rows
Editing each slide via a style-specific modal dialog (double-click)
Loading and saving slide sessions as JSON files
Exporting overlay-ready JSON (prepends a blank slide for a clean initial state)
Launching the slide controller for live output (if not already running)
Core collaborators (high-level):
core.generator.ui.slide_table_manager.SlideTableManager:Owns table row operations (add/insert/delete/move) for the main table widget.
core.generator.utils.slide_generator_data_manager.SlideGeneratorDataManager:Loads/saves and collects slide session data from the table.
core.generator.ui.slide_generator_ui_builder.SlideGeneratorUIBuilder:Builds and wires the generator window UI chrome (menus, buttons, labels).
core.generator.utils.slide_exporter.SlideExporter:Converts the internal slide session into the overlay JSON format.
core.plugin.slide_controller_launcher.SlideControllerLauncher:Launches the controller UI/process that pushes slides to the overlay target.
Note
On startup, this window may show a file-open dialog to load an existing session. If the user cancels, a blank session is created.
Table cells are intentionally non-editable; edits are performed via the modal per-style editor dialog.
- first_save_done
Tracks whether the first save action has completed in the current session. Used to choose between “save as” vs. save-to-last-path on Ctrl+S.
- Type:
bool
- reverse_style_aliases
Reverse mapping from the displayed style label (Korean UI text) to the internal style key (e.g., “가사” -> “lyrics”). Derived from
style_map.REVERSE_ALIASES.- Type:
dict[str, str]
- table
Main slide table with three columns: style, caption, headline. Rows represent slides in the current session.
- Type:
QTableWidget
- detail_widget
Right-side placeholder panel (reserved for future detail views).
- Type:
QWidget
- slide_controller_launcher
Helper that launches (or detects) the running slide controller.
- Type:
- table_manager
Encapsulates row operations and table manipulation logic.
- Type:
- data_manager
Loads/saves session JSON and collects session data from the table.
- ui_builder
UI builder responsible for wiring menus, buttons, and header labels.
- Type:
- worship_name
Session label derived from the loaded filename stem. None for a new unsaved session.
- Type:
str | None
- last_saved_path
Last known save path for the current session. When set, normal save operations write to this path without prompting.
- Type:
str | None
- __init__()[source]
Construct the main generator window and initialize the UI state.
This initializer:
Creates and configures the main slide table widget
Initializes core helper components (table manager, data manager, UI builder, launcher)
Prompts the user to load an existing slide session via an OS file dialog (if canceled, starts with a blank session)
- Parameters:
None
- Returns:
None
- save_slides_to_file(show_message=False)[source]
Save the current slide session to the last saved path.
If no previous save path exists, a timestamp-based filename is generated in the current working directory. The session data is collected from the table via
core.generator.utils.slide_generator_data_manager.SlideGeneratorDataManager.collect_slide_data()and written as UTF-8 JSON.- Parameters:
show_message (bool) – If True, shows a confirmation message box after saving.
- Returns:
None
- handle_ctrl_s()[source]
Handle the Ctrl+S shortcut for saving.
Behavior:
If this is the first save in the current session (or no prior save path exists), triggers a “save as” flow that prompts the user for a path.
Otherwise, saves to the last known path and shows a confirmation dialog.
- Parameters:
None
- Returns:
None
- _get_default_load_directory()[source]
Return the initial directory used by the slide file open dialog.
- Returns:
Directory path derived from the last opened/saved slide file, or the current working directory if no usable record exists.
- Return type:
str
- _prompt_worship_title(default_title)[source]
Ask the user for the worship title that should back the session filename.
The dialog is pre-filled with the selected JSON filename stem. Empty values and filenames containing invalid characters are rejected and the user is prompted again until a valid value is entered.
If the user presses Cancel or closes the dialog, the original default title is used instead of aborting the load flow.
- Parameters:
default_title (str) – Initial text shown in the input field.
- Returns:
Validated title text.
- Return type:
str | None
- _resolve_selected_session_path(source_path)[source]
Resolve the actual path to load after a user picks a session JSON file.
Keeping the default title loads the original file. Changing the title creates a same-folder copy with the new filename and loads that copy.
- Parameters:
source_path (str) – Path selected in the open-file dialog.
- Returns:
Path that should be loaded into the generator, or
Noneif the title step is canceled or the copy operation fails.- Return type:
str | None
- prompt_load_from_file()[source]
Interactively select a slide JSON file and load it into the generator.
This wrapper owns the startup/manual-open flow that the user sees:
Show the OS file-open dialog.
Ask for the worship title using the filename stem as the default.
If the title changes, duplicate the JSON file in the same folder.
Load the chosen original/copy into the generator.
- Returns:
Loaded file path, or
Noneif the flow is canceled.- Return type:
str | None
- load_from_file(path=None)[source]
Load a slide session JSON file into the generator.
If
pathis None, an OS file-open dialog is shown. The initial directory is derived from the last opened file record (if available); otherwise the current working directory is used.After loading:
The table is populated via the data manager
The worship/session name label is updated from the filename stem
first_save_doneis reset so the next Ctrl+S follows the intended flow
- Parameters:
path (str | None) – Absolute or relative path to the JSON file. If None, a dialog is shown.
- Returns:
The resolved path that was loaded, or None if the user canceled the dialog or no file was selected.
- Return type:
str | None
- save_as()[source]
Save the current session using an explicit “Save As” dialog.
This always forces the file-save dialog regardless of whether a previous save path exists.
- Parameters:
None
- Returns:
None
- save_to_file(path=None, force_dialog=False)[source]
Save the current slide session as a JSON file.
Save destination selection rules:
If
pathis provided, saves directly to that path.Else if
force_dialogis False andself.last_saved_pathexists, saves toself.last_saved_pathwithout prompting.Otherwise, opens an OS file-save dialog and saves to the chosen path.
The slide session is collected via
core.generator.utils.slide_generator_data_manager.SlideGeneratorDataManager.collect_slide_data()and written as UTF-8 JSON.- Parameters:
path (str | None) – Destination file path. If None, follows the selection rules above.
force_dialog (bool) – If True, always shows the OS save dialog when
pathis None.
- Returns:
None
- _get_announcement_import_settings_path()[source]
Return the JSON path used to persist announcement import range settings.
This file is stored alongside the existing “last opened file” setting so that the announcement import feature can keep its own lightweight, dedicated configuration without affecting unrelated generator settings.
- Parameters:
None
- Returns:
Absolute path to the announcement import settings JSON file.
- Return type:
str
- _load_announcement_import_settings()[source]
Load persisted announcement import marker settings.
If the settings file does not exist or cannot be parsed, default marker values are returned.
- Parameters:
None
- Returns:
Dictionary containing:
start_headline(str):Headline text that marks the first slide of the import block.
end_headline(str):Headline text that marks the last slide of the import block. If empty, the block extends to the end of the file.
- Return type:
dict
- _save_announcement_import_settings(start_headline, end_headline)[source]
Save announcement import marker settings to a dedicated JSON file.
- Parameters:
start_headline (str) – Headline text marking the first slide of the import block.
end_headline (str) – Headline text marking the last slide of the import block. If empty, the block extends to the end of the file.
- Returns:
None
- Return type:
None
- _prompt_announcement_import_range(start_default, end_default)[source]
Open a modal dialog that asks the user for announcement import markers.
The dialog lets the user define the start and end headlines used when extracting and replacing the announcement block. If the end headline is left empty, the import block extends to the end of the file.
- Parameters:
start_default (str) – Default text to pre-fill in the start marker field.
end_default (str) – Default text to pre-fill in the end marker field.
- Returns:
Two-element tuple of
(start_headline, end_headline).If the user accepts the dialog, stripped string values are returned.
If the user cancels the dialog,
(None, None)is returned.
- Return type:
tuple[str | None, str | None]
- _find_slide_index_by_headline(slides, headline, start_index=0)[source]
Find the first slide index whose headline matches the given text.
Matching is performed using stripped exact string comparison.
- Parameters:
slides (list[dict]) – Slide dictionaries to search.
headline (str) – Target headline text to match.
start_index (int) – Row index from which to begin the search.
- Returns:
Index of the first matching slide, or
Noneif not found.- Return type:
int | None
- _load_slide_list_into_table(slides)[source]
Load an in-memory slide list into the generator table.
This helper clears the current table contents, recreates the required rows, and writes each slide’s style/caption/headline into the visible UI.
- Parameters:
slides (list[dict]) – Slide dictionaries to render into the generator table.
- Returns:
None
- Return type:
None
- _compact_order_text(text)[source]
Remove whitespace from a worship-order string for loose comparisons.
- Parameters:
text (str) – Source text to normalize for structural matching.
- Returns:
Text with all whitespace removed.
- Return type:
str
- _split_choir_caption_parts(choir_name)[source]
Split a choir label like
시온찬양대into("시온", "찬양대").- Parameters:
choir_name (str) – Full choir label parsed from the worship-order source.
- Returns:
Two-element tuple of
(caption, caption_choir). If no standard suffix is found, returns(choir_name, "").- Return type:
tuple[str, str]
- _get_default_bible_version_for_order_import()[source]
Choose the preferred Bible version for worship-order verse imports.
The selection prioritizes locally available Korean Revised Version files and falls back to the first available JSON Bible dataset when the preferred names are not present.
- Parameters:
None
- Returns:
Version name to pass to
BibleDataLoader.- Return type:
str
- _build_hymn_slide_from_number(number)[source]
Build a hymn slide payload from a hymn number dataset.
- Parameters:
number (int) – Hymn number to load from
data/hymns.- Returns:
Slide dictionary containing the hymn style, caption, and headline text for the requested hymn.
- Return type:
dict
- _build_respo_slide_from_number(number)[source]
Build a responsive-reading slide payload from a reading number dataset.
- Parameters:
number (int) – Responsive reading number to load from
data/respo.- Returns:
Slide dictionary containing the responsive-reading style, caption, and combined headline HTML text.
- Return type:
dict
- _build_verse_slide_from_reference(reference)[source]
Build a Bible-reading slide payload from a parsed reference string.
- Parameters:
reference (str) – Scripture reference text extracted from the worship-order bulletin.
- Returns:
Slide dictionary containing the verse style, caption, and resolved Bible text. If parsing fails, an empty verse headline is returned with the original caption preserved.
- Return type:
dict
- _classify_worship_order_slide(slides, index)[source]
Classify a slide into a worship-order slot category.
- Parameters:
slides (list[dict]) – Full slide list currently being analyzed.
index (int) – Index of the slide to classify.
- Returns:
Normalized worship-order kind such as
hymnorsermon, orNonewhen the slide does not match a managed category.- Return type:
str | None
- _describe_worship_order_slide(slide)[source]
Produce a short human-readable description of a worship-order slide.
- Parameters:
slide (dict) – Slide dictionary to summarize.
- Returns:
Compact text summary used in removal-confirmation prompts.
- Return type:
str
- _prompt_remove_missing_worship_order(slide)[source]
Ask the user whether a slide missing from the imported order should be removed.
- Parameters:
slide (dict) – Existing slide dictionary that is not present in the imported HWPX worship-order data.
- Returns:
Trueif the user approves removal, otherwiseFalse.- Return type:
bool
- _remove_worship_order_block(slides, index)[source]
Remove a managed worship-order block from the working slide list.
Anthem blocks span two slides in this project: the
anthemslide and its following lyrics slide. Other kinds are removed as a single slide.- Parameters:
slides (list[dict]) – Mutable slide list being updated.
index (int) – Index of the first slide in the block to remove.
- Returns:
None
- Return type:
None
- _build_worship_order_blocks(slides)[source]
Group the current slide list into first-service worship-order blocks.
- Parameters:
slides (list[dict]) – Existing slide dictionaries collected from the generator table.
- Returns:
Ordered block dictionaries. Each block stores its classified kind and the slides that should move together during updates.
- Return type:
list[dict]
- _build_new_worship_order_block(entry)[source]
Build a new generic first-service slide block for a parsed entry.
- Parameters:
entry (dict) – Parsed first-service worship-order entry dictionary.
- Returns:
New slide dictionaries that represent the requested entry. The block may contain one or more slides depending on the kind.
- Return type:
list[dict]
- _update_worship_order_block(existing_slides, entry)[source]
Update an existing first-service block using a parsed entry.
- Parameters:
existing_slides (list[dict]) – Existing slide dictionaries that make up the matched block.
entry (dict) – Parsed first-service worship-order entry dictionary.
- Returns:
Updated slide dictionaries for the block. When the existing block cannot be updated safely, a new generic block is returned.
- Return type:
list[dict]
- _apply_worship_order_entries(current_slides, order_entries)[source]
Apply imported worship-order entries to the current generator session.
This routine updates matching managed slide slots, prompts about removing obsolete items, and inserts selected special-order templates when needed. Sermon titles are updated conservatively so that imported text is preserved unless an exact preacher suffix can be removed using the current sermon slide caption as context.
- Parameters:
current_slides (list[dict]) – Existing slide dictionaries collected from the generator table.
order_entries (list[dict]) – Parsed worship-order entry dictionaries extracted from HWPX.
- Returns:
Updated slide list ready to be reloaded into the generator table.
- Return type:
list[dict]
- _split_music_group_parts(group_name)[source]
Split a music-group label into its main caption and suffix.
- Parameters:
group_name (str) – Full group label such as
마리아찬양대or여호수아중창단.- Returns:
Two-element tuple of
(caption, caption_choir). If no supported suffix is found, returns(group_name, "").- Return type:
tuple[str, str]
- _classify_praise_service_slide(slides, index)[source]
Classify a slide into an afternoon praise-service slot category.
- Parameters:
slides (list[dict]) – Full slide list currently being analyzed.
index (int) – Index of the slide to classify.
- Returns:
Normalized afternoon-service kind such as
hymnorspecial_praise, orNonewhen the slide does not match a managed category.- Return type:
str | None
- _build_praise_service_blocks(slides)[source]
Group the current slide list into afternoon-service update blocks.
- Parameters:
slides (list[dict]) – Existing slide dictionaries collected from the generator table.
- Returns:
Ordered block dictionaries. Each block stores its classified kind and the slides that should move together during updates.
- Return type:
list[dict]
- _remove_exact_caption_suffix_from_imported_text(imported_text, current_caption)[source]
Remove an exact trailing caption suffix from imported text when present.
- Parameters:
imported_text (str) – Imported text that may include a glued leader or preacher suffix.
current_caption (str) – Existing slide caption used as the authoritative suffix to strip.
- Returns:
Imported text with the exact compacted caption suffix removed from the end when a match exists. Otherwise returns the input text unchanged.
- Return type:
str
- _build_new_praise_service_block(entry)[source]
Build a new generic afternoon-service slide block for a parsed entry.
- Parameters:
entry (dict) – Parsed afternoon praise-service entry dictionary.
- Returns:
New slide dictionaries that represent the requested entry. The block may contain one or more slides depending on the kind.
- Return type:
list[dict]
- _update_praise_service_block(existing_slides, entry)[source]
Update an existing afternoon-service block using a parsed entry.
- Parameters:
existing_slides (list[dict]) – Existing slide dictionaries that make up the matched block.
entry (dict) – Parsed afternoon praise-service entry dictionary.
- Returns:
Updated slide dictionaries for the block. When the existing block cannot be updated safely, a new generic block is returned.
- Return type:
list[dict]
- _flatten_slide_blocks(blocks)[source]
Flatten a block list back into a single slide list.
- Parameters:
blocks (list[dict]) – Block dictionaries that each contain a
slideslist.- Returns:
Flat slide dictionary list in block order.
- Return type:
list[dict]
- _apply_praise_service_order_entries(current_slides, order_entries)[source]
Apply imported afternoon praise-service entries to the current session.
- Parameters:
current_slides (list[dict]) – Existing slide dictionaries collected from the generator table.
order_entries (list[dict]) – Parsed afternoon praise-service entry dictionaries extracted from HWPX.
- Returns:
Updated slide list ready to be reloaded into the generator table.
- Return type:
list[dict]
- import_worship_order_from_hwpx()[source]
Import first-service worship-order information from a HWPX bulletin and conservatively update matching slots in the current generator session.
This first-pass implementation updates the existing session structure rather than rebuilding the whole file from scratch.
- Parameters:
None
- Returns:
None
- import_praise_service_order_from_hwpx()[source]
Import afternoon praise-service order information from a HWPX bulletin and conservatively update matching slots in the current generator session.
Missing special-order items are inserted with generic blocks when needed, while unmatched existing afternoon-service blocks can be kept or removed through confirmation prompts.
- Parameters:
None
- Returns:
None
- _replace_announcement_block_in_slides(slides, imported_slides, start_anchor='오늘 처음 오신 분들을 환영하고 축복합니다!', end_anchor='용서, 사랑의 시작입니다')[source]
Replace the announcement block in a slide list using fixed headline anchors.
- Parameters:
slides (list[dict]) – Working slide list that already contains the surrounding fixed welcome and closing slides.
imported_slides (list[dict]) – Announcement slides extracted from a source HWPX or JSON file.
start_anchor (str) – Headline text that marks the first fixed slide before the announcement block.
end_anchor (str) – Headline text that marks the first fixed slide after the announcement block.
- Returns:
Updated slide list with the announcement block replaced, or
Noneif the anchor range cannot be resolved safely.- Return type:
list[dict] | None
- import_announcements_from_hwpx()[source]
Import announcements from a HWPX bulletin and replace the current announcement area between the fixed welcome slide and the fixed closing-verse slide.
Imported items are intentionally inserted as
lyricsstyle only.The source HWPX file is parsed into announcement slides, and the matching range in the current session is replaced using fixed headline anchors that mark the start and end of the announcement block.
- Parameters:
None
- Returns:
None
- import_worship_order_and_announcements_from_hwpx()[source]
Import both first-service worship-order information and announcement slides from a single HWPX bulletin and apply them in one pass.
The existing session is first updated with the imported worship order, then the announcement block between the fixed anchors is replaced with announcement slides extracted from the same HWPX file.
- Parameters:
None
- Returns:
None
- import_praise_service_order_and_announcements_from_hwpx()[source]
Import both afternoon praise-service order information and announcement slides from a single HWPX bulletin and apply them in one pass.
The existing session is first updated with the imported afternoon praise-service order, then the announcement block between the fixed anchors is replaced with announcement slides extracted from the same HWPX file.
- Parameters:
None
- Returns:
None
- import_announcements()[source]
Import a configurable slide block from an external slide JSON file and replace the corresponding block in the current session.
This method opens a file-selection dialog, asks the user for the start and end headline markers that define the import range, persists those marker values to a dedicated settings JSON file, and then replaces the matching range in the currently loaded generator table.
The imported range is extracted from the selected source file and applied onto the current session using the same start/end markers.
- Parameters:
None
- Returns:
None
- Raises:
None explicitly. –
Any file I/O or JSON parsing errors are handled internally and –
reported to the user via message dialogs. –
Notes
Range boundaries are detected by exact headline matching after stripping leading and trailing whitespace.
If the end marker is empty, the import block extends to the end of the file.
If either the source file or the current session does not contain the requested start marker, no changes are applied.
This function modifies the generator table in place and does not automatically save the session file.
User-edited start/end marker values are persisted and reused as defaults in subsequent imports.
- warn_if_controller_running()[source]
Warn the user if the slide controller is currently running.
If the controller is running, edits made in the generator may not be reflected in the live output until the controller is restarted. This method shows a warning dialog and signals whether editing should be blocked.
- Parameters:
None
- Returns:
True if the controller is running (warning shown), False otherwise.
- Return type:
bool
- export_slides_for_overlay()[source]
Export the current session into overlay-ready JSON and launch the controller.
Steps:
Collect slide session data from the table.
Prepend a blank slide to ensure a clean initial screen.
Convert slides into overlay format via
core.generator.utils.slide_exporter.SlideExporter.Write the exported JSON to
core.config.paths.SLIDE_FILE(UTF-8).Launch the slide controller if it is not already running.
See also
core.config.constants.MAX_CHARS.- Parameters:
None
- Returns:
None
- handle_table_double_click(row, column)[source]
Open the style-specific slide editor dialog for the selected table row.
This method:
Reads the current style/caption/headline values from the table row
Converts the displayed style label into an internal style key
Opens
core.generator.ui.slide_generator_dialog.SlideGeneratorDialogas a modal editorIf the user accepts, writes the updated values back into the table and triggers a save flow
- Parameters:
row (int) – Row index of the double-clicked table row.
column (int) – Column index of the double-click event. (Currently unused.)
- Returns:
None
- open_settings_dialog()[source]
Open the generator settings dialog and apply changes if accepted.
If the dialog is accepted:
Settings are persisted via the dialog’s save routine
Font settings are (intended to be) applied to the generator UI
- Parameters:
None
- Returns:
None
- apply_generator_font_settings()[source]
Apply the current persistent font settings to the generator UI.
Reads the generator settings and constructs a QFont using:
font_family(default: “Malgun Gothic”)font_size(default: 24)font_weight(default: “Normal”; “Bold” enables bold)
- Parameters:
None
- Returns:
None