Files
PyQt6_LaTaniere/src/ui/main_window.py

263 lines
10 KiB
Python

import webbrowser
from sys import platform
from os import environ
from PySide6 import QtGui
from PySide6.QtCore import Qt
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QMainWindow, QSizePolicy
from PySide6.QtCore import QThread, Signal
from config.config_manager import ConfigManager
from config.constants import PlayerServerInfo, Urls
from tools.http_client import ApiError
from ui.error_dialog import show_qt_error
from ui.hazard_stripes import HazardButton
from controllers.audio_controller import AudioController
from controllers.glow_animator import GlowAnimator
from controllers.window_dragger import WindowDragger
from discord import discord_oauth
from fivemserver.whitelistmanager import WhiteList
from fivemserver.fivemlauncher import FiveMLauncher
from fivemserver.queuemanager import QueueManager
from fivemserver.get_server_token import GetServerTokenForDiscord
from fake_patch_notes import patch_note
# For Linux Wayland to authorize moving window
if platform.startswith('linux'):
environ["QT_QPA_PLATFORM"] = "xcb"
class MainWindow(QMainWindow):
#update = Signal(str) # Reçoit les callbacks de QueueManager
def __init__(self, bundle_dir: str, config_manager: ConfigManager):
super().__init__()
self.config = config_manager
self.stored_user_id = self.config.get_discord_user()
self.queue_thread = None
# UI
self.ui = QUiLoader().load(f"{bundle_dir}/ui/mainwindow_vertical_pager.ui", self)
self.setCentralWidget(self.ui.centralWidget())
self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
# Par défaut on affiche la page normal pour la connexion au serveur
self.ui.stackedWidget.setCurrentIndex(0)
# On cache par défaut les infos liste d'attente
self.ui.queue_lbl.hide()
self.ui.queue_position.hide()
# Si l'id discord = "" ou des espace, alors on affiche la page comme quoi faut être connecté à discord.
if self.stored_user_id == "" or self.stored_user_id.isspace():
self.ui.stackedWidget.setCurrentIndex(1)
else:
try:
# on vérifie si le joueur est whitelisté
WhiteList.check_whitelist(Urls.API_URL.value, self.stored_user_id)
except ApiError as exc:
show_qt_error(self, "La Tanière", f"Impossible de vérifier la whitelist.\n\n{exc}")
PlayerServerInfo.is_whitelist = False
PlayerServerInfo.is_staff = False
# si on est whitelisté, on démarre la file d'attente
if PlayerServerInfo.is_whitelist:
self.start_queue()
self.ui.queue_lbl.show()
self.ui.queue_position.show()
else:
self.ui.stackedWidget.setCurrentIndex(2)
# Test bouton en contruction
en_chantier = False
# on set la css du bouton en fonction de la valeur de la variable en_chantier
self.set_en_chantier(en_chantier)
if en_chantier:
old_btn = self.ui.connexion_btn
parent_layout = self.ui.verticalLayout_6 # layout direct du bouton dans le .ui
index = parent_layout.indexOf(old_btn)
new_btn = HazardButton(old_btn.parentWidget())
new_btn.setObjectName("connexion_btn")
new_btn.setText("EN MAINTENANCE")
new_btn.setIcon(old_btn.icon())
new_btn.setIconSize(old_btn.iconSize())
new_btn.setMinimumSize(old_btn.minimumSize())
new_btn.set_hazard(True)
parent_layout.takeAt(index)
old_btn.deleteLater()
parent_layout.insertWidget(index, new_btn)
self.ui.connexion_btn = new_btn
self.ui.connexion_btn.clicked.connect(self._on_connexion)
# centrage vertical du bouton connexion
if not PlayerServerInfo.is_staff:
self.ui.staff_btn.hide()
layout = self.ui.verticalLayout_6
# Trouver et modifier le spacer item
for i in range(layout.count()):
item = layout.itemAt(i)
if item.spacerItem(): # C'est un spacer
item.spacerItem().changeSize(20, 15, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
layout.invalidate() # Forcer le recalcul du layout
break
self.ui.info_text.setMarkdown(patch_note)
# Sous-systèmes
self._audio = AudioController(self.config, self.ui.audio_volume_adjust, self.ui.mute_btn)
self._glow = GlowAnimator(self.ui.connexion_btn)
self._dragger = WindowDragger(self)
self._connect_signals()
self._center_window()
self.show()
# ------------------------------------------------------------------
# Setup
# ------------------------------------------------------------------
def _connect_signals(self) -> None:
self.ui.close_btn.clicked.connect(self.close)
self.ui.minimize_btn.clicked.connect(self.showMinimized)
self.ui.connexion_btn.clicked.connect(self._on_connexion)
self.ui.discord_btn.clicked.connect(self._on_discord)
self.ui.intranet_btn.clicked.connect(self._on_intranet)
self.ui.discord_auth_btn.clicked.connect(self._on_discord_auth_btn)
self.ui.no_whitelist_btn.clicked.connect(self.close)
def _center_window(self) -> None:
self.adjustSize()
screen = (
QtGui.QGuiApplication.screenAt(QtGui.QCursor.pos())
or QtGui.QGuiApplication.primaryScreen()
)
rect = self.frameGeometry()
rect.moveCenter(screen.availableGeometry().center())
self.move(rect.topLeft())
# ------------------------------------------------------------------
# Button handlers
# ------------------------------------------------------------------
def _on_connexion(self) -> None:
try:
session_id = GetServerTokenForDiscord.authenticate(Urls.API_URL.value)
GetServerTokenForDiscord.register_discord_user(self.stored_user_id, session_id)
FiveMLauncher.launch()
except ApiError as exc:
show_qt_error(self, "Connexion impossible", f"Erreur lors de la connexion.\n\n{exc}")
@staticmethod
def _on_discord() -> None:
webbrowser.open(Urls.DISCORD.value)
def _on_intranet(self) -> None:
webbrowser.open(Urls.INTRANET.value)
self._glow.start()
def _on_discord_auth_btn(self) -> None:
try:
test = discord_oauth.get_discord_user_id()
self.config.set_discord_user(test[0])
PlayerServerInfo.session_id = test[1]
self.config.save()
self.ui.stackedWidget.setCurrentIndex(0)
except ApiError as exc:
show_qt_error(self, "Connexion Discord", f"Impossible de récupérer ton compte Discord.\n\n{exc}")
# ------------------------------------------------------------------
# Mouse events → délégués au WindowDragger
# ------------------------------------------------------------------
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
self._dragger.mouse_press(event)
super().mousePressEvent(event)
def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
self._dragger.mouse_move(event)
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
self._dragger.mouse_release(event)
super().mouseReleaseEvent(event)
# ------------------------------------------------------------------
# Close
# ------------------------------------------------------------------
def closeEvent(self, event) -> None:
if self.queue_thread and self.queue_thread.isRunning():
self.queue_thread.stop()
self.queue_thread.wait() # Attend que le thread se termine proprement
self.config.save()
super().closeEvent(event)
# ------------------------------------------------------------------
# Change ui on runtime
# ------------------------------------------------------------------
def set_en_chantier(self, valeur: bool):
self.en_chantier = valeur # ta variable Python
self.ui.connexion_btn.setProperty("en_chantier", valeur) # propriété Qt
self.ui.connexion_btn.style().unpolish(self.ui.connexion_btn)
self.ui.connexion_btn.style().polish(self.ui.connexion_btn)
# ------------------------------------------------------------------
# Queue managment
# ------------------------------------------------------------------
def start_queue(self):
self.queue_thread = QueueThread(self.stored_user_id)
self.queue_thread = QueueThread(self.stored_user_id, parent=self) # ← parent=self
self.queue_thread.update.connect(self.handle_update)
self.queue_thread.start()
# 🧪 TEMP - Simule une position en queue pour tester l'UI
self.handle_update("position:3:10")
def handle_update(self, message: str):
# print(f"[handle_update] reçu: {message}") # ← Debug
if message == "ok":
self.ui.queue_lbl.setVisible(False)
self.ui.queue_position.setVisible(False)
#self.ui.connexion_btn.setEnabled(True)
#self.ui.connexion_btn.setText("Lancer FiveM")
#self.launch_fivem()
elif message == "ready":
self.ui.queue_lbl.setVisible(True)
self.ui.queue_position.setVisible(False)
self.ui.queue_lbl.setText("🚀 C'est votre tour !")
self.launch_fivem()
elif message.startswith("position:"):
_, pos, total = message.split(":")
self.ui.queue_lbl.setVisible(True)
self.ui.queue_position.setVisible(True)
#self.ui.queue_position.setText(f"{pos} / {total}") <- si on veux le total de slots
self.ui.queue_position.setText(f"{pos}")
def launch_fivem(self):
pass
class QueueThread(QThread):
update = Signal(str)
def __init__(self, user_id: str, parent=None): # ← parent=None
super().__init__(parent) # ← passé à QThread
self.manager = QueueManager(
user_id=user_id,
on_update=self.update.emit
)
def run(self):
self.manager.start()
def stop(self):
self.manager.stop()