Skip to content

Modules

Top-level package for deluge-gui.

app_state

Application state store.

AppState

Bases: CardState

Application state store.

Source code in deluge_gui/app_state.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
class AppState(CardState):
    """Application state store."""

    song_table_index: int = 0  # the current id
    sample_tree_index: str = ""  # the path of the current item
    kit_table_index: int = 0  # the current id
    synth_table_index: int = 0  # the current id

    def from_card(self, card: DelugeCardFS) -> 'AppState':
        """Increment song index."""
        self.set_card(card)
        self.set_songs({str(song.path.relative_to(card.card_root)): song for song in card.songs()})
        self.set_samples({str(sample.path.relative_to(card.card_root)): sample for sample in card.samples()})
        self.set_synths(
            {str(synth.path.relative_to(card.card_root)): DelugeSynthSound.from_synth(synth) for synth in card.synths()}
        )
        # synths = {}
        # try:
        #     for synth in card.synths():
        #         synths[str(synth.path.relative_to(card.card_root))] = DelugeSynthSound.from_synth(synth)
        #     self.set_synths(synths)
        # except Exception as err:
        #     print('err', synth, synth.path)
        #     raise err
        self.set_kits({str(kit.path.relative_to(card.card_root)): kit for kit in card.kits()})
        self.sample_tree_index = list(self.samples.keys())[0]
        self.song_table_index = 0
        return self

    def decr_song_table_index(self) -> int:
        """Decrement song index."""
        if not self.song_table_index == 0:
            self.song_table_index -= 1
        return self.song_table_index

    def incr_song_table_index(self) -> int:
        """Increment song index."""
        if not self.song_table_index == len(self.songs) - 1:
            self.song_table_index += 1
        return self.song_table_index

decr_song_table_index()

Decrement song index.

Source code in deluge_gui/app_state.py
123
124
125
126
127
def decr_song_table_index(self) -> int:
    """Decrement song index."""
    if not self.song_table_index == 0:
        self.song_table_index -= 1
    return self.song_table_index

from_card(card)

Increment song index.

Source code in deluge_gui/app_state.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def from_card(self, card: DelugeCardFS) -> 'AppState':
    """Increment song index."""
    self.set_card(card)
    self.set_songs({str(song.path.relative_to(card.card_root)): song for song in card.songs()})
    self.set_samples({str(sample.path.relative_to(card.card_root)): sample for sample in card.samples()})
    self.set_synths(
        {str(synth.path.relative_to(card.card_root)): DelugeSynthSound.from_synth(synth) for synth in card.synths()}
    )
    # synths = {}
    # try:
    #     for synth in card.synths():
    #         synths[str(synth.path.relative_to(card.card_root))] = DelugeSynthSound.from_synth(synth)
    #     self.set_synths(synths)
    # except Exception as err:
    #     print('err', synth, synth.path)
    #     raise err
    self.set_kits({str(kit.path.relative_to(card.card_root)): kit for kit in card.kits()})
    self.sample_tree_index = list(self.samples.keys())[0]
    self.song_table_index = 0
    return self

incr_song_table_index()

Increment song index.

Source code in deluge_gui/app_state.py
129
130
131
132
133
def incr_song_table_index(self) -> int:
    """Increment song index."""
    if not self.song_table_index == len(self.songs) - 1:
        self.song_table_index += 1
    return self.song_table_index

AppWindows dataclass

Manage the application windows.

Source code in deluge_gui/app_state.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@dataclass
class AppWindows:
    """Manage the application windows."""

    main: sg.Window
    song: sg.Window
    sample: sg.Window
    kit: sg.Window
    synth: sg.Window

    def reveal_secondary(self, activate: sg.Window):
        """Activate given window, hiding others."""
        secondaries = [self.song, self.sample, self.kit, self.synth]
        assert activate in secondaries
        secondaries.remove(activate)
        for w in secondaries:
            w.hide()
        activate.un_hide()

reveal_secondary(activate)

Activate given window, hiding others.

Source code in deluge_gui/app_state.py
20
21
22
23
24
25
26
27
def reveal_secondary(self, activate: sg.Window):
    """Activate given window, hiding others."""
    secondaries = [self.song, self.sample, self.kit, self.synth]
    assert activate in secondaries
    secondaries.remove(activate)
    for w in secondaries:
        w.hide()
    activate.un_hide()

CardState

Represents state of the card.

Attributes:

Name Type Description
card DelugeCardFS

the deluge card.

songs Mapping[str, Song]

list of Songs (from the current card)

Source code in deluge_gui/app_state.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class CardState:
    """Represents state of the card.

    Attributes:
        card (DelugeCardFS): the deluge card.
        songs (Mapping[str, Song]): list of Songs (from the current card)
    """

    card: DelugeCardFS
    songs: Mapping[str, DelugeSong]
    song: str  # the current path
    samples: Mapping[str, Sample]
    sample: str  # the current path
    synths: Mapping[str, DelugeSynth]
    synth: str  # the current path
    kits: Mapping[str, DelugeKit]
    kit: str  # the current path

    def set_card(self, card):
        """Set the card object."""
        self.card = card
        return self

    def set_kits(self, kits: Mapping[str, DelugeKit]):
        """Set the kits mapping path -> object."""
        self.kits = kits
        return self

    def get_kit_id(self, idx: int) -> DelugeKit:
        """Get the kit identified by index."""
        kit_key = list(self.kits.keys())[idx]
        return self.kits[kit_key]

    def set_samples(self, samples: Mapping[str, Sample]):
        """Set the samples mapping path -> object."""
        self.samples = samples
        return self

    def get_sample_key(self, sample_key: str) -> Sample:
        """Get the sample identified by key (file path)."""
        # sample_key = list(self.samples.keys())[idx]
        return self.samples[sample_key]

    def set_songs(self, songs: Mapping[str, DelugeSong]):
        """Set the songs mapping path -> object."""
        self.songs = songs
        return self

    def get_song_id(self, idx: int) -> DelugeSong:
        """Get the song identified by index."""
        song_key = list(self.songs.keys())[idx]
        return self.songs[song_key]

    def set_synths(self, synths: Mapping[str, DelugeSynth]):
        """Set the synths mapping path -> object."""
        self.synths = synths
        return self

    def get_synth_id(self, idx: int) -> DelugeSynth:
        """Get the synth identified by index."""
        synth_key = list(self.synths.keys())[idx]
        return self.synths[synth_key]

get_kit_id(idx)

Get the kit identified by index.

Source code in deluge_gui/app_state.py
58
59
60
61
def get_kit_id(self, idx: int) -> DelugeKit:
    """Get the kit identified by index."""
    kit_key = list(self.kits.keys())[idx]
    return self.kits[kit_key]

get_sample_key(sample_key)

Get the sample identified by key (file path).

Source code in deluge_gui/app_state.py
68
69
70
71
def get_sample_key(self, sample_key: str) -> Sample:
    """Get the sample identified by key (file path)."""
    # sample_key = list(self.samples.keys())[idx]
    return self.samples[sample_key]

get_song_id(idx)

Get the song identified by index.

Source code in deluge_gui/app_state.py
78
79
80
81
def get_song_id(self, idx: int) -> DelugeSong:
    """Get the song identified by index."""
    song_key = list(self.songs.keys())[idx]
    return self.songs[song_key]

get_synth_id(idx)

Get the synth identified by index.

Source code in deluge_gui/app_state.py
88
89
90
91
def get_synth_id(self, idx: int) -> DelugeSynth:
    """Get the synth identified by index."""
    synth_key = list(self.synths.keys())[idx]
    return self.synths[synth_key]

set_card(card)

Set the card object.

Source code in deluge_gui/app_state.py
48
49
50
51
def set_card(self, card):
    """Set the card object."""
    self.card = card
    return self

set_kits(kits)

Set the kits mapping path -> object.

Source code in deluge_gui/app_state.py
53
54
55
56
def set_kits(self, kits: Mapping[str, DelugeKit]):
    """Set the kits mapping path -> object."""
    self.kits = kits
    return self

set_samples(samples)

Set the samples mapping path -> object.

Source code in deluge_gui/app_state.py
63
64
65
66
def set_samples(self, samples: Mapping[str, Sample]):
    """Set the samples mapping path -> object."""
    self.samples = samples
    return self

set_songs(songs)

Set the songs mapping path -> object.

Source code in deluge_gui/app_state.py
73
74
75
76
def set_songs(self, songs: Mapping[str, DelugeSong]):
    """Set the songs mapping path -> object."""
    self.songs = songs
    return self

set_synths(synths)

Set the synths mapping path -> object.

Source code in deluge_gui/app_state.py
83
84
85
86
def set_synths(self, synths: Mapping[str, DelugeSynth]):
    """Set the synths mapping path -> object."""
    self.synths = synths
    return self

card_views

Card views.

get_cards_list(path=None)

Get a list of paths that are Deluge Folder Systems.

Source code in deluge_gui/card_views.py
16
17
18
19
def get_cards_list(path: str = None):
    """Get a list of paths that are Deluge Folder Systems."""
    path = path or sg.user_settings_get_entry('-home folder-')
    return [card for card in list_deluge_fs(path)]

layout_card_info(card)

Elements for Card layout.

Source code in deluge_gui/card_views.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def layout_card_info(card):
    """Elements for Card layout."""
    songs = list(card.songs())
    samples = list(card.samples())
    synths = list(card.synths())
    kits = list(card.kits())

    view_card = [
        [
            sg.Frame(
                "Card info",
                layout=[
                    [
                        sg.Text('Path:', font=FONT_MED, size=(10,)),
                        sg.Text(card.card_root, font=FONT_MED, size=(50,), key="-CARD-INFO-PATH-"),
                    ],
                    [
                        sg.Text('Songs:', font=FONT_MED, size=(10,)),
                        sg.Text(str(len(songs)), font=FONT_MED, size=(10,), key="-CARD-INFO-SONGS-"),
                        sg.Text('Samples:', font=FONT_MED, size=(10,)),
                        sg.Text(str(len(samples)), font=FONT_MED, size=(10,), key="-CARD-INFO-SAMPLES-"),
                    ],
                    [
                        sg.Text('Synths:', font=FONT_MED, size=(10,)),
                        sg.Text(str(len(synths)), font=FONT_MED, size=(10,), key="-CARD-INFO-SYNTHS-"),
                        sg.Text('Kits:', font=FONT_MED, size=(10,)),
                        sg.Text(str(len(kits)), font=FONT_MED, size=(10,), key="-CARD-INFO-KITS-"),
                    ],
                    # [
                    #     sg.Listbox(
                    #         values=[],
                    #         select_mode=sg.SELECT_MODE_EXTENDED,
                    #         size=(50, 25),
                    #         bind_return_key=True,
                    #         key='-CARD DETAIL-',
                    #         font=FONT_MED,
                    #     )
                    # ],
                ],
            )
        ]
    ]
    return [[sg.Col(view_card, vertical_alignment='t')]]

select_card_control(default_value)

Card Selector.

Source code in deluge_gui/card_views.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def select_card_control(default_value):
    """Card Selector."""
    return [
        [
            sg.Text('Card', font=FONT_MED, size=(15,)),
            sg.Combo(
                default_value=default_value,
                values=[x.card_root for x in get_cards_list()],
                # select_mode=sg.SELECT_MODE_EXTENDED,
                # size=(30, 10),
                bind_return_key=True,
                key='-CARD LIST-',
                font=FONT_MED,
                enable_events=True,
            ),
            sg.B("Refresh Cards"),
        ],
    ]

config

Application configuration.

deluge_gui

Main module.

run_app()

Run the app......

Source code in deluge_gui/deluge_gui.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
def run_app():
    """Run the app......"""
    # set initial state
    state_store = AppState()

    card_path = sg.user_settings_get_entry('-CARD-INFO-PATH-', None)
    if card_path:
        print(card_path)
        card = DelugeCardFS(Path(card_path))  # [0] the selected card
        state_store = state_store.from_card(card)
    else:
        # Need a card - shall we throw up a dialog now??
        assert 0

    # --------- setup event dispatch dictionaries -------
    dispatch_dict_main = {
        '-CARD LIST-': do_card_list,
        '-SAMPLE-TREE-': do_sample_tree,
        '-SONG-TABLE-': do_song_table,
        '-KIT-TABLE-': do_kit_table,
        '-SYNTH-TABLE-': do_synth_table,
        '-PLAY-': do_play_sample,
    }

    dispatch_dict_sample = {'-PLAY-': do_play_sample}

    terminate_app = False
    while not terminate_app:
        # we will want to receive events from new windows
        # create the windows
        windows = create_windows(state_store)

        # Send an event to fill the windows
        windows.main.write_event_value('-CARD LIST-', Path(card_path))

        while True:  # Event Loop
            #####
            ##
            ## MAIN WINDOW EVENTS
            ##
            #####
            event, values = windows.main.read(timeout=0)
            if not event == '__TIMEOUT__':
                print(f'event: {event}, values: {values}')

            if event in dispatch_dict_main:
                func = dispatch_dict_main[event]
                func(event, values, windows, state_store)

            if event in ('Exit', sg.WINDOW_CLOSE_ATTEMPTED_EVENT):
                """Fiter by user close window, or hitting Exit button."""
                print(f'handling event {event}, goodbye!')
                sg.user_settings_set_entry('-location-', windows.main.current_location())
                terminate_app = True
                break

            if event == sg.WIN_CLOSED:
                """This event is fired by closing/reopening the windows."""
                print('windows refresh, take a moment....')
                # # Reset windows to ensure new style settings are applied.
                time.sleep(0.1)
                # windows = create_windows(state_store)
                # windows.main.write_event_value('-CARD LIST-', Path(card_path))
                break

            main_events_misc(event, values, windows, state_store)

            #####
            ##
            ## SONG WINDOW EVENTS
            ##
            #####
            event, values = windows.song.read(timeout=0)
            if not event == '__TIMEOUT__':
                print(f'song window event: {event}, values: {values}')

            if event == '+KB-UP+':
                # Adds a key & value tuple to the queue that is used by threads to communicate with the window
                windows.main.write_event_value(
                    '-SONG-TABLE-PREV-', {"-SONG-INFO-NAME-": windows.song["-SONG-INFO-NAME-"].get()}
                )

            if event == '+KB-DN+':
                # Adds a key & value tuple to the queue that is used by threads to communicate with the window
                windows.main.write_event_value(
                    '-SONG-TABLE-NEXT-', {"-SONG-INFO-NAME-": windows.song["-SONG-INFO-NAME-"].get()}
                )

            #####
            ##
            ## SAMPLE WINDOW EVENTS
            ##
            #####
            event, values = windows.sample.read(timeout=0)
            if not event == '__TIMEOUT__':
                print(f'sample window event: {event}, values: {values}')

            if event in dispatch_dict_sample:
                func = dispatch_dict_sample[event]
                func(event, values, windows, state_store)

    close_windows(windows)

event_handlers

Event handler functions.

do_card_list(event, values, windows, state_store)

User changes the selected card.

Source code in deluge_gui/event_handlers.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def do_card_list(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """User changes the selected card."""
    card = DelugeCardFS(values['-CARD LIST-'])  # [0] the selected card
    state_store.from_card(card)

    # update UI Elements with new state
    window = windows.main
    window["-CARD-INFO-PATH-"].update(value=state_store.card.card_root)
    window["-CARD-INFO-SONGS-"].update(value=len(state_store.songs))
    window["-CARD-INFO-SAMPLES-"].update(value=len(state_store.samples))
    window["-CARD-INFO-SYNTHS-"].update(value=len(state_store.synths))
    window["-CARD-INFO-KITS-"].update(len(state_store.kits))
    window['-SONG-TABLE-'].update(values=song_table_data(state_store.songs.values()))

    # update the user_settings
    sg.user_settings_set_entry('-CARD-INFO-PATH-', str(card.card_root))
    # print('** card info updated **')
    window['-SAMPLE-TREE-'].update(values=sample_tree_data(state_store.card, state_store.samples.values()))
    window['-KIT-TABLE-'].update(values=kit_table_data(state_store.kits.values()))
    window['-SYNTH-TABLE-'].update(values=synth_table_data(state_store.synths.values()))

do_kit_table(event, values, windows, state_store)

User changes the selected kit.

Source code in deluge_gui/event_handlers.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def do_kit_table(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """User changes the selected kit."""
    print('do_kit_table')
    if not values['-KIT-TABLE-']:  # handle header row click
        return
    state_store.kit_table_index = values['-KIT-TABLE-'][0]
    kit = state_store.get_kit_id(values['-KIT-TABLE-'][0])
    print('XXX', kit)
    values_dict = {
        '-KIT-INFO-NAME-': kit.path.name,
        # '-SONG-SAMPLES-TABLE-': [
        #     [s.path.relative_to(state_store.card.card_root).parent, s.path.name] for s in list(song.samples())
        # ],
    }
    windows.kit.fill(values_dict)
    windows.reveal_secondary(windows.kit)

do_play_sample(event, values, windows, state_store)

Event handler for -PLAY-.

Source code in deluge_gui/event_handlers.py
17
18
19
20
21
def do_play_sample(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """Event handler for -PLAY-."""
    sample = state_store.get_sample_key(state_store.sample_tree_index)
    data, fs = sf.read(sample.path)
    sd.play(data, fs)

do_sample_tree(event, values, windows, state_store)

Event handler for when user selects a node in the sample tree.

Source code in deluge_gui/event_handlers.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def do_sample_tree(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """Event handler for when user selects a node in the sample tree."""
    if not values['-SAMPLE-TREE-']:  # handle header row click
        return
    # print(state_store.samples)
    if values['-SAMPLE-TREE-'][0][-3:].lower() == 'wav':
        # user selected a sample - yay
        state_store.sample_tree_index = values['-SAMPLE-TREE-'][0]
        # print(f'sample_tree_index: {state_store.sample_tree_index}')
    sample = state_store.get_sample_key(state_store.sample_tree_index)
    values_dict = {
        '-SAMPLE-INFO-NAME-': sample.path.name,
        '-SAMPLE-INFO-PATH-': sample.path.relative_to(state_store.card.card_root),
        '-SAMPLE-SETTINGS-TABLE-': [
            [ss.xml_file.path.relative_to(state_store.card.card_root), ss.xml_path] for ss in sample.settings
        ],
    }
    windows.sample.fill(values_dict)
    windows.reveal_secondary(windows.sample)

do_song_table(event, values, windows, state_store)

User changes the selected song.

Source code in deluge_gui/event_handlers.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def do_song_table(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """User changes the selected song."""
    print('do_song_table')
    if not values['-SONG-TABLE-']:  # handle header row click
        return
    state_store.song_table_index = values['-SONG-TABLE-'][0]
    song = state_store.get_song_id(values['-SONG-TABLE-'][0])
    print('XXX', song)
    values_dict = {
        '-SONG-INFO-NAME-': song.path.name,
        '-SONG-INFO-SCALE-': song.scale(),
        '-SONG-INFO-TEMPO-': song.tempo(),
        '-SONG-INFO-MIN-FW-': song.minimum_firmware(),
        '-SONG-INFO-SYNTHS-': len(list(song.synths)),
        '-SONG-INFO-KITS-': len(list(song.kits)),
        '-SONG-SAMPLES-TABLE-': [
            [s.path.relative_to(state_store.card.card_root).parent, s.path.name] for s in list(song.samples())
        ],
    }
    windows.song.fill(values_dict)
    windows.reveal_secondary(windows.song)

do_synth_table(event, values, windows, state_store)

User changes the selected synth.

Source code in deluge_gui/event_handlers.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def do_synth_table(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """User changes the selected synth."""
    print('do_synth_table')
    if not values['-SYNTH-TABLE-']:  # handle header row click
        return
    state_store.synth_table_index = values['-SYNTH-TABLE-'][0]
    synth = state_store.get_synth_id(values['-SYNTH-TABLE-'][0])
    print('XXX', synth)
    print(dir(synth))
    values_dict = {
        '-SYNTH-INFO-NAME-': synth.path.name,
        '-SYNTH-INFO-MODE-': synth.mode.value,
        '-SYNTH-INFO-POLYPHONY-': synth.polyphonic.value,
        '-SYNTH-INFO-LPFMODE-': synth.lpf_mode.value,
        '-SYNTH-INFO-MODFXTYPE-': synth.mod_fx_type.value,
        # '-SONG-SAMPLES-TABLE-': [
        #     [s.path.relative_to(state_store.card.card_root).parent, s.path.name] for s in list(song.samples())
        # ],
    }
    windows.synth.fill(values_dict)
    windows.reveal_secondary(windows.synth)

main_events_misc(event, values, windows, state_store)

Misc events for the main window.

Source code in deluge_gui/event_handlers.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def main_events_misc(event: str, values: dict, windows: AppWindows, state_store: AppState):
    """Misc events for the main window."""
    if event == 'Settings':  # Open the User Settings Window.
        if settings_window() is True:
            close_windows(windows)

    if event == "Refresh Cards":  # Refresh button, maybe redundant?
        windows.main['-CARD LIST-'].update(values=[x.card_root for x in get_cards_list()])

    if isinstance(event, tuple):
        # TABLE CLICKED Event has value in format ('-TABLE-', '+CLICKED+', (row,col))
        if event[0] == '-SONG-TABLE-':
            # print(recvr, recvr.key, recvr.value)
            if event[2][0] == -1 and event[2][1] != -1:  # Header was clicked and wasn't the "row" column
                col_num_clicked = event[2][1]
                new_table = sort_table(song_table_data(state_store.songs.values()), (col_num_clicked, 0))
                windows.main['-SONG-TABLE-'].update(new_table)
                return
            else:
                print("unhandled song event 1")
            return
        if event[0] == '-KIT-TABLE-':
            # print(recvr, recvr.key, recvr.value)
            if event[2][0] == -1 and event[2][1] != -1:  # Header was clicked and wasn't the "row" column
                col_num_clicked = event[2][1]
                new_table = sort_table(kit_table_data(state_store.kits.values()), (col_num_clicked, 0))
                windows.main['-KIT-TABLE-'].update(new_table)
                return
            else:
                print("unhandled kit event 1")
            return
        if event[0] == '-SYNTH-TABLE-':
            # print(recvr, recvr.key, recvr.value)
            if event[2][0] == -1 and event[2][1] != -1:  # Header was clicked and wasn't the "row" column
                col_num_clicked = event[2][1]
                new_table = sort_table(synth_table_data(state_store.synths.values()), (col_num_clicked, 0))
                windows.main['-SYNTH-TABLE-'].update(new_table)
                return
            else:
                print("unhandled kit event 1")
            return
        else:
            print("unhandled event 2")

    if event == '-SONG-TABLE-PREV-':  # key press song_window
        windows.main['-SONG-TABLE-'].update(select_rows=[state_store.decr_song_table_index()])

    if event == '-SONG-TABLE-NEXT-':  # key press song_window
        windows.main['-SONG-TABLE-'].update(select_rows=[state_store.incr_song_table_index()])

kit_views

Kit views.

kit_table_data(kits)

Take a DFS Kits iterable, and return a table with...

Source code in deluge_gui/kit_views.py
68
69
70
71
72
73
74
def kit_table_data(kits):
    """Take a DFS Kits iterable, and return a table with..."""
    # ['Name', ]
    data = []
    for k in kits:
        data.append([k.path.name])
    return data

layout_kit_info()

Elements for Kit layout.

Source code in deluge_gui/kit_views.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def layout_kit_info():
    """Elements for Kit layout."""
    view_kit = [
        [
            sg.Frame(
                "Kit",
                key="-KIT-INFO-FRAME-",
                layout=[
                    [
                        sg.Text('', key='-KIT-INFO-NAME-', font=FONT_LRG, size=(50,)),
                    ],
                    [
                        sg.Text('Deets', font=FONT_MED, size=(10,)),
                        # sg.Text('', key='-KIT-INFO-SCALE-', font=FONT_MED, size=(10,)),
                        # sg.Text('Tempo', font=FONT_MED, size=(10,)),
                        # sg.Text('', key='-KIT-INFO-TEMPO-', font=FONT_MED, size=(10,)),
                    ],
                ],
            ),
        ],
        [layout_kit_samples()],  # -KIT-SAMPLES-TABLE-
    ]
    return view_kit

layout_kit_samples()

List samples in the kit.

Source code in deluge_gui/kit_views.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def layout_kit_samples():
    """List samples in the kit."""
    headings = ['Path', 'Sample']
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                font=FONT_MED,
                # max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='left',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-KIT-SAMPLES-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                # tooltip='This is a table',
            )
        ],
    ]
    return layout

layout_kit_table()

A layout a table of kit attributes.

Source code in deluge_gui/kit_views.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def layout_kit_table():
    """A layout a table of kit attributes."""
    headings = [
        'Name',
    ]
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                font=FONT_MED,
                max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='center',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-KIT-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                tooltip='This is a table',
            )
        ],
        [sg.Text('Cell clicked:'), sg.T(key='-KIT-CELL-CLICKED-')],
    ]
    return layout

sample_views

Sample views.

layout_sample_info()

Elements for Sample layout.

Source code in deluge_gui/sample_views.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def layout_sample_info():
    """Elements for Sample layout."""
    view_sample = [
        [
            sg.Frame(
                "Sample",
                key="-SAMPLE-INFO-FRAME-",
                layout=[
                    [
                        sg.Text('', key='-SAMPLE-INFO-NAME-', font=FONT_LRG, size=(50,)),
                        sg.Button(
                            'Play',
                            key='-PLAY-',
                            image_data=gs2,
                            image_subsample=1,
                            button_color=('black', sg.theme_background_color()),
                            border_width=0,
                            font=FONT_LRG,
                            tooltip='Play sample <spacebar>',
                        ),
                    ],
                    [
                        sg.Text('', key='-SAMPLE-INFO-PATH-', font=FONT_MED, size=(50,)),
                    ],
                ],
            )
        ],
        [layout_sample_settings()],  # -SAMPLE-SETTINGS-TABLE-
    ]
    return view_sample

layout_sample_settings()

List the sample settings.

Source code in deluge_gui/sample_views.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def layout_sample_settings():
    """List the sample settings."""
    headings = ['xml_setting', 'xml_path']
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                # max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='left',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-SAMPLE-SETTINGS-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                # tooltip='This is a table',
            )
        ],
    ]
    return layout

layout_sample_tree()

A layout a samples treem attributes.

Source code in deluge_gui/sample_views.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
def layout_sample_tree():
    """A layout a samples treem attributes."""
    layout = [
        [
            sg.Tree(
                font=FONT_MED,
                data=sg.TreeData(),
                headings=[],
                auto_size_columns=True,
                select_mode=sg.TABLE_SELECT_MODE_EXTENDED,
                num_rows=20,
                col0_width=40,
                key='-SAMPLE-TREE-',
                show_expanded=False,
                enable_events=True,
                expand_x=True,
                expand_y=True,
            ),
        ]
    ]
    return layout

sample_tree_data(card, samples)

Take a samples iterable, and return sg tree.

Source code in deluge_gui/sample_views.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def sample_tree_data(card, samples):
    """Take a samples iterable, and return sg tree."""
    sample_paths = [s.path.relative_to(card.card_root).parts for s in samples]

    print(sample_paths[0])

    try:
        sample_tree = to_tree(sample_paths)
    except Exception as e:
        # print(sample_paths)
        print(e)
        sample_tree = {}

    def add_nodes_in_dict(parent, node):
        # recurse the dict
        if isinstance(node, list):
            # print('LIST', node)
            for itm in node:
                path = parent + itm
                treedata.Insert(parent, path, itm, values=[], icon=file_icon)
        else:
            # print('DICT', node.keys())
            for key, itm in node.items():
                path = parent + key + '/'
                treedata.Insert(parent, path, key, values=[], icon=folder_icon)
                add_nodes_in_dict(path, itm)

    treedata = sg.TreeData()
    add_nodes_in_dict('', sample_tree)
    return treedata

to_tree(data)

Convert flat list of path parts to tree. ref https://stackoverflow.com/a/68288876 .

Source code in deluge_gui/sample_views.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
def to_tree(data):
    """Convert flat list of path parts to tree. ref https://stackoverflow.com/a/68288876 ."""
    d = defaultdict(list)
    try:
        # for a, *b in data:
        for x in data:
            a = x[0]
            if len(x) > 1:
                b = x[1:]
                d[a].append(b)
                # last_a = a
                # print(a, b)
            else:
                pass
                # print('wierd', x)
    except Exception as e:
        # print(last_a)
        # print(e, data)
        raise e
    return {a: [i for [i] in b] if all(len(i) == 1 for i in b) else to_tree(b) for a, b in d.items()}

settings_window

Global user settings window.

get_demo_path()

Get the top-level folder path.

:return: Path to list of files using the user settings for this file. Returns folder of this file if not found :rtype: str

Source code in deluge_gui/settings_window.py
 8
 9
10
11
12
13
14
15
16
def get_demo_path():
    """Get the top-level folder path.

    :return: Path to list of files using the user settings for this file.  Returns folder of this file if not found
    :rtype: str
    """
    demo_path = sg.user_settings_get_entry('-home folder-', os.path.join(os.path.dirname(__file__), 'demo_programs'))

    return demo_path

get_theme()

Get the theme to use for the program.

Value is in this program's user settings. If none set, then use PySimpleGUI's global default theme :return: The theme :rtype: str

Source code in deluge_gui/settings_window.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def get_theme():
    """Get the theme to use for the program.

    Value is in this program's user settings. If none set, then use PySimpleGUI's global default theme
    :return: The theme
    :rtype: str
    """
    # First get the current global theme for PySimpleGUI to use if none has been set for this program
    try:
        global_theme = sg.theme_global()
    except Exception:
        global_theme = sg.theme()
    # Get theme from user settings for this program.  Use global theme if no entry found
    user_theme = sg.user_settings_get_entry('-theme-', '')
    if user_theme == '':
        user_theme = global_theme
    return user_theme

settings_window()

Show the settings window.

This is where the folder paths and program paths are set. Returns True if settings were changed

:return: True if settings were changed :rtype: (bool)

Source code in deluge_gui/settings_window.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def settings_window():
    """Show the settings window.

    This is where the folder paths and program paths are set.
    Returns True if settings were changed

    :return: True if settings were changed
    :rtype: (bool)
    """
    try:  # in case running with old version of PySimpleGUI that doesn't have a global PSG settings path
        global_theme = sg.theme_global()
    except Exception:
        global_theme = ''

    layout = [
        [sg.T('Program Settings', font='DEFAULT 25')],
        [sg.T('Path to Deluge home folder', font='_ 16')],
        [
            sg.Combo(
                sorted(sg.user_settings_get_entry('-home folder-', [])),
                default_value=sg.user_settings_get_entry('-home folder-', get_demo_path()),
                size=(50, 1),
                key='-HOMEFOLDER-',
            ),
            sg.FolderBrowse('Folder Browse', target='-HOMEFOLDER-'),
        ],
        [sg.T('Theme', font='_ 16')],
        [sg.T('Leave blank to use global default'), sg.T(global_theme)],
        [sg.Combo([''] + sg.theme_list(), sg.user_settings_get_entry('-theme-', ''), readonly=True, k='-THEME-')],
        [sg.B('Ok', bind_return_key=True), sg.B('Cancel')],
    ]

    window = sg.Window('Settings', layout)

    settings_changed = False

    while True:
        event, values = window.read()
        # if event in ('Cancel', sg.WIN_CLOSED):
        if event == sg.WIN_CLOSED or event == 'Exit':
            break
        if event == 'Ok':
            sg.user_settings_set_entry('-home folder-', values['-HOMEFOLDER-'])
            sg.user_settings_set_entry('-theme-', values['-THEME-'])
            settings_changed = True
            break
        elif event == 'Clear History':
            sg.user_settings_set_entry('-folder names-', [])
            sg.user_settings_set_entry('-last filename-', '')
            window['-FOLDERNAME-'].update(values=[], value='')

    window.close()
    return settings_changed

song_views

Song views.

layout_song_info()

Elements for Card layout.

Source code in deluge_gui/song_views.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def layout_song_info():
    """Elements for Card layout."""
    # data.append([s.path.name, s.scale(), s.tempo(), len(list(s.samples())), s.minimum_firmware()])

    view_song = [
        [
            sg.Frame(
                "Song",
                key="-SONG-INFO-FRAME-",
                layout=[
                    [
                        sg.Text('', key='-SONG-INFO-NAME-', font=FONT_LRG, size=(50,)),
                    ],
                    [
                        sg.Text('Scale', font=FONT_MED, size=(10,)),
                        sg.Text('', key='-SONG-INFO-SCALE-', font=FONT_MED, size=(10,)),
                        sg.Text('Tempo', font=FONT_MED, size=(10,)),
                        sg.Text('', key='-SONG-INFO-TEMPO-', font=FONT_MED, size=(10,)),
                    ],
                    [
                        sg.Text('Min Firmware', font=FONT_MED, size=(10,)),
                        sg.Text(key="-SONG-INFO-MIN-FW-", font=FONT_MED, size=(10,)),
                        sg.Text('Synths', font=FONT_MED, size=(10,)),
                        sg.Text(key="-SONG-INFO-SYNTHS-", font=FONT_MED, size=(10,)),
                        sg.Text('Kits', font=FONT_MED, size=(10,)),
                        sg.Text(key="-SONG-INFO-KITS-", font=FONT_MED, size=(10,)),
                    ],
                ],
            ),
        ],
        # [
        #     sg.HorizontalSeparator(color = None,
        #         pad = None,
        #         p = None,
        #         key = None,
        #         k = None),
        # ],
        [layout_song_samples()],  # -SONG-SAMPLES-TABLE-
    ]
    return view_song

layout_song_samples()

List samples in the song.

Source code in deluge_gui/song_views.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def layout_song_samples():
    """List samples in the song."""
    headings = ['Path', 'Sample']
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                font=FONT_MED,
                # max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='left',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-SONG-SAMPLES-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                # tooltip='This is a table',
            )
        ],
    ]
    return layout

layout_song_table()

A layout a table of song attributes.

Source code in deluge_gui/song_views.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def layout_song_table():
    """A layout a table of song attributes."""
    headings = ['Name', 'Scale', 'BPM', 'Samples', "min version"]
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                font=FONT_MED,
                max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='center',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-SONG-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                tooltip='This is a table',
            )
        ],
        [sg.Text('Cell clicked:'), sg.T(key='-SONG-CELL-CLICKED-')],
    ]
    return layout

song_table_data(songs)

Take a DFS Songs iterable, and return a table with...

Source code in deluge_gui/song_views.py
87
88
89
90
91
92
93
94
95
96
def song_table_data(songs):
    """Take a DFS Songs iterable, and return a table with..."""
    # ['Name', 'BPM', "Path", "Samples"]
    # 'cardfs', 'minimum_firmware', 'mode_notes', 'path', 'root_elem', 'root_note', 'samples',
    # 'scale', 'scale_mode', 'tempo',
    data = []
    for s in songs:
        # print(dir(s))
        data.append([s.path.name, s.scale(), s.tempo(), len(list(s.samples())), s.minimum_firmware()])
    return data

sort_table(table, cols)

Sort a table by multiple columns.

a list of lists (or tuple of tuples) where each inner list

represents a row

a list (or tuple) specifying the column numbers to sort by

e.g. (1,0) would sort by column 1, then by column 0

Source code in deluge_gui/song_views.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def sort_table(table, cols):
    """Sort a table by multiple columns.

    table: a list of lists (or tuple of tuples) where each inner list
           represents a row
    cols:  a list (or tuple) specifying the column numbers to sort by
           e.g. (1,0) would sort by column 1, then by column 0
    """
    for col in reversed(cols):
        try:
            table = sorted(table, key=operator.itemgetter(col))
        except Exception as e:
            sg.popup_error('Error in sort_table', 'Exception in sort_table', e)
    return table

synth_views

Synth views.

layout_synth_info()

Elements for Synth layout.

Source code in deluge_gui/synth_views.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def layout_synth_info():
    """Elements for Synth layout."""
    view_synth = [
        [
            sg.Frame(
                "Synth",
                key="-SYNTH-INFO-FRAME-",
                layout=[
                    [
                        sg.Text('', key='-SYNTH-INFO-NAME-', font=FONT_LRG, size=(50,)),
                    ],
                    [
                        sg.Text('Mode', font=FONT_MED, size=(10,)),
                        sg.Text('', key='-SYNTH-INFO-MODE-', font=FONT_MED, size=(10,)),
                        sg.Text('Polyphony', font=FONT_MED, size=(10,)),
                        sg.Text('', key='-SYNTH-INFO-POLYPHONY-', font=FONT_MED, size=(10,)),
                        sg.Text('LPF mode', font=FONT_MED, size=(10,)),
                        sg.Text('', key='-SYNTH-INFO-LPFMODE-', font=FONT_MED, size=(10,)),
                    ],
                    [
                        sg.Text('Mod FX type', font=FONT_MED, size=(10,)),
                        sg.Text('', key='-SYNTH-INFO-MODFXTYPE-', font=FONT_MED, size=(10,)),
                    ],
                ],
            ),
        ],
        [layout_synth_samples()],  # -SYNTH-SAMPLES-TABLE-
    ]
    return view_synth

layout_synth_samples()

List samples in the synth.

Source code in deluge_gui/synth_views.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def layout_synth_samples():
    """List samples in the synth."""
    headings = ['Path', 'Sample']
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                font=FONT_MED,
                # max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='left',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-SYNTH-SAMPLES-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                # tooltip='This is a table',
            )
        ],
    ]
    return layout

layout_synth_table()

A layout a table of synth attributes.

Source code in deluge_gui/synth_views.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def layout_synth_table():
    """A layout a table of synth attributes."""
    headings = [
        'Name',
    ]
    layout = [
        [
            sg.Table(
                values=[],
                headings=headings,
                font=FONT_MED,
                max_col_width=25,
                auto_size_columns=True,
                display_row_numbers=False,
                justification='center',
                num_rows=20,
                alternating_row_color='lightblue',
                key='-SYNTH-TABLE-',
                selected_row_colors='black on yellow',
                enable_events=True,
                expand_x=True,
                expand_y=True,
                enable_click_events=True,  # Comment out to not enable header and other clicks
                tooltip='This is a table',
            )
        ],
        [sg.Text('Cell clicked:'), sg.T(key='-SYNTH-CELL-CLICKED-')],
    ]
    return layout

synth_table_data(synths)

Take a DFS Synths iterable, and return a table with...

Source code in deluge_gui/synth_views.py
75
76
77
78
79
80
81
def synth_table_data(synths):
    """Take a DFS Synths iterable, and return a table with..."""
    # ['Name', ]
    data = []
    for s in synths:
        data.append([s.path.name])
    return data

windows

Window definitions and helper functions.

close_windows(windows)

Dispose of all the windows.

Source code in deluge_gui/windows.py
188
189
190
191
192
193
def close_windows(windows):
    """Dispose of all the windows."""
    windows.song.close()
    windows.sample.close()
    windows.kit.close()
    windows.main.close()

create_windows(state_store)

Define layout and create Windows.

Source code in deluge_gui/windows.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def create_windows(state_store: AppState) -> AppWindows:
    """Define layout and create Windows."""
    window = make_main_window(state_store.card)
    loc = window.current_location()

    # draw song window with first song on card.
    song_window = make_song_window(loc[0] + window.size[0], loc[1])
    song_window.hide()

    # draw sample window with first sample on card.
    sample_window = make_sample_window(loc[0] + window.size[0], loc[1])
    sample_window.hide()

    # draw kit window with first kit on card.
    kit_window = make_kit_window(loc[0] + window.size[0], loc[1])
    kit_window.hide()

    # draw synth window with first synth on card.
    synth_window = make_synth_window(loc[0] + window.size[0], loc[1])
    synth_window.hide()

    # the ummutable list to pass around to event handlers
    return AppWindows(window, song_window, sample_window, kit_window, synth_window)

make_kit_window(x, y)

Create the kit window.

Source code in deluge_gui/windows.py
44
45
46
47
48
49
50
51
52
53
54
55
56
def make_kit_window(x: int, y: int) -> sg.Window:
    """Create the kit window."""
    layout = [[layout_kit_info()]]
    window = sg.Window(
        'Deluge Kit', layout=None, location=(x, y), return_keyboard_events=True, resizable=True, finalize=True
    )
    window.layout(layout)
    window.finalize()
    window.hide()
    window.bind('<Up>', "+KB-UP+")
    window.bind('<Down>', "+KB-DN+")
    window.bind('<space>', "-PLAY-")
    return window

make_main_window(card)

Create the main window.

Source code in deluge_gui/windows.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def make_main_window(card) -> sg.Window:
    """Create the main window."""
    theme = get_theme()
    if not theme:
        theme = sg.OFFICIAL_PYSIMPLEGUI_THEME
    sg.theme(theme)

    # OLD SKOOL TABS
    mainframe = (
        [
            sg.Frame(
                'Mainframe',
                expand_x=True,
                expand_y=True,
                layout=[
                    [
                        sg.TabGroup(
                            [
                                [
                                    sg.Tab(
                                        'Songs',
                                        layout_song_table(),
                                        expand_x=True,
                                        expand_y=True,
                                    ),
                                    sg.Tab(
                                        'Samples',
                                        layout_sample_tree(),
                                        expand_x=True,
                                        expand_y=True,
                                    ),
                                    sg.Tab(
                                        'Kits',
                                        layout_kit_table(),
                                        expand_x=True,
                                        expand_y=True,
                                    ),
                                    sg.Tab(
                                        'Synths',
                                        layout_synth_table(),
                                        expand_x=True,
                                        expand_y=True,
                                    ),
                                ]
                            ],
                            expand_x=True,
                            expand_y=True,
                            font=FONT_MED,
                        )
                    ]
                ],
            )
        ],
    )

    # First the window layout...2 columns
    layout = [
        select_card_control(card.card_root),
        layout_card_info(card),
        mainframe,
        [sg.B('Settings'), sg.Button('Exit'), sg.Sizegrip()],
    ]

    location = sg.user_settings_get_entry('-location-')
    location = (0, 0) if location == [None, None] else location
    print(f'location {location}')
    window = sg.Window(
        APP_NAME,
        layout,
        resizable=True,
        finalize=True,
        return_keyboard_events=True,
        enable_close_attempted_event=True,
        location=location,
    )
    # for keybind strings see https://www.tcl.tk/man/tcl/TkCmd/keysyms.html
    # window.bind('<KeyPress>', "+KB-KEYPRESS+")
    # window.bind('<Up>', "+KB-UP+")
    # window.bind('<Down>', "+KB-DN+")
    # window.bind('<Shift_L>', "+KB-SHIFT_L+")
    # window.bind('<Shift_R>', "+KB-SHIFT_R+")
    window.bind('<space>', "-PLAY-")

    # you can bind to elements too BUT these examples interfere with Table events :(...
    # window['-SONG-TABLE-'].bind('<Button-1>', '+CLICK-1+', True)
    # window['-SONG-TABLE-'].bind('<Button-2>', '+CLICK-2+', False)
    return window

make_sample_window(x, y)

Create the sample window.

Source code in deluge_gui/windows.py
29
30
31
32
33
34
35
36
37
38
39
40
41
def make_sample_window(x: int, y: int) -> sg.Window:
    """Create the sample window."""
    layout = [[layout_sample_info()]]
    window = sg.Window(
        'Deluge Sample', layout=None, location=(x, y), return_keyboard_events=True, resizable=True, finalize=True
    )
    window.layout(layout)
    window.finalize()
    window.hide()
    window.bind('<Up>', "+KB-UP+")
    window.bind('<Down>', "+KB-DN+")
    window.bind('<space>', "-PLAY-")
    return window

make_song_window(x, y)

Create the song window.

Source code in deluge_gui/windows.py
15
16
17
18
19
20
21
22
23
24
25
26
def make_song_window(x: int, y: int) -> sg.Window:
    """Create the song window."""
    layout = [[layout_song_info()]]
    window = sg.Window(
        'Deluge Song', layout=None, location=(x, y), return_keyboard_events=True, resizable=True, finalize=True
    )
    window.layout(layout)
    window.finalize()
    window.hide()
    window.bind('<Up>', "+KB-UP+")
    window.bind('<Down>', "+KB-DN+")
    return window

make_synth_window(x, y)

Create the synth window.

Source code in deluge_gui/windows.py
59
60
61
62
63
64
65
66
67
68
69
70
71
def make_synth_window(x: int, y: int) -> sg.Window:
    """Create the synth window."""
    layout = [[layout_synth_info()]]
    window = sg.Window(
        'Deluge Synth', layout=None, location=(x, y), return_keyboard_events=True, resizable=True, finalize=True
    )
    window.layout(layout)
    window.finalize()
    window.hide()
    window.bind('<Up>', "+KB-UP+")
    window.bind('<Down>', "+KB-DN+")
    window.bind('<space>', "-PLAY-")
    return window