scitex 2.7.0__py3-none-any.whl → 2.8.1__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 (355) hide show
  1. scitex/__init__.py +6 -2
  2. scitex/__version__.py +1 -1
  3. scitex/audio/README.md +52 -0
  4. scitex/audio/__init__.py +384 -0
  5. scitex/audio/__main__.py +129 -0
  6. scitex/audio/_tts.py +334 -0
  7. scitex/audio/engines/__init__.py +44 -0
  8. scitex/audio/engines/base.py +275 -0
  9. scitex/audio/engines/elevenlabs_engine.py +143 -0
  10. scitex/audio/engines/gtts_engine.py +162 -0
  11. scitex/audio/engines/pyttsx3_engine.py +131 -0
  12. scitex/audio/mcp_server.py +757 -0
  13. scitex/bridge/_helpers.py +1 -1
  14. scitex/bridge/_plt_vis.py +1 -1
  15. scitex/bridge/_stats_vis.py +1 -1
  16. scitex/dev/plt/__init__.py +272 -0
  17. scitex/dev/plt/plot_mpl_axhline.py +28 -0
  18. scitex/dev/plt/plot_mpl_axhspan.py +28 -0
  19. scitex/dev/plt/plot_mpl_axvline.py +28 -0
  20. scitex/dev/plt/plot_mpl_axvspan.py +28 -0
  21. scitex/dev/plt/plot_mpl_bar.py +29 -0
  22. scitex/dev/plt/plot_mpl_barh.py +29 -0
  23. scitex/dev/plt/plot_mpl_boxplot.py +28 -0
  24. scitex/dev/plt/plot_mpl_contour.py +31 -0
  25. scitex/dev/plt/plot_mpl_contourf.py +31 -0
  26. scitex/dev/plt/plot_mpl_errorbar.py +30 -0
  27. scitex/dev/plt/plot_mpl_eventplot.py +28 -0
  28. scitex/dev/plt/plot_mpl_fill.py +30 -0
  29. scitex/dev/plt/plot_mpl_fill_between.py +31 -0
  30. scitex/dev/plt/plot_mpl_hexbin.py +28 -0
  31. scitex/dev/plt/plot_mpl_hist.py +28 -0
  32. scitex/dev/plt/plot_mpl_hist2d.py +28 -0
  33. scitex/dev/plt/plot_mpl_imshow.py +29 -0
  34. scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
  35. scitex/dev/plt/plot_mpl_pie.py +29 -0
  36. scitex/dev/plt/plot_mpl_plot.py +29 -0
  37. scitex/dev/plt/plot_mpl_quiver.py +31 -0
  38. scitex/dev/plt/plot_mpl_scatter.py +28 -0
  39. scitex/dev/plt/plot_mpl_stackplot.py +31 -0
  40. scitex/dev/plt/plot_mpl_stem.py +29 -0
  41. scitex/dev/plt/plot_mpl_step.py +29 -0
  42. scitex/dev/plt/plot_mpl_violinplot.py +28 -0
  43. scitex/dev/plt/plot_sns_barplot.py +29 -0
  44. scitex/dev/plt/plot_sns_boxplot.py +29 -0
  45. scitex/dev/plt/plot_sns_heatmap.py +28 -0
  46. scitex/dev/plt/plot_sns_histplot.py +29 -0
  47. scitex/dev/plt/plot_sns_kdeplot.py +29 -0
  48. scitex/dev/plt/plot_sns_lineplot.py +31 -0
  49. scitex/dev/plt/plot_sns_scatterplot.py +29 -0
  50. scitex/dev/plt/plot_sns_stripplot.py +29 -0
  51. scitex/dev/plt/plot_sns_swarmplot.py +29 -0
  52. scitex/dev/plt/plot_sns_violinplot.py +29 -0
  53. scitex/dev/plt/plot_stx_bar.py +29 -0
  54. scitex/dev/plt/plot_stx_barh.py +29 -0
  55. scitex/dev/plt/plot_stx_box.py +28 -0
  56. scitex/dev/plt/plot_stx_boxplot.py +28 -0
  57. scitex/dev/plt/plot_stx_conf_mat.py +28 -0
  58. scitex/dev/plt/plot_stx_contour.py +31 -0
  59. scitex/dev/plt/plot_stx_ecdf.py +28 -0
  60. scitex/dev/plt/plot_stx_errorbar.py +30 -0
  61. scitex/dev/plt/plot_stx_fill_between.py +31 -0
  62. scitex/dev/plt/plot_stx_fillv.py +28 -0
  63. scitex/dev/plt/plot_stx_heatmap.py +28 -0
  64. scitex/dev/plt/plot_stx_image.py +28 -0
  65. scitex/dev/plt/plot_stx_imshow.py +28 -0
  66. scitex/dev/plt/plot_stx_joyplot.py +28 -0
  67. scitex/dev/plt/plot_stx_kde.py +28 -0
  68. scitex/dev/plt/plot_stx_line.py +28 -0
  69. scitex/dev/plt/plot_stx_mean_ci.py +28 -0
  70. scitex/dev/plt/plot_stx_mean_std.py +28 -0
  71. scitex/dev/plt/plot_stx_median_iqr.py +28 -0
  72. scitex/dev/plt/plot_stx_raster.py +28 -0
  73. scitex/dev/plt/plot_stx_rectangle.py +28 -0
  74. scitex/dev/plt/plot_stx_scatter.py +29 -0
  75. scitex/dev/plt/plot_stx_shaded_line.py +29 -0
  76. scitex/dev/plt/plot_stx_violin.py +28 -0
  77. scitex/dev/plt/plot_stx_violinplot.py +28 -0
  78. scitex/diagram/README.md +197 -0
  79. scitex/diagram/__init__.py +48 -0
  80. scitex/diagram/_compile.py +312 -0
  81. scitex/diagram/_diagram.py +355 -0
  82. scitex/diagram/_presets.py +173 -0
  83. scitex/diagram/_schema.py +182 -0
  84. scitex/diagram/_split.py +278 -0
  85. scitex/fig/__init__.py +352 -0
  86. scitex/{vis → fig}/backend/_parser.py +1 -1
  87. scitex/{vis → fig}/canvas.py +1 -1
  88. scitex/{vis → fig}/editor/__init__.py +5 -2
  89. scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
  90. scitex/{vis → fig}/editor/_defaults.py +70 -5
  91. scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
  92. scitex/{vis → fig}/editor/_qt_editor.py +182 -2
  93. scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
  94. scitex/fig/editor/edit/__init__.py +50 -0
  95. scitex/fig/editor/edit/backend_detector.py +109 -0
  96. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  97. scitex/fig/editor/edit/editor_launcher.py +239 -0
  98. scitex/fig/editor/edit/manual_handler.py +53 -0
  99. scitex/fig/editor/edit/panel_loader.py +232 -0
  100. scitex/fig/editor/edit/path_resolver.py +67 -0
  101. scitex/fig/editor/flask_editor/_bbox.py +1299 -0
  102. scitex/fig/editor/flask_editor/_core.py +1429 -0
  103. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  104. scitex/fig/editor/flask_editor/_renderer.py +813 -0
  105. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  106. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  107. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  108. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  109. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  110. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  111. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  112. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  113. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  114. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  115. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  116. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  117. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  118. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  119. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  120. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  121. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  122. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  123. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  124. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  125. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  126. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  127. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  128. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  129. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  130. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  131. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  132. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  133. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  134. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  135. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  136. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  137. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  138. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  139. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  140. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  141. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  142. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  143. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  144. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  145. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  146. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  147. scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
  148. scitex/fig/editor/flask_editor/templates/_html.py +852 -0
  149. scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
  150. scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
  151. scitex/{vis → fig}/io/__init__.py +13 -1
  152. scitex/fig/io/_bundle.py +1058 -0
  153. scitex/{vis → fig}/io/_canvas.py +1 -1
  154. scitex/{vis → fig}/io/_data.py +1 -1
  155. scitex/{vis → fig}/io/_export.py +1 -1
  156. scitex/{vis → fig}/io/_load.py +1 -1
  157. scitex/{vis → fig}/io/_panel.py +1 -1
  158. scitex/{vis → fig}/io/_save.py +1 -1
  159. scitex/{vis → fig}/model/__init__.py +1 -1
  160. scitex/{vis → fig}/model/_annotations.py +1 -1
  161. scitex/{vis → fig}/model/_axes.py +1 -1
  162. scitex/{vis → fig}/model/_figure.py +1 -1
  163. scitex/{vis → fig}/model/_guides.py +1 -1
  164. scitex/{vis → fig}/model/_plot.py +1 -1
  165. scitex/{vis → fig}/model/_styles.py +1 -1
  166. scitex/{vis → fig}/utils/__init__.py +1 -1
  167. scitex/io/__init__.py +22 -26
  168. scitex/io/_bundle.py +493 -0
  169. scitex/io/_flush.py +5 -2
  170. scitex/io/_load.py +98 -0
  171. scitex/io/_load_modules/_H5Explorer.py +5 -2
  172. scitex/io/_load_modules/_canvas.py +2 -2
  173. scitex/io/_load_modules/_image.py +3 -4
  174. scitex/io/_load_modules/_txt.py +4 -2
  175. scitex/io/_metadata.py +34 -324
  176. scitex/io/_metadata_modules/__init__.py +46 -0
  177. scitex/io/_metadata_modules/_embed.py +70 -0
  178. scitex/io/_metadata_modules/_read.py +64 -0
  179. scitex/io/_metadata_modules/_utils.py +79 -0
  180. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  181. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  182. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  183. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  184. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  185. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  186. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  187. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  188. scitex/io/_qr_utils.py +5 -3
  189. scitex/io/_save.py +548 -30
  190. scitex/io/_save_modules/_canvas.py +3 -3
  191. scitex/io/_save_modules/_image.py +5 -9
  192. scitex/io/_save_modules/_tex.py +7 -4
  193. scitex/io/_zip_bundle.py +439 -0
  194. scitex/io/utils/h5_to_zarr.py +11 -9
  195. scitex/msword/__init__.py +255 -0
  196. scitex/msword/profiles.py +357 -0
  197. scitex/msword/reader.py +753 -0
  198. scitex/msword/utils.py +289 -0
  199. scitex/msword/writer.py +362 -0
  200. scitex/plt/__init__.py +5 -2
  201. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  202. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  203. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  204. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  205. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  206. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  207. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  208. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  209. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  210. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  211. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  212. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  213. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  214. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  215. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  216. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  217. scitex/plt/_subplots/_FigWrapper.py +6 -6
  218. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  219. scitex/plt/_subplots/_export_as_csv.py +35 -5
  220. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  221. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  222. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  223. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  224. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  225. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  226. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  227. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  228. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  229. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  230. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  231. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  232. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  233. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  234. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  235. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  236. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  237. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  238. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  239. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  240. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  241. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  242. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  243. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  244. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  245. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  246. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  247. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  248. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  249. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  250. scitex/plt/gallery/_generate.py +421 -14
  251. scitex/plt/io/__init__.py +53 -0
  252. scitex/plt/io/_bundle.py +490 -0
  253. scitex/plt/io/_layered_bundle.py +1343 -0
  254. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  255. scitex/plt/styles/__init__.py +14 -0
  256. scitex/plt/styles/presets.py +78 -0
  257. scitex/plt/utils/__init__.py +13 -1
  258. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  259. scitex/plt/utils/_configure_mpl.py +6 -18
  260. scitex/plt/utils/_crop.py +32 -14
  261. scitex/plt/utils/_csv_column_naming.py +54 -0
  262. scitex/plt/utils/_figure_mm.py +116 -1
  263. scitex/plt/utils/_hitmap.py +1643 -0
  264. scitex/plt/utils/metadata/__init__.py +25 -0
  265. scitex/plt/utils/metadata/_core.py +9 -10
  266. scitex/plt/utils/metadata/_dimensions.py +6 -3
  267. scitex/plt/utils/metadata/_editable_export.py +405 -0
  268. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  269. scitex/schema/__init__.py +109 -16
  270. scitex/schema/_canvas.py +1 -1
  271. scitex/schema/_plot.py +1015 -0
  272. scitex/schema/_stats.py +2 -2
  273. scitex/stats/__init__.py +117 -0
  274. scitex/stats/io/__init__.py +29 -0
  275. scitex/stats/io/_bundle.py +156 -0
  276. scitex/tex/__init__.py +4 -0
  277. scitex/tex/_export.py +890 -0
  278. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
  279. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
  280. scitex/io/memo.md +0 -2827
  281. scitex/plt/REQUESTS.md +0 -191
  282. scitex/plt/_subplots/TODO.md +0 -53
  283. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  284. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  285. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  286. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  287. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  288. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  289. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  290. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  291. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  292. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  293. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  294. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  295. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  296. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  297. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  298. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  299. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  300. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  301. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  302. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  303. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  304. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  305. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  306. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  307. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  308. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  309. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  310. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  311. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  312. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  313. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  314. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  315. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  316. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  317. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  318. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  319. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  320. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  321. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  322. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  323. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  324. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  325. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  326. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  327. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  328. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  329. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  330. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  331. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  332. scitex/vis/__init__.py +0 -177
  333. scitex/vis/editor/_edit.py +0 -390
  334. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  335. scitex/vis/editor/flask_editor/_core.py +0 -168
  336. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  337. scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
  338. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  339. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  340. scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
  341. /scitex/{vis → fig}/README.md +0 -0
  342. /scitex/{vis → fig}/backend/__init__.py +0 -0
  343. /scitex/{vis → fig}/backend/_export.py +0 -0
  344. /scitex/{vis → fig}/backend/_render.py +0 -0
  345. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  346. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  347. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  348. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  349. /scitex/{vis → fig}/io/_directory.py +0 -0
  350. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  351. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  352. /scitex/{vis → fig}/utils/_validate.py +0 -0
  353. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  354. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  355. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -56,6 +56,7 @@ class QtEditor:
56
56
  - Real-time preview updates
57
57
  - Save to .manual.json
58
58
  - SciTeX style defaults pre-filled
59
+ - Hitmap-based element selection (when available)
59
60
  """
60
61
 
61
62
  def __init__(
@@ -65,12 +66,32 @@ class QtEditor:
65
66
  csv_data: Optional[Any] = None,
66
67
  png_path: Optional[Path] = None,
67
68
  manual_overrides: Optional[Dict[str, Any]] = None,
69
+ hitmap_path: Optional[Path] = None,
70
+ bundle_spec: Optional[Dict[str, Any]] = None,
68
71
  ):
69
72
  self.json_path = Path(json_path)
70
73
  self.metadata = metadata
71
74
  self.csv_data = csv_data
72
75
  self.png_path = Path(png_path) if png_path else None
73
76
  self.manual_overrides = manual_overrides or {}
77
+ self.hitmap_path = Path(hitmap_path) if hitmap_path else None
78
+ self.bundle_spec = bundle_spec or {}
79
+
80
+ # Load hitmap image if available
81
+ self.hitmap_array = None
82
+ if self.hitmap_path and self.hitmap_path.exists():
83
+ try:
84
+ from PIL import Image
85
+ import numpy as np
86
+ hitmap_img = Image.open(self.hitmap_path).convert('RGB')
87
+ self.hitmap_array = np.array(hitmap_img)
88
+ except Exception:
89
+ pass
90
+
91
+ # Extract hit_regions and selectable_regions from spec
92
+ self.hit_regions = self.bundle_spec.get('hit_regions', {})
93
+ self.selectable_regions = self.bundle_spec.get('selectable_regions', {})
94
+ self.color_map = self.hit_regions.get('color_map', {})
74
95
 
75
96
  # Get SciTeX defaults and merge with metadata
76
97
  from ._defaults import get_scitex_defaults, extract_defaults_from_metadata
@@ -164,7 +185,7 @@ class QtEditorWindow:
164
185
  self.window.show()
165
186
 
166
187
  def _create_canvas_panel(self, parent):
167
- """Create the matplotlib canvas panel."""
188
+ """Create the matplotlib canvas panel with hitmap-based selection."""
168
189
  from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
169
190
  from matplotlib.backends.backend_qtagg import NavigationToolbar2QT
170
191
  from matplotlib.figure import Figure
@@ -184,8 +205,167 @@ class QtEditorWindow:
184
205
  toolbar = NavigationToolbar2QT(self.canvas, canvas_widget)
185
206
  canvas_layout.addWidget(toolbar)
186
207
 
208
+ # Connect click event for hitmap-based selection
209
+ self.canvas.mpl_connect('button_press_event', self._on_canvas_click)
210
+
211
+ # Selection info label
212
+ self.selection_label = self.QtWidgets.QLabel("Click on figure to select element")
213
+ self.selection_label.setStyleSheet("padding: 5px; background: #f0f0f0; border-radius: 3px;")
214
+ canvas_layout.addWidget(self.selection_label)
215
+
187
216
  parent.addWidget(canvas_widget)
188
217
 
218
+ def _on_canvas_click(self, event):
219
+ """Handle canvas click - use hitmap to identify clicked element."""
220
+ if event.inaxes is None:
221
+ return
222
+
223
+ # Get pixel coordinates
224
+ x_px = int(event.x)
225
+ y_px = int(self.canvas.get_width_height()[1] - event.y) # Flip Y
226
+
227
+ # First check selectable_regions (bounding boxes for axis elements)
228
+ selection_info = self._check_selectable_regions(x_px, y_px)
229
+
230
+ # If no selectable_region hit, try hitmap for data elements
231
+ if selection_info is None and self.editor.hitmap_array is not None:
232
+ selection_info = self._check_hitmap(x_px, y_px)
233
+
234
+ # Update selection display
235
+ if selection_info:
236
+ self._show_selection(selection_info)
237
+ else:
238
+ self.selection_label.setText(f"No element at ({x_px}, {y_px})")
239
+ self.status_bar.showMessage(f"Click at ({x_px}, {y_px}) - no element found")
240
+
241
+ def _check_selectable_regions(self, x_px: int, y_px: int) -> Optional[Dict[str, Any]]:
242
+ """Check if click is within any selectable_region bounding box."""
243
+ regions = self.editor.selectable_regions
244
+ if not regions or 'axes' not in regions:
245
+ return None
246
+
247
+ for ax_info in regions['axes']:
248
+ ax_idx = ax_info.get('index', 0)
249
+
250
+ # Check title
251
+ if 'title' in ax_info:
252
+ bbox = ax_info['title'].get('bbox_px', [])
253
+ if len(bbox) == 4 and self._point_in_bbox(x_px, y_px, bbox):
254
+ return {
255
+ 'type': 'title',
256
+ 'axes_index': ax_idx,
257
+ 'text': ax_info['title'].get('text', ''),
258
+ 'element': 'title',
259
+ }
260
+
261
+ # Check xlabel
262
+ if 'xlabel' in ax_info:
263
+ bbox = ax_info['xlabel'].get('bbox_px', [])
264
+ if len(bbox) == 4 and self._point_in_bbox(x_px, y_px, bbox):
265
+ return {
266
+ 'type': 'xlabel',
267
+ 'axes_index': ax_idx,
268
+ 'text': ax_info['xlabel'].get('text', ''),
269
+ 'element': 'xlabel',
270
+ }
271
+
272
+ # Check ylabel
273
+ if 'ylabel' in ax_info:
274
+ bbox = ax_info['ylabel'].get('bbox_px', [])
275
+ if len(bbox) == 4 and self._point_in_bbox(x_px, y_px, bbox):
276
+ return {
277
+ 'type': 'ylabel',
278
+ 'axes_index': ax_idx,
279
+ 'text': ax_info['ylabel'].get('text', ''),
280
+ 'element': 'ylabel',
281
+ }
282
+
283
+ # Check legend
284
+ if 'legend' in ax_info:
285
+ bbox = ax_info['legend'].get('bbox_px', [])
286
+ if len(bbox) == 4 and self._point_in_bbox(x_px, y_px, bbox):
287
+ return {
288
+ 'type': 'legend',
289
+ 'axes_index': ax_idx,
290
+ 'element': 'legend',
291
+ }
292
+
293
+ # Check xaxis elements
294
+ if 'xaxis' in ax_info:
295
+ xaxis = ax_info['xaxis']
296
+ if 'spine' in xaxis and xaxis['spine']:
297
+ bbox = xaxis['spine'].get('bbox_px', [])
298
+ if len(bbox) == 4 and self._point_in_bbox(x_px, y_px, bbox):
299
+ return {'type': 'xaxis_spine', 'axes_index': ax_idx, 'element': 'xaxis'}
300
+
301
+ # Check yaxis elements
302
+ if 'yaxis' in ax_info:
303
+ yaxis = ax_info['yaxis']
304
+ if 'spine' in yaxis and yaxis['spine']:
305
+ bbox = yaxis['spine'].get('bbox_px', [])
306
+ if len(bbox) == 4 and self._point_in_bbox(x_px, y_px, bbox):
307
+ return {'type': 'yaxis_spine', 'axes_index': ax_idx, 'element': 'yaxis'}
308
+
309
+ return None
310
+
311
+ def _check_hitmap(self, x_px: int, y_px: int) -> Optional[Dict[str, Any]]:
312
+ """Check hitmap for data element at pixel position."""
313
+ hitmap = self.editor.hitmap_array
314
+ color_map = self.editor.color_map
315
+
316
+ if hitmap is None or not color_map:
317
+ return None
318
+
319
+ # Bounds check
320
+ h, w = hitmap.shape[:2]
321
+ if x_px < 0 or x_px >= w or y_px < 0 or y_px >= h:
322
+ return None
323
+
324
+ # Get RGB at position
325
+ r, g, b = hitmap[y_px, x_px]
326
+
327
+ # Skip background (black) and axes elements (dark gray)
328
+ if (r, g, b) == (0, 0, 0): # Background
329
+ return None
330
+ if (r, g, b) == (64, 64, 64): # Axes color (#404040)
331
+ return None
332
+
333
+ # Convert RGB to ID
334
+ element_id = r * 65536 + g * 256 + b
335
+
336
+ # Look up in color_map
337
+ if str(element_id) in color_map:
338
+ info = color_map[str(element_id)]
339
+ return {
340
+ 'type': 'data_element',
341
+ 'element_id': element_id,
342
+ 'element_type': info.get('type', 'unknown'),
343
+ 'label': info.get('label', ''),
344
+ 'axes_index': info.get('axes_index', 0),
345
+ 'rgb': [r, g, b],
346
+ }
347
+
348
+ return None
349
+
350
+ def _point_in_bbox(self, x: int, y: int, bbox: list) -> bool:
351
+ """Check if point (x, y) is within bounding box [x0, y0, x1, y1]."""
352
+ x0, y0, x1, y1 = bbox
353
+ return x0 <= x <= x1 and y0 <= y <= y1
354
+
355
+ def _show_selection(self, info: Dict[str, Any]):
356
+ """Display selection info in UI."""
357
+ if info['type'] == 'data_element':
358
+ text = f"Selected: {info['element_type']} - {info.get('label', 'unnamed')}"
359
+ detail = f"ID: {info['element_id']}, Axes: {info['axes_index']}"
360
+ else:
361
+ text = f"Selected: {info['type']}"
362
+ if 'text' in info:
363
+ text += f" - \"{info['text']}\""
364
+ detail = f"Axes: {info.get('axes_index', 0)}"
365
+
366
+ self.selection_label.setText(f"{text}\n{detail}")
367
+ self.status_bar.showMessage(text)
368
+
189
369
  def _create_control_panel(self, parent):
190
370
  """Create the control panel with property editors."""
191
371
  scroll_area = self.QtWidgets.QScrollArea()
@@ -837,7 +1017,7 @@ class QtEditorWindow:
837
1017
 
838
1018
  def _save_manual(self):
839
1019
  """Save to .manual.json."""
840
- from ._edit import save_manual_overrides
1020
+ from .edit import save_manual_overrides
841
1021
 
842
1022
  try:
843
1023
  self._collect_overrides()
@@ -490,7 +490,7 @@ class TkinterEditor:
490
490
 
491
491
  def _save_manual(self):
492
492
  """Save current overrides to .manual.json."""
493
- from ._edit import save_manual_overrides
493
+ from .edit import save_manual_overrides
494
494
 
495
495
  try:
496
496
  manual_path = save_manual_overrides(self.json_path, self.current_overrides)
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/__init__.py
5
+
6
+ """Edit module for launching visual figure editors.
7
+
8
+ This module provides the main edit() function and supporting utilities for:
9
+ - Backend detection and selection (flask, dearpygui, qt, tkinter, mpl)
10
+ - Path resolution for figure files (.json, .csv, .png)
11
+ - Bundle resolution for .pltz and .figz formats
12
+ - Panel data loading for multi-panel figures
13
+ - Manual override handling (.manual.json)
14
+ """
15
+
16
+ from .editor_launcher import edit
17
+ from .backend_detector import (
18
+ detect_best_backend,
19
+ print_available_backends,
20
+ )
21
+ from .path_resolver import resolve_figure_paths
22
+ from .bundle_resolver import (
23
+ resolve_pltz_bundle,
24
+ resolve_figz_bundle,
25
+ resolve_layered_pltz_bundle,
26
+ )
27
+ from .panel_loader import load_panel_data
28
+ from .manual_handler import compute_file_hash, save_manual_overrides
29
+
30
+ __all__ = [
31
+ # Main entry point
32
+ "edit",
33
+ # Backend detection
34
+ "detect_best_backend",
35
+ "print_available_backends",
36
+ # Path resolution
37
+ "resolve_figure_paths",
38
+ # Bundle resolution
39
+ "resolve_pltz_bundle",
40
+ "resolve_figz_bundle",
41
+ "resolve_layered_pltz_bundle",
42
+ # Panel loading
43
+ "load_panel_data",
44
+ # Manual overrides
45
+ "compute_file_hash",
46
+ "save_manual_overrides",
47
+ ]
48
+
49
+
50
+ # EOF
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/backend_detector.py
5
+
6
+ """Backend detection and selection for figure editor."""
7
+
8
+ import warnings
9
+
10
+ __all__ = ["print_available_backends", "detect_best_backend"]
11
+
12
+ # Backend packages mapping
13
+ BACKENDS = {
14
+ "flask": ["flask"],
15
+ "dearpygui": ["dearpygui"],
16
+ "qt": ["PyQt6", "PyQt5", "PySide6", "PySide2"],
17
+ "tkinter": ["tkinter"],
18
+ "mpl": ["matplotlib"],
19
+ }
20
+
21
+
22
+ def print_available_backends() -> None:
23
+ """Print available backends status."""
24
+ print("\n" + "=" * 50)
25
+ print("SciTeX Visual Editor - Available Backends")
26
+ print("=" * 50)
27
+
28
+ for backend, packages in BACKENDS.items():
29
+ available = False
30
+ available_pkg = None
31
+ for pkg in packages:
32
+ try:
33
+ __import__(pkg)
34
+ available = True
35
+ available_pkg = pkg
36
+ break
37
+ except ImportError:
38
+ pass
39
+
40
+ status = f"[OK] {available_pkg}" if available else "[NOT INSTALLED]"
41
+ print(f" {backend:12s}: {status}")
42
+
43
+ print("=" * 50)
44
+ print("Install: pip install scitex[gui]")
45
+ print("=" * 50 + "\n")
46
+
47
+
48
+ def detect_best_backend() -> str:
49
+ """
50
+ Detect the best available GUI backend with graceful degradation.
51
+
52
+ Order: flask > dearpygui > qt > tkinter > mpl
53
+ Shows warnings when falling back to less capable backends.
54
+ """
55
+ # Try Flask - best for modern UI
56
+ try:
57
+ import flask
58
+ return "flask"
59
+ except ImportError:
60
+ pass
61
+
62
+ # Try DearPyGui - GPU-accelerated, modern
63
+ try:
64
+ import dearpygui
65
+ return "dearpygui"
66
+ except ImportError:
67
+ warnings.warn(
68
+ "Flask not available. Consider: pip install flask\nTrying DearPyGui..."
69
+ )
70
+
71
+ # Try DearPyGui again
72
+ try:
73
+ import dearpygui
74
+ return "dearpygui"
75
+ except ImportError:
76
+ pass
77
+
78
+ # Try Qt (richest desktop features)
79
+ for qt_pkg in ["PyQt6", "PyQt5", "PySide6", "PySide2"]:
80
+ try:
81
+ __import__(qt_pkg)
82
+ warnings.warn(
83
+ "DearPyGui not available. Consider: pip install dearpygui\n"
84
+ "Using Qt backend instead."
85
+ )
86
+ return "qt"
87
+ except ImportError:
88
+ pass
89
+
90
+ # Try Tkinter (built-in, good features)
91
+ try:
92
+ import tkinter
93
+ warnings.warn(
94
+ "Qt not available. Consider: pip install PyQt6\n"
95
+ "Using Tkinter backend (basic features)."
96
+ )
97
+ return "tkinter"
98
+ except ImportError:
99
+ pass
100
+
101
+ # Fall back to matplotlib interactive (always works)
102
+ warnings.warn(
103
+ "No GUI toolkit found. Using minimal matplotlib editor.\n"
104
+ "For better experience, install: pip install flask (web) or pip install PyQt6 (desktop)"
105
+ )
106
+ return "mpl"
107
+
108
+
109
+ # EOF
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/bundle_resolver.py
5
+
6
+ """Bundle path resolution for .pltz and .figz formats."""
7
+
8
+ import json as json_module
9
+ import tempfile
10
+ import zipfile
11
+ from pathlib import Path
12
+ from typing import Optional, Tuple, Dict, Any
13
+
14
+ __all__ = ["resolve_pltz_bundle", "resolve_figz_bundle", "resolve_layered_pltz_bundle"]
15
+
16
+
17
+ def resolve_figz_bundle(path: Path, panel_index: int = 0) -> Tuple:
18
+ """
19
+ Resolve paths from a .figz bundle (multi-panel figure).
20
+
21
+ Uses in-memory zip reading for .pltz panels - no disk extraction.
22
+
23
+ Parameters
24
+ ----------
25
+ path : Path
26
+ Path to .figz bundle (.figz or .figz.d)
27
+ panel_index : int, optional
28
+ Index of panel to open (default: 0 for first panel)
29
+
30
+ Returns
31
+ -------
32
+ tuple
33
+ (json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info)
34
+ """
35
+ spath = str(path)
36
+ figz_is_zip = False
37
+
38
+ # Handle ZIP vs directory for figz
39
+ if spath.endswith('.figz') and not spath.endswith('.figz.d'):
40
+ figz_is_zip = True
41
+ if not path.exists():
42
+ raise FileNotFoundError(f"Figz bundle not found: {path}")
43
+ # For figz zip, extract to access nested pltz
44
+ temp_dir = tempfile.mkdtemp(prefix='scitex_edit_figz_')
45
+ with zipfile.ZipFile(path, 'r') as zf:
46
+ zf.extractall(temp_dir)
47
+ bundle_dir = Path(temp_dir)
48
+ for item in bundle_dir.iterdir():
49
+ if item.is_dir() and str(item).endswith('.figz.d'):
50
+ bundle_dir = item
51
+ break
52
+ else:
53
+ bundle_dir = Path(path)
54
+ if not bundle_dir.exists():
55
+ raise FileNotFoundError(f"Figz bundle directory not found: {bundle_dir}")
56
+
57
+ # Find nested pltz bundles
58
+ panel_paths = []
59
+ panel_is_zip = []
60
+
61
+ for item in sorted(bundle_dir.iterdir(), key=lambda x: x.name):
62
+ if item.is_dir() and str(item).endswith('.pltz.d'):
63
+ panel_paths.append(str(item))
64
+ panel_is_zip.append(False)
65
+ elif item.is_file() and str(item).endswith('.pltz'):
66
+ panel_paths.append(str(item))
67
+ panel_is_zip.append(True)
68
+
69
+ if not panel_paths:
70
+ raise FileNotFoundError(f"No .pltz panels found in figz bundle: {bundle_dir}")
71
+
72
+ # Validate panel index
73
+ if panel_index < 0 or panel_index >= len(panel_paths):
74
+ panel_index = 0
75
+
76
+ selected_panel_path = panel_paths[panel_index]
77
+ panel_name = Path(selected_panel_path).name
78
+ print(f"Opening panel: {panel_name}")
79
+ if len(panel_paths) > 1:
80
+ print(f" (Figz contains {len(panel_paths)} panels)")
81
+
82
+ # Build panel info
83
+ panel_names = [Path(p).name for p in panel_paths]
84
+ panel_info = {
85
+ "panels": panel_names,
86
+ "panel_paths": panel_paths,
87
+ "panel_is_zip": panel_is_zip,
88
+ "current_index": panel_index,
89
+ "figz_dir": str(bundle_dir),
90
+ "figz_is_zip": figz_is_zip,
91
+ "bundle_path": str(path) if figz_is_zip else None, # Original figz zip path for export/download
92
+ }
93
+
94
+ # Resolve the selected panel
95
+ result = resolve_pltz_bundle(Path(selected_panel_path))
96
+ return result + (panel_info,)
97
+
98
+
99
+ def resolve_pltz_bundle(path: Path) -> Tuple:
100
+ """
101
+ Resolve paths from a .pltz bundle (directory or ZIP).
102
+
103
+ Supports both:
104
+ - Legacy format (single {basename}.json)
105
+ - Layered format v2.0 (spec.json + style.json + cache/)
106
+
107
+ Parameters
108
+ ----------
109
+ path : Path
110
+ Path to .pltz bundle (.pltz or .pltz.d)
111
+
112
+ Returns
113
+ -------
114
+ tuple
115
+ (json_path, csv_path, png_path, hitmap_path, bundle_spec)
116
+ """
117
+ spath = str(path)
118
+
119
+ # Handle ZIP vs directory
120
+ if spath.endswith('.pltz') and not spath.endswith('.pltz.d'):
121
+ if not path.exists():
122
+ raise FileNotFoundError(f"Bundle not found: {path}")
123
+ temp_dir = tempfile.mkdtemp(prefix='scitex_edit_')
124
+ with zipfile.ZipFile(path, 'r') as zf:
125
+ zf.extractall(temp_dir)
126
+ bundle_dir = Path(temp_dir)
127
+ for item in bundle_dir.iterdir():
128
+ if item.is_dir() and str(item).endswith('.pltz.d'):
129
+ bundle_dir = item
130
+ break
131
+ else:
132
+ bundle_dir = Path(path)
133
+ if not bundle_dir.exists():
134
+ raise FileNotFoundError(f"Bundle directory not found: {bundle_dir}")
135
+
136
+ # Check if this is a layered bundle (v2.0)
137
+ spec_path = bundle_dir / "spec.json"
138
+ if spec_path.exists():
139
+ return resolve_layered_pltz_bundle(bundle_dir)
140
+
141
+ # === Legacy format ===
142
+ json_path = None
143
+ csv_path = None
144
+ png_path = None
145
+ svg_path = None
146
+ hitmap_path = None
147
+ bundle_spec = None
148
+
149
+ for f in bundle_dir.iterdir():
150
+ name = f.name
151
+ if name.endswith('.json') and not name.endswith('.manual.json'):
152
+ json_path = f
153
+ elif name.endswith('.csv'):
154
+ csv_path = f
155
+ elif name.endswith('_hitmap.png'):
156
+ hitmap_path = f
157
+ elif name.endswith('.svg') and '_hitmap' not in name:
158
+ svg_path = f
159
+ elif name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
160
+ png_path = f
161
+
162
+ # Prefer SVG for display
163
+ if svg_path:
164
+ png_path = svg_path
165
+
166
+ if json_path and json_path.exists():
167
+ with open(json_path, 'r') as f:
168
+ bundle_spec = json_module.load(f)
169
+
170
+ return (
171
+ json_path,
172
+ csv_path if csv_path and csv_path.exists() else None,
173
+ png_path if png_path and png_path.exists() else None,
174
+ hitmap_path if hitmap_path and hitmap_path.exists() else None,
175
+ bundle_spec,
176
+ )
177
+
178
+
179
+ def resolve_layered_pltz_bundle(bundle_dir: Path) -> Tuple:
180
+ """
181
+ Resolve paths from a layered .pltz bundle (v2.0 format).
182
+
183
+ Layered format structure:
184
+ plot.pltz.d/
185
+ spec.json # Semantic
186
+ style.json # Appearance
187
+ {basename}.csv # Data
188
+ exports/ # PNG, SVG, hitmap
189
+ cache/ # geometry_px.json
190
+
191
+ Parameters
192
+ ----------
193
+ bundle_dir : Path
194
+ Path to .pltz.d bundle directory.
195
+
196
+ Returns
197
+ -------
198
+ tuple
199
+ (json_path, csv_path, png_path, hitmap_path, bundle_spec)
200
+ """
201
+ from scitex.plt.io import load_layered_pltz_bundle
202
+
203
+ bundle_data = load_layered_pltz_bundle(bundle_dir)
204
+ spec_path = bundle_dir / "spec.json"
205
+ csv_path = None
206
+ png_path = None
207
+ hitmap_path = None
208
+
209
+ # Find CSV
210
+ for f in bundle_dir.glob("*.csv"):
211
+ csv_path = f
212
+ break
213
+
214
+ # Find exports
215
+ exports_dir = bundle_dir / "exports"
216
+ if exports_dir.exists():
217
+ for f in exports_dir.iterdir():
218
+ name = f.name
219
+ if name.endswith('_hitmap.png'):
220
+ hitmap_path = f
221
+ elif name.endswith('.svg') and '_hitmap' not in name:
222
+ png_path = f
223
+ elif name.endswith('.png') and '_hitmap' not in name and png_path is None:
224
+ png_path = f
225
+
226
+ bundle_spec = bundle_data.get("merged", {})
227
+
228
+ if hitmap_path and "hit_regions" in bundle_spec:
229
+ bundle_spec["hit_regions"]["hit_map"] = str(hitmap_path.name)
230
+
231
+ return (
232
+ spec_path,
233
+ csv_path if csv_path and csv_path.exists() else None,
234
+ png_path if png_path and png_path.exists() else None,
235
+ hitmap_path if hitmap_path and hitmap_path.exists() else None,
236
+ bundle_spec,
237
+ )
238
+
239
+
240
+ # EOF