eagleplot 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eagleplot/Home.py +362 -0
- eagleplot/__init__.py +19 -0
- eagleplot/annotations.py +215 -0
- eagleplot/assets/logo.ico +0 -0
- eagleplot/assets/logo.png +0 -0
- eagleplot/assets/nccu-horiz-logo.png +0 -0
- eagleplot/assets/nccu-wings.png +0 -0
- eagleplot/batch.py +446 -0
- eagleplot/cli.py +719 -0
- eagleplot/data_tools.py +849 -0
- eagleplot/example-data/Artificial/bimodal_distribution.csv +501 -0
- eagleplot/example-data/Artificial/correlation_matrix.npy +0 -0
- eagleplot/example-data/Artificial/gaussian_surface.npy +0 -0
- eagleplot/example-data/Artificial/grouped_categorical.csv +241 -0
- eagleplot/example-data/Artificial/ir_spectrum.csv +1802 -0
- eagleplot/example-data/Artificial/molecular_properties.csv +121 -0
- eagleplot/example-data/Artificial/normal_distribution.csv +401 -0
- eagleplot/example-data/Artificial/reaction_kinetics.csv +121 -0
- eagleplot/example-data/Artificial/scatter_correlation.csv +201 -0
- eagleplot/example-data/Artificial/sine_cosine_waves.csv +301 -0
- eagleplot/gallery/box_plot.png +0 -0
- eagleplot/gallery/contour_plot.png +0 -0
- eagleplot/gallery/distribution_plot.png +0 -0
- eagleplot/gallery/heatmap.png +0 -0
- eagleplot/gallery/histogram.png +0 -0
- eagleplot/gallery/interactive_3d_surface.png +0 -0
- eagleplot/gallery/interactive_heatmap.png +0 -0
- eagleplot/gallery/interactive_histogram.png +0 -0
- eagleplot/gallery/interactive_line.png +0 -0
- eagleplot/gallery/interactive_scatter.png +0 -0
- eagleplot/gallery/line_plot.png +0 -0
- eagleplot/gallery/manifest.json +196 -0
- eagleplot/gallery/regression_plot.png +0 -0
- eagleplot/gallery/scatter_plot.png +0 -0
- eagleplot/io.py +1008 -0
- eagleplot/math.py +1210 -0
- eagleplot/molecular/__init__.py +21 -0
- eagleplot/molecular/atom_data.py +287 -0
- eagleplot/molecular/parsers.py +747 -0
- eagleplot/nist.py +241 -0
- eagleplot/pages/10_Molecular_Viz.py +225 -0
- eagleplot/pages/11_Batch_Analysis.py +325 -0
- eagleplot/pages/12_Help.py +199 -0
- eagleplot/pages/13_Settings.py +388 -0
- eagleplot/pages/1_Data_Upload.py +521 -0
- eagleplot/pages/2_Quick_Plot.py +1693 -0
- eagleplot/pages/3_Analysis_Tools.py +2510 -0
- eagleplot/pages/4_Multi_Plot_Dashboard.py +791 -0
- eagleplot/pages/5_Advanced_Plotting.py +691 -0
- eagleplot/pages/6_Export_Results.py +442 -0
- eagleplot/pages/7_Gallery.py +201 -0
- eagleplot/pages/8_Data_Tools.py +434 -0
- eagleplot/pages/9_Spectroscopy.py +821 -0
- eagleplot/pages/__init__.py +8 -0
- eagleplot/peaks.py +513 -0
- eagleplot/plotting.py +2630 -0
- eagleplot/plugin_loader.py +208 -0
- eagleplot/report.py +448 -0
- eagleplot/signal.py +494 -0
- eagleplot/spectroscopy.py +830 -0
- eagleplot/utils/__init__.py +76 -0
- eagleplot/utils/access.py +77 -0
- eagleplot/utils/data_preview.py +352 -0
- eagleplot/utils/plot_config.py +2721 -0
- eagleplot/utils/session_state.py +535 -0
- eagleplot/utils/theming.py +312 -0
- eagleplot/utils/user_settings.py +294 -0
- eagleplot-3.0.0.dist-info/METADATA +304 -0
- eagleplot-3.0.0.dist-info/RECORD +73 -0
- eagleplot-3.0.0.dist-info/WHEEL +5 -0
- eagleplot-3.0.0.dist-info/entry_points.txt +2 -0
- eagleplot-3.0.0.dist-info/licenses/LICENSE +21 -0
- eagleplot-3.0.0.dist-info/top_level.txt +1 -0
eagleplot/Home.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"""Graphical User Interface Module - Main Entry Point.
|
|
2
|
+
|
|
3
|
+
This module provides the main entry point for the Streamlit-based GUI for
|
|
4
|
+
the EaglePlot toolkit. It provides an interactive interface for data
|
|
5
|
+
exploration, visualization, and analysis.
|
|
6
|
+
|
|
7
|
+
Navigation Structure
|
|
8
|
+
--------------------
|
|
9
|
+
Home
|
|
10
|
+
Data Upload
|
|
11
|
+
Plot
|
|
12
|
+
Basic — Quick Plot (27 plot types, Matplotlib/Seaborn/Plotly)
|
|
13
|
+
Multiplot — Multi-Plot Dashboard (grid layouts, axis sharing)
|
|
14
|
+
Advanced
|
|
15
|
+
↳ Advanced Plotting — Seaborn statistical + Plotly interactive
|
|
16
|
+
↳ Spectroscopy — IR/Raman, NMR, UV-Vis, Mass Spec + NIST
|
|
17
|
+
↳ Molecular Viz — 3D structure and vibrational modes
|
|
18
|
+
Analyze
|
|
19
|
+
Single — Analysis Tools (stats, curve fit, signal, peaks, …)
|
|
20
|
+
Batch — Batch Analysis + workflow presets
|
|
21
|
+
Export
|
|
22
|
+
Gallery
|
|
23
|
+
Help
|
|
24
|
+
Settings
|
|
25
|
+
|
|
26
|
+
Usage
|
|
27
|
+
-----
|
|
28
|
+
streamlit run eagleplot/Home.py
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import base64
|
|
32
|
+
import sys
|
|
33
|
+
import streamlit as st
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
37
|
+
sys.path.insert(0, str(_REPO_ROOT))
|
|
38
|
+
|
|
39
|
+
_PAGES_DIR = Path(__file__).resolve().parent / "pages"
|
|
40
|
+
|
|
41
|
+
# Assets are inside the package (not at the repo root) so that they survive
|
|
42
|
+
# `pip install` -- see audit A-09. Resolving them relative to the repo root meant
|
|
43
|
+
# a pip-installed GUI came up with no logo and no NCCU branding.
|
|
44
|
+
_ASSETS_DIR = Path(__file__).resolve().parent / "assets"
|
|
45
|
+
_LOGO_PNG = _ASSETS_DIR / "logo.png"
|
|
46
|
+
_NCCU_HORIZ = _ASSETS_DIR / "nccu-horiz-logo.png"
|
|
47
|
+
_NCCU_WINGS = _ASSETS_DIR / "nccu-wings.png"
|
|
48
|
+
|
|
49
|
+
from eagleplot import __version__ # noqa: E402
|
|
50
|
+
from eagleplot.utils import initialize_session_state, get_session_summary # noqa: E402
|
|
51
|
+
from eagleplot.utils.theming import app_css # noqa: E402
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
from PIL import Image as _PILImage
|
|
55
|
+
|
|
56
|
+
_page_icon = _PILImage.open(_LOGO_PNG) if _LOGO_PNG.exists() else "📊"
|
|
57
|
+
except ImportError:
|
|
58
|
+
_page_icon = "📊"
|
|
59
|
+
|
|
60
|
+
# ── Page configuration (must be the first Streamlit call) ─────────────────────
|
|
61
|
+
st.set_page_config(
|
|
62
|
+
page_title="EaglePlot",
|
|
63
|
+
page_icon=_page_icon,
|
|
64
|
+
layout="wide",
|
|
65
|
+
initial_sidebar_state="expanded",
|
|
66
|
+
menu_items={
|
|
67
|
+
"About": (
|
|
68
|
+
f"**EaglePlot** v{__version__}\n\n"
|
|
69
|
+
"Scientific data visualization and analysis toolkit for research "
|
|
70
|
+
"and teaching in computational science — developed at North Carolina "
|
|
71
|
+
"Central University.\n\n"
|
|
72
|
+
"**Institution:** NCCU Department of Chemistry and Biochemistry"
|
|
73
|
+
)
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# ── App-wide styling, derived from the active theme ───────────────────────────
|
|
78
|
+
# Colours come from eagleplot.utils.theming.app_css() rather than being hardcoded, so
|
|
79
|
+
# the stylesheet follows whatever the user selects in Settings. Previously the NCCU
|
|
80
|
+
# dark palette was baked in as literals, which left the hero subtitle
|
|
81
|
+
# white-on-white on a light theme (A-17).
|
|
82
|
+
st.markdown(app_css(), unsafe_allow_html=True)
|
|
83
|
+
|
|
84
|
+
initialize_session_state()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ── Home page content ─────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _home_page() -> None:
|
|
91
|
+
"""Render the Dashboard / Help landing page."""
|
|
92
|
+
tab_dash, tab_help = st.tabs(["Dashboard", "Help"])
|
|
93
|
+
|
|
94
|
+
with tab_dash:
|
|
95
|
+
# ── Hero ──────────────────────────────────────────────────────────────
|
|
96
|
+
_logo_col, _title_col = st.columns([1, 5], vertical_alignment="center")
|
|
97
|
+
with _logo_col:
|
|
98
|
+
if _LOGO_PNG.exists():
|
|
99
|
+
_b64 = base64.b64encode(_LOGO_PNG.read_bytes()).decode()
|
|
100
|
+
st.markdown(
|
|
101
|
+
f'<img src="data:image/png;base64,{_b64}" alt="EaglePlot logo"'
|
|
102
|
+
' style="width:110px;height:auto;display:block;">',
|
|
103
|
+
unsafe_allow_html=True,
|
|
104
|
+
)
|
|
105
|
+
with _title_col:
|
|
106
|
+
st.markdown(
|
|
107
|
+
"<h1 style='margin-bottom: 0.1rem;'>EaglePlot</h1>",
|
|
108
|
+
unsafe_allow_html=True,
|
|
109
|
+
)
|
|
110
|
+
st.markdown(
|
|
111
|
+
"<p class='eagleplot-muted'>"
|
|
112
|
+
"Scientific data visualization and analysis "
|
|
113
|
+
f" · v{__version__}"
|
|
114
|
+
"</p>",
|
|
115
|
+
unsafe_allow_html=True,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
st.divider()
|
|
119
|
+
|
|
120
|
+
# ── Session metrics ───────────────────────────────────────────────────
|
|
121
|
+
summary = get_session_summary()
|
|
122
|
+
col1, col2, col3 = st.columns(3)
|
|
123
|
+
col1.metric("Datasets", summary["num_datasets"])
|
|
124
|
+
col2.metric("Plots", summary["num_plots"])
|
|
125
|
+
col3.metric("Analyses", summary["num_analyses"])
|
|
126
|
+
|
|
127
|
+
if summary["current_dataset"]:
|
|
128
|
+
st.success(f"Active dataset: **{summary['current_dataset']}**")
|
|
129
|
+
st.page_link(basic_pg, label="Go to Quick Plot →")
|
|
130
|
+
else:
|
|
131
|
+
st.info("No dataset loaded.")
|
|
132
|
+
st.page_link(data_pg, label="Upload Data →")
|
|
133
|
+
|
|
134
|
+
with tab_help:
|
|
135
|
+
st.markdown("## Quick Start")
|
|
136
|
+
st.markdown("""
|
|
137
|
+
1. **Upload Data** — Go to *Data Upload* to load a CSV, Excel, NumPy, or other file.
|
|
138
|
+
Or load a built-in example dataset.
|
|
139
|
+
2. **Plot** — *Plot → Basic* lets you choose from 27 plot types and configure them
|
|
140
|
+
interactively.
|
|
141
|
+
3. **Analyse** — *Analyze → Single* has curve fitting, statistics, signal processing,
|
|
142
|
+
peak analysis, and more.
|
|
143
|
+
4. **Export** — *Export* saves plots as PNG/PDF/SVG and data in multiple formats.
|
|
144
|
+
""")
|
|
145
|
+
|
|
146
|
+
st.markdown("## Supported File Formats")
|
|
147
|
+
st.markdown("""
|
|
148
|
+
| Format | Extensions |
|
|
149
|
+
|---|---|
|
|
150
|
+
| Tabular | CSV, TSV, Excel (.xlsx/.xls), JSON, Parquet |
|
|
151
|
+
| NumPy | .npy, .npz |
|
|
152
|
+
| Python | Pickle (.pkl) |
|
|
153
|
+
| Spectroscopy | JCAMP-DX (.jdx/.dx), SPC (.spc), ASC (.asc) |
|
|
154
|
+
| Scientific | HDF5 (.h5/.hdf5), NetCDF (.nc/.cdf), mzML (.mzml/.mzxml) |
|
|
155
|
+
""")
|
|
156
|
+
|
|
157
|
+
st.markdown("## Tips")
|
|
158
|
+
col1, col2 = st.columns(2)
|
|
159
|
+
with col1:
|
|
160
|
+
with st.expander("Session management"):
|
|
161
|
+
st.markdown("""
|
|
162
|
+
- Datasets persist across all pages during a session
|
|
163
|
+
- Use *Export → Save Session* to preserve your work between restarts
|
|
164
|
+
- Saved sessions can be reloaded from the same page
|
|
165
|
+
""")
|
|
166
|
+
with st.expander("Performance"):
|
|
167
|
+
st.markdown("""
|
|
168
|
+
- Files >50 MB or >10k rows get a downsampled preview automatically
|
|
169
|
+
- Interactive Plotly plots are slower than Matplotlib for large data
|
|
170
|
+
- Clear plot history periodically to free memory
|
|
171
|
+
""")
|
|
172
|
+
with col2:
|
|
173
|
+
with st.expander("Choosing a plot type"):
|
|
174
|
+
st.markdown("""
|
|
175
|
+
- **Histogram** — single-variable distributions
|
|
176
|
+
- **Scatter** — relationship between two variables
|
|
177
|
+
- **Line** — time series or ordered data
|
|
178
|
+
- **Box / Violin** — compare distributions across groups
|
|
179
|
+
- **Heatmap** — 2D matrix or correlation data
|
|
180
|
+
- **Ternary** — 3-component compositions
|
|
181
|
+
""")
|
|
182
|
+
with st.expander("Publication figures"):
|
|
183
|
+
st.markdown("""
|
|
184
|
+
- Apply *Publication Style* in the style sidebar for paper-ready figures
|
|
185
|
+
- Export at 300 DPI from *Export*
|
|
186
|
+
- Use SVG export for vector graphics in manuscripts
|
|
187
|
+
""")
|
|
188
|
+
|
|
189
|
+
st.markdown("## About")
|
|
190
|
+
st.markdown(f"""
|
|
191
|
+
**EaglePlot** is developed at North Carolina Central University for research and teaching
|
|
192
|
+
in computational science.
|
|
193
|
+
Built with Streamlit · NumPy · Pandas · Matplotlib · Seaborn · Plotly · SciPy.
|
|
194
|
+
Version {__version__}
|
|
195
|
+
""")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ── Page objects ──────────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
home_pg = st.Page(_home_page, title="Home", default=True)
|
|
201
|
+
data_pg = st.Page(
|
|
202
|
+
str(_PAGES_DIR / "1_Data_Upload.py"),
|
|
203
|
+
title="Data Upload",
|
|
204
|
+
url_path="data-upload",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Plot group
|
|
208
|
+
basic_pg = st.Page(
|
|
209
|
+
str(_PAGES_DIR / "2_Quick_Plot.py"),
|
|
210
|
+
title="Basic",
|
|
211
|
+
url_path="plot-basic",
|
|
212
|
+
)
|
|
213
|
+
multi_pg = st.Page(
|
|
214
|
+
str(_PAGES_DIR / "4_Multi_Plot_Dashboard.py"),
|
|
215
|
+
title="Multiplot",
|
|
216
|
+
url_path="plot-multiplot",
|
|
217
|
+
)
|
|
218
|
+
adv_plt_pg = st.Page(
|
|
219
|
+
str(_PAGES_DIR / "5_Advanced_Plotting.py"),
|
|
220
|
+
title="Advanced Plotting",
|
|
221
|
+
url_path="plot-advanced",
|
|
222
|
+
)
|
|
223
|
+
spec_pg = st.Page(
|
|
224
|
+
str(_PAGES_DIR / "9_Spectroscopy.py"),
|
|
225
|
+
title="Spectroscopy",
|
|
226
|
+
url_path="plot-spectroscopy",
|
|
227
|
+
)
|
|
228
|
+
molvis_pg = st.Page(
|
|
229
|
+
str(_PAGES_DIR / "10_Molecular_Viz.py"),
|
|
230
|
+
title="Molecular Viz",
|
|
231
|
+
url_path="plot-molecular-viz",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Analyze group
|
|
235
|
+
single_pg = st.Page(
|
|
236
|
+
str(_PAGES_DIR / "3_Analysis_Tools.py"),
|
|
237
|
+
title="Single",
|
|
238
|
+
url_path="analyze-single",
|
|
239
|
+
)
|
|
240
|
+
batch_pg = st.Page(
|
|
241
|
+
str(_PAGES_DIR / "11_Batch_Analysis.py"),
|
|
242
|
+
title="Batch",
|
|
243
|
+
url_path="analyze-batch",
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Top-level pages
|
|
247
|
+
export_pg = st.Page(
|
|
248
|
+
str(_PAGES_DIR / "6_Export_Results.py"),
|
|
249
|
+
title="Export",
|
|
250
|
+
url_path="export",
|
|
251
|
+
)
|
|
252
|
+
gallery_pg = st.Page(
|
|
253
|
+
str(_PAGES_DIR / "7_Gallery.py"),
|
|
254
|
+
title="Gallery",
|
|
255
|
+
url_path="gallery",
|
|
256
|
+
)
|
|
257
|
+
help_pg = st.Page(
|
|
258
|
+
str(_PAGES_DIR / "12_Help.py"),
|
|
259
|
+
title="Help",
|
|
260
|
+
url_path="help",
|
|
261
|
+
)
|
|
262
|
+
settings_pg = st.Page(
|
|
263
|
+
str(_PAGES_DIR / "13_Settings.py"),
|
|
264
|
+
title="Settings",
|
|
265
|
+
url_path="settings",
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
datatools_pg = st.Page(
|
|
269
|
+
str(_PAGES_DIR / "8_Data_Tools.py"),
|
|
270
|
+
title="Data Tools",
|
|
271
|
+
url_path="analyze-data-tools",
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# ── Navigation (position="hidden" — custom sidebar built below) ───────────────
|
|
275
|
+
|
|
276
|
+
_PLOT_PAGES = {basic_pg, multi_pg, adv_plt_pg, spec_pg, molvis_pg}
|
|
277
|
+
_ANALYZE_PAGES = {single_pg, batch_pg, datatools_pg}
|
|
278
|
+
|
|
279
|
+
pg = st.navigation(
|
|
280
|
+
[
|
|
281
|
+
home_pg,
|
|
282
|
+
data_pg,
|
|
283
|
+
basic_pg,
|
|
284
|
+
multi_pg,
|
|
285
|
+
adv_plt_pg,
|
|
286
|
+
spec_pg,
|
|
287
|
+
molvis_pg,
|
|
288
|
+
single_pg,
|
|
289
|
+
batch_pg,
|
|
290
|
+
datatools_pg,
|
|
291
|
+
export_pg,
|
|
292
|
+
gallery_pg,
|
|
293
|
+
help_pg,
|
|
294
|
+
settings_pg,
|
|
295
|
+
],
|
|
296
|
+
position="hidden",
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# ── Custom sidebar navigation ─────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
_ADV_HEADER = "<p class='eagleplot-section-label'>Advanced</p>"
|
|
303
|
+
|
|
304
|
+
with st.sidebar:
|
|
305
|
+
if _LOGO_PNG.exists():
|
|
306
|
+
_sb_logo_b64 = base64.b64encode(_LOGO_PNG.read_bytes()).decode()
|
|
307
|
+
st.markdown(
|
|
308
|
+
f'<img src="data:image/png;base64,{_sb_logo_b64}" alt="EaglePlot logo"'
|
|
309
|
+
' style="width:72px;height:auto;display:block;margin:0.5rem auto 1rem;">',
|
|
310
|
+
unsafe_allow_html=True,
|
|
311
|
+
)
|
|
312
|
+
st.page_link(home_pg, label="Home")
|
|
313
|
+
st.page_link(data_pg, label="Data Upload")
|
|
314
|
+
|
|
315
|
+
with st.expander("**Plot**", expanded=pg in _PLOT_PAGES):
|
|
316
|
+
st.page_link(basic_pg, label="Basic")
|
|
317
|
+
st.page_link(multi_pg, label="Multiplot")
|
|
318
|
+
st.markdown(_ADV_HEADER, unsafe_allow_html=True)
|
|
319
|
+
st.page_link(adv_plt_pg, label="↳ Advanced Plotting")
|
|
320
|
+
st.page_link(spec_pg, label="↳ Spectroscopy")
|
|
321
|
+
st.page_link(molvis_pg, label="↳ Molecular Viz")
|
|
322
|
+
|
|
323
|
+
with st.expander("**Analyze**", expanded=pg in _ANALYZE_PAGES):
|
|
324
|
+
st.page_link(single_pg, label="Single")
|
|
325
|
+
st.page_link(batch_pg, label="Batch")
|
|
326
|
+
st.page_link(datatools_pg, label="Data Tools")
|
|
327
|
+
|
|
328
|
+
st.divider()
|
|
329
|
+
st.page_link(export_pg, label="Export")
|
|
330
|
+
st.page_link(gallery_pg, label="Gallery")
|
|
331
|
+
st.page_link(help_pg, label="Help")
|
|
332
|
+
st.page_link(settings_pg, label="Settings")
|
|
333
|
+
|
|
334
|
+
st.divider()
|
|
335
|
+
with st.expander("About", expanded=True):
|
|
336
|
+
if _NCCU_HORIZ.exists():
|
|
337
|
+
_horiz_b64 = base64.b64encode(_NCCU_HORIZ.read_bytes()).decode()
|
|
338
|
+
st.markdown(
|
|
339
|
+
f'<img src="data:image/png;base64,{_horiz_b64}"'
|
|
340
|
+
' alt="North Carolina Central University"'
|
|
341
|
+
' style="width:100%;height:auto;margin-bottom:0.4rem;">',
|
|
342
|
+
unsafe_allow_html=True,
|
|
343
|
+
)
|
|
344
|
+
if _NCCU_WINGS.exists():
|
|
345
|
+
_wings_b64 = base64.b64encode(_NCCU_WINGS.read_bytes()).decode()
|
|
346
|
+
st.markdown(
|
|
347
|
+
f'<img src="data:image/png;base64,{_wings_b64}"'
|
|
348
|
+
' alt="NCCU Eagle Wings emblem"'
|
|
349
|
+
' style="width:60%;height:auto;display:block;margin:0 auto 0.4rem;">',
|
|
350
|
+
unsafe_allow_html=True,
|
|
351
|
+
)
|
|
352
|
+
st.markdown(
|
|
353
|
+
"**EaglePlot** is a scientific data visualization and analysis "
|
|
354
|
+
"toolkit developed for research and teaching in computational "
|
|
355
|
+
"science at North Carolina Central University."
|
|
356
|
+
)
|
|
357
|
+
st.markdown("[NCCU Schultz Lab on GitHub](https://github.com/The-Schultz-Lab)")
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
# ── Execute the active page ───────────────────────────────────────────────────
|
|
361
|
+
|
|
362
|
+
pg.run()
|
eagleplot/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Modules Package.
|
|
2
|
+
|
|
3
|
+
This package contains the core functionality for the EaglePlot toolkit.
|
|
4
|
+
|
|
5
|
+
Available Modules
|
|
6
|
+
-----------------
|
|
7
|
+
io : Data input/output operations
|
|
8
|
+
math : Mathematical and statistical functions
|
|
9
|
+
plotting : Visualization and plotting tools
|
|
10
|
+
gui : Interactive GUI components
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Single source of truth for the version. `pyproject.toml` reads this via
|
|
14
|
+
# `[tool.setuptools.dynamic]`, and cli.py / Home.py / docs/conf.py import it, so
|
|
15
|
+
# the number cannot drift between the package, the CLI, and the GUI.
|
|
16
|
+
# See audit A-26.
|
|
17
|
+
__version__ = "3.0.0"
|
|
18
|
+
|
|
19
|
+
__all__ = ["io", "math", "plotting", "gui", "__version__"]
|
eagleplot/annotations.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Figure annotation helpers.
|
|
2
|
+
|
|
3
|
+
Provides ``apply_annotations`` for drawing overlay annotations
|
|
4
|
+
(reference lines, spans, text labels, and shapes) onto a Matplotlib Axes.
|
|
5
|
+
|
|
6
|
+
Used by Quick Plot (per-figure annotations) and the Multi-Panel Dashboard
|
|
7
|
+
(per-cell annotations before combined export).
|
|
8
|
+
|
|
9
|
+
Supported overlay types
|
|
10
|
+
-----------------------
|
|
11
|
+
``hline``
|
|
12
|
+
Horizontal reference line at *y*.
|
|
13
|
+
Keys: ``y``, ``color``, ``linestyle``, ``lw``, ``label``
|
|
14
|
+
|
|
15
|
+
``vline``
|
|
16
|
+
Vertical reference line at *x*.
|
|
17
|
+
Keys: ``x``, ``color``, ``linestyle``, ``lw``, ``label``
|
|
18
|
+
|
|
19
|
+
``hspan``
|
|
20
|
+
Horizontal band shading between *y1* and *y2*.
|
|
21
|
+
Keys: ``y1``, ``y2``, ``color``, ``alpha``, ``label``
|
|
22
|
+
|
|
23
|
+
``vspan``
|
|
24
|
+
Vertical band shading between *x1* and *x2*.
|
|
25
|
+
Keys: ``x1``, ``x2``, ``color``, ``alpha``, ``label``
|
|
26
|
+
|
|
27
|
+
``text``
|
|
28
|
+
Text label placed at (*x*, *y*), with optional annotating arrow.
|
|
29
|
+
Keys: ``x``, ``y``, ``text``, ``color``, ``fontsize``,
|
|
30
|
+
``arrow`` (bool), ``tx``, ``ty`` (arrow-tail coordinates)
|
|
31
|
+
|
|
32
|
+
``rectangle``
|
|
33
|
+
Filled/outlined rectangle spanning (x1, y1) → (x2, y2).
|
|
34
|
+
Keys: ``x1``, ``y1``, ``x2``, ``y2``,
|
|
35
|
+
``color``, ``facecolor``, ``lw``, ``alpha``, ``label``
|
|
36
|
+
|
|
37
|
+
``ellipse``
|
|
38
|
+
Filled/outlined ellipse centred at (*cx*, *cy*).
|
|
39
|
+
Keys: ``cx``, ``cy``, ``width``, ``height``,
|
|
40
|
+
``color``, ``facecolor``, ``lw``, ``alpha``, ``label``
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
from typing import TYPE_CHECKING
|
|
46
|
+
|
|
47
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
48
|
+
from matplotlib.axes import Axes
|
|
49
|
+
|
|
50
|
+
# Colour choices available in the GUI colour picker
|
|
51
|
+
ANNOTATION_COLORS: list[str] = [
|
|
52
|
+
"red",
|
|
53
|
+
"blue",
|
|
54
|
+
"green",
|
|
55
|
+
"orange",
|
|
56
|
+
"purple",
|
|
57
|
+
"black",
|
|
58
|
+
"gray",
|
|
59
|
+
"cyan",
|
|
60
|
+
"magenta",
|
|
61
|
+
"yellow",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
# Line-style choices available in the GUI selector
|
|
65
|
+
ANNOTATION_LINESTYLES: list[str] = ["--", "-", ":", "-."]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def apply_annotations(ax: "Axes", overlays: list) -> None: # noqa: C901
|
|
69
|
+
"""Apply a list of annotation overlays to a Matplotlib Axes.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
ax:
|
|
74
|
+
Target Matplotlib ``Axes`` object.
|
|
75
|
+
overlays:
|
|
76
|
+
Sequence of annotation dicts. Each dict **must** contain a
|
|
77
|
+
``'type'`` key; see module docstring for required/optional keys
|
|
78
|
+
per type. Unknown types are silently skipped.
|
|
79
|
+
"""
|
|
80
|
+
if not overlays:
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
for ann in overlays:
|
|
84
|
+
t = ann.get("type")
|
|
85
|
+
color: str = str(ann.get("color", "red"))
|
|
86
|
+
label = ann.get("label") or None
|
|
87
|
+
lw: float = float(ann.get("lw", 1.5))
|
|
88
|
+
ls: str = str(ann.get("linestyle", "--"))
|
|
89
|
+
alpha: float = float(ann.get("alpha", 0.3))
|
|
90
|
+
|
|
91
|
+
if t == "hline":
|
|
92
|
+
ax.axhline(
|
|
93
|
+
float(ann["y"]),
|
|
94
|
+
color=color,
|
|
95
|
+
linestyle=ls,
|
|
96
|
+
linewidth=lw,
|
|
97
|
+
label=label,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
elif t == "vline":
|
|
101
|
+
ax.axvline(
|
|
102
|
+
float(ann["x"]),
|
|
103
|
+
color=color,
|
|
104
|
+
linestyle=ls,
|
|
105
|
+
linewidth=lw,
|
|
106
|
+
label=label,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
elif t == "hspan":
|
|
110
|
+
ax.axhspan(
|
|
111
|
+
float(ann["y1"]),
|
|
112
|
+
float(ann["y2"]),
|
|
113
|
+
alpha=alpha,
|
|
114
|
+
color=color,
|
|
115
|
+
label=label,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
elif t == "vspan":
|
|
119
|
+
ax.axvspan(
|
|
120
|
+
float(ann["x1"]),
|
|
121
|
+
float(ann["x2"]),
|
|
122
|
+
alpha=alpha,
|
|
123
|
+
color=color,
|
|
124
|
+
label=label,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
elif t == "text":
|
|
128
|
+
kw: dict = {
|
|
129
|
+
"fontsize": int(ann.get("fontsize", 10)),
|
|
130
|
+
"color": color,
|
|
131
|
+
}
|
|
132
|
+
if ann.get("arrow"):
|
|
133
|
+
ax.annotate(
|
|
134
|
+
str(ann["text"]),
|
|
135
|
+
xy=(float(ann["x"]), float(ann["y"])),
|
|
136
|
+
xytext=(
|
|
137
|
+
float(ann.get("tx", ann["x"])),
|
|
138
|
+
float(ann.get("ty", ann["y"])),
|
|
139
|
+
),
|
|
140
|
+
arrowprops=dict(arrowstyle="->", color=color),
|
|
141
|
+
**kw,
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
ax.text(float(ann["x"]), float(ann["y"]), str(ann["text"]), **kw)
|
|
145
|
+
|
|
146
|
+
elif t == "rectangle":
|
|
147
|
+
from matplotlib.patches import Rectangle # local import
|
|
148
|
+
|
|
149
|
+
w = float(ann["x2"]) - float(ann["x1"])
|
|
150
|
+
h = float(ann["y2"]) - float(ann["y1"])
|
|
151
|
+
rect = Rectangle(
|
|
152
|
+
(float(ann["x1"]), float(ann["y1"])),
|
|
153
|
+
w,
|
|
154
|
+
h,
|
|
155
|
+
linewidth=lw,
|
|
156
|
+
edgecolor=color,
|
|
157
|
+
facecolor=ann.get("facecolor", "none"),
|
|
158
|
+
alpha=alpha,
|
|
159
|
+
label=label,
|
|
160
|
+
)
|
|
161
|
+
ax.add_patch(rect)
|
|
162
|
+
|
|
163
|
+
elif t == "ellipse":
|
|
164
|
+
from matplotlib.patches import Ellipse # local import
|
|
165
|
+
|
|
166
|
+
el = Ellipse(
|
|
167
|
+
(float(ann["cx"]), float(ann["cy"])),
|
|
168
|
+
float(ann["width"]),
|
|
169
|
+
float(ann["height"]),
|
|
170
|
+
linewidth=lw,
|
|
171
|
+
edgecolor=color,
|
|
172
|
+
facecolor=ann.get("facecolor", "none"),
|
|
173
|
+
alpha=alpha,
|
|
174
|
+
label=label,
|
|
175
|
+
)
|
|
176
|
+
ax.add_patch(el)
|
|
177
|
+
|
|
178
|
+
# Refresh legend if any overlay carries a label
|
|
179
|
+
if any(a.get("label") for a in overlays):
|
|
180
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
181
|
+
if handles:
|
|
182
|
+
ax.legend(handles, labels, loc="best")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def describe_overlay(ann: dict) -> str:
|
|
186
|
+
"""Return a human-readable one-line description of an annotation dict.
|
|
187
|
+
|
|
188
|
+
Used in the Quick Plot annotations list UI.
|
|
189
|
+
"""
|
|
190
|
+
t = ann.get("type", "unknown")
|
|
191
|
+
color = ann.get("color", "red")
|
|
192
|
+
label = ann.get("label", "")
|
|
193
|
+
suffix = f" [{label}]" if label else ""
|
|
194
|
+
|
|
195
|
+
if t == "hline":
|
|
196
|
+
return f"H-line y = {ann.get('y')} ({color}){suffix}"
|
|
197
|
+
if t == "vline":
|
|
198
|
+
return f"V-line x = {ann.get('x')} ({color}){suffix}"
|
|
199
|
+
if t == "hspan":
|
|
200
|
+
return f"H-span y = {ann.get('y1')} – {ann.get('y2')} ({color}){suffix}"
|
|
201
|
+
if t == "vspan":
|
|
202
|
+
return f"V-span x = {ann.get('x1')} – {ann.get('x2')} ({color}){suffix}"
|
|
203
|
+
if t == "text":
|
|
204
|
+
return f"Text '{ann.get('text')}' @ ({ann.get('x')}, {ann.get('y')}){suffix}"
|
|
205
|
+
if t == "rectangle":
|
|
206
|
+
return (
|
|
207
|
+
f"Rect ({ann.get('x1')}, {ann.get('y1')}) → "
|
|
208
|
+
f"({ann.get('x2')}, {ann.get('y2')}) ({color}){suffix}"
|
|
209
|
+
)
|
|
210
|
+
if t == "ellipse":
|
|
211
|
+
return (
|
|
212
|
+
f"Ellipse c=({ann.get('cx')}, {ann.get('cy')}) "
|
|
213
|
+
f"w={ann.get('width')} h={ann.get('height')} ({color}){suffix}"
|
|
214
|
+
)
|
|
215
|
+
return f"{t}{suffix}"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|