Grotti 3.3.0: Projekte mit Ordner, Datei-Löschen/Verschieben, Material-Archiv, UI-Polish.

This commit is contained in:
Dominic Gundel committed 2026-10-18 20:10:32 +02:00
1 parent 4ddffbbeb1
commit 67a89d1794
45 files changed
+2856 -320

No files matched your search

+80 -9
View File
@@ -91,40 +91,111 @@ def color_hex(channels):
return "#%02x%02x%02x" % tuple(rgb)
def profile_name(raw):
"""Orca-Profil → Katalogname: `Sunlu PLA Meta @Kobra 3` → `Sunlu PLA Meta`."""
text = (raw or "").strip().strip('"').strip("'").strip()
if " @" in text:
text = text.rsplit(" @", 1)[0].strip()
return text
def filament_profiles(comments):
"""Profilnamen aus `; filament_settings_id`, Index = Slicer-Slot / paint_index."""
raw = (comments or {}).get("filament_settings_id") or ""
if not raw.strip():
fallback = profile_name((comments or {}).get("default_filament_profile") or "")
return [fallback] if fallback else []
return [profile_name(part) for part in raw.split(";") if profile_name(part)]
def attach_profiles(filaments, comments):
"""Hängt `profile` (Sorte) an Filament-Einträge anhand des paint_index."""
profiles = filament_profiles(comments)
if not filaments:
return filaments
if not profiles:
return filaments
for index, row in enumerate(filaments):
if not isinstance(row, dict):
continue
try:
paint = int(row.get("paint_index"))
except (TypeError, ValueError):
paint = index
name = ""
if 0 <= paint < len(profiles):
name = profiles[paint]
elif len(filaments) == 1 and len(profiles) == 1:
name = profiles[0]
if name:
row["profile"] = name
return filaments
def filaments_from_paint_info(raw):
"""Farben aus `; paint_info` — inkl. originalem `paint_index` (T-Nummer).
Bambu/Anycubic nutzen oft Lücken (T1 + T10 bei 16 Slots). Die Liste bleibt
kompakt (nur belegte Farben), aber jeder Eintrag trägt den Slicer-Index,
damit `.acm` und ACE-Map denselben Wert wie `Tn` im G-code schreiben.
"""
try:
items = json.loads(raw or "")
except (TypeError, json.JSONDecodeError):
return []
if not isinstance(items, list):
return []
found = []
found = {}
next_fallback = 0
for item in items:
if not isinstance(item, dict):
continue
try:
index = int(item.get("paint_index"))
except (TypeError, ValueError):
index = len(found)
while len(found) <= index:
found.append({"type": "", "color": ""})
found[index] = {
index = next_fallback
if index < 0:
continue
next_fallback = max(next_fallback, index + 1)
row = {
"type": str(item.get("material_type") or "").strip(),
"color": color_hex(item.get("paint_color")),
"paint_index": index,
}
return [row for row in found if row["type"] or row["color"]]
if row["type"] or row["color"]:
found[index] = row
return [found[key] for key in sorted(found)]
def filaments_of(comments):
painted = filaments_from_paint_info(comments.get("paint_info"))
if painted:
return painted
return attach_profiles(painted, comments)
types = [part.strip() for part in (comments.get("filament_type") or "").split(";") if part.strip()]
colors = [part.strip() for part in (comments.get("filament_colour") or comments.get("filament_color") or "").split(";")]
return [
{"type": kind, "color": colors[index] if index < len(colors) else ""}
rows = [
{
"type": kind,
"color": colors[index] if index < len(colors) else "",
"paint_index": index,
}
for index, kind in enumerate(types)
]
return attach_profiles(rows, comments)
def tools_used_in_gcode(content):
"""T-/MMU-Werkzeuge aus dem G-code (wie Rinkhals mmu_ace_metadata)."""
if not content:
return set()
if isinstance(content, str):
content = content.encode("utf-8", "replace")
found = set()
for line in _gcode_lines(content):
match = _TOOL_RE.match(line)
if match:
found.add(int(match.group(1)))
return found
def _gcode_lines(content):