scitex 2.14.0__py3-none-any.whl → 2.15.2__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.
- scitex/__init__.py +71 -17
- scitex/_env_loader.py +156 -0
- scitex/_mcp_resources/__init__.py +37 -0
- scitex/_mcp_resources/_cheatsheet.py +135 -0
- scitex/_mcp_resources/_figrecipe.py +138 -0
- scitex/_mcp_resources/_formats.py +102 -0
- scitex/_mcp_resources/_modules.py +337 -0
- scitex/_mcp_resources/_session.py +149 -0
- scitex/_mcp_tools/__init__.py +4 -0
- scitex/_mcp_tools/audio.py +66 -0
- scitex/_mcp_tools/diagram.py +11 -95
- scitex/_mcp_tools/introspect.py +210 -0
- scitex/_mcp_tools/plt.py +260 -305
- scitex/_mcp_tools/scholar.py +74 -0
- scitex/_mcp_tools/social.py +244 -0
- scitex/_mcp_tools/template.py +24 -0
- scitex/_mcp_tools/writer.py +21 -204
- scitex/ai/_gen_ai/_PARAMS.py +10 -7
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
- scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
- scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
- scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
- scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
- scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
- scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
- scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
- scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
- scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
- scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
- scitex/audio/README.md +40 -36
- scitex/audio/__init__.py +129 -61
- scitex/audio/_branding.py +185 -0
- scitex/audio/_mcp/__init__.py +32 -0
- scitex/audio/_mcp/handlers.py +59 -6
- scitex/audio/_mcp/speak_handlers.py +238 -0
- scitex/audio/_relay.py +225 -0
- scitex/audio/_tts.py +18 -10
- scitex/audio/engines/base.py +17 -10
- scitex/audio/engines/elevenlabs_engine.py +7 -2
- scitex/audio/mcp_server.py +228 -75
- scitex/canvas/README.md +1 -1
- scitex/canvas/editor/_dearpygui/__init__.py +25 -0
- scitex/canvas/editor/_dearpygui/_editor.py +147 -0
- scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
- scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
- scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
- scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
- scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
- scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
- scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
- scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
- scitex/canvas/editor/_dearpygui/_selection.py +295 -0
- scitex/canvas/editor/_dearpygui/_state.py +93 -0
- scitex/canvas/editor/_dearpygui/_utils.py +61 -0
- scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
- scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
- scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
- scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
- scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
- scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
- scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
- scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
- scitex/canvas/editor/flask_editor/_core.py +25 -1684
- scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
- scitex/cli/__init__.py +38 -43
- scitex/cli/audio.py +76 -27
- scitex/cli/capture.py +13 -20
- scitex/cli/introspect.py +481 -0
- scitex/cli/main.py +200 -109
- scitex/cli/mcp.py +60 -34
- scitex/cli/plt.py +357 -0
- scitex/cli/repro.py +15 -8
- scitex/cli/resource.py +15 -8
- scitex/cli/scholar/__init__.py +23 -8
- scitex/cli/scholar/_crossref_scitex.py +296 -0
- scitex/cli/scholar/_fetch.py +25 -3
- scitex/cli/social.py +314 -0
- scitex/cli/stats.py +15 -8
- scitex/cli/template.py +129 -12
- scitex/cli/tex.py +15 -8
- scitex/cli/writer.py +132 -8
- scitex/cloud/__init__.py +41 -2
- scitex/config/README.md +1 -1
- scitex/config/__init__.py +16 -2
- scitex/config/_env_registry.py +256 -0
- scitex/context/__init__.py +22 -0
- scitex/dev/__init__.py +20 -1
- scitex/diagram/__init__.py +42 -19
- scitex/diagram/mcp_server.py +13 -125
- scitex/gen/__init__.py +50 -14
- scitex/gen/_list_packages.py +4 -4
- scitex/introspect/__init__.py +82 -0
- scitex/introspect/_call_graph.py +303 -0
- scitex/introspect/_class_hierarchy.py +163 -0
- scitex/introspect/_core.py +41 -0
- scitex/introspect/_docstring.py +131 -0
- scitex/introspect/_examples.py +113 -0
- scitex/introspect/_imports.py +271 -0
- scitex/{gen/_inspect_module.py → introspect/_list_api.py} +43 -54
- scitex/introspect/_mcp/__init__.py +41 -0
- scitex/introspect/_mcp/handlers.py +233 -0
- scitex/introspect/_members.py +155 -0
- scitex/introspect/_resolve.py +89 -0
- scitex/introspect/_signature.py +131 -0
- scitex/introspect/_source.py +80 -0
- scitex/introspect/_type_hints.py +172 -0
- scitex/io/_save.py +1 -2
- scitex/io/bundle/README.md +1 -1
- scitex/logging/_formatters.py +19 -9
- scitex/mcp_server.py +98 -5
- scitex/os/__init__.py +4 -0
- scitex/{gen → os}/_check_host.py +4 -5
- scitex/plt/__init__.py +245 -550
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
- scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/plt/gallery/README.md +1 -1
- scitex/plt/utils/_hitmap/__init__.py +82 -0
- scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
- scitex/plt/utils/_hitmap/_color_application.py +346 -0
- scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
- scitex/plt/utils/_hitmap/_constants.py +40 -0
- scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
- scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
- scitex/plt/utils/_hitmap/_query.py +113 -0
- scitex/plt/utils/_hitmap.py +46 -1616
- scitex/plt/utils/_metadata/__init__.py +80 -0
- scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
- scitex/plt/utils/_metadata/_artists/_base.py +195 -0
- scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
- scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
- scitex/plt/utils/_metadata/_artists/_images.py +80 -0
- scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
- scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
- scitex/plt/utils/_metadata/_artists/_text.py +106 -0
- scitex/plt/utils/_metadata/_csv.py +416 -0
- scitex/plt/utils/_metadata/_detect.py +225 -0
- scitex/plt/utils/_metadata/_legend.py +127 -0
- scitex/plt/utils/_metadata/_rounding.py +117 -0
- scitex/plt/utils/_metadata/_verification.py +202 -0
- scitex/schema/README.md +1 -1
- scitex/scholar/__init__.py +8 -0
- scitex/scholar/_mcp/crossref_handlers.py +265 -0
- scitex/scholar/core/Scholar.py +63 -1700
- scitex/scholar/core/_mixins/__init__.py +36 -0
- scitex/scholar/core/_mixins/_enrichers.py +270 -0
- scitex/scholar/core/_mixins/_library_handlers.py +100 -0
- scitex/scholar/core/_mixins/_loaders.py +103 -0
- scitex/scholar/core/_mixins/_pdf_download.py +375 -0
- scitex/scholar/core/_mixins/_pipeline.py +312 -0
- scitex/scholar/core/_mixins/_project_handlers.py +125 -0
- scitex/scholar/core/_mixins/_savers.py +69 -0
- scitex/scholar/core/_mixins/_search.py +103 -0
- scitex/scholar/core/_mixins/_services.py +88 -0
- scitex/scholar/core/_mixins/_url_finding.py +105 -0
- scitex/scholar/crossref_scitex.py +367 -0
- scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/scholar/examples/00_run_all.sh +120 -0
- scitex/scholar/jobs/_executors.py +27 -3
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
- scitex/scholar/pdf_download/_cli.py +154 -0
- scitex/scholar/pdf_download/strategies/__init__.py +11 -8
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
- scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
- scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
- scitex/scholar/pipelines/_single_steps.py +71 -36
- scitex/scholar/storage/_LibraryManager.py +97 -1695
- scitex/scholar/storage/_mixins/__init__.py +30 -0
- scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
- scitex/scholar/storage/_mixins/_library_operations.py +218 -0
- scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
- scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
- scitex/scholar/storage/_mixins/_resolution.py +376 -0
- scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
- scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
- scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
- scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
- scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
- scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
- scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
- scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
- scitex/security/README.md +3 -3
- scitex/session/README.md +1 -1
- scitex/session/__init__.py +26 -7
- scitex/session/_decorator.py +1 -1
- scitex/sh/README.md +1 -1
- scitex/sh/__init__.py +7 -4
- scitex/social/__init__.py +155 -0
- scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/stats/_mcp/_handlers/__init__.py +31 -0
- scitex/stats/_mcp/_handlers/_corrections.py +113 -0
- scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
- scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
- scitex/stats/_mcp/_handlers/_format.py +94 -0
- scitex/stats/_mcp/_handlers/_normality.py +110 -0
- scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
- scitex/stats/_mcp/_handlers/_power.py +247 -0
- scitex/stats/_mcp/_handlers/_recommend.py +102 -0
- scitex/stats/_mcp/_handlers/_run_test.py +279 -0
- scitex/stats/_mcp/_handlers/_stars.py +48 -0
- scitex/stats/_mcp/handlers.py +19 -1171
- scitex/stats/auto/_stat_style.py +175 -0
- scitex/stats/auto/_style_definitions.py +411 -0
- scitex/stats/auto/_styles.py +22 -620
- scitex/stats/descriptive/__init__.py +11 -8
- scitex/stats/descriptive/_ci.py +39 -0
- scitex/stats/power/_power.py +15 -4
- scitex/str/__init__.py +2 -1
- scitex/str/_title_case.py +63 -0
- scitex/template/README.md +1 -1
- scitex/template/__init__.py +25 -10
- scitex/template/_code_templates.py +147 -0
- scitex/template/_mcp/handlers.py +81 -0
- scitex/template/_mcp/tool_schemas.py +55 -0
- scitex/template/_templates/__init__.py +51 -0
- scitex/template/_templates/audio.py +233 -0
- scitex/template/_templates/canvas.py +312 -0
- scitex/template/_templates/capture.py +268 -0
- scitex/template/_templates/config.py +43 -0
- scitex/template/_templates/diagram.py +294 -0
- scitex/template/_templates/io.py +107 -0
- scitex/template/_templates/module.py +53 -0
- scitex/template/_templates/plt.py +202 -0
- scitex/template/_templates/scholar.py +267 -0
- scitex/template/_templates/session.py +130 -0
- scitex/template/_templates/session_minimal.py +43 -0
- scitex/template/_templates/session_plot.py +67 -0
- scitex/template/_templates/session_stats.py +77 -0
- scitex/template/_templates/stats.py +323 -0
- scitex/template/_templates/writer.py +296 -0
- scitex/template/clone_writer_directory.py +5 -5
- scitex/ui/_backends/_email.py +10 -2
- scitex/ui/_backends/_webhook.py +5 -1
- scitex/web/_search_pubmed.py +10 -6
- scitex/writer/README.md +1 -1
- scitex/writer/_mcp/handlers.py +11 -744
- scitex/writer/_mcp/tool_schemas.py +5 -335
- scitex-2.15.2.dist-info/METADATA +648 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/RECORD +246 -150
- scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
- scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
- scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
- scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
- scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
- scitex/diagram/_compile.py +0 -312
- scitex/diagram/_diagram.py +0 -355
- scitex/diagram/_mcp/__init__.py +0 -4
- scitex/diagram/_mcp/handlers.py +0 -400
- scitex/diagram/_mcp/tool_schemas.py +0 -157
- scitex/diagram/_presets.py +0 -173
- scitex/diagram/_schema.py +0 -182
- scitex/diagram/_split.py +0 -278
- scitex/gen/_ci.py +0 -12
- scitex/gen/_title_case.py +0 -89
- scitex/plt/_mcp/__init__.py +0 -4
- scitex/plt/_mcp/_handlers_annotation.py +0 -102
- scitex/plt/_mcp/_handlers_figure.py +0 -195
- scitex/plt/_mcp/_handlers_plot.py +0 -252
- scitex/plt/_mcp/_handlers_style.py +0 -219
- scitex/plt/_mcp/handlers.py +0 -74
- scitex/plt/_mcp/tool_schemas.py +0 -497
- scitex/plt/mcp_server.py +0 -231
- scitex/scholar/data/.gitkeep +0 -0
- scitex/scholar/data/README.md +0 -44
- scitex/scholar/data/bib_files/bibliography.bib +0 -1952
- scitex/scholar/data/bib_files/neurovista.bib +0 -277
- scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
- scitex/scholar/data/bib_files/openaccess.bib +0 -89
- scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
- scitex/scholar/data/bib_files/pac.bib +0 -698
- scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
- scitex/scholar/data/bib_files/pac_processed.bib +0 -0
- scitex/scholar/data/bib_files/pac_titles.txt +0 -75
- scitex/scholar/data/bib_files/paywalled.bib +0 -98
- scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
- scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
- scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
- scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
- scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_seizure.bib +0 -46
- scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
- scitex/scholar/data/impact_factor.db +0 -0
- scitex/scholar/examples/SUGGESTIONS.md +0 -865
- scitex/scholar/examples/dev.py +0 -38
- scitex-2.14.0.dist-info/METADATA +0 -1238
- /scitex/{gen → context}/_detect_environment.py +0 -0
- /scitex/{gen → context}/_get_notebook_path.py +0 -0
- /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/WHEEL +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/_handlers.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Event handlers for DearPyGui editor.
|
|
7
|
+
|
|
8
|
+
Handles all callbacks and user interactions.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import copy
|
|
12
|
+
import time
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from ._state import EditorState
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def on_value_change(state: "EditorState", dpg) -> None:
|
|
20
|
+
"""Handle value changes from widgets."""
|
|
21
|
+
from ._rendering import update_preview
|
|
22
|
+
|
|
23
|
+
state.user_modified = True
|
|
24
|
+
collect_overrides(state, dpg)
|
|
25
|
+
update_preview(state, dpg)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def collect_overrides(state: "EditorState", dpg) -> None:
|
|
29
|
+
"""Collect current values from all widgets."""
|
|
30
|
+
o = state.current_overrides
|
|
31
|
+
|
|
32
|
+
# Labels
|
|
33
|
+
o["title"] = dpg.get_value("title_input")
|
|
34
|
+
o["xlabel"] = dpg.get_value("xlabel_input")
|
|
35
|
+
o["ylabel"] = dpg.get_value("ylabel_input")
|
|
36
|
+
|
|
37
|
+
# Line style
|
|
38
|
+
o["linewidth"] = dpg.get_value("linewidth_slider")
|
|
39
|
+
|
|
40
|
+
# Font settings
|
|
41
|
+
o["title_fontsize"] = dpg.get_value("title_fontsize_slider")
|
|
42
|
+
o["axis_fontsize"] = dpg.get_value("axis_fontsize_slider")
|
|
43
|
+
o["tick_fontsize"] = dpg.get_value("tick_fontsize_slider")
|
|
44
|
+
o["legend_fontsize"] = dpg.get_value("legend_fontsize_slider")
|
|
45
|
+
|
|
46
|
+
# Tick settings
|
|
47
|
+
o["n_ticks"] = dpg.get_value("n_ticks_slider")
|
|
48
|
+
o["tick_length"] = dpg.get_value("tick_length_slider")
|
|
49
|
+
o["tick_width"] = dpg.get_value("tick_width_slider")
|
|
50
|
+
o["tick_direction"] = dpg.get_value("tick_direction_combo")
|
|
51
|
+
|
|
52
|
+
# Style
|
|
53
|
+
o["grid"] = dpg.get_value("grid_checkbox")
|
|
54
|
+
o["hide_top_spine"] = dpg.get_value("hide_top_spine_checkbox")
|
|
55
|
+
o["hide_right_spine"] = dpg.get_value("hide_right_spine_checkbox")
|
|
56
|
+
o["transparent"] = dpg.get_value("transparent_checkbox")
|
|
57
|
+
o["axis_width"] = dpg.get_value("axis_width_slider")
|
|
58
|
+
|
|
59
|
+
# Legend
|
|
60
|
+
o["legend_visible"] = dpg.get_value("legend_visible_checkbox")
|
|
61
|
+
o["legend_frameon"] = dpg.get_value("legend_frameon_checkbox")
|
|
62
|
+
o["legend_loc"] = dpg.get_value("legend_loc_combo")
|
|
63
|
+
|
|
64
|
+
# Dimensions
|
|
65
|
+
o["fig_size"] = [
|
|
66
|
+
dpg.get_value("fig_width_input"),
|
|
67
|
+
dpg.get_value("fig_height_input"),
|
|
68
|
+
]
|
|
69
|
+
o["dpi"] = dpg.get_value("dpi_slider")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def on_apply_limits(state: "EditorState", dpg) -> None:
|
|
73
|
+
"""Apply axis limits."""
|
|
74
|
+
from ._rendering import update_preview
|
|
75
|
+
|
|
76
|
+
xmin = dpg.get_value("xmin_input")
|
|
77
|
+
xmax = dpg.get_value("xmax_input")
|
|
78
|
+
ymin = dpg.get_value("ymin_input")
|
|
79
|
+
ymax = dpg.get_value("ymax_input")
|
|
80
|
+
|
|
81
|
+
if xmin < xmax:
|
|
82
|
+
state.current_overrides["xlim"] = [xmin, xmax]
|
|
83
|
+
if ymin < ymax:
|
|
84
|
+
state.current_overrides["ylim"] = [ymin, ymax]
|
|
85
|
+
|
|
86
|
+
state.user_modified = True
|
|
87
|
+
update_preview(state, dpg)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def on_add_annotation(state: "EditorState", dpg) -> None:
|
|
91
|
+
"""Add text annotation."""
|
|
92
|
+
from ._rendering import update_preview
|
|
93
|
+
|
|
94
|
+
text = dpg.get_value("annot_text_input")
|
|
95
|
+
if not text:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
x = dpg.get_value("annot_x_input")
|
|
99
|
+
y = dpg.get_value("annot_y_input")
|
|
100
|
+
|
|
101
|
+
if "annotations" not in state.current_overrides:
|
|
102
|
+
state.current_overrides["annotations"] = []
|
|
103
|
+
|
|
104
|
+
state.current_overrides["annotations"].append(
|
|
105
|
+
{
|
|
106
|
+
"type": "text",
|
|
107
|
+
"text": text,
|
|
108
|
+
"x": x,
|
|
109
|
+
"y": y,
|
|
110
|
+
"fontsize": state.current_overrides.get("axis_fontsize", 7),
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
dpg.set_value("annot_text_input", "")
|
|
115
|
+
update_annotations_list(state, dpg)
|
|
116
|
+
state.user_modified = True
|
|
117
|
+
update_preview(state, dpg)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def on_remove_annotation(state: "EditorState", dpg) -> None:
|
|
121
|
+
"""Remove selected annotation."""
|
|
122
|
+
from ._rendering import update_preview
|
|
123
|
+
|
|
124
|
+
selected = dpg.get_value("annotations_listbox")
|
|
125
|
+
annotations = state.current_overrides.get("annotations", [])
|
|
126
|
+
|
|
127
|
+
if selected and annotations:
|
|
128
|
+
# Find index by text
|
|
129
|
+
for i, ann in enumerate(annotations):
|
|
130
|
+
label = (
|
|
131
|
+
f"{ann.get('text', '')[:20]} "
|
|
132
|
+
f"({ann.get('x', 0):.2f}, {ann.get('y', 0):.2f})"
|
|
133
|
+
)
|
|
134
|
+
if label == selected:
|
|
135
|
+
del annotations[i]
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
update_annotations_list(state, dpg)
|
|
139
|
+
state.user_modified = True
|
|
140
|
+
update_preview(state, dpg)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def update_annotations_list(state: "EditorState", dpg) -> None:
|
|
144
|
+
"""Update the annotations listbox."""
|
|
145
|
+
annotations = state.current_overrides.get("annotations", [])
|
|
146
|
+
items = []
|
|
147
|
+
for ann in annotations:
|
|
148
|
+
if ann.get("type") == "text":
|
|
149
|
+
label = (
|
|
150
|
+
f"{ann.get('text', '')[:20]} "
|
|
151
|
+
f"({ann.get('x', 0):.2f}, {ann.get('y', 0):.2f})"
|
|
152
|
+
)
|
|
153
|
+
items.append(label)
|
|
154
|
+
|
|
155
|
+
dpg.configure_item("annotations_listbox", items=items)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def on_preview_click(state: "EditorState", dpg, app_data) -> None:
|
|
159
|
+
"""Handle click on preview image to select element."""
|
|
160
|
+
from ._selection import find_clicked_element, find_nearest_trace, select_element
|
|
161
|
+
|
|
162
|
+
# Only handle left clicks
|
|
163
|
+
if app_data != 0: # 0 = left button
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
# Get mouse position relative to viewport
|
|
167
|
+
mouse_pos = dpg.get_mouse_pos(local=False)
|
|
168
|
+
|
|
169
|
+
# Get preview image position and size
|
|
170
|
+
if not dpg.does_item_exist("preview_image"):
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# Get the image item's position in the window
|
|
174
|
+
img_pos = dpg.get_item_pos("preview_image")
|
|
175
|
+
panel_pos = dpg.get_item_pos("preview_panel")
|
|
176
|
+
|
|
177
|
+
# Calculate click position relative to image
|
|
178
|
+
click_x = mouse_pos[0] - panel_pos[0] - img_pos[0]
|
|
179
|
+
click_y = mouse_pos[1] - panel_pos[1] - img_pos[1]
|
|
180
|
+
|
|
181
|
+
# Check if click is within image bounds (800x600)
|
|
182
|
+
max_width, max_height = 800, 600
|
|
183
|
+
if not (0 <= click_x <= max_width and 0 <= click_y <= max_height):
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
# First check if click is on a fixed element (title, labels, axes, legend)
|
|
187
|
+
element = find_clicked_element(state, click_x, click_y)
|
|
188
|
+
|
|
189
|
+
if element:
|
|
190
|
+
select_element(state, element, dpg)
|
|
191
|
+
else:
|
|
192
|
+
# Fall back to trace selection
|
|
193
|
+
trace_idx = find_nearest_trace(state, click_x, click_y, max_width, max_height)
|
|
194
|
+
if trace_idx is not None:
|
|
195
|
+
select_element(state, {"type": "trace", "index": trace_idx}, dpg)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def on_preview_hover(state: "EditorState", dpg, app_data) -> None:
|
|
199
|
+
"""Handle mouse move for hover effects on preview (optimized with caching)."""
|
|
200
|
+
from ._rendering import update_hover_overlay
|
|
201
|
+
from ._selection import find_clicked_element, find_nearest_trace
|
|
202
|
+
|
|
203
|
+
# Throttle hover updates - reduced to 16ms (~60fps) since we use fast overlay
|
|
204
|
+
current_time = time.time()
|
|
205
|
+
if current_time - state.last_hover_check < 0.016:
|
|
206
|
+
return
|
|
207
|
+
state.last_hover_check = current_time
|
|
208
|
+
|
|
209
|
+
# Get mouse position relative to viewport
|
|
210
|
+
mouse_pos = dpg.get_mouse_pos(local=False)
|
|
211
|
+
|
|
212
|
+
# Get preview image position
|
|
213
|
+
if not dpg.does_item_exist("preview_image"):
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
img_pos = dpg.get_item_pos("preview_image")
|
|
217
|
+
panel_pos = dpg.get_item_pos("preview_panel")
|
|
218
|
+
|
|
219
|
+
# Calculate hover position relative to image
|
|
220
|
+
hover_x = mouse_pos[0] - panel_pos[0] - img_pos[0]
|
|
221
|
+
hover_y = mouse_pos[1] - panel_pos[1] - img_pos[1]
|
|
222
|
+
|
|
223
|
+
# Check if within image bounds
|
|
224
|
+
max_width, max_height = 800, 600
|
|
225
|
+
if not (0 <= hover_x <= max_width and 0 <= hover_y <= max_height):
|
|
226
|
+
if state.hovered_element is not None:
|
|
227
|
+
state.hovered_element = None
|
|
228
|
+
dpg.set_value("hover_text", "")
|
|
229
|
+
# Use fast overlay update instead of full redraw
|
|
230
|
+
update_hover_overlay(state, dpg)
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
# Find element under cursor
|
|
234
|
+
element = find_clicked_element(state, hover_x, hover_y)
|
|
235
|
+
|
|
236
|
+
if element is None:
|
|
237
|
+
# Check for trace hover
|
|
238
|
+
trace_idx = find_nearest_trace(state, hover_x, hover_y, max_width, max_height)
|
|
239
|
+
if trace_idx is not None:
|
|
240
|
+
element = {"type": "trace", "index": trace_idx}
|
|
241
|
+
|
|
242
|
+
# Check if hover changed
|
|
243
|
+
old_hover = state.hovered_element
|
|
244
|
+
if element != old_hover:
|
|
245
|
+
state.hovered_element = element
|
|
246
|
+
if element:
|
|
247
|
+
elem_type = element.get("type", "")
|
|
248
|
+
elem_idx = element.get("index")
|
|
249
|
+
if elem_type == "trace" and elem_idx is not None:
|
|
250
|
+
traces = state.current_overrides.get("traces", [])
|
|
251
|
+
if elem_idx < len(traces):
|
|
252
|
+
label = traces[elem_idx].get("label", f"Trace {elem_idx}")
|
|
253
|
+
dpg.set_value("hover_text", f"Hover: {label} (click to select)")
|
|
254
|
+
else:
|
|
255
|
+
label = elem_type.replace("x", "X ").replace("y", "Y ").title()
|
|
256
|
+
dpg.set_value("hover_text", f"Hover: {label} (click to select)")
|
|
257
|
+
else:
|
|
258
|
+
dpg.set_value("hover_text", "")
|
|
259
|
+
|
|
260
|
+
# Use fast overlay update for hover (no matplotlib re-render)
|
|
261
|
+
update_hover_overlay(state, dpg)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def on_element_selected(state: "EditorState", dpg, app_data) -> None:
|
|
265
|
+
"""Handle element selection from combo box."""
|
|
266
|
+
from ._selection import select_element
|
|
267
|
+
|
|
268
|
+
if app_data == "Title":
|
|
269
|
+
select_element(state, {"type": "title", "index": None}, dpg)
|
|
270
|
+
elif app_data == "X Label":
|
|
271
|
+
select_element(state, {"type": "xlabel", "index": None}, dpg)
|
|
272
|
+
elif app_data == "Y Label":
|
|
273
|
+
select_element(state, {"type": "ylabel", "index": None}, dpg)
|
|
274
|
+
elif app_data == "X Axis":
|
|
275
|
+
select_element(state, {"type": "xaxis", "index": None}, dpg)
|
|
276
|
+
elif app_data == "Y Axis":
|
|
277
|
+
select_element(state, {"type": "yaxis", "index": None}, dpg)
|
|
278
|
+
elif app_data == "Legend":
|
|
279
|
+
select_element(state, {"type": "legend", "index": None}, dpg)
|
|
280
|
+
elif app_data.startswith("Trace: "):
|
|
281
|
+
# Find trace index
|
|
282
|
+
trace_label = app_data[7:] # Remove "Trace: " prefix
|
|
283
|
+
traces = state.current_overrides.get("traces", [])
|
|
284
|
+
for i, t in enumerate(traces):
|
|
285
|
+
if t.get("label", t.get("id", f"Trace {i}")) == trace_label:
|
|
286
|
+
select_element(state, {"type": "trace", "index": i}, dpg)
|
|
287
|
+
break
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def on_text_element_change(state: "EditorState", dpg) -> None:
|
|
291
|
+
"""Handle changes to text element properties."""
|
|
292
|
+
from ._rendering import update_preview
|
|
293
|
+
|
|
294
|
+
if state.selected_element is None:
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
elem_type = state.selected_element.get("type")
|
|
298
|
+
if elem_type not in ("title", "xlabel", "ylabel"):
|
|
299
|
+
return
|
|
300
|
+
|
|
301
|
+
text = dpg.get_value("element_text_input")
|
|
302
|
+
fontsize = dpg.get_value("element_fontsize_slider")
|
|
303
|
+
|
|
304
|
+
if elem_type == "title":
|
|
305
|
+
state.current_overrides["title"] = text
|
|
306
|
+
state.current_overrides["title_fontsize"] = fontsize
|
|
307
|
+
elif elem_type == "xlabel":
|
|
308
|
+
state.current_overrides["xlabel"] = text
|
|
309
|
+
state.current_overrides["axis_fontsize"] = fontsize
|
|
310
|
+
elif elem_type == "ylabel":
|
|
311
|
+
state.current_overrides["ylabel"] = text
|
|
312
|
+
state.current_overrides["axis_fontsize"] = fontsize
|
|
313
|
+
|
|
314
|
+
state.user_modified = True
|
|
315
|
+
update_preview(state, dpg)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def on_axis_element_change(state: "EditorState", dpg) -> None:
|
|
319
|
+
"""Handle changes to axis element properties."""
|
|
320
|
+
from ._rendering import update_preview
|
|
321
|
+
|
|
322
|
+
if state.selected_element is None:
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
elem_type = state.selected_element.get("type")
|
|
326
|
+
if elem_type not in ("xaxis", "yaxis"):
|
|
327
|
+
return
|
|
328
|
+
|
|
329
|
+
state.current_overrides["axis_width"] = dpg.get_value("axis_linewidth_slider")
|
|
330
|
+
state.current_overrides["tick_length"] = dpg.get_value("axis_tick_length_slider")
|
|
331
|
+
state.current_overrides["tick_fontsize"] = dpg.get_value(
|
|
332
|
+
"axis_tick_fontsize_slider"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
show_spine = dpg.get_value("axis_show_spine_checkbox")
|
|
336
|
+
if elem_type == "xaxis":
|
|
337
|
+
state.current_overrides["hide_bottom_spine"] = not show_spine
|
|
338
|
+
else:
|
|
339
|
+
state.current_overrides["hide_left_spine"] = not show_spine
|
|
340
|
+
|
|
341
|
+
state.user_modified = True
|
|
342
|
+
update_preview(state, dpg)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def on_legend_element_change(state: "EditorState", dpg) -> None:
|
|
346
|
+
"""Handle changes to legend element properties."""
|
|
347
|
+
from ._rendering import update_preview
|
|
348
|
+
|
|
349
|
+
if state.selected_element is None:
|
|
350
|
+
return
|
|
351
|
+
|
|
352
|
+
elem_type = state.selected_element.get("type")
|
|
353
|
+
if elem_type != "legend":
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
state.current_overrides["legend_visible"] = dpg.get_value("legend_visible_edit")
|
|
357
|
+
state.current_overrides["legend_frameon"] = dpg.get_value("legend_frameon_edit")
|
|
358
|
+
state.current_overrides["legend_loc"] = dpg.get_value("legend_loc_edit")
|
|
359
|
+
state.current_overrides["legend_fontsize"] = dpg.get_value("legend_fontsize_edit")
|
|
360
|
+
|
|
361
|
+
state.user_modified = True
|
|
362
|
+
update_preview(state, dpg)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def on_trace_property_change(state: "EditorState", dpg) -> None:
|
|
366
|
+
"""Handle changes to selected trace properties."""
|
|
367
|
+
from ._rendering import update_preview
|
|
368
|
+
|
|
369
|
+
if state.selected_trace_index is None:
|
|
370
|
+
return
|
|
371
|
+
|
|
372
|
+
traces = state.current_overrides.get("traces", [])
|
|
373
|
+
if state.selected_trace_index >= len(traces):
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
trace = traces[state.selected_trace_index]
|
|
377
|
+
|
|
378
|
+
# Update trace properties from widgets
|
|
379
|
+
trace["label"] = dpg.get_value("trace_label_input")
|
|
380
|
+
|
|
381
|
+
# Convert RGB to hex
|
|
382
|
+
color_rgb = dpg.get_value("trace_color_picker")
|
|
383
|
+
if color_rgb and len(color_rgb) >= 3:
|
|
384
|
+
r, g, b = int(color_rgb[0]), int(color_rgb[1]), int(color_rgb[2])
|
|
385
|
+
trace["color"] = f"#{r:02x}{g:02x}{b:02x}"
|
|
386
|
+
|
|
387
|
+
trace["linewidth"] = dpg.get_value("trace_linewidth_slider")
|
|
388
|
+
trace["linestyle"] = dpg.get_value("trace_linestyle_combo")
|
|
389
|
+
|
|
390
|
+
marker = dpg.get_value("trace_marker_combo")
|
|
391
|
+
trace["marker"] = marker if marker else None
|
|
392
|
+
|
|
393
|
+
trace["markersize"] = dpg.get_value("trace_markersize_slider")
|
|
394
|
+
|
|
395
|
+
state.user_modified = True
|
|
396
|
+
update_preview(state, dpg)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def on_save_manual(state: "EditorState", dpg) -> None:
|
|
400
|
+
"""Save current overrides to .manual.json."""
|
|
401
|
+
from ..edit import save_manual_overrides
|
|
402
|
+
|
|
403
|
+
try:
|
|
404
|
+
collect_overrides(state, dpg)
|
|
405
|
+
manual_path = save_manual_overrides(state.json_path, state.current_overrides)
|
|
406
|
+
dpg.set_value("status_text", f"Saved: {manual_path.name}")
|
|
407
|
+
except Exception as e:
|
|
408
|
+
dpg.set_value("status_text", f"Error: {str(e)}")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def on_reset_overrides(state: "EditorState", dpg) -> None:
|
|
412
|
+
"""Reset to initial overrides."""
|
|
413
|
+
from ._rendering import update_preview
|
|
414
|
+
|
|
415
|
+
state.current_overrides = copy.deepcopy(state.initial_overrides)
|
|
416
|
+
state.user_modified = False
|
|
417
|
+
|
|
418
|
+
# Update all widgets
|
|
419
|
+
dpg.set_value("title_input", state.current_overrides.get("title", ""))
|
|
420
|
+
dpg.set_value("xlabel_input", state.current_overrides.get("xlabel", ""))
|
|
421
|
+
dpg.set_value("ylabel_input", state.current_overrides.get("ylabel", ""))
|
|
422
|
+
dpg.set_value("linewidth_slider", state.current_overrides.get("linewidth", 1.0))
|
|
423
|
+
dpg.set_value("grid_checkbox", state.current_overrides.get("grid", False))
|
|
424
|
+
dpg.set_value(
|
|
425
|
+
"transparent_checkbox", state.current_overrides.get("transparent", True)
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
update_preview(state, dpg)
|
|
429
|
+
dpg.set_value("status_text", "Reset to original")
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def on_export_png(state: "EditorState", dpg) -> None:
|
|
433
|
+
"""Export current view to PNG."""
|
|
434
|
+
import matplotlib
|
|
435
|
+
|
|
436
|
+
matplotlib.use("Agg")
|
|
437
|
+
import matplotlib.pyplot as plt
|
|
438
|
+
|
|
439
|
+
from ._plotting import plot_from_csv
|
|
440
|
+
|
|
441
|
+
try:
|
|
442
|
+
collect_overrides(state, dpg)
|
|
443
|
+
output_path = state.json_path.with_suffix(".edited.png")
|
|
444
|
+
|
|
445
|
+
# Full resolution render
|
|
446
|
+
o = state.current_overrides
|
|
447
|
+
fig_size = o.get("fig_size", [3.15, 2.68])
|
|
448
|
+
dpi = o.get("dpi", 300)
|
|
449
|
+
|
|
450
|
+
fig, ax = plt.subplots(figsize=fig_size, dpi=dpi)
|
|
451
|
+
|
|
452
|
+
if state.csv_data is not None:
|
|
453
|
+
plot_from_csv(ax, o, state.csv_data)
|
|
454
|
+
|
|
455
|
+
if o.get("title"):
|
|
456
|
+
ax.set_title(o["title"], fontsize=o.get("title_fontsize", 8))
|
|
457
|
+
if o.get("xlabel"):
|
|
458
|
+
ax.set_xlabel(o["xlabel"], fontsize=o.get("axis_fontsize", 7))
|
|
459
|
+
if o.get("ylabel"):
|
|
460
|
+
ax.set_ylabel(o["ylabel"], fontsize=o.get("axis_fontsize", 7))
|
|
461
|
+
|
|
462
|
+
fig.tight_layout()
|
|
463
|
+
fig.savefig(
|
|
464
|
+
output_path,
|
|
465
|
+
dpi=dpi,
|
|
466
|
+
bbox_inches="tight",
|
|
467
|
+
transparent=o.get("transparent", True),
|
|
468
|
+
)
|
|
469
|
+
plt.close(fig)
|
|
470
|
+
|
|
471
|
+
dpg.set_value("status_text", f"Exported: {output_path.name}")
|
|
472
|
+
except Exception as e:
|
|
473
|
+
dpg.set_value("status_text", f"Error: {str(e)}")
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# EOF
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/_panels/__init__.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
UI panel creation for DearPyGui editor.
|
|
7
|
+
|
|
8
|
+
Subpackage for organizing control panel sections.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from ._control import create_control_panel
|
|
12
|
+
from ._preview import create_preview_panel
|
|
13
|
+
|
|
14
|
+
__all__ = ["create_preview_panel", "create_control_panel"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# EOF
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-24 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/_panels/_control.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Control panel creation for DearPyGui editor.
|
|
7
|
+
|
|
8
|
+
Main orchestrator that delegates to section modules.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .._state import EditorState
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_control_panel(state: "EditorState", dpg) -> None:
|
|
18
|
+
"""Create the control panel with all editing options."""
|
|
19
|
+
from .._handlers import (
|
|
20
|
+
on_add_annotation,
|
|
21
|
+
on_apply_limits,
|
|
22
|
+
on_axis_element_change,
|
|
23
|
+
on_element_selected,
|
|
24
|
+
on_export_png,
|
|
25
|
+
on_legend_element_change,
|
|
26
|
+
on_remove_annotation,
|
|
27
|
+
on_reset_overrides,
|
|
28
|
+
on_save_manual,
|
|
29
|
+
on_text_element_change,
|
|
30
|
+
on_trace_property_change,
|
|
31
|
+
on_value_change,
|
|
32
|
+
)
|
|
33
|
+
from .._rendering import update_preview
|
|
34
|
+
from .._selection import deselect_element, get_all_element_labels
|
|
35
|
+
from ._sections import (
|
|
36
|
+
create_annotations_section,
|
|
37
|
+
create_axis_limits_section,
|
|
38
|
+
create_dimensions_section,
|
|
39
|
+
create_font_settings_section,
|
|
40
|
+
create_labels_section,
|
|
41
|
+
create_legend_section,
|
|
42
|
+
create_line_style_section,
|
|
43
|
+
create_selected_element_section,
|
|
44
|
+
create_style_section,
|
|
45
|
+
create_tick_settings_section,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
o = state.current_overrides
|
|
49
|
+
|
|
50
|
+
with dpg.child_window(width=-1, height=-1, tag="control_panel"):
|
|
51
|
+
dpg.add_text("Properties", color=(100, 200, 100))
|
|
52
|
+
dpg.add_separator()
|
|
53
|
+
|
|
54
|
+
# Labels Section
|
|
55
|
+
create_labels_section(state, dpg, o, on_value_change)
|
|
56
|
+
|
|
57
|
+
# Axis Limits Section
|
|
58
|
+
create_axis_limits_section(state, dpg, o, on_apply_limits)
|
|
59
|
+
|
|
60
|
+
# Line Style Section
|
|
61
|
+
create_line_style_section(state, dpg, o, on_value_change)
|
|
62
|
+
|
|
63
|
+
# Font Settings Section
|
|
64
|
+
create_font_settings_section(state, dpg, o, on_value_change)
|
|
65
|
+
|
|
66
|
+
# Tick Settings Section
|
|
67
|
+
create_tick_settings_section(state, dpg, o, on_value_change)
|
|
68
|
+
|
|
69
|
+
# Style Section
|
|
70
|
+
create_style_section(state, dpg, o, on_value_change)
|
|
71
|
+
|
|
72
|
+
# Legend Section
|
|
73
|
+
create_legend_section(state, dpg, o, on_value_change)
|
|
74
|
+
|
|
75
|
+
# Selected Element Section
|
|
76
|
+
create_selected_element_section(
|
|
77
|
+
state,
|
|
78
|
+
dpg,
|
|
79
|
+
get_all_element_labels,
|
|
80
|
+
on_element_selected,
|
|
81
|
+
on_trace_property_change,
|
|
82
|
+
on_text_element_change,
|
|
83
|
+
on_axis_element_change,
|
|
84
|
+
on_legend_element_change,
|
|
85
|
+
deselect_element,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Dimensions Section
|
|
89
|
+
create_dimensions_section(state, dpg, o, on_value_change)
|
|
90
|
+
|
|
91
|
+
# Annotations Section
|
|
92
|
+
create_annotations_section(state, dpg, on_add_annotation, on_remove_annotation)
|
|
93
|
+
|
|
94
|
+
dpg.add_separator()
|
|
95
|
+
|
|
96
|
+
# Action buttons
|
|
97
|
+
dpg.add_button(
|
|
98
|
+
label="Update Preview",
|
|
99
|
+
callback=lambda: update_preview(state, dpg),
|
|
100
|
+
width=-1,
|
|
101
|
+
)
|
|
102
|
+
dpg.add_button(
|
|
103
|
+
label="Save to .manual.json",
|
|
104
|
+
callback=lambda: on_save_manual(state, dpg),
|
|
105
|
+
width=-1,
|
|
106
|
+
)
|
|
107
|
+
dpg.add_button(
|
|
108
|
+
label="Reset to Original",
|
|
109
|
+
callback=lambda: on_reset_overrides(state, dpg),
|
|
110
|
+
width=-1,
|
|
111
|
+
)
|
|
112
|
+
dpg.add_button(
|
|
113
|
+
label="Export PNG",
|
|
114
|
+
callback=lambda: on_export_png(state, dpg),
|
|
115
|
+
width=-1,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# EOF
|