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.
Files changed (85) hide show
  1. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/HISTORY.md +8 -0
  2. {config_cli_gui-0.3.3/src/config_cli_gui.egg-info → config_cli_gui-0.3.4}/PKG-INFO +2 -1
  3. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/config.yaml +2 -2
  4. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/usage/config.md +10 -10
  5. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/pyproject.toml +1 -0
  6. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/_version.py +3 -3
  7. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/config.py +14 -10
  8. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/docs.py +8 -4
  9. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/gui.py +91 -57
  10. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4/src/config_cli_gui.egg-info}/PKG-INFO +2 -1
  11. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/requires.txt +1 -0
  12. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/config/config_example.py +21 -3
  13. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/gui_example.py +16 -2
  14. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/uv.lock +14 -0
  15. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/FUNDING.yml +0 -0
  16. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  17. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  18. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/actions/setup-environment/action.yml +0 -0
  20. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/dependabot.yml +0 -0
  21. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/init.sh +0 -0
  22. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/release_message.sh +0 -0
  23. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/update_funding.py +0 -0
  24. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/workflows/main.yml +0 -0
  25. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/workflows/release.yml +0 -0
  26. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.github/workflows/update_readme.yml +0 -0
  27. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.gitignore +0 -0
  28. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/config_generate.xml +0 -0
  29. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/example_project_cli.xml +0 -0
  30. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.idea/runConfigurations/example_project_gui.xml +0 -0
  31. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.pre-commit-config.yaml +0 -0
  32. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/.readthedocs.yaml +0 -0
  33. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/LICENSE +0 -0
  34. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/Makefile +0 -0
  35. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/README.md +0 -0
  36. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/.nav.yml +0 -0
  37. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/favicon.png +0 -0
  38. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/logo.png +0 -0
  39. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/settings_dlg.png +0 -0
  40. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/_static/img/settings_doc.png +0 -0
  41. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/css/custom.css +0 -0
  42. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/develop/contributing.md +0 -0
  43. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/develop/make_windows.md +0 -0
  44. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/develop/naming_convention.md +0 -0
  45. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/funding/funding.md +0 -0
  46. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/getting-started/install.md +0 -0
  47. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/getting-started/virtual-environment.md +0 -0
  48. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/index.md +0 -0
  49. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/docs/usage/cli.md +0 -0
  50. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/mkdocs.yml +0 -0
  51. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/show_filelist.ps1 +0 -0
  52. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/show_tree.ps1 +0 -0
  53. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/show_tree.py +0 -0
  54. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/scripts/update_readme.py +0 -0
  55. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/setup.cfg +0 -0
  56. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/__init__.py +0 -0
  57. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/__init__.py +0 -0
  58. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/cli.py +0 -0
  59. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/__init__.py +0 -0
  60. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/color.py +0 -0
  61. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/font.py +0 -0
  62. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/configtypes/vector.py +0 -0
  63. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui/logging.py +0 -0
  64. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/SOURCES.txt +0 -0
  65. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
  66. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
  67. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/src/config_cli_gui.egg-info/top_level.txt +0 -0
  68. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/template.yml.url +0 -0
  69. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/__init__.py +0 -0
  70. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/__init__.py +0 -0
  71. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/__main__.py +0 -0
  72. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/cli/__init__.py +0 -0
  73. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/cli/__main__.py +0 -0
  74. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/cli/cli_example.py +0 -0
  75. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/config/__init__.py +0 -0
  76. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/core/__init__.py +0 -0
  77. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/core/base.py +0 -0
  78. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/example.gpx +0 -0
  79. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/__init__.py +0 -0
  80. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/__main__.py +0 -0
  81. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/example_project/gui/config.yaml +0 -0
  82. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_cli.py +0 -0
  83. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_config_manager.py +0 -0
  84. {config_cli_gui-0.3.3 → config_cli_gui-0.3.4}/tests/test_docs.py +0 -0
  85. {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
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=['light', 'dark', 'auto']
31
- theme: light
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 | 'light' | ['light', 'dark', 'auto'] |
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
 
@@ -11,6 +11,7 @@ dependencies = [
11
11
  "pydantic>=2.11.7",
12
12
  "pyyaml>=6.0.2",
13
13
  "pillow>=12.0.0",
14
+ "ttkbootstrap>=1.20.3",
14
15
  ]
15
16
 
16
17
  # Dev dependencies as optional dependencies
@@ -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.3'
22
- __version_tuple__ = version_tuple = (0, 3, 3)
21
+ __version__ = version = '0.3.4'
22
+ __version_tuple__ = version_tuple = (0, 3, 4)
23
23
 
24
- __commit_id__ = commit_id = 'gbc73a5894'
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: Font.from_list(v)
67
- if isinstance(v, list)
68
- else Font.from_str(v),
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: Color.from_list(v)
73
- if isinstance(v, list)
74
- else (Color.from_hex(v) if isinstance(v, str) else v),
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: Vector.from_list(v)
79
- if isinstance(v, list)
80
- else (Vector.from_str(v) if isinstance(v, str) else v),
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
- """).lstrip()
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(f"""
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, parent, config_manager: ConfigManager, title="Settings", config_file="config.yaml"
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, textvariable=font_type_var, values=Font.font_names, state="readonly", width=25
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), text, fill=font_color_hex, font=font, anchor="mm"
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
- # Update configuration with widget values
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
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"
@@ -1,6 +1,7 @@
1
1
  pydantic>=2.11.7
2
2
  pyyaml>=6.0.2
3
3
  pillow>=12.0.0
4
+ ttkbootstrap>=1.20.3
4
5
 
5
6
  [dev]
6
7
  pytest>=8.4.0
@@ -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="light",
133
- choices=["light", "dark", "auto"],
134
- help="GUI theme setting",
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
- root = tk.Tk()
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