fletchtime 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fletchtime-0.2.1 → fletchtime-0.2.2}/.gitignore +4 -0
- {fletchtime-0.2.1/src/fletchtime.egg-info → fletchtime-0.2.2}/PKG-INFO +29 -1
- {fletchtime-0.2.1 → fletchtime-0.2.2}/README.md +28 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/index.md +11 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/roadmap.md +17 -8
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/__main__.py +24 -8
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/_version.py +3 -3
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/gui.py +165 -10
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/server/config_store.py +37 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2/src/fletchtime.egg-info}/PKG-INFO +29 -1
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/scm_version.json +2 -2
- {fletchtime-0.2.1 → fletchtime-0.2.2}/.github/workflows/build.yml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/.github/workflows/docs.yml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/.github/workflows/test.yml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/CONTRIBUTING.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/LICENSE +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/config/app.toml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/config/flint.toml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/config/indoor.toml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/demo.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/_static/logo.svg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/api-reference.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/architecture.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/conf.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/dev-guide/index.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/requirements.txt +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/specifications.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/docs/user-guide/index.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/fletchtime.spec +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/pyproject.toml +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/run_server.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/run_tests.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/scripts/generate_classic_sounds.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/setup.cfg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/__init__.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/__init__.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/engine.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/models.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/modes/__init__.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/modes/base.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/modes/flint.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/modes/indoor.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/sequence.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/engine/turn_modes.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/logging_setup.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/runtime.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/server/__init__.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/server/http_static.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/server/match_server.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/server/ws_server.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/__init__.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/banners/README.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/club/README.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/gui/theme.json +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/README.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/countdown_tick.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/emergency_end.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/emergency_start.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/end_of_match.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/end_of_volee.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/pause_end.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/pause_start.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/prep_start.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/shoot_start.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/sounds/packs/classic/warning_orange.wav +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_20cm_4spot.jpg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_35cm_1spot.jpg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_compound.jpg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_recurve.jpg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/config.html +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/control.html +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/display.html +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/i18n.js +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/index.html +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/logo.svg +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/manual.html +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/config.png +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/control.png +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/display.png +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/screenshots/index.png +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/theme.js +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/SOURCES.txt +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/dependency_links.txt +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/entry_points.txt +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/requires.txt +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/scm_file_list.json +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime.egg-info/top_level.txt +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/tests/test_config_store.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/tests/test_engine.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/tests/test_flint_mode.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/tests/test_indoor_mode.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/tests/test_match_server.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/tests/test_runtime.py +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/web/assets/banners/README.md +0 -0
- {fletchtime-0.2.1 → fletchtime-0.2.2}/web/assets/sounds/packs/README.md +0 -0
|
@@ -27,6 +27,10 @@ docs/_build/
|
|
|
27
27
|
# vrai secret et ne doit jamais être versionné.
|
|
28
28
|
config/auth.toml
|
|
29
29
|
|
|
30
|
+
# Préférences de la fenêtre graphique (thème...) -- propres à la machine/
|
|
31
|
+
# personne qui lance la fenêtre, pas un réglage de club à partager.
|
|
32
|
+
config/gui.toml
|
|
33
|
+
|
|
30
34
|
# Instantané du match en cours (récupération après crash) -- propre à une
|
|
31
35
|
# session de match en cours, pas un réglage du club à partager/versionner.
|
|
32
36
|
config/match_state.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fletchtime
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Chronométrage de compétitions d'archerie FFTL (Indoor, Flint) -- serveur + interfaces web incluses
|
|
5
5
|
License: GPL-3.0-or-later
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -21,6 +21,11 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
<img src="https://mrfanghodev.github.io/fletchtime/_static/logo.svg" width="80" height="80" alt="FletchTime logo">
|
|
23
23
|
|
|
24
|
+
[](pyproject.toml)
|
|
25
|
+
[](LICENSE)
|
|
26
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
27
|
+
[](https://pypi.org/project/fletchtime/)
|
|
28
|
+
|
|
24
29
|
# FletchTime
|
|
25
30
|
|
|
26
31
|
Logiciel open source de gestion du temps pour compétitions d'archerie FFTL
|
|
@@ -122,6 +127,29 @@ from fletchtime.engine import IndoorMode, IndoorConfig, MatchEngine
|
|
|
122
127
|
engine = MatchEngine(IndoorMode(IndoorConfig()))
|
|
123
128
|
```
|
|
124
129
|
|
|
130
|
+
## Plusieurs salles de compétition sur un même PC
|
|
131
|
+
|
|
132
|
+
Chaque installation de FletchTime (dossier `pip install`, ou dossier de
|
|
133
|
+
l'exécutable autoporteur) fait tourner **une seule salle** -- son propre
|
|
134
|
+
match, sa propre urgence, sa propre config. Pour plusieurs salles en
|
|
135
|
+
parallèle sur le même PC :
|
|
136
|
+
|
|
137
|
+
1. **Copie le dossier FletchTime une fois par salle** (ex. `salle-A/`,
|
|
138
|
+
`salle-B/`) -- ça isole automatiquement `config/` (temps de tir,
|
|
139
|
+
urgence...), les journaux, et l'instantané de récupération après
|
|
140
|
+
plantage : chaque copie a sa propre urgence, indépendante des autres.
|
|
141
|
+
2. Dans la fenêtre de chaque copie, section **Ports (HTTP/WS)** : donne
|
|
142
|
+
des ports différents à chaque salle (ex. salle A garde `8000`/`8765`
|
|
143
|
+
par défaut, salle B passe à `8001`/`8766`) -- **Appliquer** redémarre
|
|
144
|
+
le serveur automatiquement si besoin.
|
|
145
|
+
3. Lance chaque copie séparément. Les écrans de chaque salle se
|
|
146
|
+
connectent à l'adresse (IP + port) affichée dans la fenêtre de leur
|
|
147
|
+
salle respective.
|
|
148
|
+
|
|
149
|
+
**Windows** : le pare-feu autorise généralement par chemin d'exécutable --
|
|
150
|
+
avec des copies de dossier séparées, prévoir de valider l'alerte une fois
|
|
151
|
+
par copie au premier lancement.
|
|
152
|
+
|
|
125
153
|
## Contribuer
|
|
126
154
|
|
|
127
155
|
Envie de proposer un correctif, une idée, ou juste signaler un bug ? Voir
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<img src="https://mrfanghodev.github.io/fletchtime/_static/logo.svg" width="80" height="80" alt="FletchTime logo">
|
|
2
2
|
|
|
3
|
+
[](pyproject.toml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
6
|
+
[](https://pypi.org/project/fletchtime/)
|
|
7
|
+
|
|
3
8
|
# FletchTime
|
|
4
9
|
|
|
5
10
|
Logiciel open source de gestion du temps pour compétitions d'archerie FFTL
|
|
@@ -101,6 +106,29 @@ from fletchtime.engine import IndoorMode, IndoorConfig, MatchEngine
|
|
|
101
106
|
engine = MatchEngine(IndoorMode(IndoorConfig()))
|
|
102
107
|
```
|
|
103
108
|
|
|
109
|
+
## Plusieurs salles de compétition sur un même PC
|
|
110
|
+
|
|
111
|
+
Chaque installation de FletchTime (dossier `pip install`, ou dossier de
|
|
112
|
+
l'exécutable autoporteur) fait tourner **une seule salle** -- son propre
|
|
113
|
+
match, sa propre urgence, sa propre config. Pour plusieurs salles en
|
|
114
|
+
parallèle sur le même PC :
|
|
115
|
+
|
|
116
|
+
1. **Copie le dossier FletchTime une fois par salle** (ex. `salle-A/`,
|
|
117
|
+
`salle-B/`) -- ça isole automatiquement `config/` (temps de tir,
|
|
118
|
+
urgence...), les journaux, et l'instantané de récupération après
|
|
119
|
+
plantage : chaque copie a sa propre urgence, indépendante des autres.
|
|
120
|
+
2. Dans la fenêtre de chaque copie, section **Ports (HTTP/WS)** : donne
|
|
121
|
+
des ports différents à chaque salle (ex. salle A garde `8000`/`8765`
|
|
122
|
+
par défaut, salle B passe à `8001`/`8766`) -- **Appliquer** redémarre
|
|
123
|
+
le serveur automatiquement si besoin.
|
|
124
|
+
3. Lance chaque copie séparément. Les écrans de chaque salle se
|
|
125
|
+
connectent à l'adresse (IP + port) affichée dans la fenêtre de leur
|
|
126
|
+
salle respective.
|
|
127
|
+
|
|
128
|
+
**Windows** : le pare-feu autorise généralement par chemin d'exécutable --
|
|
129
|
+
avec des copies de dossier séparées, prévoir de valider l'alerte une fois
|
|
130
|
+
par copie au premier lancement.
|
|
131
|
+
|
|
104
132
|
## Contribuer
|
|
105
133
|
|
|
106
134
|
Envie de proposer un correctif, une idée, ou juste signaler un bug ? Voir
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
```{image} _static/logo.svg
|
|
2
|
+
:alt: FletchTime
|
|
3
|
+
:width: 140px
|
|
4
|
+
:align: center
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
[](https://github.com/MrFanghoDev/fletchtime/blob/master/pyproject.toml)
|
|
8
|
+
[](https://github.com/MrFanghoDev/fletchtime/blob/master/LICENSE)
|
|
9
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
10
|
+
[](https://pypi.org/project/fletchtime/)
|
|
11
|
+
|
|
1
12
|
# FletchTime
|
|
2
13
|
|
|
3
14
|
Logiciel de gestion du temps pour compétitions d'archerie FFTL (Indoor et Flint),
|
|
@@ -79,20 +79,29 @@ dépendent d'extensions compilées (Rust/C) peu fiables sur Pydroid 3, voir
|
|
|
79
79
|
(`logs/fletchtime.log`), en plus du journal déjà affiché dans la
|
|
80
80
|
fenêtre (en mémoire, perdu à la fermeture). Voir {doc}`architecture`,
|
|
81
81
|
section dédiée -- vérifié que le mot de passe ne s'y retrouve jamais.
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
- ~~**Fenêtre graphique -- améliorations**~~ -- toutes faites (paramétrage
|
|
83
|
+
réseau, affichage des adresses, thème, couleur du bouton Quitter) :
|
|
84
|
+
- ~~Paramétrage du serveur et de l'adresse IP exposée~~ -- fait :
|
|
85
|
+
ports HTTP/WebSocket modifiables directement dans la fenêtre
|
|
86
|
+
(`config/gui.toml`), avec redémarrage automatique du serveur si les
|
|
87
|
+
ports changent pendant qu'il tourne. Objectif principal : plusieurs
|
|
88
|
+
salles de compétition sur un même PC -- copier le dossier FletchTime
|
|
89
|
+
une fois par salle (isole aussi `config/`, `logs/`, l'instantané de
|
|
90
|
+
récupération, donc des urgences bien séparées), donner des ports
|
|
91
|
+
différents à chaque copie, lancer chacune séparément. Testé
|
|
92
|
+
concrètement : deux instances simultanées sur le même processus,
|
|
93
|
+
ports différents, contenu totalement isolé entre les deux.
|
|
86
94
|
- ~~Affichage des adresses exposées~~ -- fait : champ dédié (adresse
|
|
87
95
|
d'accueil) dans la fenêtre elle-même, en plus des boutons de
|
|
88
96
|
raccourci -- utile pour retaper l'adresse à la main sur un appareil
|
|
89
97
|
sans bouton de raccourci. Non vérifié visuellement si le texte reste
|
|
90
98
|
bien sélectionnable/copiable une fois le champ désactivé (`CTkEntry`
|
|
91
99
|
n'a pas de vrai état "readonly" contrairement à `ttk.Entry`).
|
|
92
|
-
- Choix du thème clair/sombre dans la fenêtre --
|
|
93
|
-
|
|
94
|
-
`
|
|
95
|
-
|
|
100
|
+
- ~~Choix du thème clair/sombre dans la fenêtre~~ -- fait : sélecteur
|
|
101
|
+
Système/Clair/Sombre dans l'en-tête, préférence persistée dans
|
|
102
|
+
`config/gui.toml` (jamais versionné, propre à la machine -- voir
|
|
103
|
+
`.gitignore`). Indépendant de `display.html`, qui reste lui, par
|
|
104
|
+
choix délibéré, toujours sombre.
|
|
96
105
|
- ~~Couleur du bouton Quitter à revoir~~ -- fait : couleur pleine
|
|
97
106
|
(`#3a4354`) au lieu de transparent+contour, visible sans être
|
|
98
107
|
alarmante (distincte du rouge d'Arrêter et du doré de Démarrer).
|
|
@@ -30,9 +30,15 @@ from pathlib import Path
|
|
|
30
30
|
from fletchtime import __version__
|
|
31
31
|
from fletchtime.logging_setup import configure_logging
|
|
32
32
|
from fletchtime.runtime import ServerRuntime
|
|
33
|
+
from fletchtime.server import config_store
|
|
33
34
|
|
|
34
|
-
HTTP_PORT
|
|
35
|
-
|
|
35
|
+
# Anciennement des constantes HTTP_PORT/WS_PORT figées ici -- les ports
|
|
36
|
+
# par défaut vivent maintenant uniquement dans
|
|
37
|
+
# config_store.DEFAULT_GUI_CONFIG, seule source de vérité (voir
|
|
38
|
+
# _run_headless ci-dessous et fletchtime.gui, qui lisent tous deux
|
|
39
|
+
# config_store.load_gui_config() plutôt qu'une constante figée). Permet
|
|
40
|
+
# de faire tourner plusieurs salles de compétition sur un même PC : une
|
|
41
|
+
# copie de dossier par salle, chacune avec son propre config/gui.toml.
|
|
36
42
|
|
|
37
43
|
# Dossiers pré-remplis avec un contenu par défaut fourni avec FletchTime
|
|
38
44
|
# (copié une seule fois, au tout premier lancement -- jamais écrasé
|
|
@@ -113,14 +119,14 @@ def local_ip() -> str:
|
|
|
113
119
|
s.close()
|
|
114
120
|
|
|
115
121
|
|
|
116
|
-
def _print_banner(ip: str, data_root: Path) -> None:
|
|
122
|
+
def _print_banner(ip: str, data_root: Path, http_port: int) -> None:
|
|
117
123
|
print("=" * 60)
|
|
118
124
|
print(f" FletchTime {__version__} -- serveur de contrôle et d'affichage")
|
|
119
125
|
print(f" FletchTime {__version__} -- control and display server")
|
|
120
126
|
print("=" * 60)
|
|
121
|
-
print(f" Accueil / Home : http://{ip}:{
|
|
122
|
-
print(f" Contrôle / Control : http://{ip}:{
|
|
123
|
-
print(f" Affichage / Display : http://{ip}:{
|
|
127
|
+
print(f" Accueil / Home : http://{ip}:{http_port}/")
|
|
128
|
+
print(f" Contrôle / Control : http://{ip}:{http_port}/control.html")
|
|
129
|
+
print(f" Affichage / Display : http://{ip}:{http_port}/display.html?lane=1")
|
|
124
130
|
print()
|
|
125
131
|
print(" Depuis CET appareil, remplace l'IP par 127.0.0.1 si besoin.")
|
|
126
132
|
print(" From THIS device, replace the IP with 127.0.0.1 if needed.")
|
|
@@ -156,9 +162,19 @@ def _run_headless() -> None:
|
|
|
156
162
|
log_file = configure_logging(data_root / "logs")
|
|
157
163
|
print(f"Journal détaillé / Detailed log: {log_file}")
|
|
158
164
|
|
|
159
|
-
|
|
165
|
+
# Ports lus depuis config/gui.toml (mêmes préférences que la fenêtre
|
|
166
|
+
# graphique, voir config_store.load_gui_config) -- permet de faire
|
|
167
|
+
# tourner plusieurs salles de compétition sur le même PC : une copie
|
|
168
|
+
# de dossier par salle, chacune avec son propre gui.toml donnant des
|
|
169
|
+
# ports différents. "gui.toml" malgré le nom concerne aussi ce mode
|
|
170
|
+
# terminal, pour que les deux se comportent de façon cohérente.
|
|
171
|
+
gui_config = config_store.load_gui_config()
|
|
172
|
+
http_port = gui_config["http_port"]
|
|
173
|
+
ws_port = gui_config["ws_port"]
|
|
160
174
|
|
|
161
|
-
|
|
175
|
+
_print_banner(local_ip(), data_root, http_port)
|
|
176
|
+
|
|
177
|
+
runtime = ServerRuntime(str(app_web_dir), str(assets_dir), http_port, ws_port)
|
|
162
178
|
runtime.start()
|
|
163
179
|
|
|
164
180
|
stop_event = threading.Event()
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 2)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gceb71f360'
|
|
@@ -32,8 +32,6 @@ import customtkinter as ctk
|
|
|
32
32
|
|
|
33
33
|
from fletchtime import __version__
|
|
34
34
|
from fletchtime.__main__ import (
|
|
35
|
-
HTTP_PORT,
|
|
36
|
-
WS_PORT,
|
|
37
35
|
_app_web_dir,
|
|
38
36
|
_data_root,
|
|
39
37
|
ensure_directories,
|
|
@@ -41,6 +39,7 @@ from fletchtime.__main__ import (
|
|
|
41
39
|
)
|
|
42
40
|
from fletchtime.logging_setup import configure_logging
|
|
43
41
|
from fletchtime.runtime import ServerRuntime
|
|
42
|
+
from fletchtime.server import config_store
|
|
44
43
|
|
|
45
44
|
_TRANSLATIONS = {
|
|
46
45
|
"fr": {
|
|
@@ -56,6 +55,15 @@ _TRANSLATIONS = {
|
|
|
56
55
|
"log_title": "Journal",
|
|
57
56
|
"club_data": "Données du club :",
|
|
58
57
|
"addressCaption": "Adresse :",
|
|
58
|
+
"themeSystem": "Système",
|
|
59
|
+
"themeLight": "Clair",
|
|
60
|
+
"themeDark": "Sombre",
|
|
61
|
+
"networkCaption": "Ports (HTTP/WS) :",
|
|
62
|
+
"apply": "Appliquer",
|
|
63
|
+
"networkErrorNotANumber": "Les ports doivent être des nombres",
|
|
64
|
+
"networkErrorRange": "Les ports doivent être entre 1 et 65535",
|
|
65
|
+
"networkErrorSame": "Les deux ports doivent être différents",
|
|
66
|
+
"networkApplied": "Appliqué",
|
|
59
67
|
},
|
|
60
68
|
"en": {
|
|
61
69
|
"title": "FletchTime -- Server",
|
|
@@ -70,6 +78,15 @@ _TRANSLATIONS = {
|
|
|
70
78
|
"log_title": "Log",
|
|
71
79
|
"club_data": "Club data:",
|
|
72
80
|
"addressCaption": "Address:",
|
|
81
|
+
"themeSystem": "System",
|
|
82
|
+
"themeLight": "Light",
|
|
83
|
+
"themeDark": "Dark",
|
|
84
|
+
"networkCaption": "Ports (HTTP/WS):",
|
|
85
|
+
"apply": "Apply",
|
|
86
|
+
"networkErrorNotANumber": "Ports must be numbers",
|
|
87
|
+
"networkErrorRange": "Ports must be between 1 and 65535",
|
|
88
|
+
"networkErrorSame": "Both ports must be different",
|
|
89
|
+
"networkApplied": "Applied",
|
|
73
90
|
},
|
|
74
91
|
}
|
|
75
92
|
|
|
@@ -160,10 +177,7 @@ class FletchTimeApp(ctk.CTk):
|
|
|
160
177
|
# Doit être fait AVANT super().__init__() : customtkinter applique
|
|
161
178
|
# le thème au moment de la construction de chaque widget, fenêtre
|
|
162
179
|
# racine comprise -- appelé après, la fenêtre elle-même garderait
|
|
163
|
-
# le thème par défaut.
|
|
164
|
-
# la même palette que les pages web en thème sombre, cohérent
|
|
165
|
-
# avec display.html qui reste lui aussi toujours sombre par choix
|
|
166
|
-
# délibéré (voir docs/architecture.md).
|
|
180
|
+
# le thème par défaut.
|
|
167
181
|
#
|
|
168
182
|
# Part d'un thème intégré à customtkinter ("dark-blue" -- garanti
|
|
169
183
|
# complet pour la version installée) plutôt qu'un JSON entièrement
|
|
@@ -174,7 +188,20 @@ class FletchTimeApp(ctk.CTk):
|
|
|
174
188
|
# les couleurs sont ensuite surchargées (voir _apply_brand_colors),
|
|
175
189
|
# tout le reste (rayons, épaisseurs de bordure...) reste celui,
|
|
176
190
|
# déjà testé, du thème intégré.
|
|
177
|
-
|
|
191
|
+
#
|
|
192
|
+
# Thème choisi par la personne qui lance la fenêtre (voir
|
|
193
|
+
# config/gui.toml, config_store.load_gui_config) -- "system" par
|
|
194
|
+
# défaut, qui suit le thème du système d'exploitation (sauf sous
|
|
195
|
+
# Linux, limitation connue de customtkinter -- retombe sur
|
|
196
|
+
# "light"). Indépendant de display.html, qui reste lui, par choix
|
|
197
|
+
# délibéré, toujours sombre (voir docs/architecture.md) : l'écran
|
|
198
|
+
# vu par les archers et la fenêtre de contrôle du responsable du
|
|
199
|
+
# chronométrage sont deux préoccupations distinctes.
|
|
200
|
+
gui_config = config_store.load_gui_config()
|
|
201
|
+
self.theme = gui_config["theme"]
|
|
202
|
+
self.http_port = gui_config["http_port"]
|
|
203
|
+
self.ws_port = gui_config["ws_port"]
|
|
204
|
+
ctk.set_appearance_mode(self.theme)
|
|
178
205
|
ctk.set_default_color_theme("dark-blue")
|
|
179
206
|
_apply_brand_colors()
|
|
180
207
|
|
|
@@ -188,7 +215,7 @@ class FletchTimeApp(ctk.CTk):
|
|
|
188
215
|
ensure_directories(self.data_root, self.app_web_dir)
|
|
189
216
|
self.assets_dir = self.data_root / "web" / "assets"
|
|
190
217
|
self.runtime = ServerRuntime(
|
|
191
|
-
str(self.app_web_dir), str(self.assets_dir),
|
|
218
|
+
str(self.app_web_dir), str(self.assets_dir), self.http_port, self.ws_port
|
|
192
219
|
)
|
|
193
220
|
|
|
194
221
|
# Capte le journal d'accès HTTP (http.server écrit sur stderr) et
|
|
@@ -236,6 +263,15 @@ class FletchTimeApp(ctk.CTk):
|
|
|
236
263
|
text_color="gray60",
|
|
237
264
|
).pack(side="left", padx=(0, 16), pady=12)
|
|
238
265
|
|
|
266
|
+
self.theme_menu = ctk.CTkOptionMenu(
|
|
267
|
+
header,
|
|
268
|
+
values=[self._t("themeSystem"), self._t("themeLight"), self._t("themeDark")],
|
|
269
|
+
width=110,
|
|
270
|
+
command=self._on_theme_change,
|
|
271
|
+
)
|
|
272
|
+
self.theme_menu.set(self._theme_label(self.theme))
|
|
273
|
+
self.theme_menu.pack(side="right", padx=(0, 8), pady=12)
|
|
274
|
+
|
|
239
275
|
self.lang_menu = ctk.CTkOptionMenu(
|
|
240
276
|
header, values=["FR", "EN"], width=70, command=self._on_language_change
|
|
241
277
|
)
|
|
@@ -315,6 +351,43 @@ class FletchTimeApp(ctk.CTk):
|
|
|
315
351
|
)
|
|
316
352
|
self.address_entry.pack(side="left", padx=(0, 12), pady=8, expand=True, fill="x")
|
|
317
353
|
|
|
354
|
+
# -- réseau (ports) -----------------------------------------------
|
|
355
|
+
# Ports modifiables plutôt que figés : permet de faire tourner
|
|
356
|
+
# plusieurs salles de compétition sur le même PC -- une copie de
|
|
357
|
+
# dossier par salle, chacune avec des ports différents (voir
|
|
358
|
+
# config/gui.toml, config_store). Changer un port ici redémarre le
|
|
359
|
+
# serveur automatiquement s'il tournait déjà, pour que le
|
|
360
|
+
# changement prenne effet immédiatement (voir _on_apply_network).
|
|
361
|
+
network_frame = ctk.CTkFrame(self)
|
|
362
|
+
network_frame.pack(fill="x", padx=16, pady=(0, 8))
|
|
363
|
+
|
|
364
|
+
self.network_caption = ctk.CTkLabel(
|
|
365
|
+
network_frame,
|
|
366
|
+
text=self._t("networkCaption"),
|
|
367
|
+
font=ctk.CTkFont(size=11),
|
|
368
|
+
text_color="gray60",
|
|
369
|
+
)
|
|
370
|
+
self.network_caption.pack(side="left", padx=(12, 8), pady=8)
|
|
371
|
+
|
|
372
|
+
self.http_port_entry = ctk.CTkEntry(network_frame, width=70, justify="center")
|
|
373
|
+
self.http_port_entry.insert(0, str(self.http_port))
|
|
374
|
+
self.http_port_entry.pack(side="left", padx=(0, 4), pady=8)
|
|
375
|
+
|
|
376
|
+
self.network_separator = ctk.CTkLabel(network_frame, text="/", text_color="gray60")
|
|
377
|
+
self.network_separator.pack(side="left", padx=2, pady=8)
|
|
378
|
+
|
|
379
|
+
self.ws_port_entry = ctk.CTkEntry(network_frame, width=70, justify="center")
|
|
380
|
+
self.ws_port_entry.insert(0, str(self.ws_port))
|
|
381
|
+
self.ws_port_entry.pack(side="left", padx=(4, 12), pady=8)
|
|
382
|
+
|
|
383
|
+
self.network_apply_button = ctk.CTkButton(
|
|
384
|
+
network_frame, text=self._t("apply"), width=90, command=self._on_apply_network
|
|
385
|
+
)
|
|
386
|
+
self.network_apply_button.pack(side="left", padx=(0, 8), pady=8)
|
|
387
|
+
|
|
388
|
+
self.network_status_label = ctk.CTkLabel(network_frame, text="", text_color="gray60")
|
|
389
|
+
self.network_status_label.pack(side="left", padx=(4, 12), pady=8, fill="x", expand=True)
|
|
390
|
+
|
|
318
391
|
# -- journal ----------------------------------------------------
|
|
319
392
|
self.log_label = ctk.CTkLabel(self, text=self._t("log_title"), anchor="w")
|
|
320
393
|
self.log_label.pack(fill="x", padx=20, pady=(8, 0))
|
|
@@ -345,7 +418,7 @@ class FletchTimeApp(ctk.CTk):
|
|
|
345
418
|
# -- actions ---------------------------------------------------------
|
|
346
419
|
|
|
347
420
|
def _open_link(self, path: str) -> None:
|
|
348
|
-
webbrowser.open(f"http://127.0.0.1:{
|
|
421
|
+
webbrowser.open(f"http://127.0.0.1:{self.http_port}{path}")
|
|
349
422
|
|
|
350
423
|
def _start_server(self) -> None:
|
|
351
424
|
if self.runtime.is_running:
|
|
@@ -372,7 +445,7 @@ class FletchTimeApp(ctk.CTk):
|
|
|
372
445
|
"""L'adresse ne dépend pas de si le serveur tourne réellement --
|
|
373
446
|
affichée dans tous les cas (utile même à l'arrêt, pour préparer
|
|
374
447
|
la config réseau à l'avance sur un autre appareil)."""
|
|
375
|
-
address = f"http://{local_ip()}:{
|
|
448
|
+
address = f"http://{local_ip()}:{self.http_port}/"
|
|
376
449
|
# CTkEntry ne supporte que "normal"/"disabled" (contrairement à
|
|
377
450
|
# ttk.Entry, qui a un vrai état "readonly") -- il faut donc la
|
|
378
451
|
# débloquer temporairement pour la mettre à jour, puis la
|
|
@@ -394,6 +467,88 @@ class FletchTimeApp(ctk.CTk):
|
|
|
394
467
|
self.log_label.configure(text=self._t("log_title"))
|
|
395
468
|
self.quit_button.configure(text=self._t("quit"))
|
|
396
469
|
self.footer_label.configure(text=f"{self._t('club_data')} {self.data_root}")
|
|
470
|
+
# Les valeurs du menu de thème sont des libellés traduits (pas des
|
|
471
|
+
# codes internes comme "FR"/"EN") -- il faut donc reconstruire la
|
|
472
|
+
# liste ET repositionner la sélection sur le thème actuel, pas
|
|
473
|
+
# juste changer le texte d'un widget existant.
|
|
474
|
+
self.theme_menu.configure(
|
|
475
|
+
values=[self._t("themeSystem"), self._t("themeLight"), self._t("themeDark")]
|
|
476
|
+
)
|
|
477
|
+
self.theme_menu.set(self._theme_label(self.theme))
|
|
478
|
+
self.network_caption.configure(text=self._t("networkCaption"))
|
|
479
|
+
self.network_apply_button.configure(text=self._t("apply"))
|
|
480
|
+
self._refresh_status()
|
|
481
|
+
|
|
482
|
+
def _theme_label(self, theme: str) -> str:
|
|
483
|
+
return {
|
|
484
|
+
"system": self._t("themeSystem"),
|
|
485
|
+
"light": self._t("themeLight"),
|
|
486
|
+
"dark": self._t("themeDark"),
|
|
487
|
+
}.get(theme, self._t("themeSystem"))
|
|
488
|
+
|
|
489
|
+
def _on_theme_change(self, label: str) -> None:
|
|
490
|
+
reverse = {
|
|
491
|
+
self._t("themeSystem"): "system",
|
|
492
|
+
self._t("themeLight"): "light",
|
|
493
|
+
self._t("themeDark"): "dark",
|
|
494
|
+
}
|
|
495
|
+
theme = reverse.get(label, "system")
|
|
496
|
+
self.theme = theme
|
|
497
|
+
ctk.set_appearance_mode(theme)
|
|
498
|
+
config_store.save_gui_config({"theme": theme})
|
|
499
|
+
|
|
500
|
+
def _on_apply_network(self) -> None:
|
|
501
|
+
"""Change les ports HTTP/WebSocket -- reconstruit ServerRuntime
|
|
502
|
+
avec les nouvelles valeurs (les ports sont figés à la
|
|
503
|
+
construction, voir fletchtime.runtime.ServerRuntime) et
|
|
504
|
+
redémarre automatiquement si le serveur tournait déjà, pour que
|
|
505
|
+
le changement prenne effet tout de suite plutôt que de laisser
|
|
506
|
+
la fenêtre dans un état incohérent (ports affichés différents de
|
|
507
|
+
ceux réellement utilisés par le serveur en cours)."""
|
|
508
|
+
try:
|
|
509
|
+
http_port = int(self.http_port_entry.get())
|
|
510
|
+
ws_port = int(self.ws_port_entry.get())
|
|
511
|
+
except ValueError:
|
|
512
|
+
self.network_status_label.configure(
|
|
513
|
+
text=self._t("networkErrorNotANumber"), text_color="#d6534a"
|
|
514
|
+
)
|
|
515
|
+
return
|
|
516
|
+
|
|
517
|
+
if not (1 <= http_port <= 65535) or not (1 <= ws_port <= 65535):
|
|
518
|
+
self.network_status_label.configure(
|
|
519
|
+
text=self._t("networkErrorRange"), text_color="#d6534a"
|
|
520
|
+
)
|
|
521
|
+
return
|
|
522
|
+
if http_port == ws_port:
|
|
523
|
+
self.network_status_label.configure(
|
|
524
|
+
text=self._t("networkErrorSame"), text_color="#d6534a"
|
|
525
|
+
)
|
|
526
|
+
return
|
|
527
|
+
|
|
528
|
+
try:
|
|
529
|
+
config_store.save_gui_config({"http_port": http_port, "ws_port": ws_port})
|
|
530
|
+
except ValueError:
|
|
531
|
+
# Filet de sécurité seulement -- les cas attendus sont déjà
|
|
532
|
+
# couverts par les vérifications ci-dessus, donc ce chemin ne
|
|
533
|
+
# devrait normalement jamais s'exécuter.
|
|
534
|
+
self.network_status_label.configure(
|
|
535
|
+
text=self._t("networkErrorRange"), text_color="#d6534a"
|
|
536
|
+
)
|
|
537
|
+
return
|
|
538
|
+
|
|
539
|
+
was_running = self.runtime.is_running
|
|
540
|
+
if was_running:
|
|
541
|
+
self.runtime.stop()
|
|
542
|
+
|
|
543
|
+
self.http_port = http_port
|
|
544
|
+
self.ws_port = ws_port
|
|
545
|
+
self.runtime = ServerRuntime(
|
|
546
|
+
str(self.app_web_dir), str(self.assets_dir), self.http_port, self.ws_port
|
|
547
|
+
)
|
|
548
|
+
if was_running:
|
|
549
|
+
self.runtime.start()
|
|
550
|
+
|
|
551
|
+
self.network_status_label.configure(text=self._t("networkApplied"), text_color="#2fb344")
|
|
397
552
|
self._refresh_status()
|
|
398
553
|
|
|
399
554
|
def _on_quit(self) -> None:
|
|
@@ -47,6 +47,11 @@ AUTH_TOML = CONFIG_DIR / "auth.toml"
|
|
|
47
47
|
# (voir .gitignore) : propre à une session de match en cours, pas un
|
|
48
48
|
# réglage du club.
|
|
49
49
|
MATCH_STATE_JSON = CONFIG_DIR / "match_state.json"
|
|
50
|
+
# Préférences de la fenêtre graphique (thème...) -- fichier séparé de
|
|
51
|
+
# app.toml exprès : ce ne sont pas des réglages du match/serveur diffusés
|
|
52
|
+
# aux écrans web, juste des préférences purement locales à qui lance la
|
|
53
|
+
# fenêtre sur cette machine précise.
|
|
54
|
+
GUI_TOML = CONFIG_DIR / "gui.toml"
|
|
50
55
|
|
|
51
56
|
APP_COMMENTS: dict[str, str] = {
|
|
52
57
|
"sound_pack": "Nom du dossier dans web/assets/sounds/packs/ à utiliser (ex. classic)",
|
|
@@ -54,6 +59,13 @@ APP_COMMENTS: dict[str, str] = {
|
|
|
54
59
|
}
|
|
55
60
|
DEFAULT_APP_CONFIG: dict[str, Any] = {"sound_pack": "classic", "countdown_tick_seconds": 5}
|
|
56
61
|
|
|
62
|
+
GUI_COMMENTS: dict[str, str] = {
|
|
63
|
+
"theme": 'Thème de la fenêtre : "system" (suit le thème du système), "light" ou "dark"',
|
|
64
|
+
"http_port": "Port HTTP (pages web) -- change ceci pour faire tourner plusieurs salles sur le même PC",
|
|
65
|
+
"ws_port": "Port WebSocket (temps réel) -- doit être différent du port HTTP",
|
|
66
|
+
}
|
|
67
|
+
DEFAULT_GUI_CONFIG: dict[str, Any] = {"theme": "system", "http_port": 8000, "ws_port": 8765}
|
|
68
|
+
|
|
57
69
|
AUTH_COMMENTS: dict[str, str] = {
|
|
58
70
|
"password": (
|
|
59
71
|
"Mot de passe requis pour piloter le match et modifier la "
|
|
@@ -148,6 +160,31 @@ def save_app_config(overrides: dict[str, Any]) -> dict[str, Any]:
|
|
|
148
160
|
return merged
|
|
149
161
|
|
|
150
162
|
|
|
163
|
+
def load_gui_config() -> dict[str, Any]:
|
|
164
|
+
"""Préférences de la fenêtre graphique -- voir GUI_TOML. Comme
|
|
165
|
+
load_app_config, un plain dict plutôt qu'une dataclass formelle : un
|
|
166
|
+
seul champ aujourd'hui, peut grandir."""
|
|
167
|
+
data = _load_toml(GUI_TOML)
|
|
168
|
+
merged = dict(DEFAULT_GUI_CONFIG)
|
|
169
|
+
merged.update({k: v for k, v in data.items() if k in DEFAULT_GUI_CONFIG})
|
|
170
|
+
return merged
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def save_gui_config(overrides: dict[str, Any]) -> dict[str, Any]:
|
|
174
|
+
merged = load_gui_config()
|
|
175
|
+
merged.update({k: v for k, v in overrides.items() if k in DEFAULT_GUI_CONFIG})
|
|
176
|
+
if merged["theme"] not in ("system", "light", "dark"):
|
|
177
|
+
raise ValueError(f'theme must be "system", "light" or "dark", got {merged["theme"]!r}')
|
|
178
|
+
for key in ("http_port", "ws_port"):
|
|
179
|
+
port = merged[key]
|
|
180
|
+
if not isinstance(port, int) or not (1 <= port <= 65535):
|
|
181
|
+
raise ValueError(f"{key} must be an integer between 1 and 65535, got {port!r}")
|
|
182
|
+
if merged["http_port"] == merged["ws_port"]:
|
|
183
|
+
raise ValueError("http_port and ws_port must be different")
|
|
184
|
+
_write_toml(GUI_TOML, merged, GUI_COMMENTS)
|
|
185
|
+
return merged
|
|
186
|
+
|
|
187
|
+
|
|
151
188
|
def load_auth_config() -> dict[str, Any]:
|
|
152
189
|
data = _load_toml(AUTH_TOML)
|
|
153
190
|
merged = dict(DEFAULT_AUTH_CONFIG)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fletchtime
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Chronométrage de compétitions d'archerie FFTL (Indoor, Flint) -- serveur + interfaces web incluses
|
|
5
5
|
License: GPL-3.0-or-later
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -21,6 +21,11 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
<img src="https://mrfanghodev.github.io/fletchtime/_static/logo.svg" width="80" height="80" alt="FletchTime logo">
|
|
23
23
|
|
|
24
|
+
[](pyproject.toml)
|
|
25
|
+
[](LICENSE)
|
|
26
|
+
[](https://github.com/MrFanghoDev/fletchtime/actions/workflows/test.yml)
|
|
27
|
+
[](https://pypi.org/project/fletchtime/)
|
|
28
|
+
|
|
24
29
|
# FletchTime
|
|
25
30
|
|
|
26
31
|
Logiciel open source de gestion du temps pour compétitions d'archerie FFTL
|
|
@@ -122,6 +127,29 @@ from fletchtime.engine import IndoorMode, IndoorConfig, MatchEngine
|
|
|
122
127
|
engine = MatchEngine(IndoorMode(IndoorConfig()))
|
|
123
128
|
```
|
|
124
129
|
|
|
130
|
+
## Plusieurs salles de compétition sur un même PC
|
|
131
|
+
|
|
132
|
+
Chaque installation de FletchTime (dossier `pip install`, ou dossier de
|
|
133
|
+
l'exécutable autoporteur) fait tourner **une seule salle** -- son propre
|
|
134
|
+
match, sa propre urgence, sa propre config. Pour plusieurs salles en
|
|
135
|
+
parallèle sur le même PC :
|
|
136
|
+
|
|
137
|
+
1. **Copie le dossier FletchTime une fois par salle** (ex. `salle-A/`,
|
|
138
|
+
`salle-B/`) -- ça isole automatiquement `config/` (temps de tir,
|
|
139
|
+
urgence...), les journaux, et l'instantané de récupération après
|
|
140
|
+
plantage : chaque copie a sa propre urgence, indépendante des autres.
|
|
141
|
+
2. Dans la fenêtre de chaque copie, section **Ports (HTTP/WS)** : donne
|
|
142
|
+
des ports différents à chaque salle (ex. salle A garde `8000`/`8765`
|
|
143
|
+
par défaut, salle B passe à `8001`/`8766`) -- **Appliquer** redémarre
|
|
144
|
+
le serveur automatiquement si besoin.
|
|
145
|
+
3. Lance chaque copie séparément. Les écrans de chaque salle se
|
|
146
|
+
connectent à l'adresse (IP + port) affichée dans la fenêtre de leur
|
|
147
|
+
salle respective.
|
|
148
|
+
|
|
149
|
+
**Windows** : le pare-feu autorise généralement par chemin d'exécutable --
|
|
150
|
+
avec des copies de dossier séparées, prévoir de valider l'alerte une fois
|
|
151
|
+
par copie au premier lancement.
|
|
152
|
+
|
|
125
153
|
## Contribuer
|
|
126
154
|
|
|
127
155
|
Envie de proposer un correctif, une idée, ou juste signaler un bug ? Voir
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_20cm_4spot.jpg
RENAMED
|
File without changes
|
{fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/flint_35cm_1spot.jpg
RENAMED
|
File without changes
|
{fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_compound.jpg
RENAMED
|
File without changes
|
{fletchtime-0.2.1 → fletchtime-0.2.2}/src/fletchtime/web/_defaults/targets/indoor_recurve.jpg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|