scitex 2.15.4__py3-none-any.whl → 2.15.5__py3-none-any.whl

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.
@@ -1,12 +1,20 @@
1
1
  #!/usr/bin/env python3
2
- # Timestamp: 2026-01-15
3
- # File: /home/ywatanabe/proj/scitex-code/src/scitex/_mcp_tools/canvas.py
4
- """Canvas module tools for FastMCP unified server."""
2
+ # Timestamp: 2026-01-29
3
+ # File: src/scitex/_mcp_tools/canvas.py
4
+ """Canvas module tools for FastMCP unified server.
5
+
6
+ .. deprecated:: 2.16.0
7
+ Canvas tools are deprecated. Use figrecipe MCP tools instead:
8
+ - plt_compose for multi-panel composition
9
+ - plt_plot for creating figures
10
+ - plt_reproduce for reproducing from recipes
11
+
12
+ figrecipe is mounted automatically if installed.
13
+ """
5
14
 
6
15
  from __future__ import annotations
7
16
 
8
17
  import json
9
- from typing import Optional
10
18
 
11
19
 
12
20
  def _json(data: dict) -> str:
@@ -14,8 +22,14 @@ def _json(data: dict) -> str:
14
22
 
15
23
 
16
24
  def register_canvas_tools(mcp) -> None:
17
- """Register canvas tools with FastMCP server."""
25
+ """Register canvas tools with FastMCP server.
18
26
 
27
+ Note: These tools are deprecated. figrecipe is mounted for composition.
28
+ """
29
+ # Mount figrecipe MCP server if available (preferred)
30
+ _mount_figrecipe(mcp)
31
+
32
+ # Legacy canvas tools (deprecated, for backward compatibility)
19
33
  @mcp.tool()
20
34
  async def canvas_create_canvas(
21
35
  parent_dir: str,
@@ -23,7 +37,7 @@ def register_canvas_tools(mcp) -> None:
23
37
  width_mm: float = 180,
24
38
  height_mm: float = 120,
25
39
  ) -> str:
26
- """[canvas] Create a new paper figure canvas workspace."""
40
+ """[canvas][DEPRECATED] Create a canvas workspace. Use plt_compose instead."""
27
41
  from scitex.canvas._mcp.handlers import create_canvas_handler
28
42
 
29
43
  result = await create_canvas_handler(
@@ -32,6 +46,7 @@ def register_canvas_tools(mcp) -> None:
32
46
  width_mm=width_mm,
33
47
  height_mm=height_mm,
34
48
  )
49
+ result["_deprecated"] = "Use plt_compose from figrecipe instead"
35
50
  return _json(result)
36
51
 
37
52
  @mcp.tool()
@@ -44,9 +59,9 @@ def register_canvas_tools(mcp) -> None:
44
59
  y_mm: float = 0,
45
60
  width_mm: float = 50,
46
61
  height_mm: float = 50,
47
- label: Optional[str] = None,
62
+ label: str | None = None,
48
63
  ) -> str:
49
- """[canvas] Add a panel to an existing canvas from an image or plot."""
64
+ """[canvas][DEPRECATED] Add a panel to canvas. Use plt_compose instead."""
50
65
  from scitex.canvas._mcp.handlers import add_panel_handler
51
66
 
52
67
  result = await add_panel_handler(
@@ -60,23 +75,25 @@ def register_canvas_tools(mcp) -> None:
60
75
  height_mm=height_mm,
61
76
  label=label,
62
77
  )
78
+ result["_deprecated"] = "Use plt_compose from figrecipe instead"
63
79
  return _json(result)
64
80
 
65
81
  @mcp.tool()
66
82
  async def canvas_list_panels(parent_dir: str, canvas_name: str) -> str:
67
- """[canvas] List all panels in a canvas with their properties."""
83
+ """[canvas][DEPRECATED] List panels in a canvas."""
68
84
  from scitex.canvas._mcp.handlers import list_panels_handler
69
85
 
70
86
  result = await list_panels_handler(
71
87
  parent_dir=parent_dir, canvas_name=canvas_name
72
88
  )
89
+ result["_deprecated"] = "Use figrecipe instead"
73
90
  return _json(result)
74
91
 
75
92
  @mcp.tool()
76
93
  async def canvas_remove_panel(
77
94
  parent_dir: str, canvas_name: str, panel_name: str
78
95
  ) -> str:
79
- """[canvas] Remove a panel from a canvas."""
96
+ """[canvas][DEPRECATED] Remove a panel from canvas."""
80
97
  from scitex.canvas._mcp.handlers import remove_panel_handler
81
98
 
82
99
  result = await remove_panel_handler(
@@ -84,17 +101,18 @@ def register_canvas_tools(mcp) -> None:
84
101
  canvas_name=canvas_name,
85
102
  panel_name=panel_name,
86
103
  )
104
+ result["_deprecated"] = "Use figrecipe instead"
87
105
  return _json(result)
88
106
 
89
107
  @mcp.tool()
90
108
  async def canvas_export_canvas(
91
109
  parent_dir: str,
92
110
  canvas_name: str,
93
- output_path: Optional[str] = None,
94
- format: Optional[str] = None,
111
+ output_path: str | None = None,
112
+ format: str | None = None,
95
113
  dpi: int = 300,
96
114
  ) -> str:
97
- """[canvas] Export/render canvas to PNG, PDF, or SVG format."""
115
+ """[canvas][DEPRECATED] Export canvas to image. Use plt_compose instead."""
98
116
  from scitex.canvas._mcp.handlers import export_canvas_handler
99
117
 
100
118
  result = await export_canvas_handler(
@@ -104,25 +122,41 @@ def register_canvas_tools(mcp) -> None:
104
122
  format=format,
105
123
  dpi=dpi,
106
124
  )
125
+ result["_deprecated"] = "Use plt_compose from figrecipe instead"
107
126
  return _json(result)
108
127
 
109
128
  @mcp.tool()
110
129
  async def canvas_list_canvases(parent_dir: str) -> str:
111
- """[canvas] List all canvases in a directory."""
130
+ """[canvas][DEPRECATED] List canvases in a directory."""
112
131
  from scitex.canvas._mcp.handlers import list_canvases_handler
113
132
 
114
133
  result = await list_canvases_handler(parent_dir=parent_dir)
134
+ result["_deprecated"] = "Use figrecipe instead"
115
135
  return _json(result)
116
136
 
117
137
  @mcp.tool()
118
138
  async def canvas_canvas_exists(parent_dir: str, canvas_name: str) -> str:
119
- """[canvas] Check if a canvas exists."""
139
+ """[canvas][DEPRECATED] Check if a canvas exists."""
120
140
  from scitex.canvas._mcp.handlers import canvas_exists_handler
121
141
 
122
142
  result = await canvas_exists_handler(
123
143
  parent_dir=parent_dir, canvas_name=canvas_name
124
144
  )
145
+ result["_deprecated"] = "Use figrecipe instead"
125
146
  return _json(result)
126
147
 
127
148
 
149
+ def _mount_figrecipe(mcp) -> None:
150
+ """Mount figrecipe MCP server for composition tools.
151
+
152
+ Provides: plt_plot, plt_compose, plt_reproduce, plt_info, etc.
153
+ """
154
+ try:
155
+ from figrecipe._mcp.server import mcp as figrecipe_mcp
156
+
157
+ mcp.mount(figrecipe_mcp, prefix="plt")
158
+ except ImportError:
159
+ pass # figrecipe not installed
160
+
161
+
128
162
  # EOF
scitex/canvas/__init__.py CHANGED
@@ -1,338 +1,220 @@
1
1
  #!/usr/bin/env python3
2
- # Timestamp: 2025-12-08
3
- # File: ./src/scitex/vis/__init__.py
2
+ # Timestamp: 2026-01-29
3
+ # File: src/scitex/canvas/__init__.py
4
4
  """
5
- SciTeX Visualization Module (scitex.canvas)
5
+ SciTeX Canvas Module (DEPRECATED)
6
6
 
7
- Canvas-based composition of publication-quality figures.
7
+ .. deprecated:: 2.16.0
8
+ This module is deprecated. Use figrecipe instead:
8
9
 
9
- Terminology:
10
- - Canvas: A paper figure workspace (e.g., "Figure 1" in publication)
11
- - Panel: A single component on canvas (stx.plt output or image)
12
- - Figure: Reserved for matplotlib's fig object (see scitex.plt)
10
+ - Interactive editor: ``figrecipe.edit()`` (browser GUI at port 5050)
11
+ - Multi-panel composition: ``figrecipe.compose()``
12
+ - Save/reproduce: ``figrecipe.save()`` / ``figrecipe.reproduce()``
13
13
 
14
- Quick Start:
15
- -----------
16
- >>> import scitex as stx
17
- >>>
18
- >>> # Create canvas and add panels
19
- >>> stx.vis.create_canvas("/output", "fig1")
20
- >>> stx.vis.add_panel("/output", "fig1", "panel_a", source="plot.png",
21
- ... xy_mm=(10, 10), size_mm=(80, 60), label="A")
22
- >>>
23
- >>> # Save with stx.io (auto-exports PNG/PDF/SVG)
24
- >>> canvas = stx.io.load("/output/fig1.canvas")
25
- >>> stx.io.save(canvas, "/output/fig1_copy.canvas")
14
+ Install: ``pip install figrecipe``
26
15
 
27
- Directory Structure:
28
- -------------------
29
- {parent_dir}/{canvas_name}.canvas/
30
- ├── canvas.json # Layout, panels, composition
31
- ├── panels/ # Panel directories
32
- └── exports/ # canvas.png, canvas.pdf, canvas.svg
33
- """
34
-
35
- # Submodules for advanced use
36
- from . import backend, editor, io, model, utils
16
+ Migration examples::
37
17
 
38
- # Canvas class
39
- from .canvas import Canvas
18
+ # Old (scitex.canvas)
19
+ from scitex.canvas import edit
20
+ edit(fig)
40
21
 
41
- # Editor
42
- from .editor import edit
22
+ # New (figrecipe)
23
+ import figrecipe as fr
24
+ fr.edit(fig)
43
25
 
44
- # Data integrity
45
- # Export (usually handled by stx.io.save, but available for explicit use)
46
- # =============================================================================
47
- # Primary API (minimal, reusable, flexible)
48
- # =============================================================================
49
- # Canvas operations
50
- # Panel operations
51
- from .io import add_panel_from_image, add_panel_from_scitex
52
- from .io import canvas_directory_exists as canvas_exists
53
- from .io import delete_canvas_directory as delete_canvas
54
- from .io import ensure_canvas_directory as create_canvas
55
- from .io import export_canvas_to_file as export_canvas
56
- from .io import get_canvas_directory_path as get_canvas_path
57
- from .io import list_canvas_directories as list_canvases
58
- from .io import list_panels, remove_panel, update_panel
59
- from .io import verify_all_data_hashes as verify_data
26
+ # Old (scitex.canvas multi-panel)
27
+ stx.canvas.create_canvas(...)
28
+ stx.canvas.add_panel(...)
60
29
 
30
+ # New (figrecipe.compose)
31
+ fig, axes = fr.compose(
32
+ sources={
33
+ "panel_a.png": {"xy_mm": (10, 10), "size_mm": (80, 60)},
34
+ "panel_b.png": {"xy_mm": (100, 10), "size_mm": (80, 60)},
35
+ },
36
+ canvas_size_mm=(190, 80),
37
+ panel_labels=True,
38
+ )
39
+ """
61
40
 
62
- # =============================================================================
63
- # Convenience wrapper for add_panel
64
- # =============================================================================
65
- def add_panel(
66
- parent_dir,
67
- canvas_name,
68
- panel_name,
69
- source,
70
- xy_mm=(0, 0),
71
- size_mm=(50, 50),
72
- label="",
73
- bundle=False,
74
- **kwargs,
75
- ):
76
- """
77
- Add a panel to canvas (auto-detects scitex vs image type).
41
+ from __future__ import annotations
78
42
 
79
- Parameters
80
- ----------
81
- parent_dir : str or Path
82
- Parent directory containing canvas
83
- canvas_name : str
84
- Canvas name
85
- panel_name : str
86
- Name for the panel
87
- source : str or Path
88
- Source file (PNG, JPG, SVG)
89
- xy_mm : tuple
90
- (x_mm, y_mm) position on canvas in millimeters
91
- size_mm : tuple
92
- (width_mm, height_mm) panel size in millimeters
93
- label : str
94
- Panel label (A, B, C...)
95
- bundle : bool
96
- If True, copy files. If False (default), use symlinks.
97
- **kwargs
98
- Additional panel properties (rotation_deg, opacity, flip_h, etc.)
99
- """
100
- from pathlib import Path
43
+ import warnings
101
44
 
102
- source = Path(source)
103
- panel_properties = {
104
- "position": {"x_mm": xy_mm[0], "y_mm": xy_mm[1]},
105
- "size": {"width_mm": size_mm[0], "height_mm": size_mm[1]},
106
- **kwargs,
107
- }
108
- if label:
109
- panel_properties["label"] = {"text": label, "position": "top-left"}
110
-
111
- # Check if scitex output (has .json/.csv siblings)
112
- json_sibling = source.parent / f"{source.stem}.json"
113
- if json_sibling.exists():
114
- return add_panel_from_scitex(
115
- project_dir=parent_dir,
116
- canvas_name=canvas_name,
117
- panel_name=panel_name,
118
- source_png=source,
119
- panel_properties=panel_properties,
120
- bundle=bundle,
121
- )
122
- else:
123
- return add_panel_from_image(
124
- project_dir=parent_dir,
125
- canvas_name=canvas_name,
126
- panel_name=panel_name,
127
- source_image=source,
128
- panel_properties=panel_properties,
129
- bundle=bundle,
130
- )
45
+ # Issue deprecation warning on import
46
+ warnings.warn(
47
+ "scitex.canvas is deprecated. Use figrecipe instead:\n"
48
+ " - fr.edit() for interactive GUI editor (port 5050)\n"
49
+ " - fr.compose() for multi-panel composition\n"
50
+ " - pip install figrecipe",
51
+ DeprecationWarning,
52
+ stacklevel=2,
53
+ )
131
54
 
132
55
 
133
56
  # =============================================================================
134
- # .figure Bundle Support
57
+ # Delegate to figrecipe (preferred)
135
58
  # =============================================================================
59
+ def edit(*args, **kwargs):
60
+ """Launch interactive GUI editor.
136
61
 
137
-
138
- def save_figure(
139
- panels,
140
- path,
141
- spec=None,
142
- as_zip=None,
143
- ):
144
- """
145
- Save panels as a .figure publication figure bundle.
146
-
147
- Parameters
148
- ----------
149
- panels : dict
150
- Dictionary mapping panel IDs to .plot bundle paths or data.
151
- Example: {"A": "timecourse.plot", "B": "barplot.plot"}
152
- path : str or Path
153
- Output path (e.g., "Figure1.figure.zip" or "Figure1.figure").
154
- - Path ending with ".zip" creates ZIP archive
155
- - Path ending with ".figure" creates directory bundle
156
- spec : dict, optional
157
- Figure specification. Auto-generated if None.
158
- as_zip : bool, optional
159
- If True, save as ZIP archive. If False, save as directory.
160
- Default: auto-detect from path.
161
-
162
- Returns
163
- -------
164
- Path
165
- Path to saved bundle.
166
-
167
- Examples
168
- --------
169
- >>> import scitex.canvas as sfig
170
- >>> panels = {
171
- ... "A": "timecourse.plot",
172
- ... "B": "barplot.plot"
173
- ... }
174
- >>> sfig.save_figure(panels, "Figure1.figure.zip") # Creates ZIP
175
- >>> sfig.save_figure(panels, "Figure1.figure") # Creates directory
176
- """
177
- import shutil
178
- from pathlib import Path
179
-
180
- from scitex.io.bundle import BundleType, save
181
-
182
- p = Path(path)
183
- spath = str(path)
184
-
185
- # Auto-detect as_zip from path suffix if not specified
186
- if as_zip is None:
187
- as_zip = spath.endswith(".zip")
188
-
189
- # Auto-generate spec if not provided
190
- if spec is None:
191
- spec = _generate_figure_spec(panels)
192
-
193
- # Build bundle data - pass source paths directly for file copying
194
- bundle_data = {
195
- "spec": spec,
196
- "plots": {},
197
- }
198
-
199
- # Pass source paths directly (not loaded data) to preserve all files
200
- for panel_id, plot_source in panels.items():
201
- plot_path = Path(plot_source)
202
- if plot_path.exists():
203
- # Store source path for direct copying
204
- bundle_data["plots"][panel_id] = str(plot_path)
205
-
206
- return save(bundle_data, p, bundle_type=BundleType.FIGURE, as_zip=as_zip)
207
-
208
-
209
- def load_figure(path):
62
+ .. deprecated:: 2.16.0
63
+ Use ``figrecipe.edit()`` instead.
210
64
  """
211
- Load a .figure bundle.
212
-
213
- Parameters
214
- ----------
215
- path : str or Path
216
- Path to .figure bundle (directory or ZIP).
217
-
218
- Returns
219
- -------
220
- dict
221
- Figure data with:
222
- - 'spec': Figure specification
223
- - 'panels': Dict mapping panel IDs to {'spec': ..., 'data': ...}
224
-
225
- Examples
226
- --------
227
- >>> figure = scitex.canvas.load_figure("Figure1.figure")
228
- >>> print(figure['spec']['figure']['title'])
229
- >>> panel_a = figure['panels']['A']
230
- >>> print(panel_a['spec'], panel_a['data'])
65
+ warnings.warn(
66
+ "scitex.canvas.edit() is deprecated. Use figrecipe.edit() instead.",
67
+ DeprecationWarning,
68
+ stacklevel=2,
69
+ )
70
+ try:
71
+ import figrecipe as fr
72
+
73
+ return fr.edit(*args, **kwargs)
74
+ except ImportError as e:
75
+ raise ImportError(
76
+ "figrecipe is required for the editor. Install with: pip install figrecipe"
77
+ ) from e
78
+
79
+
80
+ def compose(*args, **kwargs):
81
+ """Compose multi-panel figures.
82
+
83
+ .. deprecated:: 2.16.0
84
+ Use ``figrecipe.compose()`` instead.
231
85
  """
232
- from scitex.io.bundle import load
86
+ warnings.warn(
87
+ "scitex.canvas.compose() is deprecated. Use figrecipe.compose() instead.",
88
+ DeprecationWarning,
89
+ stacklevel=2,
90
+ )
91
+ try:
92
+ import figrecipe as fr
233
93
 
234
- bundle = load(path)
94
+ return fr.compose(*args, **kwargs)
95
+ except ImportError as e:
96
+ raise ImportError(
97
+ "figrecipe is required for composition. Install with: pip install figrecipe"
98
+ ) from e
235
99
 
236
- if bundle["type"] != "figure":
237
- raise ValueError(f"Not a .figure bundle: {path}")
238
100
 
239
- result = {
240
- "spec": bundle.get("spec", {}),
241
- "panels": {},
242
- }
243
-
244
- # Return spec and data for each panel (reconstruction is optional)
245
- for panel_id, plot_bundle in bundle.get("plots", {}).items():
246
- result["panels"][panel_id] = {
247
- "spec": plot_bundle.get("spec", {}),
248
- "data": plot_bundle.get("data"),
249
- }
250
-
251
- return result
252
-
253
-
254
- def _generate_figure_spec(panels):
255
- """Generate figure.json spec from panels."""
256
- from pathlib import Path
257
-
258
- spec = {
259
- "schema": {"name": "scitex.canvas.figure", "version": "1.0.0"},
260
- "figure": {
261
- "id": "figure",
262
- "title": "",
263
- "caption": "",
264
- "styles": {
265
- "size": {"width_mm": 180, "height_mm": 120},
266
- "background": "#ffffff",
267
- },
268
- },
269
- "panels": [],
101
+ # =============================================================================
102
+ # Legacy imports (deprecated, for backward compatibility)
103
+ # =============================================================================
104
+ def __getattr__(name):
105
+ """Lazy import legacy submodules with deprecation warnings."""
106
+ _legacy_submodules = {"backend", "editor", "io", "model", "utils"}
107
+ _legacy_functions = {
108
+ "Canvas",
109
+ "create_canvas",
110
+ "get_canvas_path",
111
+ "canvas_exists",
112
+ "list_canvases",
113
+ "delete_canvas",
114
+ "add_panel",
115
+ "update_panel",
116
+ "remove_panel",
117
+ "list_panels",
118
+ "export_canvas",
119
+ "verify_data",
120
+ "save_figure",
121
+ "load_figure",
122
+ "add_panel_from_image",
123
+ "add_panel_from_scitex",
270
124
  }
271
125
 
272
- # Auto-layout panels
273
- panel_ids = sorted(panels.keys())
274
- n_panels = len(panel_ids)
275
-
276
- if n_panels == 0:
277
- return spec
278
-
279
- # Simple grid layout
280
- cols = min(n_panels, 2)
281
- rows = (n_panels + cols - 1) // cols
282
-
283
- panel_w = 80
284
- panel_h = 50
285
- margin = 5
286
-
287
- for i, panel_id in enumerate(panel_ids):
288
- row = i // cols
289
- col = i % cols
126
+ if name in _legacy_submodules:
127
+ warnings.warn(
128
+ f"scitex.canvas.{name} is deprecated. Use figrecipe instead.",
129
+ DeprecationWarning,
130
+ stacklevel=2,
131
+ )
132
+ if name == "backend":
133
+ from . import backend as mod
134
+ elif name == "editor":
135
+ from . import editor as mod
136
+ elif name == "io":
137
+ from . import io as mod
138
+ elif name == "model":
139
+ from . import model as mod
140
+ elif name == "utils":
141
+ from . import utils as mod
142
+ return mod
143
+
144
+ if name in _legacy_functions:
145
+ warnings.warn(
146
+ f"scitex.canvas.{name} is deprecated. Use figrecipe.compose() instead.",
147
+ DeprecationWarning,
148
+ stacklevel=2,
149
+ )
150
+ # Import from legacy locations
151
+ if name == "Canvas":
152
+ from .canvas import Canvas
153
+
154
+ return Canvas
155
+ elif name in {
156
+ "create_canvas",
157
+ "get_canvas_path",
158
+ "canvas_exists",
159
+ "list_canvases",
160
+ "delete_canvas",
161
+ "add_panel_from_image",
162
+ "add_panel_from_scitex",
163
+ "list_panels",
164
+ "remove_panel",
165
+ "update_panel",
166
+ "export_canvas",
167
+ "verify_data",
168
+ }:
169
+ from . import io as _io
170
+
171
+ _name_map = {
172
+ "create_canvas": "ensure_canvas_directory",
173
+ "get_canvas_path": "get_canvas_directory_path",
174
+ "canvas_exists": "canvas_directory_exists",
175
+ "list_canvases": "list_canvas_directories",
176
+ "delete_canvas": "delete_canvas_directory",
177
+ "export_canvas": "export_canvas_to_file",
178
+ "verify_data": "verify_all_data_hashes",
179
+ }
180
+ actual_name = _name_map.get(name, name)
181
+ return getattr(_io, actual_name)
182
+ elif name == "add_panel":
183
+ from ._legacy import add_panel
290
184
 
291
- x = margin + col * (panel_w + margin)
292
- y = margin + row * (panel_h + margin)
185
+ return add_panel
186
+ elif name in {"save_figure", "load_figure"}:
187
+ from ._legacy import load_figure, save_figure
293
188
 
294
- # Note: save_bundle uses panel_id for the directory name (e.g., A.plot)
295
- spec["panels"].append(
296
- {
297
- "id": panel_id,
298
- "label": panel_id,
299
- "caption": "",
300
- "plot": f"{panel_id}.plot",
301
- "position": {"x_mm": x, "y_mm": y},
302
- "size": {"width_mm": panel_w, "height_mm": panel_h},
303
- }
304
- )
189
+ if name == "save_figure":
190
+ return save_figure
191
+ return load_figure
305
192
 
306
- return spec
193
+ raise AttributeError(f"module 'scitex.canvas' has no attribute '{name}'")
307
194
 
308
195
 
309
196
  __all__ = [
310
- # Canvas class
197
+ # Recommended (delegates to figrecipe)
198
+ "edit",
199
+ "compose",
200
+ # Legacy (deprecated)
311
201
  "Canvas",
312
- # Submodules (advanced)
313
202
  "io",
314
203
  "model",
315
204
  "backend",
316
205
  "utils",
317
206
  "editor",
318
- # Canvas operations
319
207
  "create_canvas",
320
208
  "get_canvas_path",
321
209
  "canvas_exists",
322
210
  "list_canvases",
323
211
  "delete_canvas",
324
- # Panel operations
325
212
  "add_panel",
326
213
  "update_panel",
327
214
  "remove_panel",
328
215
  "list_panels",
329
- # Export
330
216
  "export_canvas",
331
- # Data integrity
332
217
  "verify_data",
333
- # Editor
334
- "edit",
335
- # .figure bundle
336
218
  "save_figure",
337
219
  "load_figure",
338
220
  ]
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-29
3
+ # File: src/scitex/canvas/_legacy.py
4
+ """Legacy functions for backward compatibility.
5
+
6
+ All functions here are deprecated. Use figrecipe instead.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ def add_panel(
16
+ parent_dir,
17
+ canvas_name,
18
+ panel_name,
19
+ source,
20
+ xy_mm=(0, 0),
21
+ size_mm=(50, 50),
22
+ label="",
23
+ bundle=False,
24
+ **kwargs,
25
+ ):
26
+ """Add a panel to canvas (DEPRECATED).
27
+
28
+ Use figrecipe.compose() instead.
29
+ """
30
+ from .io import add_panel_from_image, add_panel_from_scitex
31
+
32
+ source = Path(source)
33
+ panel_properties = {
34
+ "position": {"x_mm": xy_mm[0], "y_mm": xy_mm[1]},
35
+ "size": {"width_mm": size_mm[0], "height_mm": size_mm[1]},
36
+ **kwargs,
37
+ }
38
+ if label:
39
+ panel_properties["label"] = {"text": label, "position": "top-left"}
40
+
41
+ json_sibling = source.parent / f"{source.stem}.json"
42
+ if json_sibling.exists():
43
+ return add_panel_from_scitex(
44
+ project_dir=parent_dir,
45
+ canvas_name=canvas_name,
46
+ panel_name=panel_name,
47
+ source_png=source,
48
+ panel_properties=panel_properties,
49
+ bundle=bundle,
50
+ )
51
+ else:
52
+ return add_panel_from_image(
53
+ project_dir=parent_dir,
54
+ canvas_name=canvas_name,
55
+ panel_name=panel_name,
56
+ source_image=source,
57
+ panel_properties=panel_properties,
58
+ bundle=bundle,
59
+ )
60
+
61
+
62
+ def save_figure(
63
+ panels,
64
+ path,
65
+ spec=None,
66
+ as_zip=None,
67
+ ):
68
+ """Save panels as a .figure bundle (DEPRECATED).
69
+
70
+ Use figrecipe.compose() and figrecipe.save() instead.
71
+ """
72
+ from scitex.io.bundle import BundleType, save
73
+
74
+ p = Path(path)
75
+ spath = str(path)
76
+
77
+ if as_zip is None:
78
+ as_zip = spath.endswith(".zip")
79
+
80
+ if spec is None:
81
+ spec = _generate_figure_spec(panels)
82
+
83
+ bundle_data = {
84
+ "spec": spec,
85
+ "plots": {},
86
+ }
87
+
88
+ for panel_id, plot_source in panels.items():
89
+ plot_path = Path(plot_source)
90
+ if plot_path.exists():
91
+ bundle_data["plots"][panel_id] = str(plot_path)
92
+
93
+ return save(bundle_data, p, bundle_type=BundleType.FIGURE, as_zip=as_zip)
94
+
95
+
96
+ def load_figure(path):
97
+ """Load a .figure bundle (DEPRECATED).
98
+
99
+ Use figrecipe.load() instead.
100
+ """
101
+ from scitex.io.bundle import load
102
+
103
+ bundle = load(path)
104
+
105
+ if bundle["type"] != "figure":
106
+ raise ValueError(f"Not a .figure bundle: {path}")
107
+
108
+ result = {
109
+ "spec": bundle.get("spec", {}),
110
+ "panels": {},
111
+ }
112
+
113
+ for panel_id, plot_bundle in bundle.get("plots", {}).items():
114
+ result["panels"][panel_id] = {
115
+ "spec": plot_bundle.get("spec", {}),
116
+ "data": plot_bundle.get("data"),
117
+ }
118
+
119
+ return result
120
+
121
+
122
+ def _generate_figure_spec(panels) -> dict[str, Any]:
123
+ """Generate figure.json spec from panels."""
124
+ spec: dict[str, Any] = {
125
+ "schema": {"name": "scitex.canvas.figure", "version": "1.0.0"},
126
+ "figure": {
127
+ "id": "figure",
128
+ "title": "",
129
+ "caption": "",
130
+ "styles": {
131
+ "size": {"width_mm": 180, "height_mm": 120},
132
+ "background": "#ffffff",
133
+ },
134
+ },
135
+ "panels": [],
136
+ }
137
+
138
+ panel_ids = sorted(panels.keys())
139
+ n_panels = len(panel_ids)
140
+
141
+ if n_panels == 0:
142
+ return spec
143
+
144
+ cols = min(n_panels, 2)
145
+
146
+ panel_w = 80
147
+ panel_h = 50
148
+ margin = 5
149
+
150
+ for i, panel_id in enumerate(panel_ids):
151
+ row = i // cols
152
+ col = i % cols
153
+
154
+ x = margin + col * (panel_w + margin)
155
+ y = margin + row * (panel_h + margin)
156
+
157
+ spec["panels"].append(
158
+ {
159
+ "id": panel_id,
160
+ "label": panel_id,
161
+ "caption": "",
162
+ "plot": f"{panel_id}.plot",
163
+ "position": {"x_mm": x, "y_mm": y},
164
+ "size": {"width_mm": panel_w, "height_mm": panel_h},
165
+ }
166
+ )
167
+
168
+ return spec
169
+
170
+
171
+ # EOF
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scitex
3
- Version: 2.15.4
3
+ Version: 2.15.5
4
4
  Summary: A comprehensive Python library for scientific computing and data analysis
5
5
  Project-URL: Homepage, https://github.com/ywatanabe1989/scitex-python
6
6
  Project-URL: Documentation, https://scitex.readthedocs.io
@@ -17,7 +17,7 @@ scitex/_mcp_resources/_scholar.py,sha256=RSYt7j6bcbKtR-tOtvCtdE6J3IS8C925_c34t0Z
17
17
  scitex/_mcp_resources/_session.py,sha256=lbe9W3cfPOyoq6bPOeN-jB8MwI4DnRWMpxHmpfn97SM,4590
18
18
  scitex/_mcp_tools/__init__.py,sha256=6krJPLr6ReVAkGoZR7vQHQrszr8AGW4dwWsY2EcmPkc,1229
19
19
  scitex/_mcp_tools/audio.py,sha256=LBX9YQZkNGAXfoIEFljnHRk39gTyJfdaVPCRrS6F8qE,6245
20
- scitex/_mcp_tools/canvas.py,sha256=FeRyJ0BKbqqsaWdsK34qABATeWzNe4Fc461VdUDEtKE,3868
20
+ scitex/_mcp_tools/canvas.py,sha256=zgu5velDxyTltn87DRN1EuGdJ1UZlcDbiEXGi5rZF7Q,5155
21
21
  scitex/_mcp_tools/capture.py,sha256=k7pwuB1vdP-VCYjutajEZEsvVAKY-8_mEYrYGWXgOo8,5658
22
22
  scitex/_mcp_tools/diagram.py,sha256=pZNq0UTRlg7piq0YDD40qY5M2mqFYoRdFwmWeRCBieA,585
23
23
  scitex/_mcp_tools/introspect.py,sha256=GY9j9EUAs-CKegzfPHubPT3OGpOjcY5Rb09M3OHEIwA,6562
@@ -241,7 +241,8 @@ scitex/browser/stealth/HumanBehavior.py,sha256=fTocrAO3-8rAgxW1aNZLFgErvHw2MUXao
241
241
  scitex/browser/stealth/StealthManager.py,sha256=mrtBAo-jo9nM7sSbzKC-s7VEci7SDK6GbidGNqmAPvs,37444
242
242
  scitex/browser/stealth/__init__.py,sha256=KVbyLjm8KnM5YwyB84A3pwLdUGkANNgwpA7YL2fyAxo,188
243
243
  scitex/canvas/README.md,sha256=jRwasTPNrmQsCyv67ul7qUIgv5xKIab1u2hi8tvlbAg,9142
244
- scitex/canvas/__init__.py,sha256=k_1mqbodRgy6phbhLDqRenEEnOLvueWwj0xNNYrCyd0,9380
244
+ scitex/canvas/__init__.py,sha256=_K3ocsEwAJMyGRw0ruCD07vjFMjrH1QQNqXujHJbOEw,6295
245
+ scitex/canvas/_legacy.py,sha256=g3pHBTuw9egpjt_CtE8I1YrWnkoauNNHsvDI0zR0xhQ,4060
245
246
  scitex/canvas/canvas.py,sha256=1aUjIqCzwKhGXukCQsHc-h-kqLNA9VqzIMazimqm2mA,12968
246
247
  scitex/canvas/mcp_server.py,sha256=fBTmtXOhO60wG-e6j-Y-WE_aWykAMsajWkidZRQ6lcU,4754
247
248
  scitex/canvas/_mcp/__init__.py,sha256=FJRAr90vUCg4PJ8Sv-WzsKuM7aY93m6UR87fhk843MU,73
@@ -2619,8 +2620,8 @@ scitex/writer/utils/_parse_latex_logs.py,sha256=K05kCJClG86-RovXG9I5e3wHP0mEGiZl
2619
2620
  scitex/writer/utils/_parse_script_args.py,sha256=Fkx1ze_jlKYIpviLuT7z0hpqE4cdtXXA_7sK3sKpVs4,4593
2620
2621
  scitex/writer/utils/_verify_tree_structure.py,sha256=Uk9NUSleR9aMHpAgUGsPZMk4a75l35EkZhQYKIfolVY,5724
2621
2622
  scitex/writer/utils/_watch.py,sha256=nYzPQ-iPAOzbwzAPBtSrrVBLLPMl08e0qxw0mtv0Y8I,2577
2622
- scitex-2.15.4.dist-info/METADATA,sha256=YhHg4lxfd6DXKl_tGIfOp6ZAXs6HkBzAo3K61emqz7U,25007
2623
- scitex-2.15.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2624
- scitex-2.15.4.dist-info/entry_points.txt,sha256=ZtDrHnPNMnsSmAnQoCHNmk0xKotNyR2X_YFDDteupW8,497
2625
- scitex-2.15.4.dist-info/licenses/LICENSE,sha256=TfPDBt3ar0uv_f9cqCDMZ5rIzW3CY8anRRd4PkL6ejs,34522
2626
- scitex-2.15.4.dist-info/RECORD,,
2623
+ scitex-2.15.5.dist-info/METADATA,sha256=wgkawnkDMSabszfw6tnGUOqFOa6kQYZco60gQB3du0w,25007
2624
+ scitex-2.15.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2625
+ scitex-2.15.5.dist-info/entry_points.txt,sha256=ZtDrHnPNMnsSmAnQoCHNmk0xKotNyR2X_YFDDteupW8,497
2626
+ scitex-2.15.5.dist-info/licenses/LICENSE,sha256=TfPDBt3ar0uv_f9cqCDMZ5rIzW3CY8anRRd4PkL6ejs,34522
2627
+ scitex-2.15.5.dist-info/RECORD,,