config-cli-gui 0.2.7__tar.gz → 0.2.9__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.2.7 → config_cli_gui-0.2.9}/HISTORY.md +16 -0
  2. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/PKG-INFO +48 -27
  3. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/README.md +1 -1
  4. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/config.yaml +12 -1
  5. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/index.md +46 -26
  6. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/usage/config.md +9 -5
  7. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/pyproject.toml +1 -0
  8. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui/_version.py +3 -3
  9. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui/config.py +8 -78
  10. config_cli_gui-0.2.9/src/config_cli_gui/configtypes/color.py +43 -0
  11. config_cli_gui-0.2.9/src/config_cli_gui/configtypes/font.py +80 -0
  12. config_cli_gui-0.2.9/src/config_cli_gui/configtypes/vector.py +31 -0
  13. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui/gui.py +130 -12
  14. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui.egg-info/PKG-INFO +48 -27
  15. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui.egg-info/SOURCES.txt +4 -0
  16. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui.egg-info/requires.txt +1 -0
  17. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/config/config_example.py +27 -2
  18. config_cli_gui-0.2.9/tests/example_project/gui/__init__.py +0 -0
  19. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/test_config_manager.py +9 -2
  20. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/test_docs.py +1 -1
  21. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/uv.lock +40 -1
  22. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/FUNDING.yml +0 -0
  23. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  24. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  25. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  26. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/actions/setup-environment/action.yml +0 -0
  27. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/dependabot.yml +0 -0
  28. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/init.sh +0 -0
  29. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/release_message.sh +0 -0
  30. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/update_funding.py +0 -0
  31. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/workflows/main.yml +0 -0
  32. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/workflows/release.yml +0 -0
  33. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.github/workflows/update_readme.yml +0 -0
  34. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.gitignore +0 -0
  35. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.idea/runConfigurations/config_generate.xml +0 -0
  36. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.idea/runConfigurations/example_project_cli.xml +0 -0
  37. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.idea/runConfigurations/example_project_gui.xml +0 -0
  38. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.pre-commit-config.yaml +0 -0
  39. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/.readthedocs.yaml +0 -0
  40. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/LICENSE +0 -0
  41. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/Makefile +0 -0
  42. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/.nav.yml +0 -0
  43. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/_static/img/favicon.png +0 -0
  44. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/_static/img/logo.png +0 -0
  45. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/css/custom.css +0 -0
  46. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/develop/contributing.md +0 -0
  47. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/develop/make_windows.md +0 -0
  48. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/develop/naming_convention.md +0 -0
  49. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/funding/funding.md +0 -0
  50. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/getting-started/install.md +0 -0
  51. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/getting-started/virtual-environment.md +0 -0
  52. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/docs/usage/cli.md +0 -0
  53. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/mkdocs.yml +0 -0
  54. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/scripts/show_filelist.ps1 +0 -0
  55. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/scripts/show_tree.ps1 +0 -0
  56. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/scripts/show_tree.py +0 -0
  57. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/scripts/update_readme.py +0 -0
  58. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/setup.cfg +0 -0
  59. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/__init__.py +0 -0
  60. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui/__init__.py +0 -0
  61. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui/cli.py +0 -0
  62. {config_cli_gui-0.2.7/tests → config_cli_gui-0.2.9/src/config_cli_gui/configtypes}/__init__.py +0 -0
  63. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui/docs.py +0 -0
  64. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
  65. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
  66. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/src/config_cli_gui.egg-info/top_level.txt +0 -0
  67. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/template.yml.url +0 -0
  68. {config_cli_gui-0.2.7/tests/example_project → config_cli_gui-0.2.9/tests}/__init__.py +0 -0
  69. {config_cli_gui-0.2.7/tests/example_project/cli → config_cli_gui-0.2.9/tests/example_project}/__init__.py +0 -0
  70. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/__main__.py +0 -0
  71. {config_cli_gui-0.2.7/tests/example_project/config → config_cli_gui-0.2.9/tests/example_project/cli}/__init__.py +0 -0
  72. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/cli/__main__.py +0 -0
  73. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/cli/cli_example.py +0 -0
  74. {config_cli_gui-0.2.7/tests/example_project/core → config_cli_gui-0.2.9/tests/example_project/config}/__init__.py +0 -0
  75. {config_cli_gui-0.2.7/tests/example_project/gui → config_cli_gui-0.2.9/tests/example_project/core}/__init__.py +0 -0
  76. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/core/base.py +0 -0
  77. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/core/logging.py +0 -0
  78. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/example.gpx +0 -0
  79. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/gui/__main__.py +0 -0
  80. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/gui/config.yaml +0 -0
  81. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/example_project/gui/gui_example.py +0 -0
  82. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/test_cli.py +0 -0
  83. {config_cli_gui-0.2.7 → config_cli_gui-0.2.9}/tests/test_generic_cli.py +0 -0
@@ -4,6 +4,22 @@ Changelog
4
4
 
5
5
  (unreleased)
6
6
  ------------
7
+ - Spinbox for vector. [Paul Magister]
8
+ - Better example, make get_image_font sensitive to dpi, [Paul Magister]
9
+
10
+
11
+ 0.2.8 (2025-12-02)
12
+ ------------------
13
+ - Docs: Update HISTORY.md for release 0.2.8. [Paul Magister]
14
+ - Refactoring: get_image_font will look for the actual font path in the
15
+ system. [Paul Magister]
16
+ - Font preview. [Paul Magister]
17
+ - Add font category in config.py. [Paul Magister]
18
+
19
+
20
+ 0.2.7 (2025-11-29)
21
+ ------------------
22
+ - Docs: Update HISTORY.md for release 0.2.7. [Paul Magister]
7
23
  - Also show required params in gui. [Paul Magister]
8
24
 
9
25
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: config-cli-gui
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Feature-rich Python project template for config-cli-gui.
5
5
  Author: pamagister
6
6
  Requires-Python: <3.12,>=3.10
@@ -8,6 +8,7 @@ Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
9
  Requires-Dist: pydantic>=2.11.7
10
10
  Requires-Dist: pyyaml>=6.0.2
11
+ Requires-Dist: pillow>=12.0.0
11
12
  Provides-Extra: dev
12
13
  Requires-Dist: pytest>=8.4.0; extra == "dev"
13
14
  Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
@@ -80,40 +81,58 @@ Start by defining your application's configuration parameters in a central `conf
80
81
  ```python
81
82
  # my_project/config_example.py
82
83
 
84
+ from datetime import datetime
85
+ from pathlib import Path
86
+
83
87
  from config_cli_gui.config import (
84
88
  ConfigCategory,
85
89
  ConfigManager,
86
90
  ConfigParameter,
87
91
  )
92
+ from config_cli_gui.configtypes.color import Color
93
+ from config_cli_gui.configtypes.font import Font
94
+ from config_cli_gui.configtypes.vector import Vector
88
95
 
89
96
 
90
- class CliConfig(ConfigCategory):
91
- """CLI-specific configuration parameters."""
92
-
97
+ class MiscConfig(ConfigCategory):
93
98
  def get_category_name(self) -> str:
94
- return "cli"
95
-
96
- # Positional argument
97
- input: ConfigParameter = ConfigParameter(
98
- name="input",
99
- default="",
100
- help="Path to input (file or folder)",
101
- required=True,
99
+ return "misc"
100
+
101
+ some_numeric: ConfigParameter = ConfigParameter(
102
+ name="some_numeric",
103
+ value=int(42),
104
+ help="Example integer",
102
105
  is_cli=True,
103
106
  )
104
107
 
105
- min_dist: ConfigParameter = ConfigParameter(
106
- name="min_dist",
107
- default=20,
108
- help="Maximum distance between two waypoints",
109
- is_cli=True,
108
+ some_vector: ConfigParameter = ConfigParameter(
109
+ name="some_vector",
110
+ value=Vector(1, 2, 3),
111
+ help="Example vector",
110
112
  )
111
113
 
112
- extract_waypoints: ConfigParameter = ConfigParameter(
113
- name="extract_waypoints",
114
- default=True,
115
- help="Extract starting points of each track as waypoint",
116
- is_cli=True,
114
+ some_file: ConfigParameter = ConfigParameter(
115
+ name="some_file",
116
+ value=Path("some_file.txt"),
117
+ help="Path to the file to use",
118
+ )
119
+
120
+ some_color: ConfigParameter = ConfigParameter(
121
+ name="some_color",
122
+ value=Color(255, 0, 0),
123
+ help="Color setting for the application",
124
+ )
125
+
126
+ some_date: ConfigParameter = ConfigParameter(
127
+ name="some_date",
128
+ value=datetime.fromisoformat("2025-12-31 10:30:45"),
129
+ help="Date setting for the application",
130
+ )
131
+
132
+ some_font: ConfigParameter = ConfigParameter(
133
+ name="some_font",
134
+ value=Font("DejaVuSans.ttf", size=12, color=Color(0, 0, 255)),
135
+ help="Font setting for the application",
117
136
  )
118
137
 
119
138
 
@@ -125,14 +144,14 @@ class AppConfig(ConfigCategory):
125
144
 
126
145
  log_level: ConfigParameter = ConfigParameter(
127
146
  name="log_level",
128
- default="INFO",
147
+ value="INFO",
129
148
  choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
130
149
  help="Logging level for the application",
131
150
  )
132
151
 
133
152
  log_file_max_size: ConfigParameter = ConfigParameter(
134
153
  name="log_file_max_size",
135
- default=10,
154
+ value=10,
136
155
  help="Maximum log file size in MB before rotation",
137
156
  )
138
157
 
@@ -140,11 +159,13 @@ class AppConfig(ConfigCategory):
140
159
  class ProjectConfigManager(ConfigManager): # Inherit from ConfigManager
141
160
  """Main configuration manager that handles all parameter categories."""
142
161
 
143
- categories = (CliConfig(), AppConfig())
144
-
162
+ app: AppConfig
163
+ misc: MiscConfig
164
+
145
165
  def __init__(self, config_file: str | None = None, **kwargs):
146
166
  """Initialize the configuration manager with all parameter categories."""
147
- super().__init__(self.categories, config_file, **kwargs)
167
+ categories = (MiscConfig(), AppConfig())
168
+ super().__init__(categories, config_file, **kwargs)
148
169
 
149
170
 
150
171
  ```
@@ -62,11 +62,11 @@ from datetime import datetime
62
62
  from pathlib import Path
63
63
 
64
64
  from config_cli_gui.config import (
65
- Color,
66
65
  ConfigCategory,
67
66
  ConfigManager,
68
67
  ConfigParameter,
69
68
  )
69
+ from config_cli_gui.configtypes.color import Color
70
70
  from config_cli_gui.docs import DocumentationGenerator
71
71
 
72
72
 
@@ -49,4 +49,15 @@ misc:
49
49
  # Date setting for the application | type=datetime
50
50
  some_date: '2025-12-31T10:30:45'
51
51
  # Path to the file to use | type=PosixPath
52
- some_file: some_file.txt
52
+ some_file: some_file.txt
53
+ # Font setting for the application | type=Font
54
+ some_font:
55
+ - DejaVuSans.ttf
56
+ - 12
57
+ - '#0000ff'
58
+ # Example integer | type=int
59
+ some_numeric: 42
60
+ # Example vector 2D | type=Vector
61
+ some_vector2d: (1, 2)
62
+ # Example vector 3D | type=Vector
63
+ some_vector3d: (1.1, 2.2, 3.3)
@@ -51,40 +51,58 @@ Start by defining your application's configuration parameters in a central `conf
51
51
  ```python
52
52
  # my_project/config_example.py
53
53
 
54
+ from datetime import datetime
55
+ from pathlib import Path
56
+
54
57
  from config_cli_gui.config import (
55
58
  ConfigCategory,
56
59
  ConfigManager,
57
60
  ConfigParameter,
58
61
  )
62
+ from config_cli_gui.configtypes.color import Color
63
+ from config_cli_gui.configtypes.font import Font
64
+ from config_cli_gui.configtypes.vector import Vector
59
65
 
60
66
 
61
- class CliConfig(ConfigCategory):
62
- """CLI-specific configuration parameters."""
63
-
67
+ class MiscConfig(ConfigCategory):
64
68
  def get_category_name(self) -> str:
65
- return "cli"
66
-
67
- # Positional argument
68
- input: ConfigParameter = ConfigParameter(
69
- name="input",
70
- default="",
71
- help="Path to input (file or folder)",
72
- required=True,
69
+ return "misc"
70
+
71
+ some_numeric: ConfigParameter = ConfigParameter(
72
+ name="some_numeric",
73
+ value=int(42),
74
+ help="Example integer",
73
75
  is_cli=True,
74
76
  )
75
77
 
76
- min_dist: ConfigParameter = ConfigParameter(
77
- name="min_dist",
78
- default=20,
79
- help="Maximum distance between two waypoints",
80
- is_cli=True,
78
+ some_vector: ConfigParameter = ConfigParameter(
79
+ name="some_vector",
80
+ value=Vector(1, 2, 3),
81
+ help="Example vector",
81
82
  )
82
83
 
83
- extract_waypoints: ConfigParameter = ConfigParameter(
84
- name="extract_waypoints",
85
- default=True,
86
- help="Extract starting points of each track as waypoint",
87
- is_cli=True,
84
+ some_file: ConfigParameter = ConfigParameter(
85
+ name="some_file",
86
+ value=Path("some_file.txt"),
87
+ help="Path to the file to use",
88
+ )
89
+
90
+ some_color: ConfigParameter = ConfigParameter(
91
+ name="some_color",
92
+ value=Color(255, 0, 0),
93
+ help="Color setting for the application",
94
+ )
95
+
96
+ some_date: ConfigParameter = ConfigParameter(
97
+ name="some_date",
98
+ value=datetime.fromisoformat("2025-12-31 10:30:45"),
99
+ help="Date setting for the application",
100
+ )
101
+
102
+ some_font: ConfigParameter = ConfigParameter(
103
+ name="some_font",
104
+ value=Font("DejaVuSans.ttf", size=12, color=Color(0, 0, 255)),
105
+ help="Font setting for the application",
88
106
  )
89
107
 
90
108
 
@@ -96,14 +114,14 @@ class AppConfig(ConfigCategory):
96
114
 
97
115
  log_level: ConfigParameter = ConfigParameter(
98
116
  name="log_level",
99
- default="INFO",
117
+ value="INFO",
100
118
  choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
101
119
  help="Logging level for the application",
102
120
  )
103
121
 
104
122
  log_file_max_size: ConfigParameter = ConfigParameter(
105
123
  name="log_file_max_size",
106
- default=10,
124
+ value=10,
107
125
  help="Maximum log file size in MB before rotation",
108
126
  )
109
127
 
@@ -111,11 +129,13 @@ class AppConfig(ConfigCategory):
111
129
  class ProjectConfigManager(ConfigManager): # Inherit from ConfigManager
112
130
  """Main configuration manager that handles all parameter categories."""
113
131
 
114
- categories = (CliConfig(), AppConfig())
115
-
132
+ app: AppConfig
133
+ misc: MiscConfig
134
+
116
135
  def __init__(self, config_file: str | None = None, **kwargs):
117
136
  """Initialize the configuration manager with all parameter categories."""
118
- super().__init__(self.categories, config_file, **kwargs)
137
+ categories = (MiscConfig(), AppConfig())
138
+ super().__init__(categories, config_file, **kwargs)
119
139
 
120
140
 
121
141
  ```
@@ -41,9 +41,13 @@ The parameters in the cli category can be accessed via the command line interfac
41
41
 
42
42
  ## Category "misc"
43
43
 
44
- | Name | Type | Description | Default | Choices |
45
- |------------|-----------|-----------------------------------|---------------------------------------------|---------|
46
- | some_file | PosixPath | Path to the file to use | PosixPath('some_file.txt') | - |
47
- | some_color | Color | Color setting for the application | Color(255, 0, 0) | - |
48
- | some_date | datetime | Date setting for the application | datetime.datetime(2025, 12, 31, 10, 30, 45) | - |
44
+ | Name | Type | Description | Default | Choices |
45
+ |---------------|-----------|-----------------------------------|--------------------------------------------------------------|---------|
46
+ | some_numeric | int | Example integer | 42 | - |
47
+ | some_vector2d | Vector | Example vector 2D | Vector(1, 2) | - |
48
+ | some_vector3d | Vector | Example vector 3D | Vector(1.1, 2.2, 3.3) | - |
49
+ | some_file | PosixPath | Path to the file to use | PosixPath('some_file.txt') | - |
50
+ | some_color | Color | Color setting for the application | Color(255, 0, 0) | - |
51
+ | some_date | datetime | Date setting for the application | datetime.datetime(2025, 12, 31, 10, 30, 45) | - |
52
+ | some_font | Font | Font setting for the application | Font(type='DejaVuSans.ttf', size=12, color=Color(0, 0, 255)) | - |
49
53
 
@@ -10,6 +10,7 @@ requires-python = ">=3.10,<3.12"
10
10
  dependencies = [
11
11
  "pydantic>=2.11.7",
12
12
  "pyyaml>=6.0.2",
13
+ "pillow>=12.0.0",
13
14
  ]
14
15
 
15
16
  # Dev dependencies as optional dependencies
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.7'
32
- __version_tuple__ = version_tuple = (0, 2, 7)
31
+ __version__ = version = '0.2.9'
32
+ __version_tuple__ = version_tuple = (0, 2, 9)
33
33
 
34
- __commit_id__ = commit_id = 'g56f4529b2'
34
+ __commit_id__ = commit_id = 'gc34b684af'
@@ -8,83 +8,9 @@ from typing import Any
8
8
  import yaml
9
9
  from pydantic import BaseModel
10
10
 
11
-
12
- class Vector:
13
- """Class that represents a vector or point in 2D or 3D"""
14
-
15
- def __init__(self, x: int | float, y: int | float, z: int | float | None = None):
16
- self.x = x
17
- self.y = y
18
- self.z = z
19
-
20
- def to_list(self) -> list[int]:
21
- return [self.x, self.y, self.z] if self.z is not None else [self.x, self.y]
22
-
23
- @classmethod
24
- def from_list(cls, coordinate: list[int | str | float]) -> "Vector":
25
- if len(coordinate) == 2:
26
- return cls(float(coordinate[0]), float(coordinate[1]))
27
- if len(coordinate) >= 3:
28
- return cls(float(coordinate[0]), float(coordinate[1]), float(coordinate[2]))
29
- return cls()
30
-
31
- @classmethod
32
- def from_str(cls, coordinate: str) -> "Vector":
33
- return cls.from_list(coordinate.strip("()[]").split(","))
34
-
35
- def to_str(self) -> str:
36
- return f"({self.x}, {self.y}, {self.z})" if self.z is not None else f"({self.x}, {self.y})" # type: ignore
37
-
38
- def __str__(self):
39
- return self.to_str()
40
-
41
- def __repr__(self):
42
- return f"Vector{str(self)}"
43
-
44
-
45
- class Color:
46
- """Simple color class for RGB values."""
47
-
48
- def __init__(self, r: int = 0, g: int = 0, b: int = 0):
49
- self.r = max(0, min(255, r))
50
- self.g = max(0, min(255, g))
51
- self.b = max(0, min(255, b))
52
-
53
- def to_list(self) -> list[int]:
54
- return [self.r, self.g, self.b]
55
-
56
- def to_rgb(self) -> tuple[float]:
57
- return (self.r / 255, self.g / 255, self.b / 255)
58
-
59
- def to_pil(self) -> tuple[int, ...]:
60
- """Convert Color object to Pillow-compatible RGB tuple."""
61
- return tuple(int(c) for c in self.to_list())
62
-
63
- def to_hex(self) -> str:
64
- return f"#{self.r:02x}{self.g:02x}{self.b:02x}"
65
-
66
- @classmethod
67
- def from_list(cls, rgb_list: list[int | str]) -> "Color":
68
- if len(rgb_list) >= 3:
69
- return cls(int(rgb_list[0]), int(rgb_list[1]), int(rgb_list[2]))
70
- return cls()
71
-
72
- @classmethod
73
- def from_hex(cls, hex_color: str) -> "Color":
74
- hex_color = hex_color.lstrip("#")
75
- if len(hex_color) == 6:
76
- return cls(
77
- int(hex_color[0:2], 16),
78
- int(hex_color[2:4], 16),
79
- int(hex_color[4:6], 16),
80
- )
81
- return cls()
82
-
83
- def __str__(self):
84
- return self.to_hex()
85
-
86
- def __repr__(self):
87
- return f"Color({self.r}, {self.g}, {self.b})"
11
+ from config_cli_gui.configtypes.color import Color
12
+ from config_cli_gui.configtypes.font import Font
13
+ from config_cli_gui.configtypes.vector import Vector
88
14
 
89
15
 
90
16
  @dataclass
@@ -197,6 +123,8 @@ class ConfigManager:
197
123
  continue
198
124
 
199
125
  # Type conversions
126
+ if isinstance(param.value, Font) and isinstance(param_value, list):
127
+ param_value = Font.from_list(param_value)
200
128
  if isinstance(param.value, Color) and isinstance(param_value, list):
201
129
  param_value = Color.from_list(param_value)
202
130
  if isinstance(param.value, Color) and isinstance(param_value, str):
@@ -237,7 +165,9 @@ class ConfigManager:
237
165
  result[category_name] = {}
238
166
  for param in category.get_parameters():
239
167
  val = getattr(category, param.name).value
240
- if isinstance(val, Color):
168
+ if isinstance(val, Font):
169
+ val = val.to_list()
170
+ elif isinstance(val, Color):
241
171
  val = val.to_hex()
242
172
  elif isinstance(val, Vector):
243
173
  val = str(val.to_str())
@@ -0,0 +1,43 @@
1
+ class Color:
2
+ """Simple color class for RGB values."""
3
+
4
+ def __init__(self, r: int = 0, g: int = 0, b: int = 0):
5
+ self.r = max(0, min(255, r))
6
+ self.g = max(0, min(255, g))
7
+ self.b = max(0, min(255, b))
8
+
9
+ def to_list(self) -> list[int]:
10
+ return [self.r, self.g, self.b]
11
+
12
+ def to_rgb(self) -> tuple[float, float, float]:
13
+ return self.r / 255, self.g / 255, self.b / 255
14
+
15
+ def to_pil(self) -> tuple[int, ...]:
16
+ """Convert Color object to Pillow-compatible RGB tuple."""
17
+ return tuple(int(c) for c in self.to_list())
18
+
19
+ def to_hex(self) -> str:
20
+ return f"#{self.r:02x}{self.g:02x}{self.b:02x}"
21
+
22
+ @classmethod
23
+ def from_list(cls, rgb_list: list[int | str]) -> "Color":
24
+ if len(rgb_list) >= 3:
25
+ return cls(int(rgb_list[0]), int(rgb_list[1]), int(rgb_list[2]))
26
+ return cls()
27
+
28
+ @classmethod
29
+ def from_hex(cls, hex_color: str) -> "Color":
30
+ hex_color = hex_color.lstrip("#")
31
+ if len(hex_color) == 6:
32
+ return cls(
33
+ int(hex_color[0:2], 16),
34
+ int(hex_color[2:4], 16),
35
+ int(hex_color[4:6], 16),
36
+ )
37
+ return cls()
38
+
39
+ def __str__(self):
40
+ return self.to_hex()
41
+
42
+ def __repr__(self):
43
+ return f"Color({self.r}, {self.g}, {self.b})"
@@ -0,0 +1,80 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ from PIL import ImageFont
6
+
7
+ from config_cli_gui.configtypes.color import Color
8
+
9
+
10
+ def list_system_fonts() -> list[str]:
11
+ font_dirs = [
12
+ "/usr/share/fonts",
13
+ "/usr/local/share/fonts",
14
+ str(Path.home() / ".fonts"),
15
+ "/Library/Fonts",
16
+ "/System/Library/Fonts",
17
+ "C:/Windows/Fonts",
18
+ ]
19
+
20
+ fonts: list[str] = []
21
+ for d in font_dirs:
22
+ if os.path.isdir(d):
23
+ for root, _, files in os.walk(d):
24
+ for f in files:
25
+ if f.lower().endswith((".ttf", ".otf")):
26
+ fonts.append(os.path.join(root, f))
27
+ return fonts
28
+
29
+
30
+ class Font:
31
+ """Represents a font with type, size and color."""
32
+
33
+ # Klassenattribute nach Klassendefinition setzen
34
+ font_files = list_system_fonts()
35
+ font_names = sorted([os.path.basename(f) for f in font_files])
36
+ font_files_sorted = sorted(font_files, key=os.path.basename)
37
+
38
+ def __init__(self, font_type: str, size: float, color: "Color"):
39
+ self.name = font_type
40
+ self.size = size
41
+ self.color = color
42
+
43
+ def to_list(self) -> list[Any]:
44
+ return [self.name, self.size, self.color.to_hex()]
45
+
46
+ @classmethod
47
+ def from_list(cls, font_data: list[Any]) -> "Font":
48
+ if len(font_data) < 3:
49
+ return cls("Arial", 12, Color(0, 0, 0))
50
+
51
+ font_type, size, color_val = font_data
52
+ color = (
53
+ Color.from_hex(color_val) if isinstance(color_val, str) else Color.from_list(color_val)
54
+ )
55
+ return cls(str(font_type), float(size), color)
56
+
57
+ def get_image_font(self, dpi=25.4) -> ImageFont.FreeTypeFont:
58
+ """
59
+ Return a PIL FreeTypeFont, with fallback to default.
60
+
61
+ :param dpi: if dpi is provided, the font size is re-calculated on base of the dpi
62
+ :return:
63
+ """
64
+ try:
65
+ if self.name in self.font_names:
66
+ idx = self.font_names.index(self.name)
67
+ path = self.font_files_sorted[idx]
68
+ size = self.size * dpi / 25.4
69
+ return ImageFont.truetype(path, size)
70
+ except Exception as e:
71
+ print(f"Fehler beim Laden der Schrift '{self.name}': {e}")
72
+
73
+ print("Fallback: Nutze Default-Font.")
74
+ return ImageFont.load_default()
75
+
76
+ def __repr__(self) -> str:
77
+ return f"Font(type='{self.name}', size={self.size}, color={self.color!r})"
78
+
79
+ def __str__(self) -> str:
80
+ return f"{self.name}, {self.size}pt, {self.color}"
@@ -0,0 +1,31 @@
1
+ class Vector:
2
+ """Class that represents a vector or point in 2D or 3D"""
3
+
4
+ def __init__(self, x: int | float, y: int | float, z: int | float | None = None):
5
+ self.x = x
6
+ self.y = y
7
+ self.z = z
8
+
9
+ def to_list(self) -> list[int]:
10
+ return [self.x, self.y, self.z] if self.z is not None else [self.x, self.y]
11
+
12
+ @classmethod
13
+ def from_list(cls, coordinate: list[int | str | float]) -> "Vector":
14
+ if len(coordinate) == 2:
15
+ return cls(float(coordinate[0]), float(coordinate[1]))
16
+ if len(coordinate) >= 3:
17
+ return cls(float(coordinate[0]), float(coordinate[1]), float(coordinate[2]))
18
+ return cls()
19
+
20
+ @classmethod
21
+ def from_str(cls, coordinate: str) -> "Vector":
22
+ return cls.from_list(coordinate.strip("()[]").split(","))
23
+
24
+ def to_str(self) -> str:
25
+ return f"({self.x}, {self.y}, {self.z})" if self.z is not None else f"({self.x}, {self.y})" # type: ignore
26
+
27
+ def __str__(self):
28
+ return self.to_str()
29
+
30
+ def __repr__(self):
31
+ return f"Vector{str(self)}"