food-records/app/ui/theme.py
Claude c8b9f5048b Учёт закупки и перепродажи булок
Десктопное приложение на PySide6. Вся база — один JSON-документ,
зашифрованный AES-256-GCM под паролем (ключ через scrypt), лежит
в data/vault.fmdb и раз в час уезжает в этот репозиторий.

Основное:
- партии с дедлайном возврата себестоимости пекарне, FIFO-разнос продаж
  по партиям и прогресс покрытия к сроку;
- типы выбытия: розница, другу по себестоимости, съел сам, подарок,
  списание — съеденное вычитается из прибыли, за него платить всё равно;
- долги контрагентов с частичными оплатами;
- номенклатура с историей изменения цен;
- журнал изменений внутри базы: git хранит непрозрачные снимки,
  поэтому настоящая история ведётся здесь.

Синхронизация коммитит только путь базы и схлопывает часовые пуши
в один коммит на день через amend + force-with-lease.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 17:29:53 +03:00

182 lines
5.2 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.

"""Тёмная тема.
Палитра задаётся через QPalette, а не только через QSS: иначе системные
диалоги и всплывающие подсказки остались бы светлыми и резали глаза.
"""
from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPalette
BG = "#1c1f24"
BG_ALT = "#23272e"
SURFACE = "#2b3038"
BORDER = "#3a4048"
TEXT = "#e6e8ea"
TEXT_DIM = "#98a0aa"
ACCENT = "#4c9aff"
OK = "#4caf7d"
WARN = "#d9a441"
DANGER = "#e0605e"
MUTED = "#6c757d"
STATUS_COLORS = {
"settled": OK,
"open": ACCENT,
"due_soon": WARN,
"overdue": DANGER,
}
def apply(app) -> None:
app.setStyle("Fusion")
palette = QPalette()
palette.setColor(QPalette.Window, QColor(BG))
palette.setColor(QPalette.WindowText, QColor(TEXT))
palette.setColor(QPalette.Base, QColor(BG_ALT))
palette.setColor(QPalette.AlternateBase, QColor(SURFACE))
palette.setColor(QPalette.Text, QColor(TEXT))
palette.setColor(QPalette.Button, QColor(SURFACE))
palette.setColor(QPalette.ButtonText, QColor(TEXT))
palette.setColor(QPalette.ToolTipBase, QColor(SURFACE))
palette.setColor(QPalette.ToolTipText, QColor(TEXT))
palette.setColor(QPalette.Highlight, QColor(ACCENT))
palette.setColor(QPalette.HighlightedText, QColor("#ffffff"))
palette.setColor(QPalette.PlaceholderText, QColor(TEXT_DIM))
palette.setColor(QPalette.Link, QColor(ACCENT))
palette.setColor(QPalette.Disabled, QPalette.Text, QColor(MUTED))
palette.setColor(QPalette.Disabled, QPalette.ButtonText, QColor(MUTED))
palette.setColor(QPalette.Disabled, QPalette.WindowText, QColor(MUTED))
app.setPalette(palette)
app.setStyleSheet(QSS)
QSS = f"""
QWidget {{
font-size: 13px;
}}
QLabel[role="h1"] {{ font-size: 21px; font-weight: 600; }}
QLabel[role="h2"] {{ font-size: 15px; font-weight: 600; }}
QLabel[role="dim"] {{ color: {TEXT_DIM}; }}
QLabel[role="metric"] {{ font-size: 24px; font-weight: 600; }}
QFrame[role="card"] {{
background: {SURFACE};
border: 1px solid {BORDER};
border-radius: 8px;
}}
QFrame[role="banner"] {{
background: #3a2f1c;
border: 1px solid {WARN};
border-radius: 6px;
}}
QListWidget#sidebar {{
background: {BG_ALT};
border: none;
border-right: 1px solid {BORDER};
outline: none;
padding-top: 8px;
}}
QListWidget#sidebar::item {{
padding: 10px 16px;
margin: 1px 6px;
border-radius: 6px;
color: {TEXT_DIM};
}}
QListWidget#sidebar::item:selected {{
background: {SURFACE};
color: {TEXT};
font-weight: 600;
}}
QListWidget#sidebar::item:hover {{ color: {TEXT}; }}
QPushButton {{
background: {SURFACE};
border: 1px solid {BORDER};
border-radius: 6px;
padding: 6px 14px;
}}
QPushButton:hover {{ border-color: {ACCENT}; }}
QPushButton:disabled {{ color: {MUTED}; border-color: {BORDER}; }}
QPushButton[role="primary"] {{
background: {ACCENT};
border-color: {ACCENT};
color: #ffffff;
font-weight: 600;
}}
QPushButton[role="primary"]:hover {{ background: #5aa6ff; }}
QPushButton[role="danger"]:hover {{ border-color: {DANGER}; color: {DANGER}; }}
QLineEdit, QDateEdit, QComboBox, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit {{
background: {BG_ALT};
border: 1px solid {BORDER};
border-radius: 6px;
padding: 5px 8px;
selection-background-color: {ACCENT};
}}
QLineEdit:focus, QDateEdit:focus, QComboBox:focus,
QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus {{
border-color: {ACCENT};
}}
QComboBox::drop-down {{ border: none; width: 18px; }}
QComboBox QAbstractItemView {{
background: {BG_ALT};
border: 1px solid {BORDER};
selection-background-color: {ACCENT};
}}
QTableWidget, QTreeWidget {{
background: {BG_ALT};
alternate-background-color: {SURFACE};
border: 1px solid {BORDER};
border-radius: 6px;
gridline-color: {BORDER};
selection-background-color: {ACCENT};
}}
QHeaderView::section {{
background: {SURFACE};
color: {TEXT_DIM};
border: none;
border-bottom: 1px solid {BORDER};
padding: 7px 8px;
font-weight: 600;
}}
QTableWidget::item, QTreeWidget::item {{ padding: 4px 6px; }}
QProgressBar {{
background: {BG};
border: 1px solid {BORDER};
border-radius: 6px;
height: 14px;
text-align: center;
color: {TEXT};
}}
QProgressBar::chunk {{ border-radius: 5px; }}
QStatusBar {{ border-top: 1px solid {BORDER}; }}
QStatusBar::item {{ border: none; }}
QScrollBar:vertical {{ background: transparent; width: 10px; margin: 0; }}
QScrollBar::handle:vertical {{ background: {BORDER}; border-radius: 5px; min-height: 30px; }}
QScrollBar::handle:vertical:hover {{ background: {MUTED}; }}
QScrollBar::add-line, QScrollBar::sub-line {{ height: 0; width: 0; }}
QScrollBar:horizontal {{ background: transparent; height: 10px; }}
QScrollBar::handle:horizontal {{ background: {BORDER}; border-radius: 5px; min-width: 30px; }}
QTabWidget::pane {{ border: 1px solid {BORDER}; border-radius: 6px; }}
QTabBar::tab {{
background: transparent;
padding: 7px 14px;
color: {TEXT_DIM};
border-bottom: 2px solid transparent;
}}
QTabBar::tab:selected {{ color: {TEXT}; border-bottom-color: {ACCENT}; }}
"""