scitex 2.7.3__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.
Files changed (563) hide show
  1. scitex/__init__.py +15 -7
  2. scitex/__version__.py +1 -2
  3. scitex/_install_guide.py +250 -0
  4. scitex/_optional_deps.py +206 -39
  5. scitex/ai/_gen_ai/_Groq.py +2 -4
  6. scitex/ai/_gen_ai/_OpenAI.py +5 -2
  7. scitex/ai/_gen_ai/_Perplexity.py +20 -6
  8. scitex/audio/__init__.py +24 -15
  9. scitex/audio/_cross_process_lock.py +139 -0
  10. scitex/audio/_mcp_handlers.py +256 -0
  11. scitex/audio/_mcp_tool_schemas.py +203 -0
  12. scitex/audio/engines/elevenlabs_engine.py +5 -2
  13. scitex/audio/mcp_server.py +98 -457
  14. scitex/bridge/__init__.py +30 -19
  15. scitex/bridge/_figrecipe.py +245 -0
  16. scitex/bridge/_helpers.py +2 -1
  17. scitex/bridge/_plt_vis.py +23 -10
  18. scitex/bridge/_stats_plt.py +18 -5
  19. scitex/bridge/_stats_vis.py +16 -2
  20. scitex/browser/__init__.py +84 -44
  21. scitex/browser/automation/__init__.py +5 -1
  22. scitex/browser/core/BrowserMixin.py +17 -4
  23. scitex/browser/core/__init__.py +11 -2
  24. scitex/browser/remote/CaptchaHandler.py +1 -1
  25. scitex/browser/remote/ZenRowsAPIClient.py +1 -1
  26. scitex/capture/grid.py +487 -0
  27. scitex/capture/mcp_handlers.py +401 -0
  28. scitex/capture/mcp_tool_defs.py +192 -0
  29. scitex/capture/mcp_tools.py +241 -0
  30. scitex/capture/mcp_utils.py +30 -0
  31. scitex/cli/convert.py +421 -0
  32. scitex/cli/main.py +6 -4
  33. scitex/datetime/__init__.py +46 -0
  34. scitex/datetime/_linspace.py +100 -0
  35. scitex/datetime/_normalize_timestamp.py +306 -0
  36. scitex/db/_delete_duplicates.py +4 -4
  37. scitex/db/_sqlite3/_delete_duplicates.py +11 -2
  38. scitex/dev/plt/__init__.py +61 -62
  39. scitex/dev/plt/demo_plotters/__init__.py +0 -0
  40. scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
  41. scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
  42. scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
  43. scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
  44. scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
  45. scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
  46. scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
  47. scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
  48. scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
  49. scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
  50. scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
  51. scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
  52. scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
  53. scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
  54. scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
  55. scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
  56. scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
  57. scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
  58. scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
  59. scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
  60. scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
  61. scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
  62. scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
  63. scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
  64. scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
  65. scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
  66. scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
  67. scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
  68. scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
  69. scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
  70. scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
  71. scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
  72. scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
  73. scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
  74. scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
  75. scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
  76. scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
  77. scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
  78. scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
  79. scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
  80. scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
  81. scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
  82. scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
  83. scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
  84. scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
  85. scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
  86. scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
  87. scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
  88. scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
  89. scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
  90. scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
  91. scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
  92. scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
  93. scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
  94. scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
  95. scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
  96. scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
  97. scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
  98. scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
  99. scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
  100. scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
  101. scitex/dev/plt/mpl/get_dir_ax.py +46 -0
  102. scitex/dev/plt/mpl/get_signatures.py +176 -0
  103. scitex/dev/plt/mpl/get_signatures_details.py +522 -0
  104. scitex/dev/plt/plot_mpl_axhline.py +0 -0
  105. scitex/dev/plt/plot_mpl_axhspan.py +0 -0
  106. scitex/dev/plt/plot_mpl_axvline.py +0 -0
  107. scitex/dev/plt/plot_mpl_axvspan.py +0 -0
  108. scitex/dev/plt/plot_mpl_bar.py +0 -0
  109. scitex/dev/plt/plot_mpl_barh.py +0 -0
  110. scitex/dev/plt/plot_mpl_boxplot.py +0 -0
  111. scitex/dev/plt/plot_mpl_contour.py +0 -0
  112. scitex/dev/plt/plot_mpl_contourf.py +0 -0
  113. scitex/dev/plt/plot_mpl_errorbar.py +0 -0
  114. scitex/dev/plt/plot_mpl_eventplot.py +0 -0
  115. scitex/dev/plt/plot_mpl_fill.py +0 -0
  116. scitex/dev/plt/plot_mpl_fill_between.py +0 -0
  117. scitex/dev/plt/plot_mpl_hexbin.py +0 -0
  118. scitex/dev/plt/plot_mpl_hist.py +0 -0
  119. scitex/dev/plt/plot_mpl_hist2d.py +0 -0
  120. scitex/dev/plt/plot_mpl_imshow.py +0 -0
  121. scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
  122. scitex/dev/plt/plot_mpl_pie.py +0 -0
  123. scitex/dev/plt/plot_mpl_plot.py +0 -0
  124. scitex/dev/plt/plot_mpl_quiver.py +0 -0
  125. scitex/dev/plt/plot_mpl_scatter.py +0 -0
  126. scitex/dev/plt/plot_mpl_stackplot.py +0 -0
  127. scitex/dev/plt/plot_mpl_stem.py +0 -0
  128. scitex/dev/plt/plot_mpl_step.py +0 -0
  129. scitex/dev/plt/plot_mpl_violinplot.py +0 -0
  130. scitex/dev/plt/plot_sns_barplot.py +0 -0
  131. scitex/dev/plt/plot_sns_boxplot.py +0 -0
  132. scitex/dev/plt/plot_sns_heatmap.py +0 -0
  133. scitex/dev/plt/plot_sns_histplot.py +0 -0
  134. scitex/dev/plt/plot_sns_kdeplot.py +0 -0
  135. scitex/dev/plt/plot_sns_lineplot.py +0 -0
  136. scitex/dev/plt/plot_sns_scatterplot.py +0 -0
  137. scitex/dev/plt/plot_sns_stripplot.py +0 -0
  138. scitex/dev/plt/plot_sns_swarmplot.py +0 -0
  139. scitex/dev/plt/plot_sns_violinplot.py +0 -0
  140. scitex/dev/plt/plot_stx_bar.py +0 -0
  141. scitex/dev/plt/plot_stx_barh.py +0 -0
  142. scitex/dev/plt/plot_stx_box.py +0 -0
  143. scitex/dev/plt/plot_stx_boxplot.py +0 -0
  144. scitex/dev/plt/plot_stx_conf_mat.py +0 -0
  145. scitex/dev/plt/plot_stx_contour.py +0 -0
  146. scitex/dev/plt/plot_stx_ecdf.py +0 -0
  147. scitex/dev/plt/plot_stx_errorbar.py +0 -0
  148. scitex/dev/plt/plot_stx_fill_between.py +0 -0
  149. scitex/dev/plt/plot_stx_fillv.py +0 -0
  150. scitex/dev/plt/plot_stx_heatmap.py +0 -0
  151. scitex/dev/plt/plot_stx_image.py +0 -0
  152. scitex/dev/plt/plot_stx_imshow.py +0 -0
  153. scitex/dev/plt/plot_stx_joyplot.py +0 -0
  154. scitex/dev/plt/plot_stx_kde.py +0 -0
  155. scitex/dev/plt/plot_stx_line.py +0 -0
  156. scitex/dev/plt/plot_stx_mean_ci.py +0 -0
  157. scitex/dev/plt/plot_stx_mean_std.py +0 -0
  158. scitex/dev/plt/plot_stx_median_iqr.py +0 -0
  159. scitex/dev/plt/plot_stx_raster.py +0 -0
  160. scitex/dev/plt/plot_stx_rectangle.py +0 -0
  161. scitex/dev/plt/plot_stx_scatter.py +0 -0
  162. scitex/dev/plt/plot_stx_shaded_line.py +0 -0
  163. scitex/dev/plt/plot_stx_violin.py +0 -0
  164. scitex/dev/plt/plot_stx_violinplot.py +0 -0
  165. scitex/diagram/README.md +197 -0
  166. scitex/diagram/__init__.py +48 -0
  167. scitex/diagram/_compile.py +312 -0
  168. scitex/diagram/_diagram.py +355 -0
  169. scitex/diagram/_presets.py +173 -0
  170. scitex/diagram/_schema.py +182 -0
  171. scitex/diagram/_split.py +278 -0
  172. scitex/dict/_pop_keys.py +1 -7
  173. scitex/dsp/__init__.py +15 -10
  174. scitex/dsp/add_noise.py +5 -2
  175. scitex/dsp/example.py +35 -22
  176. scitex/dsp/filt.py +8 -3
  177. scitex/dsp/reference.py +3 -2
  178. scitex/dsp/utils/__init__.py +2 -1
  179. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  180. scitex/dt/__init__.py +39 -2
  181. scitex/errors.py +82 -521
  182. scitex/fig/__init__.py +4 -4
  183. scitex/fig/editor/__init__.py +5 -2
  184. scitex/fig/editor/_dearpygui_editor.py +1 -1
  185. scitex/fig/editor/_mpl_editor.py +1 -1
  186. scitex/fig/editor/_qt_editor.py +1 -1
  187. scitex/fig/editor/_tkinter_editor.py +1 -1
  188. scitex/fig/editor/edit/__init__.py +50 -0
  189. scitex/fig/editor/edit/backend_detector.py +109 -0
  190. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  191. scitex/fig/editor/edit/editor_launcher.py +239 -0
  192. scitex/fig/editor/edit/manual_handler.py +53 -0
  193. scitex/fig/editor/edit/panel_loader.py +232 -0
  194. scitex/fig/editor/edit/path_resolver.py +67 -0
  195. scitex/fig/editor/flask_editor/_bbox.py +23 -0
  196. scitex/fig/editor/flask_editor/_core.py +908 -103
  197. scitex/fig/editor/flask_editor/_renderer.py +74 -0
  198. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  199. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  200. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  201. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  202. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  203. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  204. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  205. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  206. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  207. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  208. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  209. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  210. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  211. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  212. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  213. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  214. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  215. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  216. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  217. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  218. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  219. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  220. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  221. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  222. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  223. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  224. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  225. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  226. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  227. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  228. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  229. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  230. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  231. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  232. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  233. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  234. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  235. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  236. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  237. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  238. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  239. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  240. scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
  241. scitex/fig/editor/flask_editor/templates/_html.py +27 -9
  242. scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
  243. scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
  244. scitex/fig/io/_bundle.py +104 -19
  245. scitex/fts/README.md +262 -0
  246. scitex/fts/TODO.md +66 -0
  247. scitex/fts/__init__.py +90 -0
  248. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  249. scitex/fts/_bundle/_FTS.py +657 -0
  250. scitex/fts/_bundle/__init__.py +38 -0
  251. scitex/fts/_bundle/_children.py +216 -0
  252. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  253. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  254. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  255. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  256. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  257. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  258. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  259. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  260. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  261. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  262. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  263. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  264. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  265. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  266. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  267. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  268. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  269. scitex/fts/_bundle/_loader.py +134 -0
  270. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  271. scitex/fts/_bundle/_saver.py +269 -0
  272. scitex/fts/_bundle/_storage.py +200 -0
  273. scitex/fts/_bundle/_utils/__init__.py +55 -0
  274. scitex/fts/_bundle/_utils/_const.py +26 -0
  275. scitex/fts/_bundle/_utils/_errors.py +73 -0
  276. scitex/fts/_bundle/_utils/_generate.py +21 -0
  277. scitex/fts/_bundle/_utils/_types.py +76 -0
  278. scitex/fts/_bundle/_validation.py +434 -0
  279. scitex/fts/_bundle/_zipbundle.py +165 -0
  280. scitex/fts/_fig/__init__.py +22 -0
  281. scitex/fts/_fig/_backend/__init__.py +53 -0
  282. scitex/fts/_fig/_backend/_export.py +165 -0
  283. scitex/fts/_fig/_backend/_parser.py +188 -0
  284. scitex/fts/_fig/_backend/_render.py +538 -0
  285. scitex/fts/_fig/_composite.py +345 -0
  286. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  287. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  288. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  289. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  290. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  291. scitex/fts/_fig/_editor/__init__.py +14 -0
  292. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  293. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  294. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  295. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  296. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  297. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  298. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  299. scitex/fts/_fig/_editor/_defaults.py +300 -0
  300. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  301. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  302. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  303. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  304. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  305. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  306. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  307. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  308. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  309. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  310. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  311. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  312. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  313. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  314. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  315. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  316. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  317. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  318. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  319. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  320. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  321. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  322. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  323. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  324. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  325. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  326. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  327. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  328. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  329. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  330. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  331. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  332. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  333. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  334. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  335. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  336. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  337. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  338. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  339. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  340. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  341. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  342. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  343. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  344. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  345. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  346. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  347. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  348. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  349. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  350. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  351. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  352. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  353. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  354. scitex/fts/_fig/_models/_Annotations.py +115 -0
  355. scitex/fts/_fig/_models/_Axes.py +152 -0
  356. scitex/fts/_fig/_models/_Figure.py +138 -0
  357. scitex/fts/_fig/_models/_Guides.py +104 -0
  358. scitex/fts/_fig/_models/_Plot.py +123 -0
  359. scitex/fts/_fig/_models/_Styles.py +245 -0
  360. scitex/fts/_fig/_models/__init__.py +80 -0
  361. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  362. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  363. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  364. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  365. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  366. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  367. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  368. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  369. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  370. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  371. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  372. scitex/fts/_fig/_utils/__init__.py +129 -0
  373. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  374. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  375. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  376. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  377. scitex/fts/_fig/_utils/_get_template.py +178 -0
  378. scitex/fts/_fig/_utils/_normalize.py +73 -0
  379. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  380. scitex/fts/_fig/_utils/_validate.py +197 -0
  381. scitex/fts/_kinds/__init__.py +45 -0
  382. scitex/fts/_kinds/_figure/__init__.py +19 -0
  383. scitex/fts/_kinds/_figure/_composite.py +345 -0
  384. scitex/fts/_kinds/_plot/__init__.py +25 -0
  385. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  386. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  387. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  388. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  389. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  390. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  391. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  392. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  393. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  394. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  395. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  396. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  397. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  398. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  399. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  400. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  401. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  402. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  403. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  404. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  405. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  406. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  407. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  408. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  409. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  410. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  411. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  412. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  413. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  414. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  415. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  416. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  417. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  418. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  419. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  420. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  421. scitex/fts/_kinds/_shape/__init__.py +141 -0
  422. scitex/fts/_kinds/_stats/__init__.py +56 -0
  423. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  424. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  425. scitex/fts/_kinds/_table/__init__.py +72 -0
  426. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  427. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  428. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  429. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  430. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  431. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  432. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  433. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  434. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  435. scitex/fts/_kinds/_text/__init__.py +77 -0
  436. scitex/fts/_schemas/data_info.schema.json +75 -0
  437. scitex/fts/_schemas/encoding.schema.json +90 -0
  438. scitex/fts/_schemas/node.schema.json +145 -0
  439. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  440. scitex/fts/_schemas/stats.schema.json +132 -0
  441. scitex/fts/_schemas/theme.schema.json +141 -0
  442. scitex/fts/_stats/__init__.py +48 -0
  443. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  444. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  445. scitex/fts/_tables/__init__.py +65 -0
  446. scitex/fts/_tables/_latex/__init__.py +93 -0
  447. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  448. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  449. scitex/fts/_tables/_latex/_export.py +279 -0
  450. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  451. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  452. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  453. scitex/fts/_tables/_latex/_utils.py +369 -0
  454. scitex/fts/_tables/_latex/_validator.py +445 -0
  455. scitex/gen/__init__.py +66 -25
  456. scitex/gen/misc.py +28 -0
  457. scitex/io/__init__.py +47 -20
  458. scitex/io/_load.py +87 -36
  459. scitex/io/_load_modules/__init__.py +10 -7
  460. scitex/io/_load_modules/_pandas.py +6 -1
  461. scitex/io/_save.py +299 -1556
  462. scitex/io/_save_modules/__init__.py +76 -19
  463. scitex/io/_save_modules/_figure_utils.py +90 -0
  464. scitex/io/_save_modules/_image_csv.py +497 -0
  465. scitex/io/_save_modules/_legends.py +91 -0
  466. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  467. scitex/io/_save_modules/_pltz_stx.py +536 -0
  468. scitex/io/_save_modules/_stx_bundle.py +104 -0
  469. scitex/io/_save_modules/_symlink.py +96 -0
  470. scitex/io/_save_modules/_yaml.py +1 -1
  471. scitex/io/_save_modules/_zarr.py +64 -18
  472. scitex/io/bundle/README.md +212 -0
  473. scitex/io/bundle/__init__.py +110 -0
  474. scitex/io/{_bundle.py → bundle/_core.py} +219 -89
  475. scitex/io/bundle/_nested.py +713 -0
  476. scitex/io/bundle/_types.py +74 -0
  477. scitex/io/bundle/_zip.py +487 -0
  478. scitex/io/utils/h5_to_zarr.py +1 -1
  479. scitex/logging/__init__.py +108 -13
  480. scitex/logging/_errors.py +508 -0
  481. scitex/logging/_formatters.py +30 -6
  482. scitex/logging/_warnings.py +261 -0
  483. scitex/plt/__init__.py +4 -1
  484. scitex/plt/_figrecipe.py +236 -0
  485. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  486. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
  487. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
  488. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
  489. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
  490. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
  491. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
  492. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
  493. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
  494. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
  495. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
  496. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
  497. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
  498. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
  499. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  500. scitex/plt/_subplots/_FigWrapper.py +15 -0
  501. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  502. scitex/plt/_subplots/_export_as_csv.py +11 -0
  503. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  504. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  505. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  506. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
  507. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
  508. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
  509. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
  510. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  511. scitex/plt/_subplots/_fonts.py +71 -0
  512. scitex/plt/_subplots/_mm_layout.py +282 -0
  513. scitex/plt/gallery/__init__.py +99 -2
  514. scitex/plt/io/_layered_bundle.py +0 -0
  515. scitex/plt/styles/_plot_postprocess.py +3 -1
  516. scitex/plt/utils/_configure_mpl.py +16 -19
  517. scitex/repro/_RandomStateManager.py +13 -8
  518. scitex/resource/__init__.py +19 -1
  519. scitex/resource/_utils/_get_env_info.py +13 -25
  520. scitex/schema/__init__.py +149 -160
  521. scitex/schema/_encoding.py +273 -0
  522. scitex/schema/_figure_elements.py +406 -0
  523. scitex/schema/_plot.py +0 -0
  524. scitex/schema/_theme.py +360 -0
  525. scitex/schema/_validation.py +0 -98
  526. scitex/scholar/__init__.py +56 -14
  527. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  528. scitex/scholar/auth/__init__.py +11 -2
  529. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  530. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  531. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  532. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  533. scitex/scholar/config/ScholarConfig.py +1 -1
  534. scitex/scholar/core/Scholar.py +1 -1
  535. scitex/session/_decorator.py +18 -16
  536. scitex/session/_lifecycle.py +9 -11
  537. scitex/session/template.py +9 -8
  538. scitex/sh/test_sh.py +72 -0
  539. scitex/sh/test_sh_simple.py +61 -0
  540. scitex/stats/__init__.py +221 -97
  541. scitex/stats/_schema.py +21 -22
  542. scitex/stats/descriptive/_circular.py +212 -351
  543. scitex/stats/descriptive/_describe.py +81 -132
  544. scitex/stats/descriptive/_nan.py +205 -433
  545. scitex/stats/descriptive/_real.py +127 -141
  546. scitex/str/_format_plot_text.py +5 -5
  547. scitex/str/_latex.py +26 -84
  548. scitex/str/_latex_fallback.py +53 -47
  549. scitex/web/_search_pubmed.py +5 -4
  550. scitex/writer/tests/test_diff_between.py +451 -0
  551. scitex/writer/tests/test_document_section.py +311 -0
  552. scitex/writer/tests/test_document_workflow.py +393 -0
  553. scitex/writer/tests/test_writer.py +361 -0
  554. scitex/writer/tests/test_writer_integration.py +303 -0
  555. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  556. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/RECORD +479 -108
  557. scitex/fig/editor/_edit.py +0 -751
  558. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  559. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  560. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  561. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  562. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  563. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,751 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # File: ./src/scitex/vis/editor/_edit.py
4
- """Main edit function for launching visual editor."""
5
-
6
- from pathlib import Path
7
- from typing import Union, Optional, Literal
8
- import hashlib
9
- import json
10
- import warnings
11
-
12
-
13
- def _print_available_backends():
14
- """Print available backends status."""
15
- backends = {
16
- "flask": ["flask"],
17
- "dearpygui": ["dearpygui"],
18
- "qt": ["PyQt6", "PyQt5", "PySide6", "PySide2"],
19
- "tkinter": ["tkinter"],
20
- "mpl": ["matplotlib"],
21
- }
22
-
23
- print("\n" + "=" * 50)
24
- print("SciTeX Visual Editor - Available Backends")
25
- print("=" * 50)
26
-
27
- for backend, packages in backends.items():
28
- available = False
29
- available_pkg = None
30
- for pkg in packages:
31
- try:
32
- __import__(pkg)
33
- available = True
34
- available_pkg = pkg
35
- break
36
- except ImportError:
37
- pass
38
-
39
- status = f"[OK] {available_pkg}" if available else "[NOT INSTALLED]"
40
- print(f" {backend:12s}: {status}")
41
-
42
- print("=" * 50)
43
- print("Install: pip install scitex[gui]")
44
- print("=" * 50 + "\n")
45
-
46
-
47
- def edit(
48
- path: Union[str, Path],
49
- backend: Literal["auto", "flask", "dearpygui", "qt", "tkinter", "mpl"] = "auto",
50
- apply_manual: bool = True,
51
- port: int = 5050,
52
- ) -> None:
53
- """
54
- Launch interactive editor for figure style/annotation editing.
55
-
56
- Parameters
57
- ----------
58
- path : str or Path
59
- Path to figure file. Can be:
60
- - .pltz.d directory bundle (recommended for hitmap selection)
61
- - .pltz ZIP bundle
62
- - JSON file (figure.json or figure.manual.json)
63
- - CSV file (figure.csv) - for data-only start
64
- - PNG file (figure.png)
65
- Will auto-detect sibling files in same directory or organized subdirectories.
66
- backend : str, optional
67
- GUI backend to use (default: "auto"):
68
- - "auto": Pick best available with graceful degradation
69
- (flask -> dearpygui -> qt -> tkinter -> mpl)
70
- - "flask": Browser-based editor (Flask, modern UI)
71
- - "dearpygui": GPU-accelerated modern GUI (fast, requires dearpygui)
72
- - "qt": Rich desktop editor (requires PyQt5/6 or PySide2/6)
73
- - "tkinter": Built-in Python GUI (works everywhere)
74
- - "mpl": Minimal matplotlib interactive mode (always works)
75
- apply_manual : bool, optional
76
- If True, load .manual.json overrides if exists (default: True)
77
- port : int, optional
78
- Port number for web-based editors (flask). Default: 5050.
79
- If port is in use, will attempt to free it or find an alternative.
80
-
81
- Returns
82
- -------
83
- None
84
- Editor runs in GUI event loop. Changes saved to .manual.json.
85
-
86
- Examples
87
- --------
88
- >>> import scitex as stx
89
- >>> stx.fig.edit("output/myplot.pltz.d") # Edit pltz bundle with hitmap selection
90
- >>> stx.fig.edit("output/myplot.pltz") # Also works with ZIP bundles
91
- >>> stx.fig.edit("output/figure.json") # Auto-select best backend
92
- >>> stx.fig.edit("output/figure.png", backend="flask") # Force flask editor
93
-
94
- Notes
95
- -----
96
- - Changes are saved to `{basename}.manual.json` alongside the original
97
- - Manual JSON includes hash of base JSON for staleness detection
98
- - Original JSON/CSV files are never modified
99
- - Backend auto-detection order: flask > dearpygui > qt > tkinter > mpl
100
- - For .pltz bundles, hitmap-based element selection is available
101
- """
102
- path = Path(path)
103
- spath = str(path)
104
- parent_str = str(path.parent) if path.is_file() else ""
105
-
106
- # Panel info for multi-panel figures
107
- panel_info = None
108
-
109
- # Check if this is a .figz bundle (multi-panel figure)
110
- if spath.endswith('.figz.d') or spath.endswith('.figz'):
111
- json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info = _resolve_figz_bundle(path)
112
- # Check if this is a .pltz bundle (single panel) - either the directory or a file inside it
113
- elif spath.endswith('.pltz.d') or spath.endswith('.pltz') or parent_str.endswith('.pltz.d'):
114
- # If it's a file inside .pltz.d, use the parent directory
115
- bundle_path = path.parent if parent_str.endswith('.pltz.d') else path
116
- json_path, csv_path, png_path, hitmap_path, bundle_spec = _resolve_pltz_bundle(bundle_path)
117
- # Check if a JSON/CSV/PNG file is inside a .figz.d (passed individual panel file)
118
- elif parent_str.endswith('.figz.d') or (path.parent.parent and str(path.parent.parent).endswith('.figz.d')):
119
- # File is inside figz bundle, resolve from figz root
120
- figz_path = path.parent if parent_str.endswith('.figz.d') else path.parent.parent
121
- json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info = _resolve_figz_bundle(figz_path)
122
- else:
123
- # Resolve paths (JSON, CSV, PNG)
124
- json_path, csv_path, png_path = _resolve_figure_paths(path)
125
- hitmap_path = None
126
- bundle_spec = None
127
-
128
- if not json_path.exists():
129
- raise FileNotFoundError(f"JSON file not found: {json_path}")
130
-
131
- # Load data
132
- import scitex as stx
133
-
134
- metadata = bundle_spec if bundle_spec else stx.io.load(json_path)
135
- csv_data = None
136
- if csv_path and csv_path.exists():
137
- csv_data = stx.io.load(csv_path)
138
-
139
- # Load manual overrides if exists
140
- manual_path = json_path.with_suffix(".manual.json")
141
- manual_overrides = None
142
- if apply_manual and manual_path.exists():
143
- manual_data = stx.io.load(manual_path)
144
- manual_overrides = manual_data.get("overrides", {})
145
-
146
- # Resolve backend if "auto"
147
- if backend == "auto":
148
- backend = _detect_best_backend()
149
-
150
- # Print status
151
- _print_available_backends()
152
- print(f"Launching {backend} editor for: {json_path}")
153
-
154
- # Launch appropriate backend
155
- if backend == "flask":
156
- try:
157
- from ._flask_editor import WebEditor
158
-
159
- editor = WebEditor(
160
- json_path=json_path,
161
- metadata=metadata,
162
- csv_data=csv_data,
163
- png_path=png_path,
164
- hitmap_path=hitmap_path,
165
- manual_overrides=manual_overrides,
166
- port=port,
167
- panel_info=panel_info,
168
- )
169
- editor.run()
170
- except ImportError as e:
171
- raise ImportError(
172
- "Flask backend requires Flask. Install with: pip install flask"
173
- ) from e
174
- elif backend == "dearpygui":
175
- try:
176
- from ._dearpygui_editor import DearPyGuiEditor
177
-
178
- editor = DearPyGuiEditor(
179
- json_path=json_path,
180
- metadata=metadata,
181
- csv_data=csv_data,
182
- png_path=png_path,
183
- manual_overrides=manual_overrides,
184
- )
185
- editor.run()
186
- except ImportError as e:
187
- raise ImportError(
188
- "DearPyGui backend requires dearpygui. "
189
- "Install with: pip install dearpygui"
190
- ) from e
191
- elif backend == "qt":
192
- try:
193
- from ._qt_editor import QtEditor
194
-
195
- editor = QtEditor(
196
- json_path=json_path,
197
- metadata=metadata,
198
- csv_data=csv_data,
199
- png_path=png_path,
200
- manual_overrides=manual_overrides,
201
- hitmap_path=hitmap_path,
202
- bundle_spec=bundle_spec,
203
- )
204
- editor.run()
205
- except ImportError as e:
206
- raise ImportError(
207
- "Qt backend requires PyQt5/PyQt6 or PySide2/PySide6. "
208
- "Install with: pip install PyQt6"
209
- ) from e
210
- elif backend == "tkinter":
211
- from ._tkinter_editor import TkinterEditor
212
-
213
- editor = TkinterEditor(
214
- json_path=json_path,
215
- metadata=metadata,
216
- csv_data=csv_data,
217
- manual_overrides=manual_overrides,
218
- )
219
- editor.run()
220
- elif backend == "mpl":
221
- from ._mpl_editor import MplEditor
222
-
223
- editor = MplEditor(
224
- json_path=json_path,
225
- metadata=metadata,
226
- csv_data=csv_data,
227
- manual_overrides=manual_overrides,
228
- )
229
- editor.run()
230
- else:
231
- raise ValueError(
232
- f"Unknown backend: {backend}. "
233
- "Use 'auto', 'flask', 'dearpygui', 'qt', 'tkinter', or 'mpl'."
234
- )
235
-
236
-
237
- def _detect_best_backend() -> str:
238
- """
239
- Detect the best available GUI backend with graceful degradation.
240
-
241
- Order: flask > dearpygui > qt > tkinter > mpl
242
- Shows warnings when falling back to less capable backends.
243
- """
244
- import warnings
245
-
246
- # Try Flask - best for modern UI
247
- try:
248
- import flask
249
-
250
- return "flask"
251
- except ImportError:
252
- pass
253
-
254
- # Try DearPyGui - GPU-accelerated, modern
255
- try:
256
- import dearpygui
257
-
258
- return "dearpygui"
259
- except ImportError:
260
- warnings.warn(
261
- "Flask not available. Consider: pip install flask\nTrying DearPyGui..."
262
- )
263
-
264
- # Try DearPyGui
265
- try:
266
- import dearpygui
267
-
268
- return "dearpygui"
269
- except ImportError:
270
- pass
271
-
272
- # Try Qt (richest desktop features)
273
- qt_available = False
274
- try:
275
- import PyQt6
276
-
277
- qt_available = True
278
- except ImportError:
279
- pass
280
- if not qt_available:
281
- try:
282
- import PyQt5
283
-
284
- qt_available = True
285
- except ImportError:
286
- pass
287
- if not qt_available:
288
- try:
289
- import PySide6
290
-
291
- qt_available = True
292
- except ImportError:
293
- pass
294
- if not qt_available:
295
- try:
296
- import PySide2
297
-
298
- qt_available = True
299
- except ImportError:
300
- pass
301
-
302
- if qt_available:
303
- warnings.warn(
304
- "DearPyGui not available. Consider: pip install dearpygui\n"
305
- "Using Qt backend instead."
306
- )
307
- return "qt"
308
-
309
- # Try Tkinter (built-in, good features)
310
- try:
311
- import tkinter
312
-
313
- warnings.warn(
314
- "Qt not available. Consider: pip install PyQt6\n"
315
- "Using Tkinter backend (basic features)."
316
- )
317
- return "tkinter"
318
- except ImportError:
319
- pass
320
-
321
- # Fall back to matplotlib interactive (always works)
322
- warnings.warn(
323
- "No GUI toolkit found. Using minimal matplotlib editor.\n"
324
- "For better experience, install: pip install flask (web) or pip install PyQt6 (desktop)"
325
- )
326
- return "mpl"
327
-
328
-
329
- def _resolve_figure_paths(path: Path) -> tuple:
330
- """
331
- Resolve JSON, CSV, and PNG paths from any input file path.
332
-
333
- Handles two patterns:
334
- 1. Flat (sibling): path/to/figure.{json,csv,png}
335
- 2. Organized (subdirs): path/to/{json,csv,png}/figure.{ext}
336
-
337
- Parameters
338
- ----------
339
- path : Path
340
- Input path (can be JSON, CSV, or PNG)
341
-
342
- Returns
343
- -------
344
- tuple
345
- (json_path, csv_path, png_path) - csv_path/png_path may be None if not found
346
- """
347
- path = Path(path)
348
- stem = path.stem
349
- parent = path.parent
350
-
351
- # Check if this is organized pattern (parent is json/, csv/, png/)
352
- if parent.name in ("json", "csv", "png"):
353
- base_dir = parent.parent
354
- json_path = base_dir / "json" / f"{stem}.json"
355
- csv_path = base_dir / "csv" / f"{stem}.csv"
356
- png_path = base_dir / "png" / f"{stem}.png"
357
- else:
358
- # Flat pattern - sibling files
359
- json_path = parent / f"{stem}.json"
360
- csv_path = parent / f"{stem}.csv"
361
- png_path = parent / f"{stem}.png"
362
-
363
- # If input was .manual.json, get base json
364
- if stem.endswith(".manual"):
365
- base_stem = stem[:-7] # Remove '.manual'
366
- if parent.name == "json":
367
- json_path = parent / f"{base_stem}.json"
368
- csv_path = parent.parent / "csv" / f"{base_stem}.csv"
369
- png_path = parent.parent / "png" / f"{base_stem}.png"
370
- else:
371
- json_path = parent / f"{base_stem}.json"
372
- csv_path = parent / f"{base_stem}.csv"
373
- png_path = parent / f"{base_stem}.png"
374
-
375
- return (
376
- json_path,
377
- csv_path if csv_path.exists() else None,
378
- png_path if png_path.exists() else None,
379
- )
380
-
381
-
382
- def _resolve_figz_bundle(path: Path, panel_index: int = 0) -> tuple:
383
- """
384
- Resolve paths from a .figz bundle (multi-panel figure).
385
-
386
- Parameters
387
- ----------
388
- path : Path
389
- Path to .figz bundle (.figz or .figz.d)
390
- panel_index : int, optional
391
- Index of panel to open (default: 0 for first panel)
392
-
393
- Returns
394
- -------
395
- tuple
396
- (json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info)
397
- panel_info is a dict with keys: panels, current_index, figz_dir
398
- """
399
- import json as json_module
400
- import tempfile
401
- import zipfile
402
-
403
- spath = str(path)
404
-
405
- # Handle ZIP vs directory
406
- if spath.endswith('.figz') and not spath.endswith('.figz.d'):
407
- # It's a ZIP - extract to temp directory
408
- if not path.exists():
409
- raise FileNotFoundError(f"Figz bundle not found: {path}")
410
- temp_dir = tempfile.mkdtemp(prefix='scitex_edit_figz_')
411
- with zipfile.ZipFile(path, 'r') as zf:
412
- zf.extractall(temp_dir)
413
- bundle_dir = Path(temp_dir)
414
- for item in bundle_dir.iterdir():
415
- if item.is_dir() and str(item).endswith('.figz.d'):
416
- bundle_dir = item
417
- break
418
- else:
419
- bundle_dir = Path(path)
420
- if not bundle_dir.exists():
421
- raise FileNotFoundError(f"Figz bundle directory not found: {bundle_dir}")
422
-
423
- # Find nested pltz bundles
424
- pltz_dirs = sorted([d for d in bundle_dir.iterdir()
425
- if d.is_dir() and str(d).endswith('.pltz.d')])
426
-
427
- if not pltz_dirs:
428
- raise FileNotFoundError(f"No .pltz.d panels found in figz bundle: {bundle_dir}")
429
-
430
- # Validate panel index
431
- if panel_index < 0 or panel_index >= len(pltz_dirs):
432
- panel_index = 0
433
-
434
- selected_panel = pltz_dirs[panel_index]
435
- print(f"Opening panel: {selected_panel.name}")
436
- if len(pltz_dirs) > 1:
437
- print(f" (Figz contains {len(pltz_dirs)} panels: {[d.name for d in pltz_dirs]})")
438
-
439
- # Build panel info for editor
440
- panel_info = {
441
- "panels": [d.name for d in pltz_dirs],
442
- "current_index": panel_index,
443
- "figz_dir": str(bundle_dir),
444
- }
445
-
446
- # Delegate to pltz resolver
447
- result = _resolve_pltz_bundle(selected_panel)
448
- # Append panel_info to the result
449
- return result + (panel_info,)
450
-
451
-
452
- def _resolve_pltz_bundle(path: Path) -> tuple:
453
- """
454
- Resolve paths from a .pltz bundle (directory or ZIP).
455
-
456
- Supports both:
457
- - Legacy format (single {basename}.json)
458
- - Layered format v2.0 (spec.json + style.json + cache/)
459
-
460
- Parameters
461
- ----------
462
- path : Path
463
- Path to .pltz bundle (.pltz or .pltz.d)
464
-
465
- Returns
466
- -------
467
- tuple
468
- (json_path, csv_path, png_path, hitmap_path, bundle_spec)
469
- """
470
- import json as json_module
471
- import tempfile
472
- import zipfile
473
-
474
- spath = str(path)
475
-
476
- # Handle ZIP vs directory
477
- if spath.endswith('.pltz') and not spath.endswith('.pltz.d'):
478
- # It's a ZIP - extract to temp directory
479
- if not path.exists():
480
- raise FileNotFoundError(f"Bundle not found: {path}")
481
- temp_dir = tempfile.mkdtemp(prefix='scitex_edit_')
482
- with zipfile.ZipFile(path, 'r') as zf:
483
- zf.extractall(temp_dir)
484
- # Find the .pltz.d directory inside
485
- bundle_dir = Path(temp_dir)
486
- for item in bundle_dir.iterdir():
487
- if item.is_dir() and str(item).endswith('.pltz.d'):
488
- bundle_dir = item
489
- break
490
- else:
491
- # It's already a directory
492
- bundle_dir = Path(path)
493
- if not bundle_dir.exists():
494
- raise FileNotFoundError(f"Bundle directory not found: {bundle_dir}")
495
-
496
- # Check if this is a layered bundle (v2.0)
497
- spec_path = bundle_dir / "spec.json"
498
- if spec_path.exists():
499
- return _resolve_layered_pltz_bundle(bundle_dir)
500
-
501
- # === Legacy format below ===
502
- # Find files by pattern (supports basename-based naming)
503
- json_path = None
504
- csv_path = None
505
- png_path = None
506
- svg_path = None
507
- hitmap_path = None
508
- bundle_spec = None
509
-
510
- for f in bundle_dir.iterdir():
511
- name = f.name
512
- if name.endswith('.json') and not name.endswith('.manual.json'):
513
- json_path = f
514
- elif name.endswith('.csv'):
515
- csv_path = f
516
- elif name.endswith('_hitmap.png'):
517
- hitmap_path = f
518
- elif name.endswith('.svg') and '_hitmap' not in name:
519
- svg_path = f
520
- elif name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
521
- png_path = f
522
-
523
- # Prefer SVG for display (contains complete figure with all data)
524
- # PNG (panel_A.png) is transparent overlay without line data
525
- # Coordinate mapping is handled by _extract_bboxes_from_metadata with actual display dimensions
526
- if svg_path:
527
- png_path = svg_path # Use SVG for display
528
-
529
- # Load the spec
530
- if json_path and json_path.exists():
531
- with open(json_path, 'r') as f:
532
- bundle_spec = json_module.load(f)
533
-
534
- return (
535
- json_path,
536
- csv_path if csv_path and csv_path.exists() else None,
537
- png_path if png_path and png_path.exists() else None,
538
- hitmap_path if hitmap_path and hitmap_path.exists() else None,
539
- bundle_spec,
540
- )
541
-
542
-
543
- def _resolve_layered_pltz_bundle(bundle_dir: Path) -> tuple:
544
- """
545
- Resolve paths from a layered .pltz bundle (v2.0 format).
546
-
547
- Layered format structure:
548
- plot.pltz.d/
549
- spec.json # Semantic
550
- style.json # Appearance
551
- {basename}.csv # Data
552
- exports/ # PNG, SVG, hitmap
553
- cache/ # geometry_px.json
554
-
555
- Parameters
556
- ----------
557
- bundle_dir : Path
558
- Path to .pltz.d bundle directory.
559
-
560
- Returns
561
- -------
562
- tuple
563
- (json_path, csv_path, png_path, hitmap_path, bundle_spec)
564
- """
565
- import json as json_module
566
- from scitex.plt.io import load_layered_pltz_bundle, merge_layered_bundle
567
-
568
- # Load layered bundle
569
- bundle_data = load_layered_pltz_bundle(bundle_dir)
570
- basename = bundle_data.get("basename", "plot")
571
-
572
- # Paths
573
- spec_path = bundle_dir / "spec.json"
574
- csv_path = None
575
- png_path = None
576
- hitmap_path = None
577
-
578
- # Find CSV
579
- for f in bundle_dir.glob("*.csv"):
580
- csv_path = f
581
- break
582
-
583
- # Find exports
584
- exports_dir = bundle_dir / "exports"
585
- if exports_dir.exists():
586
- for f in exports_dir.iterdir():
587
- name = f.name
588
- if name.endswith('_hitmap.png'):
589
- hitmap_path = f
590
- elif name.endswith('.svg') and '_hitmap' not in name:
591
- png_path = f # Prefer SVG
592
- elif name.endswith('.png') and '_hitmap' not in name and png_path is None:
593
- png_path = f
594
-
595
- # Merged spec for backward compatibility with editor
596
- bundle_spec = bundle_data.get("merged", {})
597
-
598
- # Add hit_regions path reference for editor
599
- if hitmap_path and "hit_regions" in bundle_spec:
600
- bundle_spec["hit_regions"]["hit_map"] = str(hitmap_path.name)
601
-
602
- return (
603
- spec_path, # Return spec.json as the main JSON path
604
- csv_path if csv_path and csv_path.exists() else None,
605
- png_path if png_path and png_path.exists() else None,
606
- hitmap_path if hitmap_path and hitmap_path.exists() else None,
607
- bundle_spec,
608
- )
609
-
610
-
611
- def _load_panel_data(panel_dir: Path) -> Optional[dict]:
612
- """
613
- Load panel data from a .pltz.d directory.
614
-
615
- Used by switch_panel endpoint to load a different panel's data.
616
-
617
- Parameters
618
- ----------
619
- panel_dir : Path
620
- Path to .pltz.d panel directory
621
-
622
- Returns
623
- -------
624
- dict or None
625
- Dictionary with keys: json_path, metadata, csv_data, png_path, hitmap_path
626
- Returns None if panel cannot be loaded
627
- """
628
- import json as json_module
629
- import scitex as stx
630
-
631
- if not panel_dir.exists():
632
- return None
633
-
634
- # Check for layered vs legacy format
635
- spec_path = panel_dir / "spec.json"
636
- if spec_path.exists():
637
- # Layered format
638
- from scitex.plt.io import load_layered_pltz_bundle
639
- bundle_data = load_layered_pltz_bundle(panel_dir)
640
- metadata = bundle_data.get("merged", {})
641
-
642
- # Find CSV
643
- csv_data = None
644
- for f in panel_dir.glob("*.csv"):
645
- csv_data = stx.io.load(f)
646
- break
647
-
648
- # Find exports - prefer PNG over SVG (PIL can't open SVG)
649
- png_path = None
650
- svg_path = None
651
- hitmap_path = None
652
- exports_dir = panel_dir / "exports"
653
- if exports_dir.exists():
654
- for f in exports_dir.iterdir():
655
- name = f.name
656
- if name.endswith('_hitmap.png'):
657
- hitmap_path = f
658
- elif name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
659
- png_path = f
660
- elif name.endswith('.svg') and '_hitmap' not in name and svg_path is None:
661
- svg_path = f
662
- # Fall back to SVG only if no PNG found (though PIL can't open it)
663
- if png_path is None:
664
- png_path = svg_path
665
-
666
- return {
667
- "json_path": spec_path,
668
- "metadata": metadata,
669
- "csv_data": csv_data,
670
- "png_path": png_path,
671
- "hitmap_path": hitmap_path,
672
- }
673
- else:
674
- # Legacy format
675
- json_path = None
676
- csv_data = None
677
- png_path = None
678
- hitmap_path = None
679
-
680
- for f in panel_dir.iterdir():
681
- name = f.name
682
- if name.endswith('.json') and not name.endswith('.manual.json'):
683
- json_path = f
684
- elif name.endswith('.csv'):
685
- csv_data = stx.io.load(f)
686
- elif name.endswith('_hitmap.png'):
687
- hitmap_path = f
688
- elif name.endswith('.svg') and '_hitmap' not in name:
689
- png_path = f
690
- elif name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
691
- if png_path is None:
692
- png_path = f
693
-
694
- if json_path is None:
695
- return None
696
-
697
- with open(json_path, 'r') as f:
698
- metadata = json_module.load(f)
699
-
700
- return {
701
- "json_path": json_path,
702
- "metadata": metadata,
703
- "csv_data": csv_data,
704
- "png_path": png_path,
705
- "hitmap_path": hitmap_path,
706
- }
707
-
708
-
709
- def _compute_file_hash(path: Path) -> str:
710
- """Compute SHA256 hash of file contents."""
711
- with open(path, "rb") as f:
712
- return hashlib.sha256(f.read()).hexdigest()
713
-
714
-
715
- def save_manual_overrides(
716
- json_path: Path,
717
- overrides: dict,
718
- ) -> Path:
719
- """
720
- Save manual overrides to .manual.json file.
721
-
722
- Parameters
723
- ----------
724
- json_path : Path
725
- Path to base JSON file
726
- overrides : dict
727
- Override settings (styles, annotations, etc.)
728
-
729
- Returns
730
- -------
731
- Path
732
- Path to saved manual.json file
733
- """
734
- import scitex as stx
735
-
736
- manual_path = json_path.with_suffix(".manual.json")
737
-
738
- # Compute hash of base JSON for staleness detection
739
- base_hash = _compute_file_hash(json_path)
740
-
741
- manual_data = {
742
- "base_file": json_path.name,
743
- "base_hash": base_hash,
744
- "overrides": overrides,
745
- }
746
-
747
- stx.io.save(manual_data, manual_path)
748
- return manual_path
749
-
750
-
751
- # EOF