preset browser widget / lock preset

This commit is contained in:
Paul Lipscomb
2026-06-29 01:49:39 -04:00
parent 0a5cfa88f1
commit 0aad488db5
3 changed files with 392 additions and 63 deletions
+90 -58
View File
@@ -19,6 +19,7 @@ import threading
from togglewidget import ToggleWidget
from faderwidget import FaderWidget
from transportwidget import TransportWidget
from presetwidget import PresetWidget
from palette import (
PALETTE,
@@ -100,6 +101,7 @@ midi_in_port = None
fader_show_hide_config = False
transport_show_hide_config = False
toggle_show_hide_config = False
preset_switch_locked = False
uuid_presets_matched = []
uuid_presets_received_values = []
@@ -163,6 +165,14 @@ buttonImport = QPushButton("Import")
buttonImport.setFixedWidth(55)
buttonImport.clicked.connect(lambda: import_preset())
checkbox_lock_preset = QCheckBox("Lock Preset")
checkbox_lock_preset.setStyleSheet("color: #aaa; font-size: 11px;")
checkbox_lock_preset.setChecked(False)
def on_lock_preset_toggled(checked):
global preset_switch_locked
preset_switch_locked = checked
checkbox_lock_preset.toggled.connect(on_lock_preset_toggled)
#save status label (should be used for other stuff as well) -> save_status_label
labelStatus = QLabel("")
labelStatus.setStyleSheet("color: #00e676; font-style: italic;")
@@ -488,42 +498,16 @@ textedit_osc_out_log.setStyleSheet("""
""")
# MARK: PRESET BROWSER ROW MODULE
# REFACTOR MAP: Old names → New names
# self.browser_back_spin → spinner_preset_browser_back
# self.browser_fwd_spin → spinner_preset_browser_fwd
# Widget: label_preset_browser_title
label_preset_browser_title = QLabel("Preset Browser")
# Layout: hstack_preset_browser
hstack_preset_browser = QHBoxLayout()
hstack_preset_browser.setSpacing(8)
# Widget: button_preset_browser_back
button_preset_browser_back = QPushButton("")
button_preset_browser_back.setFixedSize(36, 28)
preset_browser_back = PresetWidget(label="Back", default_cc=22)
preset_browser_fwd = PresetWidget(label="Fwd", default_cc=23)
preset_browsers = [preset_browser_back, preset_browser_fwd]
# Widget: spinner_preset_browser_back
spinner_preset_browser_back = QSpinBox()
spinner_preset_browser_back.setMinimum(1)
spinner_preset_browser_back.setMaximum(127)
spinner_preset_browser_back.setValue(22)
spinner_preset_browser_back.setFixedWidth(55)
# Widget: button_preset_browser_fwd
button_preset_browser_fwd = QPushButton("")
button_preset_browser_fwd.setFixedSize(36, 28)
# Widget: spinner_preset_browser_fwd
spinner_preset_browser_fwd = QSpinBox()
spinner_preset_browser_fwd.setMinimum(1)
spinner_preset_browser_fwd.setMaximum(127)
spinner_preset_browser_fwd.setValue(23)
spinner_preset_browser_fwd.setFixedWidth(55)
# Widget: spacer_preset_browser
spacer_preset_browser = QWidget()
spacer_preset_browser.setFixedWidth(12)
# show/hide state (mirrors fader/toggle pattern)
preset_browser_show_hide_config = False
# MARK: TRANSPORT MODULE
# REFACTOR MAP: Old names → New names
@@ -701,6 +685,13 @@ button_transport_show_hide_config.setStyleSheet(_show_hide_btn_style)
hstack_row_toggle_control_buttons = QHBoxLayout()
hstack_row_toggle_control_buttons.setSpacing(8)
# Widget: button_preset_browser_show_hide_config
button_preset_browser_show_hide_config = QPushButton("Hide Parameters")
button_preset_browser_show_hide_config.setFixedWidth(120)
button_preset_browser_show_hide_config.setCheckable(True)
button_preset_browser_show_hide_config.setFlat(True)
button_preset_browser_show_hide_config.setStyleSheet(_show_hide_btn_style)
# Widget: button_toggle_show_hide_config
button_toggle_show_hide_config = QPushButton("Hide Parameters")
button_toggle_show_hide_config.setFixedWidth(120)
@@ -756,6 +747,7 @@ class MainWindow(QMainWindow):
hstack_preset_row.addWidget(buttonRemove) #remove button
hstack_preset_row.addWidget(buttonExport)
hstack_preset_row.addWidget(buttonImport)
hstack_preset_row.addWidget(checkbox_lock_preset)
hstack_preset_row.addWidget(label_preset_uuid)
hstack_preset_row.addWidget(button_preset_uuid_gen)
hstack_preset_row.addWidget(button_preset_uuid_copy)
@@ -1031,32 +1023,26 @@ class MainWindow(QMainWindow):
global container_preset_browser
global label_preset_browser_title
global hstack_preset_browser
global button_preset_browser_back
global spinner_preset_browser_back
global button_preset_browser_fwd
global spinner_preset_browser_fwd
global spacer_preset_browser
container_preset_browser = QWidget()
vstack_preset_browser_container = QVBoxLayout(container_preset_browser)
vstack_preset_browser_container.setContentsMargins(0, 0, 0, 0)
# assemble browser controls
hstack_preset_browser.addWidget(button_preset_browser_back)
hstack_preset_browser.addWidget(spinner_preset_browser_back)
hstack_preset_browser.addWidget(spacer_preset_browser)
hstack_preset_browser.addWidget(button_preset_browser_fwd)
hstack_preset_browser.addWidget(spinner_preset_browser_fwd)
hstack_preset_browser.addWidget(preset_browser_back)
hstack_preset_browser.addWidget(preset_browser_fwd)
hstack_preset_browser.addStretch(1)
hstack_preset_browser_controls = QHBoxLayout()
hstack_preset_browser_controls.addWidget(button_preset_browser_show_hide_config)
hstack_preset_browser_controls.addStretch()
button_preset_browser_show_hide_config.clicked.connect(preset_browser_show_hide_config_toggle)
vstack_preset_browser_container.addWidget(make_separator())
vstack_preset_browser_container.addWidget(label_preset_browser_title)
vstack_preset_browser_container.addLayout(hstack_preset_browser_controls)
vstack_preset_browser_container.addLayout(hstack_preset_browser)
main_layout.addWidget(container_preset_browser)
# connect signals
button_preset_browser_back.clicked.connect(lambda: send_cc(spinner_preset_browser_back.value(), 127))
button_preset_browser_fwd.clicked.connect(lambda: send_cc(spinner_preset_browser_fwd.value(), 127))
# MARK: CONTAINER ROWS AND WIDGETS (fader + toggle only)
global container_rows_and_widgets
@@ -1285,7 +1271,7 @@ def new_blank_preset():
if not ok or not name.strip():
return
name = name.strip()
loaded_presets["presets"][name] = {"faders": [], "toggles": [], "preset_uuid": "", "browser": {}, "show_hide": {}}
loaded_presets["presets"][name] = {"faders": [], "toggles": [], "preset_uuid": "", "browser": {"back": preset_browser_back.get_state(), "fwd": preset_browser_fwd.get_state()}, "show_hide": {}}
loaded_presets["__last_used__"] = name
save_presets_file(loaded_presets)
populate_preset_dropdown_menu()
@@ -1449,7 +1435,8 @@ def _check_track_uuid_arrived():
_track_uuid_check_pending = False
label_osc_status.setText("No preset linked to this track")
label_osc_status.setStyleSheet("color: #f0c040; font-size: 11px; font-style: italic;")
ui_show_uuid_error_overlay("No preset linked to this track", "#f0c040")
if not preset_switch_locked:
ui_show_uuid_error_overlay("No preset linked to this track", "#f0c040")
def update_ui_on_osc_track_received(name):
global _track_uuid_check_pending
@@ -1816,14 +1803,15 @@ def preset_build_snapshot(name):
"faders": [f.get_state() for f in faders],
"toggles": [t.get_state() for t in toggles],
"browser": {
"back_cc": spinner_preset_browser_back.value(),
"fwd_cc": spinner_preset_browser_fwd.value()
"back": preset_browser_back.get_state(),
"fwd": preset_browser_fwd.get_state(),
},
"preset_uuid": label_preset_uuid.text().strip(),
"show_hide": {
"fader": fader_show_hide_config,
"toggle": toggle_show_hide_config,
"transport": transport_show_hide_config,
"browser": preset_browser_show_hide_config,
}
}
@@ -1916,8 +1904,14 @@ def preset_load(preset):
hstack_toggle_zone_right.addWidget(t)
browser = preset.get("browser", {})
spinner_preset_browser_back.setValue(browser.get("back_cc", 22))
spinner_preset_browser_fwd.setValue(browser.get("fwd_cc", 23))
# migrate old format that only stored cc numbers
if "back" not in browser and "back_cc" in browser:
browser = {
"back": {"label": "Back", "cc": browser.get("back_cc", 22), "ch": 1},
"fwd": {"label": "Fwd", "cc": browser.get("fwd_cc", 23), "ch": 1},
}
preset_browser_back.set_state(browser.get("back", {"label": "Back", "cc": 22, "ch": 1}))
preset_browser_fwd.set_state(browser.get("fwd", {"label": "Fwd", "cc": 23, "ch": 1}))
show_hide = preset.get("show_hide", {})
fader_show_hide_config_set(show_hide.get("fader", False))
@@ -1928,6 +1922,10 @@ def preset_load(preset):
button_toggle_show_hide_config.setText("Show Parameters" if show_hide.get("toggle", False) else "Hide Parameters")
button_toggle_show_hide_config.setChecked(show_hide.get("toggle", False))
preset_browser_show_hide_config_set(show_hide.get("browser", False))
button_preset_browser_show_hide_config.setText("Show Parameters" if show_hide.get("browser", False) else "Hide Parameters")
button_preset_browser_show_hide_config.setChecked(show_hide.get("browser", False))
label_preset_uuid.setText(preset.get("preset_uuid", ""))
check_and_index_strip_labels()
@@ -2012,22 +2010,25 @@ def resolve_uuid_batch():
global uuid_presets_matched, uuid_presets_received_values
uuid_presets_matched = list(dict.fromkeys(uuid_presets_matched))
if len(uuid_presets_matched) == 1:
menuPresets.setCurrentText(uuid_presets_matched[0])
track_name = label_osc_track.text()
label_osc_preset.setText(uuid_presets_matched[0])
label_osc_status.setText(f"DAW Track Selected: {track_name}")
label_osc_status.setStyleSheet("color: #00e676; font-size: 11px; font-style: italic;")
ui_show_rows_and_widgets()
if not preset_switch_locked:
menuPresets.setCurrentText(uuid_presets_matched[0])
ui_show_rows_and_widgets()
elif len(uuid_presets_matched) > 1:
label_osc_status.setText("MULTIPLE PRESET IDs DETECTED")
label_osc_status.setStyleSheet("color: #ff4444; font-size: 11px; font-style: italic;")
ui_show_uuid_error_overlay("MULTIPLE PRESET IDs DETECTED", "#ff4444")
QTimer.singleShot(4000, lambda: (label_osc_status.setText(""), label_osc_status.setStyleSheet("color: #00e676; font-size: 11px; font-style: italic;")))
if not preset_switch_locked:
ui_show_uuid_error_overlay("MULTIPLE PRESET IDs DETECTED", "#ff4444")
QTimer.singleShot(4000, lambda: (label_osc_status.setText(""), label_osc_status.setStyleSheet("color: #00e676; font-size: 11px; font-style: italic;")))
else:
label_osc_status.setText("NO MATCHING ID IN PRESET LIST")
label_osc_status.setStyleSheet("color: #ff4444; font-size: 11px; font-style: italic;")
ui_show_uuid_error_overlay("NO MATCHING ID IN PRESET LIST", "#ff4444")
QTimer.singleShot(4000, lambda: (label_osc_status.setText(""), label_osc_status.setStyleSheet("color: #00e676; font-size: 11px; font-style: italic;")))
if not preset_switch_locked:
ui_show_uuid_error_overlay("NO MATCHING ID IN PRESET LIST", "#ff4444")
QTimer.singleShot(4000, lambda: (label_osc_status.setText(""), label_osc_status.setStyleSheet("color: #00e676; font-size: 11px; font-style: italic;")))
uuid_presets_matched.clear()
uuid_presets_received_values.clear()
@@ -2086,6 +2087,7 @@ def route_midi_message_input(event, data=None):
bound_fader = None
bound_transport = None
bound_browser = None
if status == 0xB0:
for fader in faders:
if fader.hw_cc == cc_num:
@@ -2101,13 +2103,20 @@ def route_midi_message_input(event, data=None):
if hasattr(t, 'hw_cc') and t.hw_cc == cc_num:
bound_transport = t
break
if not bound_fader and not bound_transport:
for w in preset_browsers:
if w.hw_cc == cc_num:
bound_browser = w
break
if status == 0xB0 and learning_strip:
learning_strip.bind_hw_cc(cc_num, channel=channel)
learning_strip = None
return
if bound_transport:
if bound_browser:
bound_browser.on_hw_trigger(cc_val)
elif bound_transport:
print(f"[transport hw] CC{cc_num} val={cc_val} → uid={bound_transport.uid}")
midi_receiver.hw_cc_signal.emit(bound_transport.uid, cc_val)
elif bound_fader:
@@ -2333,7 +2342,30 @@ def toggle_show_hide_config_toggle():
button_toggle_show_hide_config.setText("Show Parameters" if enabled else "Hide Parameters")
button_toggle_show_hide_config.setChecked(enabled)
# MARK: PRESET BROWSER SHOW HIDE CONFIG
def preset_browser_show_hide_config_set(enabled):
global preset_browser_show_hide_config
preset_browser_show_hide_config = enabled
for w in preset_browsers:
w.grp_learn.setVisible(not enabled)
w.arrow_lbl.setVisible(not enabled)
w.grp_cc.setVisible(not enabled)
w.title_edit.setVisible(not enabled)
w.momentary_btn.setVisible(not enabled)
w.toggle_mode_btn.setVisible(not enabled)
w.btn.setFixedHeight(24 if enabled else 40)
w.container.setStyleSheet(
"QFrame#presetContainer { background: transparent; border: none; }"
if enabled else
"QFrame#presetContainer { background-color: #4a5568; border-radius: 6px; border: 2px solid rgba(0,0,0,0.3); }"
)
def preset_browser_show_hide_config_toggle():
global preset_browser_show_hide_config
enabled = not preset_browser_show_hide_config
preset_browser_show_hide_config_set(enabled)
button_preset_browser_show_hide_config.setText("Show Parameters" if enabled else "Hide Parameters")
button_preset_browser_show_hide_config.setChecked(enabled)
# VS Code dark theme palette