Compare commits
40 Commits
567901b205
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b7ef4c951 | |||
| 82679a5709 | |||
| 93c37a905b | |||
| 945abae5f1 | |||
| 720b004eca | |||
| 54aa7a50b2 | |||
| 1125e5ee10 | |||
| 5d2282def1 | |||
| 968d88c18d | |||
| 8a3e487df6 | |||
| 8a87fe38c8 | |||
| 32d8185f66 | |||
| 484809356f | |||
| de8486662e | |||
| 97520e06e5 | |||
| 54a8d5c40f | |||
| 5a6470511a | |||
| e922825481 | |||
| 5b065dddfc | |||
| fd4cf5e51f | |||
| 5cfecdf856 | |||
| c1d3b98949 | |||
| a4ba0e29e8 | |||
| 3af8c97bc5 | |||
| a492a39a8c | |||
| 93e30ee178 | |||
| d877a5cc91 | |||
| 8e22b69f81 | |||
| d0ede2acd5 | |||
| e1b32688b4 | |||
| a6d4a708d8 | |||
| 983d584015 | |||
| 13b591a157 | |||
| 61e4e58ccf | |||
| 83200c4932 | |||
| b2ba3964d3 | |||
| c532fbe137 | |||
| c73756d3d7 | |||
| 6bfcec32ce | |||
| 37e815f42a |
6
.gitignore
vendored
@@ -206,9 +206,9 @@ cython_debug/
|
|||||||
.abstra/
|
.abstra/
|
||||||
|
|
||||||
# Visual Studio Code
|
# Visual Studio Code
|
||||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
# 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
|
# 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,
|
# 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
|
# you could uncomment the following to ignore the entire vscode folder
|
||||||
# .vscode/
|
# .vscode/
|
||||||
|
|
||||||
@@ -399,3 +399,5 @@ http-client.private.env.json
|
|||||||
|
|
||||||
# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
|
# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
|
||||||
.idea/**/copilot.data.migration.*.xml
|
.idea/**/copilot.data.migration.*.xml
|
||||||
|
src/config.json
|
||||||
|
config.json
|
||||||
|
|||||||
30
.vscode/launch.json
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
24
.vscode/settings.json
vendored
@@ -1,6 +1,26 @@
|
|||||||
{
|
{
|
||||||
"python.analysis.autoImportCompletions": true,
|
"python.analysis.autoImportCompletions": true,
|
||||||
"python.analysis.extraPaths": [".venv/lib/python3.11/site-packages"],
|
"python.analysis.extraPaths": [".venv/lib/site-packages"],
|
||||||
"python.analysis.fixAll": ["source.unusedImports"],
|
"python.analysis.fixAll": ["source.unusedImports"],
|
||||||
"python.languageServer": "Pylance"
|
"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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
# -*- mode: python ; coding: utf-8 -*-
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
a = Analysis(
|
a = Analysis(
|
||||||
['src\\mainwindow.py'],
|
['src\\main.py'],
|
||||||
pathex=[],
|
pathex=['src'],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[
|
datas=[
|
||||||
('.\\styles\\styles.qss', 'styles'),
|
('.\\styles\\styles.qss', 'styles'),
|
||||||
('.\\ui\\mainwindow.ui', 'ui')
|
('.\\ui\\mainwindow_vertical_pager.ui', 'ui')
|
||||||
|
],
|
||||||
|
hiddenimports=[
|
||||||
|
"asyncio",
|
||||||
|
"pypresence",
|
||||||
|
"pypresence.baseclient",
|
||||||
],
|
],
|
||||||
hiddenimports=[],
|
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
hooksconfig={
|
hooksconfig={
|
||||||
"qt_plugins": ["platforms", "styles"]
|
"qt_plugins": ["platforms", "styles"]
|
||||||
},
|
},
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
excludes=[
|
excludes=[
|
||||||
# PySide6 - modules non utilisés
|
# PySide6 - modules non utilisés
|
||||||
'PySide6.Qt3DAnimation',
|
'PySide6.Qt3DAnimation',
|
||||||
'PySide6.Qt3DCore',
|
'PySide6.Qt3DCore',
|
||||||
'PySide6.Qt3DExtras',
|
'PySide6.Qt3DExtras',
|
||||||
@@ -31,7 +34,7 @@ a = Analysis(
|
|||||||
'PySide6.QtDesigner',
|
'PySide6.QtDesigner',
|
||||||
'PySide6.QtHelp',
|
'PySide6.QtHelp',
|
||||||
'PySide6.QtLocation',
|
'PySide6.QtLocation',
|
||||||
# 'PySide6.QtMultimedia',
|
# 'PySide6.QtMultimedia', # Nécessaire pour l'audio
|
||||||
'PySide6.QtMultimediaWidgets',
|
'PySide6.QtMultimediaWidgets',
|
||||||
# 'PySide6.QtNetwork', # Dépendance à QtMultimedia
|
# 'PySide6.QtNetwork', # Dépendance à QtMultimedia
|
||||||
'PySide6.QtNetworkAuth',
|
'PySide6.QtNetworkAuth',
|
||||||
@@ -66,38 +69,168 @@ a = Analysis(
|
|||||||
'PySide6.QtWebSockets',
|
'PySide6.QtWebSockets',
|
||||||
'PySide6.QtXml',
|
'PySide6.QtXml',
|
||||||
|
|
||||||
# Stdlib inutile en prod
|
# Tests / dev tools
|
||||||
'unittest',
|
"test",
|
||||||
'email',
|
"tests",
|
||||||
'html',
|
"unittest",
|
||||||
'http',
|
"doctest",
|
||||||
'xmlrpc',
|
"pydoc",
|
||||||
'pydoc',
|
"pydoc_data",
|
||||||
'doctest',
|
|
||||||
'difflib',
|
# Packaging / build tooling
|
||||||
'tkinter',
|
"distutils",
|
||||||
'curses',
|
"setuptools",
|
||||||
'readline',
|
"pkg_resources",
|
||||||
'xml',
|
"pip",
|
||||||
'xmlrpc',
|
"ensurepip",
|
||||||
'csv',
|
|
||||||
'multiprocessing',
|
# GUI stdlib inutiles
|
||||||
'concurrent',
|
"tkinter",
|
||||||
'asyncio',
|
"turtle",
|
||||||
'sqlite3',
|
"idlelib",
|
||||||
'ssl',
|
"curses",
|
||||||
'socket',
|
|
||||||
'ctypes',
|
# Legacy / obsolete
|
||||||
'lib2to3',
|
"lib2to3",
|
||||||
'test',
|
"2to3",
|
||||||
'distutils',
|
"nis",
|
||||||
'setuptools',
|
"ossaudiodev",
|
||||||
'pkg_resources',
|
"spwd",
|
||||||
'pip',
|
|
||||||
|
# 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,
|
noarchive=False,
|
||||||
optimize=2, # optimisation bytecode
|
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)
|
pyz = PYZ(a.pure, a.zipped_data)
|
||||||
|
|
||||||
exe = EXE(
|
exe = EXE(
|
||||||
@@ -109,7 +242,7 @@ exe = EXE(
|
|||||||
name='La Tanière Launcher',
|
name='La Tanière Launcher',
|
||||||
debug=False,
|
debug=False,
|
||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
strip=False, # True = enlève symboles debug
|
strip=False,
|
||||||
upx=True,
|
upx=True,
|
||||||
upx_exclude=[
|
upx_exclude=[
|
||||||
"_uuid.pyd",
|
"_uuid.pyd",
|
||||||
@@ -129,5 +262,5 @@ exe = EXE(
|
|||||||
target_arch=None,
|
target_arch=None,
|
||||||
codesign_identity=None,
|
codesign_identity=None,
|
||||||
entitlements_file=None,
|
entitlements_file=None,
|
||||||
icon=['assets\\Icone.ico'],
|
icon=['assets\\Icon.ico'],
|
||||||
)
|
)
|
||||||
|
|||||||
BIN
assets/Icon.ico
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/Icone.ico
|
Before Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 12 KiB |
23
assets/closed-store-info.svg
Normal 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 |
8
assets/discord-icon.svg
Normal 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
@@ -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 |
21
assets/open-store-info.svg
Normal 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 |
BIN
assets/the-beat-of-nature.mp3
Normal file
36
assets/tools-repair.svg
Normal 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,17 +1,21 @@
|
|||||||
altgraph==0.17.5
|
altgraph==0.17.5
|
||||||
librt==0.8.1
|
|
||||||
modulegraph==0.19.7
|
|
||||||
mypy==1.19.1
|
|
||||||
mypy_extensions==1.1.0
|
|
||||||
packaging==26.0
|
packaging==26.0
|
||||||
pathspec==1.0.4
|
|
||||||
pefile==2024.8.26
|
pefile==2024.8.26
|
||||||
pyinstaller==6.19.0
|
pyinstaller==6.19.0
|
||||||
pyinstaller-hooks-contrib==2026.2
|
pyinstaller-hooks-contrib==2026.3
|
||||||
PySide6==6.10.2
|
|
||||||
PySide6_Addons==6.10.2
|
PySide6_Addons==6.10.2
|
||||||
PySide6_Essentials==6.10.2
|
PySide6_Essentials==6.10.2
|
||||||
pywin32-ctypes==0.2.3
|
pywin32-ctypes==0.2.3
|
||||||
setuptools==82.0.0
|
setuptools==82.0.1
|
||||||
shiboken6==6.10.2
|
shiboken6==6.10.2
|
||||||
typing_extensions==4.15.0
|
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
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>assets/the-beat-of-nature-122841.mp3</file>
|
<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/sound-speaker.svg</file>
|
||||||
|
<file>assets/the-beat-of-nature.mp3</file>
|
||||||
<file>assets/computer-tv.svg</file>
|
<file>assets/computer-tv.svg</file>
|
||||||
<file>assets/padlock-lock.svg</file>
|
<file>assets/padlock-lock.svg</file>
|
||||||
<file>assets/logo.png</file>
|
<file>assets/logo.png</file>
|
||||||
<file>assets/Logo_Discord_2015.png</file>
|
|
||||||
<file>assets/system-shutdown.png</file>
|
<file>assets/system-shutdown.png</file>
|
||||||
|
<file>assets/discord-icon.svg</file>
|
||||||
<file>assets/Avocado-Cake-Demo.otf</file>
|
<file>assets/Avocado-Cake-Demo.otf</file>
|
||||||
<file>assets/background.png</file>
|
|
||||||
<file>assets/Icone.ico</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
137
src/config/config_manager.py
Normal 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
@@ -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
|
||||||
86
src/controllers/audio_controller.py
Normal 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)
|
||||||
32
src/controllers/glow_animator.py
Normal 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)
|
||||||
25
src/controllers/window_dragger.py
Normal 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
|
||||||
84
src/discord/discord_oauth.py
Normal 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"]
|
||||||
53
src/discord/discord_tools.py
Normal 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
|
||||||
@@ -4,4 +4,10 @@ patch_note = (
|
|||||||
" 🏫 De l'auto école: nouvelle emplacement et nouveau circuit\n\n"
|
" 🏫 De l'auto école: nouvelle emplacement et nouveau circuit\n\n"
|
||||||
" 🚗 Du concessionnaire\n\n"
|
" 🚗 Du concessionnaire\n\n"
|
||||||
" 📒 Module de saisie pour le SAPD\n\n"
|
" 📒 Module de saisie pour le SAPD\n\n"
|
||||||
)
|
"\n\n"
|
||||||
|
"## 🚧 Modifications\n"
|
||||||
|
" \n\n"
|
||||||
|
" 🏫 De l'auto école: nouvelle emplacement et nouveau circuit\n\n"
|
||||||
|
" 🚗 Du concessionnaire\n\n"
|
||||||
|
" 📒 Module de saisie pour le SAPD\n\n"
|
||||||
|
)
|
||||||
|
|||||||
76
src/fivemserver/get_server_token.py
Normal 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"]
|
||||||
23
src/fivemserver/whitelistmanager.py
Normal 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
@@ -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())
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import webbrowser
|
|
||||||
from pathlib import Path
|
|
||||||
from PySide6 import QtGui
|
|
||||||
from PySide6.QtCore import Qt, QPropertyAnimation, QEasingCurve, QResource, QFile, QBuffer, QByteArray, QIODevice
|
|
||||||
from PySide6.QtGui import QFontDatabase, QFont, QColor
|
|
||||||
from PySide6.QtUiTools import QUiLoader
|
|
||||||
from PySide6.QtWidgets import QMainWindow, QApplication, QGraphicsDropShadowEffect
|
|
||||||
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
|
|
||||||
|
|
||||||
# Compile resources.qrc into resources_rc.py
|
|
||||||
# rcc -g python .\resources.qrc -o .\src\resources_rc.py
|
|
||||||
|
|
||||||
import resources as resources # This is generated from the .qrc file # noqa: F401
|
|
||||||
|
|
||||||
# Remove this into final release
|
|
||||||
from fake_patch_notes import patch_note
|
|
||||||
|
|
||||||
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
|
||||||
bundle_dir = Path(sys._MEIPASS)
|
|
||||||
else:
|
|
||||||
bundle_dir = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
# charger le fichier rcc compilé
|
|
||||||
QResource.registerResource(f"{bundle_dir}/resources.py")
|
|
||||||
|
|
||||||
NO_STAFF = True
|
|
||||||
CURRENT = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
|
|
||||||
def load_custom_font() -> str:
|
|
||||||
# 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):
|
|
||||||
super().__init__()
|
|
||||||
self.ui = QUiLoader().load(f"{bundle_dir}/ui/mainwindow.ui", self)
|
|
||||||
central = self.ui.centralWidget()
|
|
||||||
self.setCentralWidget(central)
|
|
||||||
|
|
||||||
# # 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)
|
|
||||||
|
|
||||||
# Initialize audio
|
|
||||||
self.media_player = QMediaPlayer()
|
|
||||||
self.audio_output = QAudioOutput()
|
|
||||||
self.media_player.setAudioOutput(self.audio_output)
|
|
||||||
|
|
||||||
# Using mp3 in resource
|
|
||||||
mp3file = QFile(":/assets/the-beat-of-nature-122841.mp3")
|
|
||||||
mp3file.open(QFile.ReadOnly)
|
|
||||||
mp3data = mp3file.readAll()
|
|
||||||
mp3file.close()
|
|
||||||
|
|
||||||
# buffer mémoire
|
|
||||||
mp3buffer = QBuffer()
|
|
||||||
mp3buffer.setData(QByteArray(mp3data))
|
|
||||||
mp3buffer.open(QIODevice.ReadOnly)
|
|
||||||
self.mp3buffer = mp3buffer
|
|
||||||
|
|
||||||
self.media_player.setSourceDevice(self.mp3buffer)
|
|
||||||
|
|
||||||
# Optionnel : Définir la valeur initiale du slider (ici: 10%)
|
|
||||||
self.ui.audio_volume_adjust.setValue(10)
|
|
||||||
self.audio_output.setVolume(0.1)
|
|
||||||
|
|
||||||
# Initialisation de la mémoire du volume (par défaut 10%)
|
|
||||||
self.previous_volume = 10
|
|
||||||
self.is_muted = False
|
|
||||||
|
|
||||||
# Connexion du Slider
|
|
||||||
self.ui.audio_volume_adjust.valueChanged.connect(self.update_volume)
|
|
||||||
|
|
||||||
# Lancer la lecture (par exemple ici, ou dans une fonction)
|
|
||||||
self.media_player.play()
|
|
||||||
|
|
||||||
# Track mouse position for dragging
|
|
||||||
self._drag_pos = None
|
|
||||||
|
|
||||||
if NO_STAFF :
|
|
||||||
self.ui.staff_btn.hide()
|
|
||||||
self.ui.spacer_substitution.hide()
|
|
||||||
|
|
||||||
self.ui.info_text.setMarkdown(patch_note)
|
|
||||||
|
|
||||||
# 1. Création de l'effet de lueur intense
|
|
||||||
self.ui.connexion_btn.glow = QGraphicsDropShadowEffect(self.ui.connexion_btn)
|
|
||||||
self.ui.connexion_btn.glow.setBlurRadius(20) # Point de départ
|
|
||||||
self.ui.connexion_btn.glow.setOffset(0, 0) # Centré
|
|
||||||
self.ui.connexion_btn.glow.setColor(QColor(255, 140, 0, 255)) # Orange pur (Alpha max)
|
|
||||||
#self.ui.connexion_btn.setGraphicsEffect(self.ui.connexion_btn.glow)
|
|
||||||
|
|
||||||
# 2. Animation du "Pulse" avec une grande amplitude
|
|
||||||
self.ui.connexion_btn.pulse_anim = QPropertyAnimation(self.ui.connexion_btn.glow, b"blurRadius")
|
|
||||||
self.ui.connexion_btn.pulse_anim.setDuration(1200) # Un peu plus rapide pour le dynamisme
|
|
||||||
self.ui.connexion_btn.pulse_anim.setStartValue(15) # Lueur de base
|
|
||||||
self.ui.connexion_btn.pulse_anim.setKeyValueAt(0.5, 70) # Lueur d'explosion (très large)
|
|
||||||
self.ui.connexion_btn.pulse_anim.setEndValue(15)
|
|
||||||
|
|
||||||
self.ui.connexion_btn.pulse_anim.setEasingCurve(QEasingCurve.InOutQuad)
|
|
||||||
self.ui.connexion_btn.pulse_anim.setLoopCount(-1)
|
|
||||||
#self.ui.connexion_btn.pulse_anim.start()
|
|
||||||
|
|
||||||
# Find the button by its objectName in Qt Designer
|
|
||||||
# Example: objectName = "close_btn"
|
|
||||||
self.ui.close_btn.clicked.connect(self.close)
|
|
||||||
self.ui.minimize_btn.clicked.connect(self.showMinimized)
|
|
||||||
self.ui.connexion_btn.clicked.connect(self.connexion_btn_link)
|
|
||||||
self.ui.discord_btn.clicked.connect(self.discord_btn_link)
|
|
||||||
self.ui.intranet_btn.clicked.connect(self.intranet_btn_link)
|
|
||||||
self.ui.mute_btn.clicked.connect(self.mute_btn_link)
|
|
||||||
|
|
||||||
#self.show()
|
|
||||||
|
|
||||||
# On centre la fenêtre avant de l'afficher
|
|
||||||
self.center_window()
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def center_window(self):
|
|
||||||
# On s'assure que la fenêtre a calculé sa taille
|
|
||||||
self.adjustSize()
|
|
||||||
|
|
||||||
screen = QtGui.QGuiApplication.screenAt(QtGui.QCursor.pos())
|
|
||||||
if not screen:
|
|
||||||
screen = QtGui.QGuiApplication.primaryScreen()
|
|
||||||
|
|
||||||
screen_geometry = screen.availableGeometry()
|
|
||||||
|
|
||||||
# On utilise frameGeometry() de la fenêtre elle-même
|
|
||||||
window_rect = self.frameGeometry()
|
|
||||||
window_rect.moveCenter(screen_geometry.center())
|
|
||||||
|
|
||||||
self.move(window_rect.topLeft())
|
|
||||||
|
|
||||||
# Mouse press event to start dragging
|
|
||||||
def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
|
|
||||||
if event.button() == Qt.MouseButton.LeftButton:
|
|
||||||
self._drag_pos = event.globalPosition().toPoint() - self.frameGeometry().topLeft()
|
|
||||||
super().mousePressEvent(event)
|
|
||||||
|
|
||||||
# Mouse move event to drag window
|
|
||||||
def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
|
|
||||||
if event.buttons() & Qt.MouseButton.LeftButton and self._drag_pos is not None:
|
|
||||||
self.move(event.globalPosition().toPoint() - self._drag_pos)
|
|
||||||
super().mouseMoveEvent(event)
|
|
||||||
|
|
||||||
# Mouse release event to stop dragging
|
|
||||||
def mouseReleaseEvent(self, event):
|
|
||||||
self._drag_pos = None
|
|
||||||
super().mouseReleaseEvent(event)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def connexion_btn_link():
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def discord_btn_link() -> None:
|
|
||||||
webbrowser.open('https://discord.gg/A7eanmSkp2')
|
|
||||||
return None
|
|
||||||
|
|
||||||
def intranet_btn_link(self) -> None:
|
|
||||||
webbrowser.open('https://la-taniere.fun/connexion/')
|
|
||||||
self.ui.connexion_btn.setGraphicsEffect(self.ui.connexion_btn.glow)
|
|
||||||
self.ui.connexion_btn.pulse_anim.start()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def update_volume(self, value):
|
|
||||||
# 'value' est l'entier venant du slider (ex: 0 à 100)
|
|
||||||
# On convertit en float pour QAudioOutput (0.0 à 1.0)
|
|
||||||
volume = value / 100.0
|
|
||||||
self.audio_output.setVolume(volume)
|
|
||||||
|
|
||||||
def mute_btn_link(self) -> None:
|
|
||||||
if not self.is_muted:
|
|
||||||
# --- PASSAGE EN MUTE ---
|
|
||||||
# On sauvegarde la valeur actuelle du slider
|
|
||||||
self.previous_volume = self.ui.audio_volume_adjust.value()
|
|
||||||
|
|
||||||
# On met le volume à 0 (moteur audio + interface)
|
|
||||||
self.audio_output.setVolume(0.0)
|
|
||||||
self.ui.audio_volume_adjust.setValue(0)
|
|
||||||
|
|
||||||
# Optionnel : changer l'icône ou le style du bouton
|
|
||||||
self.ui.mute_btn.setStyleSheet("background-color: red;")
|
|
||||||
|
|
||||||
self.is_muted = True
|
|
||||||
else:
|
|
||||||
# --- RETOUR DU SON ---
|
|
||||||
# On restaure le volume précédent
|
|
||||||
volume_float = self.previous_volume / 100.0
|
|
||||||
self.audio_output.setVolume(volume_float)
|
|
||||||
self.ui.audio_volume_adjust.setValue(self.previous_volume)
|
|
||||||
|
|
||||||
# Optionnel : remettre le style d'origine
|
|
||||||
self.ui.mute_btn.setStyleSheet("")
|
|
||||||
|
|
||||||
self.is_muted = False
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
|
|
||||||
with open(f"{bundle_dir}/styles/styles.qss", 'r') as f:
|
|
||||||
style = f.read()
|
|
||||||
|
|
||||||
# Load and set the global font
|
|
||||||
custom_font = QFont(load_custom_font(), 16)
|
|
||||||
if custom_font:
|
|
||||||
app.setFont(custom_font)
|
|
||||||
|
|
||||||
# Set the stylesheet of the application
|
|
||||||
app.setStyleSheet(style)
|
|
||||||
|
|
||||||
window = MainWindow()
|
|
||||||
|
|
||||||
sys.exit(app.exec())
|
|
||||||
533933
src/resources.py
27
src/tools/utils.py
Normal 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)
|
||||||
133
src/ui/custom_message_box.py
Normal 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
@@ -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
@@ -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)
|
||||||
@@ -1,19 +1,12 @@
|
|||||||
|
QLabel {
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
#main_container {
|
#main_container {
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
/*background-image: url(:/assets/background.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center; */
|
|
||||||
/*background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
|
||||||
stop: 0 #050a0f,
|
|
||||||
stop: 1 #0f172a);*/
|
|
||||||
background: qradialgradient(cx:0.5, cy:0.5, radius:0.8, fx:0.5, fy:0.5,
|
background: qradialgradient(cx:0.5, cy:0.5, radius:0.8, fx:0.5, fy:0.5,
|
||||||
stop: 0 #16213e,
|
stop: 0 #16213e,
|
||||||
stop: 1 #0a0e14);
|
stop: 1 #0a0e14);
|
||||||
|
|
||||||
/*background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
|
|
||||||
stop: 0 #0d1117,
|
|
||||||
stop: 0.7 #0d1117,
|
|
||||||
stop: 1 #1e3a8a); /* Une pointe de bleu plus vif en bas à droite */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QFrame#logo_frame {
|
QFrame#logo_frame {
|
||||||
@@ -22,9 +15,21 @@ QFrame#logo_frame {
|
|||||||
background-position: center;
|
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 {
|
QLabel#maintitle_label {
|
||||||
font-size: 38px;
|
font-size: 38px;
|
||||||
color: rgb(255, 255, 255);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QLabel#subtitle_label {
|
QLabel#subtitle_label {
|
||||||
@@ -37,13 +42,10 @@ QLabel#queue_position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QLabel#queue_lbl {
|
QLabel#queue_lbl {
|
||||||
color: rgb(255, 255, 255);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#connexion_btn {
|
QPushButton#connexion_btn[chantier="false"] {
|
||||||
/* border-radius: 15px;
|
|
||||||
background-color: rgb(255, 120, 0);
|
|
||||||
color: rgb(255, 255, 255); */
|
|
||||||
/* Dégradé chaleureux : Orange vers Orange-Rouge */
|
/* Dégradé chaleureux : Orange vers Orange-Rouge */
|
||||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||||
stop: 0 #ff9d00,
|
stop: 0 #ff9d00,
|
||||||
@@ -55,8 +57,7 @@ QPushButton#connexion_btn {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#connexion_btn:hover {
|
QPushButton#connexion_btn[chantier="false"]:hover {
|
||||||
/* background: #ffad66; */
|
|
||||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||||
stop: 0 #ffb338,
|
stop: 0 #ffb338,
|
||||||
stop: 1 #ff7a29);
|
stop: 1 #ff7a29);
|
||||||
@@ -65,17 +66,53 @@ QPushButton#connexion_btn:hover {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#connexionBtn:pressed {
|
QPushButton#connexion_btn[chantier="false"]:pressed {
|
||||||
background: #cc5200;
|
background: #cc5200;
|
||||||
padding-top: 12px; /* Effet d'enfoncement */
|
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_btn,
|
||||||
|
QPushButton#discord_auth_btn,
|
||||||
QPushButton#intranet_btn
|
QPushButton#intranet_btn
|
||||||
{
|
{
|
||||||
/*border-radius: 15px;
|
|
||||||
background-color: rgb(32, 58, 67);
|
|
||||||
color: rgb(255, 255, 255);*/
|
|
||||||
background-color: rgba(32, 58, 67, 0.6); /* Bleu très sombre semi-transparent */
|
background-color: rgba(32, 58, 67, 0.6); /* Bleu très sombre semi-transparent */
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -84,20 +121,24 @@ QPushButton#intranet_btn
|
|||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#discord_btn:hover {
|
QPushButton#discord_btn:hover,
|
||||||
|
QPushButton#discord_auth_btn:hover
|
||||||
|
{
|
||||||
background-color: rgba(88, 101, 242, 0.4); /* Fond bleu Discord translucide */
|
background-color: rgba(88, 101, 242, 0.4); /* Fond bleu Discord translucide */
|
||||||
border: 2px solid #7289da; /* Bordure plus épaisse et claire pour l'éclat */
|
border: 2px solid #7289da; /* Bordure plus épaisse et claire pour l'éclat */
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#intranet_btn:hover {
|
QPushButton#intranet_btn:hover {
|
||||||
/* background: rgb(68, 124, 143); */
|
|
||||||
background-color: rgba(0, 242, 255, 0.15); /* Teinte turquoise légère en fond */
|
background-color: rgba(0, 242, 255, 0.15); /* Teinte turquoise légère en fond */
|
||||||
border: 2px solid #00f2ff; /* Bordure turquoise vive et épaisse */
|
border: 2px solid #00f2ff; /* Bordure turquoise vive et épaisse */
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#discordBtn:pressed, QPushButton#intranetBtn:pressed {
|
QPushButton#discord_btn:pressed,
|
||||||
|
QPushButton#discord_auth_btn:pressed,
|
||||||
|
QPushButton#intranet_btn:pressed
|
||||||
|
{
|
||||||
background-color: #0f172a;
|
background-color: #0f172a;
|
||||||
padding-top: 10px; /* Petit effet d'enfoncement */
|
padding-top: 10px; /* Petit effet d'enfoncement */
|
||||||
}
|
}
|
||||||
@@ -107,6 +148,14 @@ QPushButton#mute_btn {
|
|||||||
background-color: rgb(255, 120, 0);
|
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 {
|
QPushButton#close_btn {
|
||||||
border : 0;
|
border : 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -139,42 +188,128 @@ QTextEdit#info_text {
|
|||||||
color: rgb(255, 255, 255);
|
color: rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSlider::groove:horizontal {
|
QSlider {
|
||||||
border: 1px inset #1C1C1C;
|
height: 35px;
|
||||||
height: 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSlider::groove:horizontal {
|
QSlider::groove:horizontal {
|
||||||
border: 1px inset #1C1C1C;
|
border: 1px solid #1C1C1C;
|
||||||
height: 6px;
|
height: 8px;
|
||||||
border-radius: 3px;
|
background: #7D7D7D;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSlider::sub-page:horizontal {
|
QSlider::sub-page:horizontal {
|
||||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #12b9ff, stop: 1.0 #015eea);
|
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #12b9ff, stop: 1.0 #015eea);
|
||||||
border: 1px inset #1C1C1C;
|
border: 1px solid #1C1C1C;
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
}
|
|
||||||
|
|
||||||
/* groove background on right of slider */
|
|
||||||
QSlider::add-page:horizontal {
|
|
||||||
background: #7D7D7D;
|
|
||||||
border: 1px outset #1C1C1C;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSlider::handle:horizontal {
|
QSlider::handle:horizontal {
|
||||||
/*background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 rgb(78, 195, 146), stop:1 rgb(57, 144, 107));
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FF8C00, stop:1 #FF5E00);
|
||||||
border: 1px solid rgb(78, 195, 146);
|
border: 1px solid #B34700;
|
||||||
width: 12px;
|
width: 16px;
|
||||||
height: 10px;
|
height: 16px;
|
||||||
margin-top: -8px;
|
margin: -5px 0; /* Ajusté pour centrer 16px sur barre de 8px */
|
||||||
margin-bottom: -8px;
|
border-radius: 8px; /* Moitié exacte de width/height */
|
||||||
border-radius: 2px;*/
|
}
|
||||||
background: transparent;
|
|
||||||
width: 0px;
|
/* On répète les propriétés cruciales pour les états survolés/pressés */
|
||||||
height: 0px;
|
QSlider::handle:horizontal:hover {
|
||||||
image: none;
|
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFA500, stop:1 #FF7F00);
|
||||||
border: none;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
<string>MainWindow</string>
|
<string>MainWindow</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="../resources.qrc">
|
<iconset>
|
||||||
<normaloff>:/assets/Icone.ico</normaloff>:/assets/Icone.ico</iconset>
|
<normaloff>:/assets/Icone.ico</normaloff>:/assets/Icone.ico</iconset>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="main_container">
|
<widget class="QWidget" name="main_container">
|
||||||
@@ -710,6 +710,16 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string>staff</string>
|
<string>staff</string>
|
||||||
</property>
|
</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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@@ -755,7 +765,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../resources.qrc">
|
<iconset resource="../resources.qrc">
|
||||||
<normaloff>:/assets/Logo_Discord_2015.png</normaloff>:/assets/Logo_Discord_2015.png</iconset>
|
<normaloff>:/assets/discord-icon.svg</normaloff>:/assets/discord-icon.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
@@ -870,6 +880,9 @@
|
|||||||
<property name="readOnly">
|
<property name="readOnly">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextInteractionFlag::NoTextInteraction</set>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
885
ui/mainwindow_vertical.ui
Normal 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>
|
||||||