scitex 2.5.0__py3-none-any.whl → 2.7.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 (1091) hide show
  1. scitex/__init__.py +19 -8
  2. scitex/__main__.py +2 -1
  3. scitex/__version__.py +1 -1
  4. scitex/_optional_deps.py +13 -20
  5. scitex/ai/__init__.py +5 -0
  6. scitex/ai/_gen_ai/_Anthropic.py +3 -1
  7. scitex/ai/_gen_ai/_BaseGenAI.py +3 -2
  8. scitex/ai/_gen_ai/_DeepSeek.py +1 -1
  9. scitex/ai/_gen_ai/_Google.py +3 -2
  10. scitex/ai/_gen_ai/_Llama.py +4 -2
  11. scitex/ai/_gen_ai/_OpenAI.py +3 -1
  12. scitex/ai/_gen_ai/_PARAMS.py +1 -0
  13. scitex/ai/_gen_ai/_Perplexity.py +3 -1
  14. scitex/ai/_gen_ai/__init__.py +1 -0
  15. scitex/ai/_gen_ai/_format_output_func.py +3 -1
  16. scitex/ai/classification/CrossValidationExperiment.py +8 -14
  17. scitex/ai/classification/examples/timeseries_cv_demo.py +128 -112
  18. scitex/ai/classification/reporters/_BaseClassificationReporter.py +2 -0
  19. scitex/ai/classification/reporters/_ClassificationReporter.py +30 -45
  20. scitex/ai/classification/reporters/_MultiClassificationReporter.py +8 -11
  21. scitex/ai/classification/reporters/_SingleClassificationReporter.py +126 -182
  22. scitex/ai/classification/reporters/__init__.py +1 -1
  23. scitex/ai/classification/reporters/reporter_utils/_Plotter.py +213 -119
  24. scitex/ai/classification/reporters/reporter_utils/__init__.py +28 -36
  25. scitex/ai/classification/reporters/reporter_utils/aggregation.py +125 -143
  26. scitex/ai/classification/reporters/reporter_utils/data_models.py +128 -120
  27. scitex/ai/classification/reporters/reporter_utils/reporting.py +507 -340
  28. scitex/ai/classification/reporters/reporter_utils/storage.py +4 -1
  29. scitex/ai/classification/reporters/reporter_utils/validation.py +141 -154
  30. scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +204 -129
  31. scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +215 -171
  32. scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +17 -17
  33. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +67 -143
  34. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +67 -143
  35. scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +12 -13
  36. scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +231 -144
  37. scitex/ai/classification/timeseries/__init__.py +2 -4
  38. scitex/ai/classification/timeseries/_normalize_timestamp.py +3 -0
  39. scitex/ai/clustering/_pca.py +0 -1
  40. scitex/ai/clustering/_umap.py +1 -2
  41. scitex/ai/feature_extraction/__init__.py +10 -8
  42. scitex/ai/feature_extraction/vit.py +0 -1
  43. scitex/ai/feature_selection/feature_selection.py +3 -8
  44. scitex/ai/metrics/_calc_conf_mat.py +2 -0
  45. scitex/ai/metrics/_calc_feature_importance.py +3 -7
  46. scitex/ai/metrics/_calc_pre_rec_auc.py +5 -5
  47. scitex/ai/metrics/_calc_roc_auc.py +4 -2
  48. scitex/ai/metrics/_calc_seizure_prediction_metrics.py +35 -20
  49. scitex/ai/metrics/_calc_silhouette_score.py +1 -3
  50. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +0 -3
  51. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +0 -3
  52. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +0 -3
  53. scitex/ai/optim/_optimizers.py +1 -1
  54. scitex/ai/plt/__init__.py +6 -1
  55. scitex/ai/plt/_plot_feature_importance.py +1 -3
  56. scitex/ai/plt/_plot_learning_curve.py +9 -24
  57. scitex/ai/plt/_plot_optuna_study.py +4 -3
  58. scitex/ai/plt/_plot_pre_rec_curve.py +9 -15
  59. scitex/ai/plt/_plot_roc_curve.py +6 -8
  60. scitex/ai/plt/_stx_conf_mat.py +121 -122
  61. scitex/ai/sampling/undersample.py +3 -2
  62. scitex/ai/sklearn/__init__.py +2 -2
  63. scitex/ai/training/_LearningCurveLogger.py +23 -10
  64. scitex/ai/utils/_check_params.py +0 -1
  65. scitex/benchmark/__init__.py +15 -25
  66. scitex/benchmark/benchmark.py +124 -117
  67. scitex/benchmark/monitor.py +117 -107
  68. scitex/benchmark/profiler.py +61 -58
  69. scitex/bridge/__init__.py +110 -0
  70. scitex/bridge/_helpers.py +149 -0
  71. scitex/bridge/_plt_vis.py +529 -0
  72. scitex/bridge/_protocol.py +283 -0
  73. scitex/bridge/_stats_plt.py +261 -0
  74. scitex/bridge/_stats_vis.py +265 -0
  75. scitex/browser/__init__.py +0 -2
  76. scitex/browser/auth/__init__.py +0 -0
  77. scitex/browser/auth/google.py +16 -11
  78. scitex/browser/automation/CookieHandler.py +2 -3
  79. scitex/browser/collaboration/__init__.py +3 -0
  80. scitex/browser/collaboration/auth_helpers.py +3 -1
  81. scitex/browser/collaboration/collaborative_agent.py +2 -0
  82. scitex/browser/collaboration/interactive_panel.py +2 -2
  83. scitex/browser/collaboration/shared_session.py +20 -11
  84. scitex/browser/collaboration/standard_interactions.py +1 -0
  85. scitex/browser/core/BrowserMixin.py +12 -30
  86. scitex/browser/core/ChromeProfileManager.py +9 -24
  87. scitex/browser/debugging/_browser_logger.py +15 -25
  88. scitex/browser/debugging/_failure_capture.py +9 -2
  89. scitex/browser/debugging/_highlight_element.py +15 -6
  90. scitex/browser/debugging/_show_grid.py +5 -6
  91. scitex/browser/debugging/_sync_session.py +4 -3
  92. scitex/browser/debugging/_test_monitor.py +14 -5
  93. scitex/browser/debugging/_visual_cursor.py +46 -35
  94. scitex/browser/interaction/click_center.py +4 -3
  95. scitex/browser/interaction/click_with_fallbacks.py +7 -10
  96. scitex/browser/interaction/close_popups.py +79 -66
  97. scitex/browser/interaction/fill_with_fallbacks.py +8 -8
  98. scitex/browser/pdf/__init__.py +3 -1
  99. scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +11 -10
  100. scitex/browser/pdf/detect_chrome_pdf_viewer.py +3 -6
  101. scitex/browser/remote/CaptchaHandler.py +109 -96
  102. scitex/browser/remote/ZenRowsAPIClient.py +91 -97
  103. scitex/browser/remote/ZenRowsBrowserManager.py +138 -112
  104. scitex/browser/stealth/HumanBehavior.py +4 -9
  105. scitex/browser/stealth/StealthManager.py +11 -26
  106. scitex/capture/__init__.py +17 -17
  107. scitex/capture/__main__.py +2 -3
  108. scitex/capture/capture.py +23 -51
  109. scitex/capture/cli.py +14 -39
  110. scitex/capture/gif.py +5 -9
  111. scitex/capture/mcp_server.py +7 -20
  112. scitex/capture/session.py +4 -3
  113. scitex/capture/utils.py +18 -53
  114. scitex/cli/__init__.py +1 -1
  115. scitex/cli/cloud.py +158 -116
  116. scitex/cli/config.py +224 -0
  117. scitex/cli/main.py +41 -40
  118. scitex/cli/scholar.py +60 -27
  119. scitex/cli/security.py +14 -20
  120. scitex/cli/web.py +87 -90
  121. scitex/cli/writer.py +51 -45
  122. scitex/cloud/__init__.py +14 -11
  123. scitex/cloud/_matplotlib_hook.py +6 -6
  124. scitex/config/README.md +313 -0
  125. scitex/config/{PriorityConfig.py → _PriorityConfig.py} +114 -17
  126. scitex/config/_ScitexConfig.py +319 -0
  127. scitex/config/__init__.py +41 -9
  128. scitex/config/_paths.py +325 -0
  129. scitex/config/default.yaml +81 -0
  130. scitex/context/_suppress_output.py +2 -3
  131. scitex/db/_BaseMixins/_BaseBackupMixin.py +3 -1
  132. scitex/db/_BaseMixins/_BaseBatchMixin.py +3 -1
  133. scitex/db/_BaseMixins/_BaseBlobMixin.py +3 -1
  134. scitex/db/_BaseMixins/_BaseImportExportMixin.py +1 -3
  135. scitex/db/_BaseMixins/_BaseIndexMixin.py +3 -1
  136. scitex/db/_BaseMixins/_BaseMaintenanceMixin.py +1 -3
  137. scitex/db/_BaseMixins/_BaseQueryMixin.py +3 -1
  138. scitex/db/_BaseMixins/_BaseRowMixin.py +3 -1
  139. scitex/db/_BaseMixins/_BaseTableMixin.py +3 -1
  140. scitex/db/_BaseMixins/_BaseTransactionMixin.py +1 -3
  141. scitex/db/_BaseMixins/__init__.py +1 -1
  142. scitex/db/__init__.py +9 -1
  143. scitex/db/__main__.py +8 -21
  144. scitex/db/_check_health.py +15 -31
  145. scitex/db/_delete_duplicates.py +7 -4
  146. scitex/db/_inspect.py +22 -38
  147. scitex/db/_inspect_optimized.py +89 -85
  148. scitex/db/_postgresql/_PostgreSQL.py +0 -1
  149. scitex/db/_postgresql/_PostgreSQLMixins/_BlobMixin.py +3 -1
  150. scitex/db/_postgresql/_PostgreSQLMixins/_ConnectionMixin.py +1 -3
  151. scitex/db/_postgresql/_PostgreSQLMixins/_ImportExportMixin.py +1 -3
  152. scitex/db/_postgresql/_PostgreSQLMixins/_MaintenanceMixin.py +1 -4
  153. scitex/db/_postgresql/_PostgreSQLMixins/_QueryMixin.py +3 -3
  154. scitex/db/_postgresql/_PostgreSQLMixins/_RowMixin.py +3 -1
  155. scitex/db/_postgresql/_PostgreSQLMixins/_TransactionMixin.py +1 -3
  156. scitex/db/_postgresql/__init__.py +1 -1
  157. scitex/db/_sqlite3/_SQLite3.py +2 -4
  158. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +11 -12
  159. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +19 -14
  160. scitex/db/_sqlite3/_SQLite3Mixins/_BatchMixin.py +3 -1
  161. scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +7 -7
  162. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +118 -111
  163. scitex/db/_sqlite3/_SQLite3Mixins/_ConnectionMixin.py +8 -10
  164. scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +17 -45
  165. scitex/db/_sqlite3/_SQLite3Mixins/_ImportExportMixin.py +1 -3
  166. scitex/db/_sqlite3/_SQLite3Mixins/_IndexMixin.py +3 -1
  167. scitex/db/_sqlite3/_SQLite3Mixins/_QueryMixin.py +3 -4
  168. scitex/db/_sqlite3/_SQLite3Mixins/_RowMixin.py +9 -9
  169. scitex/db/_sqlite3/_SQLite3Mixins/_TableMixin.py +18 -11
  170. scitex/db/_sqlite3/_SQLite3Mixins/__init__.py +1 -0
  171. scitex/db/_sqlite3/__init__.py +1 -1
  172. scitex/db/_sqlite3/_delete_duplicates.py +13 -11
  173. scitex/decorators/__init__.py +29 -4
  174. scitex/decorators/_auto_order.py +43 -43
  175. scitex/decorators/_batch_fn.py +12 -6
  176. scitex/decorators/_cache_disk.py +8 -9
  177. scitex/decorators/_cache_disk_async.py +8 -7
  178. scitex/decorators/_combined.py +19 -13
  179. scitex/decorators/_converters.py +16 -3
  180. scitex/decorators/_deprecated.py +32 -22
  181. scitex/decorators/_numpy_fn.py +18 -4
  182. scitex/decorators/_pandas_fn.py +17 -5
  183. scitex/decorators/_signal_fn.py +17 -3
  184. scitex/decorators/_torch_fn.py +32 -15
  185. scitex/decorators/_xarray_fn.py +23 -9
  186. scitex/dev/_analyze_code_flow.py +0 -2
  187. scitex/dict/_DotDict.py +15 -19
  188. scitex/dict/_flatten.py +1 -0
  189. scitex/dict/_listed_dict.py +1 -0
  190. scitex/dict/_pop_keys.py +1 -0
  191. scitex/dict/_replace.py +1 -0
  192. scitex/dict/_safe_merge.py +1 -0
  193. scitex/dict/_to_str.py +2 -3
  194. scitex/dsp/__init__.py +13 -4
  195. scitex/dsp/_crop.py +3 -1
  196. scitex/dsp/_detect_ripples.py +3 -1
  197. scitex/dsp/_modulation_index.py +3 -1
  198. scitex/dsp/_time.py +3 -1
  199. scitex/dsp/_wavelet.py +0 -1
  200. scitex/dsp/example.py +0 -5
  201. scitex/dsp/filt.py +4 -0
  202. scitex/dsp/utils/__init__.py +4 -1
  203. scitex/dsp/utils/pac.py +3 -3
  204. scitex/dt/_normalize_timestamp.py +4 -1
  205. scitex/errors.py +3 -6
  206. scitex/etc/__init__.py +1 -1
  207. scitex/gen/_DimHandler.py +6 -6
  208. scitex/gen/__init__.py +5 -1
  209. scitex/gen/_deprecated_close.py +1 -0
  210. scitex/gen/_deprecated_start.py +5 -3
  211. scitex/gen/_detect_environment.py +44 -41
  212. scitex/gen/_detect_notebook_path.py +51 -47
  213. scitex/gen/_embed.py +1 -1
  214. scitex/gen/_get_notebook_path.py +81 -62
  215. scitex/gen/_inspect_module.py +0 -1
  216. scitex/gen/_norm.py +16 -7
  217. scitex/gen/_norm_cache.py +78 -65
  218. scitex/gen/_print_config.py +0 -3
  219. scitex/gen/_src.py +2 -3
  220. scitex/gen/_title_case.py +3 -2
  221. scitex/gen/_to_even.py +8 -8
  222. scitex/gen/_transpose.py +3 -3
  223. scitex/gen/misc.py +0 -3
  224. scitex/gists/_SigMacro_processFigure_S.py +2 -2
  225. scitex/gists/_SigMacro_toBlue.py +2 -2
  226. scitex/gists/__init__.py +4 -1
  227. scitex/git/_branch.py +19 -11
  228. scitex/git/_clone.py +23 -15
  229. scitex/git/_commit.py +10 -12
  230. scitex/git/_init.py +15 -38
  231. scitex/git/_remote.py +9 -3
  232. scitex/git/_result.py +3 -0
  233. scitex/git/_retry.py +2 -5
  234. scitex/git/_types.py +4 -0
  235. scitex/git/_validation.py +8 -8
  236. scitex/git/_workflow.py +4 -4
  237. scitex/io/__init__.py +2 -1
  238. scitex/io/_glob.py +2 -2
  239. scitex/io/_json2md.py +3 -3
  240. scitex/io/_load.py +6 -8
  241. scitex/io/_load_cache.py +71 -71
  242. scitex/io/_load_configs.py +2 -3
  243. scitex/io/_load_modules/_H5Explorer.py +6 -12
  244. scitex/io/_load_modules/_ZarrExplorer.py +3 -3
  245. scitex/io/_load_modules/_bibtex.py +62 -63
  246. scitex/io/_load_modules/_canvas.py +4 -9
  247. scitex/io/_load_modules/_catboost.py +7 -2
  248. scitex/io/_load_modules/_hdf5.py +2 -0
  249. scitex/io/_load_modules/_image.py +5 -1
  250. scitex/io/_load_modules/_matlab.py +3 -1
  251. scitex/io/_load_modules/_optuna.py +0 -1
  252. scitex/io/_load_modules/_pdf.py +38 -29
  253. scitex/io/_load_modules/_sqlite3.py +1 -0
  254. scitex/io/_load_modules/_txt.py +2 -0
  255. scitex/io/_load_modules/_xml.py +9 -9
  256. scitex/io/_load_modules/_zarr.py +12 -10
  257. scitex/io/_metadata.py +76 -37
  258. scitex/io/_qr_utils.py +18 -13
  259. scitex/io/_save.py +220 -63
  260. scitex/io/_save_modules/__init__.py +7 -2
  261. scitex/io/_save_modules/_bibtex.py +66 -61
  262. scitex/io/_save_modules/_canvas.py +5 -6
  263. scitex/io/_save_modules/_catboost.py +2 -2
  264. scitex/io/_save_modules/_csv.py +4 -4
  265. scitex/io/_save_modules/_excel.py +5 -9
  266. scitex/io/_save_modules/_hdf5.py +9 -21
  267. scitex/io/_save_modules/_html.py +5 -5
  268. scitex/io/_save_modules/_image.py +105 -8
  269. scitex/io/_save_modules/_joblib.py +2 -2
  270. scitex/io/_save_modules/_json.py +51 -6
  271. scitex/io/_save_modules/_listed_dfs_as_csv.py +2 -1
  272. scitex/io/_save_modules/_listed_scalars_as_csv.py +2 -1
  273. scitex/io/_save_modules/_matlab.py +2 -2
  274. scitex/io/_save_modules/_numpy.py +6 -8
  275. scitex/io/_save_modules/_pickle.py +4 -4
  276. scitex/io/_save_modules/_plotly.py +3 -3
  277. scitex/io/_save_modules/_tex.py +23 -25
  278. scitex/io/_save_modules/_text.py +2 -2
  279. scitex/io/_save_modules/_yaml.py +9 -9
  280. scitex/io/_save_modules/_zarr.py +15 -15
  281. scitex/io/utils/__init__.py +2 -1
  282. scitex/io/utils/h5_to_zarr.py +173 -155
  283. scitex/linalg/__init__.py +1 -1
  284. scitex/linalg/_geometric_median.py +4 -3
  285. scitex/logging/_Tee.py +5 -7
  286. scitex/logging/__init__.py +18 -19
  287. scitex/logging/_config.py +4 -1
  288. scitex/logging/_context.py +6 -5
  289. scitex/logging/_formatters.py +2 -3
  290. scitex/logging/_handlers.py +19 -20
  291. scitex/logging/_levels.py +9 -17
  292. scitex/logging/_logger.py +74 -15
  293. scitex/logging/_print_capture.py +17 -17
  294. scitex/nn/_BNet.py +1 -3
  295. scitex/nn/_Filters.py +6 -2
  296. scitex/nn/_ModulationIndex.py +3 -1
  297. scitex/nn/_PAC.py +3 -2
  298. scitex/nn/_PSD.py +0 -1
  299. scitex/nn/__init__.py +16 -3
  300. scitex/path/_clean.py +10 -8
  301. scitex/path/_find.py +1 -1
  302. scitex/path/_get_spath.py +1 -2
  303. scitex/path/_mk_spath.py +1 -1
  304. scitex/path/_symlink.py +5 -10
  305. scitex/pd/__init__.py +4 -1
  306. scitex/pd/_force_df.py +24 -24
  307. scitex/pd/_get_unique.py +1 -0
  308. scitex/pd/_merge_columns.py +1 -1
  309. scitex/pd/_round.py +11 -7
  310. scitex/pd/_to_xy.py +0 -1
  311. scitex/plt/REQUESTS.md +191 -0
  312. scitex/plt/__init__.py +185 -87
  313. scitex/plt/_subplots/_AxesWrapper.py +22 -6
  314. scitex/plt/_subplots/_AxisWrapper.py +100 -39
  315. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +74 -52
  316. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +183 -73
  317. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +61 -45
  318. scitex/plt/_subplots/_AxisWrapperMixins/_TrackingMixin.py +26 -14
  319. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +80 -73
  320. scitex/plt/_subplots/_FigWrapper.py +93 -60
  321. scitex/plt/_subplots/_SubplotsWrapper.py +135 -68
  322. scitex/plt/_subplots/__init__.py +10 -0
  323. scitex/plt/_subplots/_export_as_csv.py +89 -47
  324. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +1 -0
  325. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +6 -4
  326. scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +88 -38
  327. scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +25 -31
  328. scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +53 -23
  329. scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +38 -25
  330. scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +17 -9
  331. scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +70 -124
  332. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +12 -10
  333. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +31 -17
  334. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +33 -21
  335. scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +14 -4
  336. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +43 -29
  337. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +14 -4
  338. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +27 -11
  339. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +7 -5
  340. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +9 -7
  341. scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +15 -6
  342. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +85 -46
  343. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +52 -27
  344. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +1 -0
  345. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +16 -17
  346. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +7 -5
  347. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -8
  348. scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +17 -6
  349. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +43 -26
  350. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +68 -47
  351. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +52 -64
  352. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +55 -50
  353. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +9 -11
  354. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +63 -29
  355. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +4 -4
  356. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +6 -4
  357. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +44 -40
  358. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +46 -39
  359. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +46 -39
  360. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +75 -94
  361. scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +12 -3
  362. scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +12 -3
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +10 -8
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +17 -15
  365. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +10 -9
  366. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_fillv.py +35 -31
  367. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +18 -18
  368. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +24 -18
  369. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +9 -7
  370. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_line.py +34 -23
  371. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +15 -13
  372. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +12 -10
  373. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +15 -13
  374. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +11 -9
  375. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_rectangle.py +84 -56
  376. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +35 -32
  377. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_shaded_line.py +46 -30
  378. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_violin.py +51 -51
  379. scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +32 -31
  380. scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +34 -31
  381. scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +44 -37
  382. scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +91 -74
  383. scitex/plt/_tpl.py +6 -5
  384. scitex/plt/ax/_plot/__init__.py +24 -0
  385. scitex/plt/ax/_plot/_add_fitted_line.py +12 -11
  386. scitex/plt/ax/_plot/_plot_circular_hist.py +3 -1
  387. scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +25 -19
  388. scitex/plt/ax/_plot/_stx_conf_mat.py +6 -3
  389. scitex/plt/ax/_plot/_stx_ecdf.py +5 -3
  390. scitex/plt/ax/_plot/_stx_fillv.py +4 -2
  391. scitex/plt/ax/_plot/_stx_heatmap.py +7 -4
  392. scitex/plt/ax/_plot/_stx_image.py +7 -5
  393. scitex/plt/ax/_plot/_stx_joyplot.py +32 -10
  394. scitex/plt/ax/_plot/_stx_raster.py +26 -11
  395. scitex/plt/ax/_plot/_stx_rectangle.py +2 -2
  396. scitex/plt/ax/_plot/_stx_shaded_line.py +15 -11
  397. scitex/plt/ax/_plot/_stx_violin.py +3 -1
  398. scitex/plt/ax/_style/_add_marginal_ax.py +6 -4
  399. scitex/plt/ax/_style/_auto_scale_axis.py +14 -10
  400. scitex/plt/ax/_style/_extend.py +3 -1
  401. scitex/plt/ax/_style/_force_aspect.py +5 -3
  402. scitex/plt/ax/_style/_format_units.py +2 -2
  403. scitex/plt/ax/_style/_hide_spines.py +5 -1
  404. scitex/plt/ax/_style/_map_ticks.py +5 -3
  405. scitex/plt/ax/_style/_rotate_labels.py +5 -4
  406. scitex/plt/ax/_style/_rotate_labels_v01.py +73 -63
  407. scitex/plt/ax/_style/_set_log_scale.py +120 -85
  408. scitex/plt/ax/_style/_set_meta.py +99 -76
  409. scitex/plt/ax/_style/_set_supxyt.py +33 -16
  410. scitex/plt/ax/_style/_set_xyt.py +27 -18
  411. scitex/plt/ax/_style/_share_axes.py +15 -5
  412. scitex/plt/ax/_style/_show_spines.py +58 -57
  413. scitex/plt/ax/_style/_style_barplot.py +1 -1
  414. scitex/plt/ax/_style/_style_boxplot.py +25 -14
  415. scitex/plt/ax/_style/_style_errorbar.py +0 -0
  416. scitex/plt/ax/_style/_style_scatter.py +1 -1
  417. scitex/plt/ax/_style/_style_suptitles.py +3 -3
  418. scitex/plt/ax/_style/_style_violinplot.py +8 -2
  419. scitex/plt/color/__init__.py +34 -2
  420. scitex/plt/color/_add_hue_col.py +1 -0
  421. scitex/plt/color/_colors.py +0 -1
  422. scitex/plt/color/_get_colors_from_conf_matap.py +3 -1
  423. scitex/plt/color/_vizualize_colors.py +0 -1
  424. scitex/plt/docs/FIGURE_ARCHITECTURE.md +155 -97
  425. scitex/plt/gallery/README.md +75 -0
  426. scitex/plt/gallery/__init__.py +29 -0
  427. scitex/plt/gallery/_generate.py +153 -0
  428. scitex/plt/gallery/_plots.py +594 -0
  429. scitex/plt/gallery/_registry.py +153 -0
  430. scitex/plt/styles/__init__.py +9 -9
  431. scitex/plt/styles/_plot_defaults.py +62 -61
  432. scitex/plt/styles/_plot_postprocess.py +126 -77
  433. scitex/plt/styles/_style_loader.py +0 -0
  434. scitex/plt/styles/presets.py +43 -18
  435. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +110 -0
  436. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +88 -0
  437. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +103 -0
  438. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +106 -0
  439. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +92 -0
  440. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +92 -0
  441. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +92 -0
  442. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +84 -0
  443. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +84 -0
  444. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +83 -0
  445. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +93 -0
  446. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +91 -0
  447. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +91 -0
  448. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +91 -0
  449. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +97 -0
  450. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +98 -0
  451. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +84 -0
  452. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +101 -0
  453. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +96 -0
  454. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +95 -0
  455. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +95 -0
  456. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +93 -0
  457. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +95 -0
  458. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +95 -0
  459. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +83 -0
  460. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +92 -0
  461. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +121 -0
  462. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +84 -0
  463. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +110 -0
  464. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +92 -0
  465. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +95 -0
  466. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +96 -0
  467. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +95 -0
  468. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +95 -0
  469. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +92 -0
  470. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +84 -0
  471. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +94 -0
  472. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +109 -0
  473. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +108 -0
  474. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +93 -0
  475. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +84 -0
  476. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +96 -0
  477. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +96 -0
  478. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +96 -0
  479. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +99 -0
  480. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +100 -0
  481. scitex/plt/utils/__init__.py +29 -2
  482. scitex/plt/utils/_close.py +8 -3
  483. scitex/plt/utils/_collect_figure_metadata.py +3031 -265
  484. scitex/plt/utils/_colorbar.py +15 -17
  485. scitex/plt/utils/_configure_mpl.py +22 -14
  486. scitex/plt/utils/_crop.py +60 -27
  487. scitex/plt/utils/_csv_column_naming.py +123 -72
  488. scitex/plt/utils/_dimension_viewer.py +7 -19
  489. scitex/plt/utils/_figure_from_axes_mm.py +70 -16
  490. scitex/plt/utils/_figure_mm.py +3 -2
  491. scitex/plt/utils/_get_actual_font.py +5 -4
  492. scitex/plt/utils/_histogram_utils.py +52 -48
  493. scitex/plt/utils/_is_valid_axis.py +19 -13
  494. scitex/plt/utils/_mk_colorbar.py +3 -3
  495. scitex/plt/utils/_scientific_captions.py +202 -139
  496. scitex/plt/utils/_scitex_config.py +98 -98
  497. scitex/plt/utils/_units.py +0 -0
  498. scitex/plt/utils/metadata/__init__.py +36 -0
  499. scitex/plt/utils/metadata/_artist_extraction.py +119 -0
  500. scitex/plt/utils/metadata/_axes_metadata.py +93 -0
  501. scitex/plt/utils/metadata/_collection_artists.py +292 -0
  502. scitex/plt/utils/metadata/_core.py +208 -0
  503. scitex/plt/utils/metadata/_csv_column_extraction.py +186 -0
  504. scitex/plt/utils/metadata/_csv_hash.py +115 -0
  505. scitex/plt/utils/metadata/_csv_verification.py +95 -0
  506. scitex/plt/utils/metadata/_data_linkage.py +263 -0
  507. scitex/plt/utils/metadata/_dimensions.py +239 -0
  508. scitex/plt/utils/metadata/_figure_metadata.py +58 -0
  509. scitex/plt/utils/metadata/_image_text_artists.py +168 -0
  510. scitex/plt/utils/metadata/_label_parsing.py +82 -0
  511. scitex/plt/utils/metadata/_legend_extraction.py +120 -0
  512. scitex/plt/utils/metadata/_line_artists.py +367 -0
  513. scitex/plt/utils/metadata/_line_semantic_handling.py +173 -0
  514. scitex/plt/utils/metadata/_patch_artists.py +211 -0
  515. scitex/plt/utils/metadata/_plot_content.py +26 -0
  516. scitex/plt/utils/metadata/_plot_type_detection.py +184 -0
  517. scitex/plt/utils/metadata/_precision.py +134 -0
  518. scitex/plt/utils/metadata/_precision_config.py +68 -0
  519. scitex/plt/utils/metadata/_precision_sections.py +211 -0
  520. scitex/plt/utils/metadata/_recipe_extraction.py +267 -0
  521. scitex/plt/utils/metadata/_style_parsing.py +174 -0
  522. scitex/repro/_RandomStateManager.py +33 -38
  523. scitex/repro/__init__.py +16 -7
  524. scitex/repro/_gen_ID.py +7 -9
  525. scitex/repro/_gen_timestamp.py +7 -6
  526. scitex/repro/_hash_array.py +8 -12
  527. scitex/reproduce/__init__.py +1 -1
  528. scitex/resource/_get_processor_usages.py +3 -1
  529. scitex/resource/_log_processor_usages.py +3 -1
  530. scitex/rng/__init__.py +1 -1
  531. scitex/schema/README.md +178 -0
  532. scitex/schema/__init__.py +144 -0
  533. scitex/schema/_canvas.py +444 -0
  534. scitex/schema/_stats.py +762 -0
  535. scitex/schema/_validation.py +590 -0
  536. scitex/scholar/.legacy/Scholar.py +5 -12
  537. scitex/scholar/.legacy/_Scholar.py +66 -99
  538. scitex/scholar/.legacy/_ScholarAPI.py +75 -66
  539. scitex/scholar/.legacy/_tmp/search_engine/_BaseSearchEngine.py +3 -3
  540. scitex/scholar/.legacy/_tmp/search_engine/_UnifiedSearcher.py +4 -9
  541. scitex/scholar/.legacy/_tmp/search_engine/__init__.py +14 -21
  542. scitex/scholar/.legacy/_tmp/search_engine/local/_LocalSearchEngine.py +40 -37
  543. scitex/scholar/.legacy/_tmp/search_engine/local/_VectorSearchEngine.py +31 -28
  544. scitex/scholar/.legacy/_tmp/search_engine/web/_ArxivSearchEngine.py +74 -65
  545. scitex/scholar/.legacy/_tmp/search_engine/web/_CrossRefSearchEngine.py +122 -116
  546. scitex/scholar/.legacy/_tmp/search_engine/web/_GoogleScholarSearchEngine.py +65 -59
  547. scitex/scholar/.legacy/_tmp/search_engine/web/_PubMedSearchEngine.py +121 -107
  548. scitex/scholar/.legacy/_tmp/search_engine/web/_SemanticScholarSearchEngine.py +5 -12
  549. scitex/scholar/.legacy/database/_DatabaseEntry.py +49 -45
  550. scitex/scholar/.legacy/database/_DatabaseIndex.py +131 -94
  551. scitex/scholar/.legacy/database/_LibraryManager.py +65 -63
  552. scitex/scholar/.legacy/database/_PaperDatabase.py +138 -124
  553. scitex/scholar/.legacy/database/_ScholarDatabaseIntegration.py +14 -36
  554. scitex/scholar/.legacy/database/_StorageIntegratedDB.py +192 -156
  555. scitex/scholar/.legacy/database/_ZoteroCompatibleDB.py +300 -237
  556. scitex/scholar/.legacy/database/__init__.py +2 -1
  557. scitex/scholar/.legacy/database/manage.py +92 -84
  558. scitex/scholar/.legacy/lookup/_LookupIndex.py +157 -101
  559. scitex/scholar/.legacy/lookup/__init__.py +2 -1
  560. scitex/scholar/.legacy/metadata/doi/batch/_MetadataHandlerForBatchDOIResolution.py +4 -9
  561. scitex/scholar/.legacy/metadata/doi/batch/_ProgressManagerForBatchDOIResolution.py +10 -23
  562. scitex/scholar/.legacy/metadata/doi/batch/_SourceStatsManagerForBatchDOIResolution.py +4 -9
  563. scitex/scholar/.legacy/metadata/doi/batch/__init__.py +3 -1
  564. scitex/scholar/.legacy/metadata/doi/resolvers/_BatchDOIResolver.py +10 -25
  565. scitex/scholar/.legacy/metadata/doi/resolvers/_BibTeXDOIResolver.py +19 -49
  566. scitex/scholar/.legacy/metadata/doi/resolvers/_DOIResolver.py +1 -0
  567. scitex/scholar/.legacy/metadata/doi/resolvers/_SingleDOIResolver.py +8 -20
  568. scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
  569. scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +49 -37
  570. scitex/scholar/.legacy/metadata/doi/sources/_ArXivSource.py +11 -30
  571. scitex/scholar/.legacy/metadata/doi/sources/_BaseDOISource.py +19 -47
  572. scitex/scholar/.legacy/metadata/doi/sources/_CrossRefLocalSource.py +1 -0
  573. scitex/scholar/.legacy/metadata/doi/sources/_CrossRefSource.py +12 -33
  574. scitex/scholar/.legacy/metadata/doi/sources/_OpenAlexSource.py +8 -20
  575. scitex/scholar/.legacy/metadata/doi/sources/_PubMedSource.py +10 -27
  576. scitex/scholar/.legacy/metadata/doi/sources/_SemanticScholarSource.py +11 -29
  577. scitex/scholar/.legacy/metadata/doi/sources/_SourceManager.py +8 -21
  578. scitex/scholar/.legacy/metadata/doi/sources/_SourceResolutionStrategy.py +24 -55
  579. scitex/scholar/.legacy/metadata/doi/sources/_SourceRotationManager.py +8 -21
  580. scitex/scholar/.legacy/metadata/doi/sources/_URLDOISource.py +9 -16
  581. scitex/scholar/.legacy/metadata/doi/sources/_UnifiedSource.py +8 -22
  582. scitex/scholar/.legacy/metadata/doi/sources/__init__.py +1 -0
  583. scitex/scholar/.legacy/metadata/doi/utils/_PubMedConverter.py +4 -8
  584. scitex/scholar/.legacy/metadata/doi/utils/_RateLimitHandler.py +17 -43
  585. scitex/scholar/.legacy/metadata/doi/utils/_TextNormalizer.py +8 -18
  586. scitex/scholar/.legacy/metadata/doi/utils/_URLDOIExtractor.py +4 -8
  587. scitex/scholar/.legacy/metadata/doi/utils/__init__.py +1 -0
  588. scitex/scholar/.legacy/metadata/doi/utils/_to_complete_metadata_structure.py +1 -0
  589. scitex/scholar/.legacy/metadata/enrichment/_LibraryEnricher.py +2 -3
  590. scitex/scholar/.legacy/metadata/enrichment/enrichers/_ImpactFactorEnricher.py +6 -12
  591. scitex/scholar/.legacy/metadata/enrichment/enrichers/_SmartEnricher.py +5 -10
  592. scitex/scholar/.legacy/metadata/enrichment/sources/_UnifiedMetadataSource.py +4 -5
  593. scitex/scholar/.legacy/metadata/query_to_full_meta_json.py +8 -12
  594. scitex/scholar/.legacy/metadata/urls/_URLMetadataHandler.py +3 -3
  595. scitex/scholar/.legacy/metadata/urls/_ZoteroTranslatorRunner.py +15 -21
  596. scitex/scholar/.legacy/metadata/urls/__init__.py +3 -3
  597. scitex/scholar/.legacy/metadata/urls/_finder.py +4 -6
  598. scitex/scholar/.legacy/metadata/urls/_handler.py +7 -15
  599. scitex/scholar/.legacy/metadata/urls/_resolver.py +6 -12
  600. scitex/scholar/.legacy/search/_Embedder.py +74 -69
  601. scitex/scholar/.legacy/search/_SemanticSearch.py +91 -90
  602. scitex/scholar/.legacy/search/_SemanticSearchEngine.py +104 -109
  603. scitex/scholar/.legacy/search/_UnifiedSearcher.py +530 -471
  604. scitex/scholar/.legacy/search/_VectorDatabase.py +111 -92
  605. scitex/scholar/.legacy/search/__init__.py +1 -0
  606. scitex/scholar/.legacy/storage/_EnhancedStorageManager.py +182 -154
  607. scitex/scholar/.legacy/storage/__init__.py +2 -1
  608. scitex/scholar/__init__.py +0 -2
  609. scitex/scholar/__main__.py +1 -3
  610. scitex/scholar/auth/ScholarAuthManager.py +13 -36
  611. scitex/scholar/auth/core/AuthenticationGateway.py +15 -29
  612. scitex/scholar/auth/core/BrowserAuthenticator.py +22 -57
  613. scitex/scholar/auth/core/StrategyResolver.py +10 -27
  614. scitex/scholar/auth/core/__init__.py +5 -1
  615. scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +11 -21
  616. scitex/scholar/auth/gateway/_OpenURLResolver.py +10 -18
  617. scitex/scholar/auth/gateway/_resolve_functions.py +3 -3
  618. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -0
  619. scitex/scholar/auth/providers/EZProxyAuthenticator.py +7 -14
  620. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +29 -57
  621. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +87 -73
  622. scitex/scholar/auth/session/AuthCacheManager.py +12 -22
  623. scitex/scholar/auth/session/SessionManager.py +4 -6
  624. scitex/scholar/auth/sso/BaseSSOAutomator.py +13 -19
  625. scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +16 -45
  626. scitex/scholar/auth/sso/SSOAutomator.py +8 -15
  627. scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +13 -23
  628. scitex/scholar/browser/ScholarBrowserManager.py +31 -56
  629. scitex/scholar/browser/__init__.py +1 -0
  630. scitex/scholar/browser/utils/click_and_wait.py +3 -4
  631. scitex/scholar/browser/utils/close_unwanted_pages.py +4 -7
  632. scitex/scholar/browser/utils/wait_redirects.py +15 -40
  633. scitex/scholar/citation_graph/__init__.py +0 -0
  634. scitex/scholar/citation_graph/builder.py +3 -7
  635. scitex/scholar/citation_graph/database.py +4 -11
  636. scitex/scholar/citation_graph/example.py +5 -10
  637. scitex/scholar/citation_graph/models.py +0 -0
  638. scitex/scholar/cli/_url_utils.py +1 -1
  639. scitex/scholar/cli/chrome.py +5 -3
  640. scitex/scholar/cli/download_pdf.py +13 -14
  641. scitex/scholar/cli/handlers/bibtex_handler.py +4 -12
  642. scitex/scholar/cli/handlers/doi_handler.py +1 -3
  643. scitex/scholar/cli/handlers/project_handler.py +6 -20
  644. scitex/scholar/cli/open_browser.py +41 -39
  645. scitex/scholar/cli/open_browser_auto.py +31 -39
  646. scitex/scholar/cli/open_browser_monitored.py +27 -24
  647. scitex/scholar/config/ScholarConfig.py +5 -8
  648. scitex/scholar/config/__init__.py +1 -0
  649. scitex/scholar/config/core/_CascadeConfig.py +3 -3
  650. scitex/scholar/config/core/_PathManager.py +16 -28
  651. scitex/scholar/core/Paper.py +79 -78
  652. scitex/scholar/core/Papers.py +16 -27
  653. scitex/scholar/core/Scholar.py +98 -229
  654. scitex/scholar/core/journal_normalizer.py +52 -49
  655. scitex/scholar/core/oa_cache.py +27 -23
  656. scitex/scholar/core/open_access.py +17 -8
  657. scitex/scholar/docs/template.py +4 -3
  658. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/clf_svm.py +0 -0
  659. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/download.py +0 -0
  660. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_conf_mat.py +0 -0
  661. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_digits.py +0 -0
  662. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_umap_space.py +0 -0
  663. scitex/scholar/examples/00_config.py +10 -9
  664. scitex/scholar/examples/01_auth.py +3 -0
  665. scitex/scholar/examples/02_browser.py +14 -10
  666. scitex/scholar/examples/03_01-engine.py +3 -0
  667. scitex/scholar/examples/03_02-engine-for-bibtex.py +4 -3
  668. scitex/scholar/examples/04_01-url.py +9 -9
  669. scitex/scholar/examples/04_02-url-for-bibtex.py +7 -3
  670. scitex/scholar/examples/04_02-url-for-dois.py +87 -97
  671. scitex/scholar/examples/05_download_pdf.py +10 -4
  672. scitex/scholar/examples/06_find_and_download.py +6 -6
  673. scitex/scholar/examples/06_parse_bibtex.py +17 -17
  674. scitex/scholar/examples/07_storage_integration.py +6 -9
  675. scitex/scholar/examples/99_fullpipeline-for-bibtex.py +14 -15
  676. scitex/scholar/examples/99_fullpipeline-for-one-entry.py +31 -23
  677. scitex/scholar/examples/99_maintenance.py +3 -0
  678. scitex/scholar/examples/dev.py +2 -3
  679. scitex/scholar/examples/zotero_integration.py +11 -18
  680. scitex/scholar/impact_factor/ImpactFactorEngine.py +7 -9
  681. scitex/scholar/impact_factor/estimation/__init__.py +4 -4
  682. scitex/scholar/impact_factor/estimation/core/__init__.py +3 -7
  683. scitex/scholar/impact_factor/estimation/core/cache_manager.py +223 -211
  684. scitex/scholar/impact_factor/estimation/core/calculator.py +165 -131
  685. scitex/scholar/impact_factor/estimation/core/journal_matcher.py +217 -172
  686. scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +6 -14
  687. scitex/scholar/impact_factor/jcr/build_database.py +4 -3
  688. scitex/scholar/integration/base.py +9 -17
  689. scitex/scholar/integration/mendeley/exporter.py +2 -4
  690. scitex/scholar/integration/mendeley/importer.py +3 -3
  691. scitex/scholar/integration/mendeley/linker.py +3 -3
  692. scitex/scholar/integration/mendeley/mapper.py +9 -6
  693. scitex/scholar/integration/zotero/__main__.py +26 -43
  694. scitex/scholar/integration/zotero/exporter.py +15 -11
  695. scitex/scholar/integration/zotero/importer.py +12 -10
  696. scitex/scholar/integration/zotero/linker.py +8 -12
  697. scitex/scholar/integration/zotero/mapper.py +17 -12
  698. scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
  699. scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +47 -35
  700. scitex/scholar/metadata_engines/ScholarEngine.py +21 -43
  701. scitex/scholar/metadata_engines/__init__.py +1 -0
  702. scitex/scholar/metadata_engines/individual/ArXivEngine.py +15 -37
  703. scitex/scholar/metadata_engines/individual/CrossRefEngine.py +15 -42
  704. scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +24 -45
  705. scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +11 -21
  706. scitex/scholar/metadata_engines/individual/PubMedEngine.py +10 -27
  707. scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +28 -35
  708. scitex/scholar/metadata_engines/individual/URLDOIEngine.py +11 -22
  709. scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +20 -49
  710. scitex/scholar/metadata_engines/utils/_PubMedConverter.py +4 -8
  711. scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +5 -10
  712. scitex/scholar/metadata_engines/utils/__init__.py +2 -0
  713. scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +3 -0
  714. scitex/scholar/metadata_engines/utils/_standardize_metadata.py +2 -3
  715. scitex/scholar/pdf_download/ScholarPDFDownloader.py +25 -37
  716. scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +11 -19
  717. scitex/scholar/pdf_download/strategies/direct_download.py +5 -9
  718. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +3 -3
  719. scitex/scholar/pdf_download/strategies/manual_download_utils.py +6 -13
  720. scitex/scholar/pdf_download/strategies/open_access_download.py +49 -31
  721. scitex/scholar/pdf_download/strategies/response_body.py +8 -19
  722. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +9 -18
  723. scitex/scholar/pipelines/ScholarPipelineMetadataParallel.py +25 -26
  724. scitex/scholar/pipelines/ScholarPipelineMetadataSingle.py +62 -23
  725. scitex/scholar/pipelines/ScholarPipelineParallel.py +13 -30
  726. scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +299 -220
  727. scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +202 -165
  728. scitex/scholar/pipelines/ScholarPipelineSingle.py +25 -51
  729. scitex/scholar/pipelines/SearchQueryParser.py +55 -55
  730. scitex/scholar/search_engines/ScholarSearchEngine.py +31 -27
  731. scitex/scholar/search_engines/_BaseSearchEngine.py +20 -23
  732. scitex/scholar/search_engines/individual/ArXivSearchEngine.py +53 -35
  733. scitex/scholar/search_engines/individual/CrossRefSearchEngine.py +47 -40
  734. scitex/scholar/search_engines/individual/OpenAlexSearchEngine.py +55 -50
  735. scitex/scholar/search_engines/individual/PubMedSearchEngine.py +8 -10
  736. scitex/scholar/search_engines/individual/SemanticScholarSearchEngine.py +55 -49
  737. scitex/scholar/storage/BibTeXHandler.py +150 -95
  738. scitex/scholar/storage/PaperIO.py +3 -6
  739. scitex/scholar/storage/ScholarLibrary.py +70 -49
  740. scitex/scholar/storage/_DeduplicationManager.py +52 -25
  741. scitex/scholar/storage/_LibraryCacheManager.py +19 -46
  742. scitex/scholar/storage/_LibraryManager.py +65 -175
  743. scitex/scholar/url_finder/ScholarURLFinder.py +9 -25
  744. scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +1 -1
  745. scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +6 -10
  746. scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +4 -6
  747. scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +8 -15
  748. scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +3 -3
  749. scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +3 -3
  750. scitex/scholar/url_finder/translators/core/patterns.py +6 -4
  751. scitex/scholar/url_finder/translators/core/registry.py +6 -9
  752. scitex/scholar/url_finder/translators/individual/BOFiP_Impots.py +60 -52
  753. scitex/scholar/url_finder/translators/individual/Baidu_Scholar.py +54 -62
  754. scitex/scholar/url_finder/translators/individual/Bangkok_Post.py +38 -44
  755. scitex/scholar/url_finder/translators/individual/Baruch_Foundation.py +43 -47
  756. scitex/scholar/url_finder/translators/individual/Beobachter.py +46 -50
  757. scitex/scholar/url_finder/translators/individual/Bezneng_Gajit.py +37 -41
  758. scitex/scholar/url_finder/translators/individual/BibLaTeX.py +59 -52
  759. scitex/scholar/url_finder/translators/individual/BibTeX.py +83 -79
  760. scitex/scholar/url_finder/translators/individual/Biblio_com.py +48 -51
  761. scitex/scholar/url_finder/translators/individual/Bibliontology_RDF.py +58 -56
  762. scitex/scholar/url_finder/translators/individual/Camara_Brasileira_do_Livro_ISBN.py +102 -99
  763. scitex/scholar/url_finder/translators/individual/CanLII.py +49 -43
  764. scitex/scholar/url_finder/translators/individual/Canada_com.py +36 -40
  765. scitex/scholar/url_finder/translators/individual/Canadian_Letters_and_Images.py +43 -43
  766. scitex/scholar/url_finder/translators/individual/Canadiana_ca.py +77 -66
  767. scitex/scholar/url_finder/translators/individual/Cascadilla_Proceedings_Project.py +68 -62
  768. scitex/scholar/url_finder/translators/individual/Central_and_Eastern_European_Online_Library_Journals.py +60 -60
  769. scitex/scholar/url_finder/translators/individual/Champlain_Society_Collection.py +63 -61
  770. scitex/scholar/url_finder/translators/individual/Chicago_Journal_of_Theoretical_Computer_Science.py +74 -58
  771. scitex/scholar/url_finder/translators/individual/Christian_Science_Monitor.py +32 -38
  772. scitex/scholar/url_finder/translators/individual/Columbia_University_Press.py +51 -47
  773. scitex/scholar/url_finder/translators/individual/Common_Place.py +66 -57
  774. scitex/scholar/url_finder/translators/individual/Cornell_LII.py +66 -62
  775. scitex/scholar/url_finder/translators/individual/Cornell_University_Press.py +38 -45
  776. scitex/scholar/url_finder/translators/individual/CourtListener.py +52 -56
  777. scitex/scholar/url_finder/translators/individual/DAI_Zenon.py +53 -54
  778. scitex/scholar/url_finder/translators/individual/access_medicine.py +27 -33
  779. scitex/scholar/url_finder/translators/individual/acm.py +1 -1
  780. scitex/scholar/url_finder/translators/individual/acm_digital_library.py +93 -63
  781. scitex/scholar/url_finder/translators/individual/airiti.py +3 -1
  782. scitex/scholar/url_finder/translators/individual/aosic.py +3 -1
  783. scitex/scholar/url_finder/translators/individual/archive_ouverte_aosic.py +3 -1
  784. scitex/scholar/url_finder/translators/individual/archive_ouverte_en_sciences_de_l_information_et_de_la_communication___aosic_.py +6 -2
  785. scitex/scholar/url_finder/translators/individual/artforum.py +35 -27
  786. scitex/scholar/url_finder/translators/individual/arxiv.py +1 -1
  787. scitex/scholar/url_finder/translators/individual/arxiv_org.py +8 -4
  788. scitex/scholar/url_finder/translators/individual/atlanta_journal_constitution.py +22 -18
  789. scitex/scholar/url_finder/translators/individual/atypon_journals.py +19 -11
  790. scitex/scholar/url_finder/translators/individual/austlii_and_nzlii.py +48 -44
  791. scitex/scholar/url_finder/translators/individual/australian_dictionary_of_biography.py +21 -17
  792. scitex/scholar/url_finder/translators/individual/bailii.py +22 -19
  793. scitex/scholar/url_finder/translators/individual/bbc.py +46 -42
  794. scitex/scholar/url_finder/translators/individual/bbc_genome.py +37 -25
  795. scitex/scholar/url_finder/translators/individual/biblioteca_nacional_de_maestros.py +24 -20
  796. scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationale_quebec_pistard.py +42 -43
  797. scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationales_quebec.py +87 -81
  798. scitex/scholar/url_finder/translators/individual/bibliotheque_nationale_france.py +39 -37
  799. scitex/scholar/url_finder/translators/individual/bibsys.py +32 -28
  800. scitex/scholar/url_finder/translators/individual/bioconductor.py +58 -52
  801. scitex/scholar/url_finder/translators/individual/biomed_central.py +23 -15
  802. scitex/scholar/url_finder/translators/individual/biorxiv.py +26 -13
  803. scitex/scholar/url_finder/translators/individual/blogger.py +39 -43
  804. scitex/scholar/url_finder/translators/individual/bloomberg.py +48 -52
  805. scitex/scholar/url_finder/translators/individual/bloomsbury_food_library.py +37 -37
  806. scitex/scholar/url_finder/translators/individual/bluesky.py +30 -28
  807. scitex/scholar/url_finder/translators/individual/bnf_isbn.py +1 -1
  808. scitex/scholar/url_finder/translators/individual/bocc.py +66 -60
  809. scitex/scholar/url_finder/translators/individual/boe.py +52 -52
  810. scitex/scholar/url_finder/translators/individual/brill.py +3 -1
  811. scitex/scholar/url_finder/translators/individual/business_standard.py +36 -38
  812. scitex/scholar/url_finder/translators/individual/cabi_cab_abstracts.py +39 -41
  813. scitex/scholar/url_finder/translators/individual/cambridge.py +3 -1
  814. scitex/scholar/url_finder/translators/individual/cambridge_core.py +30 -24
  815. scitex/scholar/url_finder/translators/individual/caod.py +50 -46
  816. scitex/scholar/url_finder/translators/individual/cbc.py +91 -67
  817. scitex/scholar/url_finder/translators/individual/ccfr_bnf.py +49 -53
  818. scitex/scholar/url_finder/translators/individual/cia_world_factbook.py +43 -33
  819. scitex/scholar/url_finder/translators/individual/crossref_rest.py +208 -174
  820. scitex/scholar/url_finder/translators/individual/current_affairs.py +29 -35
  821. scitex/scholar/url_finder/translators/individual/dabi.py +70 -66
  822. scitex/scholar/url_finder/translators/individual/dagens_nyheter.py +3 -1
  823. scitex/scholar/url_finder/translators/individual/dagstuhl.py +10 -15
  824. scitex/scholar/url_finder/translators/individual/dar_almandumah.py +13 -9
  825. scitex/scholar/url_finder/translators/individual/dart_europe.py +19 -22
  826. scitex/scholar/url_finder/translators/individual/data_gov.py +2 -2
  827. scitex/scholar/url_finder/translators/individual/databrary.py +27 -28
  828. scitex/scholar/url_finder/translators/individual/datacite_json.py +152 -137
  829. scitex/scholar/url_finder/translators/individual/dataverse.py +68 -64
  830. scitex/scholar/url_finder/translators/individual/daum_news.py +38 -38
  831. scitex/scholar/url_finder/translators/individual/dblp.py +4 -8
  832. scitex/scholar/url_finder/translators/individual/dblp_computer_science_bibliography.py +8 -3
  833. scitex/scholar/url_finder/translators/individual/dbpia.py +5 -3
  834. scitex/scholar/url_finder/translators/individual/defense_technical_information_center.py +30 -28
  835. scitex/scholar/url_finder/translators/individual/delpher.py +102 -79
  836. scitex/scholar/url_finder/translators/individual/demographic_research.py +35 -31
  837. scitex/scholar/url_finder/translators/individual/denik_cz.py +58 -54
  838. scitex/scholar/url_finder/translators/individual/depatisnet.py +7 -10
  839. scitex/scholar/url_finder/translators/individual/der_freitag.py +81 -66
  840. scitex/scholar/url_finder/translators/individual/der_spiegel.py +56 -54
  841. scitex/scholar/url_finder/translators/individual/digibib_net.py +3 -1
  842. scitex/scholar/url_finder/translators/individual/digizeitschriften.py +3 -1
  843. scitex/scholar/url_finder/translators/individual/dpla.py +13 -14
  844. scitex/scholar/url_finder/translators/individual/dspace.py +2 -2
  845. scitex/scholar/url_finder/translators/individual/ebrary.py +3 -1
  846. scitex/scholar/url_finder/translators/individual/ebscohost.py +3 -1
  847. scitex/scholar/url_finder/translators/individual/electronic_colloquium_on_computational_complexity.py +3 -1
  848. scitex/scholar/url_finder/translators/individual/elife.py +3 -1
  849. scitex/scholar/url_finder/translators/individual/elsevier_health_journals.py +3 -1
  850. scitex/scholar/url_finder/translators/individual/emerald.py +3 -1
  851. scitex/scholar/url_finder/translators/individual/emerald_insight.py +3 -1
  852. scitex/scholar/url_finder/translators/individual/epicurious.py +3 -1
  853. scitex/scholar/url_finder/translators/individual/eurogamerusgamer.py +3 -1
  854. scitex/scholar/url_finder/translators/individual/fachportal_padagogik.py +3 -1
  855. scitex/scholar/url_finder/translators/individual/frontiers.py +1 -1
  856. scitex/scholar/url_finder/translators/individual/gale_databases.py +3 -1
  857. scitex/scholar/url_finder/translators/individual/gms_german_medical_science.py +6 -2
  858. scitex/scholar/url_finder/translators/individual/ieee_computer_society.py +6 -2
  859. scitex/scholar/url_finder/translators/individual/ieee_xplore.py +41 -35
  860. scitex/scholar/url_finder/translators/individual/inter_research_science_center.py +6 -2
  861. scitex/scholar/url_finder/translators/individual/jisc_historical_texts.py +3 -1
  862. scitex/scholar/url_finder/translators/individual/jstor.py +14 -12
  863. scitex/scholar/url_finder/translators/individual/korean_national_library.py +3 -1
  864. scitex/scholar/url_finder/translators/individual/la_times.py +3 -1
  865. scitex/scholar/url_finder/translators/individual/landesbibliographie_baden_wurttemberg.py +3 -1
  866. scitex/scholar/url_finder/translators/individual/legislative_insight.py +3 -1
  867. scitex/scholar/url_finder/translators/individual/libraries_tasmania.py +3 -1
  868. scitex/scholar/url_finder/translators/individual/library_catalog__koha_.py +3 -1
  869. scitex/scholar/url_finder/translators/individual/lingbuzz.py +2 -2
  870. scitex/scholar/url_finder/translators/individual/max_planck_institute_for_the_history_of_science_virtual_laboratory_library.py +3 -1
  871. scitex/scholar/url_finder/translators/individual/mdpi.py +12 -6
  872. scitex/scholar/url_finder/translators/individual/microbiology_society_journals.py +3 -1
  873. scitex/scholar/url_finder/translators/individual/midas_journals.py +3 -1
  874. scitex/scholar/url_finder/translators/individual/nagoya_university_opac.py +3 -1
  875. scitex/scholar/url_finder/translators/individual/nature_publishing_group.py +32 -19
  876. scitex/scholar/url_finder/translators/individual/ntsb_accident_reports.py +3 -1
  877. scitex/scholar/url_finder/translators/individual/openedition_journals.py +8 -4
  878. scitex/scholar/url_finder/translators/individual/orcid.py +16 -15
  879. scitex/scholar/url_finder/translators/individual/oxford.py +25 -19
  880. scitex/scholar/url_finder/translators/individual/oxford_dictionaries_premium.py +3 -1
  881. scitex/scholar/url_finder/translators/individual/ozon_ru.py +3 -1
  882. scitex/scholar/url_finder/translators/individual/plos.py +9 -12
  883. scitex/scholar/url_finder/translators/individual/polygon.py +3 -1
  884. scitex/scholar/url_finder/translators/individual/primo.py +3 -1
  885. scitex/scholar/url_finder/translators/individual/project_muse.py +3 -1
  886. scitex/scholar/url_finder/translators/individual/pubfactory_journals.py +3 -1
  887. scitex/scholar/url_finder/translators/individual/pubmed.py +71 -65
  888. scitex/scholar/url_finder/translators/individual/pubmed_central.py +8 -6
  889. scitex/scholar/url_finder/translators/individual/rechtspraak_nl.py +3 -1
  890. scitex/scholar/url_finder/translators/individual/sage_journals.py +25 -17
  891. scitex/scholar/url_finder/translators/individual/sciencedirect.py +36 -17
  892. scitex/scholar/url_finder/translators/individual/semantics_visual_library.py +3 -1
  893. scitex/scholar/url_finder/translators/individual/silverchair.py +70 -52
  894. scitex/scholar/url_finder/translators/individual/sora.py +3 -1
  895. scitex/scholar/url_finder/translators/individual/springer.py +15 -11
  896. scitex/scholar/url_finder/translators/individual/ssrn.py +3 -3
  897. scitex/scholar/url_finder/translators/individual/stanford_encyclopedia_of_philosophy.py +3 -1
  898. scitex/scholar/url_finder/translators/individual/superlib.py +3 -1
  899. scitex/scholar/url_finder/translators/individual/treesearch.py +3 -1
  900. scitex/scholar/url_finder/translators/individual/university_of_chicago_press_books.py +3 -1
  901. scitex/scholar/url_finder/translators/individual/vlex.py +3 -1
  902. scitex/scholar/url_finder/translators/individual/web_of_science.py +3 -1
  903. scitex/scholar/url_finder/translators/individual/web_of_science_nextgen.py +3 -1
  904. scitex/scholar/url_finder/translators/individual/wiley.py +31 -25
  905. scitex/scholar/url_finder/translators/individual/wilson_center_digital_archive.py +3 -1
  906. scitex/scholar/utils/bibtex/_parse_bibtex.py +3 -3
  907. scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +5 -9
  908. scitex/scholar/utils/text/_TextNormalizer.py +249 -176
  909. scitex/scholar/utils/validation/DOIValidator.py +31 -28
  910. scitex/scholar/utils/validation/__init__.py +0 -0
  911. scitex/scholar/utils/validation/validate_library_dois.py +61 -57
  912. scitex/scholar/zotero/__init__.py +1 -1
  913. scitex/security/cli.py +7 -20
  914. scitex/security/github.py +45 -32
  915. scitex/session/__init__.py +8 -9
  916. scitex/session/_decorator.py +49 -42
  917. scitex/session/_lifecycle.py +39 -39
  918. scitex/session/_manager.py +24 -20
  919. scitex/sh/__init__.py +4 -3
  920. scitex/sh/_execute.py +10 -7
  921. scitex/sh/_security.py +3 -3
  922. scitex/sh/_types.py +2 -3
  923. scitex/stats/__init__.py +57 -6
  924. scitex/stats/_schema.py +42 -569
  925. scitex/stats/auto/__init__.py +188 -0
  926. scitex/stats/auto/_context.py +331 -0
  927. scitex/stats/auto/_formatting.py +679 -0
  928. scitex/stats/auto/_rules.py +901 -0
  929. scitex/stats/auto/_selector.py +554 -0
  930. scitex/stats/auto/_styles.py +721 -0
  931. scitex/stats/correct/__init__.py +4 -4
  932. scitex/stats/correct/_correct_bonferroni.py +43 -34
  933. scitex/stats/correct/_correct_fdr.py +14 -40
  934. scitex/stats/correct/_correct_fdr_.py +39 -46
  935. scitex/stats/correct/_correct_holm.py +14 -32
  936. scitex/stats/correct/_correct_sidak.py +36 -21
  937. scitex/stats/descriptive/_circular.py +20 -21
  938. scitex/stats/descriptive/_describe.py +19 -5
  939. scitex/stats/descriptive/_nan.py +5 -7
  940. scitex/stats/descriptive/_real.py +4 -3
  941. scitex/stats/effect_sizes/__init__.py +10 -11
  942. scitex/stats/effect_sizes/_cliffs_delta.py +35 -32
  943. scitex/stats/effect_sizes/_cohens_d.py +30 -31
  944. scitex/stats/effect_sizes/_epsilon_squared.py +19 -22
  945. scitex/stats/effect_sizes/_eta_squared.py +23 -27
  946. scitex/stats/effect_sizes/_prob_superiority.py +18 -21
  947. scitex/stats/posthoc/__init__.py +3 -3
  948. scitex/stats/posthoc/_dunnett.py +75 -55
  949. scitex/stats/posthoc/_games_howell.py +61 -43
  950. scitex/stats/posthoc/_tukey_hsd.py +42 -34
  951. scitex/stats/power/__init__.py +2 -2
  952. scitex/stats/power/_power.py +56 -56
  953. scitex/stats/tests/__init__.py +1 -1
  954. scitex/stats/tests/correlation/__init__.py +1 -1
  955. scitex/stats/tests/correlation/_test_pearson.py +28 -38
  956. scitex/stats/utils/__init__.py +14 -17
  957. scitex/stats/utils/_effect_size.py +85 -78
  958. scitex/stats/utils/_formatters.py +49 -43
  959. scitex/stats/utils/_normalizers.py +7 -14
  960. scitex/stats/utils/_power.py +56 -56
  961. scitex/str/__init__.py +1 -0
  962. scitex/str/_clean_path.py +3 -3
  963. scitex/str/_factor_out_digits.py +86 -58
  964. scitex/str/_format_plot_text.py +180 -111
  965. scitex/str/_latex.py +19 -19
  966. scitex/str/_latex_fallback.py +9 -10
  967. scitex/str/_parse.py +3 -6
  968. scitex/str/_print_debug.py +13 -13
  969. scitex/str/_printc.py +2 -0
  970. scitex/str/_search.py +3 -3
  971. scitex/template/.legacy/_clone_project.py +9 -13
  972. scitex/template/__init__.py +10 -2
  973. scitex/template/_clone_project.py +7 -2
  974. scitex/template/_copy.py +1 -0
  975. scitex/template/_customize.py +3 -6
  976. scitex/template/_git_strategy.py +2 -3
  977. scitex/template/_rename.py +1 -0
  978. scitex/template/clone_pip_project.py +6 -7
  979. scitex/template/clone_research.py +7 -10
  980. scitex/template/clone_singularity.py +6 -7
  981. scitex/template/clone_writer_directory.py +6 -7
  982. scitex/tex/_preview.py +26 -11
  983. scitex/tex/_to_vec.py +10 -7
  984. scitex/torch/__init__.py +11 -1
  985. scitex/types/_ArrayLike.py +2 -0
  986. scitex/types/_is_listed_X.py +3 -3
  987. scitex/units.py +110 -77
  988. scitex/utils/_compress_hdf5.py +3 -3
  989. scitex/utils/_email.py +8 -4
  990. scitex/utils/_notify.py +14 -8
  991. scitex/utils/_search.py +6 -6
  992. scitex/utils/_verify_scitex_format.py +17 -42
  993. scitex/utils/_verify_scitex_format_v01.py +12 -34
  994. scitex/utils/template.py +4 -3
  995. scitex/vis/__init__.py +0 -0
  996. scitex/vis/backend/__init__.py +3 -3
  997. scitex/vis/backend/{export.py → _export.py} +1 -1
  998. scitex/vis/backend/{parser.py → _parser.py} +1 -3
  999. scitex/vis/backend/{render.py → _render.py} +1 -1
  1000. scitex/vis/canvas.py +15 -3
  1001. scitex/vis/editor/__init__.py +0 -0
  1002. scitex/vis/editor/_dearpygui_editor.py +450 -304
  1003. scitex/vis/editor/_defaults.py +114 -123
  1004. scitex/vis/editor/_edit.py +38 -26
  1005. scitex/vis/editor/_flask_editor.py +8 -8
  1006. scitex/vis/editor/_mpl_editor.py +63 -48
  1007. scitex/vis/editor/_qt_editor.py +210 -159
  1008. scitex/vis/editor/_tkinter_editor.py +146 -89
  1009. scitex/vis/editor/flask_editor/__init__.py +10 -10
  1010. scitex/vis/editor/flask_editor/_bbox.py +529 -0
  1011. scitex/vis/editor/flask_editor/{core.py → _core.py} +45 -29
  1012. scitex/vis/editor/flask_editor/_plotter.py +567 -0
  1013. scitex/vis/editor/flask_editor/_renderer.py +393 -0
  1014. scitex/vis/editor/flask_editor/{utils.py → _utils.py} +13 -14
  1015. scitex/vis/editor/flask_editor/templates/__init__.py +5 -5
  1016. scitex/vis/editor/flask_editor/templates/{html.py → _html.py} +234 -16
  1017. scitex/vis/editor/flask_editor/templates/_scripts.py +1261 -0
  1018. scitex/vis/editor/flask_editor/templates/{styles.py → _styles.py} +192 -2
  1019. scitex/vis/io/__init__.py +5 -5
  1020. scitex/vis/io/{canvas.py → _canvas.py} +8 -4
  1021. scitex/vis/io/{data.py → _data.py} +13 -9
  1022. scitex/vis/io/{directory.py → _directory.py} +7 -4
  1023. scitex/vis/io/{export.py → _export.py} +15 -12
  1024. scitex/vis/io/{load.py → _load.py} +1 -1
  1025. scitex/vis/io/{panel.py → _panel.py} +21 -13
  1026. scitex/vis/io/{save.py → _save.py} +0 -0
  1027. scitex/vis/model/__init__.py +7 -7
  1028. scitex/vis/model/{annotations.py → _annotations.py} +2 -4
  1029. scitex/vis/model/{axes.py → _axes.py} +1 -1
  1030. scitex/vis/model/{figure.py → _figure.py} +0 -0
  1031. scitex/vis/model/{guides.py → _guides.py} +1 -1
  1032. scitex/vis/model/{plot.py → _plot.py} +2 -4
  1033. scitex/vis/model/{plot_types.py → _plot_types.py} +0 -0
  1034. scitex/vis/model/{styles.py → _styles.py} +0 -0
  1035. scitex/vis/utils/__init__.py +2 -2
  1036. scitex/vis/utils/{defaults.py → _defaults.py} +1 -2
  1037. scitex/vis/utils/{validate.py → _validate.py} +3 -9
  1038. scitex/web/__init__.py +7 -1
  1039. scitex/web/_scraping.py +54 -38
  1040. scitex/web/_search_pubmed.py +30 -14
  1041. scitex/writer/.legacy/Writer_v01-refactored.py +4 -4
  1042. scitex/writer/.legacy/_compile.py +18 -28
  1043. scitex/writer/Writer.py +8 -21
  1044. scitex/writer/__init__.py +11 -11
  1045. scitex/writer/_clone_writer_project.py +2 -6
  1046. scitex/writer/_compile/__init__.py +1 -0
  1047. scitex/writer/_compile/_parser.py +1 -0
  1048. scitex/writer/_compile/_runner.py +35 -38
  1049. scitex/writer/_compile/_validator.py +1 -0
  1050. scitex/writer/_compile/manuscript.py +1 -0
  1051. scitex/writer/_compile/revision.py +1 -0
  1052. scitex/writer/_compile/supplementary.py +1 -0
  1053. scitex/writer/_compile_async.py +5 -12
  1054. scitex/writer/_project/__init__.py +1 -0
  1055. scitex/writer/_project/_create.py +10 -25
  1056. scitex/writer/_project/_trees.py +4 -9
  1057. scitex/writer/_project/_validate.py +2 -3
  1058. scitex/writer/_validate_tree_structures.py +7 -18
  1059. scitex/writer/dataclasses/__init__.py +8 -10
  1060. scitex/writer/dataclasses/config/_CONSTANTS.py +2 -3
  1061. scitex/writer/dataclasses/config/_WriterConfig.py +4 -9
  1062. scitex/writer/dataclasses/contents/_ManuscriptContents.py +14 -25
  1063. scitex/writer/dataclasses/contents/_RevisionContents.py +21 -16
  1064. scitex/writer/dataclasses/contents/_SupplementaryContents.py +21 -24
  1065. scitex/writer/dataclasses/core/_Document.py +2 -3
  1066. scitex/writer/dataclasses/core/_DocumentSection.py +8 -23
  1067. scitex/writer/dataclasses/results/_CompilationResult.py +2 -3
  1068. scitex/writer/dataclasses/results/_LaTeXIssue.py +3 -6
  1069. scitex/writer/dataclasses/results/_SaveSectionsResponse.py +20 -9
  1070. scitex/writer/dataclasses/results/_SectionReadResponse.py +24 -10
  1071. scitex/writer/dataclasses/tree/_ConfigTree.py +7 -4
  1072. scitex/writer/dataclasses/tree/_ManuscriptTree.py +10 -13
  1073. scitex/writer/dataclasses/tree/_RevisionTree.py +16 -17
  1074. scitex/writer/dataclasses/tree/_ScriptsTree.py +10 -5
  1075. scitex/writer/dataclasses/tree/_SharedTree.py +10 -13
  1076. scitex/writer/dataclasses/tree/_SupplementaryTree.py +15 -14
  1077. scitex/writer/utils/.legacy_git_retry.py +3 -8
  1078. scitex/writer/utils/_parse_latex_logs.py +2 -3
  1079. scitex/writer/utils/_parse_script_args.py +20 -23
  1080. scitex/writer/utils/_watch.py +5 -5
  1081. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/METADATA +4 -10
  1082. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/RECORD +1071 -975
  1083. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +0 -583
  1084. scitex/plt/_subplots/_export_as_csv_formatters.py +0 -112
  1085. scitex/vis/editor/flask_editor/bbox.py +0 -216
  1086. scitex/vis/editor/flask_editor/plotter.py +0 -130
  1087. scitex/vis/editor/flask_editor/renderer.py +0 -184
  1088. scitex/vis/editor/flask_editor/templates/scripts.py +0 -614
  1089. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/WHEEL +0 -0
  1090. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/entry_points.txt +0 -0
  1091. {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -48,7 +48,7 @@ def _create_checkerboard(width: int, height: int, square_size: int = 10) -> "Ima
48
48
  else:
49
49
  img_array[y, x] = dark_gray
50
50
 
51
- return Image.fromarray(img_array, 'RGBA')
51
+ return Image.fromarray(img_array, "RGBA")
52
52
 
53
53
 
54
54
  class DearPyGuiEditor:
@@ -81,6 +81,7 @@ class DearPyGuiEditor:
81
81
 
82
82
  # Get SciTeX defaults and merge with metadata
83
83
  from ._defaults import get_scitex_defaults, extract_defaults_from_metadata
84
+
84
85
  self.scitex_defaults = get_scitex_defaults()
85
86
  self.metadata_defaults = extract_defaults_from_metadata(metadata)
86
87
 
@@ -97,7 +98,9 @@ class DearPyGuiEditor:
97
98
  # Click-to-select state
98
99
  self._selected_element = None # {'type': 'trace'|'title'|'xlabel'|'ylabel'|'legend'|'xaxis'|'yaxis', 'index': int|None}
99
100
  self._selected_trace_index = None # Legacy compat
100
- self._preview_bounds = None # (x_offset, y_offset, width, height) of figure in preview
101
+ self._preview_bounds = (
102
+ None # (x_offset, y_offset, width, height) of figure in preview
103
+ )
101
104
  self._axes_transform = None # Transform info for data coordinates
102
105
  self._element_bboxes = {} # Store bboxes for all selectable elements
103
106
 
@@ -139,7 +142,7 @@ class DearPyGuiEditor:
139
142
  width=width,
140
143
  height=height,
141
144
  default_value=texture_data,
142
- tag="preview_texture"
145
+ tag="preview_texture",
143
146
  )
144
147
 
145
148
  # Create main window
@@ -166,7 +169,10 @@ class DearPyGuiEditor:
166
169
  def _create_preview_panel(self, dpg):
167
170
  """Create the preview panel with figure image, click handler, and hover detection."""
168
171
  with dpg.child_window(width=900, height=-1, tag="preview_panel"):
169
- dpg.add_text("Figure Preview (click to select, hover to highlight)", color=(100, 200, 100))
172
+ dpg.add_text(
173
+ "Figure Preview (click to select, hover to highlight)",
174
+ color=(100, 200, 100),
175
+ )
170
176
  dpg.add_separator()
171
177
 
172
178
  # Image display with click and move handlers
@@ -191,7 +197,7 @@ class DearPyGuiEditor:
191
197
  with dpg.collapsing_header(label="Labels", default_open=True):
192
198
  dpg.add_input_text(
193
199
  label="Title",
194
- default_value=self.current_overrides.get('title', ''),
200
+ default_value=self.current_overrides.get("title", ""),
195
201
  tag="title_input",
196
202
  callback=self._on_value_change,
197
203
  on_enter=True,
@@ -199,7 +205,7 @@ class DearPyGuiEditor:
199
205
  )
200
206
  dpg.add_input_text(
201
207
  label="X Label",
202
- default_value=self.current_overrides.get('xlabel', ''),
208
+ default_value=self.current_overrides.get("xlabel", ""),
203
209
  tag="xlabel_input",
204
210
  callback=self._on_value_change,
205
211
  on_enter=True,
@@ -207,7 +213,7 @@ class DearPyGuiEditor:
207
213
  )
208
214
  dpg.add_input_text(
209
215
  label="Y Label",
210
- default_value=self.current_overrides.get('ylabel', ''),
216
+ default_value=self.current_overrides.get("ylabel", ""),
211
217
  tag="ylabel_input",
212
218
  callback=self._on_value_change,
213
219
  on_enter=True,
@@ -217,7 +223,7 @@ class DearPyGuiEditor:
217
223
  # Axis Limits Section
218
224
  with dpg.collapsing_header(label="Axis Limits", default_open=False):
219
225
  with dpg.group(horizontal=True):
220
- xlim = self.current_overrides.get('xlim', [0, 1])
226
+ xlim = self.current_overrides.get("xlim", [0, 1])
221
227
  dpg.add_input_float(
222
228
  label="X Min",
223
229
  default_value=xlim[0] if xlim else 0,
@@ -231,7 +237,7 @@ class DearPyGuiEditor:
231
237
  width=100,
232
238
  )
233
239
  with dpg.group(horizontal=True):
234
- ylim = self.current_overrides.get('ylim', [0, 1])
240
+ ylim = self.current_overrides.get("ylim", [0, 1])
235
241
  dpg.add_input_float(
236
242
  label="Y Min",
237
243
  default_value=ylim[0] if ylim else 0,
@@ -254,7 +260,7 @@ class DearPyGuiEditor:
254
260
  with dpg.collapsing_header(label="Line Style", default_open=True):
255
261
  dpg.add_slider_float(
256
262
  label="Line Width (pt)",
257
- default_value=self.current_overrides.get('linewidth', 1.0),
263
+ default_value=self.current_overrides.get("linewidth", 1.0),
258
264
  min_value=0.1,
259
265
  max_value=5.0,
260
266
  tag="linewidth_slider",
@@ -266,7 +272,7 @@ class DearPyGuiEditor:
266
272
  with dpg.collapsing_header(label="Font Settings", default_open=False):
267
273
  dpg.add_slider_int(
268
274
  label="Title Font Size",
269
- default_value=self.current_overrides.get('title_fontsize', 8),
275
+ default_value=self.current_overrides.get("title_fontsize", 8),
270
276
  min_value=6,
271
277
  max_value=20,
272
278
  tag="title_fontsize_slider",
@@ -275,7 +281,7 @@ class DearPyGuiEditor:
275
281
  )
276
282
  dpg.add_slider_int(
277
283
  label="Axis Font Size",
278
- default_value=self.current_overrides.get('axis_fontsize', 7),
284
+ default_value=self.current_overrides.get("axis_fontsize", 7),
279
285
  min_value=6,
280
286
  max_value=16,
281
287
  tag="axis_fontsize_slider",
@@ -284,7 +290,7 @@ class DearPyGuiEditor:
284
290
  )
285
291
  dpg.add_slider_int(
286
292
  label="Tick Font Size",
287
- default_value=self.current_overrides.get('tick_fontsize', 7),
293
+ default_value=self.current_overrides.get("tick_fontsize", 7),
288
294
  min_value=6,
289
295
  max_value=16,
290
296
  tag="tick_fontsize_slider",
@@ -293,7 +299,7 @@ class DearPyGuiEditor:
293
299
  )
294
300
  dpg.add_slider_int(
295
301
  label="Legend Font Size",
296
- default_value=self.current_overrides.get('legend_fontsize', 6),
302
+ default_value=self.current_overrides.get("legend_fontsize", 6),
297
303
  min_value=4,
298
304
  max_value=14,
299
305
  tag="legend_fontsize_slider",
@@ -305,7 +311,7 @@ class DearPyGuiEditor:
305
311
  with dpg.collapsing_header(label="Tick Settings", default_open=False):
306
312
  dpg.add_slider_int(
307
313
  label="N Ticks",
308
- default_value=self.current_overrides.get('n_ticks', 4),
314
+ default_value=self.current_overrides.get("n_ticks", 4),
309
315
  min_value=2,
310
316
  max_value=10,
311
317
  tag="n_ticks_slider",
@@ -314,7 +320,7 @@ class DearPyGuiEditor:
314
320
  )
315
321
  dpg.add_slider_float(
316
322
  label="Tick Length (mm)",
317
- default_value=self.current_overrides.get('tick_length', 0.8),
323
+ default_value=self.current_overrides.get("tick_length", 0.8),
318
324
  min_value=0.2,
319
325
  max_value=3.0,
320
326
  tag="tick_length_slider",
@@ -323,7 +329,7 @@ class DearPyGuiEditor:
323
329
  )
324
330
  dpg.add_slider_float(
325
331
  label="Tick Width (mm)",
326
- default_value=self.current_overrides.get('tick_width', 0.2),
332
+ default_value=self.current_overrides.get("tick_width", 0.2),
327
333
  min_value=0.05,
328
334
  max_value=1.0,
329
335
  tag="tick_width_slider",
@@ -333,7 +339,7 @@ class DearPyGuiEditor:
333
339
  dpg.add_combo(
334
340
  label="Tick Direction",
335
341
  items=["out", "in", "inout"],
336
- default_value=self.current_overrides.get('tick_direction', 'out'),
342
+ default_value=self.current_overrides.get("tick_direction", "out"),
337
343
  tag="tick_direction_combo",
338
344
  callback=self._on_value_change,
339
345
  width=150,
@@ -343,31 +349,31 @@ class DearPyGuiEditor:
343
349
  with dpg.collapsing_header(label="Style", default_open=True):
344
350
  dpg.add_checkbox(
345
351
  label="Show Grid",
346
- default_value=self.current_overrides.get('grid', False),
352
+ default_value=self.current_overrides.get("grid", False),
347
353
  tag="grid_checkbox",
348
354
  callback=self._on_value_change,
349
355
  )
350
356
  dpg.add_checkbox(
351
357
  label="Hide Top Spine",
352
- default_value=self.current_overrides.get('hide_top_spine', True),
358
+ default_value=self.current_overrides.get("hide_top_spine", True),
353
359
  tag="hide_top_spine_checkbox",
354
360
  callback=self._on_value_change,
355
361
  )
356
362
  dpg.add_checkbox(
357
363
  label="Hide Right Spine",
358
- default_value=self.current_overrides.get('hide_right_spine', True),
364
+ default_value=self.current_overrides.get("hide_right_spine", True),
359
365
  tag="hide_right_spine_checkbox",
360
366
  callback=self._on_value_change,
361
367
  )
362
368
  dpg.add_checkbox(
363
369
  label="Transparent Background",
364
- default_value=self.current_overrides.get('transparent', True),
370
+ default_value=self.current_overrides.get("transparent", True),
365
371
  tag="transparent_checkbox",
366
372
  callback=self._on_value_change,
367
373
  )
368
374
  dpg.add_slider_float(
369
375
  label="Axis Width (mm)",
370
- default_value=self.current_overrides.get('axis_width', 0.2),
376
+ default_value=self.current_overrides.get("axis_width", 0.2),
371
377
  min_value=0.05,
372
378
  max_value=1.0,
373
379
  tag="axis_width_slider",
@@ -379,30 +385,47 @@ class DearPyGuiEditor:
379
385
  with dpg.collapsing_header(label="Legend", default_open=False):
380
386
  dpg.add_checkbox(
381
387
  label="Show Legend",
382
- default_value=self.current_overrides.get('legend_visible', True),
388
+ default_value=self.current_overrides.get("legend_visible", True),
383
389
  tag="legend_visible_checkbox",
384
390
  callback=self._on_value_change,
385
391
  )
386
392
  dpg.add_checkbox(
387
393
  label="Show Frame",
388
- default_value=self.current_overrides.get('legend_frameon', False),
394
+ default_value=self.current_overrides.get("legend_frameon", False),
389
395
  tag="legend_frameon_checkbox",
390
396
  callback=self._on_value_change,
391
397
  )
392
398
  dpg.add_combo(
393
399
  label="Position",
394
- items=["best", "upper right", "upper left", "lower right",
395
- "lower left", "center right", "center left",
396
- "upper center", "lower center", "center"],
397
- default_value=self.current_overrides.get('legend_loc', 'best'),
400
+ items=[
401
+ "best",
402
+ "upper right",
403
+ "upper left",
404
+ "lower right",
405
+ "lower left",
406
+ "center right",
407
+ "center left",
408
+ "upper center",
409
+ "lower center",
410
+ "center",
411
+ ],
412
+ default_value=self.current_overrides.get("legend_loc", "best"),
398
413
  tag="legend_loc_combo",
399
414
  callback=self._on_value_change,
400
415
  width=150,
401
416
  )
402
417
 
403
418
  # Selected Element Section (click on preview to select)
404
- with dpg.collapsing_header(label="Selected Element", default_open=True, tag="selected_element_header"):
405
- dpg.add_text("Click on preview to select elements", tag="element_hint_text", color=(150, 150, 150))
419
+ with dpg.collapsing_header(
420
+ label="Selected Element",
421
+ default_open=True,
422
+ tag="selected_element_header",
423
+ ):
424
+ dpg.add_text(
425
+ "Click on preview to select elements",
426
+ tag="element_hint_text",
427
+ color=(150, 150, 150),
428
+ )
406
429
  dpg.add_combo(
407
430
  label="Element",
408
431
  items=self._get_all_element_labels(),
@@ -542,9 +565,18 @@ class DearPyGuiEditor:
542
565
  )
543
566
  dpg.add_combo(
544
567
  label="Position",
545
- items=["best", "upper right", "upper left", "lower right",
546
- "lower left", "center right", "center left",
547
- "upper center", "lower center", "center"],
568
+ items=[
569
+ "best",
570
+ "upper right",
571
+ "upper left",
572
+ "lower right",
573
+ "lower left",
574
+ "center right",
575
+ "center left",
576
+ "upper center",
577
+ "lower center",
578
+ "center",
579
+ ],
548
580
  default_value="best",
549
581
  tag="legend_loc_edit",
550
582
  callback=self._on_legend_element_change,
@@ -568,7 +600,7 @@ class DearPyGuiEditor:
568
600
 
569
601
  # Dimensions Section
570
602
  with dpg.collapsing_header(label="Dimensions", default_open=False):
571
- fig_size = self.current_overrides.get('fig_size', [3.15, 2.68])
603
+ fig_size = self.current_overrides.get("fig_size", [3.15, 2.68])
572
604
  with dpg.group(horizontal=True):
573
605
  dpg.add_input_float(
574
606
  label="Width (in)",
@@ -584,7 +616,7 @@ class DearPyGuiEditor:
584
616
  )
585
617
  dpg.add_slider_int(
586
618
  label="DPI",
587
- default_value=self.current_overrides.get('dpi', 300),
619
+ default_value=self.current_overrides.get("dpi", 300),
588
620
  min_value=72,
589
621
  max_value=600,
590
622
  tag="dpi_slider",
@@ -657,6 +689,7 @@ class DearPyGuiEditor:
657
689
  def _on_value_change(self, sender, app_data, user_data=None):
658
690
  """Handle value changes from widgets."""
659
691
  import dearpygui.dearpygui as dpg
692
+
660
693
  self._user_modified = True
661
694
  self._collect_overrides(dpg)
662
695
  self._update_preview(dpg)
@@ -664,43 +697,55 @@ class DearPyGuiEditor:
664
697
  def _collect_overrides(self, dpg):
665
698
  """Collect current values from all widgets."""
666
699
  # Labels
667
- self.current_overrides['title'] = dpg.get_value("title_input")
668
- self.current_overrides['xlabel'] = dpg.get_value("xlabel_input")
669
- self.current_overrides['ylabel'] = dpg.get_value("ylabel_input")
700
+ self.current_overrides["title"] = dpg.get_value("title_input")
701
+ self.current_overrides["xlabel"] = dpg.get_value("xlabel_input")
702
+ self.current_overrides["ylabel"] = dpg.get_value("ylabel_input")
670
703
 
671
704
  # Line style
672
- self.current_overrides['linewidth'] = dpg.get_value("linewidth_slider")
705
+ self.current_overrides["linewidth"] = dpg.get_value("linewidth_slider")
673
706
 
674
707
  # Font settings
675
- self.current_overrides['title_fontsize'] = dpg.get_value("title_fontsize_slider")
676
- self.current_overrides['axis_fontsize'] = dpg.get_value("axis_fontsize_slider")
677
- self.current_overrides['tick_fontsize'] = dpg.get_value("tick_fontsize_slider")
678
- self.current_overrides['legend_fontsize'] = dpg.get_value("legend_fontsize_slider")
708
+ self.current_overrides["title_fontsize"] = dpg.get_value(
709
+ "title_fontsize_slider"
710
+ )
711
+ self.current_overrides["axis_fontsize"] = dpg.get_value("axis_fontsize_slider")
712
+ self.current_overrides["tick_fontsize"] = dpg.get_value("tick_fontsize_slider")
713
+ self.current_overrides["legend_fontsize"] = dpg.get_value(
714
+ "legend_fontsize_slider"
715
+ )
679
716
 
680
717
  # Tick settings
681
- self.current_overrides['n_ticks'] = dpg.get_value("n_ticks_slider")
682
- self.current_overrides['tick_length'] = dpg.get_value("tick_length_slider")
683
- self.current_overrides['tick_width'] = dpg.get_value("tick_width_slider")
684
- self.current_overrides['tick_direction'] = dpg.get_value("tick_direction_combo")
718
+ self.current_overrides["n_ticks"] = dpg.get_value("n_ticks_slider")
719
+ self.current_overrides["tick_length"] = dpg.get_value("tick_length_slider")
720
+ self.current_overrides["tick_width"] = dpg.get_value("tick_width_slider")
721
+ self.current_overrides["tick_direction"] = dpg.get_value("tick_direction_combo")
685
722
 
686
723
  # Style
687
- self.current_overrides['grid'] = dpg.get_value("grid_checkbox")
688
- self.current_overrides['hide_top_spine'] = dpg.get_value("hide_top_spine_checkbox")
689
- self.current_overrides['hide_right_spine'] = dpg.get_value("hide_right_spine_checkbox")
690
- self.current_overrides['transparent'] = dpg.get_value("transparent_checkbox")
691
- self.current_overrides['axis_width'] = dpg.get_value("axis_width_slider")
724
+ self.current_overrides["grid"] = dpg.get_value("grid_checkbox")
725
+ self.current_overrides["hide_top_spine"] = dpg.get_value(
726
+ "hide_top_spine_checkbox"
727
+ )
728
+ self.current_overrides["hide_right_spine"] = dpg.get_value(
729
+ "hide_right_spine_checkbox"
730
+ )
731
+ self.current_overrides["transparent"] = dpg.get_value("transparent_checkbox")
732
+ self.current_overrides["axis_width"] = dpg.get_value("axis_width_slider")
692
733
 
693
734
  # Legend
694
- self.current_overrides['legend_visible'] = dpg.get_value("legend_visible_checkbox")
695
- self.current_overrides['legend_frameon'] = dpg.get_value("legend_frameon_checkbox")
696
- self.current_overrides['legend_loc'] = dpg.get_value("legend_loc_combo")
735
+ self.current_overrides["legend_visible"] = dpg.get_value(
736
+ "legend_visible_checkbox"
737
+ )
738
+ self.current_overrides["legend_frameon"] = dpg.get_value(
739
+ "legend_frameon_checkbox"
740
+ )
741
+ self.current_overrides["legend_loc"] = dpg.get_value("legend_loc_combo")
697
742
 
698
743
  # Dimensions
699
- self.current_overrides['fig_size'] = [
744
+ self.current_overrides["fig_size"] = [
700
745
  dpg.get_value("fig_width_input"),
701
746
  dpg.get_value("fig_height_input"),
702
747
  ]
703
- self.current_overrides['dpi'] = dpg.get_value("dpi_slider")
748
+ self.current_overrides["dpi"] = dpg.get_value("dpi_slider")
704
749
 
705
750
  def _apply_limits(self, sender=None, app_data=None, user_data=None):
706
751
  """Apply axis limits."""
@@ -712,9 +757,9 @@ class DearPyGuiEditor:
712
757
  ymax = dpg.get_value("ymax_input")
713
758
 
714
759
  if xmin < xmax:
715
- self.current_overrides['xlim'] = [xmin, xmax]
760
+ self.current_overrides["xlim"] = [xmin, xmax]
716
761
  if ymin < ymax:
717
- self.current_overrides['ylim'] = [ymin, ymax]
762
+ self.current_overrides["ylim"] = [ymin, ymax]
718
763
 
719
764
  self._user_modified = True
720
765
  self._update_preview(dpg)
@@ -730,16 +775,18 @@ class DearPyGuiEditor:
730
775
  x = dpg.get_value("annot_x_input")
731
776
  y = dpg.get_value("annot_y_input")
732
777
 
733
- if 'annotations' not in self.current_overrides:
734
- self.current_overrides['annotations'] = []
735
-
736
- self.current_overrides['annotations'].append({
737
- 'type': 'text',
738
- 'text': text,
739
- 'x': x,
740
- 'y': y,
741
- 'fontsize': self.current_overrides.get('axis_fontsize', 7),
742
- })
778
+ if "annotations" not in self.current_overrides:
779
+ self.current_overrides["annotations"] = []
780
+
781
+ self.current_overrides["annotations"].append(
782
+ {
783
+ "type": "text",
784
+ "text": text,
785
+ "x": x,
786
+ "y": y,
787
+ "fontsize": self.current_overrides.get("axis_fontsize", 7),
788
+ }
789
+ )
743
790
 
744
791
  dpg.set_value("annot_text_input", "")
745
792
  self._update_annotations_list(dpg)
@@ -751,7 +798,7 @@ class DearPyGuiEditor:
751
798
  import dearpygui.dearpygui as dpg
752
799
 
753
800
  selected = dpg.get_value("annotations_listbox")
754
- annotations = self.current_overrides.get('annotations', [])
801
+ annotations = self.current_overrides.get("annotations", [])
755
802
 
756
803
  if selected and annotations:
757
804
  # Find index by text
@@ -767,10 +814,10 @@ class DearPyGuiEditor:
767
814
 
768
815
  def _update_annotations_list(self, dpg):
769
816
  """Update the annotations listbox."""
770
- annotations = self.current_overrides.get('annotations', [])
817
+ annotations = self.current_overrides.get("annotations", [])
771
818
  items = []
772
819
  for ann in annotations:
773
- if ann.get('type') == 'text':
820
+ if ann.get("type") == "text":
774
821
  label = f"{ann.get('text', '')[:20]} ({ann.get('x', 0):.2f}, {ann.get('y', 0):.2f})"
775
822
  items.append(label)
776
823
 
@@ -805,27 +852,45 @@ class DearPyGuiEditor:
805
852
  try:
806
853
  # Start with a copy of cached base
807
854
  img = self._cached_base_image.copy()
808
- draw = ImageDraw.Draw(img, 'RGBA')
855
+ draw = ImageDraw.Draw(img, "RGBA")
809
856
 
810
857
  # Get hover element type
811
- hovered_type = self._hovered_element.get('type') if self._hovered_element else None
812
- selected_type = self._selected_element.get('type') if self._selected_element else None
858
+ hovered_type = (
859
+ self._hovered_element.get("type") if self._hovered_element else None
860
+ )
861
+ selected_type = (
862
+ self._selected_element.get("type") if self._selected_element else None
863
+ )
813
864
 
814
865
  # Draw hover highlight (outline only, no fill) for non-trace elements
815
- if hovered_type and hovered_type != 'trace' and hovered_type != selected_type:
866
+ if (
867
+ hovered_type
868
+ and hovered_type != "trace"
869
+ and hovered_type != selected_type
870
+ ):
816
871
  bbox = self._element_bboxes.get(hovered_type)
817
872
  if bbox:
818
873
  x0, y0, x1, y1 = bbox
819
874
  # Transparent outline only - no fill to avoid covering content
820
- draw.rectangle([x0-2, y0-2, x1+2, y1+2], fill=None, outline=(100, 180, 255, 100), width=1)
875
+ draw.rectangle(
876
+ [x0 - 2, y0 - 2, x1 + 2, y1 + 2],
877
+ fill=None,
878
+ outline=(100, 180, 255, 100),
879
+ width=1,
880
+ )
821
881
 
822
882
  # Draw selection highlight (outline only, no fill) for non-trace elements
823
- if selected_type and selected_type != 'trace':
883
+ if selected_type and selected_type != "trace":
824
884
  bbox = self._element_bboxes.get(selected_type)
825
885
  if bbox:
826
886
  x0, y0, x1, y1 = bbox
827
887
  # Transparent outline only - no fill to avoid covering content
828
- draw.rectangle([x0-2, y0-2, x1+2, y1+2], fill=None, outline=(255, 200, 80, 150), width=2)
888
+ draw.rectangle(
889
+ [x0 - 2, y0 - 2, x1 + 2, y1 + 2],
890
+ fill=None,
891
+ outline=(255, 200, 80, 150),
892
+ width=2,
893
+ )
829
894
 
830
895
  # Convert to DearPyGui texture format
831
896
  img_array = np.array(img).astype(np.float32) / 255.0
@@ -841,7 +906,8 @@ class DearPyGuiEditor:
841
906
  def _render_figure(self):
842
907
  """Render figure and return as RGBA data for texture."""
843
908
  import matplotlib
844
- matplotlib.use('Agg')
909
+
910
+ matplotlib.use("Agg")
845
911
  import matplotlib.pyplot as plt
846
912
  from matplotlib.ticker import MaxNLocator
847
913
  import numpy as np
@@ -855,72 +921,78 @@ class DearPyGuiEditor:
855
921
 
856
922
  # Dimensions - use fixed size for preview
857
923
  preview_dpi = 100
858
- fig_size = o.get('fig_size', [3.15, 2.68])
924
+ fig_size = o.get("fig_size", [3.15, 2.68])
859
925
 
860
926
  # Create figure with white background for preview
861
927
  fig, ax = plt.subplots(figsize=fig_size, dpi=preview_dpi)
862
928
 
863
929
  # For preview, use white background (transparent doesn't show well in GUI)
864
- fig.patch.set_facecolor('white')
865
- ax.patch.set_facecolor('white')
930
+ fig.patch.set_facecolor("white")
931
+ ax.patch.set_facecolor("white")
866
932
 
867
933
  # Plot from CSV data (only pass selection, hover is via PIL overlay for speed)
868
934
  if self.csv_data is not None:
869
935
  self._plot_from_csv(ax, o, highlight_trace=self._selected_trace_index)
870
936
  else:
871
- ax.text(0.5, 0.5, "No plot data available\n(CSV not found)",
872
- ha='center', va='center', transform=ax.transAxes,
873
- fontsize=o.get('axis_fontsize', 7))
937
+ ax.text(
938
+ 0.5,
939
+ 0.5,
940
+ "No plot data available\n(CSV not found)",
941
+ ha="center",
942
+ va="center",
943
+ transform=ax.transAxes,
944
+ fontsize=o.get("axis_fontsize", 7),
945
+ )
874
946
 
875
947
  # Apply labels
876
- if o.get('title'):
877
- ax.set_title(o['title'], fontsize=o.get('title_fontsize', 8))
878
- if o.get('xlabel'):
879
- ax.set_xlabel(o['xlabel'], fontsize=o.get('axis_fontsize', 7))
880
- if o.get('ylabel'):
881
- ax.set_ylabel(o['ylabel'], fontsize=o.get('axis_fontsize', 7))
948
+ if o.get("title"):
949
+ ax.set_title(o["title"], fontsize=o.get("title_fontsize", 8))
950
+ if o.get("xlabel"):
951
+ ax.set_xlabel(o["xlabel"], fontsize=o.get("axis_fontsize", 7))
952
+ if o.get("ylabel"):
953
+ ax.set_ylabel(o["ylabel"], fontsize=o.get("axis_fontsize", 7))
882
954
 
883
955
  # Tick styling
884
956
  ax.tick_params(
885
- axis='both',
886
- labelsize=o.get('tick_fontsize', 7),
887
- length=o.get('tick_length', 0.8) * mm_to_pt,
888
- width=o.get('tick_width', 0.2) * mm_to_pt,
889
- direction=o.get('tick_direction', 'out'),
957
+ axis="both",
958
+ labelsize=o.get("tick_fontsize", 7),
959
+ length=o.get("tick_length", 0.8) * mm_to_pt,
960
+ width=o.get("tick_width", 0.2) * mm_to_pt,
961
+ direction=o.get("tick_direction", "out"),
890
962
  )
891
963
 
892
964
  # Number of ticks
893
- ax.xaxis.set_major_locator(MaxNLocator(nbins=o.get('n_ticks', 4)))
894
- ax.yaxis.set_major_locator(MaxNLocator(nbins=o.get('n_ticks', 4)))
965
+ ax.xaxis.set_major_locator(MaxNLocator(nbins=o.get("n_ticks", 4)))
966
+ ax.yaxis.set_major_locator(MaxNLocator(nbins=o.get("n_ticks", 4)))
895
967
 
896
968
  # Grid
897
- if o.get('grid'):
898
- ax.grid(True, linewidth=o.get('axis_width', 0.2) * mm_to_pt, alpha=0.3)
969
+ if o.get("grid"):
970
+ ax.grid(True, linewidth=o.get("axis_width", 0.2) * mm_to_pt, alpha=0.3)
899
971
 
900
972
  # Axis limits
901
- if o.get('xlim'):
902
- ax.set_xlim(o['xlim'])
903
- if o.get('ylim'):
904
- ax.set_ylim(o['ylim'])
973
+ if o.get("xlim"):
974
+ ax.set_xlim(o["xlim"])
975
+ if o.get("ylim"):
976
+ ax.set_ylim(o["ylim"])
905
977
 
906
978
  # Spines
907
- if o.get('hide_top_spine', True):
908
- ax.spines['top'].set_visible(False)
909
- if o.get('hide_right_spine', True):
910
- ax.spines['right'].set_visible(False)
979
+ if o.get("hide_top_spine", True):
980
+ ax.spines["top"].set_visible(False)
981
+ if o.get("hide_right_spine", True):
982
+ ax.spines["right"].set_visible(False)
911
983
 
912
984
  for spine in ax.spines.values():
913
- spine.set_linewidth(o.get('axis_width', 0.2) * mm_to_pt)
985
+ spine.set_linewidth(o.get("axis_width", 0.2) * mm_to_pt)
914
986
 
915
987
  # Annotations
916
- for annot in o.get('annotations', []):
917
- if annot.get('type') == 'text':
988
+ for annot in o.get("annotations", []):
989
+ if annot.get("type") == "text":
918
990
  ax.text(
919
- annot.get('x', 0.5),
920
- annot.get('y', 0.5),
921
- annot.get('text', ''),
991
+ annot.get("x", 0.5),
992
+ annot.get("y", 0.5),
993
+ annot.get("text", ""),
922
994
  transform=ax.transAxes,
923
- fontsize=annot.get('fontsize', o.get('axis_fontsize', 7)),
995
+ fontsize=annot.get("fontsize", o.get("axis_fontsize", 7)),
924
996
  )
925
997
 
926
998
  fig.tight_layout()
@@ -945,8 +1017,11 @@ class DearPyGuiEditor:
945
1017
  if ax.title.get_text():
946
1018
  try:
947
1019
  title_bbox = ax.title.get_window_extent(fig.canvas.get_renderer())
948
- self._element_bboxes_raw['title'] = (
949
- title_bbox.x0, title_bbox.y0, title_bbox.x1, title_bbox.y1
1020
+ self._element_bboxes_raw["title"] = (
1021
+ title_bbox.x0,
1022
+ title_bbox.y0,
1023
+ title_bbox.x1,
1024
+ title_bbox.y1,
950
1025
  )
951
1026
  except Exception:
952
1027
  pass
@@ -954,9 +1029,14 @@ class DearPyGuiEditor:
954
1029
  # X label bbox
955
1030
  if ax.xaxis.label.get_text():
956
1031
  try:
957
- xlabel_bbox = ax.xaxis.label.get_window_extent(fig.canvas.get_renderer())
958
- self._element_bboxes_raw['xlabel'] = (
959
- xlabel_bbox.x0, xlabel_bbox.y0, xlabel_bbox.x1, xlabel_bbox.y1
1032
+ xlabel_bbox = ax.xaxis.label.get_window_extent(
1033
+ fig.canvas.get_renderer()
1034
+ )
1035
+ self._element_bboxes_raw["xlabel"] = (
1036
+ xlabel_bbox.x0,
1037
+ xlabel_bbox.y0,
1038
+ xlabel_bbox.x1,
1039
+ xlabel_bbox.y1,
960
1040
  )
961
1041
  except Exception:
962
1042
  pass
@@ -964,9 +1044,14 @@ class DearPyGuiEditor:
964
1044
  # Y label bbox
965
1045
  if ax.yaxis.label.get_text():
966
1046
  try:
967
- ylabel_bbox = ax.yaxis.label.get_window_extent(fig.canvas.get_renderer())
968
- self._element_bboxes_raw['ylabel'] = (
969
- ylabel_bbox.x0, ylabel_bbox.y0, ylabel_bbox.x1, ylabel_bbox.y1
1047
+ ylabel_bbox = ax.yaxis.label.get_window_extent(
1048
+ fig.canvas.get_renderer()
1049
+ )
1050
+ self._element_bboxes_raw["ylabel"] = (
1051
+ ylabel_bbox.x0,
1052
+ ylabel_bbox.y0,
1053
+ ylabel_bbox.x1,
1054
+ ylabel_bbox.y1,
970
1055
  )
971
1056
  except Exception:
972
1057
  pass
@@ -976,40 +1061,57 @@ class DearPyGuiEditor:
976
1061
  if legend:
977
1062
  try:
978
1063
  legend_bbox = legend.get_window_extent(fig.canvas.get_renderer())
979
- self._element_bboxes_raw['legend'] = (
980
- legend_bbox.x0, legend_bbox.y0, legend_bbox.x1, legend_bbox.y1
1064
+ self._element_bboxes_raw["legend"] = (
1065
+ legend_bbox.x0,
1066
+ legend_bbox.y0,
1067
+ legend_bbox.x1,
1068
+ legend_bbox.y1,
981
1069
  )
982
1070
  except Exception:
983
1071
  pass
984
1072
 
985
1073
  # X axis (bottom spine area)
986
1074
  try:
987
- xaxis_bbox = ax.spines['bottom'].get_window_extent(fig.canvas.get_renderer())
1075
+ xaxis_bbox = ax.spines["bottom"].get_window_extent(
1076
+ fig.canvas.get_renderer()
1077
+ )
988
1078
  # Expand bbox slightly for easier clicking
989
- self._element_bboxes_raw['xaxis'] = (
990
- xaxis_bbox.x0, xaxis_bbox.y0 - 20, xaxis_bbox.x1, xaxis_bbox.y1 + 10
1079
+ self._element_bboxes_raw["xaxis"] = (
1080
+ xaxis_bbox.x0,
1081
+ xaxis_bbox.y0 - 20,
1082
+ xaxis_bbox.x1,
1083
+ xaxis_bbox.y1 + 10,
991
1084
  )
992
1085
  except Exception:
993
1086
  pass
994
1087
 
995
1088
  # Y axis (left spine area)
996
1089
  try:
997
- yaxis_bbox = ax.spines['left'].get_window_extent(fig.canvas.get_renderer())
1090
+ yaxis_bbox = ax.spines["left"].get_window_extent(fig.canvas.get_renderer())
998
1091
  # Expand bbox slightly for easier clicking
999
- self._element_bboxes_raw['yaxis'] = (
1000
- yaxis_bbox.x0 - 20, yaxis_bbox.y0, yaxis_bbox.x1 + 10, yaxis_bbox.y1
1092
+ self._element_bboxes_raw["yaxis"] = (
1093
+ yaxis_bbox.x0 - 20,
1094
+ yaxis_bbox.y0,
1095
+ yaxis_bbox.x1 + 10,
1096
+ yaxis_bbox.y1,
1001
1097
  )
1002
1098
  except Exception:
1003
1099
  pass
1004
1100
 
1005
1101
  # Convert to RGBA data for DearPyGui texture
1006
1102
  buf = io.BytesIO()
1007
- fig.savefig(buf, format='png', dpi=preview_dpi, bbox_inches='tight',
1008
- facecolor='white', edgecolor='none')
1103
+ fig.savefig(
1104
+ buf,
1105
+ format="png",
1106
+ dpi=preview_dpi,
1107
+ bbox_inches="tight",
1108
+ facecolor="white",
1109
+ edgecolor="none",
1110
+ )
1009
1111
  buf.seek(0)
1010
1112
 
1011
1113
  # Load with PIL and convert to normalized RGBA
1012
- img = Image.open(buf).convert('RGBA')
1114
+ img = Image.open(buf).convert("RGBA")
1013
1115
  width, height = img.size
1014
1116
 
1015
1117
  # Resize to fit within max preview size while preserving aspect ratio
@@ -1027,7 +1129,7 @@ class DearPyGuiEditor:
1027
1129
  # Scale element bboxes to preview coordinates
1028
1130
  # Note: matplotlib uses bottom-left origin, we need top-left for preview
1029
1131
  self._element_bboxes = {}
1030
- for elem_type, raw_bbox in getattr(self, '_element_bboxes_raw', {}).items():
1132
+ for elem_type, raw_bbox in getattr(self, "_element_bboxes_raw", {}).items():
1031
1133
  if raw_bbox is None:
1032
1134
  continue
1033
1135
  rx0, ry0, rx1, ry1 = raw_bbox
@@ -1050,12 +1152,12 @@ class DearPyGuiEditor:
1050
1152
  self._axes_transform = (ax_x0, ax_y0, ax_width, ax_height, xlim, ylim)
1051
1153
 
1052
1154
  # Create background - checkerboard for transparent, white otherwise
1053
- transparent = o.get('transparent', True)
1155
+ transparent = o.get("transparent", True)
1054
1156
  if transparent:
1055
1157
  # Create checkerboard pattern for transparency preview
1056
1158
  padded = _create_checkerboard(max_width, max_height, square_size=10)
1057
1159
  else:
1058
- padded = Image.new('RGBA', (max_width, max_height), (255, 255, 255, 255))
1160
+ padded = Image.new("RGBA", (max_width, max_height), (255, 255, 255, 255))
1059
1161
 
1060
1162
  # Paste figure centered on background
1061
1163
  padded.paste(img, (x_offset, y_offset), img) # Use img as mask for alpha
@@ -1085,10 +1187,12 @@ class DearPyGuiEditor:
1085
1187
  renderer = fig.canvas.get_renderer()
1086
1188
 
1087
1189
  # Only draw selection highlights here (hover is done via fast PIL overlay)
1088
- selected_type = self._selected_element.get('type') if self._selected_element else None
1190
+ selected_type = (
1191
+ self._selected_element.get("type") if self._selected_element else None
1192
+ )
1089
1193
 
1090
1194
  # Skip if selecting traces (handled separately in _plot_from_csv)
1091
- if selected_type == 'trace':
1195
+ if selected_type == "trace":
1092
1196
  selected_type = None
1093
1197
 
1094
1198
  def add_highlight_box(text_obj, color, alpha, linewidth=2):
@@ -1104,7 +1208,7 @@ class DearPyGuiEditor:
1104
1208
  fig_bbox.width + 2 * padding,
1105
1209
  fig_bbox.height + 2 * padding,
1106
1210
  boxstyle="round,pad=0.02,rounding_size=0.01",
1107
- facecolor='none',
1211
+ facecolor="none",
1108
1212
  edgecolor=color,
1109
1213
  alpha=0.7,
1110
1214
  linewidth=linewidth,
@@ -1126,7 +1230,7 @@ class DearPyGuiEditor:
1126
1230
  fig_bbox.width + 2 * padding,
1127
1231
  fig_bbox.height + 2 * padding,
1128
1232
  boxstyle="round,pad=0.01",
1129
- facecolor='none',
1233
+ facecolor="none",
1130
1234
  edgecolor=color,
1131
1235
  alpha=0.7,
1132
1236
  linewidth=linewidth,
@@ -1139,20 +1243,22 @@ class DearPyGuiEditor:
1139
1243
 
1140
1244
  # Map element types to matplotlib objects
1141
1245
  element_map = {
1142
- 'title': ax.title,
1143
- 'xlabel': ax.xaxis.label,
1144
- 'ylabel': ax.yaxis.label,
1246
+ "title": ax.title,
1247
+ "xlabel": ax.xaxis.label,
1248
+ "ylabel": ax.yaxis.label,
1145
1249
  }
1146
1250
 
1147
1251
  # Draw selection highlight (outline only, no fill)
1148
- select_color = '#FFC850' # Soft warm yellow for outline
1252
+ select_color = "#FFC850" # Soft warm yellow for outline
1149
1253
  if selected_type in element_map:
1150
- add_highlight_box(element_map[selected_type], select_color, 0.0, linewidth=2)
1151
- elif selected_type == 'xaxis':
1152
- add_spine_highlight(ax.spines['bottom'], select_color, 0.0, linewidth=2)
1153
- elif selected_type == 'yaxis':
1154
- add_spine_highlight(ax.spines['left'], select_color, 0.0, linewidth=2)
1155
- elif selected_type == 'legend':
1254
+ add_highlight_box(
1255
+ element_map[selected_type], select_color, 0.0, linewidth=2
1256
+ )
1257
+ elif selected_type == "xaxis":
1258
+ add_spine_highlight(ax.spines["bottom"], select_color, 0.0, linewidth=2)
1259
+ elif selected_type == "yaxis":
1260
+ add_spine_highlight(ax.spines["left"], select_color, 0.0, linewidth=2)
1261
+ elif selected_type == "legend":
1156
1262
  legend = ax.get_legend()
1157
1263
  if legend:
1158
1264
  try:
@@ -1164,7 +1270,7 @@ class DearPyGuiEditor:
1164
1270
  fig_bbox.width + 2 * padding,
1165
1271
  fig_bbox.height + 2 * padding,
1166
1272
  boxstyle="round,pad=0.02",
1167
- facecolor='none',
1273
+ facecolor="none",
1168
1274
  edgecolor=select_color,
1169
1275
  alpha=0.7,
1170
1276
  linewidth=2,
@@ -1198,31 +1304,33 @@ class DearPyGuiEditor:
1198
1304
  return
1199
1305
 
1200
1306
  df = self.csv_data
1201
- linewidth = o.get('linewidth', 1.0)
1202
- legend_visible = o.get('legend_visible', True)
1203
- legend_fontsize = o.get('legend_fontsize', 6)
1204
- legend_frameon = o.get('legend_frameon', False)
1205
- legend_loc = _normalize_legend_loc(o.get('legend_loc', 'best'))
1307
+ linewidth = o.get("linewidth", 1.0)
1308
+ legend_visible = o.get("legend_visible", True)
1309
+ legend_fontsize = o.get("legend_fontsize", 6)
1310
+ legend_frameon = o.get("legend_frameon", False)
1311
+ legend_loc = _normalize_legend_loc(o.get("legend_loc", "best"))
1206
1312
 
1207
- traces = o.get('traces', [])
1313
+ traces = o.get("traces", [])
1208
1314
 
1209
1315
  if traces:
1210
1316
  for i, trace in enumerate(traces):
1211
- csv_cols = trace.get('csv_columns', {})
1212
- x_col = csv_cols.get('x')
1213
- y_col = csv_cols.get('y')
1317
+ csv_cols = trace.get("csv_columns", {})
1318
+ x_col = csv_cols.get("x")
1319
+ y_col = csv_cols.get("y")
1214
1320
 
1215
1321
  if x_col in df.columns and y_col in df.columns:
1216
- trace_linewidth = trace.get('linewidth', linewidth)
1217
- is_selected = (highlight_trace is not None and i == highlight_trace)
1218
- is_hovered = (hover_trace is not None and i == hover_trace and not is_selected)
1322
+ trace_linewidth = trace.get("linewidth", linewidth)
1323
+ is_selected = highlight_trace is not None and i == highlight_trace
1324
+ is_hovered = (
1325
+ hover_trace is not None and i == hover_trace and not is_selected
1326
+ )
1219
1327
 
1220
1328
  # Draw selection glow (yellow, stronger)
1221
1329
  if is_selected:
1222
1330
  ax.plot(
1223
1331
  df[x_col],
1224
1332
  df[y_col],
1225
- color='yellow',
1333
+ color="yellow",
1226
1334
  linewidth=trace_linewidth * 4,
1227
1335
  alpha=0.5,
1228
1336
  zorder=0,
@@ -1232,7 +1340,7 @@ class DearPyGuiEditor:
1232
1340
  ax.plot(
1233
1341
  df[x_col],
1234
1342
  df[y_col],
1235
- color='cyan',
1343
+ color="cyan",
1236
1344
  linewidth=trace_linewidth * 3,
1237
1345
  alpha=0.3,
1238
1346
  zorder=0,
@@ -1241,61 +1349,70 @@ class DearPyGuiEditor:
1241
1349
  ax.plot(
1242
1350
  df[x_col],
1243
1351
  df[y_col],
1244
- label=trace.get('label', trace.get('id', '')),
1245
- color=trace.get('color'),
1246
- linestyle=trace.get('linestyle', '-'),
1247
- linewidth=trace_linewidth * (1.5 if is_selected else (1.2 if is_hovered else 1.0)),
1248
- marker=trace.get('marker', None),
1249
- markersize=trace.get('markersize', 6),
1352
+ label=trace.get("label", trace.get("id", "")),
1353
+ color=trace.get("color"),
1354
+ linestyle=trace.get("linestyle", "-"),
1355
+ linewidth=trace_linewidth
1356
+ * (1.5 if is_selected else (1.2 if is_hovered else 1.0)),
1357
+ marker=trace.get("marker", None),
1358
+ markersize=trace.get("markersize", 6),
1250
1359
  zorder=10 if is_selected else (5 if is_hovered else 1),
1251
1360
  )
1252
1361
 
1253
- if legend_visible and any(t.get('label') for t in traces):
1254
- ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
1362
+ if legend_visible and any(t.get("label") for t in traces):
1363
+ ax.legend(
1364
+ fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc
1365
+ )
1255
1366
  else:
1256
1367
  # Fallback: parse column names
1257
1368
  cols = df.columns.tolist()
1258
1369
  trace_groups = {}
1259
1370
 
1260
1371
  for col in cols:
1261
- if col.endswith('_x'):
1372
+ if col.endswith("_x"):
1262
1373
  trace_id = col[:-2]
1263
- y_col = trace_id + '_y'
1374
+ y_col = trace_id + "_y"
1264
1375
  if y_col in cols:
1265
- parts = trace_id.split('_')
1376
+ parts = trace_id.split("_")
1266
1377
  label = parts[2] if len(parts) > 2 else trace_id
1267
1378
  trace_groups[trace_id] = {
1268
- 'x_col': col,
1269
- 'y_col': y_col,
1270
- 'label': label,
1379
+ "x_col": col,
1380
+ "y_col": y_col,
1381
+ "label": label,
1271
1382
  }
1272
1383
 
1273
1384
  if trace_groups:
1274
1385
  for trace_id, info in trace_groups.items():
1275
1386
  ax.plot(
1276
- df[info['x_col']],
1277
- df[info['y_col']],
1278
- label=info['label'],
1387
+ df[info["x_col"]],
1388
+ df[info["y_col"]],
1389
+ label=info["label"],
1279
1390
  linewidth=linewidth,
1280
1391
  )
1281
1392
  if legend_visible:
1282
- ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
1393
+ ax.legend(
1394
+ fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc
1395
+ )
1283
1396
  elif len(cols) >= 2:
1284
1397
  x_col = cols[0]
1285
1398
  for y_col in cols[1:]:
1286
1399
  try:
1287
- ax.plot(df[x_col], df[y_col], label=str(y_col), linewidth=linewidth)
1400
+ ax.plot(
1401
+ df[x_col], df[y_col], label=str(y_col), linewidth=linewidth
1402
+ )
1288
1403
  except Exception:
1289
1404
  pass
1290
1405
  if len(cols) > 2 and legend_visible:
1291
- ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
1406
+ ax.legend(
1407
+ fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc
1408
+ )
1292
1409
 
1293
1410
  def _get_trace_labels(self):
1294
1411
  """Get list of trace labels for selection combo."""
1295
- traces = self.current_overrides.get('traces', [])
1412
+ traces = self.current_overrides.get("traces", [])
1296
1413
  if not traces:
1297
1414
  return ["(no traces)"]
1298
- return [t.get('label', t.get('id', f'Trace {i}')) for i, t in enumerate(traces)]
1415
+ return [t.get("label", t.get("id", f"Trace {i}")) for i, t in enumerate(traces)]
1299
1416
 
1300
1417
  def _get_all_element_labels(self):
1301
1418
  """Get list of all selectable element labels."""
@@ -1310,9 +1427,9 @@ class DearPyGuiEditor:
1310
1427
  labels.append("Legend")
1311
1428
 
1312
1429
  # Traces
1313
- traces = self.current_overrides.get('traces', [])
1430
+ traces = self.current_overrides.get("traces", [])
1314
1431
  for i, t in enumerate(traces):
1315
- label = t.get('label', t.get('id', f'Trace {i}'))
1432
+ label = t.get("label", t.get("id", f"Trace {i}"))
1316
1433
  labels.append(f"Trace: {label}")
1317
1434
 
1318
1435
  return labels
@@ -1352,9 +1469,11 @@ class DearPyGuiEditor:
1352
1469
  self._select_element(element, dpg)
1353
1470
  else:
1354
1471
  # Fall back to trace selection
1355
- trace_idx = self._find_nearest_trace(click_x, click_y, max_width, max_height)
1472
+ trace_idx = self._find_nearest_trace(
1473
+ click_x, click_y, max_width, max_height
1474
+ )
1356
1475
  if trace_idx is not None:
1357
- self._select_element({'type': 'trace', 'index': trace_idx}, dpg)
1476
+ self._select_element({"type": "trace", "index": trace_idx}, dpg)
1358
1477
 
1359
1478
  def _on_preview_hover(self, sender, app_data):
1360
1479
  """Handle mouse move for hover effects on preview (optimized with caching)."""
@@ -1396,24 +1515,26 @@ class DearPyGuiEditor:
1396
1515
 
1397
1516
  if element is None:
1398
1517
  # Check for trace hover
1399
- trace_idx = self._find_nearest_trace(hover_x, hover_y, max_width, max_height)
1518
+ trace_idx = self._find_nearest_trace(
1519
+ hover_x, hover_y, max_width, max_height
1520
+ )
1400
1521
  if trace_idx is not None:
1401
- element = {'type': 'trace', 'index': trace_idx}
1522
+ element = {"type": "trace", "index": trace_idx}
1402
1523
 
1403
1524
  # Check if hover changed
1404
1525
  old_hover = self._hovered_element
1405
1526
  if element != old_hover:
1406
1527
  self._hovered_element = element
1407
1528
  if element:
1408
- elem_type = element.get('type', '')
1409
- elem_idx = element.get('index')
1410
- if elem_type == 'trace' and elem_idx is not None:
1411
- traces = self.current_overrides.get('traces', [])
1529
+ elem_type = element.get("type", "")
1530
+ elem_idx = element.get("index")
1531
+ if elem_type == "trace" and elem_idx is not None:
1532
+ traces = self.current_overrides.get("traces", [])
1412
1533
  if elem_idx < len(traces):
1413
- label = traces[elem_idx].get('label', f'Trace {elem_idx}')
1534
+ label = traces[elem_idx].get("label", f"Trace {elem_idx}")
1414
1535
  dpg.set_value("hover_text", f"Hover: {label} (click to select)")
1415
1536
  else:
1416
- label = elem_type.replace('x', 'X ').replace('y', 'Y ').title()
1537
+ label = elem_type.replace("x", "X ").replace("y", "Y ").title()
1417
1538
  dpg.set_value("hover_text", f"Hover: {label} (click to select)")
1418
1539
  else:
1419
1540
  dpg.set_value("hover_text", "")
@@ -1432,15 +1553,15 @@ class DearPyGuiEditor:
1432
1553
  continue
1433
1554
  x0, y0, x1, y1 = bbox
1434
1555
  if x0 <= click_x <= x1 and y0 <= click_y <= y1:
1435
- return {'type': element_type, 'index': None}
1556
+ return {"type": element_type, "index": None}
1436
1557
 
1437
1558
  return None
1438
1559
 
1439
1560
  def _select_element(self, element, dpg):
1440
1561
  """Select an element and show appropriate controls."""
1441
1562
  self._selected_element = element
1442
- elem_type = element.get('type')
1443
- elem_idx = element.get('index')
1563
+ elem_type = element.get("type")
1564
+ elem_idx = element.get("index")
1444
1565
 
1445
1566
  # Hide all control groups first
1446
1567
  dpg.configure_item("trace_controls_group", show=False)
@@ -1449,19 +1570,21 @@ class DearPyGuiEditor:
1449
1570
  dpg.configure_item("legend_controls_group", show=False)
1450
1571
 
1451
1572
  # Update combo selection
1452
- if elem_type == 'trace':
1453
- traces = self.current_overrides.get('traces', [])
1573
+ if elem_type == "trace":
1574
+ traces = self.current_overrides.get("traces", [])
1454
1575
  if elem_idx is not None and elem_idx < len(traces):
1455
1576
  trace = traces[elem_idx]
1456
- label = f"Trace: {trace.get('label', trace.get('id', f'Trace {elem_idx}'))}"
1577
+ label = (
1578
+ f"Trace: {trace.get('label', trace.get('id', f'Trace {elem_idx}'))}"
1579
+ )
1457
1580
  dpg.set_value("element_selector_combo", label)
1458
1581
 
1459
1582
  # Show trace controls and populate
1460
1583
  dpg.configure_item("trace_controls_group", show=True)
1461
1584
  self._selected_trace_index = elem_idx
1462
- dpg.set_value("trace_label_input", trace.get('label', ''))
1585
+ dpg.set_value("trace_label_input", trace.get("label", ""))
1463
1586
 
1464
- color_hex = trace.get('color', '#0080bf')
1587
+ color_hex = trace.get("color", "#0080bf")
1465
1588
  try:
1466
1589
  r = int(color_hex[1:3], 16)
1467
1590
  g = int(color_hex[3:5], 16)
@@ -1470,56 +1593,66 @@ class DearPyGuiEditor:
1470
1593
  except (ValueError, IndexError):
1471
1594
  dpg.set_value("trace_color_picker", [128, 128, 191])
1472
1595
 
1473
- dpg.set_value("trace_linewidth_slider", trace.get('linewidth', 1.0))
1474
- dpg.set_value("trace_linestyle_combo", trace.get('linestyle', '-'))
1475
- dpg.set_value("trace_marker_combo", trace.get('marker', '') or '')
1476
- dpg.set_value("trace_markersize_slider", trace.get('markersize', 6.0))
1596
+ dpg.set_value("trace_linewidth_slider", trace.get("linewidth", 1.0))
1597
+ dpg.set_value("trace_linestyle_combo", trace.get("linestyle", "-"))
1598
+ dpg.set_value("trace_marker_combo", trace.get("marker", "") or "")
1599
+ dpg.set_value("trace_markersize_slider", trace.get("markersize", 6.0))
1477
1600
 
1478
- dpg.set_value("selection_text", f"Selected: {trace.get('label', f'Trace {elem_idx}')}")
1601
+ dpg.set_value(
1602
+ "selection_text",
1603
+ f"Selected: {trace.get('label', f'Trace {elem_idx}')}",
1604
+ )
1479
1605
 
1480
- elif elem_type in ('title', 'xlabel', 'ylabel'):
1481
- dpg.set_value("element_selector_combo", elem_type.replace('x', 'X ').replace('y', 'Y ').title())
1606
+ elif elem_type in ("title", "xlabel", "ylabel"):
1607
+ dpg.set_value(
1608
+ "element_selector_combo",
1609
+ elem_type.replace("x", "X ").replace("y", "Y ").title(),
1610
+ )
1482
1611
  dpg.configure_item("text_controls_group", show=True)
1483
1612
 
1484
1613
  o = self.current_overrides
1485
- if elem_type == 'title':
1486
- dpg.set_value("element_text_input", o.get('title', ''))
1487
- dpg.set_value("element_fontsize_slider", o.get('title_fontsize', 8))
1488
- elif elem_type == 'xlabel':
1489
- dpg.set_value("element_text_input", o.get('xlabel', ''))
1490
- dpg.set_value("element_fontsize_slider", o.get('axis_fontsize', 7))
1491
- elif elem_type == 'ylabel':
1492
- dpg.set_value("element_text_input", o.get('ylabel', ''))
1493
- dpg.set_value("element_fontsize_slider", o.get('axis_fontsize', 7))
1614
+ if elem_type == "title":
1615
+ dpg.set_value("element_text_input", o.get("title", ""))
1616
+ dpg.set_value("element_fontsize_slider", o.get("title_fontsize", 8))
1617
+ elif elem_type == "xlabel":
1618
+ dpg.set_value("element_text_input", o.get("xlabel", ""))
1619
+ dpg.set_value("element_fontsize_slider", o.get("axis_fontsize", 7))
1620
+ elif elem_type == "ylabel":
1621
+ dpg.set_value("element_text_input", o.get("ylabel", ""))
1622
+ dpg.set_value("element_fontsize_slider", o.get("axis_fontsize", 7))
1494
1623
 
1495
1624
  dpg.set_value("selection_text", f"Selected: {elem_type.title()}")
1496
1625
 
1497
- elif elem_type in ('xaxis', 'yaxis'):
1498
- label = "X Axis" if elem_type == 'xaxis' else "Y Axis"
1626
+ elif elem_type in ("xaxis", "yaxis"):
1627
+ label = "X Axis" if elem_type == "xaxis" else "Y Axis"
1499
1628
  dpg.set_value("element_selector_combo", label)
1500
1629
  dpg.configure_item("axis_controls_group", show=True)
1501
1630
 
1502
1631
  o = self.current_overrides
1503
- dpg.set_value("axis_linewidth_slider", o.get('axis_width', 0.2))
1504
- dpg.set_value("axis_tick_length_slider", o.get('tick_length', 0.8))
1505
- dpg.set_value("axis_tick_fontsize_slider", o.get('tick_fontsize', 7))
1632
+ dpg.set_value("axis_linewidth_slider", o.get("axis_width", 0.2))
1633
+ dpg.set_value("axis_tick_length_slider", o.get("tick_length", 0.8))
1634
+ dpg.set_value("axis_tick_fontsize_slider", o.get("tick_fontsize", 7))
1506
1635
 
1507
- if elem_type == 'xaxis':
1508
- dpg.set_value("axis_show_spine_checkbox", not o.get('hide_bottom_spine', False))
1636
+ if elem_type == "xaxis":
1637
+ dpg.set_value(
1638
+ "axis_show_spine_checkbox", not o.get("hide_bottom_spine", False)
1639
+ )
1509
1640
  else:
1510
- dpg.set_value("axis_show_spine_checkbox", not o.get('hide_left_spine', False))
1641
+ dpg.set_value(
1642
+ "axis_show_spine_checkbox", not o.get("hide_left_spine", False)
1643
+ )
1511
1644
 
1512
1645
  dpg.set_value("selection_text", f"Selected: {label}")
1513
1646
 
1514
- elif elem_type == 'legend':
1647
+ elif elem_type == "legend":
1515
1648
  dpg.set_value("element_selector_combo", "Legend")
1516
1649
  dpg.configure_item("legend_controls_group", show=True)
1517
1650
 
1518
1651
  o = self.current_overrides
1519
- dpg.set_value("legend_visible_edit", o.get('legend_visible', True))
1520
- dpg.set_value("legend_frameon_edit", o.get('legend_frameon', False))
1521
- dpg.set_value("legend_loc_edit", o.get('legend_loc', 'best'))
1522
- dpg.set_value("legend_fontsize_edit", o.get('legend_fontsize', 6))
1652
+ dpg.set_value("legend_visible_edit", o.get("legend_visible", True))
1653
+ dpg.set_value("legend_frameon_edit", o.get("legend_frameon", False))
1654
+ dpg.set_value("legend_loc_edit", o.get("legend_loc", "best"))
1655
+ dpg.set_value("legend_fontsize_edit", o.get("legend_fontsize", 6))
1523
1656
 
1524
1657
  dpg.set_value("selection_text", "Selected: Legend")
1525
1658
 
@@ -1531,24 +1664,24 @@ class DearPyGuiEditor:
1531
1664
  import dearpygui.dearpygui as dpg
1532
1665
 
1533
1666
  if app_data == "Title":
1534
- self._select_element({'type': 'title', 'index': None}, dpg)
1667
+ self._select_element({"type": "title", "index": None}, dpg)
1535
1668
  elif app_data == "X Label":
1536
- self._select_element({'type': 'xlabel', 'index': None}, dpg)
1669
+ self._select_element({"type": "xlabel", "index": None}, dpg)
1537
1670
  elif app_data == "Y Label":
1538
- self._select_element({'type': 'ylabel', 'index': None}, dpg)
1671
+ self._select_element({"type": "ylabel", "index": None}, dpg)
1539
1672
  elif app_data == "X Axis":
1540
- self._select_element({'type': 'xaxis', 'index': None}, dpg)
1673
+ self._select_element({"type": "xaxis", "index": None}, dpg)
1541
1674
  elif app_data == "Y Axis":
1542
- self._select_element({'type': 'yaxis', 'index': None}, dpg)
1675
+ self._select_element({"type": "yaxis", "index": None}, dpg)
1543
1676
  elif app_data == "Legend":
1544
- self._select_element({'type': 'legend', 'index': None}, dpg)
1677
+ self._select_element({"type": "legend", "index": None}, dpg)
1545
1678
  elif app_data.startswith("Trace: "):
1546
1679
  # Find trace index
1547
1680
  trace_label = app_data[7:] # Remove "Trace: " prefix
1548
- traces = self.current_overrides.get('traces', [])
1681
+ traces = self.current_overrides.get("traces", [])
1549
1682
  for i, t in enumerate(traces):
1550
- if t.get('label', t.get('id', f'Trace {i}')) == trace_label:
1551
- self._select_element({'type': 'trace', 'index': i}, dpg)
1683
+ if t.get("label", t.get("id", f"Trace {i}")) == trace_label:
1684
+ self._select_element({"type": "trace", "index": i}, dpg)
1552
1685
  break
1553
1686
 
1554
1687
  def _on_text_element_change(self, sender, app_data, user_data=None):
@@ -1558,22 +1691,22 @@ class DearPyGuiEditor:
1558
1691
  if self._selected_element is None:
1559
1692
  return
1560
1693
 
1561
- elem_type = self._selected_element.get('type')
1562
- if elem_type not in ('title', 'xlabel', 'ylabel'):
1694
+ elem_type = self._selected_element.get("type")
1695
+ if elem_type not in ("title", "xlabel", "ylabel"):
1563
1696
  return
1564
1697
 
1565
1698
  text = dpg.get_value("element_text_input")
1566
1699
  fontsize = dpg.get_value("element_fontsize_slider")
1567
1700
 
1568
- if elem_type == 'title':
1569
- self.current_overrides['title'] = text
1570
- self.current_overrides['title_fontsize'] = fontsize
1571
- elif elem_type == 'xlabel':
1572
- self.current_overrides['xlabel'] = text
1573
- self.current_overrides['axis_fontsize'] = fontsize
1574
- elif elem_type == 'ylabel':
1575
- self.current_overrides['ylabel'] = text
1576
- self.current_overrides['axis_fontsize'] = fontsize
1701
+ if elem_type == "title":
1702
+ self.current_overrides["title"] = text
1703
+ self.current_overrides["title_fontsize"] = fontsize
1704
+ elif elem_type == "xlabel":
1705
+ self.current_overrides["xlabel"] = text
1706
+ self.current_overrides["axis_fontsize"] = fontsize
1707
+ elif elem_type == "ylabel":
1708
+ self.current_overrides["ylabel"] = text
1709
+ self.current_overrides["axis_fontsize"] = fontsize
1577
1710
 
1578
1711
  self._user_modified = True
1579
1712
  self._update_preview(dpg)
@@ -1585,19 +1718,21 @@ class DearPyGuiEditor:
1585
1718
  if self._selected_element is None:
1586
1719
  return
1587
1720
 
1588
- elem_type = self._selected_element.get('type')
1589
- if elem_type not in ('xaxis', 'yaxis'):
1721
+ elem_type = self._selected_element.get("type")
1722
+ if elem_type not in ("xaxis", "yaxis"):
1590
1723
  return
1591
1724
 
1592
- self.current_overrides['axis_width'] = dpg.get_value("axis_linewidth_slider")
1593
- self.current_overrides['tick_length'] = dpg.get_value("axis_tick_length_slider")
1594
- self.current_overrides['tick_fontsize'] = dpg.get_value("axis_tick_fontsize_slider")
1725
+ self.current_overrides["axis_width"] = dpg.get_value("axis_linewidth_slider")
1726
+ self.current_overrides["tick_length"] = dpg.get_value("axis_tick_length_slider")
1727
+ self.current_overrides["tick_fontsize"] = dpg.get_value(
1728
+ "axis_tick_fontsize_slider"
1729
+ )
1595
1730
 
1596
1731
  show_spine = dpg.get_value("axis_show_spine_checkbox")
1597
- if elem_type == 'xaxis':
1598
- self.current_overrides['hide_bottom_spine'] = not show_spine
1732
+ if elem_type == "xaxis":
1733
+ self.current_overrides["hide_bottom_spine"] = not show_spine
1599
1734
  else:
1600
- self.current_overrides['hide_left_spine'] = not show_spine
1735
+ self.current_overrides["hide_left_spine"] = not show_spine
1601
1736
 
1602
1737
  self._user_modified = True
1603
1738
  self._update_preview(dpg)
@@ -1609,14 +1744,16 @@ class DearPyGuiEditor:
1609
1744
  if self._selected_element is None:
1610
1745
  return
1611
1746
 
1612
- elem_type = self._selected_element.get('type')
1613
- if elem_type != 'legend':
1747
+ elem_type = self._selected_element.get("type")
1748
+ if elem_type != "legend":
1614
1749
  return
1615
1750
 
1616
- self.current_overrides['legend_visible'] = dpg.get_value("legend_visible_edit")
1617
- self.current_overrides['legend_frameon'] = dpg.get_value("legend_frameon_edit")
1618
- self.current_overrides['legend_loc'] = dpg.get_value("legend_loc_edit")
1619
- self.current_overrides['legend_fontsize'] = dpg.get_value("legend_fontsize_edit")
1751
+ self.current_overrides["legend_visible"] = dpg.get_value("legend_visible_edit")
1752
+ self.current_overrides["legend_frameon"] = dpg.get_value("legend_frameon_edit")
1753
+ self.current_overrides["legend_loc"] = dpg.get_value("legend_loc_edit")
1754
+ self.current_overrides["legend_fontsize"] = dpg.get_value(
1755
+ "legend_fontsize_edit"
1756
+ )
1620
1757
 
1621
1758
  self._user_modified = True
1622
1759
  self._update_preview(dpg)
@@ -1646,7 +1783,7 @@ class DearPyGuiEditor:
1646
1783
  if self.csv_data is None or not isinstance(self.csv_data, pd.DataFrame):
1647
1784
  return None
1648
1785
 
1649
- traces = self.current_overrides.get('traces', [])
1786
+ traces = self.current_overrides.get("traces", [])
1650
1787
  if not traces:
1651
1788
  return None
1652
1789
 
@@ -1685,13 +1822,13 @@ class DearPyGuiEditor:
1685
1822
 
1686
1823
  # Find nearest trace
1687
1824
  df = self.csv_data
1688
- min_dist = float('inf')
1825
+ min_dist = float("inf")
1689
1826
  nearest_idx = None
1690
1827
 
1691
1828
  for i, trace in enumerate(traces):
1692
- csv_cols = trace.get('csv_columns', {})
1693
- x_col = csv_cols.get('x')
1694
- y_col = csv_cols.get('y')
1829
+ csv_cols = trace.get("csv_columns", {})
1830
+ x_col = csv_cols.get("x")
1831
+ y_col = csv_cols.get("y")
1695
1832
 
1696
1833
  if x_col not in df.columns or y_col not in df.columns:
1697
1834
  continue
@@ -1713,7 +1850,9 @@ class DearPyGuiEditor:
1713
1850
  norm_trace_y = (trace_y - ylim[0]) / y_range if y_range > 0 else trace_y
1714
1851
 
1715
1852
  # Calculate distances to all points
1716
- distances = np.sqrt((norm_trace_x - norm_click_x)**2 + (norm_trace_y - norm_click_y)**2)
1853
+ distances = np.sqrt(
1854
+ (norm_trace_x - norm_click_x) ** 2 + (norm_trace_y - norm_click_y) ** 2
1855
+ )
1717
1856
  min_trace_dist = np.min(distances)
1718
1857
 
1719
1858
  if min_trace_dist < min_dist:
@@ -1733,28 +1872,28 @@ class DearPyGuiEditor:
1733
1872
  if self._selected_trace_index is None:
1734
1873
  return
1735
1874
 
1736
- traces = self.current_overrides.get('traces', [])
1875
+ traces = self.current_overrides.get("traces", [])
1737
1876
  if self._selected_trace_index >= len(traces):
1738
1877
  return
1739
1878
 
1740
1879
  trace = traces[self._selected_trace_index]
1741
1880
 
1742
1881
  # Update trace properties from widgets
1743
- trace['label'] = dpg.get_value("trace_label_input")
1882
+ trace["label"] = dpg.get_value("trace_label_input")
1744
1883
 
1745
1884
  # Convert RGB to hex
1746
1885
  color_rgb = dpg.get_value("trace_color_picker")
1747
1886
  if color_rgb and len(color_rgb) >= 3:
1748
1887
  r, g, b = int(color_rgb[0]), int(color_rgb[1]), int(color_rgb[2])
1749
- trace['color'] = f"#{r:02x}{g:02x}{b:02x}"
1888
+ trace["color"] = f"#{r:02x}{g:02x}{b:02x}"
1750
1889
 
1751
- trace['linewidth'] = dpg.get_value("trace_linewidth_slider")
1752
- trace['linestyle'] = dpg.get_value("trace_linestyle_combo")
1890
+ trace["linewidth"] = dpg.get_value("trace_linewidth_slider")
1891
+ trace["linestyle"] = dpg.get_value("trace_linestyle_combo")
1753
1892
 
1754
1893
  marker = dpg.get_value("trace_marker_combo")
1755
- trace['marker'] = marker if marker else None
1894
+ trace["marker"] = marker if marker else None
1756
1895
 
1757
- trace['markersize'] = dpg.get_value("trace_markersize_slider")
1896
+ trace["markersize"] = dpg.get_value("trace_markersize_slider")
1758
1897
 
1759
1898
  self._user_modified = True
1760
1899
  self._update_preview(dpg)
@@ -1779,12 +1918,14 @@ class DearPyGuiEditor:
1779
1918
  self._user_modified = False
1780
1919
 
1781
1920
  # Update all widgets
1782
- dpg.set_value("title_input", self.current_overrides.get('title', ''))
1783
- dpg.set_value("xlabel_input", self.current_overrides.get('xlabel', ''))
1784
- dpg.set_value("ylabel_input", self.current_overrides.get('ylabel', ''))
1785
- dpg.set_value("linewidth_slider", self.current_overrides.get('linewidth', 1.0))
1786
- dpg.set_value("grid_checkbox", self.current_overrides.get('grid', False))
1787
- dpg.set_value("transparent_checkbox", self.current_overrides.get('transparent', True))
1921
+ dpg.set_value("title_input", self.current_overrides.get("title", ""))
1922
+ dpg.set_value("xlabel_input", self.current_overrides.get("xlabel", ""))
1923
+ dpg.set_value("ylabel_input", self.current_overrides.get("ylabel", ""))
1924
+ dpg.set_value("linewidth_slider", self.current_overrides.get("linewidth", 1.0))
1925
+ dpg.set_value("grid_checkbox", self.current_overrides.get("grid", False))
1926
+ dpg.set_value(
1927
+ "transparent_checkbox", self.current_overrides.get("transparent", True)
1928
+ )
1788
1929
 
1789
1930
  self._update_preview(dpg)
1790
1931
  dpg.set_value("status_text", "Reset to original")
@@ -1793,33 +1934,38 @@ class DearPyGuiEditor:
1793
1934
  """Export current view to PNG."""
1794
1935
  import dearpygui.dearpygui as dpg
1795
1936
  import matplotlib
1796
- matplotlib.use('Agg')
1937
+
1938
+ matplotlib.use("Agg")
1797
1939
  import matplotlib.pyplot as plt
1798
1940
 
1799
1941
  try:
1800
1942
  self._collect_overrides(dpg)
1801
- output_path = self.json_path.with_suffix('.edited.png')
1943
+ output_path = self.json_path.with_suffix(".edited.png")
1802
1944
 
1803
1945
  # Full resolution render
1804
1946
  o = self.current_overrides
1805
- fig_size = o.get('fig_size', [3.15, 2.68])
1806
- dpi = o.get('dpi', 300)
1947
+ fig_size = o.get("fig_size", [3.15, 2.68])
1948
+ dpi = o.get("dpi", 300)
1807
1949
 
1808
1950
  fig, ax = plt.subplots(figsize=fig_size, dpi=dpi)
1809
1951
 
1810
1952
  if self.csv_data is not None:
1811
1953
  self._plot_from_csv(ax, o)
1812
1954
 
1813
- if o.get('title'):
1814
- ax.set_title(o['title'], fontsize=o.get('title_fontsize', 8))
1815
- if o.get('xlabel'):
1816
- ax.set_xlabel(o['xlabel'], fontsize=o.get('axis_fontsize', 7))
1817
- if o.get('ylabel'):
1818
- ax.set_ylabel(o['ylabel'], fontsize=o.get('axis_fontsize', 7))
1955
+ if o.get("title"):
1956
+ ax.set_title(o["title"], fontsize=o.get("title_fontsize", 8))
1957
+ if o.get("xlabel"):
1958
+ ax.set_xlabel(o["xlabel"], fontsize=o.get("axis_fontsize", 7))
1959
+ if o.get("ylabel"):
1960
+ ax.set_ylabel(o["ylabel"], fontsize=o.get("axis_fontsize", 7))
1819
1961
 
1820
1962
  fig.tight_layout()
1821
- fig.savefig(output_path, dpi=dpi, bbox_inches='tight',
1822
- transparent=o.get('transparent', True))
1963
+ fig.savefig(
1964
+ output_path,
1965
+ dpi=dpi,
1966
+ bbox_inches="tight",
1967
+ transparent=o.get("transparent", True),
1968
+ )
1823
1969
  plt.close(fig)
1824
1970
 
1825
1971
  dpg.set_value("status_text", f"Exported: {output_path.name}")