from PySide6.QtCore import Qt from PySide6.QtGui import QFont from PySide6.QtWidgets import ( QWidget, QLabel, QVBoxLayout, QHBoxLayout, QPushButton, QSlider, QButtonGroup, QScrollArea, QScroller, ) class SoundScreen(QWidget): def __init__(self, on_eq): super().__init__() self._on_eq = on_eq root = QVBoxLayout(self) root.setContentsMargins(0, 0, 0, 0) root.setSpacing(12) scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setFrameShape(QScrollArea.NoFrame) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) scroller = QScroller.scroller(scroll.viewport()) scroller.grabGesture( scroll.viewport(), QScroller.LeftMouseButtonGesture ) content = QWidget() content_layout = QVBoxLayout(content) content_layout.setContentsMargins(0, 0, 0, 0) content_layout.setSpacing(12) content_layout.addWidget(self._build_volume_card()) content_layout.addWidget(self._build_premute_card()) content_layout.addWidget(self._build_ducking_card()) content_layout.addWidget(self._build_balance_card()) content_layout.addWidget(self._build_tone_card()) content_layout.addWidget(self._build_eq_card()) content_layout.addStretch(1) scroll.setWidget(content) root.addWidget(scroll, 1) def _build_volume_card(self) -> QWidget: card, body = _card("Громкость") body.addWidget( _slider_row( "Базовая громкость", 0, 100, 60, lambda v: f"{v}%", ) ) return card def _build_premute_card(self) -> QWidget: card, body = _card("Премут") body.addWidget(_toggle_row("Премут", checked=False)) body.addWidget( _slider_row( "Громкость премут", 0, 100, 10, lambda v: f"{v}%", ) ) return card def _build_ducking_card(self) -> QWidget: card, body = _card("Ducking") body.addWidget(_toggle_row("Ducking", checked=True)) body.addWidget( _slider_row( "Громкость при Ducking", 0, 100, 35, lambda v: f"{v}%", ) ) return card def _build_balance_card(self) -> QWidget: card, body = _card("Баланс и фейдер") body.addWidget( _slider_row( "Баланс L/R", -50, 50, 0, _format_balance, ) ) body.addWidget( _slider_row( "Фейдер F/R", -50, 50, 0, _format_fader, ) ) return card def _build_tone_card(self) -> QWidget: card, body = _card("Тон") row = QWidget() row.setObjectName("SoundToneRow") layout = QHBoxLayout(row) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(10) group = QButtonGroup(row) group.setExclusive(True) for idx, label in enumerate(("Тёплый", "Нейтральный", "Яркий")): btn = QPushButton(label) btn.setObjectName("SoundToneBtn") btn.setCheckable(True) btn.setMinimumHeight(40) btn.setFont(QFont("", 13, 600)) if idx == 1: btn.setChecked(True) group.addButton(btn) layout.addWidget(btn, 1) body.addWidget(row) return card def _build_eq_card(self) -> QWidget: card, body = _card("Эквалайзер") btn = QPushButton("Открыть эквалайзер") btn.setObjectName("SoundEqBtn") btn.setMinimumHeight(48) btn.setFont(QFont("", 14, 700)) btn.clicked.connect(self._on_eq) body.addWidget(btn) return card def _card(title: str) -> tuple[QWidget, QVBoxLayout]: card = QWidget() card.setObjectName("SoundCard") layout = QVBoxLayout(card) layout.setContentsMargins(14, 12, 14, 12) layout.setSpacing(10) header = QLabel(title) header.setObjectName("SoundCardTitle") header.setFont(QFont("", 14, 700)) layout.addWidget(header) return card, layout def _toggle_row(label: str, checked: bool) -> QWidget: row = QWidget() row.setObjectName("SoundToggleRow") layout = QHBoxLayout(row) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(12) lbl = QLabel(label) lbl.setFont(QFont("", 13, 600)) btn = QPushButton("Выкл") btn.setObjectName("SoundToggle") btn.setCheckable(True) btn.setChecked(checked) btn.setMinimumHeight(36) btn.setMinimumWidth(86) btn.setFont(QFont("", 12, 700)) def _sync_text(is_checked: bool): btn.setText("Вкл" if is_checked else "Выкл") btn.toggled.connect(_sync_text) _sync_text(btn.isChecked()) layout.addWidget(lbl) layout.addStretch(1) layout.addWidget(btn) return row def _slider_row( label: str, minimum: int, maximum: int, value: int, formatter, ) -> QWidget: row = QWidget() row.setObjectName("SoundSliderRow") layout = QVBoxLayout(row) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(6) header = QWidget() header_layout = QHBoxLayout(header) header_layout.setContentsMargins(0, 0, 0, 0) header_layout.setSpacing(8) lbl = QLabel(label) lbl.setFont(QFont("", 13, 600)) val = QLabel(formatter(value)) val.setObjectName("SoundValue") val.setFont(QFont("", 12, 600)) val.setAlignment(Qt.AlignRight | Qt.AlignVCenter) header_layout.addWidget(lbl) header_layout.addStretch(1) header_layout.addWidget(val) slider = QSlider(Qt.Horizontal) slider.setObjectName("SoundSlider") slider.setRange(minimum, maximum) slider.setValue(value) slider.valueChanged.connect(lambda v: val.setText(formatter(v))) layout.addWidget(header) layout.addWidget(slider) return row def _format_balance(value: int) -> str: if value == 0: return "Центр" side = "L" if value < 0 else "R" return f"{side} {abs(value)}" def _format_fader(value: int) -> str: if value == 0: return "Центр" side = "F" if value < 0 else "R" return f"{side} {abs(value)}"