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.
- config_cli_gui-0.1.2/HISTORY.md +25 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/Makefile +0 -32
- {config_cli_gui-0.1.1/src/config_cli_gui.egg-info → config_cli_gui-0.1.2}/PKG-INFO +1 -1
- config_cli_gui-0.1.2/config.yaml +51 -0
- config_cli_gui-0.1.2/docs/getting-started/install.md +28 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/usage/cli.md +17 -3
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/usage/config.md +8 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/_version.py +2 -2
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/config_framework.py +70 -6
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/docs_generator.py +29 -18
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/gui_generator.py +28 -34
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2/src/config_cli_gui.egg-info}/PKG-INFO +1 -1
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/SOURCES.txt +0 -4
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/config/config.py +31 -7
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/test_generic_cli.py +1 -1
- config_cli_gui-0.1.1/.github/workflows/build-macos.yml +0 -48
- config_cli_gui-0.1.1/.github/workflows/build.yml +0 -46
- config_cli_gui-0.1.1/.idea/runConfigurations/module_gui.xml +0 -25
- config_cli_gui-0.1.1/HISTORY.md +0 -16
- config_cli_gui-0.1.1/config.yaml +0 -96
- config_cli_gui-0.1.1/docs/develop/pypi_release.md +0 -233
- config_cli_gui-0.1.1/docs/getting-started/install.md +0 -66
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/FUNDING.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/actions/setup-environment/action.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/dependabot.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/init.sh +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/release_message.sh +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/update_funding.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/workflows/main.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/workflows/release.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.github/workflows/update_readme.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.gitignore +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.idea/runConfigurations/config_generate.xml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.pre-commit-config.yaml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/.readthedocs.yaml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/LICENSE +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/README.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/.nav.yml +1 -1
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/_static/img/favicon.png +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/_static/img/logo.png +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/css/custom.css +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/develop/contributing.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/develop/make_windows.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/develop/naming_convention.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/funding/funding.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/getting-started/virtual-environment.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/docs/index.md +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/examples/kuhkopfsteig.gpx +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/examples/rother_lilienstein.gpx +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/examples/teneriffa.gpx +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/mkdocs.yml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/pyproject.toml +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/show_filelist.ps1 +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/show_tree.ps1 +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/show_tree.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/scripts/update_readme.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/setup.cfg +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui/cli_generator.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/requires.txt +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/config_cli_gui.egg-info/top_level.txt +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/src/main.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/template.yml.url +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/__main__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/cli/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/cli/__main__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/cli/cli.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/config/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/core/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/core/base.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/core/logging.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/gui/__init__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/gui/__main__.py +0 -0
- {config_cli_gui-0.1.1 → config_cli_gui-0.1.2}/tests/example_project/gui/gui.py +0 -0
- {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/
|
|
@@ -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
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
+
|
|
@@ -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__(
|
|
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
|
|
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],
|
|
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
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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=
|
|
103
|
+
width=8,
|
|
98
104
|
)
|
|
99
|
-
year_spinbox.pack(side=tk.LEFT, padx=(
|
|
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
|
-
|
|
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=(
|
|
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
|
-
|
|
120
|
+
date_input_frame, from_=1, to=31, textvariable=self.day_var, width=8
|
|
125
121
|
)
|
|
126
|
-
self.day_spinbox.pack(side=tk.LEFT,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
334
|
+
elif isinstance(param.default, dict):
|
|
341
335
|
return self._create_dict_widget(parent, param)
|
|
342
336
|
|
|
343
337
|
# Integer type - Spinbox
|
|
344
|
-
elif
|
|
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
|
|
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=
|
|
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():
|
|
@@ -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
|
-
|
|
166
|
-
# Erst den Parent initialisieren
|
|
167
|
-
super().__init__(config_file, **kwargs)
|
|
192
|
+
categories = (CliConfig(), AppConfig(), GuiConfig(), MiscConfig())
|
|
168
193
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
self.
|
|
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("#
|
|
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):
|