-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtemplate_loader.py
More file actions
291 lines (235 loc) · 8.89 KB
/
template_loader.py
File metadata and controls
291 lines (235 loc) · 8.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
"""
Template Loader - Load and manage render templates from JSON files.
Handles loading builtin and user templates, applying them to UI controls,
and saving user templates.
"""
import json
import os
from typing import Dict, List, Optional, Any
from pathlib import Path
# Template directory paths (relative to this module)
_MODULE_DIR = Path(__file__).parent
BUILTIN_TEMPLATES_DIR = _MODULE_DIR / "templates" / "builtin"
USER_TEMPLATES_DIR = _MODULE_DIR / "templates" / "user"
def get_template_dirs() -> tuple:
"""
Get paths to template directories.
Returns:
Tuple of (builtin_dir, user_dir)
"""
return BUILTIN_TEMPLATES_DIR, USER_TEMPLATES_DIR
def ensure_template_dirs():
"""Create template directories if they don't exist."""
BUILTIN_TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
USER_TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
def list_templates(template_type: str = None) -> List[Dict]:
"""
List all available templates.
Args:
template_type: Filter by type (e.g., 'spinrender', 'static')
None returns all templates
Returns:
List of template info dicts with keys: name, description, type, path, is_builtin
"""
ensure_template_dirs()
templates = []
# Load builtin templates
for path in BUILTIN_TEMPLATES_DIR.glob("*.json"):
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
if template_type is None or data.get("type") == template_type:
templates.append({
"name": data.get("name", path.stem),
"description": data.get("description", ""),
"type": data.get("type", "unknown"),
"path": str(path),
"is_builtin": True
})
except (json.JSONDecodeError, IOError):
pass # Skip invalid files
# Load user templates
for path in USER_TEMPLATES_DIR.glob("*.json"):
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
if template_type is None or data.get("type") == template_type:
templates.append({
"name": data.get("name", path.stem),
"description": data.get("description", ""),
"type": data.get("type", "unknown"),
"path": str(path),
"is_builtin": False
})
except (json.JSONDecodeError, IOError):
pass
return sorted(templates, key=lambda x: (not x["is_builtin"], x["name"].lower()))
def load_template(path: str) -> Optional[Dict]:
"""
Load a template from file.
Args:
path: Path to template JSON file
Returns:
Template dict or None if failed
"""
try:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
print(f"Failed to load template {path}: {e}")
return None
def load_template_by_name(name: str, template_type: str = None) -> Optional[Dict]:
"""
Load a template by name.
Args:
name: Template name
template_type: Optional type filter
Returns:
Template dict or None if not found
"""
templates = list_templates(template_type)
for t in templates:
if t["name"].lower() == name.lower():
return load_template(t["path"])
return None
def save_template(template: Dict, name: str = None) -> str:
"""
Save a template to user templates directory.
Args:
template: Template dict to save
name: Optional name override (uses template["name"] if not provided)
Returns:
Path to saved file
"""
ensure_template_dirs()
name = name or template.get("name", "Untitled")
# Sanitize filename
safe_name = "".join(c for c in name if c.isalnum() or c in " -_").strip()
safe_name = safe_name.replace(" ", "_").lower()
path = USER_TEMPLATES_DIR / f"{safe_name}.json"
with open(path, "w", encoding="utf-8") as f:
json.dump(template, f, indent=4)
return str(path)
def delete_template(path: str) -> bool:
"""
Delete a user template.
Args:
path: Path to template file
Returns:
True if deleted, False if builtin or failed
"""
path = Path(path)
# Don't allow deleting builtin templates
if BUILTIN_TEMPLATES_DIR in path.parents or path.parent == BUILTIN_TEMPLATES_DIR:
return False
try:
path.unlink()
return True
except IOError:
return False
def template_to_params(template: Dict) -> Dict:
"""
Convert template dict to flat CLI parameters dict.
Args:
template: Template dict with nested structure
Returns:
Flat dict of CLI parameters
"""
params = {}
# Image settings
img = template.get("image_settings", {})
params["width"] = img.get("width", 1920)
params["height"] = img.get("height", 1080)
params["side"] = img.get("side", "top")
params["background"] = img.get("background", "transparent")
params["quality"] = img.get("quality", "high")
params["preset"] = img.get("preset", "follow_plot_settings")
params["use_board_stackup_colors"] = img.get("use_board_stackup_colors", False)
params["floor"] = img.get("floor", False)
params["perspective"] = img.get("perspective", True)
# Camera settings
cam = template.get("camera_settings", {})
params["zoom"] = cam.get("zoom", 1.0)
params["pan"] = cam.get("pan", "")
params["pivot"] = cam.get("pivot", "")
params["rotate"] = cam.get("rotate", "0,0,0")
# Lighting
light = template.get("lighting", {})
params["light_top"] = light.get("top", 0.5)
params["light_bottom"] = light.get("bottom", 0.1)
params["light_side"] = light.get("side", 0.3)
params["light_camera"] = light.get("camera", 0.2)
params["light_side_elevation"] = light.get("side_elevation", 60)
# Animation (for spinrender)
anim = template.get("animation", {})
params["fps"] = anim.get("fps", 30)
params["duration"] = anim.get("duration", 3)
params["frames"] = anim.get("frames", 90)
params["format"] = anim.get("format", "mp4")
params["tilt_x"] = anim.get("tilt_x", -30)
params["tilt_y"] = anim.get("tilt_y", 0)
return params
def params_to_template(params: Dict, name: str, description: str = "",
template_type: str = "spinrender") -> Dict:
"""
Convert flat CLI parameters to template dict structure.
Args:
params: Flat dict of parameters
name: Template name
description: Template description
template_type: Template type
Returns:
Template dict with nested structure
"""
return {
"name": name,
"description": description,
"type": template_type,
"version": "1.0",
"image_settings": {
"width": params.get("width", 1920),
"height": params.get("height", 1080),
"side": params.get("side", "top"),
"background": params.get("background", "transparent"),
"quality": params.get("quality", "high"),
"preset": params.get("preset", "follow_plot_settings"),
"use_board_stackup_colors": params.get("use_board_stackup_colors", False),
"floor": params.get("floor", False),
"perspective": params.get("perspective", True)
},
"camera_settings": {
"zoom": params.get("zoom", 1.0),
"pan": params.get("pan", ""),
"pivot": params.get("pivot", ""),
"rotate": params.get("rotate", "0,0,0")
},
"lighting": {
"top": params.get("light_top", 0.5),
"bottom": params.get("light_bottom", 0.1),
"side": params.get("light_side", 0.3),
"camera": params.get("light_camera", 0.2),
"side_elevation": params.get("light_side_elevation", 60)
},
"animation": {
"fps": params.get("fps", 30),
"duration": params.get("duration", 3),
"frames": params.get("frames", 90),
"format": params.get("format", "mp4"),
"tilt_x": params.get("tilt_x", -30),
"tilt_y": params.get("tilt_y", 0)
}
}
def get_default_template(template_type: str = "spinrender") -> Dict:
"""
Get the default template for a type.
Args:
template_type: Template type
Returns:
Default template dict
"""
# Try to load builtin default
default = load_template_by_name("Default", template_type)
if default:
return default
# Return hardcoded default
return params_to_template({}, "Default", "Default settings", template_type)