scitex 2.8.1__py3-none-any.whl → 2.10.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 +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/edit/panel_loader.py +1 -1
- scitex/fig/io/_bundle.py +7 -7
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -32
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +168 -97
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/RECORD +412 -97
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2025-12-14 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/panel_loader.py
|
|
4
|
+
|
|
5
|
+
"""Panel data loading for figure editor."""
|
|
6
|
+
|
|
7
|
+
import io
|
|
8
|
+
import json as json_module
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Optional, Union
|
|
11
|
+
|
|
12
|
+
__all__ = ["load_panel_data"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_panel_data(
|
|
16
|
+
panel_path: Union[Path, str], is_zip: bool = None
|
|
17
|
+
) -> Optional[Dict[str, Any]]:
|
|
18
|
+
"""
|
|
19
|
+
Load panel data from either a .pltz.d directory or a .pltz zip file.
|
|
20
|
+
|
|
21
|
+
Handles both formats transparently using in-memory reading for zips.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
panel_path : Path or str
|
|
26
|
+
Path to .pltz.d directory or .pltz zip file
|
|
27
|
+
is_zip : bool, optional
|
|
28
|
+
If True, treat as zip file. If False, treat as directory.
|
|
29
|
+
If None, auto-detect based on path suffix and existence.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
dict or None
|
|
34
|
+
Dictionary with keys: metadata, csv_data, png_bytes, hitmap_bytes, img_size
|
|
35
|
+
For directories, also includes: json_path, png_path, hitmap_path
|
|
36
|
+
Returns None if panel cannot be loaded
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
panel_path = Path(panel_path)
|
|
40
|
+
|
|
41
|
+
# Auto-detect if not specified
|
|
42
|
+
if is_zip is None:
|
|
43
|
+
spath = str(panel_path)
|
|
44
|
+
if spath.endswith(".pltz") and not spath.endswith(".pltz.d"):
|
|
45
|
+
is_zip = panel_path.is_file()
|
|
46
|
+
else:
|
|
47
|
+
is_zip = False
|
|
48
|
+
|
|
49
|
+
if is_zip:
|
|
50
|
+
return _load_from_zip(panel_path)
|
|
51
|
+
else:
|
|
52
|
+
return _load_from_directory(panel_path)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _load_from_zip(panel_path: Path) -> Optional[Dict[str, Any]]:
|
|
56
|
+
"""Load panel data from a .pltz zip file."""
|
|
57
|
+
from PIL import Image
|
|
58
|
+
|
|
59
|
+
from scitex.fts import FTS as ZipBundle
|
|
60
|
+
|
|
61
|
+
if not panel_path.exists():
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
with ZipBundle(panel_path, mode="r") as zb:
|
|
66
|
+
# Load spec.json for metadata
|
|
67
|
+
try:
|
|
68
|
+
metadata = zb.read_json("spec.json")
|
|
69
|
+
except FileNotFoundError:
|
|
70
|
+
metadata = {}
|
|
71
|
+
|
|
72
|
+
# Load style.json if exists
|
|
73
|
+
try:
|
|
74
|
+
style = zb.read_json("style.json")
|
|
75
|
+
metadata["style"] = style
|
|
76
|
+
except FileNotFoundError:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
# Find and read PNG
|
|
80
|
+
png_bytes = None
|
|
81
|
+
for name in zb.namelist():
|
|
82
|
+
if (
|
|
83
|
+
name.endswith(".png")
|
|
84
|
+
and "_hitmap" not in name
|
|
85
|
+
and "_overview" not in name
|
|
86
|
+
):
|
|
87
|
+
if "exports/" in name:
|
|
88
|
+
png_bytes = zb.read_bytes(name)
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
# If no PNG in exports/, try root level
|
|
92
|
+
if not png_bytes:
|
|
93
|
+
for name in zb.namelist():
|
|
94
|
+
if (
|
|
95
|
+
name.endswith(".png")
|
|
96
|
+
and "_hitmap" not in name
|
|
97
|
+
and "_overview" not in name
|
|
98
|
+
):
|
|
99
|
+
png_bytes = zb.read_bytes(name)
|
|
100
|
+
break
|
|
101
|
+
|
|
102
|
+
# Get image size
|
|
103
|
+
img_size = None
|
|
104
|
+
if png_bytes:
|
|
105
|
+
img = Image.open(io.BytesIO(png_bytes))
|
|
106
|
+
img_size = {"width": img.size[0], "height": img.size[1]}
|
|
107
|
+
img.close()
|
|
108
|
+
|
|
109
|
+
# Find and read hitmap
|
|
110
|
+
hitmap_bytes = None
|
|
111
|
+
for name in zb.namelist():
|
|
112
|
+
if "_hitmap.png" in name:
|
|
113
|
+
hitmap_bytes = zb.read_bytes(name)
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
# Load geometry_px.json if available
|
|
117
|
+
geometry_data = None
|
|
118
|
+
try:
|
|
119
|
+
geometry_data = zb.read_json("cache/geometry_px.json")
|
|
120
|
+
except FileNotFoundError:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
"metadata": metadata,
|
|
125
|
+
"png_bytes": png_bytes,
|
|
126
|
+
"hitmap_bytes": hitmap_bytes,
|
|
127
|
+
"img_size": img_size,
|
|
128
|
+
"geometry_data": geometry_data,
|
|
129
|
+
"is_zip": True,
|
|
130
|
+
}
|
|
131
|
+
except Exception as e:
|
|
132
|
+
print(f"Error loading panel zip {panel_path}: {e}")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _load_from_directory(panel_path: Path) -> Optional[Dict[str, Any]]:
|
|
137
|
+
"""Load panel data from a .pltz.d directory."""
|
|
138
|
+
|
|
139
|
+
panel_dir = panel_path
|
|
140
|
+
if not panel_dir.exists():
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
# Check for layered vs legacy format
|
|
144
|
+
spec_path = panel_dir / "spec.json"
|
|
145
|
+
if spec_path.exists():
|
|
146
|
+
return _load_layered_directory(panel_dir)
|
|
147
|
+
else:
|
|
148
|
+
return _load_legacy_directory(panel_dir)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _load_layered_directory(panel_dir: Path) -> Dict[str, Any]:
|
|
152
|
+
"""Load panel data from layered format directory."""
|
|
153
|
+
import scitex as stx
|
|
154
|
+
from scitex.plt.io import load_layered_pltz_bundle
|
|
155
|
+
|
|
156
|
+
bundle_data = load_layered_pltz_bundle(panel_dir)
|
|
157
|
+
metadata = bundle_data.get("merged", {})
|
|
158
|
+
|
|
159
|
+
# Find CSV
|
|
160
|
+
csv_data = None
|
|
161
|
+
for f in panel_dir.glob("*.csv"):
|
|
162
|
+
csv_data = stx.io.load(f)
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
# Find exports - prefer PNG over SVG (PIL can't open SVG)
|
|
166
|
+
png_path = None
|
|
167
|
+
svg_path = None
|
|
168
|
+
hitmap_path = None
|
|
169
|
+
exports_dir = panel_dir / "exports"
|
|
170
|
+
if exports_dir.exists():
|
|
171
|
+
for f in exports_dir.iterdir():
|
|
172
|
+
name = f.name
|
|
173
|
+
if name.endswith("_hitmap.png"):
|
|
174
|
+
hitmap_path = f
|
|
175
|
+
elif (
|
|
176
|
+
name.endswith(".png")
|
|
177
|
+
and "_hitmap" not in name
|
|
178
|
+
and "_overview" not in name
|
|
179
|
+
):
|
|
180
|
+
png_path = f
|
|
181
|
+
elif name.endswith(".svg") and "_hitmap" not in name and svg_path is None:
|
|
182
|
+
svg_path = f
|
|
183
|
+
|
|
184
|
+
if png_path is None:
|
|
185
|
+
png_path = svg_path
|
|
186
|
+
|
|
187
|
+
# Load geometry_px.json if available
|
|
188
|
+
geometry_data = None
|
|
189
|
+
geometry_path = panel_dir / "cache" / "geometry_px.json"
|
|
190
|
+
if geometry_path.exists():
|
|
191
|
+
with open(geometry_path) as f:
|
|
192
|
+
geometry_data = json_module.load(f)
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
"json_path": panel_dir / "spec.json",
|
|
196
|
+
"metadata": metadata,
|
|
197
|
+
"csv_data": csv_data,
|
|
198
|
+
"png_path": png_path,
|
|
199
|
+
"hitmap_path": hitmap_path,
|
|
200
|
+
"geometry_data": geometry_data,
|
|
201
|
+
"is_zip": False,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _load_legacy_directory(panel_dir: Path) -> Optional[Dict[str, Any]]:
|
|
206
|
+
"""Load panel data from legacy format directory."""
|
|
207
|
+
import scitex as stx
|
|
208
|
+
|
|
209
|
+
json_path = None
|
|
210
|
+
csv_data = None
|
|
211
|
+
png_path = None
|
|
212
|
+
hitmap_path = None
|
|
213
|
+
|
|
214
|
+
for f in panel_dir.iterdir():
|
|
215
|
+
name = f.name
|
|
216
|
+
if name.endswith(".json") and not name.endswith(".manual.json"):
|
|
217
|
+
json_path = f
|
|
218
|
+
elif name.endswith(".csv"):
|
|
219
|
+
csv_data = stx.io.load(f)
|
|
220
|
+
elif name.endswith("_hitmap.png"):
|
|
221
|
+
hitmap_path = f
|
|
222
|
+
elif name.endswith(".svg") and "_hitmap" not in name:
|
|
223
|
+
png_path = f
|
|
224
|
+
elif (
|
|
225
|
+
name.endswith(".png") and "_hitmap" not in name and "_overview" not in name
|
|
226
|
+
):
|
|
227
|
+
if png_path is None:
|
|
228
|
+
png_path = f
|
|
229
|
+
|
|
230
|
+
if json_path is None:
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
with open(json_path) as f:
|
|
234
|
+
metadata = json_module.load(f)
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
"json_path": json_path,
|
|
238
|
+
"metadata": metadata,
|
|
239
|
+
"csv_data": csv_data,
|
|
240
|
+
"png_path": png_path,
|
|
241
|
+
"hitmap_path": hitmap_path,
|
|
242
|
+
"is_zip": False,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# EOF
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2025-12-14 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/path_resolver.py
|
|
4
|
+
|
|
5
|
+
"""Basic path resolution for figure files."""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional, Tuple
|
|
9
|
+
|
|
10
|
+
__all__ = ["resolve_figure_paths"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def resolve_figure_paths(path: Path) -> Tuple[Path, Optional[Path], Optional[Path]]:
|
|
14
|
+
"""
|
|
15
|
+
Resolve JSON, CSV, and PNG paths from any input file path.
|
|
16
|
+
|
|
17
|
+
Handles two patterns:
|
|
18
|
+
1. Flat (sibling): path/to/figure.{json,csv,png}
|
|
19
|
+
2. Organized (subdirs): path/to/{json,csv,png}/figure.{ext}
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
path : Path
|
|
24
|
+
Input path (can be JSON, CSV, or PNG)
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
tuple
|
|
29
|
+
(json_path, csv_path, png_path) - csv_path/png_path may be None if not found
|
|
30
|
+
"""
|
|
31
|
+
path = Path(path)
|
|
32
|
+
stem = path.stem
|
|
33
|
+
parent = path.parent
|
|
34
|
+
|
|
35
|
+
# Check if this is organized pattern (parent is json/, csv/, png/)
|
|
36
|
+
if parent.name in ("json", "csv", "png"):
|
|
37
|
+
base_dir = parent.parent
|
|
38
|
+
json_path = base_dir / "json" / f"{stem}.json"
|
|
39
|
+
csv_path = base_dir / "csv" / f"{stem}.csv"
|
|
40
|
+
png_path = base_dir / "png" / f"{stem}.png"
|
|
41
|
+
else:
|
|
42
|
+
# Flat pattern - sibling files
|
|
43
|
+
json_path = parent / f"{stem}.json"
|
|
44
|
+
csv_path = parent / f"{stem}.csv"
|
|
45
|
+
png_path = parent / f"{stem}.png"
|
|
46
|
+
|
|
47
|
+
# If input was .manual.json, get base json
|
|
48
|
+
if stem.endswith(".manual"):
|
|
49
|
+
base_stem = stem[:-7] # Remove '.manual'
|
|
50
|
+
if parent.name == "json":
|
|
51
|
+
json_path = parent / f"{base_stem}.json"
|
|
52
|
+
csv_path = parent.parent / "csv" / f"{base_stem}.csv"
|
|
53
|
+
png_path = parent.parent / "png" / f"{base_stem}.png"
|
|
54
|
+
else:
|
|
55
|
+
json_path = parent / f"{base_stem}.json"
|
|
56
|
+
csv_path = parent / f"{base_stem}.csv"
|
|
57
|
+
png_path = parent / f"{base_stem}.png"
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
json_path,
|
|
61
|
+
csv_path if csv_path.exists() else None,
|
|
62
|
+
png_path if png_path.exists() else None,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# EOF
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# File: ./src/scitex/vis/editor/_defaults.py
|
|
3
|
+
"""Default style settings for SciTeX visual editor."""
|
|
4
|
+
|
|
5
|
+
from scitex.plt.styles import get_default_dpi
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_scitex_defaults():
|
|
9
|
+
"""
|
|
10
|
+
Get default style values based on SciTeX publication standards.
|
|
11
|
+
|
|
12
|
+
Returns
|
|
13
|
+
-------
|
|
14
|
+
dict
|
|
15
|
+
Dictionary of default style settings
|
|
16
|
+
"""
|
|
17
|
+
return {
|
|
18
|
+
# Font sizes (from _configure_mpl.py)
|
|
19
|
+
"fontsize": 7, # Base font size for publication
|
|
20
|
+
"title_fontsize": 8, # Title size
|
|
21
|
+
"axis_fontsize": 7, # Axis label size
|
|
22
|
+
"tick_fontsize": 7, # Tick label size
|
|
23
|
+
"legend_fontsize": 6, # Legend font size
|
|
24
|
+
# Line settings (in points, 1mm = 2.83465pt)
|
|
25
|
+
"linewidth": 0.57, # Default line width (0.2mm)
|
|
26
|
+
# Tick settings
|
|
27
|
+
"n_ticks": 4, # Number of ticks
|
|
28
|
+
"tick_length": 0.8, # Tick length in mm
|
|
29
|
+
"tick_width": 0.2, # Tick width in mm
|
|
30
|
+
"tick_direction": "out", # Tick direction
|
|
31
|
+
# Axes settings
|
|
32
|
+
"axis_width": 0.2, # Spine/axes line width in mm
|
|
33
|
+
"hide_top_spine": True, # Hide top spine
|
|
34
|
+
"hide_right_spine": True, # Hide right spine
|
|
35
|
+
# Grid settings
|
|
36
|
+
"grid": False, # Grid off by default
|
|
37
|
+
"grid_linewidth": 0.6,
|
|
38
|
+
"grid_alpha": 0.3,
|
|
39
|
+
# Figure settings
|
|
40
|
+
"dpi": get_default_dpi(), # From SCITEX_STYLE.yaml
|
|
41
|
+
"fig_size": [3.15, 2.68], # Default figure size in inches
|
|
42
|
+
# Colors (SciTeX defaults)
|
|
43
|
+
"facecolor": "#ffffff", # White background
|
|
44
|
+
"transparent": True, # Transparent background
|
|
45
|
+
# Legend
|
|
46
|
+
"legend_visible": True,
|
|
47
|
+
"legend_frameon": False,
|
|
48
|
+
"legend_loc": "best",
|
|
49
|
+
# Font family
|
|
50
|
+
"font_family": "Arial", # Sans-serif for publication
|
|
51
|
+
# Annotations
|
|
52
|
+
"annotations": [],
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_scitex_colors():
|
|
57
|
+
"""
|
|
58
|
+
Get SciTeX color palette.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
dict
|
|
63
|
+
Dictionary of named colors with hex values
|
|
64
|
+
"""
|
|
65
|
+
# Based on scitex.plt.color.PARAMS
|
|
66
|
+
return {
|
|
67
|
+
"blue": "#1f77b4",
|
|
68
|
+
"orange": "#ff7f0e",
|
|
69
|
+
"green": "#2ca02c",
|
|
70
|
+
"red": "#d62728",
|
|
71
|
+
"purple": "#9467bd",
|
|
72
|
+
"brown": "#8c564b",
|
|
73
|
+
"pink": "#e377c2",
|
|
74
|
+
"gray": "#7f7f7f",
|
|
75
|
+
"olive": "#bcbd22",
|
|
76
|
+
"cyan": "#17becf",
|
|
77
|
+
# Additional publication colors
|
|
78
|
+
"black": "#000000",
|
|
79
|
+
"white": "#ffffff",
|
|
80
|
+
"dark_gray": "#404040",
|
|
81
|
+
"light_gray": "#d0d0d0",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def extract_defaults_from_metadata(metadata):
|
|
86
|
+
"""
|
|
87
|
+
Extract style defaults from loaded figure metadata.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
metadata : dict
|
|
92
|
+
Figure metadata loaded from JSON
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
dict
|
|
97
|
+
Dictionary of style values extracted from metadata
|
|
98
|
+
"""
|
|
99
|
+
defaults = get_scitex_defaults()
|
|
100
|
+
|
|
101
|
+
if not metadata:
|
|
102
|
+
return defaults
|
|
103
|
+
|
|
104
|
+
# Extract from scitex section (new format)
|
|
105
|
+
scitex_meta = metadata.get("scitex", {})
|
|
106
|
+
style_mm = scitex_meta.get("style_mm", {})
|
|
107
|
+
|
|
108
|
+
# Font sizes from style_mm
|
|
109
|
+
if "axis_font_size_pt" in style_mm:
|
|
110
|
+
defaults["axis_fontsize"] = style_mm["axis_font_size_pt"]
|
|
111
|
+
if "tick_font_size_pt" in style_mm:
|
|
112
|
+
defaults["tick_fontsize"] = style_mm["tick_font_size_pt"]
|
|
113
|
+
if "title_font_size_pt" in style_mm:
|
|
114
|
+
defaults["title_fontsize"] = style_mm["title_font_size_pt"]
|
|
115
|
+
if "legend_font_size_pt" in style_mm:
|
|
116
|
+
defaults["legend_fontsize"] = style_mm["legend_font_size_pt"]
|
|
117
|
+
|
|
118
|
+
# Line/axis thickness from metadata (in mm)
|
|
119
|
+
if "trace_thickness_mm" in style_mm:
|
|
120
|
+
defaults["linewidth"] = style_mm["trace_thickness_mm"] * 2.83465 # mm to pt
|
|
121
|
+
if "axis_thickness_mm" in style_mm:
|
|
122
|
+
defaults["axis_width"] = style_mm["axis_thickness_mm"]
|
|
123
|
+
if "tick_length_mm" in style_mm:
|
|
124
|
+
defaults["tick_length"] = style_mm["tick_length_mm"]
|
|
125
|
+
if "tick_thickness_mm" in style_mm:
|
|
126
|
+
defaults["tick_width"] = style_mm["tick_thickness_mm"]
|
|
127
|
+
if "n_ticks" in style_mm:
|
|
128
|
+
defaults["n_ticks"] = style_mm["n_ticks"]
|
|
129
|
+
|
|
130
|
+
# Dimensions from metadata (support both old and new formats)
|
|
131
|
+
dimensions = metadata.get("dimensions", {})
|
|
132
|
+
if "dpi" in dimensions:
|
|
133
|
+
defaults["dpi"] = dimensions["dpi"]
|
|
134
|
+
if "figure_size_inch" in dimensions:
|
|
135
|
+
defaults["fig_size"] = dimensions["figure_size_inch"]
|
|
136
|
+
|
|
137
|
+
# New format: size.width_mm, size.height_mm, size.dpi
|
|
138
|
+
size = metadata.get("size", {})
|
|
139
|
+
if "dpi" in size:
|
|
140
|
+
defaults["dpi"] = size["dpi"]
|
|
141
|
+
if "width_mm" in size and "height_mm" in size:
|
|
142
|
+
# Convert mm to inches
|
|
143
|
+
width_inch = size["width_mm"] / 25.4
|
|
144
|
+
height_inch = size["height_mm"] / 25.4
|
|
145
|
+
defaults["fig_size"] = [width_inch, height_inch]
|
|
146
|
+
|
|
147
|
+
# Axis labels from metadata
|
|
148
|
+
if "xlabel" in metadata:
|
|
149
|
+
defaults["xlabel"] = metadata["xlabel"]
|
|
150
|
+
if "ylabel" in metadata:
|
|
151
|
+
defaults["ylabel"] = metadata["ylabel"]
|
|
152
|
+
if "title" in metadata:
|
|
153
|
+
defaults["title"] = metadata["title"]
|
|
154
|
+
|
|
155
|
+
# Try to extract from axes if present
|
|
156
|
+
axes = metadata.get("axes", {})
|
|
157
|
+
if isinstance(axes, dict):
|
|
158
|
+
# New format: {'x': {'label': ...}, 'y': {'label': ...}}
|
|
159
|
+
x_axis = axes.get("x", {})
|
|
160
|
+
y_axis = axes.get("y", {})
|
|
161
|
+
if "label" in x_axis:
|
|
162
|
+
unit = x_axis.get("unit", "")
|
|
163
|
+
label = x_axis["label"]
|
|
164
|
+
if unit:
|
|
165
|
+
defaults["xlabel"] = f"{label} [{unit}]"
|
|
166
|
+
else:
|
|
167
|
+
defaults["xlabel"] = label
|
|
168
|
+
if "label" in y_axis:
|
|
169
|
+
unit = y_axis.get("unit", "")
|
|
170
|
+
label = y_axis["label"]
|
|
171
|
+
if unit:
|
|
172
|
+
defaults["ylabel"] = f"{label} [{unit}]"
|
|
173
|
+
else:
|
|
174
|
+
defaults["ylabel"] = label
|
|
175
|
+
# Extract axis limits
|
|
176
|
+
if "lim" in x_axis:
|
|
177
|
+
defaults["xlim"] = x_axis["lim"]
|
|
178
|
+
if "lim" in y_axis:
|
|
179
|
+
defaults["ylim"] = y_axis["lim"]
|
|
180
|
+
elif isinstance(axes, list) and len(axes) > 0:
|
|
181
|
+
# Legacy format: list of axes
|
|
182
|
+
ax = axes[0]
|
|
183
|
+
if "xlabel" in ax:
|
|
184
|
+
defaults["xlabel"] = ax["xlabel"]
|
|
185
|
+
if "ylabel" in ax:
|
|
186
|
+
defaults["ylabel"] = ax["ylabel"]
|
|
187
|
+
if "title" in ax:
|
|
188
|
+
defaults["title"] = ax["title"]
|
|
189
|
+
if "grid" in ax:
|
|
190
|
+
defaults["grid"] = ax["grid"]
|
|
191
|
+
# Extract axis limits from list format
|
|
192
|
+
if "xlim" in ax:
|
|
193
|
+
defaults["xlim"] = ax["xlim"]
|
|
194
|
+
if "ylim" in ax:
|
|
195
|
+
defaults["ylim"] = ax["ylim"]
|
|
196
|
+
|
|
197
|
+
# Extract traces information - check multiple possible locations
|
|
198
|
+
traces = metadata.get("traces", [])
|
|
199
|
+
|
|
200
|
+
# Also check axes[].lines (pltz bundle format)
|
|
201
|
+
if not traces and isinstance(axes, list) and len(axes) > 0:
|
|
202
|
+
ax = axes[0]
|
|
203
|
+
lines = ax.get("lines", [])
|
|
204
|
+
if lines:
|
|
205
|
+
traces = lines
|
|
206
|
+
|
|
207
|
+
# Also check hit_regions.color_map for trace info
|
|
208
|
+
if not traces:
|
|
209
|
+
hit_regions = metadata.get("hit_regions", {})
|
|
210
|
+
color_map = hit_regions.get("color_map", {})
|
|
211
|
+
if color_map:
|
|
212
|
+
traces = []
|
|
213
|
+
for trace_id, trace_info in color_map.items():
|
|
214
|
+
traces.append(
|
|
215
|
+
{
|
|
216
|
+
"id": trace_id,
|
|
217
|
+
"label": trace_info.get("label", f"Trace {trace_id}"),
|
|
218
|
+
"type": trace_info.get("type", "line"),
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if traces:
|
|
223
|
+
defaults["traces"] = traces
|
|
224
|
+
|
|
225
|
+
# Extract legend information from multiple possible locations
|
|
226
|
+
legend = metadata.get("legend", {})
|
|
227
|
+
|
|
228
|
+
# Also check selectable_regions.axes[0].legend (pltz bundle format)
|
|
229
|
+
if not legend:
|
|
230
|
+
selectable = metadata.get("selectable_regions", {})
|
|
231
|
+
sel_axes = selectable.get("axes", [])
|
|
232
|
+
if sel_axes and len(sel_axes) > 0:
|
|
233
|
+
legend = sel_axes[0].get("legend", {})
|
|
234
|
+
|
|
235
|
+
if legend:
|
|
236
|
+
defaults["legend_visible"] = legend.get("visible", True)
|
|
237
|
+
defaults["legend_frameon"] = legend.get("frameon", False)
|
|
238
|
+
# Support both old format (loc) and new format (location)
|
|
239
|
+
loc = legend.get("location") or legend.get("loc", "best")
|
|
240
|
+
# Convert numeric legend loc to string (matplotlib accepts both but GUI needs string)
|
|
241
|
+
defaults["legend_loc"] = _normalize_legend_loc(loc)
|
|
242
|
+
# Extract fontsize if present
|
|
243
|
+
if "fontsize" in legend:
|
|
244
|
+
defaults["legend_fontsize"] = legend["fontsize"]
|
|
245
|
+
# Extract ncols if present
|
|
246
|
+
if "ncols" in legend:
|
|
247
|
+
defaults["legend_ncols"] = legend["ncols"]
|
|
248
|
+
# Extract title if present
|
|
249
|
+
if "title" in legend:
|
|
250
|
+
defaults["legend_title"] = legend["title"]
|
|
251
|
+
|
|
252
|
+
# Extract legend entries if present
|
|
253
|
+
entries = legend.get("entries", [])
|
|
254
|
+
if entries and not traces:
|
|
255
|
+
# Use legend entries as trace labels
|
|
256
|
+
defaults["traces"] = [
|
|
257
|
+
{"label": e.get("text", f"Trace {i}")} for i, e in enumerate(entries)
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
return defaults
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _normalize_legend_loc(loc):
|
|
264
|
+
"""Convert legend location to string format for GUI compatibility.
|
|
265
|
+
|
|
266
|
+
Matplotlib accepts both numeric codes and string locations for legends.
|
|
267
|
+
This function ensures consistency by converting numeric codes to strings.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
loc : int or str
|
|
272
|
+
Legend location (numeric or string)
|
|
273
|
+
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
str
|
|
277
|
+
String representation of legend location
|
|
278
|
+
"""
|
|
279
|
+
if isinstance(loc, str):
|
|
280
|
+
return loc
|
|
281
|
+
|
|
282
|
+
# Numeric to string mapping (matplotlib legend location codes)
|
|
283
|
+
loc_map = {
|
|
284
|
+
0: "best",
|
|
285
|
+
1: "upper right",
|
|
286
|
+
2: "upper left",
|
|
287
|
+
3: "lower left",
|
|
288
|
+
4: "lower right",
|
|
289
|
+
5: "right",
|
|
290
|
+
6: "center left",
|
|
291
|
+
7: "center right",
|
|
292
|
+
8: "lower center",
|
|
293
|
+
9: "upper center",
|
|
294
|
+
10: "center",
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return loc_map.get(loc, "best")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# EOF
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_fig/_editor/_gui/__init__.py
|
|
4
|
+
|
|
5
|
+
"""GUI-based FTS bundle editors (Flask only)."""
|
|
6
|
+
|
|
7
|
+
from ._flask_editor import WebEditor
|
|
8
|
+
|
|
9
|
+
__all__ = ["WebEditor"]
|
|
10
|
+
|
|
11
|
+
# EOF
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# File: ./src/scitex/vis/editor/flask_editor/__init__.py
|
|
3
|
+
"""Flask-based web editor for SciTeX figures."""
|
|
4
|
+
|
|
5
|
+
from ._core import WebEditor
|
|
6
|
+
from ._plotter import plot_from_csv
|
|
7
|
+
from ._renderer import render_preview_with_bboxes
|
|
8
|
+
from ._utils import check_port_available, find_available_port, kill_process_on_port
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"WebEditor",
|
|
12
|
+
"find_available_port",
|
|
13
|
+
"kill_process_on_port",
|
|
14
|
+
"check_port_available",
|
|
15
|
+
"render_preview_with_bboxes",
|
|
16
|
+
"plot_from_csv",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# EOF
|