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.
- scitex/_mcp_tools/canvas.py +49 -15
- scitex/canvas/__init__.py +169 -287
- scitex/canvas/_legacy.py +171 -0
- {scitex-2.15.4.dist-info → scitex-2.15.5.dist-info}/METADATA +1 -1
- {scitex-2.15.4.dist-info → scitex-2.15.5.dist-info}/RECORD +8 -7
- {scitex-2.15.4.dist-info → scitex-2.15.5.dist-info}/WHEEL +0 -0
- {scitex-2.15.4.dist-info → scitex-2.15.5.dist-info}/entry_points.txt +0 -0
- {scitex-2.15.4.dist-info → scitex-2.15.5.dist-info}/licenses/LICENSE +0 -0
scitex/_mcp_tools/canvas.py
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# Timestamp: 2026-01-
|
|
3
|
-
# File:
|
|
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
|
|
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:
|
|
62
|
+
label: str | None = None,
|
|
48
63
|
) -> str:
|
|
49
|
-
"""[canvas] Add a panel to
|
|
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
|
|
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
|
|
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:
|
|
94
|
-
format:
|
|
111
|
+
output_path: str | None = None,
|
|
112
|
+
format: str | None = None,
|
|
95
113
|
dpi: int = 300,
|
|
96
114
|
) -> str:
|
|
97
|
-
"""[canvas] Export
|
|
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
|
|
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:
|
|
3
|
-
# File:
|
|
2
|
+
# Timestamp: 2026-01-29
|
|
3
|
+
# File: src/scitex/canvas/__init__.py
|
|
4
4
|
"""
|
|
5
|
-
SciTeX
|
|
5
|
+
SciTeX Canvas Module (DEPRECATED)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
.. deprecated:: 2.16.0
|
|
8
|
+
This module is deprecated. Use figrecipe instead:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
39
|
-
from .canvas import
|
|
18
|
+
# Old (scitex.canvas)
|
|
19
|
+
from scitex.canvas import edit
|
|
20
|
+
edit(fig)
|
|
40
21
|
|
|
41
|
-
#
|
|
42
|
-
|
|
22
|
+
# New (figrecipe)
|
|
23
|
+
import figrecipe as fr
|
|
24
|
+
fr.edit(fig)
|
|
43
25
|
|
|
44
|
-
#
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
#
|
|
57
|
+
# Delegate to figrecipe (preferred)
|
|
135
58
|
# =============================================================================
|
|
59
|
+
def edit(*args, **kwargs):
|
|
60
|
+
"""Launch interactive GUI editor.
|
|
136
61
|
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
"
|
|
260
|
-
"
|
|
261
|
-
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
292
|
-
|
|
185
|
+
return add_panel
|
|
186
|
+
elif name in {"save_figure", "load_figure"}:
|
|
187
|
+
from ._legacy import load_figure, save_figure
|
|
293
188
|
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
193
|
+
raise AttributeError(f"module 'scitex.canvas' has no attribute '{name}'")
|
|
307
194
|
|
|
308
195
|
|
|
309
196
|
__all__ = [
|
|
310
|
-
#
|
|
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
|
]
|
scitex/canvas/_legacy.py
ADDED
|
@@ -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.
|
|
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=
|
|
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=
|
|
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.
|
|
2623
|
-
scitex-2.15.
|
|
2624
|
-
scitex-2.15.
|
|
2625
|
-
scitex-2.15.
|
|
2626
|
-
scitex-2.15.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|