From 399357dc9517630956d58472db6f0623cbd748ef Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 9 Jan 2026 02:43:16 +0300 Subject: [PATCH] add sound info --- screens/setting/eq_screen.py | 19 +++ screens/setting/sound_screen.py | 248 ++++++++++++++++++++++++++++++++ screens/settings.py | 19 ++- themes/day.py | 40 ++++++ themes/night.py | 37 +++++ 5 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 screens/setting/eq_screen.py create mode 100644 screens/setting/sound_screen.py diff --git a/screens/setting/eq_screen.py b/screens/setting/eq_screen.py new file mode 100644 index 0000000..81651c6 --- /dev/null +++ b/screens/setting/eq_screen.py @@ -0,0 +1,19 @@ +from PySide6.QtCore import Qt +from PySide6.QtGui import QFont +from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout + + +class EqualizerScreen(QWidget): + def __init__(self): + super().__init__() + root = QVBoxLayout(self) + root.setContentsMargins(0, 0, 0, 0) + root.setSpacing(0) + + root.addStretch(1) + label = QLabel("Эквалайзер\n(в разработке)") + label.setObjectName("EqPlaceholder") + label.setAlignment(Qt.AlignCenter) + label.setFont(QFont("", 18, 700)) + root.addWidget(label) + root.addStretch(1) diff --git a/screens/setting/sound_screen.py b/screens/setting/sound_screen.py new file mode 100644 index 0000000..2b320df --- /dev/null +++ b/screens/setting/sound_screen.py @@ -0,0 +1,248 @@ +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)}" diff --git a/screens/settings.py b/screens/settings.py index 6a79d60..f35c23d 100644 --- a/screens/settings.py +++ b/screens/settings.py @@ -15,6 +15,8 @@ from PySide6.QtWidgets import QScroller from screens.setting.bluetooth_screen import BluetoothScreen from screens.setting.about_screen import build_about_screen from screens.setting.dev_screen import build_dev_screen +from screens.setting.sound_screen import SoundScreen +from screens.setting.eq_screen import EqualizerScreen class SettingsRow(QPushButton): @@ -105,7 +107,7 @@ class SettingsScreen(QWidget): ) bt_row = bt_rows.get("Bluetooth") - self._add_section( + display_rows = self._add_section( content_layout, "Дисплей и звук", [ @@ -113,6 +115,7 @@ class SettingsScreen(QWidget): ("Звук", "Громкость, эквалайзер"), ], ) + sound_row = display_rows.get("Звук") system_rows = self._add_section( content_layout, @@ -132,10 +135,14 @@ class SettingsScreen(QWidget): self._dev_screen = build_dev_screen(self._exit_app) self._about_screen = build_about_screen() self._bt_screen = BluetoothScreen(self._show_list) + self._eq_screen = EqualizerScreen() + self._sound_screen = SoundScreen(self._show_equalizer) self.stack.addWidget(self._list_screen) self.stack.addWidget(self._dev_screen) self.stack.addWidget(self._about_screen) self.stack.addWidget(self._bt_screen) + self.stack.addWidget(self._sound_screen) + self.stack.addWidget(self._eq_screen) if dev_row is not None: dev_row.clicked.connect(self._show_dev) @@ -143,6 +150,8 @@ class SettingsScreen(QWidget): about_row.clicked.connect(self._show_about) if bt_row is not None: bt_row.clicked.connect(self._show_bluetooth) + if sound_row is not None: + sound_row.clicked.connect(self._show_sound) self._show_list() def _add_section( @@ -183,6 +192,14 @@ class SettingsScreen(QWidget): def show_list(self): self._show_list() + def _show_sound(self): + self.stack.setCurrentWidget(self._sound_screen) + self.view_changed.emit("Звук", True) + + def _show_equalizer(self): + self.stack.setCurrentWidget(self._eq_screen) + self.view_changed.emit("Эквалайзер", True) + def _exit_app(self): app = QApplication.instance() if app is not None: diff --git a/themes/day.py b/themes/day.py index cb0c4fe..fae3dc7 100644 --- a/themes/day.py +++ b/themes/day.py @@ -46,6 +46,46 @@ QMenu::item:selected { background: #F3F4F6; } #SettingsChevron { color: rgba(107,114,128,0.95); } #SettingsRowTitle, #SettingsRowSub, #SettingsChevron { background: transparent; } +#SoundCard { + background: #FFFFFF; + border-radius: 14px; + border: 1px solid #E5E7EB; +} +#SoundCardTitle { color: rgba(55,65,81,0.9); } +#SoundValue { color: rgba(107,114,128,0.95); } +#SoundSlider::groove:horizontal { height: 8px; background: #E5E7EB; border-radius: 4px; } +#SoundSlider::handle:horizontal { width: 18px; margin: -6px 0; background: #111827; border-radius: 9px; } +#SoundToggle { + background: #F3F4F6; + color: #111827; + border-radius: 10px; + border: 1px solid #E5E7EB; + padding: 6px 12px; +} +#SoundToggle:checked { + background: #111827; + color: #FFFFFF; + border: 1px solid #111827; +} +#SoundToneBtn { + background: #FFFFFF; + color: #111827; + border-radius: 10px; + border: 1px solid #E5E7EB; + padding: 6px 10px; +} +#SoundToneBtn:checked { background: #E5E7EB; } +#SoundEqBtn { + background: #FFFFFF; + color: #111827; + border-radius: 12px; + border: 1px solid #E5E7EB; + padding: 8px 14px; + text-align: left; +} +#SoundEqBtn:hover { background: #F9FAFB; } +#EqPlaceholder { color: rgba(107,114,128,0.95); } + #SettingsBackBtn { background: #FFFFFF; border-radius: 12px; diff --git a/themes/night.py b/themes/night.py index 287a95a..8a0e793 100644 --- a/themes/night.py +++ b/themes/night.py @@ -43,6 +43,43 @@ QMenu::item:selected { background: #1B2330; } #SettingsChevron { color: rgba(138,147,166,0.95); } #SettingsRowTitle, #SettingsRowSub, #SettingsChevron { background: transparent; } +#SoundCard { + background: #141A22; + border-radius: 14px; +} +#SoundCardTitle { color: rgba(138,147,166,0.95); } +#SoundValue { color: rgba(138,147,166,0.95); } +#SoundSlider::groove:horizontal { height: 8px; background: #1B2330; border-radius: 4px; } +#SoundSlider::handle:horizontal { width: 18px; margin: -6px 0; background: #E6EAF0; border-radius: 9px; } +#SoundToggle { + background: #1B2330; + color: #E6EAF0; + border-radius: 10px; + padding: 6px 12px; +} +#SoundToggle:checked { + background: #E6EAF0; + color: #0B0E11; +} +#SoundToneBtn { + background: #141A22; + color: #E6EAF0; + border-radius: 10px; + border: 1px solid #1B2330; + padding: 6px 10px; +} +#SoundToneBtn:checked { background: #2A3A52; } +#SoundEqBtn { + background: #141A22; + color: #E6EAF0; + border-radius: 12px; + border: 1px solid #1B2330; + padding: 8px 14px; + text-align: left; +} +#SoundEqBtn:hover { background: #1B2330; } +#EqPlaceholder { color: rgba(138,147,166,0.95); } + #SettingsBackBtn { background: #141A22; border-radius: 12px;