feat: per-strip CC/CH translation, layout groups, show/hide improvements
This commit is contained in:
+97
-23
@@ -22,10 +22,12 @@ from midi_sender import send_cc
|
||||
|
||||
class FaderWidget(QWidget):
|
||||
def __init__(self, label, default_cc):
|
||||
|
||||
super().__init__()
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||
self.setFixedWidth(100)
|
||||
self.current_color = "transparent"
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Maximum)
|
||||
self.setFixedWidth(115)
|
||||
self.current_color = PALETTE_HEX.get("Gray", "#263238")
|
||||
|
||||
self.uid = str(uuid.uuid4())
|
||||
|
||||
@@ -48,41 +50,96 @@ class FaderWidget(QWidget):
|
||||
inner.setSpacing(4)
|
||||
inner.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
# Learn button
|
||||
# Learn / CC / CH group
|
||||
self.hw_cc = None
|
||||
self.hw_channel = None
|
||||
self.is_learning = False
|
||||
|
||||
_grp_style = "QFrame { border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; }"
|
||||
|
||||
# Group 1: Learn + input readout (border always visible; learn_btn hideable)
|
||||
self.grp_learn = QFrame()
|
||||
self.grp_learn.setStyleSheet(_grp_style)
|
||||
_grp1_layout = QVBoxLayout(self.grp_learn)
|
||||
_grp1_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp1_layout.setSpacing(3)
|
||||
|
||||
self.learn_btn = QPushButton("Learn")
|
||||
self.learn_btn.setFixedHeight(20)
|
||||
self.learn_btn.setStyleSheet("border: none;")
|
||||
self.learn_btn.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
self.learn_btn.customContextMenuRequested.connect(self.on_learn_context_menu)
|
||||
inner.addWidget(self.learn_btn)
|
||||
_grp1_layout.addWidget(self.learn_btn)
|
||||
|
||||
# CC In label
|
||||
self.cc_input_lbl = QLabel("")
|
||||
self.cc_input_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px;")
|
||||
inner.addWidget(self.cc_input_lbl)
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px; border: none;")
|
||||
self.cc_input_lbl.setFixedHeight(16)
|
||||
_grp1_layout.addWidget(self.cc_input_lbl)
|
||||
|
||||
inner.addWidget(self.grp_learn)
|
||||
|
||||
_arrow_lbl = QLabel("▼")
|
||||
_arrow_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
_arrow_lbl.setStyleSheet("color: rgba(255,255,255,0.3); font-size: 9px; background: transparent;")
|
||||
_arrow_lbl.setFixedHeight(12)
|
||||
inner.addWidget(_arrow_lbl)
|
||||
|
||||
# Group 2: CC spin + CH spin + output readout (border always visible; spins hideable)
|
||||
self.grp_cc = QFrame()
|
||||
self.grp_cc.setStyleSheet(_grp_style)
|
||||
_grp2_layout = QVBoxLayout(self.grp_cc)
|
||||
_grp2_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp2_layout.setSpacing(3)
|
||||
|
||||
# CC spin
|
||||
self.cc_spin = QSpinBox()
|
||||
self.cc_spin.setMinimum(1)
|
||||
self.cc_spin.setMinimum(0)
|
||||
self.cc_spin.setMaximum(127)
|
||||
self.cc_spin.setValue(default_cc)
|
||||
self.cc_spin.setFixedWidth(75)
|
||||
inner.addWidget(self.cc_spin, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_spin.setFixedWidth(55)
|
||||
_cc_lbl = QLabel("CC")
|
||||
_cc_lbl.setStyleSheet("color: #fff; font-size: 10px; border: none;")
|
||||
self.cc_spin_row = QWidget()
|
||||
_hbox_cc = QHBoxLayout(self.cc_spin_row)
|
||||
_hbox_cc.setContentsMargins(0, 0, 0, 0)
|
||||
_hbox_cc.setSpacing(4)
|
||||
_hbox_cc.addWidget(_cc_lbl)
|
||||
_hbox_cc.addWidget(self.cc_spin)
|
||||
_grp2_layout.addWidget(self.cc_spin_row)
|
||||
|
||||
self.cc_output_lbl = QLabel("")
|
||||
self.ch_spin = QSpinBox()
|
||||
self.ch_spin.setMinimum(1)
|
||||
self.ch_spin.setMaximum(16)
|
||||
self.ch_spin.setValue(1)
|
||||
self.ch_spin.setFixedWidth(55)
|
||||
_ch_lbl = QLabel("CH")
|
||||
_ch_lbl.setStyleSheet("color: #fff; font-size: 10px; border: none;")
|
||||
self.ch_spin_row = QWidget()
|
||||
_hbox_ch = QHBoxLayout(self.ch_spin_row)
|
||||
_hbox_ch.setContentsMargins(0, 0, 0, 0)
|
||||
_hbox_ch.setSpacing(4)
|
||||
_hbox_ch.addWidget(_ch_lbl)
|
||||
_hbox_ch.addWidget(self.ch_spin)
|
||||
_grp2_layout.addWidget(self.ch_spin_row)
|
||||
|
||||
self.cc_output_lbl = QLabel(f"CC{default_cc} [--]")
|
||||
self.cc_output_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px;")
|
||||
inner.addWidget(self.cc_output_lbl)
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self.cc_output_lbl.setFixedHeight(16)
|
||||
_grp2_layout.addWidget(self.cc_output_lbl)
|
||||
|
||||
inner.addWidget(self.grp_cc)
|
||||
|
||||
|
||||
|
||||
# Fader
|
||||
self.fader = QSlider(Qt.Orientation.Vertical)
|
||||
self.fader.setMinimum(0)
|
||||
self.fader.setMaximum(127)
|
||||
self.fader.setValue(64)
|
||||
self.fader.setFixedHeight(200)
|
||||
self.fader.setMinimumHeight(200)
|
||||
self.fader.setFixedWidth(40)
|
||||
self.fader.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
|
||||
self.fader.setStyleSheet(FADER_STYLE)
|
||||
inner.addWidget(self.fader, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
@@ -214,7 +271,7 @@ class FaderWidget(QWidget):
|
||||
|
||||
def on_fader_moved(self, value):
|
||||
cc_num = self.cc_spin.value()
|
||||
self.cc_output_lbl.setText(f"Out CC{cc_num} [{value}]")
|
||||
self.cc_output_lbl.setText(self._cc_output_label(value))
|
||||
if self.pickup_mode and self.hunting:
|
||||
crossed = (
|
||||
(self.prev_position < self.last_sent <= value) or
|
||||
@@ -226,21 +283,21 @@ class FaderWidget(QWidget):
|
||||
self._stop_blink()
|
||||
self._set_readout_style("orange")
|
||||
self.last_sent = value
|
||||
send_cc(cc_num, value)
|
||||
send_cc(cc_num, value, channel=self.ch_spin.value())
|
||||
else:
|
||||
self.readout.setText(f"CC {cc_num} → {value}")
|
||||
return
|
||||
else:
|
||||
self.last_sent = value
|
||||
self.prev_position = value
|
||||
send_cc(cc_num, value)
|
||||
send_cc(cc_num, value, channel=self.ch_spin.value())
|
||||
self.readout.setText(f"CC {cc_num} → {value}")
|
||||
|
||||
def on_cc_changed(self):
|
||||
cc_num = self.cc_spin.value()
|
||||
value = self.fader.value()
|
||||
self.readout.setText(f"CC {cc_num} → {value}")
|
||||
self.cc_output_lbl.setText(f"Out CC{cc_num} [{value}]")
|
||||
self.cc_output_lbl.setText(self._cc_output_label(value))
|
||||
if self.pickup_mode:
|
||||
self.hunting = True
|
||||
self._start_blink()
|
||||
@@ -307,6 +364,7 @@ class FaderWidget(QWidget):
|
||||
action = menu.exec(self.learn_btn.mapToGlobal(pos))
|
||||
if action == clear_action:
|
||||
self.hw_cc = None
|
||||
self.hw_channel = None
|
||||
self.is_learning = False
|
||||
self.learn_btn.setText("Learn")
|
||||
self.learn_btn.setStyleSheet("")
|
||||
@@ -326,20 +384,32 @@ class FaderWidget(QWidget):
|
||||
self.learn_btn.setText("Listening...")
|
||||
self.learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def _cc_input_label(self, vel="--"):
|
||||
if self.hw_cc is None:
|
||||
return ""
|
||||
ch = f" CH{self.hw_channel}" if self.hw_channel is not None else ""
|
||||
return f"CC{self.hw_cc}{ch} [{vel}]"
|
||||
|
||||
def _cc_output_label(self, vel="--"):
|
||||
out_cc = self.cc_spin.value()
|
||||
out_ch = self.ch_spin.value()
|
||||
return f"CC{out_cc} CH{out_ch} [{vel}]"
|
||||
|
||||
def stop_learn(self):
|
||||
self.is_learning = False
|
||||
self.learn_btn.setStyleSheet("")
|
||||
if self.hw_cc is not None:
|
||||
self.learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.cc_input_lbl.setText(f"In CC{self.hw_cc} [--]")
|
||||
self.cc_input_lbl.setText(self._cc_input_label())
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px;")
|
||||
else:
|
||||
self.learn_btn.setText("Learn")
|
||||
self.cc_input_lbl.setText("")
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px;")
|
||||
|
||||
def bind_hw_cc(self, cc_number):
|
||||
def bind_hw_cc(self, cc_number, channel=None):
|
||||
self.hw_cc = cc_number
|
||||
self.hw_channel = channel
|
||||
self.stop_learn()
|
||||
|
||||
def get_state(self):
|
||||
@@ -347,11 +417,13 @@ class FaderWidget(QWidget):
|
||||
"uid": self.uid,
|
||||
"label": self.title_edit.text(),
|
||||
"cc": self.cc_spin.value(),
|
||||
"ch": self.ch_spin.value(),
|
||||
"value": self.fader.value(),
|
||||
"color": self.color_name,
|
||||
"pickup": self.pickup_checkbox.isChecked(),
|
||||
"last_sent": self.last_sent,
|
||||
"hw_cc": self.hw_cc,
|
||||
"hw_channel": self.hw_channel,
|
||||
"zone": self.zone,
|
||||
"center_index": self.center_index,
|
||||
"zone_index": self.zone_index
|
||||
@@ -362,6 +434,7 @@ class FaderWidget(QWidget):
|
||||
self.uid = state["uid"]
|
||||
self.title_edit.setText(state.get("label", ""))
|
||||
self.cc_spin.setValue(state.get("cc", 1))
|
||||
self.ch_spin.setValue(state.get("ch", 1))
|
||||
self.last_sent = state.get("last_sent", 64)
|
||||
self.fader.setValue(state.get("value", 64))
|
||||
color_name = state.get("color", "Gray")
|
||||
@@ -379,9 +452,10 @@ class FaderWidget(QWidget):
|
||||
self.zone_index = state.get("zone_index", None)
|
||||
self.zone_btn.set_state(self.zone == "left", self.zone == "right")
|
||||
self.hw_cc = state.get("hw_cc", None)
|
||||
self.hw_channel = state.get("hw_channel", None)
|
||||
if self.hw_cc is not None:
|
||||
self.learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.cc_input_lbl.setText(f"In CC{self.hw_cc} [--]")
|
||||
self.cc_input_lbl.setText(self._cc_input_label())
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px;")
|
||||
else:
|
||||
self.learn_btn.setText("Learn")
|
||||
|
||||
Reference in New Issue
Block a user