Compare commits

57 Commits

Author SHA1 Message Date
5b7ef4c951 Configuration debug vscode 2026-03-20 18:42:04 +01:00
82679a5709 Style pour le bouton en travaux 2026-03-20 18:36:31 +01:00
93c37a905b Fix server handler on .exe, add hazard stripe button 2026-03-20 11:26:56 +01:00
945abae5f1 new tool for quitting app from child windows 2026-03-19 14:22:54 +01:00
720b004eca Refacto 2026-03-19 13:39:08 +01:00
54aa7a50b2 Refacto QSS, add comments 2026-03-19 12:38:15 +01:00
1125e5ee10 Refacto constants 2026-03-19 09:31:36 +01:00
5d2282def1 Fix discord user is connected with pypresence 2026-03-18 17:33:27 +01:00
968d88c18d Merge remote-tracking branch 'origin/master'
# Conflicts:
#	src/main.py
2026-03-18 17:16:33 +01:00
8a3e487df6 Branchement de discord oauth 2026-03-18 17:15:23 +01:00
8a87fe38c8 WIP: connexion discord 2026-03-18 15:56:25 +01:00
32d8185f66 hide pycache from vscode explorer 2026-03-18 10:35:12 +01:00
484809356f Custome message box, secure code, discord, config 2026-03-17 11:06:09 +01:00
de8486662e disable open terminal on each vscode startup 2026-03-16 22:00:58 +01:00
97520e06e5 Mise à jour des modules requis pour un nouvel environement. 2026-03-16 21:55:17 +01:00
54a8d5c40f ajout du code pour l'api REST et discord oauth. Refacto 2026-03-16 21:42:25 +01:00
5a6470511a passage à l'ui avec la pager 2026-03-16 13:56:03 +01:00
e922825481 Refacto, enum, utils, WIP 2026-03-16 13:47:30 +01:00
5b065dddfc update .gitignore pour le config.json à ne pas avoir dans le dépot 2026-03-16 12:29:19 +01:00
fd4cf5e51f Wip page auth discord 2026-03-16 11:51:46 +01:00
5cfecdf856 Add vscode setting for python 2026-03-16 09:29:57 +01:00
c1d3b98949 WIP: design, discord page 2026-03-16 09:28:16 +01:00
a4ba0e29e8 Update default volume 2026-03-13 16:06:27 +01:00
3af8c97bc5 ajout de commentaires 2026-03-13 14:55:41 +01:00
a492a39a8c suppression de css obsolète 2026-03-13 14:42:57 +01:00
93e30ee178 Fix des valeur et des resources 2026-03-13 14:37:51 +01:00
d877a5cc91 déplacement de valeur vers des constantes et config mypy vscode 2026-03-13 14:21:05 +01:00
8e22b69f81 Bug fix et mise à jour fichier .spec 2026-03-13 14:02:59 +01:00
d0ede2acd5 Refacto en sous fichiers pour la maintenabilité 2026-03-13 13:55:45 +01:00
e1b32688b4 Refacto à 100% pour la maintenabilité 2026-03-13 13:45:49 +01:00
a6d4a708d8 Style du bouton mute passé en QSS dynamique 2026-03-13 12:30:39 +01:00
983d584015 refacto pour simplifier la maintenance. Voir à séparer dans des sous class plus tard 2026-03-13 12:20:16 +01:00
13b591a157 Amélioration de la configuration pour réduire les écritures disque 2026-03-13 11:21:11 +01:00
61e4e58ccf la config passe en variable locale plutot que global 2026-03-13 11:03:15 +01:00
83200c4932 Intrégration du module de configuration pour ici sauvergarder le volume audio 2026-03-13 10:59:05 +01:00
b2ba3964d3 Retour du QSlider handle via QSS 2026-03-12 14:47:42 +01:00
c532fbe137 Flag non interactive pour le textedit des patch notes 2026-03-12 12:07:50 +01:00
c73756d3d7 Set infinite loop for music 2026-03-11 11:15:57 +01:00
6bfcec32ce Retrait des dll Qt6 non nécéssaires. 2026-03-11 11:09:29 +01:00
37e815f42a WIp: resources, minimal pyside6 2026-03-11 10:28:46 +01:00
567901b205 Wip audio 2026-03-10 17:15:55 +01:00
935dbf6253 Wip Audio 2026-03-10 16:54:09 +01:00
abd1a1073a wip 2026-03-09 15:08:46 +01:00
18c708597a Retrait win11toast et compilation agressive via fichier spec 2026-03-09 12:52:05 +01:00
52e4c01f3d Passage à PySide6 car licence plus permissive 2026-03-09 12:22:49 +01:00
75893a8a01 Change for windows onyl & pyinstaller script 2026-03-07 17:17:14 +01:00
cc8aa03102 Hide QSlider handler 2026-03-07 10:45:58 +01:00
a2e1153340 upgrade readme 2026-03-06 11:41:59 +01:00
8c4aa284b7 upgrade extension recommendations 2026-03-06 11:33:47 +01:00
a437de91df wip: refacto qss, new assets 2026-03-06 11:24:45 +01:00
42768aa78a fix mypy config 2026-03-05 14:48:17 +01:00
8e7242937e fix window platform 2026-03-05 14:31:19 +01:00
a4bcd764db Adapting for windows and linux 2026-03-05 14:08:33 +01:00
8395b2f220 update qrc file path for ui file 2026-03-05 11:49:46 +01:00
b643188418 reorganize folders, add gitignore 2026-03-05 11:44:47 +01:00
097524cd27 cleaning file for dependencies 2026-03-05 11:19:45 +01:00
4be6dd8b84 Configure modules for vscode 2026-03-05 11:05:38 +01:00
63 changed files with 186393 additions and 18553 deletions

15
.editorconfig Normal file
View File

@@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
indent_style = space
insert_final_newline = true
indent_size = 2
[*.py]
indent_size = 4
[pyproject.toml]
indent_size = 4

403
.gitignore vendored Normal file
View File

@@ -0,0 +1,403 @@
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
!*.code-workspace
# Built Visual Studio Code Extensions
*.vsix
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
# Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
# poetry.lock
# poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
# pdm.lock
# pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
# pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# Redis
*.rdb
*.aof
*.pid
# RabbitMQ
mnesia/
rabbitmq/
rabbitmq-data/
# ActiveMQ
activemq-data/
# SageMath parsed files
*.sage.py
# Environments
.env
.envrc
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
# .idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
# Streamlit
.streamlit/secrets.toml
# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
.idea/sonarlint.xml # see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based HTTP Client
.idea/httpRequests
http-client.private.env.json
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Apifox Helper cache
.idea/.cache/.Apifox_Helper
.idea/ApifoxUploaderProjectSetting.xml
# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
.idea/**/copilot.data.migration.*.xml
# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
.idea/sonarlint.xml # see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based HTTP Client
.idea/httpRequests
http-client.private.env.json
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# Apifox Helper cache
.idea/.cache/.Apifox_Helper
.idea/ApifoxUploaderProjectSetting.xml
# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
.idea/**/copilot.data.migration.*.xml
src/config.json
config.json

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

11
.idea/PyQt6_LaTaniere.iml generated Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="uv (PyQt6_LaTaniere)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="uv (PyQt6_LaTaniere)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="uv (PyQt6_LaTaniere)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/PyQt6_LaTaniere.iml" filepath="$PROJECT_DIR$/.idea/PyQt6_LaTaniere.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

19
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"recommendations": [
"ms-python.python",
"ms-python.debugpy",
"ms-python.vscode-pylance",
"ms-python.vscode-python-envs",
"ms-python.mypy-type-checker",
"charliermarsh.ruff",
"editorconfig.editorconfig",
"njpwerner.autodocstring",
"redhat.vscode-yaml",
"tamasfe.even-better-toml",
"codezombiech.gitignore",
"theqtcompany.qt-core",
"theqtcompany.qt-ui",
"theqtcompany.qt-python",
"theqtcompany.qt-qml",
]
}

30
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Attach using Process Id",
"type": "debugpy",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python Debugger: Attach",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
}
}
]
}

26
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,26 @@
{
"python.analysis.autoImportCompletions": true,
"python.analysis.extraPaths": [".venv/lib/site-packages"],
"python.analysis.fixAll": ["source.unusedImports"],
"python.languageServer": "Pylance",
"mypy-type-checker.args": [
"--disallow-untyped-defs=True",
"--disallow-untyped-calls=True",
"--check-untyped-defs=True",
"--warn_return_any=True",
"--no-implicit-optional=True",
"--strict_optional=True",
"--ignore_missing_imports=True",
],
"editor.defaultFormatter": "charliermarsh.ruff",
"python.useEnvironmentsExtension": true,
"terminal.integrated.persistentSessionReviveProcess": "never",
"terminal.integrated.enablePersistentSessions": false,
"terminal.integrated.hideOnStartup": "always",
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"files.exclude": {
"**/__pycache__": true
}
}

266
La Tanière Launcher.spec Normal file
View File

@@ -0,0 +1,266 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['src\\main.py'],
pathex=['src'],
binaries=[],
datas=[
('.\\styles\\styles.qss', 'styles'),
('.\\ui\\mainwindow_vertical_pager.ui', 'ui')
],
hiddenimports=[
"asyncio",
"pypresence",
"pypresence.baseclient",
],
hookspath=[],
hooksconfig={
"qt_plugins": ["platforms", "styles"]
},
runtime_hooks=[],
excludes=[
# PySide6 - modules non utilisés
'PySide6.Qt3DAnimation',
'PySide6.Qt3DCore',
'PySide6.Qt3DExtras',
'PySide6.Qt3DInput',
'PySide6.Qt3DLogic',
'PySide6.Qt3DRender',
'PySide6.QtAxContainer',
'PySide6.QtBluetooth',
'PySide6.QtCharts',
'PySide6.QtConcurrent',
'PySide6.QtDataVisualization',
'PySide6.QtDesigner',
'PySide6.QtHelp',
'PySide6.QtLocation',
# 'PySide6.QtMultimedia', # Nécessaire pour l'audio
'PySide6.QtMultimediaWidgets',
# 'PySide6.QtNetwork', # Dépendance à QtMultimedia
'PySide6.QtNetworkAuth',
'PySide6.QtNfc',
'PySide6.QtOpenGL',
'PySide6.QtOpenGLWidgets',
'PySide6.QtPdf',
'PySide6.QtPdfWidgets',
'PySide6.QtPositioning',
'PySide6.QtPrintSupport',
'PySide6.QtQml',
'PySide6.QtQuick',
'PySide6.QtQuick3D',
'PySide6.QtQuickControls2',
'PySide6.QtQuickWidgets',
'PySide6.QtRemoteObjects',
'PySide6.QtScxml',
'PySide6.QtSensors',
'PySide6.QtSerialBus',
'PySide6.QtSerialPort',
'PySide6.QtSpatialAudio',
'PySide6.QtSql',
'PySide6.QtStateMachine',
'PySide6.QtSvg',
'PySide6.QtSvgWidgets',
'PySide6.QtTest',
'PySide6.QtTextToSpeech',
'PySide6.QtWebChannel',
'PySide6.QtWebEngineCore',
'PySide6.QtWebEngineQuick',
'PySide6.QtWebEngineWidgets',
'PySide6.QtWebSockets',
'PySide6.QtXml',
# Tests / dev tools
"test",
"tests",
"unittest",
"doctest",
"pydoc",
"pydoc_data",
# Packaging / build tooling
"distutils",
"setuptools",
"pkg_resources",
"pip",
"ensurepip",
# GUI stdlib inutiles
"tkinter",
"turtle",
"idlelib",
"curses",
# Legacy / obsolete
"lib2to3",
"2to3",
"nis",
"ossaudiodev",
"spwd",
# RPC / servers non utilisés
"xmlrpc",
"wsgiref",
"cgi",
"cgitb",
# Data / DB non utilisés
"sqlite3",
"dbm",
"dbm.dumb",
"csv",
# Concurrency non utilisée dans ton code
"multiprocessing",
#"concurrent",
#"asyncio",
# REPL / terminal
"readline",
"code",
# "codeop", # Nécessaire
"cmd",
# mail / network protocols non utilisés
"mailbox",
"imaplib",
"poplib",
"smtplib",
"nntplib",
"telnetlib",
"ftplib",
"netrc",
# Docs / browsing / parsing non utilisés directement
"pydoc_data",
"mailbox",
"imaplib",
"poplib",
"smtplib",
"nntplib",
"telnetlib",
# XML optionnel : agressif mais plutôt safe ici
"xml.dom",
"xml.etree",
"xml.parsers",
"xml.sax",
# Compression / archive optionnelles si non utilisées
"bz2",
"lzma",
"gzip",
#"zipfile", # Nécessaire à cause de pyinstaller
"tarfile",
"zipapp",
# audio stdlib non utilisée
"aifc",
"wave",
"sunau",
"chunk",
# divers peu probables
"mailcap",
"xdrlib",
"tabnanny",
"getpass",
# Windows services non utilisés
"win32service",
"win32serviceutil",
],
noarchive=False,
optimize=2,
)
# --- Filtre des DLLs Qt inutiles ---
# Ces DLLs sont incluses par analyse binaire malgré les excludes Python
unwanted_dlls = [
'Qt6Pdf',
'Qt6Quick',
'Qt6Qml',
'Qt6QmlModels',
'Qt6QmlWorkerScript',
'Qt6WebEngine',
'Qt6WebEngineCore',
'Qt63DCore',
'Qt63DRender',
'Qt63DAnimation',
'Qt63DExtras',
'Qt63DInput',
'Qt63DLogic',
'Qt6Charts',
'Qt6DataVisualization',
'Qt6Bluetooth',
'Qt6Nfc',
'Qt6Positioning',
'Qt6Location',
'Qt6RemoteObjects',
'Qt6Scxml',
'Qt6Sensors',
'Qt6SerialBus',
'Qt6SerialPort',
'Qt6SpatialAudio',
'Qt6StateMachine',
# 'Qt6Svg', # Dependance lecteur svg
'Qt6TextToSpeech',
'Qt6WebChannel',
'Qt6WebSockets',
'Qt6Sql',
'Qt6Test',
# 'Qt6OpenGL', # Dependance a Qt6UiTools
'Qt6PrintSupport',
# 'Qt6Xml', # Dependance fichier .ui
'Qt6Help',
'Qt6Designer',
'Qt6Concurrent',
]
a.binaries = [
(name, path, typecode)
for name, path, typecode in a.binaries
if not any(u.lower() in name.lower() for u in unwanted_dlls)
]
# AJOUTE CECI ICI :
# On filtre la liste des fichiers de données (datas)
# On exclut tout ce qui se trouve dans le dossier 'translations' de PySide6
a.datas = [f for f in a.datas if "translations" not in f[0].lower()]
# Si tu veux aussi supprimer les traductions système de Qt (fichiers .qm)
a.datas = [f for f in a.datas if not f[0].endswith('.qm')]
pyz = PYZ(a.pure, a.zipped_data)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='La Tanière Launcher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[
"_uuid.pyd",
"Qt6Core.dll",
"Qt6Gui.dll",
"Qt6Widgets.dll",
"Qt6UiTools.dll",
"qwindows.dll",
"python3*.dll",
"vcruntime*.dll",
"msvcp*.dll",
],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=True,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['assets\\Icon.ico'],
)

BIN
assets/Icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

BIN
assets/Logo_rond.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" ?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 120 120" id="Layer_1" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style type="text/css">
.st0{fill:#FF5462;}
.st1{opacity:0.1;}
.st2{fill:#C44151;}
.st3{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M100.4,97.2H19.6c-2.3,0-4.1-1.8-4.1-4.1V57.5c0-2.3,1.8-4.1,4.1-4.1h80.7c2.3,0,4.1,1.8,4.1,4.1v35.6 C104.5,95.3,102.6,97.2,100.4,97.2z"/>
<path class="st1" d="M102.5,96.2h-80c-2.5,0-4.5-2-4.5-4.5V56.8c0-1.4,0.7-2.6,1.7-3.5c-2.3,0.1-4.2,2.1-4.2,4.5v34.9 c0,2.5,2,4.5,4.5,4.5h80c1.1,0,2.1-0.4,2.8-1C102.7,96.2,102.6,96.2,102.5,96.2z"/>
<path class="st2" d="M57,33.5L37.1,53.4h4.6L60,35.1l18.3,18.3h4.6L63,33.5c1.6-1,2.8-2.8,2.8-4.9c0-3.2-2.6-5.8-5.8-5.8 c-3.2,0-5.8,2.6-5.8,5.8C54.2,30.7,55.3,32.5,57,33.5z M60,26.1c1.4,0,2.5,1.1,2.5,2.5s-1.1,2.5-2.5,2.5c-1.4,0-2.5-1.1-2.5-2.5 S58.6,26.1,60,26.1z"/>
<g>
<path class="st3" d="M28.9,73c0.2-0.3,0.5-0.5,0.9-0.6c0.3-0.1,0.7-0.2,1.1-0.2c0.5,0,1,0.1,1.4,0.4c0.4,0.3,0.8,0.6,1.1,1.1 l2.5-2.4c-0.6-0.8-1.3-1.4-2.2-1.9c-0.9-0.4-1.9-0.7-3-0.7c-0.9,0-1.8,0.2-2.6,0.5c-0.8,0.3-1.5,0.8-2.1,1.4 c-0.6,0.6-1,1.3-1.4,2.1c-0.3,0.8-0.5,1.7-0.5,2.7c0,1,0.2,1.9,0.5,2.7c0.3,0.8,0.8,1.5,1.4,2.1c0.6,0.6,1.3,1,2.1,1.4 c0.8,0.3,1.7,0.5,2.6,0.5c1.1,0,2.2-0.2,3-0.7c0.9-0.4,1.6-1.1,2.2-1.9L33.5,77c-0.3,0.5-0.7,0.8-1.1,1.1 c-0.4,0.3-0.9,0.4-1.4,0.4c-0.4,0-0.8-0.1-1.1-0.2c-0.3-0.1-0.6-0.4-0.9-0.6c-0.2-0.3-0.4-0.6-0.6-1c-0.1-0.4-0.2-0.8-0.2-1.3 c0-0.5,0.1-0.9,0.2-1.3C28.5,73.6,28.7,73.2,28.9,73z"/>

After

Width:  |  Height:  |  Size: 3.9 KiB

23
assets/computer-tv.svg Normal file
View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 501.333 501.333" xml:space="preserve">
<path style="fill:#637888;" d="M458.667,241.067c0,11.733-8.533,21.333-20.267,21.333H62.933c-10.667,0-20.267-9.6-20.267-21.333
V21.333C42.667,9.6,51.2,0,62.933,0H438.4c10.667,0,20.267,9.6,20.267,21.333L458.667,241.067L458.667,241.067z"/>
<path style="fill:#53C2EF;" d="M62.933,249.6c-5.333,0-8.533-4.267-8.533-9.6V21.333c0-5.333,4.267-9.6,8.533-9.6H438.4
c5.333,0,8.533,4.267,8.533,9.6v219.733c0,5.333-4.267,9.6-8.533,9.6H62.933V249.6z"/>
<rect x="166.4" y="262.4" style="fill:#3A5569;" width="168.533" height="61.867"/>
<path style="fill:#637888;" d="M363.733,317.867H137.6c-18.133,0-34.133,10.667-34.133,23.467h294.4
C397.867,328.533,382.933,317.867,363.733,317.867z"/>
<rect y="342.4" style="fill:#3A5569;" width="501.333" height="158.933"/>
<g>
<rect x="268.8" y="375.467" style="fill:#637888;" width="194.133" height="45.867"/>
<rect x="28.8" y="364.8" style="fill:#637888;" width="11.733" height="113.067"/>
<rect x="60.8" y="364.8" style="fill:#637888;" width="11.733" height="113.067"/>
<rect x="91.733" y="364.8" style="fill:#637888;" width="11.733" height="113.067"/>
<rect x="122.667" y="364.8" style="fill:#637888;" width="11.733" height="113.067"/>
<rect x="153.6" y="364.8" style="fill:#637888;" width="11.733" height="113.067"/>
</g>
<circle style="fill:#F16D6E;" cx="225.067" cy="400" r="11.733"/>
<circle style="fill:#60C3AB;" cx="225.067" cy="443.733" r="11.733"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

8
assets/discord-icon.svg Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -28.5 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M216.856339,16.5966031 C200.285002,8.84328665 182.566144,3.2084988 164.041564,0 C161.766523,4.11318106 159.108624,9.64549908 157.276099,14.0464379 C137.583995,11.0849896 118.072967,11.0849896 98.7430163,14.0464379 C96.9108417,9.64549908 94.1925838,4.11318106 91.8971895,0 C73.3526068,3.2084988 55.6133949,8.86399117 39.0420583,16.6376612 C5.61752293,67.146514 -3.4433191,116.400813 1.08711069,164.955721 C23.2560196,181.510915 44.7403634,191.567697 65.8621325,198.148576 C71.0772151,190.971126 75.7283628,183.341335 79.7352139,175.300261 C72.104019,172.400575 64.7949724,168.822202 57.8887866,164.667963 C59.7209612,163.310589 61.5131304,161.891452 63.2445898,160.431257 C105.36741,180.133187 151.134928,180.133187 192.754523,160.431257 C194.506336,161.891452 196.298154,163.310589 198.110326,164.667963 C191.183787,168.842556 183.854737,172.420929 176.223542,175.320965 C180.230393,183.341335 184.861538,190.991831 190.096624,198.16893 C211.238746,191.588051 232.743023,181.531619 254.911949,164.955721 C260.227747,108.668201 245.831087,59.8662432 216.856339,16.5966031 Z M85.4738752,135.09489 C72.8290281,135.09489 62.4592217,123.290155 62.4592217,108.914901 C62.4592217,94.5396472 72.607595,82.7145587 85.4738752,82.7145587 C98.3405064,82.7145587 108.709962,94.5189427 108.488529,108.914901 C108.508531,123.290155 98.3405064,135.09489 85.4738752,135.09489 Z M170.525237,135.09489 C157.88039,135.09489 147.510584,123.290155 147.510584,108.914901 C147.510584,94.5396472 157.658606,82.7145587 170.525237,82.7145587 C183.391518,82.7145587 193.761324,94.5189427 193.539891,108.914901 C193.539891,123.290155 183.391518,135.09489 170.525237,135.09489 Z" fill="#5865F2" fill-rule="nonzero">
</path>
</g>

After

Width:  |  Height:  |  Size: 2.0 KiB

11
assets/letter-i-info.svg Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<g>
<circle style="fill:#FFD24D;" cx="255.999" cy="75.469" r="75.469"/>
<path style="fill:#FFD24D;" d="M359.345,230.952v-45.874H152.654v45.874c15.395,0,27.874,12.479,27.874,27.873v179.426
c0,15.394-12.48,27.874-27.874,27.874V512h206.692v-45.873c-15.395,0-27.874-12.48-27.874-27.874V258.825
C331.471,243.431,343.951,230.952,359.345,230.952z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 671 B

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" ?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 120 120" id="Layer_1" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style type="text/css">
.st0{fill:#00CB72;}
.st1{opacity:0.1;}
.st2{fill:#009A64;}
.st3{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M100.4,97.2H19.6c-2.3,0-4.1-1.8-4.1-4.1V57.5c0-2.3,1.8-4.1,4.1-4.1h80.7c2.3,0,4.1,1.8,4.1,4.1v35.6 C104.5,95.3,102.6,97.2,100.4,97.2z"/>
<path class="st1" d="M102.5,96.2h-80c-2.5,0-4.5-2-4.5-4.5V56.8c0-1.4,0.7-2.6,1.7-3.5c-2.3,0.1-4.2,2.1-4.2,4.5v34.9 c0,2.5,2,4.5,4.5,4.5h80c1.1,0,2.1-0.4,2.8-1C102.7,96.2,102.6,96.2,102.5,96.2z"/>
<path class="st2" d="M57,33.5L37.1,53.4h4.6L60,35.1l18.3,18.3h4.6L63,33.5c1.6-1,2.8-2.8,2.8-4.9c0-3.2-2.6-5.8-5.8-5.8 c-3.2,0-5.8,2.6-5.8,5.8C54.2,30.7,55.3,32.5,57,33.5z M60,26.1c1.4,0,2.5,1.1,2.5,2.5s-1.1,2.5-2.5,2.5c-1.4,0-2.5-1.1-2.5-2.5 S58.6,26.1,60,26.1z"/>
<g>

After

Width:  |  Height:  |  Size: 2.5 KiB

14
assets/padlock-lock.svg Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512.001 512.001" xml:space="preserve">
<path style="fill:#FF9E16;" d="M459.897,231.945c0-16.19-13.246-29.436-29.436-29.436H81.54c-16.19,0-29.436,13.247-29.436,29.436
v250.619C52.104,498.754,65.349,512,81.54,512H430.46c16.189,0,29.436-13.246,29.436-29.436V231.945H459.897z"/>
<path style="fill:#0071CE;" d="M52.104,458.106v24.459c0,16.189,13.245,29.436,29.436,29.436H430.46
c16.189,0,29.436-13.246,29.436-29.436v-24.459H52.104z"/>
<path style="fill:#0055B8;" d="M284.42,344.612c14.456-9.34,24.033-25.585,24.033-44.077c0-28.969-23.484-52.453-52.453-52.453
s-52.453,23.484-52.453,52.453c0,18.491,9.578,34.736,24.034,44.077l-9.012,61.623c-0.809,5.538,3.107,10.069,8.703,10.069h57.457
c5.597,0,9.513-4.531,8.703-10.069L284.42,344.612z"/>
<path style="fill:#333E48;" d="M384.933,202.51h-52.985v-73.576c0-41.878-34.069-75.948-75.948-75.948s-75.949,34.069-75.949,75.948
v73.576h-52.985v-73.576C127.067,57.839,184.907,0,256.001,0s128.933,57.839,128.933,128.933L384.933,202.51L384.933,202.51z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

30
assets/sound-speaker.svg Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<path style="fill:#4A5058;" d="M144.24,167.708H29.247c-11.636,0-21.069,9.433-21.069,21.067v134.448
c0,11.636,9.433,21.067,21.069,21.067H144.24V167.708z"/>
<rect x="62.112" y="167.704" style="fill:#E45D4C;" width="28.195" height="176.582"/>
<path style="fill:#7E858B;" d="M297.578,62.32L144.24,167.708v176.583L297.578,449.68c7.783,5.349,18.374-0.222,18.374-9.666V71.986
C315.952,62.542,305.361,56.97,297.578,62.32z"/>
<path style="opacity:0.2;enable-background:new ;" d="M311.097,436.161L162.493,334.026c-3.083-2.119-6.738-3.253-10.479-3.253
H42.765c-11.636,0-21.067-9.433-21.067-21.067V175.258c0-2.274,0.371-4.46,1.037-6.512c-8.446,2.745-14.555,10.669-14.555,20.03
v134.448c0,11.636,9.433,21.067,21.069,21.067h114.992L297.579,449.68c7.783,5.349,18.374-0.222,18.374-9.666v-1.91
C314.293,437.846,312.64,437.222,311.097,436.161z"/>
<path d="M429.806,79.937c-3.457-2.908-8.618-2.46-11.523,0.999c-2.906,3.458-2.459,8.617,0.999,11.523
c48.528,40.785,76.361,100.396,76.361,163.546c0,63.149-27.833,122.76-76.361,163.545c-3.458,2.907-3.905,8.066-0.999,11.523
c1.617,1.926,3.933,2.917,6.266,2.917c1.857,0,3.726-0.629,5.258-1.918C482.041,388.171,512,323.996,512,256.004
C512,188.012,482.041,123.837,429.806,79.937z M393.581,123.107c-3.457-2.906-8.617-2.462-11.523,0.999
c-2.906,3.458-2.459,8.617,0.999,11.523c35.719,30.021,56.205,73.897,56.205,120.375s-20.486,90.353-56.205,120.374
c-3.458,2.906-3.905,8.066-0.999,11.523c1.617,1.926,3.934,2.917,6.266,2.917c1.857,0,3.726-0.629,5.258-1.918
c39.427-33.137,62.04-81.576,62.04-132.896S433.008,156.246,393.581,123.107z M313.492,54.367
c-6.567-3.454-14.438-2.987-20.547,1.212L141.7,159.529H29.247C13.12,159.528,0,172.649,0,188.776v134.448
c0,16.127,13.12,29.247,29.247,29.247H141.7l151.246,103.95c3.39,2.331,7.321,3.511,11.272,3.511c3.17,0,6.353-0.76,9.276-2.298
c6.562-3.453,10.639-10.205,10.639-17.619V71.986C324.131,64.571,320.055,57.82,313.492,54.367z M136.061,336.113H29.247
c-7.106,0-12.888-5.781-12.888-12.888V188.776c0-7.108,5.782-12.888,12.888-12.888H136.06v160.225H136.061z M307.772,440.014
c0,1.865-1.189,2.769-1.897,3.142c-0.709,0.371-2.126,0.841-3.663-0.216L152.42,339.989V172.011L302.212,69.06
c1.539-1.057,2.956-0.586,3.663-0.216c0.709,0.373,1.897,1.277,1.897,3.142V440.014z M357.385,166.238
c-3.461-2.904-8.619-2.455-11.525,1.002c-2.905,3.459-2.456,8.619,1.002,11.525c22.889,19.225,36.017,47.379,36.017,77.24
s-13.128,58.013-36.017,77.239c-3.458,2.905-3.908,8.066-1.002,11.525c1.618,1.926,3.935,2.918,6.268,2.918
c1.856,0,3.723-0.628,5.257-1.916c26.599-22.342,41.854-55.06,41.854-89.766C399.239,221.299,383.984,188.581,357.385,166.238z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

36
assets/tools-repair.svg Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<rect x="224.236" y="84.672" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 251.8026 619.7746)" style="fill:#D7D5D9;" width="60.049" height="346.13"/>
<path style="fill:#FF3F62;" d="M301.182,253.278l-47.095,47.095c-8.99,8.99-8.99,23.701,0,32.692l153.555,153.555
c8.99,8.99,23.702,8.99,32.692,0l47.095-47.095c8.99-8.99,8.99-23.701,0-32.692L333.874,253.278
C324.884,244.288,310.173,244.288,301.182,253.278z"/>
<polygon style="fill:#D7D5D9;" points="82.211,19.389 20.989,80.611 57.004,116.626 72.013,101.618 227.017,257.43 257.431,227.017
102.427,71.203 118.226,55.404 "/>
<g>
<rect x="327.715" y="127.531" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 501.6223 516.2964)" style="opacity:0.3;fill:#3E3B43;enable-background:new ;" width="60.049" height="53.456"/>
<rect x="129.012" y="331.181" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 21.903 715.0185)" style="opacity:0.3;fill:#3E3B43;enable-background:new ;" width="60.049" height="43.584"/>
</g>
<g>
<path style="fill:#77757E;" d="M488.276,74.56l-50.32,50.32l-50.837-50.835l50.32-50.32l-12.132-12.132
c-15.457-15.457-40.519-15.457-55.976,0l-44.664,44.664c-15.457,15.457-15.457,40.519,0,55.976l75.099,75.099
c15.457,15.457,40.519,15.457,55.976,0l44.665-44.665c15.457-15.457,15.457-40.519,0-55.976L488.276,74.56z"/>
<path style="fill:#77757E;" d="M23.725,437.439l50.32-50.32l50.836,50.836l-50.32,50.32l12.132,12.132
c15.457,15.457,40.519,15.457,55.976,0l44.665-44.665c15.457-15.457,15.457-40.519,0-55.976l-75.099-75.099
c-15.457-15.457-40.519-15.457-55.976,0l-44.665,44.665c-15.457,15.457-15.457,40.519,0,55.976L23.725,437.439z"/>
</g>
<path style="fill:#C70024;" d="M301.182,253.278l-47.095,47.095c-8.99,8.99-8.99,23.701,0,32.692l16.919,16.919l79.787-79.787
l-16.919-16.919C324.884,244.288,310.173,244.288,301.182,253.278z"/>
<g>
<rect x="207.016" y="220.106" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 551.4321 228.509)" style="opacity:0.3;fill:#3E3B43;enable-background:new ;" width="42.748" height="16.708"/>
<rect x="21.519" y="44.57" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 155.3011 62.1083)" style="opacity:0.07;fill:#3E3B43;enable-background:new ;" width="86.538" height="37.297"/>
</g>
<path style="fill:#FF728B;" d="M427.612,441.54L322.604,336.532c-4.069-4.069-4.069-10.668,0-14.738l0,0
c4.069-4.069,10.668-4.069,14.738,0L442.35,426.803c4.069,4.069,4.069,10.668,0,14.738l0,0
C438.281,445.61,431.682,445.61,427.612,441.54z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

1
build.bat Normal file
View File

@@ -0,0 +1 @@
pyinstaller --noconfirm --onedir --onefile --windowed --icon .\assets\Icone.ico --name "La Tanière Launcher" .\src\mainwindow.py --paths ".\.venv\Lib\site-packages\" --add-data ".\styles\styles.qss:styles" --add-data ".\ui\mainwindow.ui:ui" --hidden-import win11toast

View File

@@ -1,7 +0,0 @@
patch_note = (
"## 🚧 Modifications\n"
"&nbsp;\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;🏫 De l'auto école: nouvelle emplacement et nouveau circuit\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;🚗 Du concessionnaire\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;📒 Module de saisie pour le SAPD\n\n"
)

View File

@@ -1,48 +0,0 @@
import sys
import os
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
from PyQt6.QtCore import Qt, QPoint
# À placer tout en haut, avant les imports PyQt6 si possible
os.environ["QT_QPA_PLATFORM"] = "xcb"
class FenetreSansTitre(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.resize(400, 300)
# IMPORTANT : On active le suivi de souris
self.setMouseTracking(True)
layout = QVBoxLayout()
btn_quitter = QPushButton("Quitter")
btn_quitter.clicked.connect(self.close)
layout.addWidget(btn_quitter)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
self._drag_pos = None
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
# On enregistre la position du clic RELATIVE au coin haut-gauche de la fenêtre
self._drag_pos = event.position().toPoint()
def mouseMoveEvent(self, event):
if self._drag_pos is not None:
# On déplace la fenêtre vers la position globale du curseur
# MOINS le décalage initial (offset) pour éviter que le curseur ne saute au centre
self.move(event.globalPosition().toPoint() - self._drag_pos)
def mouseReleaseEvent(self, event):
self._drag_pos = None
if __name__ == "__main__":
app = QApplication(sys.argv)
fenetre = FenetreSansTitre()
fenetre.show()
sys.exit(app.exec())

View File

@@ -1,53 +0,0 @@
import sys
import os
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget
from PyQt6.QtCore import Qt, QPoint
# À placer tout en haut, avant les imports PyQt6 si possible
os.environ["QT_QPA_PLATFORM"] = "xcb"
class FenetreSansTitre(QMainWindow):
def __init__(self):
super().__init__()
# 1. Retirer la barre de titre
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.resize(400, 300)
# Interface simple
layout = QVBoxLayout()
btn_quitter = QPushButton("Fermer la fenêtre")
btn_quitter.clicked.connect(self.close)
layout.addWidget(btn_quitter)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
# Variable pour stocker la position du clic
self.old_pos = None
# --- Logique de déplacement ---
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
# On sauvegarde la position relative du curseur dans la fenêtre
self.old_pos = event.globalPosition().toPoint()
def mouseMoveEvent(self, event):
if self.old_pos is not None:
# Calcul de la distance parcourue
delta = QPoint(event.globalPosition().toPoint() - self.old_pos)
# Déplacement de la fenêtre
self.move(self.x() + delta.x(), self.y() + delta.y())
# Mise à jour de la position de référence
self.old_pos = event.globalPosition().toPoint()
def mouseReleaseEvent(self, event):
self.old_pos = None
if __name__ == "__main__":
app = QApplication(sys.argv)
fenetre = FenetreSansTitre()
fenetre.show()
sys.exit(app.exec())

View File

@@ -1,117 +0,0 @@
import sys
import os
from PyQt6 import QtGui, QtWidgets
from PyQt6 import uic
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFontDatabase, QFont, QColor
from PyQt6.QtWidgets import QApplication, QMainWindow
from win11toast import toast
from slidergroovecolorstyle import ThinSubPageLineStyle, ThinAddPageLineStyle
# Compile resources.qrc into resources_rc.py
# rcc -g python resources.qrc -o resources_rc.py
import resources # This is generated from the .qrc file
# À placer tout en haut, avant les imports PyQt6 si possible
if sys.platform.startswith('linux'):
os.environ["QT_QPA_PLATFORM"] = "xcb"
# Remove this into final release
from fake_patch_notes import patch_note
NO_STAFF = True
def load_custom_font():
# Load font from Qt resource
font_id = QFontDatabase.addApplicationFont(":/assets/Avocado-Cake-Demo.otf")
if font_id == -1:
raise RuntimeError("Failed to load font from resources.")
# Get the family name of the loaded font
font_families = QFontDatabase.applicationFontFamilies(font_id)
if not font_families:
raise RuntimeError("No font families found in the loaded font.")
return font_families[0]
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Remove the title bar and window frame
self.setWindowFlags(Qt.WindowType.FramelessWindowHint |Qt.WindowType.Window)
# Optional: Make background transparent (if you want rounded corners, etc.)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
# Track mouse position for dragging
self._drag_pos = None
# Load font family from resource
font_family = load_custom_font()
uic.loadUi("mainwindow.ui", self)
# Adjust UI
self.maintitle_label.setFont(QFont(font_family, 38))
self.subtitle_label.setStyleSheet("color: rgb(163, 177, 198)")
#self.horizontalSlider.setStyle(ThinAddPageLineStyle(app.style(), QColor("#2196F3")))
if NO_STAFF :
self.staff_btn.hide()
self.spacer_substitution.hide()
self.info_text.setMarkdown(patch_note)
# Find the button by its objectName in Qt Designer
# Example: objectName = "close_btn"
self.close_btn.clicked.connect(self.close_link)
self.minimize_btn.clicked.connect(self.minimize_link)
self.connexion_btn.clicked.connect(self.connexion_btn_link)
def close_link(self):
sys.exit(app.exec())
def minimize_link(self):
# Minimize the application
self.setWindowState(Qt.WindowState.WindowMinimized)
# Mouse press event to start dragging
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
event.accept()
# Mouse move event to drag window
def mouseMoveEvent(self, event):
if event.buttons() == Qt.MouseButton.LeftButton and self._drag_pos is not None:
self.move(event.globalPosition().toPoint() - self._drag_pos)
event.accept()
# Mouse release event to stop dragging
def mouseReleaseEvent(self, event):
self._drag_pos = None
event.accept()
def connexion_btn_link(self):
toast('Hello Python🐍')
if __name__ == "__main__":
app = QApplication(sys.argv)
with open('styles.qss', 'r') as f:
style = f.read()
# Set the stylesheet of the application
app.setStyleSheet(style)
# Load and set the global font
custom_font = QFont(load_custom_font(), 16)
if custom_font:
app.setFont(custom_font)
window = MainWindow()
window.show()
sys.exit(app.exec())

View File

@@ -1,9 +0,0 @@
[project]
name = "LaTaniere_UI"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"pyside6>=6.10.2",
]

51
readme.md Normal file
View File

@@ -0,0 +1,51 @@
# LaTaniere Launcher UI
⚠️ you need [Qt Designer](https://build-system.fman.io/qt-designer-download).
## First: create virtualenv
python -m venv .venv
## Second: activate your virtual environment
### Windows
Execute in powershell:
```powershell
.venv/bin/activate.ps1
```
or batch file
```bat
.venv/bin/activate.bat
```
### Linux
Execute into terminal in project folder:
`source .venv/bin/actiave`
----
## Third install packages
Ensure your virtual environment is active and execute these commands within it.
### Using Pip
#### Global packages
`pip install -r .\requirements.txt`
#### packages for Windows only
`pip install -r .\windows_requirements.txt`
### Using install.py script
Execute `python install.py`
Note: in case if you aren't virtual environment, the script stop.
Enjoy.

View File

@@ -1,31 +1,21 @@
certifi==2026.2.25
distlib==0.4.0
filelock==3.25.0
altgraph==0.17.5
packaging==26.0
pipenv==2026.0.3
platformdirs==4.9.2
PyQt6==6.10.2
PyQt6-Qt6==6.10.2
PyQt6_sip==13.11.0
PySide6==6.10.2
pefile==2024.8.26
pyinstaller==6.19.0
pyinstaller-hooks-contrib==2026.3
PySide6_Addons==6.10.2
PySide6_Essentials==6.10.2
python-discovery==1.1.0
setuptools==82.0.0
pywin32-ctypes==0.2.3
setuptools==82.0.1
shiboken6==6.10.2
typing_extensions==4.15.0
virtualenv==21.1.0
win11toast==0.36.3
winrt-runtime==3.2.1
winrt-Windows.Data.Xml.Dom==3.2.1
winrt-Windows.Foundation==3.2.1
winrt-Windows.Foundation.Collections==3.2.1
winrt-Windows.Globalization==3.2.1
winrt-Windows.Graphics.Imaging==3.2.1
winrt-Windows.Media.Core==3.2.1
winrt-Windows.Media.Ocr==3.2.1
winrt-Windows.Media.Playback==3.2.1
winrt-Windows.Media.SpeechSynthesis==3.2.1
winrt-Windows.Storage==3.2.1
winrt-Windows.Storage.Streams==3.2.1
winrt-Windows.UI.Notifications==3.2.1
certifi==2026.1.4
cffi==2.0.0
charset-normalizer==3.4.4
cryptography==46.0.4
idna==3.11
psutil==7.2.2
puremagic==1.30
pycparser==3.0
pypresence==4.6.1
requests==2.32.5
urllib3==2.6.3

17792
resources.py

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,17 @@
<RCC>
<qresource prefix="/">
<file>assets/closed-store-info.svg</file>
<file>assets/letter-i-info.svg</file>
<file>assets/open-store-info.svg</file>
<file>assets/tools-repair.svg</file>
<file>assets/Icon.ico</file>
<file>assets/sound-speaker.svg</file>
<file>assets/the-beat-of-nature.mp3</file>
<file>assets/computer-tv.svg</file>
<file>assets/padlock-lock.svg</file>
<file>assets/logo.png</file>
<file>assets/system-shutdown.png</file>
<file>assets/discord-icon.svg</file>
<file>assets/Avocado-Cake-Demo.otf</file>
<file>assets/background.png</file>
<file>assets/Icone.ico</file>
</qresource>
</RCC>

View File

@@ -1,272 +0,0 @@
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
QSlider, QProxyStyle, QStyle
)
from PyQt6.QtGui import QColor
class SliderGrooveColorStyle(QProxyStyle):
def __init__(self, base_style=None, groove_color=QColor("lightblue")):
super().__init__(base_style)
self.groove_color = groove_color
def drawComplexControl(self, control, option, painter, widget=None):
if control == QStyle.ComplexControl.CC_Slider and isinstance(widget, QSlider):
# Récupérer la zone du groove
groove_rect = self.subControlRect(
QStyle.ComplexControl.CC_Slider,
option,
QStyle.SubControl.SC_SliderGroove,
widget
)
# Dessiner notre fond
painter.save()
painter.setBrush(self.groove_color)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRect(groove_rect)
painter.restore()
# Puis laisser Qt dessiner normalement (handle etc.)
super().drawComplexControl(control, option, painter, widget)
return
super().drawComplexControl(control, option, painter, widget)
class SliderSubPageColorStyle(QProxyStyle):
def __init__(self, base_style=None, color=QColor("#4CAF50")):
super().__init__(base_style)
self.color = color
def drawComplexControl(self, control, option, painter, widget=None):
if control == QStyle.ComplexControl.CC_Slider and isinstance(widget, QSlider):
# Laisser Qt dessiner le slider normalement
super().drawComplexControl(control, option, painter, widget)
# Récupérer les rectangles du groove et du handle
groove_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderGroove,
widget
)
handle_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderHandle,
widget
)
painter.save()
painter.setBrush(self.color)
painter.setPen(Qt.PenStyle.NoPen)
if widget.orientation() == Qt.Orientation.Horizontal:
# Partie avant le handle (gauche → centre du handle)
sub_rect = groove_rect.adjusted(
0,
0,
handle_rect.center().x() - groove_rect.right(),
0
)
else:
# Vertical (bas → centre du handle)
sub_rect = groove_rect.adjusted(
0,
handle_rect.center().y() - groove_rect.bottom(),
0,
0
)
painter.drawRect(sub_rect)
painter.restore()
return
super().drawComplexControl(control, option, painter, widget)
class SliderAddPageColorStyle(QProxyStyle):
def __init__(self, base_style=None, color=QColor("#FF9800")):
super().__init__(base_style)
self.color = color
def drawComplexControl(self, control, option, painter, widget=None):
if control == QStyle.ComplexControl.CC_Slider and isinstance(widget, QSlider):
# Récupérer groove + handle
groove_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderGroove,
widget
)
handle_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderHandle,
widget
)
painter.save()
painter.setBrush(self.color)
painter.setPen(Qt.PenStyle.NoPen)
if widget.orientation() == Qt.Orientation.Horizontal:
# Partie après le handle (centre → fin)
add_rect = groove_rect.adjusted(
handle_rect.center().x() - groove_rect.left(),
0,
0,
0
)
else:
# Vertical (centre → haut)
add_rect = groove_rect.adjusted(
0,
0,
0,
handle_rect.center().y() - groove_rect.top()
)
painter.drawRect(add_rect)
painter.restore()
# IMPORTANT : Qt dessine ensuite le handle par-dessus
super().drawComplexControl(control, option, painter, widget)
return
super().drawComplexControl(control, option, painter, widget)
class ThinAddPageLineStyle(QProxyStyle):
def __init__(self, base_style=None, color=QColor("#E91E63")):
super().__init__(base_style)
self.color = color
def drawComplexControl(self, control, option, painter, widget=None):
if control == QStyle.ComplexControl.CC_Slider and isinstance(widget, QSlider):
groove_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderGroove,
widget
)
handle_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderHandle,
widget
)
painter.save()
painter.setBrush(self.color)
painter.setPen(Qt.PenStyle.NoPen)
if widget.orientation() == Qt.Orientation.Horizontal:
# Épaisseur fine (2 px)
thickness = 2
y = groove_rect.center().y() - thickness // 2
add_rect = groove_rect.adjusted(
handle_rect.center().x() - groove_rect.left(),
0,
0,
0
)
add_rect.setTop(y)
add_rect.setHeight(thickness)
else:
thickness = 2
x = groove_rect.center().x() - thickness // 2
add_rect = groove_rect.adjusted(
0,
0,
0,
handle_rect.center().y() - groove_rect.top()
)
add_rect.setLeft(x)
add_rect.setWidth(thickness)
painter.drawRect(add_rect)
painter.restore()
# Dessiner ensuite le slider normalement (handle au-dessus)
super().drawComplexControl(control, option, painter, widget)
return
super().drawComplexControl(control, option, painter, widget)
class ThinSubPageLineStyle(QProxyStyle):
def __init__(self, base_style=None, color=QColor("#4CAF50")):
super().__init__(base_style)
self.color = color
def drawComplexControl(self, control, option, painter, widget=None):
if control == QStyle.ComplexControl.CC_Slider and isinstance(widget, QSlider):
groove_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderGroove,
widget
)
handle_rect = self.subControlRect(
control,
option,
QStyle.SubControl.SC_SliderHandle,
widget
)
painter.save()
painter.setBrush(self.color)
painter.setPen(Qt.PenStyle.NoPen)
# Épaisseur fine (ajuste si besoin)
thickness = 2
if widget.orientation() == Qt.Orientation.Horizontal:
y = groove_rect.center().y() - thickness // 2
sub_rect = groove_rect.adjusted(
0,
0,
handle_rect.center().x() - groove_rect.right(),
0
)
sub_rect.setTop(y)
sub_rect.setHeight(thickness)
else:
x = groove_rect.center().x() - thickness // 2
sub_rect = groove_rect.adjusted(
0,
handle_rect.center().y() - groove_rect.bottom(),
0,
0
)
sub_rect.setLeft(x)
sub_rect.setWidth(thickness)
painter.drawRect(sub_rect)
painter.restore()
# Qt redessine le slider (handle au-dessus)
super().drawComplexControl(control, option, painter, widget)
return
super().drawComplexControl(control, option, painter, widget)

View File

@@ -0,0 +1,137 @@
import json
from pathlib import Path
from typing import Any, Callable, NotRequired, TypedDict, cast
from tools.utils import get_executable_dir
class ConfigData(TypedDict):
discord_user_id: NotRequired[str]
volume: NotRequired[int]
Validator = Callable[[Any], bool]
Normalizer = Callable[[Any], Any]
class ConfigField(TypedDict):
default: Any
validator: Validator
normalizer: Normalizer
CONFIG_PATH = get_executable_dir() / "config.json"
DISCORD_USER_KEY = "discord_user_id"
VOLUME_KEY = "volume"
CONFIG_SCHEMA: dict[str, ConfigField] = {
DISCORD_USER_KEY: {
"default": "",
"validator": lambda value: isinstance(value, str),
"normalizer": lambda value: str(value).strip(),
},
VOLUME_KEY: {
"default": 30,
"validator": lambda value: isinstance(value, int) and 0 <= value <= 100,
"normalizer": lambda value: max(0, min(int(value), 100)),
},
}
class ConfigManager:
def __init__(self, path: Path | None = None) -> None:
self.path = path or CONFIG_PATH
self._data: ConfigData = self._load()
self._dirty = False
# Lecture du fichier de configuration
def _load(self) -> ConfigData:
if not self.path.exists():
return {}
try:
with self.path.open("r", encoding="utf-8") as file:
data = json.load(file)
except (json.JSONDecodeError, OSError):
return {}
if not isinstance(data, dict):
return {}
return cast(ConfigData, data)
# Sauvegarde du fichier de configuration
def save(self) -> None:
if not self._dirty:
return
self.path.parent.mkdir(parents=True, exist_ok=True)
with self.path.open("w", encoding="utf-8") as file:
json.dump(self._data, file, indent=4, ensure_ascii=False)
self._dirty = False
def _get_field(self, key: str) -> ConfigField:
if key not in CONFIG_SCHEMA:
raise KeyError(f"Unknown config key: {key}")
return CONFIG_SCHEMA[key]
def get(self, key: str) -> Any:
field = self._get_field(key)
value = self._data.get(key, field["default"])
if not field["validator"](value):
return field["default"]
return value
def set(self, key: str, value: Any) -> None:
field = self._get_field(key)
normalized = field["normalizer"](value)
if not field["validator"](normalized):
raise ValueError(f"Invalid value for {key}")
if self._data.get(key) == normalized:
return
self._data[key] = normalized
self._dirty = True
def reset_all(self) -> None:
defaults: ConfigData = cast(
ConfigData,
{key: field["default"] for key, field in CONFIG_SCHEMA.items()},
)
self.save(defaults)
def get_all(self) -> ConfigData:
return cast(
ConfigData,
{key: self.get(key) for key in CONFIG_SCHEMA},
)
# ---------------------------------------------------------------------------
# SETTERS MÉTIER
# ---------------------------------------------------------------------------
# Set Discord ID
def set_discord_user(self, user_id: str) -> None:
self.set(DISCORD_USER_KEY, user_id)
# Set volume
def set_volume(self, volume: int) -> None:
self.set(VOLUME_KEY, volume)
# ---------------------------------------------------------------------------
# GETTERS MÉTIER
# ---------------------------------------------------------------------------
# Get discord ID
def get_default(self, key: str):
return CONFIG_SCHEMA[key]["default"]
# Get volume value
def get_discord_user(self) -> str:
return cast(str, self.get(DISCORD_USER_KEY))
def get_volume(self) -> int:
return cast(int, self.get(VOLUME_KEY))

43
src/config/constants.py Normal file
View File

@@ -0,0 +1,43 @@
from enum import Enum
from PySide6.QtGui import QColor
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
NO_STAFF = True
NO_WHITELIST = False
REDIRECT_URI = "http://localhost:5000/callback"
SCOPES = ["identify"]
CLIENT_ID = "1240007913175781508"
AUTENTICATION_SUCCESS_MESSAGE = """
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body>
<h1>Authentication réussie</h1>
<p>Vous pouvez maintenant fermer cette fenêtre et revenir au launcher de La Tanière.</p>
</body>
</html>
""".encode('utf-8')
# ---------------------------------------------------------------------------
# ENUMS
# ---------------------------------------------------------------------------
class Resources(Enum):
MP3 = ":/assets/the-beat-of-nature.mp3"
FONT = ":/assets/Avocado-Cake-Demo.otf"
class Urls(Enum):
DISCORD = "https://discord.gg/A7eanmSkp2"
INTRANET = "https://la-taniere.fun/connexion/"
API_URL = 'https://prod.la-taniere.fun:30121/'
class Glow(Enum):
COLOR = QColor(255, 140, 0, 255)
BLUR_BASE = 15
BLUR_PEAK = 70
ANIM_DURATION = 1200

View File

@@ -0,0 +1,86 @@
from PySide6.QtCore import QFile, QBuffer, QByteArray, QIODevice
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
from config.config_manager import ConfigManager, VOLUME_KEY
from config.constants import Resources
class AudioController:
# Encapsule toute la logique audio : lecture, volume, mute.
def __init__(self, config: ConfigManager, slider, mute_btn):
self._config = config
self._slider = slider
self._mute_btn = mute_btn
# Lecteur
self._player = QMediaPlayer()
self._output = QAudioOutput()
self._player.setAudioOutput(self._output)
self._player.setLoops(-1)
# Chargement du MP3 depuis les ressources Qt
mp3file = QFile(Resources.MP3.value)
mp3file.open(QFile.ReadOnly)
mp3data = mp3file.readAll()
mp3file.close()
# Mise en buffer du MP3
self._buffer = QBuffer()
self._buffer.setData(QByteArray(mp3data))
self._buffer.open(QIODevice.ReadOnly)
self._player.setSourceDevice(self._buffer)
# État initial du lecteur
volume = config.get_volume()
self._is_muted = volume == 0
self._previous_volume = volume if volume != 0 else config.get_default(VOLUME_KEY)
self._apply_volume(volume, save=False)
self._refresh_mute_btn()
self._player.play()
# Connexions
self._slider.valueChanged.connect(self._on_slider_changed)
self._mute_btn.clicked.connect(self.toggle_mute)
# ------------------------------------------------------------------
# Public API
# ------------------------------------------------------------------
def toggle_mute(self) -> None:
if not self._is_muted:
self._previous_volume = self._slider.value()
self._apply_volume(0)
self._is_muted = True
else:
self._apply_volume(self._previous_volume)
self._is_muted = False
# ------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------
def _on_slider_changed(self, value: int) -> None:
self._is_muted = value == 0
self._output.setVolume(value / 100.0)
self._config.set_volume(value)
self._refresh_mute_btn()
def _apply_volume(self, value: int, save: bool = True) -> None:
self._slider.blockSignals(True)
self._slider.setValue(value)
self._slider.blockSignals(False)
self._output.setVolume(value / 100.0)
if save:
self._config.set_volume(value)
self._refresh_mute_btn()
def _refresh_mute_btn(self) -> None:
muted = self._slider.value() == 0
self._mute_btn.setProperty("muted", muted)
self._mute_btn.style().unpolish(self._mute_btn)
self._mute_btn.style().polish(self._mute_btn)

View File

@@ -0,0 +1,32 @@
from PySide6.QtCore import QPropertyAnimation, QEasingCurve
from PySide6.QtWidgets import QGraphicsDropShadowEffect
from config.constants import Glow
class GlowAnimator:
# Gère l'effet de lueur pulsée sur un widget.
def __init__(self, widget):
self._widget = widget
self._effect = QGraphicsDropShadowEffect(widget)
self._effect.setBlurRadius(Glow.BLUR_BASE.value)
self._effect.setOffset(0, 0)
self._effect.setColor(Glow.COLOR.value)
self._anim = QPropertyAnimation(self._effect, b"blurRadius")
self._anim.setDuration(Glow.ANIM_DURATION.value)
self._anim.setStartValue(Glow.BLUR_BASE.value)
self._anim.setKeyValueAt(0.5, Glow.BLUR_PEAK.value)
self._anim.setEndValue(Glow.BLUR_BASE.value)
self._anim.setEasingCurve(QEasingCurve.InOutQuad)
self._anim.setLoopCount(-1)
def start(self) -> None:
self._widget.setGraphicsEffect(self._effect)
self._anim.start()
def stop(self) -> None:
self._anim.stop()
self._widget.setGraphicsEffect(None)

View File

@@ -0,0 +1,25 @@
from PySide6 import QtGui
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QMainWindow
class WindowDragger:
# Permet de déplacer une fenêtre sans barre de titre.
def __init__(self, window: QMainWindow):
self._window = window
self._drag_pos = None
def mouse_press(self, event: QtGui.QMouseEvent) -> None:
if event.button() == Qt.MouseButton.LeftButton:
self._drag_pos = (
event.globalPosition().toPoint()
- self._window.frameGeometry().topLeft()
)
def mouse_move(self, event: QtGui.QMouseEvent) -> None:
if event.buttons() & Qt.MouseButton.LeftButton and self._drag_pos is not None:
self._window.move(event.globalPosition().toPoint() - self._drag_pos)
def mouse_release(self, _event) -> None:
self._drag_pos = None

View File

@@ -0,0 +1,84 @@
import requests
import webbrowser
import os
from urllib.parse import urlencode
from http.server import HTTPServer, BaseHTTPRequestHandler
from fivemserver.get_server_token import GetServerTokenForDiscord
from config.constants import CLIENT_ID, REDIRECT_URI, SCOPES, AUTENTICATION_SUCCESS_MESSAGE
# Disable stderr output
os.environ['PYTHONWARNINGS'] = 'ignore'
class OAuthCallbackHandler(BaseHTTPRequestHandler):
code: str | None = None
# Ajoute ceci pour empêcher le serveur d'écrire dans la console/stderr
def log_message(self, format, *args):
return # Ne fait rien, donc pas de blocage sur stdout/stderr
def do_GET(self):
"""
callback pour discord auth
"""
if "/callback" in self.path:
query = self.path.split("?")[1]
params = dict(p.split("=") for p in query.split("&"))
OAuthCallbackHandler.code = params.get("code")
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(AUTENTICATION_SUCCESS_MESSAGE)
# return discord application id (client id)
def get_discord_client_id() -> str:
"""
return discord application id
"""
return CLIENT_ID
# return discord user id
def get_discord_user_id() -> str:
"""
Retourne l'id du compte discord de l'utilisateur via l'oauh discord.
"""
# récupération des infos serveur la tanière
# récupération d
session_id = GetServerTokenForDiscord.authenticate()
client_secret = GetServerTokenForDiscord.get_token(session_id)
auth_url = "https://discord.com/api/oauth2/authorize"
params = {
"client_id": CLIENT_ID,
"redirect_uri": REDIRECT_URI,
"response_type": "code",
"scope": " ".join(SCOPES),
}
webbrowser.open(f"{auth_url}?{urlencode(params)}")
server = HTTPServer(("localhost", 5000), OAuthCallbackHandler)
server.handle_request()
if not OAuthCallbackHandler.code:
raise RuntimeError("OAuth échoué")
token = requests.post(
"https://discord.com/api/oauth2/token",
data={
"client_id": CLIENT_ID,
"client_secret": client_secret,
"grant_type": "authorization_code",
"code": OAuthCallbackHandler.code,
"redirect_uri": REDIRECT_URI,
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
).json()
user = requests.get(
"https://discord.com/api/users/@me",
headers={"Authorization": f"Bearer {token['access_token']}"},
).json()
return user["id"]

View File

@@ -0,0 +1,53 @@
import psutil
from pypresence import Presence
from fivemserver.get_server_token import GetServerTokenForDiscord
from config.constants import Urls
from discord.discord_oauth import CLIENT_ID
class DiscordToken:
"""
Décode le token discord
"""
@staticmethod
def decode_discord_token():
discord_token = GetServerTokenForDiscord.get_token(
GetServerTokenForDiscord.authenticate(Urls.API_URL.value)
)
return discord_token
class CheckDiscord:
@staticmethod
def isdiscordrunning() -> bool:
"""
Vérifie si Discord est en cours d'exécution sur l'ordinateur. (Vérifie aussi pour Linux)
"""
for process in psutil.process_iter(["name"]):
if (
process.info["name"].lower() == "discord.exe"
or process.info["name"].lower() == "discordcanary.exe"
or process.info["name"].lower() == "discord"
or process.info["name"].lower() == "discord canary"
):
return True
return False
@staticmethod
def isuserconnected() -> bool:
"""
Vérifie si l'utilisateur Discord est connecté.
ne vérifie pas le user id discord.
"""
rpc = Presence(CLIENT_ID)
try:
rpc.connect()
return True
except Exception as e:
return False
finally:
try:
rpc.close()
except Exception as e:
pass

13
src/fake_patch_notes.py Normal file
View File

@@ -0,0 +1,13 @@
patch_note = (
"## 🚧 Modifications\n"
"&nbsp;\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;🏫 De l'auto école: nouvelle emplacement et nouveau circuit\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;🚗 Du concessionnaire\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;📒 Module de saisie pour le SAPD\n\n"
"\n\n"
"## 🚧 Modifications\n"
"&nbsp;\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;🏫 De l'auto école: nouvelle emplacement et nouveau circuit\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;🚗 Du concessionnaire\n\n"
"&nbsp;&nbsp;&nbsp;&nbsp;📒 Module de saisie pour le SAPD\n\n"
)

View File

@@ -0,0 +1,76 @@
import base64
import requests
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from config.constants import Urls
class GetServerTokenForDiscord:
derived_key: bytes | None = None
@staticmethod
def authenticate(server = Urls.API_URL.value):
if server is None:
server = Urls.API_URL.value
# ==========================
# Génération clé ECDH client
# ==========================
client_private = ec.generate_private_key(ec.SECP256R1())
client_public = client_private.public_key()
client_pub_pem = client_public.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
# ==========================
# AUTH
# ==========================
auth = requests.post(server + "/api_v2/auth", verify=False, json={
"client_pub": base64.b64encode(client_pub_pem).decode()
}).json()
server_pub = serialization.load_pem_public_key(
base64.b64decode(auth["server_pub"])
)
shared_key = client_private.exchange(ec.ECDH(), server_pub)
GetServerTokenForDiscord.derived_key = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info=b"fivem-private-server"
).derive(shared_key)
return auth["session_id"]
@staticmethod
def get_token(session_id: bytes, server = Urls.API_URL.value):
# ==========================
# DISCORD TOKEN
# ==========================
if server is None:
server = Urls.API_URL.value
download = requests.post(server + "/api_v2/tkn_auth", verify=False, headers={
"x-session-id": session_id
}).json()
nonce = base64.b64decode(download["nonce"])
encrypted_data = base64.b64decode(download["data"])
aesgcm = AESGCM(GetServerTokenForDiscord.derived_key) # type: ignore[arg-type]
return aesgcm.decrypt(nonce, encrypted_data, None)
@staticmethod
def register_discord_user(user_id: str, server = Urls.API_URL.value) -> str:
if server is None:
server = Urls.API_URL.value
registeredId = requests.post(server + "/api_v2/connection/register", verify=False, json={
"x-session-id": user_id
}).json()
return registeredId["discord_id"]

View File

@@ -0,0 +1,23 @@
import requests
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
# Supress only InsecureRequestWarning
disable_warnings(InsecureRequestWarning)
WHITELIST_URL_ENDPOINT = f'iswhitelist/'
class WhiteList:
@staticmethod
def checkwhitelist(url, discord_user_id: str) -> bool:
print('🗒️ Vérification de la whitelist...')
response = requests.get(url + WHITELIST_URL_ENDPOINT + discord_user_id, verify=False)
api_data = response.json()
if api_data['whitelisted']:
print("👍 Vous êtes en whitelist")
return True
else:
print('🙅‍♂️ Désole mais vous n\'êtes pas whitelisté sur le serveur.')
return False

95
src/main.py Normal file
View File

@@ -0,0 +1,95 @@
import sys
from tools.utils import get_internal_dir
from PySide6.QtCore import QResource
from PySide6.QtGui import QFontDatabase, QFont
from PySide6.QtWidgets import QApplication
# Imports pour la gestion de la configuration
from config.config_manager import ConfigManager
from config.constants import Resources
# Imports pour la vérification Discord
from discord.discord_tools import CheckDiscord
# Import pour la partie ui
from ui.custom_message_box import CustomMessageBox
from ui.main_window import MainWindow
# Ne pas supprimer ! Enregistre les ressources Qt
import resources # noqa: F401 - required to register Qt resources
# ---------------------------------------------------------------------------
# Bundle path resolution
# ---------------------------------------------------------------------------
bundle_dir = get_internal_dir()
QResource.registerResource(f"{bundle_dir}/resources.py")
# ---------------------------------------------------------------------------
# Font helper
# ---------------------------------------------------------------------------
def load_custom_font() -> str:
font_id = QFontDatabase.addApplicationFont(Resources.FONT.value)
if font_id == -1:
raise RuntimeError("Failed to load font from resources.")
font_families = QFontDatabase.applicationFontFamilies(font_id)
if not font_families:
raise RuntimeError("No font families found in the loaded font.")
return font_families[0]
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
if __name__ == "__main__":
app = QApplication(sys.argv)
# 1. Initialisation UNIQUE du gestionnaire de config
config = ConfigManager()
# 2. Setup environnemental (Styles & Fonts)
try:
with open(f"{bundle_dir}/styles/styles.qss", 'r') as f:
app.setStyleSheet(f.read())
app.setFont(QFont(load_custom_font(), 16))
except Exception as e:
print(f"Erreur lors du chargement des styles : {e}")
# 3. Garde-fou Discord
if not CheckDiscord.isdiscordrunning():
msg = CustomMessageBox(
title="La Tanière: Discord non détecté",
message="Discord ne semble pas lancé.\n\n"
"Tu dois avoir démarré Discord et y être connecté pour utiliser l'application.\n\n"
"Lorsque cela sera fait, relance le launcher.",
icon_type=CustomMessageBox.WARNING,
buttons=CustomMessageBox.OK
)
msg.exec()
sys.exit(0) # On quitte proprement sans lancer MainWindow
# On récupère l'ID stocké (sera "" si absent grâce au schéma)
stored_user_id = config.get_discord_user()
if stored_user_id != "" and not stored_user_id.isspace():
if not CheckDiscord.isuserconnected():
msg = CustomMessageBox(
title="La Tanière: connexion Discord",
message="Tu n'est pas connecté à Discord\n\n"
"Assure-toi que tu es connecté à Discord.\n\n"
"Lorsque cela sera fait, relance le launcher.",
icon_type=CustomMessageBox.WARNING,
buttons=CustomMessageBox.OK
)
msg.exec()
sys.exit(0) # On quitte proprement sans lancer MainWindow
# 4. Lancement de l'application si tout est OK
window = MainWindow(bundle_dir, config)
# Note: Assure-toi que self.show() est bien dans le __init__ de MainWindow
# ou ajoute window.show() ici si tu l'en lèves du constructeur.
sys.exit(app.exec())

181261
src/resources.py Normal file

File diff suppressed because it is too large Load Diff

27
src/tools/utils.py Normal file
View File

@@ -0,0 +1,27 @@
import sys
from pathlib import Path
from PySide6.QtWidgets import QApplication
PROJECT_ROOT = Path(__file__).resolve().parents[2]
def get_internal_dir() -> Path:
# Retourne le chemin vers les ressources figées à l'intérieur de l'EXE (_MEIPASS).
# En mode script, retourne le dossier du fichier .py.
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
return Path(sys._MEIPASS).resolve()
return Path(__file__).resolve().parents[2]
def get_executable_dir() -> Path:
# Retourne le chemin du dossier contenant réellement le fichier .exe.
# C'est ici que se trouve votre 'config.json'.
if getattr(sys, 'frozen', False):
# sys.executable est le chemin complet vers l'application .exe
return Path(sys.executable).parent.resolve()
return Path(__file__).resolve().parents[2]
def quit_application(exit_code: int = 0) -> None:
app = QApplication.instance()
if app is not None:
app.closeAllWindows()
app.exit(exit_code)
sys.exit(exit_code)

View File

@@ -0,0 +1,133 @@
from PySide6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QWidget, QGraphicsDropShadowEffect)
from PySide6.QtCore import Qt, QPropertyAnimation, QEasingCurve
from PySide6.QtGui import QColor
class CustomMessageBox(QDialog):
# Enums pour la configuration
INFO = "info"
WARNING = "warning"
OK = "ok"
OK_CANCEL = "ok_cancel"
def __init__(self, title="Notification", message="", icon_type="info", buttons="ok", parent=None):
super().__init__(parent)
# --- CONFIGURATION FENÊTRE ---
self.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setMinimumWidth(400)
# --- ANIMATION DE FONDU ---
self.setWindowOpacity(0)
self.fade_anim = QPropertyAnimation(self, b"windowOpacity")
self.fade_anim.setDuration(350)
self.fade_anim.setStartValue(0)
self.fade_anim.setEndValue(1)
self.fade_anim.setEasingCurve(QEasingCurve.OutCubic)
# --- UI SETUP ---
self.container = QWidget(self)
self.container.setObjectName("MsgBoxMainContainer")
# Utilisé dans le fichier QSS comme condition dynamique de style
self.container.setProperty("iconType", icon_type)
self.container.setProperty("buttonsType", buttons)
# LAYOUT PRINCIPAL DU CONTAINER (Marges à 0 pour coller le bouton au bord)
layout = QVBoxLayout(self.container)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# 1. Barre de titre (Contenu collé en haut et à droite)
title_bar_layout = QHBoxLayout()
title_bar_layout.setContentsMargins(15, 0, 0, 0)
title_bar_layout.setSpacing(0)
title_label = QLabel(title.upper())
title_label.setObjectName("MsgBoxTitleLabel")
self.close_btn = QPushButton("")
self.close_btn.setObjectName("MsgBoxCloseButton")
self.close_btn.setFixedSize(45, 35)
self.close_btn.clicked.connect(self.reject)
self.close_btn.setCursor(Qt.PointingHandCursor)
title_bar_layout.addWidget(title_label)
title_bar_layout.addStretch()
title_bar_layout.addWidget(self.close_btn)
layout.addLayout(title_bar_layout)
# 2. SOUS-LAYOUT POUR LE CONTENU (Ici on remet des marges pour le texte)
body_layout = QVBoxLayout()
body_layout.setContentsMargins(20, 10, 20, 20)
body_layout.setSpacing(15)
# Contenu central (Icône + Message)
content_layout = QHBoxLayout()
icon_label = QLabel()
icon_label.setObjectName("MsgBoxIconLabel")
icon_text = "" if icon_type == self.INFO else "⚠️"
icon_label.setText(icon_text)
msg_label = QLabel(message)
msg_label.setObjectName("MsgBoxMessageLabel")
msg_label.setWordWrap(True)
content_layout.addWidget(icon_label)
content_layout.addWidget(msg_label, 1)
body_layout.addLayout(content_layout)
# Boutons d'action
btn_layout = QHBoxLayout()
btn_layout.setSpacing(10)
btn_layout.addStretch()
if buttons == self.OK_CANCEL:
self.btn_cancel = QPushButton("ANNULER")
self.btn_cancel.setObjectName("MsgBoxCancelButton")
self.btn_cancel.clicked.connect(self.reject)
btn_layout.addWidget(self.btn_cancel)
self.btn_ok = QPushButton("COMPRIS")
self.btn_ok.setObjectName("MsgBoxOkButton")
self.btn_ok.setCursor(Qt.PointingHandCursor)
self.btn_ok.clicked.connect(self.accept)
btn_layout.addWidget(self.btn_ok)
body_layout.addLayout(btn_layout)
# Ajout du body_layout dans le layout principal
layout.addLayout(body_layout)
# --- OMBRE PORTÉE ---
shadow = QGraphicsDropShadowEffect(self)
shadow.setBlurRadius(20)
shadow.setXOffset(0)
shadow.setYOffset(8)
shadow.setColor(QColor(0, 0, 0, 180))
self.container.setGraphicsEffect(shadow)
final_layout = QVBoxLayout(self)
final_layout.addWidget(self.container)
self.old_pos = None
def showEvent(self, event):
super().showEvent(event)
self.fade_anim.start()
def mousePressEvent(self, e):
# On permet le déplacement uniquement si on clique sur la barre de titre
# ou n'importe où sauf sur les boutons
if e.button() == Qt.LeftButton:
self.old_pos = e.globalPosition().toPoint()
def mouseMoveEvent(self, e):
if self.old_pos:
delta = e.globalPosition().toPoint() - self.old_pos
self.move(self.x() + delta.x(), self.y() + delta.y())
self.old_pos = e.globalPosition().toPoint()
def mouseReleaseEvent(self, e):
self.old_pos = None

75
src/ui/hazard_stripes.py Normal file
View File

@@ -0,0 +1,75 @@
# ui/hazard_stripes.py
from PySide6.QtWidgets import QPushButton, QStyleOptionButton, QStyle
from PySide6.QtGui import QPainter, QColor, QPainterPath, QPen, QPolygon
from PySide6.QtCore import Qt, QPoint
class HazardButton(QPushButton):
def __init__(self, parent=None):
super().__init__(parent)
self._hazard = False
def set_hazard(self, enabled: bool):
self._hazard = enabled
self.update()
def paintEvent(self, event):
if not hasattr(self, '_hazard'):
self.__dict__['_hazard'] = False
p = QPainter(self)
p.setRenderHint(QPainter.RenderHint.Antialiasing)
r = self.rect()
radius = 4
path = QPainterPath()
path.addRoundedRect(r, radius, radius)
p.setClipPath(path)
if self._hazard:
p.fillRect(r, QColor("#FFD700"))
p.setPen(Qt.PenStyle.NoPen)
p.setBrush(QColor("#000000"))
stripe_width = 20
stripe_gap = 30
period = stripe_width + stripe_gap
diag = r.width() + r.height()
for x in range(-diag, diag * 2, period):
stripe = QPolygon([
QPoint(x, r.bottom() + 10),
QPoint(x + stripe_width, r.bottom() + 10),
QPoint(x + stripe_width + r.height() + 10, r.top() - 10),
QPoint(x + r.height() + 10, r.top() - 10),
])
p.drawPolygon(stripe)
# ↓ Fond semi-transparent derrière le texte
text_bg = QColor(255, 215, 0, 230) # noir à 63% d'opacité
p.setPen(Qt.PenStyle.NoPen)
p.setBrush(text_bg)
bg_rect = r.adjusted(60, 8, -60, -8) # marges internes
p.drawRoundedRect(bg_rect, 4, 4)
else:
p.fillRect(r, QColor("#FFD700"))
p.setClipping(False)
p.setPen(QPen(QColor("#000000"), 2))
p.setBrush(Qt.BrushStyle.NoBrush)
p.drawRoundedRect(r.adjusted(1, 1, -1, -1), radius, radius)
p.setClipping(False)
opt = QStyleOptionButton()
self.initStyleOption(opt)
opt.palette.setColor(
opt.palette.ColorRole.ButtonText,
self.palette().color(self.palette().ColorRole.ButtonText)
)
self.style().drawControl(
QStyle.ControlElement.CE_PushButtonLabel, opt, p, self
)

167
src/ui/main_window.py Normal file
View File

@@ -0,0 +1,167 @@
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 config.config_manager import ConfigManager
from config.constants import NO_STAFF, Urls, NO_WHITELIST
from ui.custom_message_box import CustomMessageBox
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 tools.utils import quit_application
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):
def __init__(self, bundle_dir: str, config_manager: ConfigManager):
super().__init__()
self.config = config_manager
# 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)
# Test bouton en contruction
en_chantier = True
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 NO_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.spacer_substitution.hide()
if config_manager.get_discord_user() == "" or config_manager.get_discord_user().isspace():
self.ui.queue_lbl.hide()
self.ui.queue_position.hide()
self.ui.stackedWidget.setCurrentIndex(1)
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()
if NO_WHITELIST:
msg = CustomMessageBox(
title="La Tanière: Non whitelisté",
message="\n\nTu n'est pas whitelisté sur le serveur\n\n"
"Assure-toi de te faire whitelister.\n\n"
"Lorsque cela sera fait, relance le launcher.",
icon_type=CustomMessageBox.WARNING,
buttons=CustomMessageBox.OK
)
msg.exec()
quit_application()
# ------------------------------------------------------------------
# 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)
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:
pass # à implémenter
@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:
self.config.set_discord_user(discord_oauth.get_discord_user_id())
self.config.save()
self.ui.stackedWidget.setCurrentIndex(0)
# ------------------------------------------------------------------
# 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:
self.config.save()
super().closeEvent(event)

View File

@@ -1,98 +0,0 @@
QPushButton#connexion_btn {
border-radius: 15px;
background-color: rgb(255, 120, 0);
}
QPushButton#connexion_btn:hover {
background: #ffad66;
}
QPushButton#discord_btn,
QPushButton#intranet_btn
{
border-radius: 15px;
background-color: #203a43;
}
QPushButton#close_btn {
border : 0;
background: transparent;
}
QPushButton#minimize_btn {
background-color: transparent;
border: none;
border-bottom: 5px solid white;
color: white;
font-size: 18px;
padding: 0px;
padding-top: 0
}
QTextEdit#info_text {
background-color: transparent;
border: none;
}
/*
QSlider::groove:horizontal {
border: 1px solid #262626;
height: 10px;
}
QSlider::handle:horizontal {
background: rgb(236, 127, 43);
border: 1px solid rgb(236, 127, 43);
width: 23px;
border-radius: 3px;
height: 100px;
margin: -24px -12px;
}
QSlider::sub-page:horizontal{
border:0px;
border-radius:6px;
background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #12b9ff, stop: 1.0 #015eea);
}
QSlider::add-page:horizontal{
border:0px;
border-radius:6px;
background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(146, 149, 150), stop: 1.0 rgb(253, 254, 254));
}
*/
QSlider::groove:horizontal {
border: 1px inset #1C1C1C;
height: 6px;
border-radius: 3px;
}
QSlider::groove:horizontal {
border: 1px inset #1C1C1C;
height: 6px;
border-radius: 3px;
}
QSlider::sub-page:horizontal {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #12b9ff, stop: 1.0 #015eea);
border: 1px inset #1C1C1C;
border-radius: 3px;
}
/* groove background on right of slider */
QSlider::add-page:horizontal {
background: #7D7D7D;
border: 1px outset #1C1C1C;
border-radius: 3px;
}
QSlider::handle:horizontal {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgb(241, 160, 61), stop:1 rgb(233, 111, 29));
border: 1px solid rgb(213, 125, 2);
width: 12px;
height: 10px;
margin-top: -8px;
margin-bottom: -8px;
border-radius: 2px;
}

315
styles/styles.qss Normal file
View File

@@ -0,0 +1,315 @@
QLabel {
color: rgb(255, 255, 255);
}
#main_container {
border-radius: 30px;
background: qradialgradient(cx:0.5, cy:0.5, radius:0.8, fx:0.5, fy:0.5,
stop: 0 #16213e,
stop: 1 #0a0e14);
}
QFrame#logo_frame {
background-image: url(:/assets/logo.png);
background-repeat: no-repeat;
background-position: center;
}
QFrame#frame_2 QLabel {
color: rgb(163, 177, 198);
}
QFrame#frame_2 QLabel#discord_title_label {
font-size: 28px;
color: rgb(255, 255, 255) /* label enfant, obligé de définir la couleur car ne prend pas la général */
}
QFrame#frame_2 QLabel#label_2 {
/* font-family: 'sans serif'; */
}
QLabel#maintitle_label {
font-size: 38px;
}
QLabel#subtitle_label {
color: rgb(163, 177, 198);
}
QLabel#queue_position {
font-size: 36px;
color: rgb(17, 248, 183);
}
QLabel#queue_lbl {
}
QPushButton#connexion_btn[chantier="false"] {
/* Dégradé chaleureux : Orange vers Orange-Rouge */
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop: 0 #ff9d00,
stop: 1 #f56100);
border-radius: 12px;
border: 1px solid #b34700;
color: white;
padding: 10px;
}
QPushButton#connexion_btn[chantier="false"]:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop: 0 #ffb338,
stop: 1 #ff7a29);
border: 1px solid #ffcc80;
/* Un léger halo autour du bouton */
outline: none;
}
QPushButton#connexion_btn[chantier="false"]:pressed {
background: #cc5200;
padding-top: 12px; /* Effet d'enfoncement */
}
/* État normal - Rouge Corail Vibrant */
QPushButton#staff_btn {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #FF4B2B, stop:1 #FF416C);
color: white;
border-radius: 12px;
border: 1px solid #d03522;
padding: 5px 15px;
}
HazardButton#connexion_btn {
color: #0A1A3A;
/* color: #0D2A6B;*/
font-weight: bold;
}
HazardButton#connexion_btn:hover {
/* color: #ffffff;*/
color: #0D2A6B;
}
HazardButton#connexion_btn:pressed {
color: #333333;
}
QPushButton#staff_btn:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 #FF6046, stop:1 #FF527B);
border: 1px solid #FF4B2B;
}
QPushButton#staff_btn:pressed
{
background-color: #d03522;
padding-top: 7px;
padding-left: 17px;
}
QPushButton#discord_btn,
QPushButton#discord_auth_btn,
QPushButton#intranet_btn
{
background-color: rgba(32, 58, 67, 0.6); /* Bleu très sombre semi-transparent */
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: #e2e8f0;
font-weight: bold;
padding: 8px 15px;
}
QPushButton#discord_btn:hover,
QPushButton#discord_auth_btn:hover
{
background-color: rgba(88, 101, 242, 0.4); /* Fond bleu Discord translucide */
border: 2px solid #7289da; /* Bordure plus épaisse et claire pour l'éclat */
color: white;
}
QPushButton#intranet_btn:hover {
background-color: rgba(0, 242, 255, 0.15); /* Teinte turquoise légère en fond */
border: 2px solid #00f2ff; /* Bordure turquoise vive et épaisse */
color: #ffffff;
}
QPushButton#discord_btn:pressed,
QPushButton#discord_auth_btn:pressed,
QPushButton#intranet_btn:pressed
{
background-color: #0f172a;
padding-top: 10px; /* Petit effet d'enfoncement */
}
QPushButton#mute_btn {
border-radius: 15px;
background-color: rgb(255, 120, 0);
}
QPushButton#mute_btn[muted="true"] {
background-color: rgb(200, 0, 0);
}
QPushButton#mute_btn:hover {
background-color: rgb(255, 150, 40);
}
QPushButton#close_btn {
border : 0;
background: transparent;
}
QPushButton#minimize_btn {
background-color: transparent;
border: none;
border-bottom: 5px solid white;
color: white;
font-size: 18px;
padding: 0px;
padding-top: 0
}
QFrame#info_frame{
background: qlineargradient(
x1:0, y1:0,
x2:0, y2:1,
stop:0 rgba(255,255,255,30),
stop:1 rgba(255,255,255,30)
);
border-radius: 20px;
border: 1px solid rgba(255,255,255,140);
}
QTextEdit#info_text {
background-color: transparent;
border: none;
color: rgb(255, 255, 255);
}
QSlider {
height: 35px;
}
QSlider::groove:horizontal {
border: 1px solid #1C1C1C;
height: 8px;
background: #7D7D7D;
border-radius: 4px;
}
QSlider::sub-page:horizontal {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #12b9ff, stop: 1.0 #015eea);
border: 1px solid #1C1C1C;
border-radius: 4px;
}
QSlider::handle:horizontal {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FF8C00, stop:1 #FF5E00);
border: 1px solid #B34700;
width: 16px;
height: 16px;
margin: -5px 0; /* Ajusté pour centrer 16px sur barre de 8px */
border-radius: 8px; /* Moitié exacte de width/height */
}
/* On répète les propriétés cruciales pour les états survolés/pressés */
QSlider::handle:horizontal:hover {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFA500, stop:1 #FF7F00);
border-radius: 8px;
}
QSlider::handle:horizontal:pressed {
background: #E65100;
border-radius: 8px;
}
/* ----------------------------------------------
Custom Message Box
----------------------------------------------*/
QWidget#MsgBoxMainContainer {
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
QWidget#MsgBoxMainContainer[iconType="info"] {
background: qlineargradient(
x1: 0, y1: 0, x2: 1, y2: 1,
stop: 0 #101624,
stop: 1 #248277
);
}
QWidget#MsgBoxMainContainer[iconType="warning"] {
background: qlineargradient(
x1: 0, y1: 0, x2: 1, y2: 1,
stop: 0 #101624,
stop: 1 #cf5b16
);
}
QWidget#MsgBoxMainContainer QLabel,
QWidget#MsgBoxMainContainer QPushButton
{
color: white;
/*font-size: 18px;
/* font-family: 'Segoe UI';*/
}
QPushButton#MsgBoxOkButton,
QPushButton#MsgBoxCancelButton {
border-radius: 6px;
/* color: white;*/
padding: 8px 20px;
font-weight: bold;
font-size: 12px;
border: 1px solid rgba(255,255,255,0.05);
}
QPushButton#MsgBoxOkButton {
background: #248277;
}
QPushButton#MsgBoxCancelButton {
background: #2a313d;
}
QPushButton#MsgBoxOkButton:hover,
QPushButton#MsgBoxCancelButton:hover{
background: #363d4a;
border: 1px solid white;
}
QPushButton#MsgBoxCloseButton {
background: transparent;
color: white;
border: none;
font-size: 14px;
/* Rayon identique au container (15px) pour épouser parfaitement le coin */
border-top-right-radius: 15px;
border-bottom-left-radius: 10px;
}
QPushButton#MsgBoxCloseButton:hover {
background-color: #e74c3c;
color: white;
}
QLabel#MsgBoxTitleLabel {
font-weight: bold;
font-size: 14px;
color: rgba(255,255,255,0.7);
letter-spacing: 1px;
}
QLabel#MsgBoxMessageLabel {
font-size: 14px;
color: #f0f0f0;
}
QLabel#MsgBoxIconLabel {
font-size: 35px;
margin-right: 10px;
}

View File

@@ -38,12 +38,9 @@
<string>MainWindow</string>
</property>
<property name="windowIcon">
<iconset resource="resources.qrc">
<iconset>
<normaloff>:/assets/Icone.ico</normaloff>:/assets/Icone.ico</iconset>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(255, 255, 255);</string>
</property>
<widget class="QWidget" name="main_container">
<property name="minimumSize">
<size>
@@ -57,14 +54,6 @@
<height>703</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#main_container {
border-radius: 30px;
background-image: url(:/assets/background.png);
background-repeat: no-repeat;
background-position: center;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
@@ -189,7 +178,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<iconset resource="../resources.qrc">
<normaloff>:/assets/system-shutdown.png</normaloff>:/assets/system-shutdown.png</iconset>
</property>
<property name="iconSize">
@@ -292,7 +281,7 @@
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<widget class="QFrame" name="logo_frame">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
@@ -359,37 +348,25 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="queue_lbl">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string>Position en file d'attente: </string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="lcdNumber">
<property name="maximumSize">
<size>
<width>70</width>
<height>50</height>
</size>
<widget class="QLabel" name="queue_position">
<property name="text">
<string>20</string>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::RightToLeft</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Sunken</enum>
</property>
<property name="smallDecimalPoint">
<bool>false</bool>
</property>
<property name="digitCount">
<number>2</number>
</property>
<property name="value" stdset="0">
<double>20.000000000000000</double>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
@@ -446,7 +423,7 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSlider" name="horizontalSlider">
<widget class="QSlider" name="audio_volume_adjust">
<property name="autoFillBackground">
<bool>false</bool>
</property>
@@ -465,12 +442,22 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<widget class="QPushButton" name="mute_btn">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::AudioVolumeHigh"/>
<iconset resource="../resources.qrc">
<normaloff>:/assets/sound-speaker.svg</normaloff>:/assets/sound-speaker.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
@@ -577,7 +564,7 @@
<item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QLabel" name="maintitle_label">
<property name="text">
<string>la taniere</string>
<string>LA TANIÈRE</string>
</property>
</widget>
</item>
@@ -606,7 +593,7 @@
</size>
</property>
<property name="text">
<string>un espace pour se retrouver</string>
<string>Un espace pour se retrouver</string>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::AutoText</enum>
@@ -682,7 +669,17 @@
<string notr="true"/>
</property>
<property name="text">
<string>connexion</string>
<string> Connexion</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/padlock-lock.svg</normaloff>:/assets/padlock-lock.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
@@ -713,6 +710,16 @@
<property name="text">
<string>staff</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/tools-repair.svg</normaloff>:/assets/tools-repair.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
@@ -754,7 +761,17 @@
</size>
</property>
<property name="text">
<string>discord</string>
<string> Discord</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/discord-icon.svg</normaloff>:/assets/discord-icon.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
@@ -780,7 +797,17 @@
</size>
</property>
<property name="text">
<string>intranet</string>
<string> Intranet</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/computer-tv.svg</normaloff>:/assets/computer-tv.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
@@ -839,16 +866,7 @@
</size>
</property>
<property name="styleSheet">
<string notr="true">QFrame#info_frame{
background: qlineargradient(
x1:0, y1:0,
x2:0, y2:1,
stop:0 rgba(255,255,255,30),
stop:1 rgba(255,255,255,30)
);
border-radius: 20px;
border: 1px solid rgba(255,255,255,140);
}</string>
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
@@ -862,6 +880,9 @@
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::NoTextInteraction</set>
</property>
</widget>
</item>
</layout>
@@ -890,7 +911,7 @@
</widget>
</widget>
<resources>
<include location="resources.qrc"/>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

395
ui/mainwindow_ui.py Normal file
View File

@@ -0,0 +1,395 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'mainwindowdJHBSQ.ui'
##
## Created by: Qt User Interface Compiler version 6.10.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
import resources_rc
from PySide6.QtCore import (QCoreApplication, QMetaObject, QSize, Qt)
from PySide6.QtGui import (QIcon)
from PySide6.QtWidgets import (QFrame, QHBoxLayout, QLabel,
QPushButton, QSizePolicy, QSlider,
QSpacerItem, QTextEdit, QVBoxLayout, QWidget)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.setWindowModality(Qt.WindowModality.ApplicationModal)
MainWindow.resize(1199, 703)
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
MainWindow.setSizePolicy(sizePolicy)
MainWindow.setMinimumSize(QSize(1199, 703))
MainWindow.setMaximumSize(QSize(1199, 703))
MainWindow.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
icon = QIcon()
icon.addFile(u":/assets/Icone.ico", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
MainWindow.setWindowIcon(icon)
self.main_container = QWidget(MainWindow)
self.main_container.setObjectName(u"main_container")
self.main_container.setMinimumSize(QSize(1199, 703))
self.main_container.setMaximumSize(QSize(1199, 703))
self.verticalLayout = QVBoxLayout(self.main_container)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.title_bar = QFrame(self.main_container)
self.title_bar.setObjectName(u"title_bar")
self.title_bar.setMinimumSize(QSize(1199, 50))
self.title_bar.setMaximumSize(QSize(1199, 50))
self.title_bar.setFrameShape(QFrame.Shape.NoFrame)
self.title_bar.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout = QHBoxLayout(self.title_bar)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalLayout.setContentsMargins(0, 9, 0, 0)
self.horizontalSpacer = QSpacerItem(1006, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
self.minimize_btn = QPushButton(self.title_bar)
self.minimize_btn.setObjectName(u"minimize_btn")
self.minimize_btn.setMinimumSize(QSize(0, 0))
self.minimize_btn.setMaximumSize(QSize(25, 25))
self.minimize_btn.setStyleSheet(u"")
self.minimize_btn.setIconSize(QSize(32, 32))
self.horizontalLayout.addWidget(self.minimize_btn)
self.horizontalSpacer_2 = QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer_2)
self.close_btn = QPushButton(self.title_bar)
self.close_btn.setObjectName(u"close_btn")
self.close_btn.setMaximumSize(QSize(42, 42))
self.close_btn.setStyleSheet(u"")
icon1 = QIcon()
icon1.addFile(u":/assets/system-shutdown.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.close_btn.setIcon(icon1)
self.close_btn.setIconSize(QSize(32, 32))
self.horizontalLayout.addWidget(self.close_btn)
self.horizontalSpacer_3 = QSpacerItem(30, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer_3)
self.verticalLayout.addWidget(self.title_bar)
self.frame_2 = QFrame(self.main_container)
self.frame_2.setObjectName(u"frame_2")
self.frame_2.setMinimumSize(QSize(1199, 0))
self.frame_2.setMaximumSize(QSize(1199, 658))
self.frame_2.setFrameShape(QFrame.Shape.NoFrame)
self.frame_2.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_2 = QHBoxLayout(self.frame_2)
self.horizontalLayout_2.setSpacing(0)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.left_column = QFrame(self.frame_2)
self.left_column.setObjectName(u"left_column")
self.left_column.setMinimumSize(QSize(450, 630))
self.left_column.setMaximumSize(QSize(450, 630))
self.left_column.setFrameShape(QFrame.Shape.NoFrame)
self.left_column.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout_5 = QVBoxLayout(self.left_column)
self.verticalLayout_5.setSpacing(0)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.frame = QFrame(self.left_column)
self.frame.setObjectName(u"frame")
self.frame.setFrameShape(QFrame.Shape.NoFrame)
self.frame.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout_5.addWidget(self.frame)
self.frame_3 = QFrame(self.left_column)
self.frame_3.setObjectName(u"frame_3")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.frame_3.sizePolicy().hasHeightForWidth())
self.frame_3.setSizePolicy(sizePolicy1)
self.frame_3.setMinimumSize(QSize(0, 100))
self.frame_3.setFrameShape(QFrame.Shape.NoFrame)
self.frame_3.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_6 = QHBoxLayout(self.frame_3)
self.horizontalLayout_6.setSpacing(0)
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
self.frame_5 = QFrame(self.frame_3)
self.frame_5.setObjectName(u"frame_5")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.frame_5.sizePolicy().hasHeightForWidth())
self.frame_5.setSizePolicy(sizePolicy2)
self.frame_5.setMinimumSize(QSize(0, 0))
self.frame_5.setFrameShape(QFrame.Shape.NoFrame)
self.frame_5.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_8 = QHBoxLayout(self.frame_5)
self.horizontalLayout_8.setObjectName(u"horizontalLayout_8")
self.queue_lbl = QLabel(self.frame_5)
self.queue_lbl.setObjectName(u"queue_lbl")
self.queue_lbl.setAutoFillBackground(False)
self.queue_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.horizontalLayout_8.addWidget(self.queue_lbl)
self.queue_position = QLabel(self.frame_5)
self.queue_position.setObjectName(u"queue_position")
self.queue_position.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.horizontalLayout_8.addWidget(self.queue_position)
self.horizontalSpacer_5 = QSpacerItem(80, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_8.addItem(self.horizontalSpacer_5)
self.horizontalLayout_6.addWidget(self.frame_5)
self.verticalLayout_5.addWidget(self.frame_3)
self.frame_4 = QFrame(self.left_column)
self.frame_4.setObjectName(u"frame_4")
sizePolicy1.setHeightForWidth(self.frame_4.sizePolicy().hasHeightForWidth())
self.frame_4.setSizePolicy(sizePolicy1)
self.frame_4.setMinimumSize(QSize(0, 0))
self.frame_4.setMaximumSize(QSize(16777215, 50))
self.frame_4.setStyleSheet(u"")
self.frame_4.setFrameShape(QFrame.Shape.NoFrame)
self.frame_4.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_7 = QHBoxLayout(self.frame_4)
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
self.horizontalSlider = QSlider(self.frame_4)
self.horizontalSlider.setObjectName(u"horizontalSlider")
self.horizontalSlider.setAutoFillBackground(False)
self.horizontalSlider.setStyleSheet(u"")
self.horizontalSlider.setValue(20)
self.horizontalSlider.setOrientation(Qt.Orientation.Horizontal)
self.horizontalSlider.setInvertedAppearance(False)
self.horizontalLayout_7.addWidget(self.horizontalSlider)
self.volume_btn = QPushButton(self.frame_4)
self.volume_btn.setObjectName(u"volume_btn")
self.volume_btn.setMinimumSize(QSize(32, 32))
icon2 = QIcon()
icon2.addFile(u":/assets/sound-speaker.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.volume_btn.setIcon(icon2)
self.volume_btn.setFlat(True)
self.horizontalLayout_7.addWidget(self.volume_btn)
self.horizontalSpacer_10 = QSpacerItem(180, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum)
self.horizontalLayout_7.addItem(self.horizontalSpacer_10)
self.verticalLayout_5.addWidget(self.frame_4)
self.horizontalLayout_2.addWidget(self.left_column)
self.right_column = QFrame(self.frame_2)
self.right_column.setObjectName(u"right_column")
sizePolicy2.setHeightForWidth(self.right_column.sizePolicy().hasHeightForWidth())
self.right_column.setSizePolicy(sizePolicy2)
self.right_column.setMinimumSize(QSize(0, 650))
self.right_column.setMaximumSize(QSize(700, 650))
self.right_column.setFrameShape(QFrame.Shape.NoFrame)
self.right_column.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout_2 = QVBoxLayout(self.right_column)
self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.title_frame = QFrame(self.right_column)
self.title_frame.setObjectName(u"title_frame")
sizePolicy1.setHeightForWidth(self.title_frame.sizePolicy().hasHeightForWidth())
self.title_frame.setSizePolicy(sizePolicy1)
self.title_frame.setMinimumSize(QSize(0, 140))
self.title_frame.setMaximumSize(QSize(16777215, 180))
self.title_frame.setStyleSheet(u"")
self.title_frame.setFrameShape(QFrame.Shape.NoFrame)
self.title_frame.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout_3 = QVBoxLayout(self.title_frame)
self.verticalLayout_3.setSpacing(0)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalLayout_3.setContentsMargins(-1, -1, -1, 9)
self.maintitle_label = QLabel(self.title_frame)
self.maintitle_label.setObjectName(u"maintitle_label")
self.verticalLayout_3.addWidget(self.maintitle_label, 0, Qt.AlignmentFlag.AlignHCenter)
self.verticalSpacer = QSpacerItem(20, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
self.verticalLayout_3.addItem(self.verticalSpacer)
self.subtitle_label = QLabel(self.title_frame)
self.subtitle_label.setObjectName(u"subtitle_label")
self.subtitle_label.setMinimumSize(QSize(0, 0))
self.subtitle_label.setTextFormat(Qt.TextFormat.AutoText)
self.subtitle_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.verticalLayout_3.addWidget(self.subtitle_label)
self.verticalLayout_2.addWidget(self.title_frame, 0, Qt.AlignmentFlag.AlignHCenter)
self.btn_frame = QFrame(self.right_column)
self.btn_frame.setObjectName(u"btn_frame")
sizePolicy1.setHeightForWidth(self.btn_frame.sizePolicy().hasHeightForWidth())
self.btn_frame.setSizePolicy(sizePolicy1)
self.btn_frame.setMinimumSize(QSize(0, 150))
self.btn_frame.setMaximumSize(QSize(16777215, 150))
self.btn_frame.setStyleSheet(u"")
self.btn_frame.setFrameShape(QFrame.Shape.NoFrame)
self.btn_frame.setFrameShadow(QFrame.Shadow.Raised)
self.verticalLayout_4 = QVBoxLayout(self.btn_frame)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer_4)
self.connexion_btn = QPushButton(self.btn_frame)
self.connexion_btn.setObjectName(u"connexion_btn")
self.connexion_btn.setMinimumSize(QSize(250, 50))
self.connexion_btn.setMaximumSize(QSize(16777215, 16777215))
self.connexion_btn.setStyleSheet(u"")
icon3 = QIcon()
icon3.addFile(u":/assets/padlock-lock.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.connexion_btn.setIcon(icon3)
self.connexion_btn.setIconSize(QSize(32, 32))
self.horizontalLayout_3.addWidget(self.connexion_btn)
self.spacer_substitution = QWidget(self.btn_frame)
self.spacer_substitution.setObjectName(u"spacer_substitution")
self.spacer_substitution.setMinimumSize(QSize(53, 0))
self.horizontalLayout_3.addWidget(self.spacer_substitution)
self.staff_btn = QPushButton(self.btn_frame)
self.staff_btn.setObjectName(u"staff_btn")
self.staff_btn.setMinimumSize(QSize(250, 50))
self.staff_btn.setMaximumSize(QSize(16777215, 16777215))
self.horizontalLayout_3.addWidget(self.staff_btn)
self.horizontalSpacer_6 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer_6)
self.verticalLayout_4.addLayout(self.horizontalLayout_3)
self.horizontalLayout_4 = QHBoxLayout()
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_4.addItem(self.horizontalSpacer_7)
self.discord_btn = QPushButton(self.btn_frame)
self.discord_btn.setObjectName(u"discord_btn")
self.discord_btn.setMinimumSize(QSize(250, 50))
icon4 = QIcon()
icon4.addFile(u":/assets/Logo_Discord_2015.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.discord_btn.setIcon(icon4)
self.discord_btn.setIconSize(QSize(32, 32))
self.horizontalLayout_4.addWidget(self.discord_btn)
self.horizontalSpacer_8 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_4.addItem(self.horizontalSpacer_8)
self.intranet_btn = QPushButton(self.btn_frame)
self.intranet_btn.setObjectName(u"intranet_btn")
self.intranet_btn.setMinimumSize(QSize(250, 50))
icon5 = QIcon()
icon5.addFile(u":/assets/computer-tv.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.intranet_btn.setIcon(icon5)
self.intranet_btn.setIconSize(QSize(32, 32))
self.horizontalLayout_4.addWidget(self.intranet_btn)
self.horizontalSpacer_9 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_4.addItem(self.horizontalSpacer_9)
self.verticalLayout_4.addLayout(self.horizontalLayout_4)
self.verticalLayout_2.addWidget(self.btn_frame)
self.verticalSpacer_3 = QSpacerItem(20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
self.verticalLayout_2.addItem(self.verticalSpacer_3)
self.info_frame = QFrame(self.right_column)
self.info_frame.setObjectName(u"info_frame")
sizePolicy1.setHeightForWidth(self.info_frame.sizePolicy().hasHeightForWidth())
self.info_frame.setSizePolicy(sizePolicy1)
self.info_frame.setMinimumSize(QSize(0, 310))
self.info_frame.setMaximumSize(QSize(16777215, 280))
self.info_frame.setStyleSheet(u"")
self.info_frame.setFrameShape(QFrame.Shape.StyledPanel)
self.info_frame.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_5 = QHBoxLayout(self.info_frame)
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
self.info_text = QTextEdit(self.info_frame)
self.info_text.setObjectName(u"info_text")
self.info_text.setReadOnly(True)
self.horizontalLayout_5.addWidget(self.info_text)
self.verticalLayout_2.addWidget(self.info_frame)
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.verticalLayout_2.addItem(self.verticalSpacer_2)
self.horizontalLayout_2.addWidget(self.right_column)
self.verticalLayout.addWidget(self.frame_2)
MainWindow.setCentralWidget(self.main_container)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.minimize_btn.setText("")
self.close_btn.setText("")
self.queue_lbl.setText(QCoreApplication.translate("MainWindow", u"Position en file d'attente: ", None))
self.queue_position.setText(QCoreApplication.translate("MainWindow", u"20", None))
self.volume_btn.setText("")
self.maintitle_label.setText(QCoreApplication.translate("MainWindow", u"LA TANI\u00c8RE", None))
self.subtitle_label.setText(QCoreApplication.translate("MainWindow", u"Un espace pour se retrouver", None))
self.connexion_btn.setText(QCoreApplication.translate("MainWindow", u" Connexion", None))
self.staff_btn.setText(QCoreApplication.translate("MainWindow", u"staff", None))
self.discord_btn.setText(QCoreApplication.translate("MainWindow", u" Discord", None))
self.intranet_btn.setText(QCoreApplication.translate("MainWindow", u" Intranet", None))
# retranslateUi

885
ui/mainwindow_vertical.ui Normal file
View File

@@ -0,0 +1,885 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="windowModality">
<enum>Qt::WindowModality::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1199</width>
<height>703</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1199</width>
<height>703</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1199</width>
<height>703</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ContextMenuPolicy::NoContextMenu</enum>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>:/assets/Icone.ico</normaloff>:/assets/Icone.ico</iconset>
</property>
<widget class="QWidget" name="main_container">
<property name="minimumSize">
<size>
<width>1199</width>
<height>703</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1199</width>
<height>703</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="title_bar">
<property name="minimumSize">
<size>
<width>1199</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1199</width>
<height>50</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1006</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="minimize_btn">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="close_btn">
<property name="maximumSize">
<size>
<width>42</width>
<height>42</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/system-shutdown.png</normaloff>:/assets/system-shutdown.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="minimumSize">
<size>
<width>1199</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1199</width>
<height>658</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="left_column">
<property name="minimumSize">
<size>
<width>450</width>
<height>630</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>450</width>
<height>630</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="logo_frame">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="queue_lbl">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string>Position en file d'attente: </string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="queue_position">
<property name="text">
<string>20</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSlider" name="audio_volume_adjust">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mute_btn">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/sound-speaker.svg</normaloff>:/assets/sound-speaker.svg</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>180</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="right_column">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>650</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>700</width>
<height>650</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QFrame" name="title_frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>140</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>180</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item alignment="Qt::AlignmentFlag::AlignHCenter">
<widget class="QLabel" name="maintitle_label">
<property name="text">
<string>LA TANIÈRE</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="subtitle_label">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Un espace pour se retrouver</string>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::AutoText</enum>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="btn_frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>150</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="connexion_btn">
<property name="minimumSize">
<size>
<width>250</width>
<height>50</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string> Connexion</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/padlock-lock.svg</normaloff>:/assets/padlock-lock.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>15</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="staff_btn">
<property name="minimumSize">
<size>
<width>250</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>staff</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/tools-repair.svg</normaloff>:/assets/tools-repair.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QPushButton" name="discord_btn">
<property name="minimumSize">
<size>
<width>250</width>
<height>50</height>
</size>
</property>
<property name="text">
<string> Discord</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/discord-icon.svg</normaloff>:/assets/discord-icon.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>15</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="intranet_btn">
<property name="minimumSize">
<size>
<width>250</width>
<height>50</height>
</size>
</property>
<property name="text">
<string> Intranet</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/assets/computer-tv.svg</normaloff>:/assets/computer-tv.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="info_frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>310</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>280</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QTextEdit" name="info_text">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::NoTextInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

File diff suppressed because it is too large Load Diff

74
uv.lock generated
View File

@@ -1,74 +0,0 @@
version = 1
revision = 3
requires-python = ">=3.14"
[[package]]
name = "myproject"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "pyside6" },
]
[package.metadata]
requires-dist = [{ name = "pyside6", specifier = ">=6.10.2" }]
[[package]]
name = "pyside6"
version = "6.10.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyside6-addons" },
{ name = "pyside6-essentials" },
{ name = "shiboken6" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/35/0f/5736889fc850794623692cb369e295a994175e51295fa52134626f486296/pyside6-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:4b084293caa7845d0064aaf6af258e0f7caae03a14a33537d0a552131afddaf0", size = 563185, upload-time = "2026-02-02T08:50:47.161Z" },
{ url = "https://files.pythonhosted.org/packages/35/d3/ab5cd2fac3d34469c7376e0cd18eec92905dbe44748c70bda7699a2a7206/pyside6-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1b89ce8558d4b4f35b85bff1db90d680912e4d3ce9e79ff804d6fef1d1a151ef", size = 563357, upload-time = "2026-02-02T08:50:48.919Z" },
{ url = "https://files.pythonhosted.org/packages/ea/8c/55bbd50c138c8dc12edc9f25e9d94760a33e574905468e98dff399094baa/pyside6-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:0439f5e9b10ebe6177981bac9e219096ec970ac6ec215bef055279802ba50601", size = 563357, upload-time = "2026-02-02T08:50:50.077Z" },
{ url = "https://files.pythonhosted.org/packages/4f/d4/673b8112b4a260377f760be835c4e357163fdaf68a56a1aec59aeb8e584b/pyside6-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:032bad6b18a17fcbf4dddd0397f49b07f8aae7f1a45b7e4de7037bf7fd6e0edf", size = 569554, upload-time = "2026-02-02T08:50:51.147Z" },
{ url = "https://files.pythonhosted.org/packages/14/95/bda648fcccf61fe58cb417284716ae30acdddd44f7d4cbad6eea4ccaa872/pyside6-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:65a59ad0bc92525639e3268d590948ce07a80ee97b55e7a9200db41d493cac31", size = 553828, upload-time = "2026-02-02T08:50:52.244Z" },
]
[[package]]
name = "pyside6-addons"
version = "6.10.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyside6-essentials" },
{ name = "shiboken6" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/61/06/c283567628ffa2cefc3c72374ad607f1dfc9842a03db65f1347b9ae52bee/pyside6_addons-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:0de7d0c9535e17d5e3b634b61314a1867f3b0f6d35c3d7cdc99efc353192faff", size = 322745605, upload-time = "2026-02-02T08:39:19.929Z" },
{ url = "https://files.pythonhosted.org/packages/a5/69/e1ab8c756fd3984b1fd7b186446227f524f6b561160bfbfdba8874b4709a/pyside6_addons-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:030a851163b51dbf0063be59e9ddb6a9e760bde89a28e461ccc81a224d286eaf", size = 170718434, upload-time = "2026-02-02T08:40:55.989Z" },
{ url = "https://files.pythonhosted.org/packages/df/e5/18ba86ba86d1231c486d36f9accfe862ed6eb52ca0b698aeaf6e837a87ca/pyside6_addons-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:fcee0373e3fd7b98f014094e5e37b4a39e4de7c5a47c13f654a7d557d4a426ad", size = 166423836, upload-time = "2026-02-02T08:42:44.918Z" },
{ url = "https://files.pythonhosted.org/packages/99/13/503bec9201881968c372cb634069535e80aec2489f3907d676e151a1023f/pyside6_addons-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:c20150068525a17494f3b6576c5d61c417cf9a5870659e29f5ebd83cd20a78ea", size = 164712775, upload-time = "2026-02-02T08:43:23.729Z" },
{ url = "https://files.pythonhosted.org/packages/b6/39/44d6710b4dd18d745077b5fc6ded4ba6f32987a6e49c5834529e50f02155/pyside6_addons-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:3d18db739b46946ba7b722d8ad4cc2097135033aa6ea57076e64d591e6a345f3", size = 34041396, upload-time = "2026-02-02T08:43:31.246Z" },
]
[[package]]
name = "pyside6-essentials"
version = "6.10.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "shiboken6" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/1d/2e/5f18a77f5e0bd730bacec93a690d0ef3c96a9711d213653eacecbf241b8d/pyside6_essentials-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:1dee2cb9803ff135f881dadeb5c0edcef793d1ec4f8a9140a1348cecb71074e1", size = 105913067, upload-time = "2026-02-02T08:45:37.508Z" },
{ url = "https://files.pythonhosted.org/packages/99/20/3a6ca95052e1744b5a3eba164e2dd451d358a3dcaf78179de4b45c8e3f47/pyside6_essentials-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:660aea45bfa36f1e06f799b934c2a7df963bd31abc5083e8bb8a5bfaef45686b", size = 77027153, upload-time = "2026-02-02T08:45:53.09Z" },
{ url = "https://files.pythonhosted.org/packages/93/a6/6073e4ddc2a5c7b3941606e4bc8bbaadcf0737f57450620b0793041c8d22/pyside6_essentials-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:c2b028e4c6f8047a02c31f373408e23b4eedfd405f56c6aba8d0525c29472835", size = 76114242, upload-time = "2026-02-02T08:46:07.184Z" },
{ url = "https://files.pythonhosted.org/packages/22/a8/616bbbd009efd3e17bf9a2db09d90c6764c010565cd2bdea2a240bfd18f7/pyside6_essentials-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:0741018c2b6395038cad4c41775cfae3f13a409e87995ac9f7d89e5b1fb6b22a", size = 74546490, upload-time = "2026-02-02T08:46:26.395Z" },
{ url = "https://files.pythonhosted.org/packages/b9/f9/c9757a984c4ffb6d12fab69e966d95dfc862a5d44e12b7900f3a03780b76/pyside6_essentials-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:db5f4913648bb6afddb8b347edae151ee2378f12bceb03c8b2515a530a4b38d9", size = 55258626, upload-time = "2026-02-02T08:46:36.788Z" },
]
[[package]]
name = "shiboken6"
version = "6.10.2"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/38/3912eb08a3b865b5fcdb4bdce8076cacc211986cee587f5cb62e637791af/shiboken6-6.10.2-cp39-abi3-macosx_13_0_universal2.whl", hash = "sha256:3bd4e94e9a3c8c1fa8362fd752d399ef39265d5264e4e37bae61cdaa2a00c8c7", size = 479829, upload-time = "2026-02-02T08:50:22.495Z" },
{ url = "https://files.pythonhosted.org/packages/52/88/292e0576489c46624ab419ee284ac5a59ae10e2eb34a58b6abca51dfd290/shiboken6-6.10.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ace0790032d9cb0adda644b94ee28d59410180d9773643bb6cf8438c361987ad", size = 273052, upload-time = "2026-02-02T08:50:24.539Z" },
{ url = "https://files.pythonhosted.org/packages/06/c2/03d44d34e8264e1f25671677fece95b414c70fd85dcc2be8d5e821ee2628/shiboken6-6.10.2-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:f74d3ed1f92658077d0630c39e694eb043aeb1d830a5d275176c45d07147427f", size = 269868, upload-time = "2026-02-02T08:50:25.662Z" },
{ url = "https://files.pythonhosted.org/packages/71/5d/5ca52c0ef86b3d01572131b6709bd531a080995f7e680720e9424328ce1d/shiboken6-6.10.2-cp39-abi3-win_amd64.whl", hash = "sha256:10f3c8c5e1b8bee779346f21c10dbc14cff068f0b0b4e62420c82a6bf36ac2e7", size = 1222052, upload-time = "2026-02-02T08:50:27.502Z" },
{ url = "https://files.pythonhosted.org/packages/46/52/421fd378313c89b67ee7d584bf4e9ec088fa1804891b8d74e02b16703457/shiboken6-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:20c671645d70835af212ee05df60361d734c5305edb2746e9875c6a31283f963", size = 1784089, upload-time = "2026-02-02T08:50:29.069Z" },
]