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 isinstance(self._ir_enabled, str): self._ir_enabled = self._ir_enabled.lower() in ("true", "1", "yes") 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()) # Запускаем ИК-пульт if self._ir_enabled: 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()