config-cli-gui 0.1.1__tar.gz → 0.1.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.
Files changed (83) hide show
  1. config_cli_gui-0.1.2/HISTORY.md +25 -0
  2. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/Makefile +0 -32
  3. {config_cli_gui-0.1.1/src/config_cli_gui.egg-info → config_cli_gui-0.1.2}/PKG-INFO +1 -1
  4. config_cli_gui-0.1.2/config.yaml +51 -0
  5. config_cli_gui-0.1.2/docs/getting-started/install.md +28 -0
  6. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/usage/cli.md +17 -3
  7. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/usage/config.md +8 -0
  8. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/_version.py +2 -2
  9. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/config_framework.py +70 -6
  10. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/docs_generator.py +29 -18
  11. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/gui_generator.py +28 -34
  12. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2/src/config_cli_gui.egg-info}/PKG-INFO +1 -1
  13. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/SOURCES.txt +0 -4
  14. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/config/config.py +31 -7
  15. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/test_generic_cli.py +1 -1
  16. config_cli_gui-0.1.1/.github/workflows/build-macos.yml +0 -48
  17. config_cli_gui-0.1.1/.github/workflows/build.yml +0 -46
  18. config_cli_gui-0.1.1/.idea/runConfigurations/module_gui.xml +0 -25
  19. config_cli_gui-0.1.1/HISTORY.md +0 -16
  20. config_cli_gui-0.1.1/config.yaml +0 -96
  21. config_cli_gui-0.1.1/docs/develop/pypi_release.md +0 -233
  22. config_cli_gui-0.1.1/docs/getting-started/install.md +0 -66
  23. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/FUNDING.yml +0 -0
  24. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  26. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  27. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/actions/setup-environment/action.yml +0 -0
  28. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/dependabot.yml +0 -0
  29. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/init.sh +0 -0
  30. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/release_message.sh +0 -0
  31. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/update_funding.py +0 -0
  32. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/workflows/main.yml +0 -0
  33. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/workflows/release.yml +0 -0
  34. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/workflows/update_readme.yml +0 -0
  35. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.gitignore +0 -0
  36. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.idea/runConfigurations/config_generate.xml +0 -0
  37. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.pre-commit-config.yaml +0 -0
  38. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.readthedocs.yaml +0 -0
  39. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/LICENSE +0 -0
  40. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/README.md +0 -0
  41. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/.nav.yml +1 -1
  42. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/_static/img/favicon.png +0 -0
  43. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/_static/img/logo.png +0 -0
  44. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/css/custom.css +0 -0
  45. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/develop/contributing.md +0 -0
  46. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/develop/make_windows.md +0 -0
  47. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/develop/naming_convention.md +0 -0
  48. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/funding/funding.md +0 -0
  49. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/getting-started/virtual-environment.md +0 -0
  50. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/index.md +0 -0
  51. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/examples/kuhkopfsteig.gpx +0 -0
  52. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/examples/rother_lilienstein.gpx +0 -0
  53. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/examples/teneriffa.gpx +0 -0
  54. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/mkdocs.yml +0 -0
  55. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/pyproject.toml +0 -0
  56. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/show_filelist.ps1 +0 -0
  57. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/show_tree.ps1 +0 -0
  58. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/show_tree.py +0 -0
  59. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/update_readme.py +0 -0
  60. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/setup.cfg +0 -0
  61. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/__init__.py +0 -0
  62. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/__init__.py +0 -0
  63. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/cli_generator.py +0 -0
  64. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
  65. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
  66. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/requires.txt +0 -0
  67. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/top_level.txt +0 -0
  68. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/main.py +0 -0
  69. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/template.yml.url +0 -0
  70. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/__init__.py +0 -0
  71. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/__init__.py +0 -0
  72. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/__main__.py +0 -0
  73. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/cli/__init__.py +0 -0
  74. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/cli/__main__.py +0 -0
  75. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/cli/cli.py +0 -0
  76. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/config/__init__.py +0 -0
  77. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/core/__init__.py +0 -0
  78. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/core/base.py +0 -0
  79. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/core/logging.py +0 -0
  80. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/gui/__init__.py +0 -0
  81. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/gui/__main__.py +0 -0
  82. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/gui/gui.py +0 -0
  83. {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/uv.lock +0 -0
@@ -0,0 +1,25 @@
1
+ Changelog
2
+ =========
3
+
4
+
5
+ (unreleased)
6
+ ------------
7
+ - Feat #3: avoid type_ [Paul Magister]
8
+ - Feat #3: Improved paramter types: [Paul Magister]
9
+ - Feat #3: Improved paramter types: * move example project to tests *
10
+ remove type_ * add improved widgets to gui_generator.py * separate
11
+ module for docs_generator.py. [Paul Magister]
12
+
13
+
14
+ 0.1.0 (2025-06-22)
15
+ ------------------
16
+ - Remove unnecessary example files and deps. [Paul Magister]
17
+ - Update README.md from docs/index.md. [github-actions]
18
+ - Fix doc: formatting. [Paul Magister]
19
+
20
+
21
+ 0.0.2 (2025-06-22)
22
+ ------------------
23
+ - Remove _version.py. [Paul Magister]
24
+
25
+
@@ -55,38 +55,6 @@ test: lint ## Run tests and generate coverage report.
55
55
  uv run coverage xml
56
56
  uv run coverage html
57
57
 
58
- .PHONY: build-win
59
- build-win: ## Build the Windows executable.
60
- echo "Building unified CLI/GUI application"
61
- uv run pyinstaller --onefile src/main.py --name config-cli-gui --add-data "config.yaml;." --hidden-import config_cli_gui.cli.cli --hidden-import config_cli_gui.gui.gui
62
- rm -rf release
63
- mkdir release
64
- cp dist/config-cli-gui.exe release
65
- cp config.yaml release
66
- cp README.md release
67
-
68
- .PHONY: build-macos
69
- build-macos: ## Build the macOS executable.
70
- echo "Building unified CLI/GUI application as executable"
71
- uv run pyinstaller --onefile src/main.py --name config-cli-gui --add-data "config.yaml:." --hidden-import config_cli_gui.cli.cli --hidden-import config_cli_gui.gui.gui
72
-
73
- echo "Building unified CLI/GUI application as .app bundle"
74
- # --windowed is important to hide the console for GUI mode
75
- # The name "TemplateApp" becomes the name of the .app
76
- uv run pyinstaller --windowed --name "TemplateApp" src/main.py --add-data "config.yaml:." --hidden-import config_cli_gui.cli.cli --hidden-import config_cli_gui.gui.gui
77
-
78
- # Prepare ZIP file for release
79
- rm -rf release
80
- mkdir release
81
- echo "Copy the CLI/GUI executable"
82
- cp dist/config-cli-gui release/
83
- echo "Copy the .app bundle (directory) recursively"
84
- cp -R "dist/TemplateApp.app" release/
85
- echo "Copy configuration and documentation"
86
- cp config.yaml release/
87
- cp README.md release/
88
- echo "Create usage instructions"
89
-
90
58
  .PHONY: watch
91
59
  watch: ## Run tests on every change.
92
60
  ls **/**.py | entr uv run pytest -s -vvv -l --tb=long --maxfail=1 tests/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: config-cli-gui
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Feature-rich Python project template for config-cli-gui.
5
5
  Author: pamagister
6
6
  Requires-Python: <3.12,>=3.10
@@ -0,0 +1,51 @@
1
+ app:
2
+ # Date format to use | type=ConfigParameter, default=%Y-%m-%d
3
+ date_format: '%Y-%m-%d'
4
+ # Enable logging to console | type=ConfigParameter, default=True
5
+ enable_console_logging: true
6
+ # Enable logging to file | type=ConfigParameter, default=True
7
+ enable_file_logging: true
8
+ # Number of backup log files to keep | type=ConfigParameter, default=5
9
+ log_backup_count: 5
10
+ # Maximum log file size in MB before rotation | type=ConfigParameter, default=10
11
+ log_file_max_size: 10
12
+ # Log message format style | type=ConfigParameter, default=detailed
13
+ log_format: detailed
14
+ # Logging level for the application | type=ConfigParameter, default=INFO
15
+ log_level: INFO
16
+ # Maximum number of worker threads | type=ConfigParameter, default=4
17
+ max_workers: 4
18
+ cli:
19
+ # Include elevation data in waypoints | type=ConfigParameter, default=True
20
+ elevation: true
21
+ # Extract starting points of each track as waypoint | type=ConfigParameter, default=True
22
+ extract_waypoints: true
23
+ # Path to input (file or folder) | type=ConfigParameter, default=
24
+ input: ''
25
+ # Maximum distance between two waypoints | type=ConfigParameter, default=20
26
+ min_dist: 20
27
+ # Path to output destination | type=ConfigParameter, default=
28
+ output: ''
29
+ gui:
30
+ # Automatically scroll to newest log entries | type=ConfigParameter, default=True
31
+ auto_scroll_log: true
32
+ # Height of the log window in pixels | type=ConfigParameter, default=200
33
+ log_window_height: 200
34
+ # Maximum number of log lines to keep in GUI | type=ConfigParameter, default=1000
35
+ max_log_lines: 1000
36
+ # GUI theme setting | type=ConfigParameter, default=light
37
+ theme: light
38
+ # Default window height | type=ConfigParameter, default=600
39
+ window_height: 600
40
+ # Default window width | type=ConfigParameter, default=800
41
+ window_width: 800
42
+ misc:
43
+ # Color setting for the application | type=ConfigParameter, default=#ff0000
44
+ some_color:
45
+ - 255
46
+ - 0
47
+ - 0
48
+ # Date setting for the application | type=ConfigParameter, default=2025-06-23 18:58:52.946103
49
+ some_date: '2025-06-23T18:58:52.946103'
50
+ # Path to the file to use | type=ConfigParameter, default=some_file.txt
51
+ some_file: some_file.txt
@@ -0,0 +1,28 @@
1
+ # Installation
2
+
3
+
4
+ ## 🐍 PyPI
5
+
6
+ ### Install the package from PyPI
7
+
8
+ Download from [PyPI](https://pypi.org/):
9
+
10
+ ```bash
11
+ pip install config-cli-gui
12
+ ```
13
+
14
+ ## 👩🏼‍💻 Run from source
15
+
16
+ ### Clone the repository
17
+
18
+ ```bash
19
+ git clone
20
+ ```
21
+
22
+ ### Install dependencies
23
+
24
+ ```bash
25
+ uv venv
26
+ uv pip install -e .[dev,docs]
27
+ ```
28
+
@@ -26,19 +26,33 @@ python -m app [OPTIONS] input
26
26
  python -m app input
27
27
  ```
28
28
 
29
- ### 2. With min_dist parameter
29
+ ### 2. With verbose logging
30
+
31
+ ```bash
32
+ python -m app -v input
33
+ python -m app --verbose input
34
+ ```
35
+
36
+ ### 3. With quiet mode
37
+
38
+ ```bash
39
+ python -m app -q input
40
+ python -m app --quiet input
41
+ ```
42
+
43
+ ### 4. With min_dist parameter
30
44
 
31
45
  ```bash
32
46
  python -m app --min_dist 20 input
33
47
  ```
34
48
 
35
- ### 3. With extract_waypoints parameter
49
+ ### 5. With extract_waypoints parameter
36
50
 
37
51
  ```bash
38
52
  python -m app --extract_waypoints True input
39
53
  ```
40
54
 
41
- ### 4. With elevation parameter
55
+ ### 6. With elevation parameter
42
56
 
43
57
  ```bash
44
58
  python -m app --elevation True input
@@ -37,3 +37,11 @@ The parameters in the cli category can be accessed via the command line interfac
37
37
  | auto_scroll_log | bool | Automatically scroll to newest log entries | True | [True, False] |
38
38
  | max_log_lines | int | Maximum number of log lines to keep in GUI | 1000 | - |
39
39
 
40
+ ## Category "misc"
41
+
42
+ | Name | Type | Description | Default | Choices |
43
+ |------------|-------------|-----------------------------------|----------------------------------------------------|---------|
44
+ | some_file | WindowsPath | Path to the file to use | WindowsPath('some_file.txt') | - |
45
+ | some_color | Color | Color setting for the application | Color(255, 0, 0) | - |
46
+ | some_date | datetime | Date setting for the application | datetime.datetime(2025, 6, 23, 18, 58, 52, 946103) | - |
47
+
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.1'
21
- __version_tuple__ = version_tuple = (0, 1, 1)
20
+ __version__ = version = '0.1.2'
21
+ __version_tuple__ = version_tuple = (0, 1, 2)
@@ -1,6 +1,3 @@
1
- # config_framework/core.py
2
- """Generic configuration framework for Python applications."""
3
-
4
1
  import json
5
2
  from abc import ABC, abstractmethod
6
3
  from dataclasses import dataclass
@@ -93,7 +90,9 @@ class BaseConfigCategory(BaseModel, ABC):
93
90
  class ConfigManager:
94
91
  """Generic configuration manager that can handle multiple configuration categories."""
95
92
 
96
- def __init__(self, config_file: str = None, **kwargs):
93
+ def __init__(
94
+ self, categories: tuple[BaseConfigCategory, ...], config_file: str = None, **kwargs
95
+ ):
97
96
  """Initialize configuration manager.
98
97
 
99
98
  Args:
@@ -102,6 +101,14 @@ class ConfigManager:
102
101
  """
103
102
  self._categories: dict[str, BaseConfigCategory] = {}
104
103
 
104
+ for category in categories:
105
+ if isinstance(category, BaseConfigCategory):
106
+ self.add_category(category.get_category_name(), category)
107
+ else:
108
+ raise TypeError(
109
+ f"Category must be an instance of BaseConfigCategory, got {type(category)}"
110
+ )
111
+
105
112
  # Load from file if provided
106
113
  if config_file:
107
114
  self.load_from_file(config_file)
@@ -146,8 +153,14 @@ class ConfigManager:
146
153
  else:
147
154
  config_data = json.load(f)
148
155
 
156
+ # Store loaded data for later application
157
+ self._apply_config_data(config_data)
158
+
159
+ def _apply_config_data(self, _loaded_config_data):
160
+ """Apply configuration data to categories."""
161
+
149
162
  # Apply loaded configuration
150
- for category_name, category_data in config_data.items():
163
+ for category_name, category_data in _loaded_config_data.items():
151
164
  if category_name in self._categories:
152
165
  category = self._categories[category_name]
153
166
  for param_name, param_value in category_data.items():
@@ -165,7 +178,12 @@ class ConfigManager:
165
178
  param.default = param_value
166
179
 
167
180
  def save_to_file(self, config_file: str, format_: str = "auto"):
168
- """Save current configuration to file."""
181
+ """Save current configuration to file with enhanced YAML formatting and comments.
182
+
183
+ Args:
184
+ config_file (str): The path to the configuration file.
185
+ format_ (str): The format to save the file in ('auto', 'json', 'yaml').
186
+ """
169
187
  config_path = Path(config_file)
170
188
  config_data = self.to_dict()
171
189
 
@@ -182,6 +200,10 @@ class ConfigManager:
182
200
  else:
183
201
  json.dump(config_data, f, indent=2)
184
202
 
203
+ # Append comments for YAML files
204
+ if format_ == "yaml":
205
+ self._append_comments_to_yaml(config_path)
206
+
185
207
  def to_dict(self) -> dict[str, Any]:
186
208
  """Convert configuration to dictionary."""
187
209
  result = {}
@@ -215,3 +237,45 @@ class ConfigManager:
215
237
  if param.is_cli:
216
238
  cli_parameters.append(param)
217
239
  return cli_parameters
240
+
241
+ def _append_comments_to_yaml(self, config_path: Path):
242
+ """Appends comments to a YAML file based on ConfigParameter metadata.
243
+
244
+ Args:
245
+ config_path (Path): The path to the YAML configuration file.
246
+ """
247
+ lines = config_path.read_text(encoding="utf-8").splitlines()
248
+ new_lines = []
249
+ all_parameters = {param.name: param for param in self.get_all_parameters()}
250
+ current_category = None
251
+
252
+ for line in lines:
253
+ stripped_line = line.strip()
254
+ # Check for category (e.g., 'app:')
255
+ # A category should end with ':', not start with '#', and not be indented.
256
+ if (
257
+ stripped_line.endswith(":")
258
+ and not stripped_line.startswith("#")
259
+ and line.startswith(stripped_line)
260
+ ):
261
+ current_category = stripped_line[:-1]
262
+ new_lines.append(line)
263
+ else:
264
+ # Check for parameter (e.g., ' date_format: '%Y-%m-%d'')
265
+ # This needs to handle cases where the value spans multiple lines
266
+ parts = stripped_line.split(":", 1)
267
+ if len(parts) > 1: # This line might be a parameter definition
268
+ param_name = parts[0].strip()
269
+ if param_name in all_parameters:
270
+ param = all_parameters[param_name]
271
+ # Ensure the parameter belongs to the current category
272
+ # and is not a sub-item of a multi-line value
273
+ if current_category and param.category == current_category:
274
+ comment_indent = " " * (len(line) - len(stripped_line))
275
+ comment = (
276
+ f"{comment_indent}# {param.help} | "
277
+ f"type={type(param).__name__}, default={param.default}"
278
+ )
279
+ new_lines.append(comment)
280
+ new_lines.append(line)
281
+ config_path.write_text("\n".join(new_lines), encoding="utf-8")
@@ -70,23 +70,7 @@ class DocumentationGenerator:
70
70
  """Generate a default configuration file with all parameters and descriptions."""
71
71
  output_path = Path(output_file)
72
72
  output_path.parent.mkdir(parents=True, exist_ok=True)
73
-
74
- with open(output_path, "w", encoding="utf-8") as f:
75
- f.write("# Configuration File\n")
76
- f.write("# This file was auto-generated. Modify as needed.\n\n")
77
-
78
- for category_name, category in self.config_manager._categories.items():
79
- f.write(f"# {category_name.upper()} Configuration\n")
80
- f.write(f"{category_name}:\n")
81
-
82
- for param in category.get_parameters():
83
- f.write(f" # {param.help}\n")
84
- if param.choices:
85
- f.write(f" # Choices: {param.choices}\n")
86
- f.write(f" # Type: {type(param.default).__name__}\n")
87
- f.write(f" {param.name}: {repr(param.default)}\n\n")
88
-
89
- f.write("\n")
73
+ self.config_manager.save_to_file(output_path.as_posix())
90
74
 
91
75
  def generate_cli_markdown_doc(self, output_file: str, app_name: str = "app"):
92
76
  """Generate Markdown CLI documentation."""
@@ -150,8 +134,35 @@ class DocumentationGenerator:
150
134
  )
151
135
  )
152
136
 
137
+ # Add logging examples
138
+ examples.append(
139
+ dedent(
140
+ f"""
141
+ ### 2. With verbose logging
142
+
143
+ ```bash
144
+ python -m {app_name} -v {required_arg}
145
+ python -m {app_name} --verbose {required_arg}
146
+ ```
147
+ """
148
+ )
149
+ )
150
+
151
+ examples.append(
152
+ dedent(
153
+ f"""
154
+ ### 3. With quiet mode
155
+
156
+ ```bash
157
+ python -m {app_name} -q {required_arg}
158
+ python -m {app_name} --quiet {required_arg}
159
+ ```
160
+ """
161
+ )
162
+ )
163
+
153
164
  # Add more examples with optional parameters
154
- for i, param in enumerate(optional_params[:3], 2):
165
+ for i, param in enumerate(optional_params[:3], 4):
155
166
  if param.name in ["verbose", "quiet"]:
156
167
  continue
157
168
  example_value = param.choices[0] if param.choices else param.default
@@ -82,48 +82,44 @@ class CalendarDialog:
82
82
  main_frame = ttk.Frame(self.dialog)
83
83
  main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
84
84
 
85
- # Year selection
86
- year_frame = ttk.Frame(main_frame)
87
- year_frame.pack(fill=tk.X, pady=(0, 5))
85
+ # Date selection in a single row
86
+ date_label_frame = ttk.Frame(main_frame)
87
+ date_label_frame.pack(fill=tk.X, pady=(0, 2))
88
+
89
+ ttk.Label(date_label_frame, text="Year:").pack(side=tk.LEFT, expand=True)
90
+ ttk.Label(date_label_frame, text="Month:").pack(side=tk.LEFT, expand=True)
91
+ ttk.Label(date_label_frame, text="Day:").pack(side=tk.LEFT, expand=True)
92
+
93
+ date_input_frame = ttk.Frame(main_frame)
94
+ date_input_frame.pack(fill=tk.X, pady=(0, 10))
88
95
 
89
- ttk.Label(year_frame, text="Year:").pack(side=tk.LEFT)
90
96
  self.year_var = tk.IntVar(value=self.initial_date.year)
91
97
  year_spinbox = ttk.Spinbox(
92
- year_frame,
98
+ date_input_frame,
93
99
  from_=1900,
94
100
  to=2100,
95
101
  textvariable=self.year_var,
96
102
  command=self._update_calendar,
97
- width=10,
103
+ width=8,
98
104
  )
99
- year_spinbox.pack(side=tk.LEFT, padx=(5, 0))
105
+ year_spinbox.pack(side=tk.LEFT, expand=True, padx=(0, 5))
100
106
 
101
- # Month selection
102
- month_frame = ttk.Frame(main_frame)
103
- month_frame.pack(fill=tk.X, pady=(0, 5))
104
-
105
- ttk.Label(month_frame, text="Month:").pack(side=tk.LEFT)
106
107
  self.month_var = tk.IntVar(value=self.initial_date.month)
107
108
  month_combo = ttk.Combobox(
108
- month_frame,
109
+ date_input_frame,
109
110
  textvariable=self.month_var,
110
111
  values=list(range(1, 13)),
111
112
  state="readonly",
112
113
  width=8,
113
114
  )
114
- month_combo.pack(side=tk.LEFT, padx=(5, 0))
115
+ month_combo.pack(side=tk.LEFT, expand=True, padx=(0, 5))
115
116
  month_combo.bind("<<ComboboxSelected>>", lambda e: self._update_calendar())
116
117
 
117
- # Day selection
118
- day_frame = ttk.Frame(main_frame)
119
- day_frame.pack(fill=tk.X, pady=(0, 5))
120
-
121
- ttk.Label(day_frame, text="Day:").pack(side=tk.LEFT)
122
118
  self.day_var = tk.IntVar(value=self.initial_date.day)
123
119
  self.day_spinbox = ttk.Spinbox(
124
- day_frame, from_=1, to=31, textvariable=self.day_var, width=8
120
+ date_input_frame, from_=1, to=31, textvariable=self.day_var, width=8
125
121
  )
126
- self.day_spinbox.pack(side=tk.LEFT, padx=(5, 0))
122
+ self.day_spinbox.pack(side=tk.LEFT, expand=True)
127
123
 
128
124
  # Time selection
129
125
  time_frame = ttk.Frame(main_frame)
@@ -247,7 +243,7 @@ class GenericSettingsDialog:
247
243
  """Create a tab for a configuration category."""
248
244
  # Create tab frame
249
245
  tab_frame = ttk.Frame(self.notebook)
250
- self.notebook.add(tab_frame, text=category_name.title())
246
+ self.notebook.add(tab_frame, text=" " + category_name.title() + " ")
251
247
 
252
248
  # Create scrollable frame
253
249
  canvas = tk.Canvas(tab_frame)
@@ -302,29 +298,27 @@ class GenericSettingsDialog:
302
298
 
303
299
  def _create_parameter_widget(self, parent, param: ConfigParameter):
304
300
  """Create appropriate widget for parameter type."""
305
- param_type = type(param.default)
306
-
307
301
  # Boolean type - Checkbox
308
- if param_type == bool:
302
+ if isinstance(param.default, bool):
309
303
  var = tk.BooleanVar(value=param.default)
310
304
  widget = ttk.Checkbutton(parent, variable=var)
311
305
  widget.var = var
312
306
  return widget
313
307
 
314
308
  # Path type - File/Directory selector
315
- elif param_type == Path:
309
+ elif isinstance(param.default, Path):
316
310
  return self._create_path_widget(parent, param)
317
311
 
318
312
  # Color type - Color picker
319
- elif param_type == Color:
313
+ elif isinstance(param.default, Color):
320
314
  return self._create_color_widget(parent, param)
321
315
 
322
316
  # DateTime type - DateTime picker
323
- elif param_type == datetime:
317
+ elif isinstance(param.default, datetime):
324
318
  return self._create_datetime_widget(parent, param)
325
319
 
326
320
  # List/Tuple with choices - Combobox
327
- elif param.choices and param_type != bool:
321
+ elif param.choices and not isinstance(param.default, bool):
328
322
  var = tk.StringVar(value=str(param.default))
329
323
  widget = ttk.Combobox(
330
324
  parent, textvariable=var, values=list(param.choices), state="readonly"
@@ -333,22 +327,22 @@ class GenericSettingsDialog:
333
327
  return widget
334
328
 
335
329
  # List/Tuple type - Multi-entry widget
336
- elif param_type in (list, tuple):
330
+ elif isinstance(param.default, list) or isinstance(param.default, tuple):
337
331
  return self._create_list_widget(parent, param)
338
332
 
339
333
  # Dict type - Key-Value editor
340
- elif param_type == dict:
334
+ elif isinstance(param.default, dict):
341
335
  return self._create_dict_widget(parent, param)
342
336
 
343
337
  # Integer type - Spinbox
344
- elif param_type == int:
338
+ elif isinstance(param.default, int):
345
339
  var = tk.IntVar(value=param.default)
346
340
  widget = ttk.Spinbox(parent, from_=-999999, to=999999, textvariable=var)
347
341
  widget.var = var
348
342
  return widget
349
343
 
350
344
  # Float type - Spinbox
351
- elif param_type == float:
345
+ elif isinstance(param.default, float):
352
346
  var = tk.DoubleVar(value=param.default)
353
347
  widget = ttk.Spinbox(
354
348
  parent, from_=-999999.0, to=999999.0, increment=1.0, textvariable=var
@@ -396,7 +390,7 @@ class GenericSettingsDialog:
396
390
  entry = ttk.Entry(frame, textvariable=var, width=10)
397
391
  entry.pack(side=tk.LEFT)
398
392
 
399
- color_display = tk.Label(frame, width=3, bg=color_value.to_hex())
393
+ color_display = tk.Label(frame, width=7, bg=color_value.to_hex())
400
394
  color_display.pack(side=tk.LEFT, padx=(5, 0))
401
395
 
402
396
  def pick_color():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: config-cli-gui
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Feature-rich Python project template for config-cli-gui.
5
5
  Author: pamagister
6
6
  Requires-Python: <3.12,>=3.10
@@ -19,13 +19,10 @@ uv.lock
19
19
  .github/ISSUE_TEMPLATE/bug_report.md
20
20
  .github/ISSUE_TEMPLATE/feature_request.md
21
21
  .github/actions/setup-environment/action.yml
22
- .github/workflows/build-macos.yml
23
- .github/workflows/build.yml
24
22
  .github/workflows/main.yml
25
23
  .github/workflows/release.yml
26
24
  .github/workflows/update_readme.yml
27
25
  .idea/runConfigurations/config_generate.xml
28
- .idea/runConfigurations/module_gui.xml
29
26
  docs/.nav.yml
30
27
  docs/index.md
31
28
  docs/_static/img/favicon.png
@@ -34,7 +31,6 @@ docs/css/custom.css
34
31
  docs/develop/contributing.md
35
32
  docs/develop/make_windows.md
36
33
  docs/develop/naming_convention.md
37
- docs/develop/pypi_release.md
38
34
  docs/funding/funding.md
39
35
  docs/getting-started/install.md
40
36
  docs/getting-started/virtual-environment.md
@@ -5,8 +5,12 @@ organized in categories (CLI, App, GUI). It can generate config files, CLI modul
5
5
  and documentation from the parameter definitions.
6
6
  """
7
7
 
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
8
11
  from config_cli_gui.config_framework import (
9
12
  BaseConfigCategory,
13
+ Color,
10
14
  ConfigManager,
11
15
  ConfigParameter,
12
16
  )
@@ -159,17 +163,37 @@ class GuiConfig(BaseConfigCategory):
159
163
  )
160
164
 
161
165
 
166
+ class MiscConfig(BaseConfigCategory):
167
+ def get_category_name(self) -> str:
168
+ return "misc"
169
+
170
+ some_file: ConfigParameter = ConfigParameter(
171
+ name="some_file",
172
+ default=Path("some_file.txt"),
173
+ help="Path to the file to use",
174
+ )
175
+
176
+ some_color: ConfigParameter = ConfigParameter(
177
+ name="some_color",
178
+ default=Color(255, 0, 0),
179
+ help="Color setting for the application",
180
+ )
181
+
182
+ some_date: ConfigParameter = ConfigParameter(
183
+ name="some_date",
184
+ default=datetime.now(),
185
+ help="Date setting for the application",
186
+ )
187
+
188
+
162
189
  class ConfigParameterManager(ConfigManager): # Inherit from ConfigManager
163
190
  """Main configuration manager that handles all parameter categories."""
164
191
 
165
- def __init__(self, config_file: str | None = None, **kwargs):
166
- # Erst den Parent initialisieren
167
- super().__init__(config_file, **kwargs)
192
+ categories = (CliConfig(), AppConfig(), GuiConfig(), MiscConfig())
168
193
 
169
- # Dann die Kategorien hinzufügen
170
- self.add_category("cli", CliConfig())
171
- self.add_category("app", AppConfig())
172
- self.add_category("gui", GuiConfig())
194
+ def __init__(self, config_file: str | None = None, **kwargs):
195
+ """Initialize the configuration manager with all parameter categories."""
196
+ super().__init__(self.categories, config_file, **kwargs)
173
197
 
174
198
 
175
199
  def main():
@@ -79,7 +79,7 @@ class TestGenericCLI(unittest.TestCase):
79
79
 
80
80
  # Check file content
81
81
  content = output_file.read_text()
82
- self.assertIn("# Configuration File", content)
82
+ self.assertIn("# Include elevation data in waypoints", content)
83
83
 
84
84
  for param in self.default_cli_config.keys():
85
85
  with self.subTest(parameter=param):