297 lines
12 KiB
Python
297 lines
12 KiB
Python
from PySide6.QtWidgets import (
|
||
QApplication,
|
||
QMainWindow,
|
||
QWidget,
|
||
QVBoxLayout,
|
||
QHBoxLayout,
|
||
QStackedWidget,
|
||
QToolButton,
|
||
QMenu,
|
||
QPushButton,
|
||
QLabel,
|
||
)
|
||
from PySide6.QtCore import QSize, Qt, QTimer, QSettings
|
||
from PySide6.QtGui import QFont
|
||
from themes import THEME_DAY, THEME_NIGHT
|
||
from screens.media import MediaScreen
|
||
from screens.stub import StubScreen
|
||
from screens.settings import SettingsScreen
|
||
from services.bluetooth_service import BluetoothService
|
||
from services.ir_remote_service import IrRemoteService
|
||
from controllers.media_controller import MediaController
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class MainWindowNew(QMainWindow):
|
||
def __init__(self, app: QApplication):
|
||
super().__init__()
|
||
self.app = app
|
||
self._settings = QSettings("car_ui", "ui")
|
||
theme_pref = self._settings.value("display/theme", "night")
|
||
self.is_night = theme_pref != "day"
|
||
|
||
self.setWindowTitle("Car UI (New)")
|
||
self.setMinimumSize(QSize(1024, 600))
|
||
self.showFullScreen()
|
||
|
||
central = QWidget()
|
||
outer = QVBoxLayout(central)
|
||
outer.setContentsMargins(0, 0, 0, 0)
|
||
outer.setSpacing(0)
|
||
|
||
self.topbar = QWidget()
|
||
self.topbar.setObjectName("TopBar")
|
||
self.topbar.setAttribute(Qt.WA_StyledBackground, True)
|
||
self.topbar.setAutoFillBackground(True)
|
||
self.topbar.setMinimumHeight(86)
|
||
top = QHBoxLayout(self.topbar)
|
||
top.setContentsMargins(18, 14, 18, 14)
|
||
top.setSpacing(14)
|
||
|
||
self.menu_button = QToolButton()
|
||
self.menu_button.setObjectName("MenuButton")
|
||
self.menu_button.setText("▼")
|
||
self.menu_button.setPopupMode(QToolButton.InstantPopup)
|
||
self.menu_button.setCursor(Qt.PointingHandCursor)
|
||
self.menu_button.setMinimumSize(64, 48)
|
||
|
||
menu = QMenu(self.menu_button)
|
||
menu.setMinimumWidth(340)
|
||
self.menu_button.setMenu(menu)
|
||
|
||
# Кнопка "Media" - открывает текущий режим
|
||
self.act_media = menu.addAction("Media")
|
||
menu.addSeparator()
|
||
|
||
# Подменю с переключателями Bluetooth/CarPlay
|
||
self.menu_source = menu.addMenu("Режим Media")
|
||
self.act_source_bluetooth = self.menu_source.addAction("Bluetooth")
|
||
self.act_source_carplay = self.menu_source.addAction("CarPlay")
|
||
self.act_source_bluetooth.setCheckable(True)
|
||
self.act_source_carplay.setCheckable(True)
|
||
|
||
self.act_car = menu.addAction("Car")
|
||
self.act_maps = menu.addAction("Maps")
|
||
|
||
self.btn_back = QPushButton("Назад")
|
||
self.btn_back.setObjectName("TopBackBtn")
|
||
self.btn_back.setMinimumSize(120, 48)
|
||
self.btn_back.setVisible(False)
|
||
|
||
self.lbl_source = QLabel("Media")
|
||
self.lbl_source.setFont(QFont("", 20, 600))
|
||
|
||
self.lbl_bt = QLabel("Источник")
|
||
self.lbl_bt.setObjectName("BluetoothStatus")
|
||
self.lbl_bt.setFont(QFont("", 18, 600))
|
||
|
||
self.lbl_time = QLabel("--:--")
|
||
self.lbl_time.setFont(QFont("", 20, 600))
|
||
|
||
self.btn_settings = QPushButton("⚙")
|
||
self.btn_settings.setObjectName("SettingsButton")
|
||
self.btn_settings.setMinimumSize(56, 48)
|
||
self.btn_settings.clicked.connect(self.toggle_settings)
|
||
|
||
top.addWidget(self.menu_button)
|
||
top.addWidget(self.btn_back)
|
||
top.addWidget(self.lbl_source)
|
||
top.addStretch(1)
|
||
top.addWidget(self.lbl_bt)
|
||
top.addWidget(self.lbl_time)
|
||
top.addWidget(self.btn_settings)
|
||
|
||
self.stack = QStackedWidget()
|
||
# Создаём сервис и контроллер
|
||
self._bt_service = BluetoothService(self)
|
||
self._media_controller = MediaController(self._bt_service, self)
|
||
|
||
# ИК-пульт
|
||
self._ir_enabled = self._settings.value("ir_remote/enabled", False)
|
||
if self._ir_enabled:
|
||
self._ir_service = IrRemoteService(self)
|
||
self._connect_ir_remote()
|
||
|
||
self.media_screen = MediaScreen(self._media_controller)
|
||
self.stack.addWidget(self.media_screen) # 0
|
||
self.stack.addWidget(StubScreen("Car")) # 1
|
||
self.stack.addWidget(StubScreen("Maps")) # 2
|
||
self.settings_screen = SettingsScreen()
|
||
self.stack.addWidget(self.settings_screen) # 3
|
||
|
||
self.settings_idx = 3
|
||
self.last_non_settings_idx = 0
|
||
self.settings_title = "Настройки"
|
||
self.settings_show_back = False
|
||
|
||
# Читаем режим источника
|
||
self._source_mode = self._settings.value("media/source_mode", "bluetooth")
|
||
self._media_controller.set_mode(self._source_mode)
|
||
self._update_source_menu()
|
||
|
||
self.act_media.triggered.connect(lambda: self._open_media())
|
||
self.act_source_bluetooth.triggered.connect(lambda: self._set_source_mode("bluetooth"))
|
||
self.act_source_carplay.triggered.connect(lambda: self._set_source_mode("carplay"))
|
||
self.act_car.triggered.connect(lambda: self.go(1))
|
||
self.act_maps.triggered.connect(lambda: self.go(2))
|
||
self.media_screen.source_changed.connect(self.lbl_bt.setText)
|
||
self.settings_screen.view_changed.connect(self._on_settings_view_changed)
|
||
self.settings_screen.theme_changed.connect(self._on_theme_changed)
|
||
self.btn_back.clicked.connect(self._settings_back)
|
||
|
||
outer.addWidget(self.topbar)
|
||
outer.addWidget(self.stack, 1)
|
||
|
||
self.setCentralWidget(central)
|
||
|
||
self._clock_timer = QTimer(self)
|
||
self._clock_timer.timeout.connect(self.update_time)
|
||
self._clock_timer.start(500)
|
||
|
||
self.apply_theme()
|
||
self.go(0)
|
||
# Обновляем заголовок с учётом режима
|
||
mode_text = "Bluetooth" if self._source_mode == "bluetooth" else "CarPlay"
|
||
self.lbl_source.setText(f"Media • {mode_text}")
|
||
self.lbl_bt.setText(self.media_screen.source.text())
|
||
|
||
# Запускаем ИК-пульт
|
||
self._ir_service.start()
|
||
|
||
def _connect_ir_remote(self):
|
||
"""Подключить сигналы ИК-пульта."""
|
||
# Треки
|
||
self._ir_service.play_pause_clicked.connect(self._on_ir_play_pause)
|
||
self._ir_service.next_track_clicked.connect(self._on_ir_next_track)
|
||
self._ir_service.prev_track_clicked.connect(self._on_ir_prev_track)
|
||
self._ir_service.next_track_hold.connect(self._on_ir_next_track_hold)
|
||
self._ir_service.prev_track_hold.connect(self._on_ir_prev_track_hold)
|
||
|
||
# Громкость
|
||
self._ir_service.volume_up_clicked.connect(self._on_ir_volume_up)
|
||
self._ir_service.volume_down_clicked.connect(self._on_ir_volume_down)
|
||
self._ir_service.volume_up_hold.connect(self._on_ir_volume_up_hold)
|
||
self._ir_service.volume_down_hold.connect(self._on_ir_volume_down_hold)
|
||
|
||
def _on_ir_play_pause(self):
|
||
"""Обработка кнопки Play/Pause с ИК-пульта."""
|
||
logger.info("IR: Play/Pause clicked")
|
||
if self._media_controller:
|
||
self._media_controller.toggle_play()
|
||
|
||
def _on_ir_next_track(self):
|
||
"""Обработка кнопки Next с ИК-пульта."""
|
||
logger.info("IR: Next track clicked")
|
||
if self._media_controller:
|
||
self._media_controller.next_track()
|
||
|
||
def _on_ir_prev_track(self):
|
||
"""Обработка кнопки Prev с ИК-пульта."""
|
||
logger.info("IR: Prev track clicked")
|
||
if self._media_controller:
|
||
self._media_controller.previous_track()
|
||
|
||
def _on_ir_next_track_hold(self):
|
||
"""Обработка зажатия Next (перемотка вперед)."""
|
||
# TODO: реализовать перемотку вперед
|
||
pass
|
||
|
||
def _on_ir_prev_track_hold(self):
|
||
"""Обработка зажатия Prev (перемотка назад)."""
|
||
# TODO: реализовать перемотку назад
|
||
pass
|
||
|
||
def _on_ir_volume_up(self):
|
||
"""Обработка кнопки Громкость + с ИК-пульта."""
|
||
# TODO: реализовать увеличение громкости
|
||
pass
|
||
|
||
def _on_ir_volume_down(self):
|
||
"""Обработка кнопки Громкость - с ИК-пульта."""
|
||
# TODO: реализовать уменьшение громкости
|
||
pass
|
||
|
||
def _on_ir_volume_up_hold(self):
|
||
"""Обработка зажатия Громкость + (быстрое увеличение)."""
|
||
# TODO: реализовать быстрое увеличение громкости
|
||
pass
|
||
|
||
def _on_ir_volume_down_hold(self):
|
||
"""Обработка зажатия Громкость - (быстрое уменьшение)."""
|
||
# TODO: реализовать быстрое уменьшение громкости
|
||
pass
|
||
|
||
def apply_theme(self):
|
||
self.app.setStyleSheet(THEME_NIGHT if self.is_night else THEME_DAY)
|
||
|
||
def _on_theme_changed(self, theme_key: str):
|
||
self.is_night = theme_key != "day"
|
||
self.apply_theme()
|
||
|
||
def _sync_topbar(self, idx: int):
|
||
is_settings = idx == self.settings_idx
|
||
self.menu_button.setVisible(not is_settings)
|
||
self.btn_back.setVisible(is_settings and self.settings_show_back)
|
||
self.lbl_bt.setVisible(not is_settings)
|
||
self.lbl_time.setVisible(not is_settings)
|
||
self.btn_settings.setText("✕" if is_settings else "⚙")
|
||
if is_settings:
|
||
self.lbl_source.setText(self.settings_title)
|
||
|
||
def toggle_settings(self):
|
||
if self.stack.currentIndex() == self.settings_idx:
|
||
self.go(self.last_non_settings_idx)
|
||
else:
|
||
self.settings_screen.show_list()
|
||
self.go(self.settings_idx)
|
||
|
||
def update_time(self):
|
||
from datetime import datetime
|
||
self.lbl_time.setText(datetime.now().strftime("%H:%M"))
|
||
|
||
def go(self, idx: int):
|
||
if idx != self.settings_idx:
|
||
self.last_non_settings_idx = idx
|
||
self.stack.setCurrentIndex(idx)
|
||
if idx == 0:
|
||
self.lbl_source.setText("Media")
|
||
elif idx == 1:
|
||
self.lbl_source.setText("Car")
|
||
elif idx == 2:
|
||
self.lbl_source.setText("Maps")
|
||
self._sync_topbar(idx)
|
||
|
||
def _open_media(self):
|
||
"""Открыть экран Media с текущим режимом."""
|
||
self.go(0)
|
||
mode_text = "Bluetooth" if self._source_mode == "bluetooth" else "CarPlay"
|
||
self.lbl_source.setText(f"Media • {mode_text}")
|
||
self.lbl_bt.setText(self.media_screen.source.text())
|
||
|
||
def _set_source_mode(self, mode: str):
|
||
"""Установить режим источника (Bluetooth/CarPlay)."""
|
||
self._settings.setValue("media/source_mode", mode)
|
||
self._source_mode = mode
|
||
self._media_controller.set_mode(mode)
|
||
self._update_source_menu()
|
||
# Обновляем заголовок
|
||
if self.stack.currentIndex() == 0:
|
||
mode_text = "Bluetooth" if mode == "bluetooth" else "CarPlay"
|
||
self.lbl_source.setText(f"Media • {mode_text}")
|
||
|
||
def _update_source_menu(self):
|
||
"""Обновить состояние переключателей в меню."""
|
||
self.act_source_bluetooth.setChecked(self._source_mode == "bluetooth")
|
||
self.act_source_carplay.setChecked(self._source_mode == "carplay")
|
||
|
||
def _on_settings_view_changed(self, title: str, show_back: bool):
|
||
self.settings_title = title
|
||
self.settings_show_back = show_back
|
||
if self.stack.currentIndex() == self.settings_idx:
|
||
self._sync_topbar(self.settings_idx)
|
||
|
||
def _settings_back(self):
|
||
self.settings_screen.show_list()
|