config-cli-gui 0.3.3__tar.gz → 0.3.4__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.
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/HISTORY.md +8 -0
- {config_cli_gui-0.3.3/src/config_cli_gui.egg-info → config_cli_gui-0.3.4}/PKG-INFO +2 -1
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/config.yaml +2 -2
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/usage/config.md +10 -10
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/pyproject.toml +1 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/_version.py +3 -3
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/config.py +14 -10
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/docs.py +8 -4
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/gui.py +91 -57
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4/src/config_cli_gui.egg-info}/PKG-INFO +2 -1
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/requires.txt +1 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/config/config_example.py +21 -3
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/gui_example.py +16 -2
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/uv.lock +14 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/FUNDING.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/actions/setup-environment/action.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/dependabot.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/init.sh +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/release_message.sh +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/update_funding.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/workflows/main.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/workflows/release.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/workflows/update_readme.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.gitignore +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/config_generate.xml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/example_project_cli.xml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/example_project_gui.xml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.pre-commit-config.yaml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.readthedocs.yaml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/LICENSE +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/Makefile +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/README.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/.nav.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/favicon.png +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/logo.png +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/settings_dlg.png +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/settings_doc.png +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/css/custom.css +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/develop/contributing.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/develop/make_windows.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/develop/naming_convention.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/funding/funding.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/getting-started/install.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/getting-started/virtual-environment.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/index.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/usage/cli.md +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/mkdocs.yml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/show_filelist.ps1 +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/show_tree.ps1 +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/show_tree.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/update_readme.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/setup.cfg +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/cli.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/color.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/font.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/vector.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/logging.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/SOURCES.txt +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/top_level.txt +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/template.yml.url +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/__main__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/cli/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/cli/__main__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/cli/cli_example.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/config/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/core/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/core/base.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/example.gpx +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/__init__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/__main__.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/config.yaml +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_cli.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_config_manager.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_docs.py +0 -0
- {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_generic_cli.py +0 -0
|
@@ -4,6 +4,14 @@ Changelog
|
|
|
4
4
|
|
|
5
5
|
(unreleased)
|
|
6
6
|
------------
|
|
7
|
+
- Bugfix: init color display properly. [Paul Magister]
|
|
8
|
+
- Use ttkbootstrap gui styles. [Paul Magister]
|
|
9
|
+
- Improve GUI scaling and font sizes. [Paul Magister]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
0.3.3 (2026-06-13)
|
|
13
|
+
------------------
|
|
14
|
+
- Docs: Update HISTORY.md for release 0.3.3. [Paul Magister]
|
|
7
15
|
- Update to minimum python 3.11. [Paul Magister]
|
|
8
16
|
|
|
9
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: config-cli-gui
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Feature-rich Python project template for config-cli-gui.
|
|
5
5
|
Author: pamagister
|
|
6
6
|
Requires-Python: <3.12,>=3.11
|
|
@@ -9,6 +9,7 @@ License-File: LICENSE
|
|
|
9
9
|
Requires-Dist: pydantic>=2.11.7
|
|
10
10
|
Requires-Dist: pyyaml>=6.0.2
|
|
11
11
|
Requires-Dist: pillow>=12.0.0
|
|
12
|
+
Requires-Dist: ttkbootstrap>=1.20.3
|
|
12
13
|
Provides-Extra: dev
|
|
13
14
|
Requires-Dist: pytest>=8.4.0; extra == "dev"
|
|
14
15
|
Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
|
|
@@ -27,8 +27,8 @@ app:
|
|
|
27
27
|
# Enable logging to console | type=bool | choices=[True, False]
|
|
28
28
|
enable_console_logging: true
|
|
29
29
|
gui:
|
|
30
|
-
# GUI theme setting | type=str | choices=['
|
|
31
|
-
theme:
|
|
30
|
+
# GUI theme setting suppoerted by ttkbootstrap | type=str | choices=['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone', 'yeti', 'pulse', 'united', 'darkly', 'superhero', 'solar', 'cyborg', 'vapor', 'simplex']
|
|
31
|
+
theme: darkly
|
|
32
32
|
# Default window width | type=int
|
|
33
33
|
window_width: 800
|
|
34
34
|
# Default window height | type=int
|
|
@@ -28,16 +28,16 @@ The parameters in the cli category can be accessed via the command line interfac
|
|
|
28
28
|
|
|
29
29
|
## Category "gui"
|
|
30
30
|
|
|
31
|
-
| Name | Type | Description | Default | Choices
|
|
32
|
-
|
|
33
|
-
| theme | str | GUI theme setting
|
|
34
|
-
| window_width | int | Default window width | 800 | -
|
|
35
|
-
| window_height | int | Default window height | 600 | -
|
|
36
|
-
| log_window_height | int | Height of the log window in pixels | 200 | -
|
|
37
|
-
| auto_scroll_log | bool | Automatically scroll to the newest log entries | True | [True, False]
|
|
38
|
-
| max_log_lines | int | Maximum number of log lines to keep in GUI | 1000 | -
|
|
39
|
-
| point2D | Vector | Point in 2D space | Vector(7, 11) | -
|
|
40
|
-
| point3D | Vector | Point in 3D space | Vector(1.2, 3.4, 5.6) | -
|
|
31
|
+
| Name | Type | Description | Default | Choices |
|
|
32
|
+
|-------------------|--------|------------------------------------------------|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
33
|
+
| theme | str | GUI theme setting suppoerted by ttkbootstrap | 'darkly' | ['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone', 'yeti', 'pulse', 'united', 'darkly', 'superhero', 'solar', 'cyborg', 'vapor', 'simplex'] |
|
|
34
|
+
| window_width | int | Default window width | 800 | - |
|
|
35
|
+
| window_height | int | Default window height | 600 | - |
|
|
36
|
+
| log_window_height | int | Height of the log window in pixels | 200 | - |
|
|
37
|
+
| auto_scroll_log | bool | Automatically scroll to the newest log entries | True | [True, False] |
|
|
38
|
+
| max_log_lines | int | Maximum number of log lines to keep in GUI | 1000 | - |
|
|
39
|
+
| point2D | Vector | Point in 2D space | Vector(7, 11) | - |
|
|
40
|
+
| point3D | Vector | Point in 3D space | Vector(1.2, 3.4, 5.6) | - |
|
|
41
41
|
|
|
42
42
|
## Category "misc"
|
|
43
43
|
|
|
@@ -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.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
21
|
+
__version__ = version = '0.3.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 4)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gf109662f6'
|
|
@@ -63,21 +63,25 @@ class ConfigSerializer:
|
|
|
63
63
|
TYPE_MAPPING = {
|
|
64
64
|
Font: {
|
|
65
65
|
"to_serializable": lambda v: v.to_str(),
|
|
66
|
-
"from_serializable": lambda v:
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
"from_serializable": lambda v: (
|
|
67
|
+
Font.from_list(v) if isinstance(v, list) else Font.from_str(v)
|
|
68
|
+
),
|
|
69
69
|
},
|
|
70
70
|
Color: {
|
|
71
71
|
"to_serializable": lambda v: v.to_hex(),
|
|
72
|
-
"from_serializable": lambda v:
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
"from_serializable": lambda v: (
|
|
73
|
+
Color.from_list(v)
|
|
74
|
+
if isinstance(v, list)
|
|
75
|
+
else (Color.from_hex(v) if isinstance(v, str) else v)
|
|
76
|
+
),
|
|
75
77
|
},
|
|
76
78
|
Vector: {
|
|
77
79
|
"to_serializable": lambda v: v.to_str(),
|
|
78
|
-
"from_serializable": lambda v:
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
"from_serializable": lambda v: (
|
|
81
|
+
Vector.from_list(v)
|
|
82
|
+
if isinstance(v, list)
|
|
83
|
+
else (Vector.from_str(v) if isinstance(v, str) else v)
|
|
84
|
+
),
|
|
81
85
|
},
|
|
82
86
|
Path: {
|
|
83
87
|
"to_serializable": lambda v: str(v.as_posix()),
|
|
@@ -85,7 +89,7 @@ class ConfigSerializer:
|
|
|
85
89
|
},
|
|
86
90
|
datetime: {
|
|
87
91
|
"to_serializable": lambda v: v.isoformat(),
|
|
88
|
-
"from_serializable": lambda v: datetime.fromisoformat(v) if isinstance(v, str) else v,
|
|
92
|
+
"from_serializable": lambda v: (datetime.fromisoformat(v) if isinstance(v, str) else v),
|
|
89
93
|
},
|
|
90
94
|
}
|
|
91
95
|
|
|
@@ -16,13 +16,15 @@ class DocumentationGenerator:
|
|
|
16
16
|
def pad(s, width):
|
|
17
17
|
return s + " " * (width - len(s))
|
|
18
18
|
|
|
19
|
-
markdown_content = dedent(
|
|
19
|
+
markdown_content = dedent(
|
|
20
|
+
"""
|
|
20
21
|
# Configuration Parameters
|
|
21
22
|
|
|
22
23
|
These parameters are available to configure the behavior of your application.
|
|
23
24
|
The parameters in the cli category can be accessed via the command line interface.
|
|
24
25
|
|
|
25
|
-
"""
|
|
26
|
+
"""
|
|
27
|
+
).lstrip()
|
|
26
28
|
|
|
27
29
|
for category_name, category in self.config_manager._categories.items():
|
|
28
30
|
markdown_content += f'## Category "{category_name}"\n\n'
|
|
@@ -165,13 +167,15 @@ class DocumentationGenerator:
|
|
|
165
167
|
continue
|
|
166
168
|
example_value = param.choices[0] if param.choices else param.value
|
|
167
169
|
examples.append(
|
|
168
|
-
dedent(
|
|
170
|
+
dedent(
|
|
171
|
+
f"""
|
|
169
172
|
### {i}. With {param.name} parameter
|
|
170
173
|
|
|
171
174
|
```bash
|
|
172
175
|
python -m {app_name} --{param.name} {example_value} {required_arg}
|
|
173
176
|
```
|
|
174
|
-
"""
|
|
177
|
+
"""
|
|
178
|
+
)
|
|
175
179
|
)
|
|
176
180
|
|
|
177
181
|
markdown = dedent(
|
|
@@ -8,6 +8,7 @@ from datetime import datetime
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from tkinter import colorchooser, filedialog, messagebox, ttk
|
|
10
10
|
|
|
11
|
+
import ttkbootstrap
|
|
11
12
|
from PIL import Image, ImageDraw, ImageTk
|
|
12
13
|
|
|
13
14
|
from config_cli_gui.config import (
|
|
@@ -205,7 +206,11 @@ class GenericSettingsDialog:
|
|
|
205
206
|
"""Generic settings dialog for ConfigManager."""
|
|
206
207
|
|
|
207
208
|
def __init__(
|
|
208
|
-
self,
|
|
209
|
+
self,
|
|
210
|
+
parent: ttkbootstrap.Window | tk.Tk,
|
|
211
|
+
config_manager: ConfigManager,
|
|
212
|
+
title="Settings",
|
|
213
|
+
config_file="config.yaml",
|
|
209
214
|
):
|
|
210
215
|
self.parent = parent
|
|
211
216
|
self.config_manager = config_manager
|
|
@@ -246,6 +251,10 @@ class GenericSettingsDialog:
|
|
|
246
251
|
button_frame = ttk.Frame(main_frame)
|
|
247
252
|
button_frame.pack(fill=tk.X, pady=(10, 0))
|
|
248
253
|
|
|
254
|
+
# Apply: apply current settings (in-memory) but keep dialog open
|
|
255
|
+
ttk.Button(button_frame, text="Apply", command=self._on_apply).pack(
|
|
256
|
+
side=tk.RIGHT, padx=(5, 0)
|
|
257
|
+
)
|
|
249
258
|
ttk.Button(button_frame, text="OK", command=self._on_ok).pack(side=tk.RIGHT, padx=(5, 0))
|
|
250
259
|
ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(side=tk.RIGHT)
|
|
251
260
|
|
|
@@ -429,6 +438,7 @@ class GenericSettingsDialog:
|
|
|
429
438
|
|
|
430
439
|
pick_btn = ttk.Button(frame, text="Pick", command=pick_color)
|
|
431
440
|
pick_btn.pack(side=tk.LEFT, padx=(5, 0))
|
|
441
|
+
color_display.config(bg=color_value.to_hex())
|
|
432
442
|
|
|
433
443
|
def on_color_change(*args):
|
|
434
444
|
try:
|
|
@@ -450,7 +460,11 @@ class GenericSettingsDialog:
|
|
|
450
460
|
# Font type
|
|
451
461
|
font_type_var = tk.StringVar(value=os.path.basename(font_value.name))
|
|
452
462
|
font_type_combo = ttk.Combobox(
|
|
453
|
-
frame,
|
|
463
|
+
frame,
|
|
464
|
+
textvariable=font_type_var,
|
|
465
|
+
values=Font.font_names,
|
|
466
|
+
state="readonly",
|
|
467
|
+
width=25,
|
|
454
468
|
)
|
|
455
469
|
font_type_combo.pack(side=tk.LEFT, padx=(0, 5))
|
|
456
470
|
|
|
@@ -463,6 +477,7 @@ class GenericSettingsDialog:
|
|
|
463
477
|
color_var = tk.StringVar(value=font_value.color.to_hex())
|
|
464
478
|
color_display = tk.Label(frame, width=8, bg=font_value.color.to_hex())
|
|
465
479
|
color_display.pack(side=tk.LEFT, padx=(8, 2))
|
|
480
|
+
color_display.config(bg=font_value.color.to_hex())
|
|
466
481
|
|
|
467
482
|
def pick_color():
|
|
468
483
|
color = colorchooser.askcolor(color=color_var.get())
|
|
@@ -503,7 +518,11 @@ class GenericSettingsDialog:
|
|
|
503
518
|
text = "Sample"
|
|
504
519
|
|
|
505
520
|
draw.text(
|
|
506
|
-
(img_width / 2, img_height / 2),
|
|
521
|
+
(img_width / 2, img_height / 2),
|
|
522
|
+
text,
|
|
523
|
+
fill=font_color_hex,
|
|
524
|
+
font=font,
|
|
525
|
+
anchor="mm",
|
|
507
526
|
)
|
|
508
527
|
|
|
509
528
|
photo = ImageTk.PhotoImage(img)
|
|
@@ -617,66 +636,81 @@ class GenericSettingsDialog:
|
|
|
617
636
|
def _on_ok(self):
|
|
618
637
|
"""Handle OK button click."""
|
|
619
638
|
try:
|
|
620
|
-
|
|
621
|
-
overrides = {}
|
|
622
|
-
for key, widget in self.widgets.items():
|
|
623
|
-
category_name, param_name = key.split("__", 1)
|
|
624
|
-
category = self.config_manager.get_category(category_name)
|
|
625
|
-
param_value = getattr(category, param_name).value
|
|
626
|
-
|
|
627
|
-
if isinstance(param_value, Font):
|
|
628
|
-
selected_font_name = widget.font_type_var.get()
|
|
629
|
-
font_type = selected_font_name
|
|
630
|
-
font_size = widget.font_size_var.get()
|
|
631
|
-
font_color = Color.from_hex(widget.color_var.get())
|
|
632
|
-
overrides[key] = Font(font_type, font_size, font_color)
|
|
633
|
-
continue
|
|
634
|
-
|
|
635
|
-
if isinstance(param_value, Vector):
|
|
636
|
-
components = [v.get() for v in widget.vars]
|
|
637
|
-
overrides[key] = Vector.from_list(components)
|
|
638
|
-
continue
|
|
639
|
-
|
|
640
|
-
value = widget.var.get()
|
|
641
|
-
|
|
642
|
-
# Convert value to appropriate type
|
|
643
|
-
if type(param_value) == bool:
|
|
644
|
-
overrides[key] = value
|
|
645
|
-
elif type(param_value) == Path:
|
|
646
|
-
overrides[key] = Path(value)
|
|
647
|
-
elif type(param_value) == Color:
|
|
648
|
-
overrides[key] = Color.from_hex(value)
|
|
649
|
-
elif type(param_value) == datetime:
|
|
650
|
-
overrides[key] = datetime.strptime(value, "%Y-%m-%d %H:%M")
|
|
651
|
-
elif type(param_value) in (list, tuple):
|
|
652
|
-
# Parse comma-separated values
|
|
653
|
-
items = [item.strip() for item in value.split(",") if item.strip()]
|
|
654
|
-
overrides[key] = type(param_value)(items)
|
|
655
|
-
elif type(param_value) == dict:
|
|
656
|
-
# Parse JSON format
|
|
657
|
-
import json
|
|
658
|
-
|
|
659
|
-
overrides[key] = json.loads(value)
|
|
660
|
-
elif type(param_value) == int:
|
|
661
|
-
overrides[key] = int(value)
|
|
662
|
-
elif type(param_value) == float:
|
|
663
|
-
overrides[key] = float(value)
|
|
664
|
-
else:
|
|
665
|
-
overrides[key] = value
|
|
666
|
-
|
|
667
|
-
# Apply overrides to config manager
|
|
668
|
-
self.config_manager.apply_overrides(overrides)
|
|
669
|
-
|
|
670
|
-
# Save to file
|
|
671
|
-
self.config_manager.save_to_file(self.config_file)
|
|
672
|
-
|
|
673
|
-
self.result = "ok"
|
|
639
|
+
self.__persist_settings()
|
|
674
640
|
self.dialog.destroy()
|
|
675
641
|
|
|
676
642
|
except Exception as e:
|
|
677
643
|
messagebox.showerror("Error", f"Failed to save configuration: {e}")
|
|
678
644
|
|
|
645
|
+
def __persist_settings(self):
|
|
646
|
+
# Update configuration with widget values
|
|
647
|
+
overrides = {}
|
|
648
|
+
for key, widget in self.widgets.items():
|
|
649
|
+
category_name, param_name = key.split("__", 1)
|
|
650
|
+
category = self.config_manager.get_category(category_name)
|
|
651
|
+
param_value = getattr(category, param_name).value
|
|
652
|
+
|
|
653
|
+
if isinstance(param_value, Font):
|
|
654
|
+
selected_font_name = widget.font_type_var.get()
|
|
655
|
+
font_type = selected_font_name
|
|
656
|
+
font_size = widget.font_size_var.get()
|
|
657
|
+
font_color = Color.from_hex(widget.color_var.get())
|
|
658
|
+
overrides[key] = Font(font_type, font_size, font_color)
|
|
659
|
+
continue
|
|
660
|
+
|
|
661
|
+
if isinstance(param_value, Vector):
|
|
662
|
+
components = [v.get() for v in widget.vars]
|
|
663
|
+
overrides[key] = Vector.from_list(components)
|
|
664
|
+
continue
|
|
665
|
+
|
|
666
|
+
value = widget.var.get()
|
|
667
|
+
|
|
668
|
+
# Convert value to appropriate type
|
|
669
|
+
if type(param_value) == bool:
|
|
670
|
+
overrides[key] = value
|
|
671
|
+
elif type(param_value) == Path:
|
|
672
|
+
overrides[key] = Path(value)
|
|
673
|
+
elif type(param_value) == Color:
|
|
674
|
+
overrides[key] = Color.from_hex(value)
|
|
675
|
+
elif type(param_value) == datetime:
|
|
676
|
+
overrides[key] = datetime.strptime(value, "%Y-%m-%d %H:%M")
|
|
677
|
+
elif type(param_value) in (list, tuple):
|
|
678
|
+
# Parse comma-separated values
|
|
679
|
+
items = [item.strip() for item in value.split(",") if item.strip()]
|
|
680
|
+
overrides[key] = type(param_value)(items)
|
|
681
|
+
elif type(param_value) == dict:
|
|
682
|
+
# Parse JSON format
|
|
683
|
+
import json
|
|
684
|
+
|
|
685
|
+
overrides[key] = json.loads(value)
|
|
686
|
+
elif type(param_value) == int:
|
|
687
|
+
overrides[key] = int(value)
|
|
688
|
+
elif type(param_value) == float:
|
|
689
|
+
overrides[key] = float(value)
|
|
690
|
+
else:
|
|
691
|
+
overrides[key] = value
|
|
692
|
+
|
|
693
|
+
# Apply overrides to config manager (in-memory)
|
|
694
|
+
self.config_manager.apply_overrides(overrides)
|
|
695
|
+
|
|
696
|
+
# Persist to file
|
|
697
|
+
self.config_manager.save_to_file(self.config_file)
|
|
698
|
+
|
|
699
|
+
self.result = "ok"
|
|
700
|
+
|
|
679
701
|
def _on_cancel(self):
|
|
680
702
|
"""Handle Cancel button click."""
|
|
681
703
|
self.result = "cancel"
|
|
682
704
|
self.dialog.destroy()
|
|
705
|
+
|
|
706
|
+
def _on_apply(self):
|
|
707
|
+
"""Apply current settings without closing the dialog.
|
|
708
|
+
|
|
709
|
+
This applies the widget values to the in-memory configuration and
|
|
710
|
+
applies the theme change live if the GUI theme parameter is present.
|
|
711
|
+
"""
|
|
712
|
+
try:
|
|
713
|
+
self.__persist_settings()
|
|
714
|
+
|
|
715
|
+
except Exception as e:
|
|
716
|
+
messagebox.showerror("Error", f"Failed to save configuration: {e}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: config-cli-gui
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Feature-rich Python project template for config-cli-gui.
|
|
5
5
|
Author: pamagister
|
|
6
6
|
Requires-Python: <3.12,>=3.11
|
|
@@ -9,6 +9,7 @@ License-File: LICENSE
|
|
|
9
9
|
Requires-Dist: pydantic>=2.11.7
|
|
10
10
|
Requires-Dist: pyyaml>=6.0.2
|
|
11
11
|
Requires-Dist: pillow>=12.0.0
|
|
12
|
+
Requires-Dist: ttkbootstrap>=1.20.3
|
|
12
13
|
Provides-Extra: dev
|
|
13
14
|
Requires-Dist: pytest>=8.4.0; extra == "dev"
|
|
14
15
|
Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
|
{config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/config/config_example.py
RENAMED
|
@@ -8,6 +8,7 @@ and documentation from the parameter definitions.
|
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
|
+
# import ttkbootstrap
|
|
11
12
|
from config_cli_gui.config import (
|
|
12
13
|
ConfigCategory,
|
|
13
14
|
ConfigManager,
|
|
@@ -127,11 +128,28 @@ class GuiConfig(ConfigCategory):
|
|
|
127
128
|
def get_category_name(self) -> str:
|
|
128
129
|
return "gui"
|
|
129
130
|
|
|
131
|
+
# ttkbootstrap.Style().theme_names()
|
|
130
132
|
theme: ConfigParameter = ConfigParameter(
|
|
131
133
|
name="theme",
|
|
132
|
-
value="
|
|
133
|
-
choices=[
|
|
134
|
-
|
|
134
|
+
value="darkly",
|
|
135
|
+
choices=[
|
|
136
|
+
"cosmo",
|
|
137
|
+
"flatly",
|
|
138
|
+
"litera",
|
|
139
|
+
"minty",
|
|
140
|
+
"lumen",
|
|
141
|
+
"sandstone",
|
|
142
|
+
"yeti",
|
|
143
|
+
"pulse",
|
|
144
|
+
"united",
|
|
145
|
+
"darkly",
|
|
146
|
+
"superhero",
|
|
147
|
+
"solar",
|
|
148
|
+
"cyborg",
|
|
149
|
+
"vapor",
|
|
150
|
+
"simplex",
|
|
151
|
+
],
|
|
152
|
+
help="GUI theme setting suppoerted by ttkbootstrap",
|
|
135
153
|
)
|
|
136
154
|
|
|
137
155
|
window_width: ConfigParameter = ConfigParameter(
|
|
@@ -15,7 +15,9 @@ import traceback
|
|
|
15
15
|
import webbrowser
|
|
16
16
|
from functools import partial
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from tkinter import filedialog, messagebox, ttk
|
|
18
|
+
from tkinter import filedialog, font, messagebox, ttk
|
|
19
|
+
|
|
20
|
+
import ttkbootstrap
|
|
19
21
|
|
|
20
22
|
from config_cli_gui.gui import SettingsDialogGenerator
|
|
21
23
|
from config_cli_gui.logging import (
|
|
@@ -526,7 +528,19 @@ class MainGui:
|
|
|
526
528
|
|
|
527
529
|
def main():
|
|
528
530
|
"""Main entry point for the GUI application."""
|
|
529
|
-
|
|
531
|
+
|
|
532
|
+
# Determine theme from configuration (fall back to sandstone on error)
|
|
533
|
+
|
|
534
|
+
_config = ProjectConfigManager("config.yaml")
|
|
535
|
+
theme_choice = _config.gui.theme.value
|
|
536
|
+
|
|
537
|
+
root: ttkbootstrap.Window = ttkbootstrap.Window(themename=theme_choice)
|
|
538
|
+
|
|
539
|
+
font.nametofont("TkDefaultFont").configure(size=11)
|
|
540
|
+
font.nametofont("TkTextFont").configure(size=11)
|
|
541
|
+
font.nametofont("TkMenuFont").configure(size=11)
|
|
542
|
+
font.nametofont("TkHeadingFont").configure(size=11)
|
|
543
|
+
|
|
530
544
|
try:
|
|
531
545
|
MainGui(root)
|
|
532
546
|
root.mainloop()
|
|
@@ -87,6 +87,7 @@ dependencies = [
|
|
|
87
87
|
{ name = "pillow" },
|
|
88
88
|
{ name = "pydantic" },
|
|
89
89
|
{ name = "pyyaml" },
|
|
90
|
+
{ name = "ttkbootstrap" },
|
|
90
91
|
]
|
|
91
92
|
|
|
92
93
|
[package.optional-dependencies]
|
|
@@ -130,6 +131,7 @@ requires-dist = [
|
|
|
130
131
|
{ name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.14.1" },
|
|
131
132
|
{ name = "pyyaml", specifier = ">=6.0.2" },
|
|
132
133
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11.13" },
|
|
134
|
+
{ name = "ttkbootstrap", specifier = ">=1.20.3" },
|
|
133
135
|
]
|
|
134
136
|
provides-extras = ["dev", "docs"]
|
|
135
137
|
|
|
@@ -799,6 +801,18 @@ wheels = [
|
|
|
799
801
|
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
|
|
800
802
|
]
|
|
801
803
|
|
|
804
|
+
[[package]]
|
|
805
|
+
name = "ttkbootstrap"
|
|
806
|
+
version = "1.20.3"
|
|
807
|
+
source = { registry = "https://pypi.org/simple" }
|
|
808
|
+
dependencies = [
|
|
809
|
+
{ name = "pillow" },
|
|
810
|
+
]
|
|
811
|
+
sdist = { url = "https://files.pythonhosted.org/packages/bd/88/bb1535edbd12e7b60223bee86241368b5d27043c001ef81d620ae8a56faf/ttkbootstrap-1.20.3.tar.gz", hash = "sha256:3350b80f79b0454c12558a7174c4bc4bd47905758843c3e8b0fbb19a0286fb69", size = 176262, upload-time = "2026-05-07T03:09:26.443Z" }
|
|
812
|
+
wheels = [
|
|
813
|
+
{ url = "https://files.pythonhosted.org/packages/13/48/a5fd0d5d360e363f9bb289bef29b66103fd4ae1be1e3b6896a01b6e0f993/ttkbootstrap-1.20.3-py3-none-any.whl", hash = "sha256:95a05a4a663e3047be54edc220aa59c494a54f5c617a72a2c59a4bf634b97a5a", size = 191453, upload-time = "2026-05-07T03:09:24.935Z" },
|
|
814
|
+
]
|
|
815
|
+
|
|
802
816
|
[[package]]
|
|
803
817
|
name = "typing-extensions"
|
|
804
818
|
version = "4.15.0"
|
|
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
|
{config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/example_project_cli.xml
RENAMED
|
File without changes
|
{config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/example_project_gui.xml
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/dependency_links.txt
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
|