import sys from PySide6.QtCore import Qt, QTimer, QSize from PySide6.QtGui import QFont from PySide6.QtWidgets import ( QApplication, QMainWindow, QWidget, QLabel, QPushButton, QHBoxLayout, QVBoxLayout, QGridLayout, QStackedWidget, QSizePolicy ) # ---------- Themes (QSS) ---------- THEME_NIGHT = """ /* Global */ QWidget { background: #0B0E11; color: #E6EAF0; font-family: Inter, Roboto, Sans-Serif; } QLabel { color: #E6EAF0; } #TopBar, #BottomBar { background: #0F1318; border: 0px; } #Divider { background: #1B2330; } QPushButton { border: 0px; } QPushButton#IconChip { background: #161A1F; padding: 10px 14px; border-radius: 14px; color: #E6EAF0; font-size: 16px; } QPushButton#IconChip:pressed { background: #1B2330; } QPushButton#NavBtn { background: transparent; padding: 10px 8px; border-radius: 14px; color: #8A93A6; font-size: 14px; } QPushButton#NavBtn[active="true"] { color: #E6EAF0; } QPushButton#NavBtn[active="true"] #NavUnderline { background: #3A86FF; } QWidget#Card { background: #161A1F; border-radius: 20px; } QLabel#CardTitle { color: #8A93A6; font-size: 16px; } QLabel#CardValue { color: #E6EAF0; font-size: 34px; font-weight: 600; } QLabel#CardSub { color: #8A93A6; font-size: 14px; } QPushButton#PrimaryBtn { background: #3A86FF; color: #0B0E11; border-radius: 16px; padding: 12px 16px; font-size: 16px; font-weight: 600; } QPushButton#PrimaryBtn:pressed { background: #2F6BE0; } """ THEME_DAY = """ /* Global */ QWidget { background: #F4F6F8; color: #111827; font-family: Inter, Roboto, Sans-Serif; } QLabel { color: #111827; } #TopBar, #BottomBar { background: #FFFFFF; border: 0px; } #Divider { background: #E5E7EB; } QPushButton { border: 0px; } QPushButton#IconChip { background: #F1F5F9; padding: 10px 14px; border-radius: 14px; color: #111827; font-size: 16px; } QPushButton#IconChip:pressed { background: #E2E8F0; } QPushButton#NavBtn { background: transparent; padding: 10px 8px; border-radius: 14px; color: #6B7280; font-size: 14px; } QPushButton#NavBtn[active="true"] { color: #111827; } QPushButton#NavBtn[active="true"] #NavUnderline { background: #2563EB; } QWidget#Card { background: #FFFFFF; border-radius: 20px; } QLabel#CardTitle { color: #6B7280; font-size: 16px; } QLabel#CardValue { color: #111827; font-size: 34px; font-weight: 600; } QLabel#CardSub { color: #6B7280; font-size: 14px; } QPushButton#PrimaryBtn { background: #2563EB; color: #FFFFFF; border-radius: 16px; padding: 12px 16px; font-size: 16px; font-weight: 600; } QPushButton#PrimaryBtn:pressed { background: #1D4ED8; } """ # ---------- Small UI helpers ---------- def divider(height=1): w = QWidget() w.setObjectName("Divider") w.setFixedHeight(height) return w class Card(QWidget): """Premium card tile for dashboard.""" def __init__(self, title: str, value: str, sub: str, icon_text: str = "◻"): super().__init__() self.setObjectName("Card") self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) root = QVBoxLayout(self) root.setContentsMargins(18, 16, 18, 16) root.setSpacing(10) top = QHBoxLayout() top.setSpacing(10) lbl_title = QLabel(title) lbl_title.setObjectName("CardTitle") icon = QLabel(icon_text) icon.setAlignment(Qt.AlignCenter) icon.setFixedSize(44, 44) icon.setStyleSheet("background: rgba(58,134,255,0.12); border-radius: 14px;") top.addWidget(lbl_title, 1, Qt.AlignLeft | Qt.AlignVCenter) top.addWidget(icon, 0, Qt.AlignRight | Qt.AlignVCenter) lbl_value = QLabel(value) lbl_value.setObjectName("CardValue") lbl_sub = QLabel(sub) lbl_sub.setObjectName("CardSub") root.addLayout(top) root.addStretch(1) root.addWidget(lbl_value) root.addWidget(lbl_sub) # clickable feel self.setCursor(Qt.PointingHandCursor) def mousePressEvent(self, event): # заглушка клика по карточке print(f"[Card] Click: {self.objectName()}") super().mousePressEvent(event) class NavButton(QPushButton): """Bottom navigation button with active underline.""" def __init__(self, text: str): super().__init__() self.setObjectName("NavBtn") self.setCheckable(False) self.setCursor(Qt.PointingHandCursor) self._wrap = QWidget() wrap_layout = QVBoxLayout(self._wrap) wrap_layout.setContentsMargins(0, 0, 0, 0) wrap_layout.setSpacing(6) self._label = QLabel(text) self._label.setAlignment(Qt.AlignCenter) self._underline = QWidget() self._underline.setObjectName("NavUnderline") self._underline.setFixedHeight(3) self._underline.setFixedWidth(36) self._underline.setStyleSheet("background: transparent; border-radius: 2px;") wrap_layout.addStretch(1) wrap_layout.addWidget(self._label) wrap_layout.addWidget(self._underline, 0, Qt.AlignHCenter) wrap_layout.addStretch(1) btn_layout = QVBoxLayout(self) btn_layout.setContentsMargins(0, 0, 0, 0) btn_layout.addWidget(self._wrap) def set_active(self, active: bool): self.setProperty("active", "true" if active else "false") # important to re-polish to apply dynamic property styling: self.style().unpolish(self) self.style().polish(self) # ---------- Screens ---------- class HomeScreen(QWidget): def __init__(self): super().__init__() root = QVBoxLayout(self) root.setContentsMargins(18, 16, 18, 16) root.setSpacing(16) hdr = QHBoxLayout() title = QLabel("Dashboard") title.setFont(QFont("", 22, 600)) hint = QLabel("Premium UI • 1024×600") hint.setStyleSheet("color: rgba(138,147,166,0.9);") hdr.addWidget(title, 0, Qt.AlignLeft | Qt.AlignVCenter) hdr.addStretch(1) hdr.addWidget(hint, 0, Qt.AlignRight | Qt.AlignVCenter) grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) grid.setHorizontalSpacing(16) grid.setVerticalSpacing(16) # 2×2 cards grid.addWidget(Card("MEDIA", "No track", "Tap to open", "♫"), 0, 0) grid.addWidget(Card("MAP / TRACK", "Idle", "Last trip: —", "⌁"), 0, 1) grid.addWidget(Card("CAR STATUS", "OK", "OBD: not connected", "⛭"), 1, 0) grid.addWidget(Card("CLOUD / YOUBLE", "Offline", "No sync yet", "☁"), 1, 1) root.addLayout(hdr) root.addLayout(grid, 1) class StubScreen(QWidget): def __init__(self, title_text: str): super().__init__() root = QVBoxLayout(self) root.setContentsMargins(18, 16, 18, 16) root.setSpacing(12) title = QLabel(title_text) title.setFont(QFont("", 22, 600)) sub = QLabel("Заглушка экрана. Тут будет функционал.") sub.setStyleSheet("color: rgba(138,147,166,0.9);") root.addWidget(title) root.addWidget(sub) root.addStretch(1) # ---------- Main Window ---------- class MainWindow(QMainWindow): def __init__(self, app: QApplication): super().__init__() self.app = app self.is_night = True self.setWindowTitle("Car UI (Premium)") self.setMinimumSize(QSize(1024, 600)) self.showFullScreen() # Root layout container central = QWidget() outer = QVBoxLayout(central) outer.setContentsMargins(0, 0, 0, 0) outer.setSpacing(0) # Top bar self.topbar = QWidget() self.topbar.setObjectName("TopBar") top = QHBoxLayout(self.topbar) top.setContentsMargins(16, 10, 16, 10) top.setSpacing(10) self.lbl_time = QLabel("--:--") self.lbl_time.setFont(QFont("", 16, 600)) chip_bt = QPushButton("BT") chip_bt.setObjectName("IconChip") chip_wifi = QPushButton("Wi-Fi") chip_wifi.setObjectName("IconChip") chip_lte = QPushButton("LTE") chip_lte.setObjectName("IconChip") self.lbl_vol = QLabel("VOL 45%") self.lbl_vol.setStyleSheet("color: rgba(138,147,166,0.95); font-size: 14px;") self.btn_theme = QPushButton("🌙") self.btn_theme.setObjectName("IconChip") self.btn_theme.clicked.connect(self.toggle_theme) top.addWidget(self.lbl_time) top.addSpacing(10) top.addWidget(chip_bt) top.addWidget(chip_wifi) top.addWidget(chip_lte) top.addStretch(1) top.addWidget(self.lbl_vol) top.addWidget(self.btn_theme) # Content stack self.stack = QStackedWidget() self.stack.addWidget(HomeScreen()) # 0 self.stack.addWidget(StubScreen("Media")) # 1 self.stack.addWidget(StubScreen("Car")) # 2 self.stack.addWidget(StubScreen("Maps")) # 3 self.stack.addWidget(StubScreen("Settings")) # 4 # Bottom bar (nav) self.bottombar = QWidget() self.bottombar.setObjectName("BottomBar") bottom = QHBoxLayout(self.bottombar) bottom.setContentsMargins(16, 8, 16, 8) bottom.setSpacing(10) self.nav_buttons = [ NavButton("HOME"), NavButton("MEDIA"), NavButton("CAR"), NavButton("MAPS"), NavButton("SETTINGS"), ] for i, btn in enumerate(self.nav_buttons): btn.clicked.connect(lambda checked=False, idx=i: self.go(idx)) bottom.addWidget(btn, 1) outer.addWidget(self.topbar) outer.addWidget(divider(1)) outer.addWidget(self.stack, 1) outer.addWidget(divider(1)) outer.addWidget(self.bottombar) self.setCentralWidget(central) # timers self._clock_timer = QTimer(self) self._clock_timer.timeout.connect(self.update_time) self._clock_timer.start(500) self.apply_theme() self.go(0) def update_time(self): from datetime import datetime self.lbl_time.setText(datetime.now().strftime("%H:%M")) def apply_theme(self): self.app.setStyleSheet(THEME_NIGHT if self.is_night else THEME_DAY) self.btn_theme.setText("🌙" if self.is_night else "☀") def toggle_theme(self): self.is_night = not self.is_night self.apply_theme() def go(self, idx: int): self.stack.setCurrentIndex(idx) for i, b in enumerate(self.nav_buttons): b.set_active(i == idx) def main(): app = QApplication(sys.argv) win = MainWindow(app) win.show() sys.exit(app.exec()) if __name__ == "__main__": main()