-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
95 lines (77 loc) · 3.81 KB
/
main.py
File metadata and controls
95 lines (77 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""
main.py — Ponto de entrada do JoyBind.
Inicializa pygame, configura o CustomTkinter e lança a janela principal.
Execute com:
python main.py
"""
import sys
import ctypes
import tempfile
from pathlib import Path
import pygame
import customtkinter as ctk
from PIL import Image
import i18n
from core.presets import load_settings
from gui.app import App
# Caminho base funciona tanto em dev quanto em executável PyInstaller
_BASE = Path(sys._MEIPASS) if getattr(sys, "frozen", False) else Path(__file__).resolve().parent
_LOGO_PATH = _BASE / "img" / "logo.png"
# Windows: identifica o processo como app própria, não como python.exe.
# Precisa ser chamado ANTES de qualquer janela ser criada.
if sys.platform == "win32":
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("mateus.joybind.1")
def _apply_taskbar_icon(root: ctk.CTk, ico_path: str) -> None:
"""Envia WM_SETICON para o HWND real da janela (barra de tarefas)."""
try:
# LoadImage com LR_LOADFROMFILE retorna um HICON
hicon = ctypes.windll.user32.LoadImageW(
None, ico_path,
1, # IMAGE_ICON
0, 0, # cx, cy = 0 → usa tamanho default do sistema
0x00000010 | 0x00000040, # LR_LOADFROMFILE | LR_DEFAULTSIZE
)
if not hicon:
return
# O HWND da barra de tarefas é o pai do frame interno do Tk
hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
ctypes.windll.user32.SendMessageW(hwnd, 0x0080, 1, hicon) # WM_SETICON, ICON_BIG
ctypes.windll.user32.SendMessageW(hwnd, 0x0080, 0, hicon) # WM_SETICON, ICON_SMALL
except Exception as e:
print(i18n.t("err_icon_taskbar", e=e))
def main() -> None:
# ── Idioma ─────────────────────────────────────────────────────
_settings = load_settings()
i18n.set_lang(_settings.get("language", "en"))
# ── Aparência do CustomTkinter ─────────────────────────────────
ctk.set_appearance_mode("dark") # "dark" | "light" | "system"
ctk.set_default_color_theme("blue") # "blue" | "green" | "dark-blue"
# ── Inicialização do pygame ────────────────────────────────────
# Precisamos inicializar antes de criar a App pois o ControllerListener
# usa pygame.event.pump() no loop de polling.
pygame.init()
# ── Janela principal ───────────────────────────────────────────
root = ctk.CTk()
# Ícone da janela: gera .ico no temp e aplica no titlebar + taskbar
if _LOGO_PATH.exists():
try:
pil_img = Image.open(_LOGO_PATH).convert("RGBA")
ico_path = Path(tempfile.gettempdir()) / "joybind_icon.ico"
_sizes = [(256,256),(128,128),(96,96),(72,72),(64,64),
(48,48),(40,40),(32,32),(24,24),(20,20),(16,16)]
_frames = [pil_img.resize(s, Image.LANCZOS) for s in _sizes]
_frames[0].save(str(ico_path), format="ICO", append_images=_frames[1:])
ico_str = str(ico_path)
root.iconbitmap(ico_str) # titlebar
root.after(0, lambda: _apply_taskbar_icon(root, ico_str)) # taskbar
except Exception as e:
print(i18n.t("err_icon", e=e))
app = App(root)
def on_close() -> None:
"""Encerramento gracioso: garante que a thread do controller seja parada."""
app.shutdown()
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_close)
root.mainloop()
if __name__ == "__main__":
main()