scitex 2.14.0__py3-none-any.whl → 2.15.3__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 (264) hide show
  1. scitex/__init__.py +71 -17
  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 +210 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +27 -0
  16. scitex/_mcp_tools/template.py +24 -0
  17. scitex/_mcp_tools/writer.py +17 -210
  18. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  19. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  20. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  21. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  22. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  23. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  24. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  25. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  26. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  27. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  28. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
  29. scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
  30. scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
  31. scitex/audio/README.md +40 -36
  32. scitex/audio/__init__.py +129 -61
  33. scitex/audio/_branding.py +185 -0
  34. scitex/audio/_mcp/__init__.py +32 -0
  35. scitex/audio/_mcp/handlers.py +59 -6
  36. scitex/audio/_mcp/speak_handlers.py +238 -0
  37. scitex/audio/_relay.py +225 -0
  38. scitex/audio/_tts.py +18 -10
  39. scitex/audio/engines/base.py +17 -10
  40. scitex/audio/engines/elevenlabs_engine.py +7 -2
  41. scitex/audio/mcp_server.py +228 -75
  42. scitex/canvas/README.md +1 -1
  43. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  44. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  45. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  46. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  47. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  48. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  49. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  50. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  51. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  52. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  53. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  54. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  55. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  56. scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
  57. scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
  58. scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
  59. scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
  60. scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
  61. scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
  62. scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
  63. scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
  64. scitex/canvas/editor/flask_editor/_core.py +25 -1684
  65. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  66. scitex/cli/__init__.py +38 -43
  67. scitex/cli/audio.py +160 -41
  68. scitex/cli/capture.py +133 -20
  69. scitex/cli/introspect.py +488 -0
  70. scitex/cli/main.py +200 -109
  71. scitex/cli/mcp.py +60 -34
  72. scitex/cli/plt.py +414 -0
  73. scitex/cli/repro.py +15 -8
  74. scitex/cli/resource.py +15 -8
  75. scitex/cli/scholar/__init__.py +154 -8
  76. scitex/cli/scholar/_crossref_scitex.py +296 -0
  77. scitex/cli/scholar/_fetch.py +25 -3
  78. scitex/cli/social.py +355 -0
  79. scitex/cli/stats.py +136 -11
  80. scitex/cli/template.py +129 -12
  81. scitex/cli/tex.py +15 -8
  82. scitex/cli/writer.py +49 -299
  83. scitex/cloud/__init__.py +41 -2
  84. scitex/config/README.md +1 -1
  85. scitex/config/__init__.py +16 -2
  86. scitex/config/_env_registry.py +256 -0
  87. scitex/context/__init__.py +22 -0
  88. scitex/dev/__init__.py +20 -1
  89. scitex/diagram/__init__.py +42 -19
  90. scitex/diagram/mcp_server.py +13 -125
  91. scitex/gen/__init__.py +50 -14
  92. scitex/gen/_list_packages.py +4 -4
  93. scitex/introspect/__init__.py +82 -0
  94. scitex/introspect/_call_graph.py +303 -0
  95. scitex/introspect/_class_hierarchy.py +163 -0
  96. scitex/introspect/_core.py +41 -0
  97. scitex/introspect/_docstring.py +131 -0
  98. scitex/introspect/_examples.py +113 -0
  99. scitex/introspect/_imports.py +271 -0
  100. scitex/{gen/_inspect_module.py → introspect/_list_api.py} +48 -56
  101. scitex/introspect/_mcp/__init__.py +41 -0
  102. scitex/introspect/_mcp/handlers.py +233 -0
  103. scitex/introspect/_members.py +155 -0
  104. scitex/introspect/_resolve.py +89 -0
  105. scitex/introspect/_signature.py +131 -0
  106. scitex/introspect/_source.py +80 -0
  107. scitex/introspect/_type_hints.py +172 -0
  108. scitex/io/_save.py +1 -2
  109. scitex/io/bundle/README.md +1 -1
  110. scitex/logging/_formatters.py +19 -9
  111. scitex/mcp_server.py +98 -5
  112. scitex/os/__init__.py +4 -0
  113. scitex/{gen → os}/_check_host.py +4 -5
  114. scitex/plt/__init__.py +245 -550
  115. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  116. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  117. scitex/plt/gallery/README.md +1 -1
  118. scitex/plt/utils/_hitmap/__init__.py +82 -0
  119. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  120. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  121. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  122. scitex/plt/utils/_hitmap/_constants.py +40 -0
  123. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  124. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  125. scitex/plt/utils/_hitmap/_query.py +113 -0
  126. scitex/plt/utils/_hitmap.py +46 -1616
  127. scitex/plt/utils/_metadata/__init__.py +80 -0
  128. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  129. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  130. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  131. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  132. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  133. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  134. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  135. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  136. scitex/plt/utils/_metadata/_csv.py +416 -0
  137. scitex/plt/utils/_metadata/_detect.py +225 -0
  138. scitex/plt/utils/_metadata/_legend.py +127 -0
  139. scitex/plt/utils/_metadata/_rounding.py +117 -0
  140. scitex/plt/utils/_metadata/_verification.py +202 -0
  141. scitex/schema/README.md +1 -1
  142. scitex/scholar/__init__.py +8 -0
  143. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  144. scitex/scholar/core/Scholar.py +63 -1700
  145. scitex/scholar/core/_mixins/__init__.py +36 -0
  146. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  147. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  148. scitex/scholar/core/_mixins/_loaders.py +103 -0
  149. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  150. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  151. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  152. scitex/scholar/core/_mixins/_savers.py +69 -0
  153. scitex/scholar/core/_mixins/_search.py +103 -0
  154. scitex/scholar/core/_mixins/_services.py +88 -0
  155. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  156. scitex/scholar/crossref_scitex.py +367 -0
  157. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  158. scitex/scholar/examples/00_run_all.sh +120 -0
  159. scitex/scholar/jobs/_executors.py +27 -3
  160. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  161. scitex/scholar/pdf_download/_cli.py +154 -0
  162. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  163. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  164. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  165. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  166. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  167. scitex/scholar/pipelines/_single_steps.py +71 -36
  168. scitex/scholar/storage/_LibraryManager.py +97 -1695
  169. scitex/scholar/storage/_mixins/__init__.py +30 -0
  170. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  171. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  172. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  173. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  174. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  175. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  176. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  177. scitex/security/README.md +3 -3
  178. scitex/session/README.md +1 -1
  179. scitex/session/__init__.py +26 -7
  180. scitex/session/_decorator.py +1 -1
  181. scitex/sh/README.md +1 -1
  182. scitex/sh/__init__.py +7 -4
  183. scitex/social/__init__.py +155 -0
  184. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  185. scitex/stats/_mcp/_handlers/__init__.py +31 -0
  186. scitex/stats/_mcp/_handlers/_corrections.py +113 -0
  187. scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
  188. scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
  189. scitex/stats/_mcp/_handlers/_format.py +94 -0
  190. scitex/stats/_mcp/_handlers/_normality.py +110 -0
  191. scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
  192. scitex/stats/_mcp/_handlers/_power.py +247 -0
  193. scitex/stats/_mcp/_handlers/_recommend.py +102 -0
  194. scitex/stats/_mcp/_handlers/_run_test.py +279 -0
  195. scitex/stats/_mcp/_handlers/_stars.py +48 -0
  196. scitex/stats/_mcp/handlers.py +19 -1171
  197. scitex/stats/auto/_stat_style.py +175 -0
  198. scitex/stats/auto/_style_definitions.py +411 -0
  199. scitex/stats/auto/_styles.py +22 -620
  200. scitex/stats/descriptive/__init__.py +11 -8
  201. scitex/stats/descriptive/_ci.py +39 -0
  202. scitex/stats/power/_power.py +15 -4
  203. scitex/str/__init__.py +2 -1
  204. scitex/str/_title_case.py +63 -0
  205. scitex/template/README.md +1 -1
  206. scitex/template/__init__.py +25 -10
  207. scitex/template/_code_templates.py +147 -0
  208. scitex/template/_mcp/handlers.py +81 -0
  209. scitex/template/_mcp/tool_schemas.py +55 -0
  210. scitex/template/_templates/__init__.py +51 -0
  211. scitex/template/_templates/audio.py +233 -0
  212. scitex/template/_templates/canvas.py +312 -0
  213. scitex/template/_templates/capture.py +268 -0
  214. scitex/template/_templates/config.py +43 -0
  215. scitex/template/_templates/diagram.py +294 -0
  216. scitex/template/_templates/io.py +107 -0
  217. scitex/template/_templates/module.py +53 -0
  218. scitex/template/_templates/plt.py +202 -0
  219. scitex/template/_templates/scholar.py +267 -0
  220. scitex/template/_templates/session.py +130 -0
  221. scitex/template/_templates/session_minimal.py +43 -0
  222. scitex/template/_templates/session_plot.py +67 -0
  223. scitex/template/_templates/session_stats.py +77 -0
  224. scitex/template/_templates/stats.py +323 -0
  225. scitex/template/_templates/writer.py +296 -0
  226. scitex/template/clone_writer_directory.py +5 -5
  227. scitex/ui/_backends/_email.py +10 -2
  228. scitex/ui/_backends/_webhook.py +5 -1
  229. scitex/web/_search_pubmed.py +10 -6
  230. scitex/writer/README.md +1 -1
  231. scitex/writer/__init__.py +43 -34
  232. scitex/writer/_mcp/handlers.py +11 -744
  233. scitex/writer/_mcp/tool_schemas.py +5 -335
  234. scitex-2.15.3.dist-info/METADATA +667 -0
  235. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/RECORD +241 -120
  236. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  237. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  238. scitex/diagram/_compile.py +0 -312
  239. scitex/diagram/_diagram.py +0 -355
  240. scitex/diagram/_mcp/__init__.py +0 -4
  241. scitex/diagram/_mcp/handlers.py +0 -400
  242. scitex/diagram/_mcp/tool_schemas.py +0 -157
  243. scitex/diagram/_presets.py +0 -173
  244. scitex/diagram/_schema.py +0 -182
  245. scitex/diagram/_split.py +0 -278
  246. scitex/gen/_ci.py +0 -12
  247. scitex/gen/_title_case.py +0 -89
  248. scitex/plt/_mcp/__init__.py +0 -4
  249. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  250. scitex/plt/_mcp/_handlers_figure.py +0 -195
  251. scitex/plt/_mcp/_handlers_plot.py +0 -252
  252. scitex/plt/_mcp/_handlers_style.py +0 -219
  253. scitex/plt/_mcp/handlers.py +0 -74
  254. scitex/plt/_mcp/tool_schemas.py +0 -497
  255. scitex/plt/mcp_server.py +0 -231
  256. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  257. scitex/scholar/examples/dev.py +0 -38
  258. scitex-2.14.0.dist-info/METADATA +0 -1238
  259. /scitex/{gen → context}/_detect_environment.py +0 -0
  260. /scitex/{gen → context}/_get_notebook_path.py +0 -0
  261. /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
  262. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/WHEEL +0 -0
  263. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/entry_points.txt +0 -0
  264. {scitex-2.14.0.dist-info → scitex-2.15.3.dist-info}/licenses/LICENSE +0 -0
scitex/gen/_title_case.py DELETED
@@ -1,89 +0,0 @@
1
- #!./env/bin/python3
2
- # -*- coding: utf-8 -*-
3
- # Time-stamp: "2024-04-24 15:05:34"
4
- # Author: Yusuke Watanabe (ywatanabe@scitex.ai)
5
-
6
-
7
- """
8
- This script does XYZ.
9
- """
10
-
11
- """
12
- Imports
13
- """
14
- import sys
15
-
16
- import matplotlib.pyplot as plt
17
-
18
-
19
- """
20
- Config
21
- """
22
- # CONFIG = scitex.gen.load_configs()
23
-
24
-
25
- """
26
- Functions & Classes
27
- """
28
-
29
-
30
- def title_case(text):
31
- """
32
- Converts a string to title case while keeping certain prepositions, conjunctions, and articles in lowercase,
33
- and ensuring words detected as potential acronyms (all uppercase) are fully capitalized.
34
-
35
- Parameters:
36
- - text (str): The text to convert to title case.
37
-
38
- Returns:
39
- - str: The converted text in title case with certain words in lowercase and potential acronyms fully capitalized.
40
-
41
- Examples:
42
- --------
43
- print(title_case("welcome to the world of ai and using CPUs for gaming")) # Welcome to the World of AI and Using CPUs for Gaming
44
- """
45
- # List of words to keep in lowercase
46
- lowercase_words = [
47
- "a",
48
- "an",
49
- "the",
50
- "and",
51
- "but",
52
- "or",
53
- "nor",
54
- "at",
55
- "by",
56
- "to",
57
- "in",
58
- "with",
59
- "of",
60
- "on",
61
- ]
62
-
63
- words = text.split()
64
- final_words = []
65
- for word in words:
66
- # Check if the word is fully in uppercase and more than one character, suggesting an acronym
67
- if word.isupper() and len(word) > 1:
68
- final_words.append(word)
69
- elif word.lower() in lowercase_words:
70
- final_words.append(word.lower())
71
- else:
72
- final_words.append(word.capitalize())
73
- return " ".join(final_words)
74
-
75
-
76
- def main():
77
- # Example usage:
78
- text = "welcome to the world of ai and using CPUs for gaming"
79
- print(title_case(text))
80
-
81
-
82
- if __name__ == "__main__":
83
- CONFIG, sys.stdout, sys.stderr, plt, CC = scitex.session.start(
84
- sys, plt, verbose=False
85
- )
86
- main()
87
- scitex.session.close(CONFIG, verbose=False, notify=False)
88
-
89
- # EOF
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env python3
2
- # File: __init__.py
3
- """MCP server components."""
4
-
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Timestamp: 2026-01-13
3
- # File: src/scitex/plt/_mcp/_handlers_annotation.py
4
-
5
- """Annotation MCP handlers for SciTeX plt module."""
6
-
7
- from __future__ import annotations
8
-
9
- from typing import Optional
10
-
11
- from ._handlers_figure import _get_axes
12
-
13
-
14
- async def add_significance_handler(
15
- x1: float,
16
- x2: float,
17
- y: float,
18
- text: str,
19
- figure_id: Optional[str] = None,
20
- panel: str = "0,0",
21
- height: Optional[float] = None,
22
- ) -> dict:
23
- """Add significance bracket between two groups."""
24
- try:
25
- from scitex.plt.styles.presets import SCITEX_STYLE
26
- from scitex.plt.utils import mm_to_pt
27
-
28
- fig, ax, fid = _get_axes(figure_id, panel)
29
-
30
- ylim = ax.get_ylim()
31
- ylim_range = float(ylim[1]) - float(ylim[0])
32
- h = height if height else 0.1 * ylim_range
33
-
34
- # Use SCITEX line width (0.2mm converted to points)
35
- line_width_mm = SCITEX_STYLE.get("trace_thickness_mm", 0.2)
36
- line_width_pt = mm_to_pt(line_width_mm)
37
-
38
- # Draw bracket with SCITEX styling
39
- ax.plot(
40
- [x1, x1, x2, x2],
41
- [y, y + h, y + h, y],
42
- color="black",
43
- linewidth=line_width_pt,
44
- )
45
-
46
- ax.text(
47
- (x1 + x2) / 2,
48
- y + h + 0.02 * ylim_range,
49
- text,
50
- ha="center",
51
- va="bottom",
52
- fontsize=6,
53
- )
54
-
55
- return {
56
- "success": True,
57
- "figure_id": fid,
58
- "panel": panel,
59
- "bracket": {"x1": x1, "x2": x2, "y": y, "text": text},
60
- }
61
- except Exception as e:
62
- return {"success": False, "error": str(e)}
63
-
64
-
65
- async def add_panel_label_handler(
66
- label: str,
67
- figure_id: Optional[str] = None,
68
- panel: str = "0,0",
69
- x: float = -0.15,
70
- y: float = 1.1,
71
- fontsize: float = 10,
72
- fontweight: str = "bold",
73
- ) -> dict:
74
- """Add panel label (A, B, C, etc.) to a panel."""
75
- try:
76
- fig, ax, fid = _get_axes(figure_id, panel)
77
-
78
- ax.text(
79
- x,
80
- y,
81
- label,
82
- transform=ax.transAxes,
83
- fontsize=fontsize,
84
- fontweight=fontweight,
85
- )
86
-
87
- return {
88
- "success": True,
89
- "figure_id": fid,
90
- "panel": panel,
91
- "label": label,
92
- }
93
- except Exception as e:
94
- return {"success": False, "error": str(e)}
95
-
96
-
97
- __all__ = [
98
- "add_significance_handler",
99
- "add_panel_label_handler",
100
- ]
101
-
102
- # EOF
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Timestamp: 2026-01-13
3
- # File: src/scitex/plt/_mcp/_handlers_figure.py
4
-
5
- """Figure management MCP handlers for SciTeX plt module."""
6
-
7
- from __future__ import annotations
8
-
9
- import asyncio
10
- from typing import Any, Optional
11
-
12
- # Figure registry for tracking active figures across MCP calls
13
- _FIGURE_REGISTRY: dict[str, dict[str, Any]] = {}
14
-
15
-
16
- def _get_axes(figure_id: Optional[str], panel: str):
17
- """Helper to get axes from figure registry."""
18
- if figure_id is None:
19
- if not _FIGURE_REGISTRY:
20
- raise ValueError("No active figures. Call create_figure first.")
21
- figure_id = list(_FIGURE_REGISTRY.keys())[-1]
22
-
23
- if figure_id not in _FIGURE_REGISTRY:
24
- raise ValueError(f"Figure '{figure_id}' not found")
25
-
26
- fig_data = _FIGURE_REGISTRY[figure_id]
27
- axes = fig_data["axes"]
28
-
29
- if "," in panel:
30
- row, col = map(int, panel.split(","))
31
- try:
32
- ax = axes[row, col]
33
- except (TypeError, IndexError):
34
- ax = axes
35
- else:
36
- idx = ord(panel.upper()) - ord("A")
37
- if hasattr(axes, "flat"):
38
- ax = list(axes.flat)[idx]
39
- elif hasattr(axes, "__getitem__"):
40
- ax = axes[idx]
41
- else:
42
- ax = axes
43
-
44
- return fig_data["fig"], ax, figure_id
45
-
46
-
47
- async def create_figure_handler(
48
- nrows: int = 1,
49
- ncols: int = 1,
50
- axes_width_mm: Optional[float] = None,
51
- axes_height_mm: Optional[float] = None,
52
- space_w_mm: Optional[float] = None,
53
- space_h_mm: Optional[float] = None,
54
- ) -> dict:
55
- """Create a multi-panel figure canvas with SciTeX style."""
56
- import uuid
57
-
58
- try:
59
- import scitex.plt as splt
60
-
61
- kwargs = {"nrows": nrows, "ncols": ncols}
62
- if axes_width_mm is not None:
63
- kwargs["axes_width_mm"] = axes_width_mm
64
- if axes_height_mm is not None:
65
- kwargs["axes_height_mm"] = axes_height_mm
66
- if space_w_mm is not None:
67
- kwargs["space_w_mm"] = space_w_mm
68
- if space_h_mm is not None:
69
- kwargs["space_h_mm"] = space_h_mm
70
-
71
- fig, axes = splt.subplots(**kwargs)
72
-
73
- figure_id = str(uuid.uuid4())[:8]
74
-
75
- _FIGURE_REGISTRY[figure_id] = {
76
- "fig": fig,
77
- "axes": axes,
78
- "nrows": nrows,
79
- "ncols": ncols,
80
- }
81
-
82
- return {
83
- "success": True,
84
- "figure_id": figure_id,
85
- "nrows": nrows,
86
- "ncols": ncols,
87
- "message": f"Created {nrows}x{ncols} figure with SciTeX style",
88
- }
89
- except Exception as e:
90
- return {"success": False, "error": str(e)}
91
-
92
-
93
- async def crop_figure_handler(
94
- input_path: str,
95
- output_path: Optional[str] = None,
96
- margin: int = 12,
97
- overwrite: bool = False,
98
- ) -> dict:
99
- """Auto-crop whitespace from a saved figure image."""
100
- try:
101
- from scitex.plt import crop
102
-
103
- loop = asyncio.get_event_loop()
104
- result_path = await loop.run_in_executor(
105
- None,
106
- lambda: crop(
107
- input_path=input_path,
108
- output_path=output_path,
109
- margin=margin,
110
- overwrite=overwrite,
111
- ),
112
- )
113
-
114
- return {
115
- "success": True,
116
- "input_path": input_path,
117
- "output_path": str(result_path),
118
- "margin_pixels": margin,
119
- "message": f"Cropped figure saved to {result_path}",
120
- }
121
- except Exception as e:
122
- return {"success": False, "error": str(e)}
123
-
124
-
125
- async def save_figure_handler(
126
- output_path: str,
127
- figure_id: Optional[str] = None,
128
- dpi: int = 300,
129
- crop: bool = True,
130
- ) -> dict:
131
- """Save the figure to file."""
132
- try:
133
- if figure_id is None:
134
- if not _FIGURE_REGISTRY:
135
- raise ValueError("No active figures")
136
- figure_id = list(_FIGURE_REGISTRY.keys())[-1]
137
-
138
- if figure_id not in _FIGURE_REGISTRY:
139
- raise ValueError(f"Figure '{figure_id}' not found")
140
-
141
- fig = _FIGURE_REGISTRY[figure_id]["fig"]
142
-
143
- fig.savefig(output_path, dpi=dpi, bbox_inches="tight")
144
-
145
- final_path = output_path
146
- if crop and output_path.endswith(".png"):
147
- from scitex.plt import crop as crop_fn
148
-
149
- final_path = crop_fn(output_path, overwrite=True)
150
-
151
- return {
152
- "success": True,
153
- "figure_id": figure_id,
154
- "output_path": str(final_path),
155
- "dpi": dpi,
156
- "cropped": crop,
157
- }
158
- except Exception as e:
159
- return {"success": False, "error": str(e)}
160
-
161
-
162
- async def close_figure_handler(figure_id: Optional[str] = None) -> dict:
163
- """Close a figure and free memory."""
164
- try:
165
- import scitex.plt as splt
166
-
167
- if figure_id is None:
168
- if not _FIGURE_REGISTRY:
169
- return {"success": True, "message": "No figures to close"}
170
- figure_id = list(_FIGURE_REGISTRY.keys())[-1]
171
-
172
- if figure_id in _FIGURE_REGISTRY:
173
- fig = _FIGURE_REGISTRY[figure_id]["fig"]
174
- splt.close(fig)
175
- del _FIGURE_REGISTRY[figure_id]
176
-
177
- return {
178
- "success": True,
179
- "figure_id": figure_id,
180
- "message": f"Closed figure {figure_id}",
181
- }
182
- except Exception as e:
183
- return {"success": False, "error": str(e)}
184
-
185
-
186
- __all__ = [
187
- "_FIGURE_REGISTRY",
188
- "_get_axes",
189
- "create_figure_handler",
190
- "crop_figure_handler",
191
- "save_figure_handler",
192
- "close_figure_handler",
193
- ]
194
-
195
- # EOF
@@ -1,252 +0,0 @@
1
- #!/usr/bin/env python3
2
- # Timestamp: 2026-01-13
3
- # File: src/scitex/plt/_mcp/_handlers_plot.py
4
-
5
- """Plot-related MCP handlers for SciTeX plt module."""
6
-
7
- from __future__ import annotations
8
-
9
- from typing import Optional
10
-
11
- from ._handlers_figure import _get_axes
12
-
13
-
14
- async def plot_bar_handler(
15
- x: list[str],
16
- y: list[float],
17
- figure_id: Optional[str] = None,
18
- panel: str = "0,0",
19
- yerr: Optional[list[float]] = None,
20
- colors: Optional[list[str]] = None,
21
- xlabel: Optional[str] = None,
22
- ylabel: Optional[str] = None,
23
- title: Optional[str] = None,
24
- ) -> dict:
25
- """Create a bar plot on specified panel."""
26
- try:
27
- fig, ax, fid = _get_axes(figure_id, panel)
28
-
29
- if colors is None:
30
- from scitex.plt import color as color_module
31
-
32
- params = getattr(color_module, "PARAMS", {})
33
- rgba_cycle = params.get("RGBA_NORM_FOR_CYCLE", {})
34
- color_list = list(rgba_cycle.values())
35
- colors = color_list[: len(x)] if color_list else None
36
-
37
- ax.bar(x, y, yerr=yerr, capsize=3, color=colors)
38
-
39
- if xlabel:
40
- ax.set_xlabel(xlabel)
41
- if ylabel:
42
- ax.set_ylabel(ylabel)
43
- if title:
44
- ax.set_title(title)
45
-
46
- return {
47
- "success": True,
48
- "figure_id": fid,
49
- "panel": panel,
50
- "plot_type": "bar",
51
- "n_bars": len(x),
52
- }
53
- except Exception as e:
54
- return {"success": False, "error": str(e)}
55
-
56
-
57
- async def plot_scatter_handler(
58
- x: list[float],
59
- y: list[float],
60
- figure_id: Optional[str] = None,
61
- panel: str = "0,0",
62
- color: Optional[str] = None,
63
- size: Optional[float] = None,
64
- alpha: float = 0.7,
65
- add_regression: bool = False,
66
- xlabel: Optional[str] = None,
67
- ylabel: Optional[str] = None,
68
- title: Optional[str] = None,
69
- ) -> dict:
70
- """Create a scatter plot on specified panel."""
71
- try:
72
- import numpy as np
73
-
74
- fig, ax, fid = _get_axes(figure_id, panel)
75
-
76
- if color is None:
77
- color = "#c633ff" # SciTeX purple
78
-
79
- s = (size * 2.83465) ** 2 if size else 15
80
-
81
- ax.scatter(x, y, c=color, s=s, alpha=alpha)
82
-
83
- if add_regression:
84
- z = np.polyfit(x, y, 1)
85
- p = np.poly1d(z)
86
- x_line = np.linspace(min(x), max(x), 100)
87
- ax.plot(x_line, p(x_line), color="#e25e33", linestyle="--", linewidth=1)
88
-
89
- if xlabel:
90
- ax.set_xlabel(xlabel)
91
- if ylabel:
92
- ax.set_ylabel(ylabel)
93
- if title:
94
- ax.set_title(title)
95
-
96
- return {
97
- "success": True,
98
- "figure_id": fid,
99
- "panel": panel,
100
- "plot_type": "scatter",
101
- "n_points": len(x),
102
- "regression_added": add_regression,
103
- }
104
- except Exception as e:
105
- return {"success": False, "error": str(e)}
106
-
107
-
108
- async def plot_line_handler(
109
- x: list[float],
110
- y: list[float],
111
- figure_id: Optional[str] = None,
112
- panel: str = "0,0",
113
- yerr: Optional[list[float]] = None,
114
- color: Optional[str] = None,
115
- label: Optional[str] = None,
116
- linestyle: str = "-",
117
- xlabel: Optional[str] = None,
118
- ylabel: Optional[str] = None,
119
- title: Optional[str] = None,
120
- ) -> dict:
121
- """Create a line plot on specified panel."""
122
- try:
123
- fig, ax, fid = _get_axes(figure_id, panel)
124
-
125
- if color is None:
126
- color = "#007fbf" # SciTeX blue
127
-
128
- ax.plot(x, y, color=color, label=label, linestyle=linestyle)
129
-
130
- if yerr:
131
- import numpy as np
132
-
133
- y_arr = np.array(y)
134
- yerr_arr = np.array(yerr)
135
- ax.fill_between(
136
- x, y_arr - yerr_arr, y_arr + yerr_arr, alpha=0.3, color=color
137
- )
138
-
139
- if xlabel:
140
- ax.set_xlabel(xlabel)
141
- if ylabel:
142
- ax.set_ylabel(ylabel)
143
- if title:
144
- ax.set_title(title)
145
- if label:
146
- ax.legend(loc="upper right", frameon=False)
147
-
148
- return {
149
- "success": True,
150
- "figure_id": fid,
151
- "panel": panel,
152
- "plot_type": "line",
153
- "n_points": len(x),
154
- }
155
- except Exception as e:
156
- return {"success": False, "error": str(e)}
157
-
158
-
159
- async def plot_box_handler(
160
- data: list[list[float]],
161
- figure_id: Optional[str] = None,
162
- panel: str = "0,0",
163
- labels: Optional[list[str]] = None,
164
- colors: Optional[list[str]] = None,
165
- xlabel: Optional[str] = None,
166
- ylabel: Optional[str] = None,
167
- title: Optional[str] = None,
168
- ) -> dict:
169
- """Create a box plot on specified panel."""
170
- try:
171
- fig, ax, fid = _get_axes(figure_id, panel)
172
-
173
- bp = ax.boxplot(data, patch_artist=True, widths=0.6)
174
-
175
- if colors is None:
176
- colors = ["#007fbf", "#ff4433", "#14b514", "#c633ff", "#e25e33"]
177
- for i, box in enumerate(bp["boxes"]):
178
- box.set_facecolor(colors[i % len(colors)])
179
-
180
- if labels:
181
- ax.set_xticklabels(labels)
182
- if xlabel:
183
- ax.set_xlabel(xlabel)
184
- if ylabel:
185
- ax.set_ylabel(ylabel)
186
- if title:
187
- ax.set_title(title)
188
-
189
- return {
190
- "success": True,
191
- "figure_id": fid,
192
- "panel": panel,
193
- "plot_type": "box",
194
- "n_groups": len(data),
195
- }
196
- except Exception as e:
197
- return {"success": False, "error": str(e)}
198
-
199
-
200
- async def plot_violin_handler(
201
- data: list[list[float]],
202
- figure_id: Optional[str] = None,
203
- panel: str = "0,0",
204
- labels: Optional[list[str]] = None,
205
- colors: Optional[list[str]] = None,
206
- xlabel: Optional[str] = None,
207
- ylabel: Optional[str] = None,
208
- title: Optional[str] = None,
209
- ) -> dict:
210
- """Create a violin plot on specified panel."""
211
- try:
212
- fig, ax, fid = _get_axes(figure_id, panel)
213
-
214
- positions = list(range(1, len(data) + 1))
215
- vp = ax.violinplot(data, positions=positions, showmedians=True, widths=0.7)
216
-
217
- if colors is None:
218
- colors = ["#007fbf", "#ff4433", "#14b514", "#c633ff", "#e25e33"]
219
- for i, body in enumerate(vp["bodies"]):
220
- body.set_facecolor(colors[i % len(colors)])
221
- body.set_alpha(0.6)
222
-
223
- if labels:
224
- ax.set_xticks(positions)
225
- ax.set_xticklabels(labels)
226
- if xlabel:
227
- ax.set_xlabel(xlabel)
228
- if ylabel:
229
- ax.set_ylabel(ylabel)
230
- if title:
231
- ax.set_title(title)
232
-
233
- return {
234
- "success": True,
235
- "figure_id": fid,
236
- "panel": panel,
237
- "plot_type": "violin",
238
- "n_groups": len(data),
239
- }
240
- except Exception as e:
241
- return {"success": False, "error": str(e)}
242
-
243
-
244
- __all__ = [
245
- "plot_bar_handler",
246
- "plot_scatter_handler",
247
- "plot_line_handler",
248
- "plot_box_handler",
249
- "plot_violin_handler",
250
- ]
251
-
252
- # EOF