Přeskočit obsah

Launcher2 reference (Python customtkinter)

Aktivní vývoj. Pro implementační stav, fáze a řešené chyby viz root-level launcher2.md.

Verze a stack

Python:          3.13.13 (Windows)
customtkinter:   5.2.2
requests:        2.33.1
Pillow:          (tray ikony)
pystray:         (system tray)
watchdog:        (file watcher)
pyinstaller:     6.20.0 (build)

Adresářová struktura desktop/launcher2/

main.py          ← LoginWindow (startovací bod)
main_window.py   ← MainWindow (top-level okno po přihlášení)
screens.py       ← všechny obrazovky a dialogy (~6500 řádků)
api.py           ← HTTP volání na backend (requests, sync)
sync_agent.py    ← SyncAgent (S3 sync, peer shares, file watcher)
installer.py     ← stahování a instalace aplikací
theme.py         ← barevná témata (dark/light/high_contrast)
settings.py      ← SettingsManager (JSON + Registry autostart)
db.py            ← LocalDB (SQLite pro offline session + manifest)
tray.py          ← SystemTray (pystray)
ipc.py           ← IPC server (named pipe, pro AVAX SDK)
updater.py       ← auto-update launcheru
avax-launcher2.spec ← PyInstaller build spec

Klíčové vzory

Přidání nové obrazovky

# 1. screens.py — nová třída
class MyScreen(ctk.CTkFrame):
    def __init__(self, parent, session: dict):
        super().__init__(parent, fg_color=colors.BG1)
        self._session = session
        self._token   = session.get("access_token", "")
        self._build()

# 2. main_window.py — registrace
if name == "my_screen": return MyScreen(self._content, self.session)

# 3. main_window.py — menu položka
_nb("Moje sekce", lambda: self.show_screen("my_screen"))

Async načítání dat

def _load_async(self):
    def _work():
        try:
            data = api.get_something(self._token)
        except Exception:
            data = []
        self.after(0, lambda: self._on_loaded(data))
    threading.Thread(target=_work, daemon=True).start()

Barvy (theme.py)

from theme import colors
colors.BG1        # hlavní pozadí
colors.BG2        # karta/panel
colors.BG3        # hover/border
colors.ACCENT     # primární barva (modrá ve dark)
colors.TEXT       # hlavní text
colors.TEXT_DIM   # sekundární text
colors.ERROR      # chyba (červená)
colors.SUCCESS    # úspěch (zelená, pokud existuje)

Session dict (po přihlášení)

{
    "access_token":   str,   # JWT Bearer token
    "refresh_token":  str,
    "offline_jwt":    str,   # RS256 JWT pro offline mode
    "id":             str,   # UUID uživatele
    "email":          str,
    "name":           str,
    "company_id":     str,   # UUID aktivní firmy
    "company_name":   str,
    "company_ico":    str,   # IČO firmy (pro AVAX tab: 01695541)
    "system_role":    str,   # "super_admin" | "company_admin" | "user"
    "tfa_enabled":    bool,
}

Přístup k záložce AVAX

# Podmínka: zaměstnanec avaxis (IČO 01695541) + dostatečná role
_AVAX_ICO = "01695541"
_is_avax_staff = (
    session.get("system_role") in ("super_admin", "company_admin")
    and session.get("company_ico", "") == _AVAX_ICO
)

SyncAgent (sync_agent.py)

Princip: - Běží v background threadu po celou dobu běhu launcheru - Každých 30 minut (nebo v nastavený čas) spustí _do_sync_all() - Na startu: _startup_check() → detekuje nové soubory na serveru → dialog - Peer shares: synchronizuje přijaté sdílené adresáře (presigned URL, bez boto3)

Tokenový systém:

self._primary_token    # primární JWT (login token)
self._company_tokens   # {company_id: token} — po switch_company
self._token            # = _primary_token (backwards compat)

# Token pro konkrétní sync dir:
def _token_for_dir(self, d: dict) -> str:
    cid = str(d.get("company_id", ""))
    return self._company_tokens.get(cid) or self._primary_token

Peer share manifest:

%LOCALAPPDATA%\Avaxis\peer\{share_id}\manifest.json
Format: { "rel/path/file.txt": { "mtime": float, "s3_modified": float } }

Konvence kódu (launcher2)

  • Python: snake_case, žádné type hints na triviální místa
  • Třídy screen: class XyzScreen(ctk.CTkFrame) + __init__(parent, session)
  • Třídy dialog: class _XyzDialog(ctk.CTkToplevel) (underscore prefix = interní)
  • API funkce: synchronní (requests), házejí ValueError(detail) při chybě
  • Komentáře: minimálně, jen kde WHY není zřejmé z kódu

AVAX App Bundle Format

Viz docs/dev/app-bundle-format.md.