Welcome to config-cli-gui#

Unified Configuration and Interface Management

Provides a generic configuration framework that automatically generates both command-line interfaces and GUI settings dialogs from configuration parameters.

Github CI Status GitHub release Read the Docs License GitHub issues PyPI Downloads

config-cli-gui is a Python library designed to streamline the management of application configurations, generating command-line interfaces (CLIs), and dynamically creating graphical user interface (GUI) settings dialogs from a single source of truth. It leverages Pydantic for robust parameter definition and offers powerful features for consistent configuration across different application entry points.


๐Ÿš€ Installation#

You can install config-cli-gui using pip:

pip install config-cli-gui

โœจ Features#

  • 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.

    yaml gui: # GUI theme setting | type=str | choices=['light', 'dark', 'auto'] theme: light misc: # Example integer | type=int some_numeric: 42 # Path to the file to use | type=PosixPath some_file: some_file.txt # Color setting for the application | type=Color some_color: '#ff0000' # Font setting for the application | type=Font some_font: 'DejaVuSans.ttf, 12, #0000ff'

  • Categorized Configuration: Organize your parameters into logical categories (e.g., cli, app, gui) for better structure and maintainability.

  • Dynamic CLI Generation: Automatically generate argparse-compatible command-line arguments directly from your defined configuration parameters, including help texts, types, and choices.
  • Config File Management: Easily load and save configurations from/to YAML or JSON files, allowing users to customize default settings.
  • GUI Settings Dialogs: Dynamically create Tkinter-based settings dialogs for your application, allowing users to intuitively modify configuration parameters via a graphical interface.

    settings_dlg.png

  • 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.

    settings_doc.png

  • Override System: Supports robust overriding of configuration values via configuration files and command-line arguments, with clear precedence.


๐Ÿ“š Usage#

1. Define Your Configuration#

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.

# my_project/config_example.py

from datetime import datetime
from pathlib import Path

from config_cli_gui.config import (
    ConfigCategory,
    ConfigManager,
    ConfigParameter,
)
from config_cli_gui.configtypes.color import Color
from config_cli_gui.configtypes.font import Font
from config_cli_gui.configtypes.vector import Vector


class MiscConfig(ConfigCategory):
    def get_category_name(self) -> str:
        return "misc"

    some_numeric: ConfigParameter = ConfigParameter(
        name="some_numeric",
        value=int(42),
        help="Example integer",
        is_cli=True,
    )

    some_vector: ConfigParameter = ConfigParameter(
        name="some_vector",
        value=Vector(1, 2, 3),
        help="Example vector",
    )

    some_file: ConfigParameter = ConfigParameter(
        name="some_file",
        value=Path("some_file.txt"),
        help="Path to the file to use",
    )

    some_color: ConfigParameter = ConfigParameter(
        name="some_color",
        value=Color(255, 0, 0),
        help="Color setting for the application",
    )

    some_date: ConfigParameter = ConfigParameter(
        name="some_date",
        value=datetime.fromisoformat("2025-12-31 10:30:45"),
        help="Date setting for the application",
    )

    some_font: ConfigParameter = ConfigParameter(
        name="some_font",
        value=Font("DejaVuSans.ttf", size=12, color=Color(0, 0, 255)),
        help="Font setting for the application",
    )


class AppConfig(ConfigCategory):
    """Application-specific configuration parameters."""

    def get_category_name(self) -> str:
        return "app"

    log_level: ConfigParameter = ConfigParameter(
        name="log_level",
        value="INFO",
        choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
        help="Logging level for the application",
    )

    log_file_max_size: ConfigParameter = ConfigParameter(
        name="log_file_max_size",
        value=10,
        help="Maximum log file size in MB before rotation",
    )


class ProjectConfigManager(ConfigManager):  # Inherit from ConfigManager
    """Main configuration manager that handles all parameter categories."""

    app: AppConfig
    misc: MiscConfig

    def __init__(self, config_file: str | None = None, **kwargs):
        """Initialize the configuration manager with all parameter categories."""
        categories = (MiscConfig(), AppConfig())
        super().__init__(categories, config_file, **kwargs)

2. Generate CLI#

Use the generic CLI functions to parse command-line arguments based on your defined CliConfig.

# my_project/cli_example.py
from config_cli_gui.cli import CliGenerator
from config_cli_gui.config import ConfigManager
from tests.example_project.config.config_example import ProjectConfigManager
from tests.example_project.core.base import BaseGPXProcessor
from tests.example_project.core.logging import initialize_logging


def run_main_processing(_config: ConfigManager) -> int:
    """Main processing function that gets called by the CLI generator.

    Args:
        _config: Configuration manager with all settings

    Returns:
        Exit code (0 for success, non-zero for error)
    """
    # Initialize logging system
    logger_manager = initialize_logging(_config)
    logger = logger_manager.get_logger("config_cli_gui.cli")

    try:
        # Log startup information
        logger.info("Starting config_cli_gui CLI")
        logger_manager.log_config_summary()

        logger.info(f"Processing input")

        # Create and run BaseGPXProcessor
        processor = BaseGPXProcessor(
            _config.get_category("cli").input.default,
            _config.get_category("cli").output.default,
            _config.get_category("cli").min_dist.default,
            _config.get_category("app").date_format.default,
            _config.get_category("cli").elevation.default,
            logger=logger,
        )

        logger.info("Starting conversion process")

        # Run the processing (adjust method name based on your actual implementation)
        result_files = processor.compress_files()
        logger.info(f"Successfully processed {result_files}")
        return 0

    except Exception as e:
        logger.error(f"Processing failed: {e}")
        logger.debug("Full traceback:", exc_info=True)
        return 1


def main():
    """Main entry point for the CLI application."""
    # Create the base configuration manager
    config_manager = ProjectConfigManager()

    # Create CLI generator
    cli_generator = CliGenerator(config_manager=config_manager, app_name="config_cli_gui")

    # Run the CLI with our main processing function
    return cli_generator.run_cli(
        main_function=run_main_processing,
        description="Example CLI for config-cli-gui using the generic config framework.",
    )


if __name__ == "__main__":
    import sys

    sys.exit(main())

3. Integrate GUI Settings Dialog#

The SettingsDialog from config-cli-gui (or your project's adapted version) can be used to easily create a settings window.

# my_project/gui_example.py (Simplified example)
import tkinter as tk
from tests.example_project.config.config_example import ProjectConfigManager
from config_cli_gui.gui import GenericSettingsDialog  # Assuming gui_settings is part of the generic lib or adapted


def open_settings_window(parent_root, config_manager: ProjectConfigManager):
    dialog = GenericSettingsDialog(parent_root, config_manager)
    parent_root.wait_window(dialog.dialog)
    # After dialog closes, config_manager will have updated values if 'OK' was clicked
    print("Settings updated or cancelled.")
    print(f"New GUI Theme: {config_manager.get_category('gui').theme.default}")


if __name__ == "__main__":
    root = tk.Tk()
    root.withdraw()  # Hide main window for this example

    # Initialize your project's config manager
    project_config = ProjectConfigManager()

    open_settings_window(root, project_config)

    root.destroy()

4. Generate Documentation and Default Config#

Use the static methods on your ProjectConfigManager to generate config.yaml, cli.md, and config.md files.

# scripts/generate_docs.py (or similar script in your project)
from tests.example_project.config.config_example import ProjectConfigManager
from config_cli_gui.docs import DocumentationGenerator
import os

# Define output paths
output_dir = "docs/generated"
os.makedirs(output_dir, exist_ok=True)

default_config = "config.yaml"  # At the project root or similar
default_cli_doc = os.path.join(output_dir, "cli.md")
default_config_doc = os.path.join(output_dir, "config.md")
_config = ProjectConfigManager()
doc_gen = DocumentationGenerator(_config)
doc_gen.generate_default_config_file(output_file=default_config)
print(f"Generated: {default_config}")

doc_gen.generate_config_markdown_doc(output_file=default_config_doc)
print(f"Generated: {default_config_doc}")

doc_gen.generate_cli_markdown_doc(output_file=default_cli_doc)
print(f"Generated: {default_cli_doc}")

print("Documentation and default config generation complete.")

By following this structure, config-cli-gui provides a robust and maintainable foundation for your application's configuration needs.