scitex 2.14.0__py3-none-any.whl → 2.15.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. scitex/__init__.py +47 -0
  2. scitex/_env_loader.py +156 -0
  3. scitex/_mcp_resources/__init__.py +37 -0
  4. scitex/_mcp_resources/_cheatsheet.py +135 -0
  5. scitex/_mcp_resources/_figrecipe.py +138 -0
  6. scitex/_mcp_resources/_formats.py +102 -0
  7. scitex/_mcp_resources/_modules.py +337 -0
  8. scitex/_mcp_resources/_session.py +149 -0
  9. scitex/_mcp_tools/__init__.py +4 -0
  10. scitex/_mcp_tools/audio.py +66 -0
  11. scitex/_mcp_tools/diagram.py +11 -95
  12. scitex/_mcp_tools/introspect.py +191 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +244 -0
  16. scitex/_mcp_tools/writer.py +21 -204
  17. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  18. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  19. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  20. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  21. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  22. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  23. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  24. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  25. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  26. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  27. scitex/audio/README.md +40 -36
  28. scitex/audio/__init__.py +127 -59
  29. scitex/audio/_branding.py +185 -0
  30. scitex/audio/_mcp/__init__.py +32 -0
  31. scitex/audio/_mcp/handlers.py +59 -6
  32. scitex/audio/_mcp/speak_handlers.py +238 -0
  33. scitex/audio/_relay.py +225 -0
  34. scitex/audio/engines/elevenlabs_engine.py +6 -1
  35. scitex/audio/mcp_server.py +228 -75
  36. scitex/canvas/README.md +1 -1
  37. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  38. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  39. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  40. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  41. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  42. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  43. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  44. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  45. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  46. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  47. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  48. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  49. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  50. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  51. scitex/cli/__init__.py +38 -43
  52. scitex/cli/audio.py +76 -27
  53. scitex/cli/capture.py +13 -20
  54. scitex/cli/introspect.py +443 -0
  55. scitex/cli/main.py +198 -109
  56. scitex/cli/mcp.py +60 -34
  57. scitex/cli/scholar/__init__.py +8 -0
  58. scitex/cli/scholar/_crossref_scitex.py +296 -0
  59. scitex/cli/scholar/_fetch.py +25 -3
  60. scitex/cli/social.py +314 -0
  61. scitex/cli/writer.py +117 -0
  62. scitex/config/README.md +1 -1
  63. scitex/config/__init__.py +16 -2
  64. scitex/config/_env_registry.py +191 -0
  65. scitex/diagram/__init__.py +42 -19
  66. scitex/diagram/mcp_server.py +13 -125
  67. scitex/introspect/__init__.py +75 -0
  68. scitex/introspect/_call_graph.py +303 -0
  69. scitex/introspect/_class_hierarchy.py +163 -0
  70. scitex/introspect/_core.py +42 -0
  71. scitex/introspect/_docstring.py +131 -0
  72. scitex/introspect/_examples.py +113 -0
  73. scitex/introspect/_imports.py +271 -0
  74. scitex/introspect/_mcp/__init__.py +37 -0
  75. scitex/introspect/_mcp/handlers.py +208 -0
  76. scitex/introspect/_members.py +151 -0
  77. scitex/introspect/_resolve.py +89 -0
  78. scitex/introspect/_signature.py +131 -0
  79. scitex/introspect/_source.py +80 -0
  80. scitex/introspect/_type_hints.py +172 -0
  81. scitex/io/bundle/README.md +1 -1
  82. scitex/mcp_server.py +98 -5
  83. scitex/plt/__init__.py +248 -550
  84. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  85. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  86. scitex/plt/gallery/README.md +1 -1
  87. scitex/plt/utils/_hitmap/__init__.py +82 -0
  88. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  89. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  90. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  91. scitex/plt/utils/_hitmap/_constants.py +40 -0
  92. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  93. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  94. scitex/plt/utils/_hitmap/_query.py +113 -0
  95. scitex/plt/utils/_hitmap.py +46 -1616
  96. scitex/plt/utils/_metadata/__init__.py +80 -0
  97. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  98. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  99. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  100. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  101. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  102. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  103. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  104. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  105. scitex/plt/utils/_metadata/_csv.py +416 -0
  106. scitex/plt/utils/_metadata/_detect.py +225 -0
  107. scitex/plt/utils/_metadata/_legend.py +127 -0
  108. scitex/plt/utils/_metadata/_rounding.py +117 -0
  109. scitex/plt/utils/_metadata/_verification.py +202 -0
  110. scitex/schema/README.md +1 -1
  111. scitex/scholar/__init__.py +8 -0
  112. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  113. scitex/scholar/core/Scholar.py +63 -1700
  114. scitex/scholar/core/_mixins/__init__.py +36 -0
  115. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  116. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  117. scitex/scholar/core/_mixins/_loaders.py +103 -0
  118. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  119. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  120. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  121. scitex/scholar/core/_mixins/_savers.py +69 -0
  122. scitex/scholar/core/_mixins/_search.py +103 -0
  123. scitex/scholar/core/_mixins/_services.py +88 -0
  124. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  125. scitex/scholar/crossref_scitex.py +367 -0
  126. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  127. scitex/scholar/examples/00_run_all.sh +120 -0
  128. scitex/scholar/jobs/_executors.py +27 -3
  129. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  130. scitex/scholar/pdf_download/_cli.py +154 -0
  131. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  132. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  133. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  134. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  135. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  136. scitex/scholar/pipelines/_single_steps.py +71 -36
  137. scitex/scholar/storage/_LibraryManager.py +97 -1695
  138. scitex/scholar/storage/_mixins/__init__.py +30 -0
  139. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  140. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  141. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  142. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  143. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  144. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  145. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  146. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  147. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  148. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  149. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  150. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  151. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  152. scitex/security/README.md +3 -3
  153. scitex/session/README.md +1 -1
  154. scitex/sh/README.md +1 -1
  155. scitex/social/__init__.py +153 -0
  156. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  157. scitex/template/README.md +1 -1
  158. scitex/template/clone_writer_directory.py +5 -5
  159. scitex/writer/README.md +1 -1
  160. scitex/writer/_mcp/handlers.py +11 -744
  161. scitex/writer/_mcp/tool_schemas.py +5 -335
  162. scitex-2.15.1.dist-info/METADATA +648 -0
  163. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/RECORD +166 -111
  164. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  165. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  166. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  167. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  168. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  169. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  170. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  171. scitex/diagram/_compile.py +0 -312
  172. scitex/diagram/_diagram.py +0 -355
  173. scitex/diagram/_mcp/__init__.py +0 -4
  174. scitex/diagram/_mcp/handlers.py +0 -400
  175. scitex/diagram/_mcp/tool_schemas.py +0 -157
  176. scitex/diagram/_presets.py +0 -173
  177. scitex/diagram/_schema.py +0 -182
  178. scitex/diagram/_split.py +0 -278
  179. scitex/plt/_mcp/__init__.py +0 -4
  180. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  181. scitex/plt/_mcp/_handlers_figure.py +0 -195
  182. scitex/plt/_mcp/_handlers_plot.py +0 -252
  183. scitex/plt/_mcp/_handlers_style.py +0 -219
  184. scitex/plt/_mcp/handlers.py +0 -74
  185. scitex/plt/_mcp/tool_schemas.py +0 -497
  186. scitex/plt/mcp_server.py +0 -231
  187. scitex/scholar/data/.gitkeep +0 -0
  188. scitex/scholar/data/README.md +0 -44
  189. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  190. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  191. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  192. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  193. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  194. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  195. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  196. scitex/scholar/data/bib_files/pac.bib +0 -698
  197. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  198. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  199. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  200. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  201. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  202. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  203. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  204. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  205. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  206. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  207. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  208. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  209. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  210. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  211. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  212. scitex/scholar/data/impact_factor.db +0 -0
  213. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  214. scitex/scholar/examples/dev.py +0 -38
  215. scitex-2.14.0.dist-info/METADATA +0 -1238
  216. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/WHEEL +0 -0
  217. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/entry_points.txt +0 -0
  218. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,390 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/_panels/_sections.py
4
+
5
+ """
6
+ Control panel section creators for DearPyGui editor.
7
+
8
+ Each function creates a collapsible section with related controls.
9
+ """
10
+
11
+ from typing import TYPE_CHECKING, Callable
12
+
13
+ if TYPE_CHECKING:
14
+ from .._state import EditorState
15
+
16
+
17
+ def create_labels_section(state: "EditorState", dpg, o: dict, on_value_change) -> None:
18
+ """Create labels section."""
19
+ with dpg.collapsing_header(label="Labels", default_open=True):
20
+ dpg.add_input_text(
21
+ label="Title",
22
+ default_value=o.get("title", ""),
23
+ tag="title_input",
24
+ callback=lambda s, a: on_value_change(state, dpg),
25
+ on_enter=True,
26
+ width=250,
27
+ )
28
+ dpg.add_input_text(
29
+ label="X Label",
30
+ default_value=o.get("xlabel", ""),
31
+ tag="xlabel_input",
32
+ callback=lambda s, a: on_value_change(state, dpg),
33
+ on_enter=True,
34
+ width=250,
35
+ )
36
+ dpg.add_input_text(
37
+ label="Y Label",
38
+ default_value=o.get("ylabel", ""),
39
+ tag="ylabel_input",
40
+ callback=lambda s, a: on_value_change(state, dpg),
41
+ on_enter=True,
42
+ width=250,
43
+ )
44
+
45
+
46
+ def create_axis_limits_section(
47
+ state: "EditorState", dpg, o: dict, on_apply_limits
48
+ ) -> None:
49
+ """Create axis limits section."""
50
+ with dpg.collapsing_header(label="Axis Limits", default_open=False):
51
+ with dpg.group(horizontal=True):
52
+ xlim = o.get("xlim", [0, 1])
53
+ dpg.add_input_float(
54
+ label="X Min",
55
+ default_value=xlim[0] if xlim else 0,
56
+ tag="xmin_input",
57
+ width=100,
58
+ )
59
+ dpg.add_input_float(
60
+ label="X Max",
61
+ default_value=xlim[1] if xlim else 1,
62
+ tag="xmax_input",
63
+ width=100,
64
+ )
65
+ with dpg.group(horizontal=True):
66
+ ylim = o.get("ylim", [0, 1])
67
+ dpg.add_input_float(
68
+ label="Y Min",
69
+ default_value=ylim[0] if ylim else 0,
70
+ tag="ymin_input",
71
+ width=100,
72
+ )
73
+ dpg.add_input_float(
74
+ label="Y Max",
75
+ default_value=ylim[1] if ylim else 1,
76
+ tag="ymax_input",
77
+ width=100,
78
+ )
79
+ dpg.add_button(
80
+ label="Apply Limits",
81
+ callback=lambda: on_apply_limits(state, dpg),
82
+ width=150,
83
+ )
84
+
85
+
86
+ def create_line_style_section(
87
+ state: "EditorState", dpg, o: dict, on_value_change
88
+ ) -> None:
89
+ """Create line style section."""
90
+ with dpg.collapsing_header(label="Line Style", default_open=True):
91
+ dpg.add_slider_float(
92
+ label="Line Width (pt)",
93
+ default_value=o.get("linewidth", 1.0),
94
+ min_value=0.1,
95
+ max_value=5.0,
96
+ tag="linewidth_slider",
97
+ callback=lambda s, a: on_value_change(state, dpg),
98
+ width=200,
99
+ )
100
+
101
+
102
+ def create_font_settings_section(
103
+ state: "EditorState", dpg, o: dict, on_value_change
104
+ ) -> None:
105
+ """Create font settings section."""
106
+ with dpg.collapsing_header(label="Font Settings", default_open=False):
107
+ dpg.add_slider_int(
108
+ label="Title Font Size",
109
+ default_value=o.get("title_fontsize", 8),
110
+ min_value=6,
111
+ max_value=20,
112
+ tag="title_fontsize_slider",
113
+ callback=lambda s, a: on_value_change(state, dpg),
114
+ width=200,
115
+ )
116
+ dpg.add_slider_int(
117
+ label="Axis Font Size",
118
+ default_value=o.get("axis_fontsize", 7),
119
+ min_value=6,
120
+ max_value=16,
121
+ tag="axis_fontsize_slider",
122
+ callback=lambda s, a: on_value_change(state, dpg),
123
+ width=200,
124
+ )
125
+ dpg.add_slider_int(
126
+ label="Tick Font Size",
127
+ default_value=o.get("tick_fontsize", 7),
128
+ min_value=6,
129
+ max_value=16,
130
+ tag="tick_fontsize_slider",
131
+ callback=lambda s, a: on_value_change(state, dpg),
132
+ width=200,
133
+ )
134
+ dpg.add_slider_int(
135
+ label="Legend Font Size",
136
+ default_value=o.get("legend_fontsize", 6),
137
+ min_value=4,
138
+ max_value=14,
139
+ tag="legend_fontsize_slider",
140
+ callback=lambda s, a: on_value_change(state, dpg),
141
+ width=200,
142
+ )
143
+
144
+
145
+ def create_tick_settings_section(
146
+ state: "EditorState", dpg, o: dict, on_value_change
147
+ ) -> None:
148
+ """Create tick settings section."""
149
+ with dpg.collapsing_header(label="Tick Settings", default_open=False):
150
+ dpg.add_slider_int(
151
+ label="N Ticks",
152
+ default_value=o.get("n_ticks", 4),
153
+ min_value=2,
154
+ max_value=10,
155
+ tag="n_ticks_slider",
156
+ callback=lambda s, a: on_value_change(state, dpg),
157
+ width=200,
158
+ )
159
+ dpg.add_slider_float(
160
+ label="Tick Length (mm)",
161
+ default_value=o.get("tick_length", 0.8),
162
+ min_value=0.2,
163
+ max_value=3.0,
164
+ tag="tick_length_slider",
165
+ callback=lambda s, a: on_value_change(state, dpg),
166
+ width=200,
167
+ )
168
+ dpg.add_slider_float(
169
+ label="Tick Width (mm)",
170
+ default_value=o.get("tick_width", 0.2),
171
+ min_value=0.05,
172
+ max_value=1.0,
173
+ tag="tick_width_slider",
174
+ callback=lambda s, a: on_value_change(state, dpg),
175
+ width=200,
176
+ )
177
+ dpg.add_combo(
178
+ label="Tick Direction",
179
+ items=["out", "in", "inout"],
180
+ default_value=o.get("tick_direction", "out"),
181
+ tag="tick_direction_combo",
182
+ callback=lambda s, a: on_value_change(state, dpg),
183
+ width=150,
184
+ )
185
+
186
+
187
+ def create_style_section(state: "EditorState", dpg, o: dict, on_value_change) -> None:
188
+ """Create style section."""
189
+ with dpg.collapsing_header(label="Style", default_open=True):
190
+ dpg.add_checkbox(
191
+ label="Show Grid",
192
+ default_value=o.get("grid", False),
193
+ tag="grid_checkbox",
194
+ callback=lambda s, a: on_value_change(state, dpg),
195
+ )
196
+ dpg.add_checkbox(
197
+ label="Hide Top Spine",
198
+ default_value=o.get("hide_top_spine", True),
199
+ tag="hide_top_spine_checkbox",
200
+ callback=lambda s, a: on_value_change(state, dpg),
201
+ )
202
+ dpg.add_checkbox(
203
+ label="Hide Right Spine",
204
+ default_value=o.get("hide_right_spine", True),
205
+ tag="hide_right_spine_checkbox",
206
+ callback=lambda s, a: on_value_change(state, dpg),
207
+ )
208
+ dpg.add_checkbox(
209
+ label="Transparent Background",
210
+ default_value=o.get("transparent", True),
211
+ tag="transparent_checkbox",
212
+ callback=lambda s, a: on_value_change(state, dpg),
213
+ )
214
+ dpg.add_slider_float(
215
+ label="Axis Width (mm)",
216
+ default_value=o.get("axis_width", 0.2),
217
+ min_value=0.05,
218
+ max_value=1.0,
219
+ tag="axis_width_slider",
220
+ callback=lambda s, a: on_value_change(state, dpg),
221
+ width=200,
222
+ )
223
+
224
+
225
+ def create_legend_section(state: "EditorState", dpg, o: dict, on_value_change) -> None:
226
+ """Create legend section."""
227
+ with dpg.collapsing_header(label="Legend", default_open=False):
228
+ dpg.add_checkbox(
229
+ label="Show Legend",
230
+ default_value=o.get("legend_visible", True),
231
+ tag="legend_visible_checkbox",
232
+ callback=lambda s, a: on_value_change(state, dpg),
233
+ )
234
+ dpg.add_checkbox(
235
+ label="Show Frame",
236
+ default_value=o.get("legend_frameon", False),
237
+ tag="legend_frameon_checkbox",
238
+ callback=lambda s, a: on_value_change(state, dpg),
239
+ )
240
+ dpg.add_combo(
241
+ label="Position",
242
+ items=[
243
+ "best",
244
+ "upper right",
245
+ "upper left",
246
+ "lower right",
247
+ "lower left",
248
+ "center right",
249
+ "center left",
250
+ "upper center",
251
+ "lower center",
252
+ "center",
253
+ ],
254
+ default_value=o.get("legend_loc", "best"),
255
+ tag="legend_loc_combo",
256
+ callback=lambda s, a: on_value_change(state, dpg),
257
+ width=150,
258
+ )
259
+
260
+
261
+ def create_dimensions_section(
262
+ state: "EditorState", dpg, o: dict, on_value_change
263
+ ) -> None:
264
+ """Create dimensions section."""
265
+ with dpg.collapsing_header(label="Dimensions", default_open=False):
266
+ fig_size = o.get("fig_size", [3.15, 2.68])
267
+ with dpg.group(horizontal=True):
268
+ dpg.add_input_float(
269
+ label="Width (in)",
270
+ default_value=fig_size[0],
271
+ tag="fig_width_input",
272
+ width=100,
273
+ )
274
+ dpg.add_input_float(
275
+ label="Height (in)",
276
+ default_value=fig_size[1],
277
+ tag="fig_height_input",
278
+ width=100,
279
+ )
280
+ dpg.add_slider_int(
281
+ label="DPI",
282
+ default_value=o.get("dpi", 300),
283
+ min_value=72,
284
+ max_value=600,
285
+ tag="dpi_slider",
286
+ callback=lambda s, a: on_value_change(state, dpg),
287
+ width=200,
288
+ )
289
+
290
+
291
+ def create_annotations_section(
292
+ state: "EditorState", dpg, on_add_annotation, on_remove_annotation
293
+ ) -> None:
294
+ """Create annotations section."""
295
+ with dpg.collapsing_header(label="Annotations", default_open=False):
296
+ dpg.add_input_text(
297
+ label="Text",
298
+ tag="annot_text_input",
299
+ width=200,
300
+ )
301
+ with dpg.group(horizontal=True):
302
+ dpg.add_input_float(
303
+ label="X",
304
+ default_value=0.5,
305
+ tag="annot_x_input",
306
+ width=80,
307
+ )
308
+ dpg.add_input_float(
309
+ label="Y",
310
+ default_value=0.5,
311
+ tag="annot_y_input",
312
+ width=80,
313
+ )
314
+ dpg.add_button(
315
+ label="Add Annotation",
316
+ callback=lambda: on_add_annotation(state, dpg),
317
+ width=150,
318
+ )
319
+ dpg.add_listbox(
320
+ label="",
321
+ items=[],
322
+ tag="annotations_listbox",
323
+ num_items=4,
324
+ width=250,
325
+ )
326
+ dpg.add_button(
327
+ label="Remove Selected",
328
+ callback=lambda: on_remove_annotation(state, dpg),
329
+ width=150,
330
+ )
331
+
332
+
333
+ def create_selected_element_section(
334
+ state: "EditorState",
335
+ dpg,
336
+ get_all_element_labels: Callable,
337
+ on_element_selected: Callable,
338
+ on_trace_property_change: Callable,
339
+ on_text_element_change: Callable,
340
+ on_axis_element_change: Callable,
341
+ on_legend_element_change: Callable,
342
+ deselect_element: Callable,
343
+ ) -> None:
344
+ """Create selected element section."""
345
+ from ._element_controls import (
346
+ create_axis_controls,
347
+ create_legend_controls,
348
+ create_text_controls,
349
+ create_trace_controls,
350
+ )
351
+
352
+ with dpg.collapsing_header(
353
+ label="Selected Element",
354
+ default_open=True,
355
+ tag="selected_element_header",
356
+ ):
357
+ dpg.add_text(
358
+ "Click on preview to select elements",
359
+ tag="element_hint_text",
360
+ color=(150, 150, 150),
361
+ )
362
+ dpg.add_combo(
363
+ label="Element",
364
+ items=get_all_element_labels(state),
365
+ tag="element_selector_combo",
366
+ callback=lambda s, a: on_element_selected(state, dpg, a),
367
+ width=200,
368
+ )
369
+ dpg.add_separator()
370
+
371
+ # Trace-specific controls (shown when trace selected)
372
+ create_trace_controls(state, dpg, on_trace_property_change)
373
+
374
+ # Text element controls (title, xlabel, ylabel)
375
+ create_text_controls(state, dpg, on_text_element_change)
376
+
377
+ # Axis element controls (xaxis, yaxis)
378
+ create_axis_controls(state, dpg, on_axis_element_change)
379
+
380
+ # Legend controls
381
+ create_legend_controls(state, dpg, on_legend_element_change)
382
+
383
+ dpg.add_button(
384
+ label="Deselect",
385
+ callback=lambda: deselect_element(state, dpg),
386
+ width=100,
387
+ )
388
+
389
+
390
+ # EOF
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/_plotting.py
4
+
5
+ """
6
+ CSV data plotting for DearPyGui editor.
7
+
8
+ Handles reconstructing plots from CSV data with trace info.
9
+ """
10
+
11
+ from typing import Any, Dict, Optional
12
+
13
+ import pandas as pd
14
+
15
+
16
+ def plot_from_csv(
17
+ ax,
18
+ overrides: Dict[str, Any],
19
+ csv_data: pd.DataFrame,
20
+ highlight_trace: Optional[int] = None,
21
+ hover_trace: Optional[int] = None,
22
+ ) -> None:
23
+ """Reconstruct plot from CSV data using trace info.
24
+
25
+ Parameters
26
+ ----------
27
+ ax : matplotlib.axes.Axes
28
+ The axes to plot on
29
+ overrides : dict
30
+ Current overrides containing trace info
31
+ csv_data : pd.DataFrame
32
+ CSV data to plot
33
+ highlight_trace : int, optional
34
+ Index of trace to highlight with selection effect (yellow glow)
35
+ hover_trace : int, optional
36
+ Index of trace to highlight with hover effect (cyan glow)
37
+ """
38
+ from .._defaults import _normalize_legend_loc
39
+
40
+ if not isinstance(csv_data, pd.DataFrame):
41
+ return
42
+
43
+ df = csv_data
44
+ linewidth = overrides.get("linewidth", 1.0)
45
+ legend_visible = overrides.get("legend_visible", True)
46
+ legend_fontsize = overrides.get("legend_fontsize", 6)
47
+ legend_frameon = overrides.get("legend_frameon", False)
48
+ legend_loc = _normalize_legend_loc(overrides.get("legend_loc", "best"))
49
+
50
+ traces = overrides.get("traces", [])
51
+
52
+ if traces:
53
+ _plot_with_traces(
54
+ ax,
55
+ df,
56
+ traces,
57
+ linewidth,
58
+ highlight_trace,
59
+ hover_trace,
60
+ legend_visible,
61
+ legend_fontsize,
62
+ legend_frameon,
63
+ legend_loc,
64
+ )
65
+ else:
66
+ _plot_fallback(
67
+ ax,
68
+ df,
69
+ linewidth,
70
+ legend_visible,
71
+ legend_fontsize,
72
+ legend_frameon,
73
+ legend_loc,
74
+ )
75
+
76
+
77
+ def _plot_with_traces(
78
+ ax,
79
+ df: pd.DataFrame,
80
+ traces: list,
81
+ linewidth: float,
82
+ highlight_trace: Optional[int],
83
+ hover_trace: Optional[int],
84
+ legend_visible: bool,
85
+ legend_fontsize: int,
86
+ legend_frameon: bool,
87
+ legend_loc: str,
88
+ ) -> None:
89
+ """Plot using trace definitions."""
90
+ for i, trace in enumerate(traces):
91
+ csv_cols = trace.get("csv_columns", {})
92
+ x_col = csv_cols.get("x")
93
+ y_col = csv_cols.get("y")
94
+
95
+ if x_col in df.columns and y_col in df.columns:
96
+ trace_linewidth = trace.get("linewidth", linewidth)
97
+ is_selected = highlight_trace is not None and i == highlight_trace
98
+ is_hovered = (
99
+ hover_trace is not None and i == hover_trace and not is_selected
100
+ )
101
+
102
+ # Draw selection glow (yellow, stronger)
103
+ if is_selected:
104
+ ax.plot(
105
+ df[x_col],
106
+ df[y_col],
107
+ color="yellow",
108
+ linewidth=trace_linewidth * 4,
109
+ alpha=0.5,
110
+ zorder=0,
111
+ )
112
+ # Draw hover glow (cyan, subtler)
113
+ elif is_hovered:
114
+ ax.plot(
115
+ df[x_col],
116
+ df[y_col],
117
+ color="cyan",
118
+ linewidth=trace_linewidth * 3,
119
+ alpha=0.3,
120
+ zorder=0,
121
+ )
122
+
123
+ ax.plot(
124
+ df[x_col],
125
+ df[y_col],
126
+ label=trace.get("label", trace.get("id", "")),
127
+ color=trace.get("color"),
128
+ linestyle=trace.get("linestyle", "-"),
129
+ linewidth=trace_linewidth
130
+ * (1.5 if is_selected else (1.2 if is_hovered else 1.0)),
131
+ marker=trace.get("marker", None),
132
+ markersize=trace.get("markersize", 6),
133
+ zorder=10 if is_selected else (5 if is_hovered else 1),
134
+ )
135
+
136
+ if legend_visible and any(t.get("label") for t in traces):
137
+ ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
138
+
139
+
140
+ def _plot_fallback(
141
+ ax,
142
+ df: pd.DataFrame,
143
+ linewidth: float,
144
+ legend_visible: bool,
145
+ legend_fontsize: int,
146
+ legend_frameon: bool,
147
+ legend_loc: str,
148
+ ) -> None:
149
+ """Fallback plotting when no traces defined - parse column names."""
150
+ cols = df.columns.tolist()
151
+ trace_groups = {}
152
+
153
+ for col in cols:
154
+ if col.endswith("_x"):
155
+ trace_id = col[:-2]
156
+ y_col = trace_id + "_y"
157
+ if y_col in cols:
158
+ parts = trace_id.split("_")
159
+ label = parts[2] if len(parts) > 2 else trace_id
160
+ trace_groups[trace_id] = {
161
+ "x_col": col,
162
+ "y_col": y_col,
163
+ "label": label,
164
+ }
165
+
166
+ if trace_groups:
167
+ for trace_id, info in trace_groups.items():
168
+ ax.plot(
169
+ df[info["x_col"]],
170
+ df[info["y_col"]],
171
+ label=info["label"],
172
+ linewidth=linewidth,
173
+ )
174
+ if legend_visible:
175
+ ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
176
+ elif len(cols) >= 2:
177
+ x_col = cols[0]
178
+ for y_col in cols[1:]:
179
+ try:
180
+ ax.plot(df[x_col], df[y_col], label=str(y_col), linewidth=linewidth)
181
+ except Exception:
182
+ pass
183
+ if len(cols) > 2 and legend_visible:
184
+ ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
185
+
186
+
187
+ # EOF