jdxi_editor.ui.editors.io.program ================================= .. py:module:: jdxi_editor.ui.editors.io.program .. autoapi-nested-parse:: ProgramEditor Module This module defines the `ProgramEditor` class, a PySide6-based GUI for managing and selecting MIDI programs. It allows users to browse, filter, and load programs based on bank, genre, and program number. The class also facilitates MIDI integration by sending Program Change (PC) and Bank Select (CC#0, CC#32) messages. Key Features: - Graphical UI for selecting and managing MIDI programs. - Filtering options based on bank and genre. - MIDI integration for program selection and loading. - Image display for program categories. - Program list population based on predefined program data. Classes: ProgramEditor(QMainWindow) A main window class for handling MIDI program selection and management. Signals: program_changed (int, str, int) Emitted when a program selection changes. Parameters: - MIDI channel (int) - Preset name (str) - Program number (int) Dependencies: - PySide6.QtWidgets - PySide6.QtCore - MIDIHelper for MIDI message handling - PresetHandler for managing program presets - JDXiProgramList.PROGRAM_LIST for predefined program data Classes ------- .. autoapisummary:: jdxi_editor.ui.editors.io.program.MidiFileDelegate jdxi_editor.ui.editors.io.program.PlayButtonDelegate jdxi_editor.ui.editors.io.program.ProgramEditor Module Contents --------------- .. py:class:: MidiFileDelegate(table_widget=None, parent=None) Bases: :py:obj:`PySide6.QtWidgets.QStyledItemDelegate` Delegate for MIDI file selection with file dialog. .. py:attribute:: _dialog_open :value: False .. py:attribute:: table_widget :value: None .. py:method:: paint(painter, option, index) Paint the cell with a button-like appearance. .. py:method:: editorEvent(event, model, option, index) Handle mouse clicks to open file dialog. .. py:method:: sizeHint(option, index) Return appropriate size for the button. .. py:class:: PlayButtonDelegate(parent=None, play_callback=None) Bases: :py:obj:`PySide6.QtWidgets.QStyledItemDelegate` Delegate for Play button in table. .. py:attribute:: play_callback :value: None .. py:method:: paint(painter, option, index) Draw a play button. .. py:method:: editorEvent(event, model, option, index) Handle button click. .. py:method:: sizeHint(option, index) Return button size. .. py:class:: ProgramEditor(midi_helper: Optional[jdxi_editor.midi.io.helper.MidiIOHelper] = None, parent: Optional[PySide6.QtWidgets.QWidget] = None, preset_helper: jdxi_editor.jdxi.preset.helper.JDXiPresetHelper = None) Bases: :py:obj:`jdxi_editor.ui.editors.synth.simple.BasicEditor` Program Editor Window .. py:attribute:: program_changed .. py:attribute:: title_right_vlayout :value: None .. py:attribute:: program_list :value: None .. py:attribute:: file_label :value: None Initialize the ProgramEditor :param midi_helper: Optional[MidiIOHelper] :param parent: Optional[QWidget] :param preset_helper: JDXIPresetHelper .. py:attribute:: midi_helper :value: None .. py:attribute:: preset_helper :value: None .. py:attribute:: channel .. py:attribute:: midi_requests .. py:attribute:: default_image :value: 'programs.png' .. py:attribute:: instrument_icon_folder :value: 'programs' .. py:attribute:: instrument_title_label .. py:attribute:: layout :value: None .. py:attribute:: midi_channel :value: 0 .. py:attribute:: genre_label :value: None .. py:attribute:: program_number_combo_box :value: None .. py:attribute:: program_name :value: '' .. py:attribute:: bank_combo_box :value: None .. py:attribute:: load_button :value: None .. py:attribute:: save_button :value: None .. py:attribute:: image_label :value: None .. py:attribute:: title_label :value: None .. py:attribute:: bank_label :value: None .. py:attribute:: program_label :value: None .. py:attribute:: genre_combo_box :value: None .. py:attribute:: preset_type :value: None .. py:attribute:: programs .. py:attribute:: _current_playlist_row :value: None .. py:attribute:: _playlist_midi_editor :value: None .. py:attribute:: controls :type: Dict[picomidi.sysex.parameter.address.AddressParameter, PySide6.QtWidgets.QWidget] .. py:method:: setup_ui() set up ui elements .. py:method:: _create_preset_selection_widget() -> PySide6.QtWidgets.QWidget create_preset_selection_widget :return: QWidget .. py:method:: load_preset_by_program_change(preset_index: int) -> None Load a preset by program change. :param preset_index: int .. py:method:: on_category_changed(_: int) -> None Handle category selection change. .. py:method:: _create_transport_group() -> PySide6.QtWidgets.QGroupBox _create_transport_group :return: QGroupBox Transport controls area .. py:method:: _create_program_selection_box() -> PySide6.QtWidgets.QGroupBox create_program_selection_box :return: QGroupBox .. py:method:: edit_program_name() edit_tone_name :return: None .. py:method:: on_preset_type_changed(index: int) -> None on_preset_type_changed :param index: int Handle preset type selection change .. py:method:: set_channel_and_preset_lists(preset_type: str) -> None set_channel_and_preset_lists :param preset_type: :return: None .. py:method:: update_category_combo_box_categories() -> None update_category_combo_box_categories :return: None Update the category combo box. .. py:method:: _populate_programs(search_text: str = '') -> None Populate the program list with available presets. :param search_text: str :return: None .. py:method:: _populate_presets(search_text: str = '') -> None Populate the program list with available presets. :param search_text: str :return: None .. py:method:: _init_synth_data(synth_type: jdxi_editor.jdxi.synth.type.JDXiSynth = JDXiSynth.DIGITAL_SYNTH_1, partial_number: Optional[int] = 0) -> None :param synth_type: JDXiSynth :param partial_number: int :return: None Initialize synth-specific data .. py:method:: _create_mixer_section() -> PySide6.QtWidgets.QWidget _create_mixer_section :return: QWidget Create general vocal effect controls section with scrolling .. py:method:: update_tone_name_for_synth(tone_name: str, synth_type: str) -> None Update the tone name. :param tone_name: str :param synth_type: str .. py:method:: set_current_program_name(program_name: str, synth_type: str = None) -> None Set the current program name in the file label :param program_name: str :param synth_type: str (optional), discarded for now :return: None .. py:method:: start_playback() Start playback of the MIDI file. .. py:method:: stop_playback() Stop playback of the MIDI file. .. py:method:: populate_programs(search_text: str = '') Populate the program list with available presets. Uses SQLite database to ensure all user bank programs are loaded correctly. .. py:method:: add_user_banks(filtered_list: list, bank: str, search_text: str = None) -> None Add user banks to the program list. Only adds generic entries for programs that don't exist in the database. Uses SQLite database for reliable lookups. :param search_text: :param filtered_list: list of programs already loaded from database :param bank: str .. py:method:: _create_user_programs_tab() -> PySide6.QtWidgets.QWidget Create the User Programs tab with a sortable, searchable table. :return: QWidget containing the user programs table .. py:method:: _get_table_style() -> str Get custom styling for tables with rounded corners and charcoal embossed cells. :return: str CSS style string .. py:method:: _populate_user_programs_table(search_text: str = '') -> None Populate the user programs table from SQLite database. :param search_text: Optional search text to filter programs .. py:method:: _save_user_programs_changes() -> None Save changes made to the user programs table (e.g., genre edits) to the database. .. py:method:: _create_playlist_tab() -> PySide6.QtWidgets.QWidget Create the Playlist tab with a table showing all playlists. :return: QWidget containing the playlist table .. py:method:: _populate_playlist_table() -> None Populate the playlist table from SQLite database. .. py:method:: _create_new_playlist() -> None Create a new playlist. .. py:method:: _refresh_playlists() -> None populate programs .. py:method:: _delete_selected_playlist() -> None Delete the selected playlist. .. py:method:: _on_playlist_item_changed(item: PySide6.QtWidgets.QTableWidgetItem) -> None Handle changes to playlist name or description. :param item: The table item that was changed .. py:method:: _on_playlist_selected(item: PySide6.QtWidgets.QTableWidgetItem) -> None Handle double-click on a playlist. Could open playlist editor or show playlist programs. :param item: The table item that was double-clicked .. py:method:: _create_playlist_editor_tab() -> PySide6.QtWidgets.QWidget Create the Playlist Editor tab for editing playlist contents. :return: QWidget containing the playlist editor .. py:method:: _populate_playlist_editor_combo() -> None Populate the playlist selection combo box. .. py:method:: _on_playlist_programs_selection_changed() -> None Handle selection change in playlist programs table. .. py:method:: _on_playlist_editor_playlist_changed(index: int) -> None Handle playlist selection change in the editor. .. py:method:: _populate_playlist_programs_table(playlist_id: int) -> None Populate the playlist programs table with programs from the selected playlist. :param playlist_id: Playlist ID .. py:method:: _on_cheat_preset_changed(row: int, cheat_preset_id: Optional[str]) -> None Handle cheat preset selection change. :param row: Table row index :param cheat_preset_id: Selected cheat preset ID or None .. py:method:: _on_playlist_program_item_changed(item: PySide6.QtWidgets.QTableWidgetItem) -> None Handle changes to playlist program items (e.g., MIDI file path). :param item: The table item that was changed .. py:method:: _add_program_to_playlist() -> None Add a program to the selected playlist. .. py:method:: _delete_program_from_playlist() -> None Delete selected program(s) from the playlist. .. py:method:: _play_playlist_program(index) -> None Play the MIDI file associated with a playlist program. :param index: QModelIndex of the play button .. py:method:: _on_playlist_playback_finished() Called when MIDI playback finishes. Advances to the next playlist item. .. py:method:: _on_playlist_program_double_clicked(item: PySide6.QtWidgets.QTableWidgetItem) -> None Handle double-click on a playlist program item. If the Program Name column (column 2) is clicked, show the Program Editor. :param item: The table item that was double-clicked .. py:method:: _load_program_from_table_for_playlist(row: int) -> None Load a program from the playlist programs table and send MIDI Program Change. :param row: Row index in the table .. py:method:: _load_cheat_preset(preset_id: str) -> None Load a cheat preset (Digital Synth preset) on the Analog Synth channel (Ch3). :param preset_id: Preset ID (e.g., "113") .. py:method:: _on_user_program_selected(item: PySide6.QtWidgets.QTableWidgetItem) -> None Handle double-click on a program in the user programs table. Loads the program via MIDI Program Change. :param item: The table item that was double-clicked .. py:method:: _on_user_program_selection_changed() -> None Handle selection change in the user programs table. Loads the program via MIDI Program Change when a row is selected. .. py:method:: _play_user_program(index) -> None Callback for Play button delegate - loads and plays the program. :param index: QModelIndex from the delegate .. py:method:: _load_program_from_table(row: int) -> None Load a program from the table and send MIDI Program Change. :param row: Row index in the table .. py:method:: on_bank_changed(_: int) -> None Handle bank selection change. .. py:method:: on_program_number_changed(index: int) -> None Handle program number selection change. :param index: int .. py:method:: load_program() Load the selected program based on bank and number. .. py:method:: update_current_synths(program_details: jdxi_editor.jdxi.program.program.JDXiProgram) -> None Update the current synth label. :param program_details: dict :return: None .. py:method:: load_preset(program_number: int) -> None load_preset :param program_number: int :return: None Load preset data and update UI .. py:method:: _update_program_list() -> None Update the program list with available presets. .. py:method:: on_genre_changed(_: int) -> None Handle genre selection change. :param _: int .. py:method:: _dispatch_sysex_to_area(json_sysex_data: str) -> None Dispatch SysEx data to the appropriate area for processing. :param json_sysex_data: :return: None .. py:method:: _update_common_controls(partial_number: int, sysex_data: Dict, successes: list = None, failures: list = None) -> None Update the UI components for tone common and modify parameters. :param partial_number: int partial number :param sysex_data: Dictionary containing SysEx data :param successes: List of successful parameters :param failures: List of failed parameters :return: None .. py:method:: _update_slider(param: picomidi.sysex.parameter.address.AddressParameter, midi_value: int, successes: list = None, failures: list = None, slider: PySide6.QtWidgets.QWidget = None) -> None Update slider based on parameter and value. :param param: AddressParameter :param midi_value: int value :param successes: list :param failures: list :return: None