scitex 2.4.2__py3-none-any.whl → 2.5.0__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/__version__.py +1 -1
- scitex/browser/__init__.py +53 -0
- scitex/browser/debugging/__init__.py +56 -0
- scitex/browser/debugging/_failure_capture.py +372 -0
- scitex/browser/debugging/_sync_session.py +259 -0
- scitex/browser/debugging/_test_monitor.py +284 -0
- scitex/browser/debugging/_visual_cursor.py +432 -0
- scitex/io/_load.py +5 -0
- scitex/io/_load_modules/_canvas.py +171 -0
- scitex/io/_save.py +8 -0
- scitex/io/_save_modules/_canvas.py +356 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +77 -22
- scitex/plt/docs/FIGURE_ARCHITECTURE.md +257 -0
- scitex/plt/utils/__init__.py +10 -0
- scitex/plt/utils/_collect_figure_metadata.py +14 -12
- scitex/plt/utils/_csv_column_naming.py +237 -0
- scitex/scholar/citation_graph/database.py +9 -2
- scitex/scholar/config/ScholarConfig.py +23 -3
- scitex/scholar/config/default.yaml +55 -0
- scitex/scholar/core/Paper.py +102 -0
- scitex/scholar/core/__init__.py +44 -0
- scitex/scholar/core/journal_normalizer.py +524 -0
- scitex/scholar/core/oa_cache.py +285 -0
- scitex/scholar/core/open_access.py +457 -0
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +137 -0
- scitex/scholar/pdf_download/strategies/__init__.py +6 -0
- scitex/scholar/pdf_download/strategies/open_access_download.py +186 -0
- scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +18 -3
- scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +15 -2
- scitex/session/_decorator.py +13 -1
- scitex/vis/README.md +246 -615
- scitex/vis/__init__.py +138 -78
- scitex/vis/canvas.py +423 -0
- scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
- scitex/vis/editor/__init__.py +1 -1
- scitex/vis/editor/_dearpygui_editor.py +1830 -0
- scitex/vis/editor/_defaults.py +40 -1
- scitex/vis/editor/_edit.py +54 -18
- scitex/vis/editor/_flask_editor.py +37 -0
- scitex/vis/editor/_qt_editor.py +865 -0
- scitex/vis/editor/flask_editor/__init__.py +21 -0
- scitex/vis/editor/flask_editor/bbox.py +216 -0
- scitex/vis/editor/flask_editor/core.py +152 -0
- scitex/vis/editor/flask_editor/plotter.py +130 -0
- scitex/vis/editor/flask_editor/renderer.py +184 -0
- scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
- scitex/vis/editor/flask_editor/templates/html.py +295 -0
- scitex/vis/editor/flask_editor/templates/scripts.py +614 -0
- scitex/vis/editor/flask_editor/templates/styles.py +549 -0
- scitex/vis/editor/flask_editor/utils.py +81 -0
- scitex/vis/io/__init__.py +84 -21
- scitex/vis/io/canvas.py +226 -0
- scitex/vis/io/data.py +204 -0
- scitex/vis/io/directory.py +202 -0
- scitex/vis/io/export.py +460 -0
- scitex/vis/io/panel.py +424 -0
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/METADATA +9 -2
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/RECORD +61 -32
- scitex/vis/DJANGO_INTEGRATION.md +0 -677
- scitex/vis/editor/_web_editor.py +0 -1440
- scitex/vis/tmp.txt +0 -239
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/WHEEL +0 -0
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/licenses/LICENSE +0 -0
scitex/vis/README.md
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
<!-- ---
|
|
2
|
-
!-- Timestamp: 2025-
|
|
2
|
+
!-- Timestamp: 2025-12-08
|
|
3
3
|
!-- Author: ywatanabe
|
|
4
4
|
!-- File: /home/ywatanabe/proj/scitex-code/src/scitex/vis/README.md
|
|
5
5
|
!-- --- -->
|
|
6
6
|
|
|
7
|
-
# scitex.vis -
|
|
7
|
+
# scitex.vis - Canvas-Based Figure Composition
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**Compose publication-quality figures from multiple panels**
|
|
10
|
+
|
|
11
|
+
Schema Version: 2.0.0
|
|
10
12
|
|
|
11
13
|
---
|
|
12
14
|
|
|
13
15
|
## Overview
|
|
14
16
|
|
|
15
|
-
`scitex.vis`
|
|
17
|
+
`scitex.vis` provides canvas-based composition of publication figures. A **canvas** represents a complete paper figure (e.g., "Figure 1") that can contain multiple **panels** (A, B, C...).
|
|
18
|
+
|
|
19
|
+
### Terminology
|
|
20
|
+
|
|
21
|
+
| Term | Meaning | Example |
|
|
22
|
+
|------|---------|---------|
|
|
23
|
+
| **Canvas** | Paper figure workspace | "Figure 1" in a publication |
|
|
24
|
+
| **Panel** | Single component on canvas | Panel A, B, C... |
|
|
25
|
+
| **Figure** | Reserved for matplotlib's `fig` object | `stx.plt` output |
|
|
16
26
|
|
|
17
27
|
### The SciTeX Ecosystem
|
|
18
28
|
|
|
@@ -20,736 +30,357 @@
|
|
|
20
30
|
scitex/
|
|
21
31
|
├── scholar/ → Literature & metadata management
|
|
22
32
|
├── writer/ → Document generation (LaTeX/manuscripts)
|
|
23
|
-
|
|
33
|
+
├── plt/ → Plotting (matplotlib wrapper, outputs PNG+JSON+CSV)
|
|
34
|
+
└── vis/ → Canvas composition (this module)
|
|
24
35
|
```
|
|
25
36
|
|
|
26
|
-
### Why scitex.vis?
|
|
27
|
-
|
|
28
|
-
Traditional plotting workflows have several pain points:
|
|
29
|
-
- **Not version-controllable**: Figures are binary blobs
|
|
30
|
-
- **Not reproducible**: Code scattered across notebooks, hard to recreate exact output
|
|
31
|
-
- **Not collaborative**: Hard to edit someone else's plotting code
|
|
32
|
-
- **Not structured**: No standard way to represent figure specifications
|
|
33
|
-
|
|
34
|
-
`scitex.vis` solves these by:
|
|
35
|
-
- ✅ **JSON-based**: Figures as structured, version-controllable text
|
|
36
|
-
- ✅ **Reproducible**: One JSON → One figure, guaranteed
|
|
37
|
-
- ✅ **Collaborative**: Easy to edit, review, and share specifications
|
|
38
|
-
- ✅ **Structured**: Standard schema for all figure types
|
|
39
|
-
- ✅ **Publication-ready**: Built-in templates for Nature, Science, etc.
|
|
40
|
-
|
|
41
37
|
---
|
|
42
38
|
|
|
43
39
|
## Quick Start
|
|
44
40
|
|
|
45
|
-
###
|
|
41
|
+
### Create and Compose a Canvas
|
|
46
42
|
|
|
47
43
|
```python
|
|
48
44
|
import scitex as stx
|
|
49
|
-
import numpy as np
|
|
50
|
-
|
|
51
|
-
# 1. Create figure using publication template
|
|
52
|
-
fig_json = stx.vis.get_template("nature_single", height_mm=100)
|
|
53
|
-
|
|
54
|
-
# 2. Define your plot
|
|
55
|
-
x = np.linspace(0, 2*np.pi, 100)
|
|
56
|
-
y = np.sin(x)
|
|
57
|
-
|
|
58
|
-
fig_json["axes"] = [{
|
|
59
|
-
"row": 0,
|
|
60
|
-
"col": 0,
|
|
61
|
-
"xlabel": "Time (s)",
|
|
62
|
-
"ylabel": "Amplitude",
|
|
63
|
-
"title": "Sine Wave",
|
|
64
|
-
"plots": [{
|
|
65
|
-
"plot_type": "line",
|
|
66
|
-
"data": {"x": x.tolist(), "y": y.tolist()},
|
|
67
|
-
"color": "blue",
|
|
68
|
-
"linewidth": 2,
|
|
69
|
-
"label": "sin(x)"
|
|
70
|
-
}],
|
|
71
|
-
"grid": True,
|
|
72
|
-
"legend": True
|
|
73
|
-
}]
|
|
74
|
-
|
|
75
|
-
# 3. Save the specification
|
|
76
|
-
stx.vis.save_figure_json(fig_json, "figure.json")
|
|
77
|
-
|
|
78
|
-
# 4. Render to matplotlib
|
|
79
|
-
fig, axes = stx.vis.build_figure_from_json(fig_json)
|
|
80
|
-
|
|
81
|
-
# 5. Export to image
|
|
82
|
-
stx.vis.export_figure(fig_json, "figure.png", dpi=300)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Project-Based Workflow
|
|
86
45
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
stx.vis.save_figure_json_to_project(
|
|
46
|
+
# 1. Create a canvas
|
|
47
|
+
stx.vis.ensure_canvas_directory(
|
|
90
48
|
project_dir="/path/to/project",
|
|
91
|
-
|
|
92
|
-
fig_json=fig_json
|
|
49
|
+
canvas_name="fig1_results"
|
|
93
50
|
)
|
|
94
51
|
|
|
95
|
-
#
|
|
96
|
-
|
|
52
|
+
# 2. Add panels from stx.plt outputs
|
|
53
|
+
stx.vis.add_panel_from_scitex(
|
|
97
54
|
project_dir="/path/to/project",
|
|
98
|
-
|
|
55
|
+
canvas_name="fig1_results",
|
|
56
|
+
panel_name="panel_a",
|
|
57
|
+
source_png="./output/timeseries.png",
|
|
58
|
+
panel_properties={
|
|
59
|
+
"position": {"x_mm": 10, "y_mm": 10},
|
|
60
|
+
"size": {"width_mm": 80, "height_mm": 60},
|
|
61
|
+
"label": {"text": "A"}
|
|
62
|
+
}
|
|
99
63
|
)
|
|
100
64
|
|
|
101
|
-
#
|
|
102
|
-
stx.vis.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
65
|
+
# 3. Add an image panel
|
|
66
|
+
stx.vis.add_panel_from_image(
|
|
67
|
+
project_dir="/path/to/project",
|
|
68
|
+
canvas_name="fig1_results",
|
|
69
|
+
panel_name="panel_b",
|
|
70
|
+
source_image="./external/diagram.png",
|
|
71
|
+
panel_properties={
|
|
72
|
+
"position": {"x_mm": 100, "y_mm": 10},
|
|
73
|
+
"size": {"width_mm": 70, "height_mm": 60},
|
|
74
|
+
"label": {"text": "B"}
|
|
75
|
+
}
|
|
76
|
+
)
|
|
108
77
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
│ ├── GuideModel # Reference lines, spans
|
|
116
|
-
│ └── AnnotationModel # Text, arrows, shapes
|
|
117
|
-
│
|
|
118
|
-
├── backend/ # Rendering engine
|
|
119
|
-
│ ├── parser.py # JSON → Python objects
|
|
120
|
-
│ ├── render.py # Objects → matplotlib figures
|
|
121
|
-
│ └── export.py # Export to PNG/PDF/SVG
|
|
122
|
-
│
|
|
123
|
-
├── io/ # Load/save operations
|
|
124
|
-
│ ├── load.py # Load figure JSONs
|
|
125
|
-
│ └── save.py # Save figure JSONs
|
|
126
|
-
│
|
|
127
|
-
└── utils/ # Utilities
|
|
128
|
-
├── validate.py # JSON validation
|
|
129
|
-
└── defaults.py # Publication templates
|
|
78
|
+
# 4. Export composed canvas
|
|
79
|
+
stx.vis.export_canvas_to_file(
|
|
80
|
+
project_dir="/path/to/project",
|
|
81
|
+
canvas_name="fig1_results",
|
|
82
|
+
output_format="png"
|
|
83
|
+
)
|
|
130
84
|
```
|
|
131
85
|
|
|
132
86
|
---
|
|
133
87
|
|
|
134
|
-
##
|
|
135
|
-
|
|
136
|
-
### Available Templates
|
|
137
|
-
|
|
138
|
-
```python
|
|
139
|
-
# List all templates
|
|
140
|
-
templates = stx.vis.list_templates()
|
|
141
|
-
# ['nature_single', 'nature_double', 'science_single', 'a4', 'square', 'presentation']
|
|
142
|
-
|
|
143
|
-
# Get template dimensions
|
|
144
|
-
for name in templates:
|
|
145
|
-
template = stx.vis.get_template(name)
|
|
146
|
-
print(f"{name}: {template['width_mm']} × {template['height_mm']} mm")
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Template Dimensions
|
|
150
|
-
|
|
151
|
-
| Template | Width (mm) | Use Case |
|
|
152
|
-
|----------|------------|----------|
|
|
153
|
-
| `nature_single` | 89 | Nature single column |
|
|
154
|
-
| `nature_double` | 183 | Nature double column |
|
|
155
|
-
| `science_single` | 84 | Science single column |
|
|
156
|
-
| `a4` | 180 | A4 document figures |
|
|
157
|
-
| `square` | 120 | Square aspect ratio |
|
|
158
|
-
| `presentation` | 254 | Presentation slides (16:9) |
|
|
159
|
-
|
|
160
|
-
### Creating Custom Templates
|
|
161
|
-
|
|
162
|
-
```python
|
|
163
|
-
# Start with a template
|
|
164
|
-
fig_json = stx.vis.get_template("nature_single")
|
|
88
|
+
## Directory Structure
|
|
165
89
|
|
|
166
|
-
|
|
167
|
-
fig_json["height_mm"] = 120
|
|
168
|
-
fig_json["nrows"] = 2
|
|
169
|
-
fig_json["ncols"] = 1
|
|
90
|
+
Canvas directories use `.canvas` extension for portability and distinguishability:
|
|
170
91
|
|
|
171
|
-
# Or create from scratch
|
|
172
|
-
fig_json = {
|
|
173
|
-
"width_mm": 180,
|
|
174
|
-
"height_mm": 120,
|
|
175
|
-
"nrows": 1,
|
|
176
|
-
"ncols": 1,
|
|
177
|
-
"dpi": 300,
|
|
178
|
-
"axes": []
|
|
179
|
-
}
|
|
180
92
|
```
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
{
|
|
196
|
-
"row": 0,
|
|
197
|
-
"col": 0,
|
|
198
|
-
"xlabel": "X",
|
|
199
|
-
"ylabel": "Y",
|
|
200
|
-
"plots": [
|
|
201
|
-
{
|
|
202
|
-
"plot_type": "line",
|
|
203
|
-
"data": {"x": [0, 1, 2], "y": [0, 1, 4]},
|
|
204
|
-
"color": "blue"
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
}
|
|
208
|
-
]
|
|
209
|
-
}
|
|
93
|
+
project/scitex/vis/canvases/
|
|
94
|
+
└── fig1_results.canvas/ # .canvas extension for bundle
|
|
95
|
+
├── canvas.json # Layout, panels, composition
|
|
96
|
+
├── panels/
|
|
97
|
+
│ ├── panel_a/ # type: scitex (full stx.plt output)
|
|
98
|
+
│ │ ├── panel.json
|
|
99
|
+
│ │ ├── panel.csv
|
|
100
|
+
│ │ └── panel.png
|
|
101
|
+
│ └── panel_b/ # type: image (static)
|
|
102
|
+
│ └── panel.png
|
|
103
|
+
└── exports/
|
|
104
|
+
├── canvas.png # Final composed output
|
|
105
|
+
├── canvas.pdf
|
|
106
|
+
└── canvas.svg
|
|
210
107
|
```
|
|
211
108
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
```json
|
|
215
|
-
{
|
|
216
|
-
"width_mm": 183,
|
|
217
|
-
"height_mm": 120,
|
|
218
|
-
"nrows": 1,
|
|
219
|
-
"ncols": 2,
|
|
220
|
-
"dpi": 300,
|
|
221
|
-
"facecolor": "white",
|
|
222
|
-
"suptitle": "Figure 1: Example Results",
|
|
223
|
-
"suptitle_fontsize": 12,
|
|
224
|
-
"axes": [
|
|
225
|
-
{
|
|
226
|
-
"row": 0,
|
|
227
|
-
"col": 0,
|
|
228
|
-
"xlabel": "Time (s)",
|
|
229
|
-
"ylabel": "Amplitude",
|
|
230
|
-
"title": "Experimental Data",
|
|
231
|
-
"xlim": [0, 10],
|
|
232
|
-
"ylim": [-1, 1],
|
|
233
|
-
"grid": true,
|
|
234
|
-
"legend": true,
|
|
235
|
-
"plots": [
|
|
236
|
-
{
|
|
237
|
-
"plot_type": "line",
|
|
238
|
-
"data": {"x": [...], "y": [...]},
|
|
239
|
-
"color": "blue",
|
|
240
|
-
"linewidth": 2,
|
|
241
|
-
"label": "Measurement"
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
"plot_type": "scatter",
|
|
245
|
-
"data": {"x": [...], "y": [...]},
|
|
246
|
-
"color": "red",
|
|
247
|
-
"alpha": 0.6,
|
|
248
|
-
"label": "Control"
|
|
249
|
-
}
|
|
250
|
-
],
|
|
251
|
-
"guides": [
|
|
252
|
-
{
|
|
253
|
-
"guide_type": "axhline",
|
|
254
|
-
"y": 0,
|
|
255
|
-
"color": "gray",
|
|
256
|
-
"linestyle": "--"
|
|
257
|
-
}
|
|
258
|
-
],
|
|
259
|
-
"annotations": [
|
|
260
|
-
{
|
|
261
|
-
"annotation_type": "text",
|
|
262
|
-
"text": "Peak",
|
|
263
|
-
"x": 5,
|
|
264
|
-
"y": 0.8,
|
|
265
|
-
"fontsize": 10
|
|
266
|
-
}
|
|
267
|
-
]
|
|
268
|
-
}
|
|
269
|
-
]
|
|
270
|
-
}
|
|
271
|
-
```
|
|
109
|
+
The `.canvas` extension makes directories self-documenting, portable, and detectable by `scitex.io`.
|
|
272
110
|
|
|
273
111
|
---
|
|
274
112
|
|
|
275
|
-
##
|
|
276
|
-
|
|
277
|
-
### Supported Plot Types
|
|
278
|
-
|
|
279
|
-
| Plot Type | Description | Required Data Fields |
|
|
280
|
-
|-----------|-------------|---------------------|
|
|
281
|
-
| `line` | Line plot | `x`, `y` |
|
|
282
|
-
| `scatter` | Scatter plot | `x`, `y` |
|
|
283
|
-
| `errorbar` | Error bars | `x`, `y`, optional `xerr`, `yerr` |
|
|
284
|
-
| `bar` | Bar chart | `x`, `height` (or `y`) |
|
|
285
|
-
| `barh` | Horizontal bar chart | `y`, `width` (or `x`) |
|
|
286
|
-
| `hist` | Histogram | `x`, optional `bins` |
|
|
287
|
-
| `fill_between` | Filled area | `x`, `y1`, `y2` |
|
|
288
|
-
| `heatmap` | Heatmap | `z` (or `img`) |
|
|
289
|
-
| `imshow` | Image display | `img` (or `z`) |
|
|
290
|
-
| `contour` | Contour lines | `x`, `y`, `z` |
|
|
291
|
-
| `contourf` | Filled contours | `x`, `y`, `z` |
|
|
113
|
+
## Panel Types
|
|
292
114
|
|
|
293
|
-
|
|
115
|
+
| Type | Contents | Editable | Re-renderable |
|
|
116
|
+
|------|----------|----------|---------------|
|
|
117
|
+
| `scitex` | PNG + JSON + CSV | Full (data, style) | Yes |
|
|
118
|
+
| `image` | PNG/JPG/SVG only | Position/size/transform | No |
|
|
294
119
|
|
|
295
|
-
|
|
120
|
+
### Panel Properties
|
|
296
121
|
|
|
297
122
|
```python
|
|
298
|
-
{
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
123
|
+
panel_properties = {
|
|
124
|
+
# Position and size (required)
|
|
125
|
+
"position": {"x_mm": 10, "y_mm": 10},
|
|
126
|
+
"size": {"width_mm": 70, "height_mm": 50},
|
|
127
|
+
|
|
128
|
+
# Transform
|
|
129
|
+
"z_index": 0, # Stacking order
|
|
130
|
+
"rotation_deg": 0, # Rotation (clockwise)
|
|
131
|
+
"opacity": 1.0, # 0.0 - 1.0
|
|
132
|
+
"flip_h": False, # Horizontal flip
|
|
133
|
+
"flip_v": False, # Vertical flip
|
|
134
|
+
"visible": True,
|
|
135
|
+
|
|
136
|
+
# Clip (crop)
|
|
137
|
+
"clip": {
|
|
138
|
+
"enabled": False,
|
|
139
|
+
"x_mm": 0, "y_mm": 0,
|
|
140
|
+
"width_mm": None, "height_mm": None
|
|
141
|
+
},
|
|
308
142
|
|
|
309
|
-
|
|
143
|
+
# Label (A, B, C...)
|
|
144
|
+
"label": {
|
|
145
|
+
"text": "A",
|
|
146
|
+
"position": "top-left",
|
|
147
|
+
"fontsize": 12,
|
|
148
|
+
"fontweight": "bold"
|
|
149
|
+
},
|
|
310
150
|
|
|
311
|
-
|
|
312
|
-
{
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
"markersize": 50,
|
|
318
|
-
"label": "Data points"
|
|
151
|
+
# Border
|
|
152
|
+
"border": {
|
|
153
|
+
"visible": False,
|
|
154
|
+
"color": "#000000",
|
|
155
|
+
"width_mm": 0.2
|
|
156
|
+
}
|
|
319
157
|
}
|
|
320
158
|
```
|
|
321
159
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
```python
|
|
325
|
-
{
|
|
326
|
-
"plot_type": "errorbar",
|
|
327
|
-
"data": {"x": [...], "y": [...]},
|
|
328
|
-
"yerr": [...], # Can be scalar or array
|
|
329
|
-
"capsize": 5,
|
|
330
|
-
"color": "green",
|
|
331
|
-
"label": "Mean ± SD"
|
|
332
|
-
}
|
|
333
|
-
```
|
|
160
|
+
---
|
|
334
161
|
|
|
335
|
-
|
|
162
|
+
## canvas.json Schema
|
|
336
163
|
|
|
337
|
-
```
|
|
164
|
+
```json
|
|
338
165
|
{
|
|
339
|
-
"
|
|
340
|
-
"
|
|
341
|
-
"cmap": "viridis",
|
|
342
|
-
"vmin": 0,
|
|
343
|
-
"vmax": 1
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
---
|
|
166
|
+
"schema_version": "2.0.0",
|
|
167
|
+
"canvas_name": "fig1_results",
|
|
348
168
|
|
|
349
|
-
|
|
169
|
+
"size": {
|
|
170
|
+
"width_mm": 180,
|
|
171
|
+
"height_mm": 240
|
|
172
|
+
},
|
|
350
173
|
|
|
351
|
-
|
|
174
|
+
"background": {
|
|
175
|
+
"color": "#ffffff",
|
|
176
|
+
"grid": false
|
|
177
|
+
},
|
|
352
178
|
|
|
353
|
-
|
|
354
|
-
# 2×2 subplot layout
|
|
355
|
-
fig_json = stx.vis.get_template("nature_double", height_mm=160)
|
|
356
|
-
fig_json["nrows"] = 2
|
|
357
|
-
fig_json["ncols"] = 2
|
|
358
|
-
|
|
359
|
-
# Define each subplot
|
|
360
|
-
fig_json["axes"] = [
|
|
361
|
-
# Top-left (row=0, col=0)
|
|
362
|
-
{
|
|
363
|
-
"row": 0,
|
|
364
|
-
"col": 0,
|
|
365
|
-
"title": "Plot A",
|
|
366
|
-
"plots": [...]
|
|
367
|
-
},
|
|
368
|
-
# Top-right (row=0, col=1)
|
|
179
|
+
"panels": [
|
|
369
180
|
{
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
181
|
+
"name": "panel_a",
|
|
182
|
+
"type": "scitex",
|
|
183
|
+
"position": {"x_mm": 10, "y_mm": 10},
|
|
184
|
+
"size": {"width_mm": 80, "height_mm": 60},
|
|
185
|
+
"z_index": 0,
|
|
186
|
+
"label": {"text": "A", "position": "top-left"}
|
|
374
187
|
},
|
|
375
|
-
# Bottom-left (row=1, col=0)
|
|
376
188
|
{
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
{
|
|
384
|
-
"row": 1,
|
|
385
|
-
"col": 1,
|
|
386
|
-
"title": "Plot D",
|
|
387
|
-
"plots": [...]
|
|
189
|
+
"name": "panel_b",
|
|
190
|
+
"type": "image",
|
|
191
|
+
"source": "panel.png",
|
|
192
|
+
"position": {"x_mm": 100, "y_mm": 10},
|
|
193
|
+
"size": {"width_mm": 70, "height_mm": 60},
|
|
194
|
+
"label": {"text": "B"}
|
|
388
195
|
}
|
|
389
|
-
]
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
---
|
|
196
|
+
],
|
|
393
197
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
```python
|
|
399
|
-
# Horizontal line
|
|
400
|
-
{
|
|
401
|
-
"guide_type": "axhline",
|
|
402
|
-
"y": 0,
|
|
403
|
-
"color": "gray",
|
|
404
|
-
"linestyle": "--",
|
|
405
|
-
"linewidth": 1
|
|
406
|
-
}
|
|
198
|
+
"annotations": [
|
|
199
|
+
{"type": "text", "content": "p < 0.05", "position": {"x_mm": 50, "y_mm": 80}}
|
|
200
|
+
],
|
|
407
201
|
|
|
408
|
-
|
|
409
|
-
{
|
|
410
|
-
|
|
411
|
-
"x": 5,
|
|
412
|
-
"color": "red",
|
|
413
|
-
"alpha": 0.5
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
# Horizontal span (shaded region)
|
|
417
|
-
{
|
|
418
|
-
"guide_type": "axhspan",
|
|
419
|
-
"ymin": -0.5,
|
|
420
|
-
"ymax": 0.5,
|
|
421
|
-
"color": "yellow",
|
|
422
|
-
"alpha": 0.3
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
# Vertical span
|
|
426
|
-
{
|
|
427
|
-
"guide_type": "axvspan",
|
|
428
|
-
"xmin": 2,
|
|
429
|
-
"xmax": 4,
|
|
430
|
-
"color": "blue",
|
|
431
|
-
"alpha": 0.2
|
|
432
|
-
}
|
|
433
|
-
```
|
|
202
|
+
"data_files": [
|
|
203
|
+
{"path": "panels/panel_a/panel.csv", "hash": "sha256:abc123..."}
|
|
204
|
+
],
|
|
434
205
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
# Simple text
|
|
439
|
-
{
|
|
440
|
-
"annotation_type": "text",
|
|
441
|
-
"text": "Important point",
|
|
442
|
-
"x": 5,
|
|
443
|
-
"y": 10,
|
|
444
|
-
"fontsize": 12,
|
|
445
|
-
"color": "red",
|
|
446
|
-
"ha": "center", # horizontal alignment
|
|
447
|
-
"va": "bottom" # vertical alignment
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
# Annotate with arrow
|
|
451
|
-
{
|
|
452
|
-
"annotation_type": "annotate",
|
|
453
|
-
"text": "Peak value",
|
|
454
|
-
"x": 5, # Point to annotate
|
|
455
|
-
"y": 10,
|
|
456
|
-
"xytext": [6, 12], # Text position
|
|
457
|
-
"arrowprops": {
|
|
458
|
-
"arrowstyle": "->",
|
|
459
|
-
"color": "black",
|
|
460
|
-
"lw": 1.5
|
|
206
|
+
"metadata": {
|
|
207
|
+
"created_at": "2025-12-08T12:00:00Z",
|
|
208
|
+
"updated_at": "2025-12-08T15:30:00Z"
|
|
461
209
|
}
|
|
462
210
|
}
|
|
463
211
|
```
|
|
464
212
|
|
|
465
213
|
---
|
|
466
214
|
|
|
467
|
-
##
|
|
215
|
+
## API Reference
|
|
468
216
|
|
|
469
|
-
###
|
|
217
|
+
### Directory Operations
|
|
470
218
|
|
|
471
219
|
```python
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
# Create models programmatically
|
|
475
|
-
fig_model = FigureModel(
|
|
476
|
-
width_mm=stx.vis.NATURE_SINGLE_COLUMN_MM,
|
|
477
|
-
height_mm=100,
|
|
478
|
-
nrows=1,
|
|
479
|
-
ncols=1
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
axes_model = AxesModel(
|
|
483
|
-
row=0,
|
|
484
|
-
col=0,
|
|
485
|
-
xlabel="Time",
|
|
486
|
-
ylabel="Signal"
|
|
487
|
-
)
|
|
220
|
+
# Create canvas directory structure
|
|
221
|
+
canvas_dir = stx.vis.ensure_canvas_directory(project_dir, canvas_name)
|
|
488
222
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
data={"x": [0, 1, 2], "y": [0, 1, 4]},
|
|
492
|
-
color="blue"
|
|
493
|
-
)
|
|
223
|
+
# Get canvas path
|
|
224
|
+
path = stx.vis.get_canvas_directory_path(project_dir, canvas_name)
|
|
494
225
|
|
|
495
|
-
#
|
|
496
|
-
|
|
497
|
-
fig_model.add_axes(axes_model.to_dict())
|
|
226
|
+
# List all canvases
|
|
227
|
+
canvases = stx.vis.list_canvas_directories(project_dir)
|
|
498
228
|
|
|
499
|
-
#
|
|
500
|
-
|
|
229
|
+
# Check existence
|
|
230
|
+
exists = stx.vis.canvas_directory_exists(project_dir, canvas_name)
|
|
501
231
|
|
|
502
|
-
#
|
|
503
|
-
|
|
504
|
-
fig, axes = stx.vis.build_figure_from_json(fig_json)
|
|
232
|
+
# Delete canvas
|
|
233
|
+
deleted = stx.vis.delete_canvas_directory(project_dir, canvas_name)
|
|
505
234
|
```
|
|
506
235
|
|
|
507
|
-
###
|
|
236
|
+
### Canvas Operations
|
|
508
237
|
|
|
509
238
|
```python
|
|
510
|
-
|
|
239
|
+
# Save canvas.json
|
|
240
|
+
stx.vis.save_canvas_json(project_dir, canvas_name, canvas_json)
|
|
511
241
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
print("✓ Valid figure JSON")
|
|
515
|
-
except ValueError as e:
|
|
516
|
-
print(f"✗ Invalid: {e}")
|
|
517
|
-
```
|
|
242
|
+
# Load canvas.json (with hash verification)
|
|
243
|
+
canvas_json = stx.vis.load_canvas_json(project_dir, canvas_name)
|
|
518
244
|
|
|
519
|
-
|
|
245
|
+
# Partial update
|
|
246
|
+
stx.vis.update_canvas_json(project_dir, canvas_name, {"size": {"width_mm": 200}})
|
|
520
247
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
paths = stx.vis.backend.export_multiple_formats(
|
|
524
|
-
fig_json=fig_json,
|
|
525
|
-
output_dir="output",
|
|
526
|
-
base_name="figure-01",
|
|
527
|
-
formats=["png", "pdf", "svg"],
|
|
528
|
-
dpi=300,
|
|
529
|
-
auto_crop=True
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
# Returns: {'png': Path(...), 'pdf': Path(...), 'svg': Path(...)}
|
|
248
|
+
# Get schema version
|
|
249
|
+
version = stx.vis.get_canvas_schema_version(project_dir, canvas_name)
|
|
533
250
|
```
|
|
534
251
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
## Integration with scitex.plt
|
|
538
|
-
|
|
539
|
-
`scitex.vis` uses `scitex.plt` as its rendering backend, which means:
|
|
540
|
-
|
|
541
|
-
1. **All figures maintain mm-exact dimensions**
|
|
542
|
-
2. **Automatic metadata embedding** (creation time, dimensions, etc.)
|
|
543
|
-
3. **Consistent styling** across all figures
|
|
544
|
-
4. **High-quality output** optimized for publications
|
|
252
|
+
### Panel Operations
|
|
545
253
|
|
|
546
254
|
```python
|
|
547
|
-
#
|
|
548
|
-
|
|
255
|
+
# Add panel from stx.plt output
|
|
256
|
+
stx.vis.add_panel_from_scitex(
|
|
257
|
+
project_dir, canvas_name, panel_name,
|
|
258
|
+
source_png="plot.png",
|
|
259
|
+
panel_properties={...}
|
|
260
|
+
)
|
|
549
261
|
|
|
550
|
-
#
|
|
551
|
-
|
|
262
|
+
# Add panel from image
|
|
263
|
+
stx.vis.add_panel_from_image(
|
|
264
|
+
project_dir, canvas_name, panel_name,
|
|
265
|
+
source_image="image.png",
|
|
266
|
+
panel_properties={...}
|
|
267
|
+
)
|
|
552
268
|
|
|
553
|
-
#
|
|
554
|
-
|
|
555
|
-
```
|
|
269
|
+
# Update panel properties
|
|
270
|
+
stx.vis.update_panel(project_dir, canvas_name, panel_name, {"opacity": 0.8})
|
|
556
271
|
|
|
557
|
-
|
|
272
|
+
# Remove panel
|
|
273
|
+
stx.vis.remove_panel(project_dir, canvas_name, panel_name)
|
|
558
274
|
|
|
559
|
-
|
|
275
|
+
# List panels
|
|
276
|
+
panels = stx.vis.list_panels(project_dir, canvas_name)
|
|
560
277
|
|
|
561
|
-
|
|
278
|
+
# Get single panel
|
|
279
|
+
panel = stx.vis.get_panel(project_dir, canvas_name, panel_name)
|
|
562
280
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
stx.vis.get_template(name, **kwargs) -> Dict
|
|
566
|
-
stx.vis.list_templates() -> List[str]
|
|
567
|
-
|
|
568
|
-
# Build & Export
|
|
569
|
-
stx.vis.build_figure_from_json(fig_json) -> (fig, axes)
|
|
570
|
-
stx.vis.export_figure(fig_json, output_path, fmt=None, dpi=300, **kwargs) -> Path
|
|
571
|
-
stx.vis.export_figure_from_file(json_path, output_path, **kwargs) -> Path
|
|
572
|
-
|
|
573
|
-
# I/O
|
|
574
|
-
stx.vis.load_figure_json(path, validate=True) -> Dict
|
|
575
|
-
stx.vis.save_figure_json(fig_json, path) -> Path
|
|
576
|
-
stx.vis.load_figure_json_from_project(project_dir, figure_id) -> Dict
|
|
577
|
-
stx.vis.save_figure_json_to_project(project_dir, figure_id, fig_json) -> Path
|
|
578
|
-
|
|
579
|
-
# Models
|
|
580
|
-
stx.vis.FigureModel
|
|
581
|
-
stx.vis.AxesModel
|
|
582
|
-
stx.vis.PlotModel
|
|
583
|
-
stx.vis.GuideModel
|
|
584
|
-
stx.vis.AnnotationModel
|
|
585
|
-
|
|
586
|
-
# Constants
|
|
587
|
-
stx.vis.NATURE_SINGLE_COLUMN_MM # 89
|
|
588
|
-
stx.vis.NATURE_DOUBLE_COLUMN_MM # 183
|
|
281
|
+
# Reorder panels (z-index)
|
|
282
|
+
stx.vis.reorder_panels(project_dir, canvas_name, ["panel_b", "panel_a"])
|
|
589
283
|
```
|
|
590
284
|
|
|
591
|
-
###
|
|
285
|
+
### Data Operations (Hash Verification)
|
|
592
286
|
|
|
593
287
|
```python
|
|
594
|
-
|
|
288
|
+
# Compute file hash
|
|
289
|
+
hash_str = stx.vis.compute_file_hash(filepath) # "sha256:abc123..."
|
|
595
290
|
|
|
596
|
-
#
|
|
597
|
-
|
|
598
|
-
backend.validate_figure_json(fig_json) -> bool
|
|
291
|
+
# Verify hash
|
|
292
|
+
is_valid = stx.vis.verify_data_hash(filepath, expected_hash)
|
|
599
293
|
|
|
600
|
-
#
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
# Export
|
|
605
|
-
backend.export_figure(fig_json, output_path, **kwargs) -> Path
|
|
606
|
-
backend.export_multiple_formats(fig_json, output_dir, base_name, formats) -> Dict
|
|
294
|
+
# Verify all data files in canvas
|
|
295
|
+
results = stx.vis.verify_all_data_hashes(project_dir, canvas_name)
|
|
296
|
+
# {"panels/panel_a/panel.csv": True, ...}
|
|
607
297
|
```
|
|
608
298
|
|
|
609
|
-
###
|
|
299
|
+
### Export Operations
|
|
610
300
|
|
|
611
301
|
```python
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
io.list_figures_in_project(project_dir) -> List[str]
|
|
619
|
-
|
|
620
|
-
# Save
|
|
621
|
-
io.save_figure_json(fig_json, path) -> Path
|
|
622
|
-
io.save_figure_json_to_project(project_dir, figure_id, fig_json) -> Path
|
|
623
|
-
io.save_figure_model(fig_model, path) -> Path
|
|
624
|
-
```
|
|
302
|
+
# Export to single format
|
|
303
|
+
export_path = stx.vis.export_canvas_to_file(
|
|
304
|
+
project_dir, canvas_name,
|
|
305
|
+
output_format="png", # png, pdf, svg
|
|
306
|
+
dpi=300
|
|
307
|
+
)
|
|
625
308
|
|
|
626
|
-
|
|
309
|
+
# Export to multiple formats
|
|
310
|
+
paths = stx.vis.export_canvas_to_multiple_formats(
|
|
311
|
+
project_dir, canvas_name,
|
|
312
|
+
formats=["png", "pdf", "svg"]
|
|
313
|
+
)
|
|
627
314
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
# Templates
|
|
632
|
-
utils.get_nature_single_column(height_mm=89, nrows=1, ncols=1) -> Dict
|
|
633
|
-
utils.get_nature_double_column(height_mm=120, nrows=1, ncols=1) -> Dict
|
|
634
|
-
utils.get_science_single_column(height_mm=84, nrows=1, ncols=1) -> Dict
|
|
635
|
-
utils.get_a4_figure(width_mm=180, height_mm=120) -> Dict
|
|
636
|
-
utils.get_square_figure(size_mm=120) -> Dict
|
|
637
|
-
utils.get_presentation_slide(aspect_ratio="16:9") -> Dict
|
|
638
|
-
|
|
639
|
-
# Validation
|
|
640
|
-
utils.validate_json_structure(fig_json) -> bool
|
|
641
|
-
utils.validate_plot_data(plot_data) -> bool
|
|
642
|
-
utils.check_schema_version(fig_json) -> str
|
|
643
|
-
|
|
644
|
-
# Constants
|
|
645
|
-
utils.NATURE_SINGLE_COLUMN_MM
|
|
646
|
-
utils.NATURE_DOUBLE_COLUMN_MM
|
|
647
|
-
utils.SCIENCE_SINGLE_COLUMN_MM
|
|
648
|
-
utils.A4_WIDTH_MM
|
|
649
|
-
utils.A4_HEIGHT_MM
|
|
315
|
+
# List existing exports
|
|
316
|
+
exports = stx.vis.list_canvas_exports(project_dir, canvas_name)
|
|
650
317
|
```
|
|
651
318
|
|
|
652
319
|
---
|
|
653
320
|
|
|
654
|
-
##
|
|
321
|
+
## Integration with stx.plt
|
|
655
322
|
|
|
656
|
-
|
|
323
|
+
`stx.plt` outputs (PNG + JSON + CSV) can be directly added as panels:
|
|
657
324
|
|
|
325
|
+
```python
|
|
326
|
+
# 1. Create figures with stx.plt
|
|
327
|
+
fig, ax = stx.plt.subplots()
|
|
328
|
+
ax.plot(x, y)
|
|
329
|
+
stx.io.save(fig, "./output/timeseries.png")
|
|
330
|
+
# Creates: timeseries.png, timeseries.json, timeseries.csv
|
|
331
|
+
|
|
332
|
+
# 2. Add to canvas as panel
|
|
333
|
+
stx.vis.add_panel_from_scitex(
|
|
334
|
+
project_dir, canvas_name, "panel_a",
|
|
335
|
+
source_png="./output/timeseries.png" # Auto-finds .json and .csv
|
|
336
|
+
)
|
|
658
337
|
```
|
|
659
|
-
project/
|
|
660
|
-
└── scitex/
|
|
661
|
-
└── vis/
|
|
662
|
-
├── figs/ # Figure JSON specifications
|
|
663
|
-
│ ├── fig-001.json
|
|
664
|
-
│ ├── fig-002.json
|
|
665
|
-
│ └── ...
|
|
666
|
-
└── export/ # Exported images (optional)
|
|
667
|
-
├── fig-001.png
|
|
668
|
-
├── fig-002.pdf
|
|
669
|
-
└── ...
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
This structure:
|
|
673
|
-
- ✅ **Version controllable**: JSON files track with your code
|
|
674
|
-
- ✅ **Organized**: Clear separation of specs and outputs
|
|
675
|
-
- ✅ **Collaborative**: Easy to review and edit
|
|
676
|
-
- ✅ **Reproducible**: One JSON → One figure
|
|
677
|
-
|
|
678
|
-
---
|
|
679
|
-
|
|
680
|
-
## Comparison with Other Tools
|
|
681
|
-
|
|
682
|
-
| Feature | scitex.vis | Matplotlib | Plotly | Seaborn |
|
|
683
|
-
|---------|-----------|-----------|--------|---------|
|
|
684
|
-
| JSON-based specs | ✅ | ❌ | ✅ | ❌ |
|
|
685
|
-
| Version controllable | ✅ | ❌ | Partial | ❌ |
|
|
686
|
-
| Publication templates | ✅ | ❌ | ❌ | ❌ |
|
|
687
|
-
| mm-exact dimensions | ✅ | ❌ | ❌ | ❌ |
|
|
688
|
-
| Metadata embedding | ✅ | ❌ | ❌ | ❌ |
|
|
689
|
-
| Project structure | ✅ | ❌ | ❌ | ❌ |
|
|
690
|
-
| Backend flexibility | ✅ | N/A | ❌ | Depends on mpl |
|
|
691
338
|
|
|
692
339
|
---
|
|
693
340
|
|
|
694
|
-
##
|
|
695
|
-
|
|
696
|
-
Planned features for future versions:
|
|
697
|
-
|
|
698
|
-
- [ ] **Interactive editing**: Web UI for figure JSON editing
|
|
699
|
-
- [ ] **AI figure generation**: LLM-based figure creation from descriptions
|
|
700
|
-
- [ ] **Figure diff/merge**: Git-friendly figure comparison
|
|
701
|
-
- [ ] **Style inheritance**: Template inheritance and composition
|
|
702
|
-
- [ ] **Figure collections**: Multi-figure documents
|
|
703
|
-
- [ ] **Backend plugins**: Support for alternative renderers
|
|
704
|
-
- [ ] **Automatic layouts**: Smart subplot arrangement
|
|
705
|
-
- [ ] **3D plots**: Support for 3D visualizations
|
|
706
|
-
|
|
707
|
-
---
|
|
708
|
-
|
|
709
|
-
## Contributing
|
|
710
|
-
|
|
711
|
-
When contributing to `scitex.vis`:
|
|
712
|
-
|
|
713
|
-
1. **Models**: Extend models in `model/` for new features
|
|
714
|
-
2. **Renderers**: Add plot types in `backend/render.py`
|
|
715
|
-
3. **Templates**: Add publication formats in `utils/defaults.py`
|
|
716
|
-
4. **Validation**: Update validators in `utils/validate.py`
|
|
717
|
-
|
|
718
|
-
---
|
|
341
|
+
## Examples
|
|
719
342
|
|
|
720
|
-
|
|
343
|
+
See `examples/vis/` for complete examples:
|
|
721
344
|
|
|
722
|
-
|
|
345
|
+
- `demo_canvas.py` - Canvas composition workflow
|
|
346
|
+
- `demo_vis_editor.py` - Interactive editor usage
|
|
347
|
+
- `demo_vis_module.py` - Legacy JSON-based workflow
|
|
723
348
|
|
|
724
349
|
---
|
|
725
350
|
|
|
726
351
|
## Related Documentation
|
|
727
352
|
|
|
353
|
+
- [CANVAS_ARCHITECTURE.md](./docs/CANVAS_ARCHITECTURE.md) - Full architecture details
|
|
728
354
|
- [scitex.plt](../plt/README.md) - Plotting backend
|
|
729
|
-
- [
|
|
730
|
-
- [scitex.scholar](../scholar/README.md) - Literature management
|
|
731
|
-
- [scitex.writer](../writer/README.md) - Document generation
|
|
355
|
+
- [FIGURE_ARCHITECTURE.md](../plt/docs/FIGURE_ARCHITECTURE.md) - Figure output format
|
|
732
356
|
|
|
733
357
|
---
|
|
734
358
|
|
|
735
|
-
##
|
|
359
|
+
## Integration with stx.io
|
|
736
360
|
|
|
737
|
-
|
|
738
|
-
- Basic figure creation
|
|
739
|
-
- Multi-subplot layouts
|
|
740
|
-
- Project workflows
|
|
741
|
-
- Model validation
|
|
742
|
-
- Template usage
|
|
361
|
+
Canvas directories (`.canvas`) are first-class citizens in the scitex I/O system:
|
|
743
362
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
363
|
+
```python
|
|
364
|
+
# Save canvas to a portable directory
|
|
365
|
+
stx.io.save(canvas_dict, "/path/to/fig1_results.canvas")
|
|
366
|
+
|
|
367
|
+
# Load canvas from directory
|
|
368
|
+
canvas = stx.io.load("/path/to/fig1_results.canvas")
|
|
369
|
+
|
|
370
|
+
# Access canvas properties
|
|
371
|
+
print(canvas["canvas_name"])
|
|
372
|
+
print(canvas["panels"])
|
|
747
373
|
```
|
|
748
374
|
|
|
749
375
|
---
|
|
750
376
|
|
|
751
|
-
|
|
377
|
+
## Constants
|
|
752
378
|
|
|
753
|
-
|
|
379
|
+
```python
|
|
380
|
+
stx.vis.SCHEMA_VERSION # "2.0.0"
|
|
381
|
+
stx.vis.CANVAS_EXTENSION # ".canvas"
|
|
382
|
+
stx.vis.NATURE_SINGLE_COLUMN_MM # 89
|
|
383
|
+
stx.vis.NATURE_DOUBLE_COLUMN_MM # 183
|
|
384
|
+
```
|
|
754
385
|
|
|
755
|
-
<!-- EOF -->
|
|
386
|
+
<!-- EOF -->
|