scitex 2.10.2__py3-none-any.whl → 2.11.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/__init__.py +1 -4
- scitex/__version__.py +1 -1
- scitex/_install_guide.py +14 -2
- scitex/bridge/_figrecipe.py +1 -1
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_plt.py +1 -1
- scitex/bridge/_stats_vis.py +2 -2
- scitex/{fig → canvas}/__init__.py +84 -96
- scitex/{fig → canvas}/backend/_parser.py +1 -1
- scitex/{fig → canvas}/canvas.py +13 -14
- scitex/{fts/_fig/_editor → canvas/editor}/_defaults.py +2 -2
- scitex/{fig → canvas}/editor/edit/__init__.py +11 -14
- scitex/{fig → canvas}/editor/edit/bundle_resolver.py +56 -48
- scitex/{fig → canvas}/editor/edit/editor_launcher.py +79 -26
- scitex/{fts/_fig/_editor/_cui/_panel_loader.py → canvas/editor/edit/panel_loader.py} +8 -8
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_bbox.py +2 -1
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_core.py +84 -84
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_renderer.py +7 -6
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/static/css/features/canvas.css +2 -2
- scitex/{fig → canvas}/editor/flask_editor/static/css/features/panel-grid.css +1 -1
- scitex/{fig → canvas}/editor/flask_editor/static/js/core/api.js +3 -4
- scitex/{fig → canvas}/editor/flask_editor/static/js/editor/preview.js +5 -5
- scitex/{fig → canvas}/editor/flask_editor/templates/_html.py +3 -3
- scitex/{fig → canvas}/editor/flask_editor/templates/_scripts.py +10 -10
- scitex/{fig → canvas}/editor/flask_editor/templates/_styles.py +3 -3
- scitex/{fig → canvas}/io/__init__.py +32 -38
- scitex/{fig → canvas}/io/_bundle.py +217 -154
- scitex/{fig → canvas}/io/_canvas.py +1 -1
- scitex/{fig → canvas}/io/_data.py +1 -1
- scitex/{fig → canvas}/io/_export.py +1 -1
- scitex/{fig → canvas}/io/_load.py +1 -1
- scitex/{fig → canvas}/io/_panel.py +1 -1
- scitex/{fig → canvas}/io/_save.py +1 -1
- scitex/{fig → canvas}/model/__init__.py +1 -1
- scitex/{fig → canvas}/model/_annotations.py +1 -1
- scitex/{fig → canvas}/model/_axes.py +1 -1
- scitex/{fig → canvas}/model/_figure.py +1 -1
- scitex/{fig → canvas}/model/_guides.py +1 -1
- scitex/{fig → canvas}/model/_plot.py +1 -1
- scitex/{fig → canvas}/model/_styles.py +1 -1
- scitex/{fig → canvas}/utils/__init__.py +1 -1
- scitex/cli/convert.py +10 -6
- scitex/diagram/README.md +7 -7
- scitex/io/__init__.py +7 -19
- scitex/io/_load.py +15 -19
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_con.py +5 -5
- scitex/io/_load_modules/_eeg.py +16 -12
- scitex/io/_save.py +11 -16
- scitex/io/_save_modules/__init__.py +6 -10
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_plot_bundle.py +112 -0
- scitex/io/_save_modules/{_pltz_stx.py → _plot_scitex.py} +7 -7
- scitex/io/_save_modules/_stx_bundle.py +16 -16
- scitex/io/bundle/README.md +89 -80
- scitex/{fts/_bundle/_FTS.py → io/bundle/_Bundle.py} +197 -95
- scitex/io/bundle/__init__.py +67 -35
- scitex/{fts/_bundle → io/bundle}/_children.py +32 -40
- scitex/io/bundle/_core.py +184 -97
- scitex/{fts/_bundle/_dataclasses/_Node.py → io/bundle/_dataclasses/_Spec.py} +29 -23
- scitex/{fts/_bundle/_dataclasses/_NodeRefs.py → io/bundle/_dataclasses/_SpecRefs.py} +6 -6
- scitex/{fts/_bundle → io/bundle}/_dataclasses/__init__.py +4 -4
- scitex/{fts/_bundle → io/bundle}/_loader.py +19 -19
- scitex/io/bundle/_manifest.py +99 -0
- scitex/{fts/_bundle → io/bundle}/_mpl_helpers.py +119 -28
- scitex/io/bundle/_nested.py +113 -100
- scitex/{fts/_bundle → io/bundle}/_saver.py +13 -14
- scitex/{fts/_bundle → io/bundle}/_storage.py +3 -3
- scitex/io/bundle/_types.py +41 -16
- scitex/{fts/_bundle → io/bundle}/_validation.py +20 -18
- scitex/io/bundle/_zip.py +21 -31
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_backend/_parser.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Annotations.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Axes.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Figure.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Guides.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Plot.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Styles.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_utils/_plot_layout.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/__init__.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/_app.py +1 -1
- scitex/{fts/_tables → io/bundle/kinds/_table}/_latex/_export.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_figure_exporter.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_table_exporter.py +1 -1
- scitex/io/bundle/schemas/__init__.py +30 -0
- scitex/parallel/_run.py +5 -4
- scitex/path/_find.py +60 -83
- scitex/path/_get_module_path.py +23 -21
- scitex/path/_get_spath.py +6 -27
- scitex/path/_getsize.py +23 -9
- scitex/path/_increment_version.py +31 -38
- scitex/path/_mk_spath.py +26 -29
- scitex/path/_path.py +5 -12
- scitex/path/_split.py +27 -15
- scitex/path/_this_path.py +23 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +2 -1
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +2 -2
- scitex/plt/gallery/_generate.py +76 -50
- scitex/plt/io/__init__.py +17 -19
- scitex/plt/io/_bundle.py +99 -52
- scitex/plt/io/_layered_bundle.py +303 -168
- scitex/plt/utils/_csv_column_naming.py +250 -118
- scitex/schema/__init__.py +69 -73
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +30 -33
- scitex/stats/_schema.py +1 -1
- scitex/stats/io/__init__.py +10 -11
- scitex/stats/io/_bundle.py +16 -16
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/METADATA +191 -72
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/RECORD +237 -360
- scitex/fig/editor/_defaults.py +0 -300
- scitex/fig/editor/edit/panel_loader.py +0 -232
- scitex/fig/editor/flask_editor/_bbox.py +0 -1299
- scitex/fig/editor/flask_editor/_core.py +0 -1429
- scitex/fig/editor/flask_editor/_renderer.py +0 -813
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +0 -176
- scitex/fts/README.md +0 -262
- scitex/fts/TODO.md +0 -66
- scitex/fts/__init__.py +0 -90
- scitex/fts/_bundle/README_IN_BUNDLE.md +0 -102
- scitex/fts/_bundle/__init__.py +0 -38
- scitex/fts/_bundle/_utils/__init__.py +0 -55
- scitex/fts/_bundle/_utils/_const.py +0 -26
- scitex/fts/_bundle/_utils/_errors.py +0 -73
- scitex/fts/_bundle/_utils/_generate.py +0 -21
- scitex/fts/_bundle/_utils/_types.py +0 -76
- scitex/fts/_bundle/_zipbundle.py +0 -165
- scitex/fts/_fig/__init__.py +0 -22
- scitex/fts/_fig/_backend/_parser.py +0 -188
- scitex/fts/_fig/_editor/__init__.py +0 -14
- scitex/fts/_fig/_editor/_cui/__init__.py +0 -33
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +0 -39
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +0 -366
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +0 -175
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +0 -52
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +0 -66
- scitex/fts/_fig/_editor/_gui/__init__.py +0 -11
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +0 -20
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +0 -664
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +0 -79
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +0 -41
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +0 -16
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +0 -85
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +0 -217
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +0 -93
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +0 -112
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +0 -212
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +0 -190
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +0 -45
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +0 -95
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +0 -101
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +0 -138
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +0 -31
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +0 -7
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +0 -56
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +0 -78
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +0 -314
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +0 -107
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +0 -54
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +0 -172
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +0 -258
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +0 -48
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +0 -71
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +0 -288
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +0 -143
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +0 -245
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +0 -992
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +0 -339
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +0 -286
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +0 -371
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +0 -293
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +0 -426
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +0 -152
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +0 -265
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +0 -184
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +0 -100
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +0 -34
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +0 -124
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +0 -851
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +0 -4932
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +0 -1657
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +0 -36
- scitex/fts/_fig/_models/_Annotations.py +0 -115
- scitex/fts/_fig/_models/_Axes.py +0 -152
- scitex/fts/_fig/_models/_Figure.py +0 -138
- scitex/fts/_fig/_models/_Plot.py +0 -123
- scitex/fts/_fig/_utils/_plot_layout.py +0 -397
- scitex/fts/_kinds/_figure/_composite.py +0 -345
- scitex/fts/_kinds/_plot/_backend/__init__.py +0 -53
- scitex/fts/_kinds/_plot/_backend/_export.py +0 -165
- scitex/fts/_kinds/_plot/_backend/_render.py +0 -538
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +0 -46
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +0 -82
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +0 -441
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +0 -52
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +0 -47
- scitex/fts/_kinds/_plot/_models/_Guides.py +0 -104
- scitex/fts/_kinds/_plot/_models/_Styles.py +0 -245
- scitex/fts/_kinds/_plot/_models/__init__.py +0 -80
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +0 -156
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +0 -43
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +0 -38
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +0 -36
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +0 -60
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +0 -61
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +0 -57
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +0 -121
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +0 -36
- scitex/fts/_kinds/_plot/_utils/__init__.py +0 -129
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +0 -127
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +0 -111
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +0 -48
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +0 -77
- scitex/fts/_kinds/_plot/_utils/_get_template.py +0 -178
- scitex/fts/_kinds/_plot/_utils/_normalize.py +0 -73
- scitex/fts/_kinds/_plot/_utils/_validate.py +0 -197
- scitex/fts/_kinds/_table/_latex/_export.py +0 -279
- scitex/fts/_stats/__init__.py +0 -48
- scitex/fts/_stats/_dataclasses/_Stats.py +0 -423
- scitex/fts/_stats/_dataclasses/__init__.py +0 -48
- scitex/fts/_tables/__init__.py +0 -65
- scitex/fts/_tables/_latex/__init__.py +0 -93
- scitex/fts/_tables/_latex/_editor/__init__.py +0 -11
- scitex/fts/_tables/_latex/_editor/_app.py +0 -725
- scitex/fts/_tables/_latex/_figure_exporter.py +0 -153
- scitex/fts/_tables/_latex/_stats_formatter.py +0 -274
- scitex/fts/_tables/_latex/_table_exporter.py +0 -362
- scitex/fts/_tables/_latex/_utils.py +0 -369
- scitex/fts/_tables/_latex/_validator.py +0 -445
- scitex/io/_save_modules/_pltz_bundle.py +0 -356
- /scitex/{fig → canvas}/README.md +0 -0
- /scitex/{fig → canvas}/backend/__init__.py +0 -0
- /scitex/{fig → canvas}/backend/_export.py +0 -0
- /scitex/{fig → canvas}/backend/_render.py +0 -0
- /scitex/{fig → canvas}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{fig → canvas}/editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/_dearpygui_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_flask_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_mpl_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_qt_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_tkinter_editor.py +0 -0
- /scitex/{fig → canvas}/editor/edit/backend_detector.py +0 -0
- /scitex/{fig → canvas}/editor/edit/manual_handler.py +0 -0
- /scitex/{fig → canvas}/editor/edit/path_resolver.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_plotter.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_utils.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/reset.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/typography.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/variables.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/buttons.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/context-menu.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/dropdown.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/forms.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/modal.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/sections.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/element-inspector.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/loading.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/overlay.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/selection.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/statistics.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/index.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/container.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/controls.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/preview.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/axis.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/basic.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/distribute.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/canvas.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/dragging.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/resize.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/selection.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/state.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/utils.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/dev/element-inspector.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/bbox.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/element-drag.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/overlay.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/main.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/context-menu.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/keyboard.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/controls.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/download.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/help.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/theme.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/templates/__init__.py +0 -0
- /scitex/{fig → canvas}/io/_directory.py +0 -0
- /scitex/{fig → canvas}/model/_plot_types.py +0 -0
- /scitex/{fig → canvas}/utils/_defaults.py +0 -0
- /scitex/{fig → canvas}/utils/_validate.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_bundle2dict.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_dict2bundle.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_Axes.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_BBox.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_ColumnDef.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataFormat.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataInfo.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataSource.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_SizeMM.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_bar.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_line.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_scatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_figure/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_figure}/_composite.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_plot/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_export.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_render.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_ChannelEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Encoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Theme.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_TraceEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_bar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_box.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_distribution.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_errorbar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_histogram.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_image.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_line.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_scatter.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_seaborn.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_violin.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_auto_layout.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_calc_bounds.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_const_sizes.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_convert_coords.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_get_template.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_normalize.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_validate.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_shape/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/_Stats.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_stats_formatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_utils.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_validator.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_text/__init__.py +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/data_info.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/encoding.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/node.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/render_manifest.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/stats.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/theme.schema.json +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
2
3
|
# File: ./src/scitex/vis/editor/flask_editor/core.py
|
|
3
4
|
"""Core WebEditor class for Flask-based figure editing."""
|
|
4
5
|
|
|
@@ -10,7 +11,7 @@ import webbrowser
|
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any, Dict, Optional
|
|
12
13
|
|
|
13
|
-
from ._utils import check_port_available, kill_process_on_port
|
|
14
|
+
from ._utils import check_port_available, find_available_port, kill_process_on_port
|
|
14
15
|
from .templates import build_html_template
|
|
15
16
|
|
|
16
17
|
|
|
@@ -19,7 +20,7 @@ class WebEditor:
|
|
|
19
20
|
Browser-based figure editor using Flask.
|
|
20
21
|
|
|
21
22
|
Features:
|
|
22
|
-
- Displays existing PNG from
|
|
23
|
+
- Displays existing PNG from plot bundle (no re-rendering)
|
|
23
24
|
- Hitmap-based element selection for precise clicking
|
|
24
25
|
- Property editors with sliders and color pickers
|
|
25
26
|
- Save to .manual.json
|
|
@@ -46,7 +47,7 @@ class WebEditor:
|
|
|
46
47
|
self.manual_overrides = manual_overrides or {}
|
|
47
48
|
self._requested_port = port
|
|
48
49
|
self.port = port
|
|
49
|
-
self.panel_info = panel_info # For multi-panel
|
|
50
|
+
self.panel_info = panel_info # For multi-panel figure bundles
|
|
50
51
|
|
|
51
52
|
# Extract hit_regions from metadata for color-based element detection
|
|
52
53
|
self.hit_regions = metadata.get("hit_regions", {})
|
|
@@ -107,7 +108,7 @@ class WebEditor:
|
|
|
107
108
|
import numpy as np
|
|
108
109
|
from PIL import Image
|
|
109
110
|
|
|
110
|
-
from scitex.
|
|
111
|
+
from scitex.io import ZipBundle
|
|
111
112
|
|
|
112
113
|
matplotlib.use("Agg")
|
|
113
114
|
import io
|
|
@@ -120,16 +121,18 @@ class WebEditor:
|
|
|
120
121
|
return {"success": False, "error": "No panel info"}
|
|
121
122
|
|
|
122
123
|
bundle_path = editor.panel_info.get("bundle_path")
|
|
123
|
-
|
|
124
|
+
figure_dir = editor.panel_info.get("figure_dir")
|
|
124
125
|
|
|
125
|
-
if not bundle_path and not
|
|
126
|
+
if not bundle_path and not figure_dir:
|
|
126
127
|
return {"success": False, "error": "No bundle path"}
|
|
127
128
|
|
|
128
129
|
figure_name = (
|
|
129
130
|
Path(bundle_path).stem
|
|
130
131
|
if bundle_path
|
|
131
132
|
else (
|
|
132
|
-
Path(
|
|
133
|
+
Path(figure_dir).stem.replace(".figure", "")
|
|
134
|
+
if figure_dir
|
|
135
|
+
else "figure"
|
|
133
136
|
)
|
|
134
137
|
)
|
|
135
138
|
|
|
@@ -146,12 +149,12 @@ class WebEditor:
|
|
|
146
149
|
pass
|
|
147
150
|
except:
|
|
148
151
|
pass
|
|
149
|
-
elif
|
|
150
|
-
spec_path = Path(
|
|
152
|
+
elif figure_dir:
|
|
153
|
+
spec_path = Path(figure_dir) / "spec.json"
|
|
151
154
|
if spec_path.exists():
|
|
152
155
|
with open(spec_path) as f:
|
|
153
156
|
spec = json_module.load(f)
|
|
154
|
-
layout_path = Path(
|
|
157
|
+
layout_path = Path(figure_dir) / "layout.json"
|
|
155
158
|
if layout_path.exists():
|
|
156
159
|
with open(layout_path) as f:
|
|
157
160
|
layout_overrides = json_module.load(f)
|
|
@@ -200,7 +203,7 @@ class WebEditor:
|
|
|
200
203
|
is_zip = False
|
|
201
204
|
panel_name = None
|
|
202
205
|
for idx, pp in enumerate(panel_paths):
|
|
203
|
-
pp_name = Path(pp).stem.replace(".
|
|
206
|
+
pp_name = Path(pp).stem.replace(".plot", "")
|
|
204
207
|
if (
|
|
205
208
|
pp_name == panel_id
|
|
206
209
|
or pp_name.startswith(f"panel_{panel_id}_")
|
|
@@ -208,7 +211,7 @@ class WebEditor:
|
|
|
208
211
|
or f"_{panel_id}_" in pp_name
|
|
209
212
|
):
|
|
210
213
|
panel_path = pp
|
|
211
|
-
panel_name = Path(pp).name # e.g., "panel_A_twinx.
|
|
214
|
+
panel_name = Path(pp).name # e.g., "panel_A_twinx.plot"
|
|
212
215
|
is_zip = panel_is_zip[idx] if idx < len(panel_is_zip) else False
|
|
213
216
|
break
|
|
214
217
|
|
|
@@ -237,7 +240,7 @@ class WebEditor:
|
|
|
237
240
|
exclude_patterns = ["hitmap", "overview", "thumb", "preview"]
|
|
238
241
|
|
|
239
242
|
if is_zip:
|
|
240
|
-
with ZipBundle(panel_path, mode="r") as
|
|
243
|
+
with ZipBundle(panel_path, mode="r") as plot_bundle:
|
|
241
244
|
with zipfile.ZipFile(panel_path, "r") as zf:
|
|
242
245
|
png_files = [
|
|
243
246
|
n
|
|
@@ -250,18 +253,16 @@ class WebEditor:
|
|
|
250
253
|
]
|
|
251
254
|
if png_files:
|
|
252
255
|
preview_path = png_files[0]
|
|
253
|
-
if ".
|
|
254
|
-
preview_path = preview_path.split(".
|
|
255
|
-
|
|
256
|
-
]
|
|
257
|
-
img_data = pltz_bundle.read_bytes(preview_path)
|
|
256
|
+
if ".plot/" in preview_path:
|
|
257
|
+
preview_path = preview_path.split(".plot/")[-1]
|
|
258
|
+
img_data = plot_bundle.read_bytes(preview_path)
|
|
258
259
|
img = Image.open(io.BytesIO(img_data))
|
|
259
260
|
ax = fig.add_axes([x_frac, y_frac, w_frac, h_frac])
|
|
260
261
|
ax.imshow(np.array(img))
|
|
261
262
|
ax.axis("off")
|
|
262
263
|
else:
|
|
263
|
-
|
|
264
|
-
exports_dir =
|
|
264
|
+
plot_dir = Path(panel_path)
|
|
265
|
+
exports_dir = plot_dir / "exports"
|
|
265
266
|
if exports_dir.exists():
|
|
266
267
|
for png_file in exports_dir.glob("*.png"):
|
|
267
268
|
name_lower = png_file.name.lower()
|
|
@@ -329,24 +330,24 @@ class WebEditor:
|
|
|
329
330
|
|
|
330
331
|
# Extract figz and panel paths for display
|
|
331
332
|
json_path_str = str(editor.json_path.resolve())
|
|
332
|
-
|
|
333
|
+
figure_path = ""
|
|
333
334
|
panel_path = ""
|
|
334
335
|
|
|
335
|
-
# Check if this is inside a
|
|
336
|
-
if ".
|
|
337
|
-
parts = json_path_str.split(".
|
|
338
|
-
|
|
336
|
+
# Check if this is inside a figure bundle
|
|
337
|
+
if ".figure/" in json_path_str:
|
|
338
|
+
parts = json_path_str.split(".figure/")
|
|
339
|
+
figure_path = parts[0] + ".figure"
|
|
339
340
|
panel_path = parts[1] if len(parts) > 1 else ""
|
|
340
|
-
elif ".
|
|
341
|
-
parts = json_path_str.split(".
|
|
342
|
-
|
|
341
|
+
elif ".plot/" in json_path_str:
|
|
342
|
+
parts = json_path_str.split(".plot/")
|
|
343
|
+
figure_path = parts[0] + ".plot"
|
|
343
344
|
panel_path = parts[1] if len(parts) > 1 else ""
|
|
344
345
|
else:
|
|
345
|
-
|
|
346
|
+
figure_path = json_path_str
|
|
346
347
|
|
|
347
348
|
return render_template_string(
|
|
348
349
|
html_template,
|
|
349
|
-
filename=
|
|
350
|
+
filename=figure_path,
|
|
350
351
|
panel_path=panel_path,
|
|
351
352
|
overrides=json.dumps(editor.current_overrides),
|
|
352
353
|
)
|
|
@@ -378,7 +379,7 @@ class WebEditor:
|
|
|
378
379
|
|
|
379
380
|
@app.route("/panels")
|
|
380
381
|
def panels():
|
|
381
|
-
"""Return all panel images with bboxes for interactive grid view (
|
|
382
|
+
"""Return all panel images with bboxes for interactive grid view (figure bundles only).
|
|
382
383
|
|
|
383
384
|
Uses smart load_panel_data helper for transparent zip/directory handling.
|
|
384
385
|
Returns layout info from figz spec.json for unified canvas positioning.
|
|
@@ -392,27 +393,27 @@ class WebEditor:
|
|
|
392
393
|
)
|
|
393
394
|
|
|
394
395
|
if not editor.panel_info:
|
|
395
|
-
return jsonify({"error": "Not a multi-panel
|
|
396
|
+
return jsonify({"error": "Not a multi-panel figure bundle"}), 400
|
|
396
397
|
|
|
397
398
|
panel_names = editor.panel_info["panels"]
|
|
398
399
|
panel_paths = editor.panel_info.get("panel_paths", [])
|
|
399
400
|
panel_is_zip = editor.panel_info.get(
|
|
400
401
|
"panel_is_zip", [False] * len(panel_names)
|
|
401
402
|
)
|
|
402
|
-
|
|
403
|
+
figure_dir = Path(editor.panel_info["figure_dir"])
|
|
403
404
|
|
|
404
405
|
if not panel_paths:
|
|
405
|
-
panel_paths = [str(
|
|
406
|
+
panel_paths = [str(figure_dir / name) for name in panel_names]
|
|
406
407
|
|
|
407
408
|
# Load figz spec.json to get panel layout
|
|
408
|
-
|
|
409
|
-
spec_path =
|
|
409
|
+
figure_layout = {}
|
|
410
|
+
spec_path = figure_dir / "spec.json"
|
|
410
411
|
if spec_path.exists():
|
|
411
412
|
with open(spec_path) as f:
|
|
412
|
-
|
|
413
|
-
for panel_spec in
|
|
413
|
+
figure_spec = json_module.load(f)
|
|
414
|
+
for panel_spec in figure_spec.get("panels", []):
|
|
414
415
|
panel_id = panel_spec.get("id", "")
|
|
415
|
-
|
|
416
|
+
figure_layout[panel_id] = {
|
|
416
417
|
"position": panel_spec.get("position", {}),
|
|
417
418
|
"size": panel_spec.get("size", {}),
|
|
418
419
|
}
|
|
@@ -422,7 +423,7 @@ class WebEditor:
|
|
|
422
423
|
for idx, panel_name in enumerate(panel_names):
|
|
423
424
|
panel_path = panel_paths[idx]
|
|
424
425
|
is_zip = panel_is_zip[idx] if idx < len(panel_is_zip) else None
|
|
425
|
-
display_name = panel_name.replace(".
|
|
426
|
+
display_name = panel_name.replace(".plot", "").replace(".plot", "")
|
|
426
427
|
|
|
427
428
|
# Use smart helper to load panel data
|
|
428
429
|
loaded = load_panel_data(panel_path, is_zip=is_zip)
|
|
@@ -435,8 +436,8 @@ class WebEditor:
|
|
|
435
436
|
}
|
|
436
437
|
|
|
437
438
|
# Add layout info from figz spec
|
|
438
|
-
if display_name in
|
|
439
|
-
panel_data["layout"] =
|
|
439
|
+
if display_name in figure_layout:
|
|
440
|
+
panel_data["layout"] = figure_layout[display_name]
|
|
440
441
|
|
|
441
442
|
if loaded:
|
|
442
443
|
# Get image data
|
|
@@ -495,13 +496,13 @@ class WebEditor:
|
|
|
495
496
|
{
|
|
496
497
|
"panels": panel_images,
|
|
497
498
|
"count": len(panel_images),
|
|
498
|
-
"layout":
|
|
499
|
+
"layout": figure_layout,
|
|
499
500
|
}
|
|
500
501
|
)
|
|
501
502
|
|
|
502
503
|
@app.route("/switch_panel/<int:panel_index>")
|
|
503
504
|
def switch_panel(panel_index):
|
|
504
|
-
"""Switch to a different panel in the
|
|
505
|
+
"""Switch to a different panel in the figure bundle.
|
|
505
506
|
|
|
506
507
|
Uses smart load_panel_data helper for transparent zip/directory handling.
|
|
507
508
|
"""
|
|
@@ -512,7 +513,7 @@ class WebEditor:
|
|
|
512
513
|
)
|
|
513
514
|
|
|
514
515
|
if not editor.panel_info:
|
|
515
|
-
return jsonify({"error": "Not a multi-panel
|
|
516
|
+
return jsonify({"error": "Not a multi-panel figure bundle"}), 400
|
|
516
517
|
|
|
517
518
|
panels = editor.panel_info["panels"]
|
|
518
519
|
panel_paths = editor.panel_info.get("panel_paths", [])
|
|
@@ -525,7 +526,7 @@ class WebEditor:
|
|
|
525
526
|
panel_path = (
|
|
526
527
|
panel_paths[panel_index]
|
|
527
528
|
if panel_paths
|
|
528
|
-
else str(Path(editor.panel_info["
|
|
529
|
+
else str(Path(editor.panel_info["figure_dir"]) / panel_name)
|
|
529
530
|
)
|
|
530
531
|
is_zip = (
|
|
531
532
|
panel_is_zip[panel_index] if panel_index < len(panel_is_zip) else None
|
|
@@ -689,7 +690,7 @@ class WebEditor:
|
|
|
689
690
|
|
|
690
691
|
@app.route("/save_layout", methods=["POST"])
|
|
691
692
|
def save_layout():
|
|
692
|
-
"""Save panel layout positions to
|
|
693
|
+
"""Save panel layout positions to figure bundle."""
|
|
693
694
|
try:
|
|
694
695
|
data = request.get_json()
|
|
695
696
|
layout = data.get("layout", {})
|
|
@@ -699,12 +700,12 @@ class WebEditor:
|
|
|
699
700
|
{"success": False, "error": "No layout data provided"}
|
|
700
701
|
)
|
|
701
702
|
|
|
702
|
-
# Check if we have panel_info (
|
|
703
|
+
# Check if we have panel_info (figure bundle)
|
|
703
704
|
if not editor.panel_info:
|
|
704
705
|
return jsonify(
|
|
705
706
|
{
|
|
706
707
|
"success": False,
|
|
707
|
-
"error": "No panel info available (not a
|
|
708
|
+
"error": "No panel info available (not a figure bundle)",
|
|
708
709
|
}
|
|
709
710
|
)
|
|
710
711
|
|
|
@@ -714,8 +715,8 @@ class WebEditor:
|
|
|
714
715
|
{"success": False, "error": "Bundle path not available"}
|
|
715
716
|
)
|
|
716
717
|
|
|
717
|
-
# Update layout in the
|
|
718
|
-
from scitex.
|
|
718
|
+
# Update layout in the figure bundle
|
|
719
|
+
from scitex.canvas.io import ZipBundle
|
|
719
720
|
|
|
720
721
|
bundle = ZipBundle(bundle_path)
|
|
721
722
|
|
|
@@ -778,7 +779,7 @@ class WebEditor:
|
|
|
778
779
|
|
|
779
780
|
@app.route("/save_element_position", methods=["POST"])
|
|
780
781
|
def save_element_position():
|
|
781
|
-
"""Save element position (legend/panel_letter) to
|
|
782
|
+
"""Save element position (legend/panel_letter) to figure bundle.
|
|
782
783
|
|
|
783
784
|
ONLY legends and panel letters can be repositioned to maintain
|
|
784
785
|
scientific rigor. Data elements are never moved.
|
|
@@ -812,7 +813,7 @@ class WebEditor:
|
|
|
812
813
|
{"success": False, "error": "Bundle path not available"}
|
|
813
814
|
)
|
|
814
815
|
|
|
815
|
-
from scitex.
|
|
816
|
+
from scitex.canvas.io import ZipBundle
|
|
816
817
|
|
|
817
818
|
bundle = ZipBundle(bundle_path)
|
|
818
819
|
|
|
@@ -878,7 +879,7 @@ class WebEditor:
|
|
|
878
879
|
|
|
879
880
|
@app.route("/export", methods=["POST"])
|
|
880
881
|
def export_figure():
|
|
881
|
-
"""Export composed figure to various formats and update
|
|
882
|
+
"""Export composed figure to various formats and update figure bundle."""
|
|
882
883
|
try:
|
|
883
884
|
data = request.get_json()
|
|
884
885
|
formats = data.get("formats", ["png", "svg"])
|
|
@@ -899,7 +900,7 @@ class WebEditor:
|
|
|
899
900
|
|
|
900
901
|
import matplotlib
|
|
901
902
|
|
|
902
|
-
from scitex.
|
|
903
|
+
from scitex.io import ZipBundle
|
|
903
904
|
|
|
904
905
|
matplotlib.use("Agg")
|
|
905
906
|
import matplotlib.pyplot as plt
|
|
@@ -943,7 +944,7 @@ class WebEditor:
|
|
|
943
944
|
# Compose panels onto figure
|
|
944
945
|
for panel_spec in panels_spec:
|
|
945
946
|
panel_id = panel_spec.get("id", "")
|
|
946
|
-
|
|
947
|
+
plot_name = panel_spec.get("plot", "")
|
|
947
948
|
|
|
948
949
|
# Get position and size from spec
|
|
949
950
|
pos = panel_spec.get("position", {})
|
|
@@ -962,24 +963,24 @@ class WebEditor:
|
|
|
962
963
|
|
|
963
964
|
# Try to read panel image from pltz exports
|
|
964
965
|
img_loaded = False
|
|
965
|
-
for
|
|
966
|
-
f"{panel_id}.
|
|
967
|
-
|
|
966
|
+
for plot_path in [
|
|
967
|
+
f"{panel_id}.plot",
|
|
968
|
+
plot_name.replace(".d", ""),
|
|
968
969
|
]:
|
|
969
970
|
if img_loaded:
|
|
970
971
|
break
|
|
971
972
|
try:
|
|
972
973
|
# Read pltz as nested bundle
|
|
973
|
-
|
|
974
|
+
plot_bytes = bundle.read_bytes(plot_path)
|
|
974
975
|
import tempfile
|
|
975
976
|
|
|
976
977
|
with tempfile.NamedTemporaryFile(
|
|
977
|
-
suffix=".
|
|
978
|
+
suffix=".plot", delete=False
|
|
978
979
|
) as tmp:
|
|
979
|
-
tmp.write(
|
|
980
|
+
tmp.write(plot_bytes)
|
|
980
981
|
tmp_path = tmp.name
|
|
981
982
|
try:
|
|
982
|
-
with ZipBundle(tmp_path, mode="r") as
|
|
983
|
+
with ZipBundle(tmp_path, mode="r") as plot_bundle:
|
|
983
984
|
# Try various preview paths
|
|
984
985
|
for preview_path in [
|
|
985
986
|
"exports/preview.png",
|
|
@@ -987,7 +988,7 @@ class WebEditor:
|
|
|
987
988
|
f"exports/{panel_id}.png",
|
|
988
989
|
]:
|
|
989
990
|
try:
|
|
990
|
-
img_data =
|
|
991
|
+
img_data = plot_bundle.read_bytes(
|
|
991
992
|
preview_path
|
|
992
993
|
)
|
|
993
994
|
img = Image.open(io.BytesIO(img_data))
|
|
@@ -1008,7 +1009,7 @@ class WebEditor:
|
|
|
1008
1009
|
|
|
1009
1010
|
os.unlink(tmp_path)
|
|
1010
1011
|
except Exception as e:
|
|
1011
|
-
print(f"Could not load
|
|
1012
|
+
print(f"Could not load plot {plot_path}: {e}")
|
|
1012
1013
|
continue
|
|
1013
1014
|
|
|
1014
1015
|
exported = {}
|
|
@@ -1084,26 +1085,26 @@ class WebEditor:
|
|
|
1084
1085
|
if fmt not in mime_types:
|
|
1085
1086
|
return f"Unsupported format: {fmt}", 400
|
|
1086
1087
|
|
|
1087
|
-
# For
|
|
1088
|
+
# For figure bundles, download the composed figure
|
|
1088
1089
|
if editor.panel_info:
|
|
1089
1090
|
bundle_path = editor.panel_info.get("bundle_path")
|
|
1090
|
-
|
|
1091
|
+
figure_dir = editor.panel_info.get("figure_dir")
|
|
1091
1092
|
figure_name = (
|
|
1092
1093
|
Path(bundle_path).stem
|
|
1093
1094
|
if bundle_path
|
|
1094
1095
|
else (
|
|
1095
|
-
Path(
|
|
1096
|
-
if
|
|
1096
|
+
Path(figure_dir).stem.replace(".figure", "")
|
|
1097
|
+
if figure_dir
|
|
1097
1098
|
else "figure"
|
|
1098
1099
|
)
|
|
1099
1100
|
)
|
|
1100
1101
|
|
|
1101
|
-
if bundle_path or
|
|
1102
|
+
if bundle_path or figure_dir:
|
|
1102
1103
|
import matplotlib
|
|
1103
1104
|
import numpy as np
|
|
1104
1105
|
from PIL import Image
|
|
1105
1106
|
|
|
1106
|
-
from scitex.
|
|
1107
|
+
from scitex.io import ZipBundle
|
|
1107
1108
|
|
|
1108
1109
|
matplotlib.use("Agg")
|
|
1109
1110
|
import json as json_module
|
|
@@ -1112,7 +1113,6 @@ class WebEditor:
|
|
|
1112
1113
|
|
|
1113
1114
|
# Always compose on-demand to ensure current panel state
|
|
1114
1115
|
# (existing exports in bundle may be stale or blank)
|
|
1115
|
-
|
|
1116
1116
|
# Read spec.json and layout.json for position overrides
|
|
1117
1117
|
spec = {}
|
|
1118
1118
|
layout_overrides = {}
|
|
@@ -1128,12 +1128,12 @@ class WebEditor:
|
|
|
1128
1128
|
pass
|
|
1129
1129
|
except:
|
|
1130
1130
|
pass
|
|
1131
|
-
elif
|
|
1132
|
-
spec_path = Path(
|
|
1131
|
+
elif figure_dir:
|
|
1132
|
+
spec_path = Path(figure_dir) / "spec.json"
|
|
1133
1133
|
if spec_path.exists():
|
|
1134
1134
|
with open(spec_path) as f:
|
|
1135
1135
|
spec = json_module.load(f)
|
|
1136
|
-
layout_path = Path(
|
|
1136
|
+
layout_path = Path(figure_dir) / "layout.json"
|
|
1137
1137
|
if layout_path.exists():
|
|
1138
1138
|
with open(layout_path) as f:
|
|
1139
1139
|
layout_overrides = json_module.load(f)
|
|
@@ -1185,7 +1185,7 @@ class WebEditor:
|
|
|
1185
1185
|
is_zip = False
|
|
1186
1186
|
panel_name = None
|
|
1187
1187
|
for idx, pp in enumerate(panel_paths):
|
|
1188
|
-
pp_name = Path(pp).stem.replace(".
|
|
1188
|
+
pp_name = Path(pp).stem.replace(".plot", "")
|
|
1189
1189
|
# Match exact name, or name contains panel_id pattern
|
|
1190
1190
|
# e.g., "panel_A_twinx" matches panel_id "A"
|
|
1191
1191
|
if (
|
|
@@ -1200,7 +1200,7 @@ class WebEditor:
|
|
|
1200
1200
|
panel_path = pp
|
|
1201
1201
|
panel_name = Path(
|
|
1202
1202
|
pp
|
|
1203
|
-
).name # e.g., "panel_A_twinx.
|
|
1203
|
+
).name # e.g., "panel_A_twinx.plot"
|
|
1204
1204
|
is_zip = (
|
|
1205
1205
|
panel_is_zip[idx]
|
|
1206
1206
|
if idx < len(panel_is_zip)
|
|
@@ -1246,7 +1246,7 @@ class WebEditor:
|
|
|
1246
1246
|
]
|
|
1247
1247
|
|
|
1248
1248
|
if is_zip:
|
|
1249
|
-
with ZipBundle(panel_path, mode="r") as
|
|
1249
|
+
with ZipBundle(panel_path, mode="r") as plot_bundle:
|
|
1250
1250
|
# Find PNG in exports (exclude hitmap, overview, thumbnails)
|
|
1251
1251
|
import zipfile
|
|
1252
1252
|
|
|
@@ -1265,12 +1265,12 @@ class WebEditor:
|
|
|
1265
1265
|
# Use first matching PNG
|
|
1266
1266
|
preview_path = png_files[0]
|
|
1267
1267
|
# Extract the path relative to .d directory
|
|
1268
|
-
if ".
|
|
1268
|
+
if ".plot/" in preview_path:
|
|
1269
1269
|
preview_path = preview_path.split(
|
|
1270
|
-
".
|
|
1270
|
+
".plot/"
|
|
1271
1271
|
)[-1]
|
|
1272
1272
|
try:
|
|
1273
|
-
img_data =
|
|
1273
|
+
img_data = plot_bundle.read_bytes(
|
|
1274
1274
|
preview_path
|
|
1275
1275
|
)
|
|
1276
1276
|
img = Image.open(
|
|
@@ -1288,8 +1288,8 @@ class WebEditor:
|
|
|
1288
1288
|
)
|
|
1289
1289
|
else:
|
|
1290
1290
|
# Directory-based pltz
|
|
1291
|
-
|
|
1292
|
-
exports_dir =
|
|
1291
|
+
plot_dir = Path(panel_path)
|
|
1292
|
+
exports_dir = plot_dir / "exports"
|
|
1293
1293
|
if exports_dir.exists():
|
|
1294
1294
|
for png_file in exports_dir.glob("*.png"):
|
|
1295
1295
|
name_lower = png_file.name.lower()
|
|
@@ -1417,7 +1417,7 @@ class WebEditor:
|
|
|
1417
1417
|
|
|
1418
1418
|
@app.route("/download_figz")
|
|
1419
1419
|
def download_figz():
|
|
1420
|
-
"""Download as
|
|
1420
|
+
"""Download as figure bundle (re-editable format)."""
|
|
1421
1421
|
try:
|
|
1422
1422
|
if not editor.panel_info:
|
|
1423
1423
|
return "No panel info available", 404
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
2
3
|
# File: ./src/scitex/vis/editor/flask_editor/renderer.py
|
|
3
4
|
"""Figure rendering for Flask editor - supports single and multi-axis figures."""
|
|
4
5
|
|
|
5
6
|
import base64
|
|
6
7
|
import io
|
|
7
|
-
from typing import Any, Dict, Optional, Tuple
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
8
9
|
|
|
9
10
|
import matplotlib
|
|
10
11
|
|
|
@@ -412,7 +413,7 @@ def _build_ax_to_rowcol_map(axes_spec: Dict[str, Any]) -> Dict[str, Tuple[int, i
|
|
|
412
413
|
positions.append((ax_id, pos[0], pos[1])) # (id, x, y)
|
|
413
414
|
|
|
414
415
|
if not positions:
|
|
415
|
-
return
|
|
416
|
+
return {ax_id: (0, 0) for ax_id in axes_spec}
|
|
416
417
|
|
|
417
418
|
# Get unique y-values (rows) - higher y = lower row number (top of figure)
|
|
418
419
|
y_values = sorted(set(round(p[2], 2) for p in positions), reverse=True)
|
|
@@ -734,10 +735,10 @@ def render_panel_preview(
|
|
|
734
735
|
panel_dir,
|
|
735
736
|
dark_mode: bool = False,
|
|
736
737
|
) -> Tuple[Optional[str], Optional[Dict[str, Any]], Optional[Dict[str, int]]]:
|
|
737
|
-
"""Render a panel from its
|
|
738
|
+
"""Render a panel from its plot bundle directory with dark mode support.
|
|
738
739
|
|
|
739
740
|
Args:
|
|
740
|
-
panel_dir: Path to the .
|
|
741
|
+
panel_dir: Path to the .plot panel directory
|
|
741
742
|
dark_mode: Whether to render with dark mode colors
|
|
742
743
|
|
|
743
744
|
Returns:
|
|
@@ -763,7 +764,7 @@ def render_panel_preview(
|
|
|
763
764
|
if not spec_path.exists():
|
|
764
765
|
return None, None, None
|
|
765
766
|
|
|
766
|
-
with open(spec_path) as f:
|
|
767
|
+
with open(spec_path, "r") as f:
|
|
767
768
|
metadata = json.load(f)
|
|
768
769
|
|
|
769
770
|
# Load CSV data
|
|
@@ -781,7 +782,7 @@ def render_panel_preview(
|
|
|
781
782
|
style_path = panel_dir / "style.json"
|
|
782
783
|
overrides = {}
|
|
783
784
|
if style_path.exists():
|
|
784
|
-
with open(style_path) as f:
|
|
785
|
+
with open(style_path, "r") as f:
|
|
785
786
|
style = json.load(f)
|
|
786
787
|
# Convert style to overrides format
|
|
787
788
|
size = style.get("size", {})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* =============================================================================
|
|
2
|
-
* Unified Panel Canvas (matches
|
|
2
|
+
* Unified Panel Canvas (matches figure export layout)
|
|
3
3
|
* Exactly matches SciTeX Cloud's .plot-preview-area styling
|
|
4
4
|
* ============================================================================= */
|
|
5
5
|
.panel-canvas {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
background-image: none !important;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/* Unified canvas - panels appear as single figure matching
|
|
33
|
+
/* Unified canvas - panels appear as single figure matching figure export */
|
|
34
34
|
.panel-canvas-item {
|
|
35
35
|
position: absolute;
|
|
36
36
|
user-select: none;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* =============================================================================
|
|
2
|
-
* Panel Grid View (for multi-panel
|
|
2
|
+
* Panel Grid View (for multi-panel figure bundles)
|
|
3
3
|
* ============================================================================= */
|
|
4
4
|
.panel-grid-section {
|
|
5
5
|
width: 100%;
|
|
@@ -125,10 +125,10 @@ function downloadFile(path, format) {
|
|
|
125
125
|
// Panel API (Multi-panel figures)
|
|
126
126
|
// ============================================================================
|
|
127
127
|
|
|
128
|
-
// Helper to normalize panel names for comparison (strip .
|
|
128
|
+
// Helper to normalize panel names for comparison (strip .plot extensions)
|
|
129
129
|
function normalizePanelName(name) {
|
|
130
130
|
if (typeof name !== 'string') return '';
|
|
131
|
-
return name.replace('.
|
|
131
|
+
return name.replace('.plot', '').replace('.plot', '');
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function loadPanelForEditing(panelName) {
|
|
@@ -136,7 +136,7 @@ function loadPanelForEditing(panelName) {
|
|
|
136
136
|
|
|
137
137
|
// Find panel index from panel name
|
|
138
138
|
// Handle both formats: array of strings ["A", "B"] or array of objects [{name: "A"}, ...]
|
|
139
|
-
// Also handle extension mismatches (e.g., "A" vs "A.
|
|
139
|
+
// Also handle extension mismatches (e.g., "A" vs "A.plot")
|
|
140
140
|
let panelIndex = -1;
|
|
141
141
|
const normalizedSearch = normalizePanelName(panelName);
|
|
142
142
|
|
|
@@ -285,4 +285,3 @@ function setAutoUpdateInterval() {
|
|
|
285
285
|
}, intervalMs);
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
-
|
|
@@ -78,11 +78,11 @@ async function loadInitialPreview() {
|
|
|
78
78
|
updateOverlay();
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// Handle multi-panel
|
|
81
|
+
// Handle multi-panel figure bundles
|
|
82
82
|
if (data.panel_info && data.panel_info.panels) {
|
|
83
83
|
panelData = data.panel_info;
|
|
84
84
|
currentPanelIndex = data.panel_info.current_index || 0;
|
|
85
|
-
console.log('Multi-panel
|
|
85
|
+
console.log('Multi-panel figure detected:', panelData.panels.length, 'panels');
|
|
86
86
|
loadPanelGrid();
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -129,7 +129,7 @@ async function loadPanelGrid() {
|
|
|
129
129
|
const canvasEl = document.getElementById('panel-canvas');
|
|
130
130
|
canvasEl.innerHTML = '';
|
|
131
131
|
|
|
132
|
-
// Use
|
|
132
|
+
// Use figure layout to position panels as unified canvas (matching export)
|
|
133
133
|
const hasLayout = data.layout && Object.keys(data.layout).length > 0;
|
|
134
134
|
|
|
135
135
|
// Calculate scale factor: convert mm to pixels
|
|
@@ -162,7 +162,7 @@ async function loadPanelGrid() {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
// Use
|
|
165
|
+
// Use figure layout for positioning (unified canvas like export)
|
|
166
166
|
let pos, posMm;
|
|
167
167
|
if (panel.layout && panel.layout.position && panel.layout.size) {
|
|
168
168
|
const x_mm = panel.layout.position.x_mm || 0;
|
|
@@ -177,7 +177,7 @@ async function loadPanelGrid() {
|
|
|
177
177
|
};
|
|
178
178
|
posMm = { x_mm, y_mm, width_mm, height_mm };
|
|
179
179
|
} else {
|
|
180
|
-
// Fallback grid layout if no
|
|
180
|
+
// Fallback grid layout if no figure layout
|
|
181
181
|
const cols = Math.ceil(Math.sqrt(data.panels.length));
|
|
182
182
|
const baseWidth = 220, baseHeight = 180, padding = 15;
|
|
183
183
|
const col = idx % cols;
|