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
@@ -102,3 +102,29 @@ behavior:
102
102
  hide_right_spine: true # Hide right spine
103
103
  grid: false # Show grid
104
104
 
105
+ # =============================================================================
106
+ # THEME / COLOR MODE
107
+ # =============================================================================
108
+ # Dark mode uses eye-friendly colors optimized for dark backgrounds
109
+ # Light mode uses standard black on white (default matplotlib behavior)
110
+ theme:
111
+ mode: "light" # "light" or "dark"
112
+
113
+ # Dark mode color palette (eye-friendly, reduced strain)
114
+ dark:
115
+ background: "transparent" # Figure background (transparent by default)
116
+ axes_bg: "#1a1a2e" # Axes background (deep navy-black)
117
+ text: "#e8e8e8" # Text, labels, titles (soft white)
118
+ spine: "#4a4a5a" # Axis spines (muted gray)
119
+ tick: "#e8e8e8" # Tick marks and labels
120
+ grid: "#3a3a4a" # Grid lines (subtle)
121
+
122
+ # Light mode color palette (publication standard)
123
+ light:
124
+ background: "transparent" # Figure background
125
+ axes_bg: "white" # Axes background
126
+ text: "black" # Text, labels, titles
127
+ spine: "black" # Axis spines
128
+ tick: "black" # Tick marks and labels
129
+ grid: "#cccccc" # Grid lines
130
+
@@ -37,6 +37,13 @@ from .presets import (
37
37
  set_style,
38
38
  get_style,
39
39
  resolve_style_value,
40
+ # DPI utilities
41
+ get_default_dpi,
42
+ get_display_dpi,
43
+ get_preview_dpi,
44
+ DPI_SAVE,
45
+ DPI_DISPLAY,
46
+ DPI_PREVIEW,
40
47
  )
41
48
 
42
49
  __all__ = [
@@ -51,6 +58,13 @@ __all__ = [
51
58
  "set_style",
52
59
  "get_style",
53
60
  "resolve_style_value",
61
+ # DPI utilities
62
+ "get_default_dpi",
63
+ "get_display_dpi",
64
+ "get_preview_dpi",
65
+ "DPI_SAVE",
66
+ "DPI_DISPLAY",
67
+ "DPI_PREVIEW",
54
68
  ]
55
69
 
56
70
 
@@ -40,6 +40,13 @@ __all__ = [
40
40
  "set_style",
41
41
  "get_style",
42
42
  "resolve_style_value",
43
+ # DPI utilities
44
+ "get_default_dpi",
45
+ "get_display_dpi",
46
+ "get_preview_dpi",
47
+ "DPI_SAVE",
48
+ "DPI_DISPLAY",
49
+ "DPI_PREVIEW",
43
50
  ]
44
51
 
45
52
  from pathlib import Path
@@ -79,6 +86,77 @@ def resolve_style_value(
79
86
  return _get_config().resolve(key, direct_val, default, type)
80
87
 
81
88
 
89
+ # =============================================================================
90
+ # DPI Resolution - Central Source of Truth
91
+ # =============================================================================
92
+ #
93
+ # DPI Priority Chain:
94
+ # 1. Bundle's geometry_px.json (highest - existing figure's actual DPI)
95
+ # 2. User override / session setting
96
+ # 3. SCITEX_STYLE.yaml output.dpi (project default)
97
+ # 4. Hardcoded fallback (only if config missing)
98
+ #
99
+ # Usage:
100
+ # from scitex.plt.styles import get_default_dpi, DPI_SAVE
101
+ #
102
+ # # For saving figures (publication quality)
103
+ # fig.savefig("out.png", dpi=get_default_dpi())
104
+ #
105
+ # # For display/preview (lower resolution)
106
+ # fig.savefig("preview.png", dpi=get_display_dpi())
107
+ #
108
+ # # When loading from bundle, use bundle's DPI:
109
+ # dpi = bundle.get("geometry", {}).get("dpi") or get_default_dpi()
110
+ #
111
+
112
+ # Fallback values (only used if config unavailable)
113
+ _FALLBACK_DPI_SAVE = 300
114
+ _FALLBACK_DPI_DISPLAY = 100
115
+ _FALLBACK_DPI_PREVIEW = 150
116
+
117
+
118
+ def get_default_dpi() -> int:
119
+ """Get default DPI for saving/publication from config.
120
+
121
+ Priority: SCITEX_STYLE.yaml → env var → fallback 300
122
+
123
+ Returns:
124
+ int: DPI value for publication-quality output
125
+ """
126
+ return int(resolve_style_value("output.dpi", None, _FALLBACK_DPI_SAVE, int))
127
+
128
+
129
+ def get_display_dpi() -> int:
130
+ """Get DPI for screen display (lower resolution for speed).
131
+
132
+ Returns approximately 1/3 of save DPI, minimum 100.
133
+
134
+ Returns:
135
+ int: DPI value for screen display
136
+ """
137
+ save_dpi = get_default_dpi()
138
+ return max(_FALLBACK_DPI_DISPLAY, save_dpi // 3)
139
+
140
+
141
+ def get_preview_dpi() -> int:
142
+ """Get DPI for editor previews and thumbnails.
143
+
144
+ Returns 1/2 of save DPI, clamped between 100-200.
145
+
146
+ Returns:
147
+ int: DPI value for previews
148
+ """
149
+ save_dpi = get_default_dpi()
150
+ return max(_FALLBACK_DPI_DISPLAY, min(200, save_dpi // 2))
151
+
152
+
153
+ # Module-level constants (evaluated at import time)
154
+ # Use functions for dynamic resolution, constants for static defaults
155
+ DPI_SAVE = _FALLBACK_DPI_SAVE # Use get_default_dpi() for dynamic
156
+ DPI_DISPLAY = _FALLBACK_DPI_DISPLAY
157
+ DPI_PREVIEW = _FALLBACK_DPI_PREVIEW
158
+
159
+
82
160
  def load_style(path: Optional[Union[str, Path]] = None) -> Dict[str, Any]:
83
161
  """Load style from YAML as subplots kwargs."""
84
162
  cfg = _get_config(Path(path) if path else None)
@@ -6,7 +6,7 @@ from ._calc_nice_ticks import calc_nice_ticks
6
6
  from ._close import close
7
7
  from ._colorbar import add_shared_colorbar, colorbar
8
8
  from ._configure_mpl import configure_mpl
9
- from ._figure_mm import apply_style_mm, create_figure_ax_mm
9
+ from ._figure_mm import apply_style_mm, create_figure_ax_mm, THEME_COLORS, _apply_theme_colors
10
10
  from ._histogram_utils import HistogramBinManager, histogram_bin_manager
11
11
  from ._im2grid import im2grid
12
12
  from ._is_valid_axis import assert_valid_axis, is_valid_axis
@@ -48,6 +48,13 @@ from ._csv_column_naming import (
48
48
  parse_csv_column_name,
49
49
  sanitize_trace_id,
50
50
  )
51
+ from ._hitmap import (
52
+ extract_path_data,
53
+ generate_hitmap_id_colors,
54
+ get_all_artists,
55
+ query_hitmap_neighborhood,
56
+ save_hitmap_png,
57
+ )
51
58
 
52
59
  __all__ = [
53
60
  "HistogramBinManager",
@@ -79,6 +86,9 @@ __all__ = [
79
86
  "cross_ref",
80
87
  "enhance_scitex_save_with_captions",
81
88
  "export_captions",
89
+ "extract_path_data",
90
+ "generate_hitmap_id_colors",
91
+ "get_all_artists",
82
92
  "get_csv_column_name",
83
93
  "get_csv_column_prefix",
84
94
  "get_dimension_info",
@@ -94,8 +104,10 @@ __all__ = [
94
104
  "parse_csv_column_name",
95
105
  "print_dimension_info",
96
106
  "pt_to_mm",
107
+ "query_hitmap_neighborhood",
97
108
  "quick_caption",
98
109
  "sanitize_trace_id",
110
+ "save_hitmap_png",
99
111
  "save_with_caption",
100
112
  "verify_csv_json_consistency",
101
113
  "view_dimensions",
@@ -15,6 +15,10 @@ __FILE__ = __file__
15
15
 
16
16
  from typing import Dict, Optional, Union, List
17
17
 
18
+ from scitex import logging
19
+
20
+ logger = logging.getLogger(__name__)
21
+
18
22
  # Precision settings for JSON output
19
23
  PRECISION = {
20
24
  "mm": 2, # Millimeters: 0.01mm precision (10 microns)
@@ -205,8 +209,7 @@ def _collect_single_axes_metadata(fig, ax, ax_index: int) -> dict:
205
209
  }
206
210
 
207
211
  except Exception as e:
208
- import warnings
209
- warnings.warn(f"Could not extract dimension info for axes {ax_index}: {e}")
212
+ logger.warning(f"Could not extract dimension info for axes {ax_index}: {e}")
210
213
 
211
214
  # Extract axes labels and units
212
215
  # X-axis - using matplotlib terminology (xaxis)
@@ -444,8 +447,7 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
444
447
  if "axes_bbox_mm" in dim_info:
445
448
  metadata["axes_bbox_mm"] = dim_info["axes_bbox_mm"]
446
449
  except Exception as e:
447
- import warnings
448
- warnings.warn(f"Could not extract figure dimension info: {e}")
450
+ logger.warning(f"Could not extract figure dimension info: {e}")
449
451
 
450
452
  # Collect per-axes metadata
451
453
  if all_axes:
@@ -461,8 +463,7 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
461
463
  ax_metadata["grid_position"] = {"row": row, "col": col}
462
464
  metadata["axes"][ax_key] = ax_metadata
463
465
  except Exception as e:
464
- import warnings
465
- warnings.warn(f"Could not extract metadata for {ax_key}: {e}")
466
+ logger.warning(f"Could not extract metadata for {ax_key}: {e}")
466
467
 
467
468
  # Add scitex-specific metadata if axes was tagged
468
469
  scitex_meta = None
@@ -542,11 +543,8 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
542
543
  f"For {requested_font}: sudo apt-get install ttf-mscorefonts-installer && fc-cache -fv"
543
544
  )
544
545
  except ImportError:
545
- import warnings
546
-
547
- warnings.warn(
548
- f"Font mismatch: Requested '{requested_font}' but using '{actual_font}'",
549
- UserWarning,
546
+ logger.warning(
547
+ f"Font mismatch: Requested '{requested_font}' but using '{actual_font}'"
550
548
  )
551
549
  else:
552
550
  # If no style section, add font info to runtime section
@@ -688,9 +686,7 @@ def collect_figure_metadata(fig, ax=None) -> Dict:
688
686
 
689
687
  except Exception as e:
690
688
  # If Phase 1 extraction fails, continue without it
691
- import warnings
692
-
693
- warnings.warn(f"Could not extract Phase 1 metadata: {e}")
689
+ logger.warning(f"Could not extract Phase 1 metadata: {e}")
694
690
 
695
691
  # Apply precision rounding to all numeric values
696
692
  metadata = _round_metadata(metadata)
@@ -318,25 +318,13 @@ def configure_mpl(
318
318
  )
319
319
 
320
320
  # Warn user about missing Arial using scitex.logging
321
- try:
322
- from scitex.logging import getLogger
321
+ from scitex import logging as _logging
323
322
 
324
- logger = getLogger(__name__)
325
- logger.warning(
326
- "Arial font not found. Using fallback fonts (Helvetica/DejaVu Sans). "
327
- "For publication figures with Arial: sudo apt-get install ttf-mscorefonts-installer && fc-cache -fv"
328
- )
329
- except ImportError:
330
- # Fallback to warnings if scitex.logging not available
331
- import warnings
332
-
333
- warnings.warn(
334
- "Arial font not found on system. Using fallback fonts (Helvetica/DejaVu Sans). "
335
- "For publication-quality figures with Arial, install Microsoft Core Fonts: "
336
- "sudo apt-get install ttf-mscorefonts-installer && fc-cache -fv",
337
- UserWarning,
338
- stacklevel=2,
339
- )
323
+ _logger = _logging.getLogger(__name__)
324
+ _logger.warning(
325
+ "Arial font not found. Using fallback fonts (Helvetica/DejaVu Sans). "
326
+ "For publication figures with Arial: sudo apt-get install ttf-mscorefonts-installer && fc-cache -fv"
327
+ )
340
328
 
341
329
  # Suppress matplotlib's own font warnings
342
330
  import logging
scitex/plt/utils/_crop.py CHANGED
@@ -97,6 +97,7 @@ def crop(
97
97
  overwrite: bool = False,
98
98
  verbose: bool = False,
99
99
  return_offset: bool = False,
100
+ crop_box: Optional[Tuple[int, int, int, int]] = None,
100
101
  ) -> str:
101
102
  """
102
103
  Crop a figure image to its content area with a specified margin.
@@ -113,7 +114,8 @@ def crop(
113
114
  Path to save the cropped image. If None and overwrite=True,
114
115
  overwrites the input. If None and overwrite=False, adds '_cropped' suffix.
115
116
  margin : int, optional
116
- Margin in pixels to add around the content area (default: 12, ~1mm at 300 DPI)
117
+ Margin in pixels to add around the content area (default: 12, ~1mm at 300 DPI).
118
+ Only used when crop_box is None (auto-detection mode).
117
119
  overwrite : bool, optional
118
120
  Whether to overwrite the input file (default: False)
119
121
  verbose : bool, optional
@@ -121,6 +123,10 @@ def crop(
121
123
  return_offset : bool, optional
122
124
  If True, also return the crop offset (left, upper) for metadata adjustment.
123
125
  Default is False.
126
+ crop_box : tuple, optional
127
+ Explicit crop coordinates (left, upper, right, lower). If provided,
128
+ skips auto-detection and uses these exact coordinates for cropping.
129
+ This is useful for applying the same crop to multiple images (e.g., hitmap).
124
130
 
125
131
  Returns
126
132
  -------
@@ -144,6 +150,11 @@ def crop(
144
150
 
145
151
  >>> # Or crop in place
146
152
  >>> stx.plt.crop("figure.png", overwrite=True)
153
+
154
+ >>> # Apply explicit crop coordinates (e.g., for hitmap)
155
+ >>> _, offset = stx.plt.crop("figure.png", return_offset=True)
156
+ >>> crop_box = (offset['left'], offset['upper'], offset['right'], offset['lower'])
157
+ >>> stx.plt.crop("hitmap.png", crop_box=crop_box)
147
158
  """
148
159
  # Determine output path
149
160
  if output_path is None:
@@ -164,19 +175,26 @@ def crop(
164
175
  if "dpi" in img.info:
165
176
  print(f"Original DPI: {img.info['dpi']}")
166
177
 
167
- # Find the content area (returns left, upper, right, lower)
168
- left, upper, right, lower = find_content_area(input_path)
169
-
170
- if verbose:
171
- print(
172
- f"Content area detected at: left={left}, upper={upper}, right={right}, lower={lower}"
173
- )
174
-
175
- # Calculate the coordinates with margin, clamping to the image boundaries
176
- left = max(left - margin, 0)
177
- upper = max(upper - margin, 0)
178
- right = min(right + margin, img.width)
179
- lower = min(lower + margin, img.height)
178
+ # Use explicit crop_box if provided, otherwise auto-detect
179
+ if crop_box is not None:
180
+ # Use explicit crop coordinates (no margin adjustment - use as-is)
181
+ left, upper, right, lower = crop_box
182
+ if verbose:
183
+ print(f"Using explicit crop_box: left={left}, upper={upper}, right={right}, lower={lower}")
184
+ else:
185
+ # Find the content area (returns left, upper, right, lower)
186
+ left, upper, right, lower = find_content_area(input_path)
187
+
188
+ if verbose:
189
+ print(
190
+ f"Content area detected at: left={left}, upper={upper}, right={right}, lower={lower}"
191
+ )
192
+
193
+ # Calculate the coordinates with margin, clamping to the image boundaries
194
+ left = max(left - margin, 0)
195
+ upper = max(upper - margin, 0)
196
+ right = min(right + margin, img.width)
197
+ lower = min(lower + margin, img.height)
180
198
 
181
199
  if verbose:
182
200
  print(f"Cropping to: left={left}, upper={upper}, right={right}, lower={lower}")
@@ -40,6 +40,7 @@ __all__ = [
40
40
  "get_csv_column_prefix",
41
41
  "parse_csv_column_name",
42
42
  "sanitize_trace_id",
43
+ "get_unique_trace_id",
43
44
  ]
44
45
 
45
46
 
@@ -91,6 +92,59 @@ def sanitize_trace_id(trace_id: str) -> str:
91
92
  return sanitized if sanitized else "unnamed"
92
93
 
93
94
 
95
+ def get_unique_trace_id(trace_id: str, existing_ids: set) -> str:
96
+ """Get unique trace ID, adding suffix if collision detected.
97
+
98
+ This function ensures trace IDs remain unique even when multiple traces
99
+ have IDs that sanitize to the same value (e.g., "A" and "a" both become "a").
100
+ When a collision is detected, suffixes are added: a -> a-1 -> a-2, etc.
101
+
102
+ Parameters
103
+ ----------
104
+ trace_id : str
105
+ Raw trace identifier
106
+ existing_ids : set
107
+ Set of already-used trace IDs (will be modified in-place)
108
+
109
+ Returns
110
+ -------
111
+ str
112
+ Unique sanitized trace ID
113
+
114
+ Examples
115
+ --------
116
+ >>> ids = set()
117
+ >>> get_unique_trace_id("A", ids)
118
+ 'a'
119
+ >>> ids
120
+ {'a'}
121
+ >>> get_unique_trace_id("a", ids) # collision!
122
+ 'a-1'
123
+ >>> ids
124
+ {'a', 'a-1'}
125
+ >>> get_unique_trace_id("A ", ids) # another collision!
126
+ 'a-2'
127
+ >>> ids
128
+ {'a', 'a-1', 'a-2'}
129
+ >>> get_unique_trace_id("B", ids) # no collision
130
+ 'b'
131
+ """
132
+ base_id = sanitize_trace_id(trace_id)
133
+
134
+ if base_id not in existing_ids:
135
+ existing_ids.add(base_id)
136
+ return base_id
137
+
138
+ # Find unique suffix
139
+ counter = 1
140
+ while f"{base_id}-{counter}" in existing_ids:
141
+ counter += 1
142
+
143
+ unique_id = f"{base_id}-{counter}"
144
+ existing_ids.add(unique_id)
145
+ return unique_id
146
+
147
+
94
148
  def get_csv_column_prefix(
95
149
  ax_row: int = 0, ax_col: int = 0, trace_id: str = None, trace_index: int = None
96
150
  ) -> str:
@@ -10,11 +10,13 @@ This module provides functions to create matplotlib figures and axes with
10
10
  precise millimeter-based control over dimensions, margins, and styling.
11
11
  This is particularly useful for creating publication-quality figures that
12
12
  need to meet specific size requirements (e.g., Nature, Science journals).
13
+
14
+ Supports dark/light theme modes for eye-friendly visualization.
13
15
  """
14
16
 
15
17
  __FILE__ = __file__
16
18
 
17
- from typing import Dict, Optional, Tuple, TYPE_CHECKING
19
+ from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING
18
20
 
19
21
  import matplotlib.pyplot as plt
20
22
  from matplotlib.axes import Axes
@@ -22,6 +24,111 @@ from matplotlib.figure import Figure
22
24
 
23
25
  from ._units import mm_to_inch, mm_to_pt
24
26
 
27
+ # Default theme color palettes
28
+ # Both modes use transparent background by default for flexibility
29
+ # Dark mode: all text elements (labels, ticks, spines) use same soft white color
30
+ # Light mode: all text elements use black
31
+ THEME_COLORS = {
32
+ "dark": {
33
+ "background": "transparent", # Transparent for overlay on dark backgrounds
34
+ "axes_bg": "transparent", # Transparent axes background
35
+ "text": "#e8e8e8", # Soft white (reduced strain)
36
+ "spine": "#e8e8e8", # Same as text (like black in light mode)
37
+ "tick": "#e8e8e8", # Same as text
38
+ "grid": "#3a3a4a", # Subtle grid
39
+ },
40
+ "light": {
41
+ "background": "transparent", # Transparent for overlay on light backgrounds
42
+ "axes_bg": "transparent", # Transparent axes background
43
+ "text": "black", # Black text
44
+ "spine": "black", # Black spines
45
+ "tick": "black", # Black ticks
46
+ "grid": "#cccccc", # Light gray grid
47
+ },
48
+ }
49
+
50
+
51
+ def _apply_theme_colors(
52
+ ax: Axes,
53
+ theme: str = "light",
54
+ custom_colors: Optional[Dict[str, str]] = None
55
+ ) -> None:
56
+ """
57
+ Apply theme colors to axes for dark/light mode support.
58
+
59
+ Parameters
60
+ ----------
61
+ ax : matplotlib.axes.Axes
62
+ Target axes to apply theme to
63
+ theme : str
64
+ Color theme: "light" or "dark" (default: "light")
65
+ custom_colors : dict, optional
66
+ Custom color overrides. Keys: background, axes_bg, text, spine, tick, grid
67
+
68
+ Examples
69
+ --------
70
+ >>> fig, ax = plt.subplots()
71
+ >>> _apply_theme_colors(ax, theme="dark") # Eye-friendly dark mode
72
+ """
73
+ # Get base theme colors
74
+ colors = THEME_COLORS.get(theme, THEME_COLORS["light"]).copy()
75
+
76
+ # Apply custom overrides
77
+ if custom_colors:
78
+ colors.update(custom_colors)
79
+
80
+ # Apply axes background
81
+ if colors["axes_bg"] != "transparent":
82
+ ax.set_facecolor(colors["axes_bg"])
83
+ ax.patch.set_alpha(1.0)
84
+ else:
85
+ ax.patch.set_alpha(0.0)
86
+
87
+ # Apply figure background if accessible
88
+ fig = ax.get_figure()
89
+ if fig is not None:
90
+ if colors["background"] != "transparent":
91
+ fig.patch.set_facecolor(colors["background"])
92
+ fig.patch.set_alpha(1.0)
93
+ else:
94
+ fig.patch.set_alpha(0.0)
95
+
96
+ # Apply text colors (labels, titles)
97
+ ax.xaxis.label.set_color(colors["text"])
98
+ ax.yaxis.label.set_color(colors["text"])
99
+ ax.title.set_color(colors["text"])
100
+
101
+ # Apply spine colors
102
+ for spine in ax.spines.values():
103
+ spine.set_color(colors["spine"])
104
+
105
+ # Apply tick colors (both marks and labels)
106
+ ax.tick_params(colors=colors["tick"], which="both")
107
+ for label in ax.get_xticklabels() + ax.get_yticklabels():
108
+ label.set_color(colors["tick"])
109
+
110
+ # Apply legend colors if legend exists
111
+ legend = ax.get_legend()
112
+ if legend is not None:
113
+ # Legend text color
114
+ for text in legend.get_texts():
115
+ text.set_color(colors["text"])
116
+ # Legend title if present
117
+ title = legend.get_title()
118
+ if title:
119
+ title.set_color(colors["text"])
120
+ # Legend frame
121
+ frame = legend.get_frame()
122
+ if frame:
123
+ if colors["axes_bg"] != "transparent":
124
+ frame.set_facecolor(colors["axes_bg"])
125
+ frame.set_edgecolor(colors["spine"])
126
+
127
+ # Store theme in axes metadata for reference
128
+ if hasattr(ax, "_scitex_metadata"):
129
+ ax._scitex_metadata["theme"] = theme
130
+ ax._scitex_metadata["theme_colors"] = colors
131
+
25
132
  if TYPE_CHECKING:
26
133
  from scitex.plt._subplots._FigWrapper import FigWrapper
27
134
  from scitex.plt._subplots._AxisWrapper import AxisWrapper
@@ -197,6 +304,8 @@ def apply_style_mm(ax: Axes, style: Dict) -> float:
197
304
  - 'axis_font_size_pt' (float): Axis label font size in points (default: 8)
198
305
  - 'tick_font_size_pt' (float): Tick label font size in points (default: 7)
199
306
  - 'n_ticks' (int): Number of ticks on each axis (default: 4)
307
+ - 'theme' (str): Color theme "light" or "dark" (default: "light")
308
+ - 'theme_colors' (dict): Custom theme color overrides
200
309
 
201
310
  Returns
202
311
  -------
@@ -213,6 +322,7 @@ def apply_style_mm(ax: Axes, style: Dict) -> float:
213
322
  ... 'tick_thickness_mm': 0.2,
214
323
  ... 'axis_font_size_pt': 7,
215
324
  ... 'tick_font_size_pt': 7,
325
+ ... 'theme': 'dark', # Enable dark mode
216
326
  ... }
217
327
  >>> trace_lw = apply_style_mm(ax, style)
218
328
  >>> ax.plot(x, y, lw=trace_lw)
@@ -224,6 +334,11 @@ def apply_style_mm(ax: Axes, style: Dict) -> float:
224
334
  - The returned trace linewidth should be used when plotting to maintain
225
335
  consistent styling across all plot elements
226
336
  """
337
+ # Apply theme colors (dark/light mode)
338
+ theme = style.get("theme", "light")
339
+ theme_colors = style.get("theme_colors", None)
340
+ _apply_theme_colors(ax, theme, theme_colors)
341
+
227
342
  # Convert spine thickness from mm to points
228
343
  axis_lw_pt = mm_to_pt(style.get("axis_thickness_mm", 0.2))
229
344
  for spine in ax.spines.values():