car_ui/ui/main_window_new.py
2026-04-01 05:40:57 +03:00

297 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()