config-cli-gui 0.0.2__py3-none-any.whl
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.
- __init__.py +0 -0
- config_cli_gui/__init__.py +0 -0
- config_cli_gui/_version.py +21 -0
- config_cli_gui/cli_generator.py +177 -0
- config_cli_gui/config_framework.py +362 -0
- config_cli_gui/gui_generator.py +225 -0
- config_cli_gui-0.0.2.dist-info/METADATA +282 -0
- config_cli_gui-0.0.2.dist-info/RECORD +26 -0
- config_cli_gui-0.0.2.dist-info/WHEEL +5 -0
- config_cli_gui-0.0.2.dist-info/entry_points.txt +3 -0
- config_cli_gui-0.0.2.dist-info/licenses/LICENSE +24 -0
- config_cli_gui-0.0.2.dist-info/top_level.txt +4 -0
- example_project/__init__.py +0 -0
- example_project/__main__.py +8 -0
- example_project/cli/__init__.py +0 -0
- example_project/cli/__main__.py +8 -0
- example_project/cli/cli.py +132 -0
- example_project/config/__init__.py +0 -0
- example_project/config/config.py +209 -0
- example_project/core/__init__.py +0 -0
- example_project/core/base.py +634 -0
- example_project/core/logging.py +219 -0
- example_project/gui/__init__.py +0 -0
- example_project/gui/__main__.py +8 -0
- example_project/gui/gui.py +542 -0
- main.py +153 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# config_framework/gui.py
|
|
2
|
+
"""Generic GUI settings dialog generator for configuration framework."""
|
|
3
|
+
|
|
4
|
+
import tkinter as tk
|
|
5
|
+
from tkinter import messagebox, ttk
|
|
6
|
+
|
|
7
|
+
from config_cli_gui.config_framework import ConfigManager, ConfigParameter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ToolTip:
|
|
11
|
+
"""Create a tooltip for a given widget."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, widget, text="widget info"):
|
|
14
|
+
self.widget = widget
|
|
15
|
+
self.text = text
|
|
16
|
+
self.tipwindow = None
|
|
17
|
+
self.id = None
|
|
18
|
+
self.x = self.y = 0
|
|
19
|
+
|
|
20
|
+
self.widget.bind("<Enter>", self.on_enter)
|
|
21
|
+
self.widget.bind("<Leave>", self.on_leave)
|
|
22
|
+
|
|
23
|
+
def on_enter(self, event=None):
|
|
24
|
+
self.show_tooltip()
|
|
25
|
+
|
|
26
|
+
def on_leave(self, event=None):
|
|
27
|
+
self.hide_tooltip()
|
|
28
|
+
|
|
29
|
+
def show_tooltip(self):
|
|
30
|
+
if self.tipwindow or not self.text:
|
|
31
|
+
return
|
|
32
|
+
x, y, cx, cy = self.widget.bbox("insert")
|
|
33
|
+
x = x + self.widget.winfo_rootx() + 25
|
|
34
|
+
y = y + cy + self.widget.winfo_rooty() + 25
|
|
35
|
+
self.tipwindow = tw = tk.Toplevel(self.widget)
|
|
36
|
+
tw.wm_overrideredirect(True)
|
|
37
|
+
tw.wm_geometry(f"+{x}+{y}")
|
|
38
|
+
label = tk.Label(
|
|
39
|
+
tw,
|
|
40
|
+
text=self.text,
|
|
41
|
+
justify=tk.LEFT,
|
|
42
|
+
background="#ffffe0",
|
|
43
|
+
relief=tk.SOLID,
|
|
44
|
+
borderwidth=1,
|
|
45
|
+
font=("tahoma", "8", "normal"),
|
|
46
|
+
)
|
|
47
|
+
label.pack(ipadx=1)
|
|
48
|
+
|
|
49
|
+
def hide_tooltip(self):
|
|
50
|
+
tw = self.tipwindow
|
|
51
|
+
self.tipwindow = None
|
|
52
|
+
if tw:
|
|
53
|
+
tw.destroy()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SettingsDialogGenerator:
|
|
57
|
+
"""Generates settings dialog from ConfigManager."""
|
|
58
|
+
|
|
59
|
+
def __init__(self, config_manager: ConfigManager):
|
|
60
|
+
self.config_manager = config_manager
|
|
61
|
+
|
|
62
|
+
def create_settings_dialog(
|
|
63
|
+
self, parent, title="Settings", config_file="config.yaml"
|
|
64
|
+
) -> "GenericSettingsDialog":
|
|
65
|
+
"""Create a settings dialog for the configuration."""
|
|
66
|
+
return GenericSettingsDialog(parent, self.config_manager, title, config_file)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class GenericSettingsDialog:
|
|
70
|
+
"""Generic settings dialog for ConfigManager."""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self, parent, config_manager: ConfigManager, title="Settings", config_file="config.yaml"
|
|
74
|
+
):
|
|
75
|
+
self.parent = parent
|
|
76
|
+
self.config_manager = config_manager
|
|
77
|
+
self.config_file = config_file
|
|
78
|
+
self.result = None
|
|
79
|
+
self.widgets = {}
|
|
80
|
+
|
|
81
|
+
# Create dialog window
|
|
82
|
+
self.dialog = tk.Toplevel(parent)
|
|
83
|
+
self.dialog.title(title)
|
|
84
|
+
self.dialog.geometry("600x500")
|
|
85
|
+
self.dialog.transient(parent)
|
|
86
|
+
self.dialog.grab_set()
|
|
87
|
+
|
|
88
|
+
# Center the dialog
|
|
89
|
+
self.dialog.geometry(f"+{int(parent.winfo_rootx() + 50)}+{int(parent.winfo_rooty() + 50)}")
|
|
90
|
+
|
|
91
|
+
self._create_widgets()
|
|
92
|
+
|
|
93
|
+
# Handle window closing
|
|
94
|
+
self.dialog.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
|
95
|
+
|
|
96
|
+
def _create_widgets(self):
|
|
97
|
+
"""Create the settings dialog widgets."""
|
|
98
|
+
# Main frame
|
|
99
|
+
main_frame = ttk.Frame(self.dialog)
|
|
100
|
+
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
101
|
+
|
|
102
|
+
# Create notebook for tabs
|
|
103
|
+
self.notebook = ttk.Notebook(main_frame)
|
|
104
|
+
self.notebook.pack(fill=tk.BOTH, expand=True)
|
|
105
|
+
|
|
106
|
+
# Create tabs for each configuration category
|
|
107
|
+
for category_name, category in self.config_manager._categories.items():
|
|
108
|
+
self._create_category_tab(category_name, category)
|
|
109
|
+
|
|
110
|
+
# Button frame
|
|
111
|
+
button_frame = ttk.Frame(main_frame)
|
|
112
|
+
button_frame.pack(fill=tk.X, pady=(10, 0))
|
|
113
|
+
|
|
114
|
+
ttk.Button(button_frame, text="OK", command=self._on_ok).pack(side=tk.RIGHT, padx=(5, 0))
|
|
115
|
+
ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(side=tk.RIGHT)
|
|
116
|
+
|
|
117
|
+
def _create_category_tab(self, category_name: str, category):
|
|
118
|
+
"""Create a tab for a configuration category."""
|
|
119
|
+
# Create tab frame
|
|
120
|
+
tab_frame = ttk.Frame(self.notebook)
|
|
121
|
+
self.notebook.add(tab_frame, text=category_name.title())
|
|
122
|
+
|
|
123
|
+
# Create scrollable frame
|
|
124
|
+
canvas = tk.Canvas(tab_frame)
|
|
125
|
+
scrollbar = ttk.Scrollbar(tab_frame, orient="vertical", command=canvas.yview)
|
|
126
|
+
scrollable_frame = ttk.Frame(canvas)
|
|
127
|
+
|
|
128
|
+
scrollable_frame.bind(
|
|
129
|
+
"<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
|
133
|
+
canvas.configure(yscrollcommand=scrollbar.set)
|
|
134
|
+
|
|
135
|
+
# Add parameters
|
|
136
|
+
self._add_category_parameters(scrollable_frame, category_name, category)
|
|
137
|
+
|
|
138
|
+
canvas.pack(side="left", fill="both", expand=True)
|
|
139
|
+
scrollbar.pack(side="right", fill="y")
|
|
140
|
+
|
|
141
|
+
def _add_category_parameters(self, parent, category_name: str, category):
|
|
142
|
+
"""Add parameter widgets for a specific category."""
|
|
143
|
+
row = 0
|
|
144
|
+
parameters = category.get_parameters()
|
|
145
|
+
|
|
146
|
+
for param in parameters:
|
|
147
|
+
if param.required:
|
|
148
|
+
# Skip required parameters as they are not configurable in GUI
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
# Create label
|
|
152
|
+
label = ttk.Label(parent, text=f"{param.name}:")
|
|
153
|
+
label.grid(row=row, column=0, sticky="w", padx=5, pady=2)
|
|
154
|
+
|
|
155
|
+
# Create appropriate widget based on parameter type
|
|
156
|
+
widget = self._create_parameter_widget(parent, param)
|
|
157
|
+
widget.grid(row=row, column=1, sticky="ew", padx=5, pady=2)
|
|
158
|
+
|
|
159
|
+
# Add tooltip
|
|
160
|
+
ToolTip(label, param.help)
|
|
161
|
+
ToolTip(widget, param.help)
|
|
162
|
+
|
|
163
|
+
# Store widget reference
|
|
164
|
+
self.widgets[f"{category_name}__{param.name}"] = widget
|
|
165
|
+
|
|
166
|
+
row += 1
|
|
167
|
+
|
|
168
|
+
# Configure column weights
|
|
169
|
+
parent.columnconfigure(1, weight=1)
|
|
170
|
+
|
|
171
|
+
def _create_parameter_widget(self, parent, param: ConfigParameter):
|
|
172
|
+
"""Create appropriate widget for parameter type."""
|
|
173
|
+
if param.type_ == bool:
|
|
174
|
+
# Checkbox for boolean values
|
|
175
|
+
var = tk.BooleanVar(value=param.default)
|
|
176
|
+
widget = ttk.Checkbutton(parent, variable=var)
|
|
177
|
+
widget.var = var
|
|
178
|
+
return widget
|
|
179
|
+
|
|
180
|
+
elif param.choices and param.type_ != bool:
|
|
181
|
+
# Combobox for choices
|
|
182
|
+
var = tk.StringVar(value=str(param.default))
|
|
183
|
+
widget = ttk.Combobox(parent, textvariable=var, values=param.choices, state="readonly")
|
|
184
|
+
widget.var = var
|
|
185
|
+
return widget
|
|
186
|
+
|
|
187
|
+
elif param.type_ == int:
|
|
188
|
+
# Spinbox for integers
|
|
189
|
+
var = tk.IntVar(value=param.default)
|
|
190
|
+
widget = ttk.Spinbox(parent, from_=-999999, to=999999, textvariable=var)
|
|
191
|
+
widget.var = var
|
|
192
|
+
return widget
|
|
193
|
+
|
|
194
|
+
else: # str or other types
|
|
195
|
+
# Entry for strings
|
|
196
|
+
var = tk.StringVar(value=str(param.default))
|
|
197
|
+
widget = ttk.Entry(parent, textvariable=var)
|
|
198
|
+
widget.var = var
|
|
199
|
+
return widget
|
|
200
|
+
|
|
201
|
+
def _on_ok(self):
|
|
202
|
+
"""Handle OK button click."""
|
|
203
|
+
try:
|
|
204
|
+
# Update configuration with widget values
|
|
205
|
+
overrides = {}
|
|
206
|
+
for key, widget in self.widgets.items():
|
|
207
|
+
value = widget.var.get()
|
|
208
|
+
overrides[key] = value
|
|
209
|
+
|
|
210
|
+
# Apply overrides to config manager
|
|
211
|
+
self.config_manager._apply_kwargs(overrides)
|
|
212
|
+
|
|
213
|
+
# Save to file
|
|
214
|
+
self.config_manager.save_to_file(self.config_file)
|
|
215
|
+
|
|
216
|
+
self.result = "ok"
|
|
217
|
+
self.dialog.destroy()
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
messagebox.showerror("Error", f"Failed to save configuration: {e}")
|
|
221
|
+
|
|
222
|
+
def _on_cancel(self):
|
|
223
|
+
"""Handle Cancel button click."""
|
|
224
|
+
self.result = "cancel"
|
|
225
|
+
self.dialog.destroy()
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: config-cli-gui
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Feature-rich Python project template for config-cli-gui.
|
|
5
|
+
Author: pamagister
|
|
6
|
+
Requires-Python: <3.12,>=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: fastkml>=1.1.0
|
|
10
|
+
Requires-Dist: gpxpy>=1.6.2
|
|
11
|
+
Requires-Dist: pydantic>=2.11.7
|
|
12
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
13
|
+
Requires-Dist: shapely>=2.1.1
|
|
14
|
+
Requires-Dist: srtm-py>=0.3.7
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest>=8.4.0; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
|
|
18
|
+
Requires-Dist: coverage>=7.8.2; extra == "dev"
|
|
19
|
+
Requires-Dist: flake8>=7.2.0; extra == "dev"
|
|
20
|
+
Requires-Dist: black>=25.1.0; extra == "dev"
|
|
21
|
+
Requires-Dist: isort>=6.0.1; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-cov>=6.1.1; extra == "dev"
|
|
23
|
+
Requires-Dist: mypy>=1.16.0; extra == "dev"
|
|
24
|
+
Requires-Dist: gitchangelog>=3.0.4; extra == "dev"
|
|
25
|
+
Requires-Dist: pyinstaller>=5.8; extra == "dev"
|
|
26
|
+
Requires-Dist: pre-commit>=4.2.0; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.11.13; extra == "dev"
|
|
28
|
+
Provides-Extra: docs
|
|
29
|
+
Requires-Dist: mkdocs>=1.6.1; extra == "docs"
|
|
30
|
+
Requires-Dist: mkdocs-awesome-nav>=2.6.1; extra == "docs"
|
|
31
|
+
Requires-Dist: pygments>=2.19.1; extra == "docs"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# config-cli-gui: Unified Configuration and Interface Management
|
|
35
|
+
|
|
36
|
+
Provides a generic configuration framework that automatically generates both command-line interfaces and GUI settings dialogs from configuration parameters.
|
|
37
|
+
|
|
38
|
+
[](https://github.com/pamagister/config-cli-gui/actions)
|
|
39
|
+
[](https://github.com/pamagister/config-cli-gui/releases)
|
|
40
|
+
[](https://config-cli-gui.readthedocs.io/en/stable/)
|
|
41
|
+
[](https://github.com/pamagister/config-cli-gui/blob/main/LICENSE)
|
|
42
|
+
[](https://github.com/pamagister/config-cli-gui/issues)
|
|
43
|
+
[](https://pypi.org/project/config-cli-gui/)
|
|
44
|
+
[](https://pepy.tech/project/config-cli-gui/)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
`config-cli-gui` is a Python library designed to streamline the management of application configurations,
|
|
48
|
+
generating command-line interfaces (CLIs), and dynamically creating graphical user interface (GUI) settings dialogs
|
|
49
|
+
from a single source of truth. It leverages Pydantic for robust parameter definition and offers
|
|
50
|
+
powerful features for consistent configuration across different application entry points.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🚀 Installation
|
|
55
|
+
|
|
56
|
+
You can install `config-cli-gui` using pip:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install config-cli-gui
|
|
60
|
+
````
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## ✨ Features
|
|
65
|
+
|
|
66
|
+
* **Single Source of Truth**: Define all your application parameters in one place using simple, dataclass-like structures based on Pydantic's `BaseModel`. This ensures consistency and reduces errors across your application.
|
|
67
|
+
* **Categorized Configuration**: Organize your parameters into logical categories (e.g., `cli`, `app`, `gui`) for better structure and maintainability.
|
|
68
|
+
* **Dynamic CLI Generation**: Automatically generate `argparse`-compatible command-line arguments directly from your defined configuration parameters, including help texts, types, and choices.
|
|
69
|
+
* **Config File Management**: Easily load and save configurations from/to YAML or JSON files, allowing users to customize default settings.
|
|
70
|
+
* **GUI Settings Dialogs**: Dynamically create Tkinter-based settings dialogs for your application, allowing users to intuitively modify configuration parameters via a graphical interface.
|
|
71
|
+
* **Documentation Generation**: Generate detailed Markdown documentation for both your CLI options and all configuration parameters, keeping your user guides always up-to-date with your codebase.
|
|
72
|
+
* **Override System**: Supports robust overriding of configuration values via configuration files and command-line arguments, with clear precedence.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 📚 Usage
|
|
77
|
+
|
|
78
|
+
### 1\. Define Your Configuration
|
|
79
|
+
|
|
80
|
+
Start by defining your application's configuration parameters in a central `config.py` file within your project. You will inherit from `config-cli-gui`'s `GenericConfigManager` and `BaseConfigCategory`.
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
# my_project/config.py
|
|
84
|
+
from config_cli_gui.config import ConfigParameter, GenericConfigManager, BaseConfigCategory
|
|
85
|
+
from pydantic import Field # Make sure pydantic is installed
|
|
86
|
+
|
|
87
|
+
class MyCliConfig(BaseConfigCategory):
|
|
88
|
+
"""CLI-specific parameters for MyProject."""
|
|
89
|
+
input_path: ConfigParameter = ConfigParameter(
|
|
90
|
+
name="input_path",
|
|
91
|
+
default="",
|
|
92
|
+
type_=str,
|
|
93
|
+
help="Path to the input file or directory",
|
|
94
|
+
required=True,
|
|
95
|
+
cli_arg=None # Positional argument
|
|
96
|
+
)
|
|
97
|
+
output_dir: ConfigParameter = ConfigParameter(
|
|
98
|
+
name="output_dir",
|
|
99
|
+
default="./output",
|
|
100
|
+
type_=str,
|
|
101
|
+
help="Directory for output files",
|
|
102
|
+
cli_arg="--output"
|
|
103
|
+
)
|
|
104
|
+
dry_run: ConfigParameter = ConfigParameter(
|
|
105
|
+
name="dry_run",
|
|
106
|
+
default=False,
|
|
107
|
+
type_=bool,
|
|
108
|
+
help="Perform a dry run without making actual changes",
|
|
109
|
+
cli_arg="--dry-run"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
class MyAppConfig(BaseConfigCategory):
|
|
113
|
+
"""Application-wide settings."""
|
|
114
|
+
log_level: ConfigParameter = ConfigParameter(
|
|
115
|
+
name="log_level",
|
|
116
|
+
default="INFO",
|
|
117
|
+
type_=str,
|
|
118
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
119
|
+
help="Logging verbosity level"
|
|
120
|
+
)
|
|
121
|
+
max_threads: ConfigParameter = ConfigParameter(
|
|
122
|
+
name="max_threads",
|
|
123
|
+
default=4,
|
|
124
|
+
type_=int,
|
|
125
|
+
help="Maximum number of processing threads"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
class MyGuiConfig(BaseConfigCategory):
|
|
129
|
+
"""GUI-specific settings."""
|
|
130
|
+
theme: ConfigParameter = ConfigParameter(
|
|
131
|
+
name="theme",
|
|
132
|
+
default="dark",
|
|
133
|
+
type_=str,
|
|
134
|
+
choices=["light", "dark", "system"],
|
|
135
|
+
help="GUI theme"
|
|
136
|
+
)
|
|
137
|
+
window_size: ConfigParameter = ConfigParameter(
|
|
138
|
+
name="window_size",
|
|
139
|
+
default="800x600",
|
|
140
|
+
type_=str,
|
|
141
|
+
help="Initial GUI window size"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
class ProjectConfigManager(GenericConfigManager):
|
|
145
|
+
"""Main configuration manager for MyProject."""
|
|
146
|
+
cli: MyCliConfig = Field(default_factory=MyCliConfig)
|
|
147
|
+
app: MyAppConfig = Field(default_factory=MyAppConfig)
|
|
148
|
+
gui: MyGuiConfig = Field(default_factory=MyGuiConfig)
|
|
149
|
+
|
|
150
|
+
def __init__(self, config_file: str | None = None, **kwargs):
|
|
151
|
+
# Dynamically register categories for the generic manager
|
|
152
|
+
self.__class__.add_config_category("cli", MyCliConfig)
|
|
153
|
+
self.__class__.add_config_category("app", MyAppConfig)
|
|
154
|
+
self.__class__.add_config_category("gui", MyGuiConfig)
|
|
155
|
+
super().__init__(config_file, **kwargs)
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 2\. Generate CLI
|
|
160
|
+
|
|
161
|
+
Use the generic CLI functions to parse command-line arguments based on your defined `CliConfig`.
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
# my_project/cli.py
|
|
165
|
+
import argparse
|
|
166
|
+
from my_project.config import ProjectConfigManager
|
|
167
|
+
from config_cli_gui.cli import create_argument_parser, create_config_overrides_from_args
|
|
168
|
+
|
|
169
|
+
def parse_my_args():
|
|
170
|
+
# Define any project-specific hardcoded CLI args (e.g., --version)
|
|
171
|
+
extra_cli_args = {
|
|
172
|
+
"--version": {"action": "version", "version": "MyProject 1.0.0", "help": "Show program's version number and exit."}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
parser = create_argument_parser(
|
|
176
|
+
config_manager_class=ProjectConfigManager,
|
|
177
|
+
description="MyProject CLI application",
|
|
178
|
+
epilog="""
|
|
179
|
+
Examples:
|
|
180
|
+
python -m my_project.cli my_input.txt --output ./results
|
|
181
|
+
python -m my_project.cli --config custom.yaml another_input.csv
|
|
182
|
+
""",
|
|
183
|
+
cli_category_name="cli", # The name of your CLI config category
|
|
184
|
+
extra_arguments=extra_cli_args
|
|
185
|
+
)
|
|
186
|
+
return parser.parse_args()
|
|
187
|
+
|
|
188
|
+
def main_cli():
|
|
189
|
+
args = parse_my_args()
|
|
190
|
+
|
|
191
|
+
# Map generic flags like verbose/quiet to specific log levels if desired
|
|
192
|
+
log_level_map = {
|
|
193
|
+
"verbose": "DEBUG", # Assuming you added a --verbose flag in extra_cli_args
|
|
194
|
+
"quiet": "WARNING" # Assuming you added a --quiet flag
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
cli_overrides = create_config_overrides_from_args(
|
|
198
|
+
args,
|
|
199
|
+
config_manager_class=ProjectConfigManager,
|
|
200
|
+
cli_category_name="cli",
|
|
201
|
+
log_level_map=log_level_map
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Initialize your project's configuration
|
|
205
|
+
config = ProjectConfigManager(
|
|
206
|
+
config_file=args.config if hasattr(args, "config") and args.config else None,
|
|
207
|
+
**cli_overrides
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
print(f"Input Path: {config.cli.input_path.default}")
|
|
211
|
+
print(f"Output Directory: {config.cli.output_dir.default}")
|
|
212
|
+
print(f"Dry Run: {config.cli.dry_run.default}")
|
|
213
|
+
print(f"Log Level: {config.app.log_level.default}")
|
|
214
|
+
# ... your application logic using 'config'
|
|
215
|
+
|
|
216
|
+
if __name__ == "__main__":
|
|
217
|
+
main_cli()
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 3\. Integrate GUI Settings Dialog
|
|
221
|
+
|
|
222
|
+
The `SettingsDialog` from `config-cli-gui` (or your project's adapted version) can be used to easily create a settings window.
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
# my_project/gui.py (Simplified example)
|
|
226
|
+
import tkinter as tk
|
|
227
|
+
from my_project.config import ProjectConfigManager
|
|
228
|
+
from config_cli_gui.gui_settings import SettingsDialog # Assuming gui_settings is part of the generic lib or adapted
|
|
229
|
+
|
|
230
|
+
def open_settings_window(parent_root, config_manager: ProjectConfigManager):
|
|
231
|
+
dialog = SettingsDialog(parent_root, config_manager)
|
|
232
|
+
parent_root.wait_window(dialog.dialog)
|
|
233
|
+
# After dialog closes, config_manager will have updated values if 'OK' was clicked
|
|
234
|
+
print("Settings updated or cancelled.")
|
|
235
|
+
print(f"New GUI Theme: {config_manager.gui.theme.default}")
|
|
236
|
+
|
|
237
|
+
if __name__ == "__main__":
|
|
238
|
+
root = tk.Tk()
|
|
239
|
+
root.withdraw() # Hide main window for this example
|
|
240
|
+
|
|
241
|
+
# Initialize your project's config manager
|
|
242
|
+
project_config = ProjectConfigManager()
|
|
243
|
+
|
|
244
|
+
open_settings_window(root, project_config)
|
|
245
|
+
|
|
246
|
+
root.destroy()
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### 4\. Generate Documentation and Default Config
|
|
250
|
+
|
|
251
|
+
Use the static methods on your `ProjectConfigManager` to generate `config.yaml`, `cli.md`, and `config.md` files.
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
# scripts/generate_docs.py (or similar script in your project)
|
|
255
|
+
from my_project.config import ProjectConfigManager
|
|
256
|
+
import os
|
|
257
|
+
|
|
258
|
+
# Define output paths
|
|
259
|
+
output_dir = "docs/generated"
|
|
260
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
261
|
+
|
|
262
|
+
config_file_path = "config.yaml" # At the project root or similar
|
|
263
|
+
cli_doc_path = os.path.join(output_dir, "cli.md")
|
|
264
|
+
config_doc_path = os.path.join(output_dir, "config.md")
|
|
265
|
+
|
|
266
|
+
print(f"Generating default config to: {config_file_path}")
|
|
267
|
+
ProjectConfigManager.generate_default_config_file(config_file_path)
|
|
268
|
+
|
|
269
|
+
print(f"Generating general config documentation to: {config_doc_path}")
|
|
270
|
+
ProjectConfigManager.generate_config_markdown_doc(config_doc_path)
|
|
271
|
+
|
|
272
|
+
print(f"Generating CLI documentation to: {cli_doc_path}")
|
|
273
|
+
ProjectConfigManager.generate_cli_markdown_doc(
|
|
274
|
+
output_file=cli_doc_path,
|
|
275
|
+
cli_category_name="cli", # Ensure this matches your CLI config category
|
|
276
|
+
cli_entry_point="python -m my_project.cli" # Your project's actual CLI entry point
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
print("Documentation and default config generation complete.")
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
By following this structure, `config-cli-gui` provides a robust and maintainable foundation for your application's configuration needs.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
main.py,sha256=PbF_c10dnW7FCmtNtKwuABfLh5tfDWfnhZwA0Udb7bQ,4430
|
|
3
|
+
config_cli_gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
config_cli_gui/_version.py,sha256=wO7XWlZte1hxA4mMvRc6zhNdGm74Nhhn2bfWRAxaKbI,511
|
|
5
|
+
config_cli_gui/cli_generator.py,sha256=UkRbpAxQA6Wrs7omSk5SUwE_ci3c1BXsYTN5d3h2skU,6380
|
|
6
|
+
config_cli_gui/config_framework.py,sha256=0DttxYz_tnB_qJ6wM-KMwiGXsQzCEr9UbDh84DpxdGM,12514
|
|
7
|
+
config_cli_gui/gui_generator.py,sha256=K5kDsFzo0pZPaCLaas3FUateiHwPjjrvF9hiT8QhftM,7620
|
|
8
|
+
config_cli_gui-0.0.2.dist-info/licenses/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
|
|
9
|
+
example_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
example_project/__main__.py,sha256=m9ddpnP0vLdye1PBnWaOi-KFumFVmqAVaJF4SAjEueY,186
|
|
11
|
+
example_project/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
example_project/cli/__main__.py,sha256=qnGkoNNbhnvLTA24X8YTEDxRqDbNEAzgjp9Ki_JYIhs,184
|
|
13
|
+
example_project/cli/cli.py,sha256=gG16WypUSfMfn0vs0w2uq3PlmQdgHDm_Iw_-cJcTMlU,4213
|
|
14
|
+
example_project/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
example_project/config/config.py,sha256=QLQUF0HFfbIh2iDjnPPlNimmS75JahQIGQFXrE9VUfM,5787
|
|
16
|
+
example_project/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
example_project/core/base.py,sha256=uYKbnkfM6lY7IN_jEewJKsRxjMJ0UMnL4r5bMEi6xTQ,27036
|
|
18
|
+
example_project/core/logging.py,sha256=aadgQxw_0ErQ4Ruupv7JtbS09vcNK70GDTNzgcGwook,6971
|
|
19
|
+
example_project/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
example_project/gui/__main__.py,sha256=5AaiBxNCAKWcuSQFcQ_lsEQfEsXm6BA5-i5MScdf30c,184
|
|
21
|
+
example_project/gui/gui.py,sha256=8zwYnfr3SSO2v1PAWfZbf3qazd60Tc6wAPUYsMoK418,21705
|
|
22
|
+
config_cli_gui-0.0.2.dist-info/METADATA,sha256=-OkyKO7wXBjIUgQYhQUBboLp_Nl2VdUepje4TbnKhu0,11311
|
|
23
|
+
config_cli_gui-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
config_cli_gui-0.0.2.dist-info/entry_points.txt,sha256=T-j8vHWuq5H3v0j5kMBJ93FqNNEE84gDCL5fRLDiGzQ,112
|
|
25
|
+
config_cli_gui-0.0.2.dist-info/top_level.txt,sha256=uV2Y_uzKV2W2AUm1BxjlrIyNxuAXYxYDMtY3BELGtNU,45
|
|
26
|
+
config_cli_gui-0.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
|
File without changes
|
|
File without changes
|