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.