From 3e76230f611ea261d40f3ea9d216c35fb418e2ac Mon Sep 17 00:00:00 2001 From: chuan-1-1 Date: Wed, 13 May 2026 19:00:26 +0800 Subject: [PATCH] Add Tecplot .map format export support Add save_tecot_map() function for exporting palettes as Tecplot color map files (.map) compatible with Tecplot 360. Add "Tecplot (.map)" option to the export dialog UI with both Chinese and English labels, and include it in the "All Formats" bulk export. --- app/storage.py | 36 ++++++++++++++++++++++++++++++++++++ app/ui/main_window.py | 17 +++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/storage.py b/app/storage.py index 6650a7e..0281024 100644 --- a/app/storage.py +++ b/app/storage.py @@ -82,6 +82,42 @@ def save_palette_ase(palette: Palette, output_path: Path) -> None: output_path.write_bytes(header + b"".join(blocks)) +def save_tecot_map(palette: Palette, output_path: Path) -> None: + ensure_directory(output_path.parent) + colors = palette.colors + num_points = len(colors) + if num_points < 2: + return + + lines: list[str] = ["#!MC 1410"] + lines.append("$!CreateColorMap") + lines.append(f" Name = '{palette.name}'") + lines.append(f" NumControlPoints = {num_points}") + + for idx, color in enumerate(colors): + r, g, b = color.rgb + fraction = idx / (num_points - 1) if num_points > 1 else 0.0 + fraction_str = f"{fraction:.4f}".rstrip("0").rstrip(".") + lines.append(f" ControlPoint {idx + 1}") + lines.append(" {") + lines.append(f" ColorMapFraction = {fraction_str}") + lines.append(" LeadRGB") + lines.append(" {") + lines.append(f" R = {r}") + lines.append(f" G = {g}") + lines.append(f" B = {b}") + lines.append(" }") + lines.append(" TrailRGB") + lines.append(" {") + lines.append(f" R = {r}") + lines.append(f" G = {g}") + lines.append(f" B = {b}") + lines.append(" }") + lines.append(" }") + + output_path.write_text("\n".join(lines), encoding="utf-8") + + def save_originlab_pal(palette: Palette, output_path: Path, steps: int = 256) -> None: ensure_directory(output_path.parent) rgb_values = [color.rgb for color in palette.colors] diff --git a/app/ui/main_window.py b/app/ui/main_window.py index 4b56837..d5f2742 100644 --- a/app/ui/main_window.py +++ b/app/ui/main_window.py @@ -49,7 +49,7 @@ from app.config import AppConfig from app.models import ColorEntry, Palette from app.parsers import load_image_grid_palette, load_palette, load_pdf_palette, pdf_page_count, render_pdf_page, scan_palettes -from app.storage import ensure_directory, save_originlab_pal, save_palette_ase, save_palette_csv, save_palette_json +from app.storage import ensure_directory, save_originlab_pal, save_palette_ase, save_palette_csv, save_palette_json, save_tecot_map from app.webdav import WebDavClient, WebDavError from app.ui.pdf_dialog import PdfExtractDialog @@ -72,6 +72,7 @@ def ui_text(language: str, key: str, **kwargs: object) -> str: "target_general": "通用", "target_python": "Python", "target_matlab": "MATLAB", + "target_tecot": "Tecplot (.map)", "target_all_formats": "全部格式", "order_current": "当前顺序", "order_reverse": "反向顺序", @@ -150,6 +151,7 @@ def ui_text(language: str, key: str, **kwargs: object) -> str: "target_general": "General", "target_python": "Python", "target_matlab": "MATLAB", + "target_tecot": "Tecplot (.map)", "target_all_formats": "All Formats", "order_current": "Current order", "order_reverse": "Reverse order", @@ -1405,6 +1407,7 @@ def __init__(self, colors: list[ColorEntry], parent: QWidget | None = None) -> N [ ui_text(self.ui_language, "target_originlab"), ui_text(self.ui_language, "target_general"), + ui_text(self.ui_language, "target_tecot"), "R", ui_text(self.ui_language, "target_python"), ui_text(self.ui_language, "target_matlab"), @@ -1444,6 +1447,7 @@ def target_key(self) -> str: mapping = { ui_text(self.ui_language, "target_general"): "general", ui_text(self.ui_language, "target_originlab"): "originlab", + ui_text(self.ui_language, "target_tecot"): "tecot", "R": "r", ui_text(self.ui_language, "target_python"): "python", ui_text(self.ui_language, "target_matlab"): "matlab", @@ -4130,6 +4134,9 @@ def build_clipboard_text(self, target_key: str, palette: Palette) -> str: if target_key == "matlab": rows = [f"{r/255:.6f} {g/255:.6f} {b/255:.6f}" for r, g, b in (color.rgb for color in palette.colors)] return f"{self.make_safe_filename(palette.name)} = [ ...\n " + "; ...\n ".join(rows) + "\n];" + if target_key == "tecot": + rows = [f"{r/255:.6f} {g/255:.6f} {b/255:.6f}" for r, g, b in (color.rgb for color in palette.colors)] + return f"{self.make_safe_filename(palette.name)} = [ ...\n " + "; ...\n ".join(rows) + "\n];" if target_key == "originlab": return "\n".join(hexes) return ", ".join(hexes) @@ -4143,7 +4150,7 @@ def export_palette_files(self, target_key: str, palette: Palette) -> Path | None save_palette_json(palette, library_json_path) created_files: list[Path] = [library_json_path] if target_key == "all_formats": - for export_key in ("originlab", "general", "r", "python", "matlab"): + for export_key in ("originlab", "general", "tecot", "r", "python", "matlab"): self.export_palette_files(export_key, palette) return library_json_path exports_root = Path(self.config.library_dir) / "exports" / target_key @@ -4164,6 +4171,12 @@ def export_palette_files(self, target_key: str, palette: Palette) -> Path | None created_files.extend([ase_path, csv_path]) self.upload_webdav_files(created_files) return library_json_path + if target_key == "tecot": + map_path = exports_root / f"{file_stem}.map" + save_tecot_map(palette, map_path) + created_files.append(map_path) + self.upload_webdav_files(created_files) + return library_json_path content = self.build_clipboard_text(target_key, palette) extension = {"r": ".R", "python": ".py", "matlab": ".m"}[target_key] output_path = exports_root / f"{file_stem}{extension}"