199 lines
5.4 KiB
Python
199 lines
5.4 KiB
Python
from pathlib import Path
|
||
import subprocess
|
||
|
||
from PySide6.QtCore import Qt, QSettings
|
||
from PySide6.QtGui import QFont
|
||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
|
||
|
||
import build_info
|
||
from ui.confirm_dialog import ConfirmDialog
|
||
|
||
|
||
def build_dev_screen(on_exit) -> QWidget:
|
||
screen = QWidget()
|
||
layout = QVBoxLayout(screen)
|
||
layout.setContentsMargins(0, 0, 0, 0)
|
||
layout.setSpacing(12)
|
||
|
||
layout.addWidget(_build_persist_toggle())
|
||
layout.addWidget(_build_sound_toggles())
|
||
|
||
hdr = QHBoxLayout()
|
||
hdr.setContentsMargins(0, 0, 0, 0)
|
||
hdr.setSpacing(12)
|
||
|
||
exit_btn = QPushButton("Переход к рабочему столу")
|
||
exit_btn.setObjectName("DevExitBtn")
|
||
exit_btn.setMinimumHeight(72)
|
||
exit_btn.clicked.connect(lambda: _confirm_exit(on_exit))
|
||
|
||
reboot_btn = QPushButton("Выполнить перезагрузку")
|
||
reboot_btn.setObjectName("DevRebootBtn")
|
||
reboot_btn.setMinimumHeight(72)
|
||
reboot_btn.clicked.connect(_confirm_reboot)
|
||
|
||
reset_btn = QPushButton("Сброс до заводских")
|
||
reset_btn.setObjectName("DevResetBtn")
|
||
reset_btn.setMinimumHeight(72)
|
||
reset_btn.clicked.connect(_confirm_factory_reset)
|
||
|
||
layout.addLayout(hdr)
|
||
layout.addWidget(exit_btn)
|
||
layout.addWidget(reboot_btn)
|
||
layout.addWidget(reset_btn)
|
||
layout.addStretch(1)
|
||
return screen
|
||
|
||
|
||
def _build_persist_toggle() -> QWidget:
|
||
row = QWidget()
|
||
layout = QHBoxLayout(row)
|
||
layout.setContentsMargins(12, 6, 12, 6)
|
||
layout.setSpacing(12)
|
||
|
||
lbl = QLabel("Показывать после перезагрузки")
|
||
lbl.setFont(QFont("", 14, 600))
|
||
|
||
btn = QPushButton("Выкл")
|
||
btn.setObjectName("SoundToggle")
|
||
btn.setCheckable(True)
|
||
btn.setChecked(_dev_flag_path().exists())
|
||
btn.setMinimumHeight(40)
|
||
btn.setMinimumWidth(110)
|
||
btn.setFont(QFont("", 12, 700))
|
||
|
||
def _sync_text(is_checked: bool):
|
||
btn.setText("Вкл" if is_checked else "Выкл")
|
||
|
||
def _persist_flag(is_checked: bool):
|
||
flag_path = _dev_flag_path()
|
||
if is_checked:
|
||
flag_path.touch(exist_ok=True)
|
||
else:
|
||
if flag_path.exists():
|
||
flag_path.unlink()
|
||
|
||
btn.toggled.connect(_sync_text)
|
||
btn.toggled.connect(_persist_flag)
|
||
_sync_text(btn.isChecked())
|
||
|
||
layout.addWidget(lbl)
|
||
layout.addStretch(1)
|
||
layout.addWidget(btn)
|
||
return row
|
||
|
||
|
||
def _build_sound_toggles() -> QWidget:
|
||
settings = QSettings("car_ui", "ui")
|
||
|
||
container = QWidget()
|
||
layout = QVBoxLayout(container)
|
||
layout.setContentsMargins(12, 6, 12, 6)
|
||
layout.setSpacing(8)
|
||
|
||
layout.addWidget(
|
||
_toggle_row(
|
||
"Премут",
|
||
settings,
|
||
"sound/premute_enabled",
|
||
False,
|
||
)
|
||
)
|
||
layout.addWidget(
|
||
_toggle_row(
|
||
"Ducking",
|
||
settings,
|
||
"sound/ducking_enabled",
|
||
False,
|
||
)
|
||
)
|
||
return container
|
||
|
||
|
||
def _toggle_row(
|
||
label: str,
|
||
settings: QSettings,
|
||
key: str,
|
||
default: bool,
|
||
) -> QWidget:
|
||
row = QWidget()
|
||
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(_read_bool_setting(settings, key, default))
|
||
btn.setMinimumHeight(40)
|
||
btn.setMinimumWidth(110)
|
||
btn.setFont(QFont("", 12, 700))
|
||
|
||
def _sync_text(is_checked: bool):
|
||
btn.setText("Вкл" if is_checked else "Выкл")
|
||
|
||
def _persist_flag(is_checked: bool):
|
||
settings.setValue(key, is_checked)
|
||
|
||
btn.toggled.connect(_sync_text)
|
||
btn.toggled.connect(_persist_flag)
|
||
_sync_text(btn.isChecked())
|
||
|
||
layout.addWidget(lbl)
|
||
layout.addStretch(1)
|
||
layout.addWidget(btn)
|
||
return row
|
||
|
||
|
||
def _read_bool_setting(settings: QSettings, key: str, default: bool) -> bool:
|
||
raw = settings.value(key, default)
|
||
if isinstance(raw, bool):
|
||
return raw
|
||
if isinstance(raw, str):
|
||
return raw.strip().lower() in ("1", "true", "yes", "on")
|
||
try:
|
||
return bool(int(raw))
|
||
except (TypeError, ValueError):
|
||
return default
|
||
|
||
|
||
def _dev_flag_path() -> Path:
|
||
return Path(build_info.__file__).resolve().parent / "dev_mode_enable"
|
||
|
||
|
||
def _confirm_exit(on_exit):
|
||
dialog = ConfirmDialog(
|
||
"Подтверждение",
|
||
"Закрыть приложение и перейти к рабочему столу?",
|
||
"Выйти",
|
||
)
|
||
if dialog.exec() == ConfirmDialog.Accepted:
|
||
on_exit()
|
||
|
||
|
||
def _confirm_reboot():
|
||
dialog = ConfirmDialog(
|
||
"Подтверждение",
|
||
"Выполнить перезагрузку устройства?",
|
||
"Перезагрузить",
|
||
ok_object_name="ConfirmOkDanger",
|
||
)
|
||
if dialog.exec() == ConfirmDialog.Accepted:
|
||
subprocess.run(["sudo", "reboot"], check=False)
|
||
|
||
|
||
def _confirm_factory_reset():
|
||
dialog = ConfirmDialog(
|
||
"Подтверждение",
|
||
"Сбросить настройки до заводских? Приложение будет закрыто.",
|
||
"Сбросить",
|
||
ok_object_name="ConfirmOkDanger",
|
||
)
|
||
if dialog.exec() == ConfirmDialog.Accepted:
|
||
reset_marker = Path(build_info.__file__).resolve().parent / "reset"
|
||
reset_marker.touch(exist_ok=True)
|
||
subprocess.run(["sudo", "reboot"], check=False)
|