scitex 2.15.3__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/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