eagleplot 3.0.0__py3-none-any.whl → 3.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. eagleplot/Home.py +448 -362
  2. eagleplot/__init__.py +19 -19
  3. eagleplot/annotations.py +215 -215
  4. eagleplot/assets/logo.ico +0 -0
  5. eagleplot/assets/logo.png +0 -0
  6. eagleplot/batch.py +446 -446
  7. eagleplot/cli.py +719 -719
  8. eagleplot/data_tools.py +849 -849
  9. eagleplot/example-data/Artificial/bimodal_distribution.csv +501 -501
  10. eagleplot/example-data/Artificial/grouped_categorical.csv +241 -241
  11. eagleplot/example-data/Artificial/ir_spectrum.csv +1802 -1802
  12. eagleplot/example-data/Artificial/molecular_properties.csv +121 -121
  13. eagleplot/example-data/Artificial/normal_distribution.csv +401 -401
  14. eagleplot/example-data/Artificial/reaction_kinetics.csv +121 -121
  15. eagleplot/example-data/Artificial/scatter_correlation.csv +201 -201
  16. eagleplot/example-data/Artificial/sine_cosine_waves.csv +301 -301
  17. eagleplot/gallery/manifest.json +195 -195
  18. eagleplot/io.py +1021 -1008
  19. eagleplot/math.py +1210 -1210
  20. eagleplot/molecular/__init__.py +21 -21
  21. eagleplot/molecular/atom_data.py +287 -287
  22. eagleplot/molecular/parsers.py +747 -747
  23. eagleplot/nist.py +241 -241
  24. eagleplot/pages/10_Molecular_Viz.py +251 -225
  25. eagleplot/pages/11_Batch_Analysis.py +360 -325
  26. eagleplot/pages/12_Help.py +199 -199
  27. eagleplot/pages/13_Settings.py +396 -388
  28. eagleplot/pages/1_Data_Upload.py +531 -521
  29. eagleplot/pages/2_Quick_Plot.py +1721 -1693
  30. eagleplot/pages/3_Analysis_Tools.py +2622 -2510
  31. eagleplot/pages/4_Multi_Plot_Dashboard.py +803 -791
  32. eagleplot/pages/5_Advanced_Plotting.py +736 -691
  33. eagleplot/pages/6_Export_Results.py +519 -442
  34. eagleplot/pages/7_Gallery.py +203 -201
  35. eagleplot/pages/8_Data_Tools.py +434 -434
  36. eagleplot/pages/9_Spectroscopy.py +830 -821
  37. eagleplot/pages/__init__.py +8 -8
  38. eagleplot/peaks.py +513 -513
  39. eagleplot/plotting.py +2648 -2630
  40. eagleplot/plugin_loader.py +208 -208
  41. eagleplot/report.py +448 -448
  42. eagleplot/signal.py +494 -494
  43. eagleplot/spectroscopy.py +830 -830
  44. eagleplot/utils/__init__.py +97 -76
  45. eagleplot/utils/access.py +96 -77
  46. eagleplot/utils/data_preview.py +352 -352
  47. eagleplot/utils/figures.py +200 -0
  48. eagleplot/utils/host_folders.py +123 -0
  49. eagleplot/utils/plot_config.py +2721 -2721
  50. eagleplot/utils/runtime.py +265 -0
  51. eagleplot/utils/session_state.py +550 -535
  52. eagleplot/utils/theming.py +315 -312
  53. eagleplot/utils/user_settings.py +305 -294
  54. {eagleplot-3.0.0.dist-info → eagleplot-3.2.0.dist-info}/METADATA +311 -304
  55. eagleplot-3.2.0.dist-info/RECORD +76 -0
  56. eagleplot-3.0.0.dist-info/RECORD +0 -73
  57. {eagleplot-3.0.0.dist-info → eagleplot-3.2.0.dist-info}/WHEEL +0 -0
  58. {eagleplot-3.0.0.dist-info → eagleplot-3.2.0.dist-info}/entry_points.txt +0 -0
  59. {eagleplot-3.0.0.dist-info → eagleplot-3.2.0.dist-info}/licenses/LICENSE +0 -0
  60. {eagleplot-3.0.0.dist-info → eagleplot-3.2.0.dist-info}/top_level.txt +0 -0
eagleplot/Home.py CHANGED
@@ -1,362 +1,448 @@
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"&nbsp;&middot;&nbsp; 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()
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
+ Quick Plot — 27 plot types, Matplotlib/Seaborn/Plotly
13
+ Dashboard — 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
+ Analysis Tools — stats, curve fit, signal, peaks, …
20
+ Batch Analysis — the same across several datasets, + 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 runtime # noqa: E402
51
+ from eagleplot.utils import initialize_session_state, get_session_summary # noqa: E402
52
+ from eagleplot.utils.theming import app_css # noqa: E402
53
+
54
+ try:
55
+ from PIL import Image as _PILImage
56
+
57
+ _page_icon = _PILImage.open(_LOGO_PNG) if _LOGO_PNG.exists() else "📊"
58
+ except ImportError:
59
+ _page_icon = "📊"
60
+
61
+ # ── Page configuration (must be the first Streamlit call) ─────────────────────
62
+ st.set_page_config(
63
+ page_title="EaglePlot",
64
+ page_icon=_page_icon,
65
+ layout="wide",
66
+ # "auto": open on wide screens, closed on phones (where it would cover the page).
67
+ initial_sidebar_state="auto",
68
+ menu_items={
69
+ "About": (
70
+ f"**EaglePlot** v{__version__}\n\n"
71
+ "Scientific data visualization and analysis toolkit for research "
72
+ "and teaching in computational science — developed at North Carolina "
73
+ "Central University.\n\n"
74
+ "**Institution:** NCCU Department of Chemistry and Biochemistry"
75
+ )
76
+ },
77
+ )
78
+
79
+ # ── App-wide styling, derived from the active theme ───────────────────────────
80
+ # Colours come from eagleplot.utils.theming.app_css() rather than being hardcoded, so
81
+ # the stylesheet follows whatever the user selects in Settings. Previously the NCCU
82
+ # dark palette was baked in as literals, which left the hero subtitle
83
+ # white-on-white on a light theme (A-17).
84
+ st.markdown(app_css(), unsafe_allow_html=True)
85
+
86
+ initialize_session_state()
87
+
88
+
89
+ # ── Home page content ─────────────────────────────────────────────────────────
90
+
91
+
92
+ # Examples offered on the first screen: one click loads the data and opens Quick Plot.
93
+ _EXAMPLE_DIR = Path(__file__).resolve().parent / "example-data" / "Artificial"
94
+ _STARTERS = [
95
+ ("sine_cosine_waves.csv", "Sine & cosine waves", "Line plot of three series"),
96
+ ("ir_spectrum.csv", "IR spectrum", "A spectrum from 4000 to 400 cm⁻¹"),
97
+ ("scatter_correlation.csv", "Scatter correlation", "Yield vs reaction time"),
98
+ ]
99
+
100
+
101
+ def _get_started() -> None:
102
+ """First screen for a session without data: one-click examples, or upload."""
103
+ st.markdown("### Get started")
104
+ st.caption("Try EaglePlot with an example dataset, or bring your own data.")
105
+ cols = st.columns(len(_STARTERS))
106
+ for col, (fname, title, desc) in zip(cols, _STARTERS):
107
+ path = _EXAMPLE_DIR / fname
108
+ if not path.exists():
109
+ continue
110
+ with col, st.container(border=True):
111
+ st.markdown(f"**{title}**")
112
+ st.caption(desc)
113
+ if st.button(
114
+ "Plot this example",
115
+ key=f"home_try_{fname}",
116
+ type="primary" if fname == _STARTERS[0][0] else "secondary",
117
+ width="stretch",
118
+ ):
119
+ from eagleplot.io import load_data
120
+ from eagleplot.utils import add_dataset
121
+
122
+ add_dataset(
123
+ fname,
124
+ load_data(path),
125
+ metadata={"source": "example", "file_type": path.suffix},
126
+ )
127
+ st.switch_page(basic_pg)
128
+ with st.container(border=True):
129
+ _up_col, _ex_col = st.columns(2)
130
+ with _up_col:
131
+ st.markdown("**Your own data**")
132
+ st.caption("CSV, Excel, JSON, NumPy, spectra (JCAMP-DX) and more.")
133
+ if st.button("Upload a file", key="home_upload", width="stretch"):
134
+ st.switch_page(data_pg)
135
+ with _ex_col:
136
+ st.markdown("**More examples**")
137
+ st.caption("Distributions, kinetics, molecular properties, 2-D arrays.")
138
+ if st.button("Browse all examples", key="home_examples", width="stretch"):
139
+ st.switch_page(data_pg)
140
+
141
+
142
+ def _home_page() -> None:
143
+ """Render the Dashboard / Help landing page."""
144
+ tab_dash, tab_help = st.tabs(["Dashboard", "Help"])
145
+
146
+ with tab_dash:
147
+ # ── Hero ──────────────────────────────────────────────────────────────
148
+ _logo_col, _title_col = st.columns([1, 5], vertical_alignment="center")
149
+ with _logo_col:
150
+ if _LOGO_PNG.exists():
151
+ _b64 = base64.b64encode(_LOGO_PNG.read_bytes()).decode()
152
+ st.markdown(
153
+ f'<img src="data:image/png;base64,{_b64}" alt="EaglePlot logo"'
154
+ ' style="width:110px;height:auto;display:block;">',
155
+ unsafe_allow_html=True,
156
+ )
157
+ with _title_col:
158
+ st.markdown(
159
+ "<h1 style='margin-bottom: 0.1rem;'>EaglePlot</h1>",
160
+ unsafe_allow_html=True,
161
+ )
162
+ st.markdown(
163
+ "<p class='eagleplot-muted'>"
164
+ "Scientific data visualization and analysis "
165
+ f"&nbsp;&middot;&nbsp; v{__version__}"
166
+ "</p>",
167
+ unsafe_allow_html=True,
168
+ )
169
+
170
+ st.divider()
171
+
172
+ summary = get_session_summary()
173
+ if summary["num_datasets"] == 0:
174
+ _get_started()
175
+ else:
176
+ # ── Session metrics ───────────────────────────────────────────────
177
+ col1, col2, col3 = st.columns(3)
178
+ col1.metric("Datasets", summary["num_datasets"])
179
+ col2.metric("Plots", summary["num_plots"])
180
+ col3.metric("Analyses", summary["num_analyses"])
181
+ if summary["current_dataset"]:
182
+ st.success(f"Active dataset: **{summary['current_dataset']}**")
183
+ _go_col, _more_col, _ = st.columns([1, 1, 2])
184
+ with _go_col:
185
+ st.page_link(basic_pg, label="Go to Quick Plot →")
186
+ with _more_col:
187
+ st.page_link(data_pg, label="Load more data →")
188
+
189
+ with tab_help:
190
+ st.markdown("## Quick Start")
191
+ st.markdown("""
192
+ 1. **Upload Data** — Go to *Data Upload* to load a CSV, Excel, NumPy, or other file.
193
+ Or load a built-in example dataset.
194
+ 2. **Plot** — *Plot → Quick Plot* lets you choose from 27 plot types and configure
195
+ them interactively.
196
+ 3. **Analyse** — *Analyze → Analysis Tools* has curve fitting, statistics, signal
197
+ processing, peak analysis, and more.
198
+ 4. **Export** — *Export* saves plots as PNG/PDF/SVG and data in multiple formats.
199
+ """)
200
+
201
+ st.markdown("## Supported File Formats")
202
+ st.markdown("""
203
+ | Format | Extensions |
204
+ |---|---|
205
+ | Tabular | CSV, TSV, Excel (.xlsx/.xls), JSON, Parquet |
206
+ | NumPy | .npy, .npz |
207
+ | Python | Pickle (.pkl) |
208
+ | Spectroscopy | JCAMP-DX (.jdx/.dx), SPC (.spc), ASC (.asc) |
209
+ | Scientific | HDF5 (.h5/.hdf5), NetCDF (.nc/.cdf), mzML (.mzml/.mzxml) |
210
+ """)
211
+
212
+ st.markdown("## Tips")
213
+ col1, col2 = st.columns(2)
214
+ with col1:
215
+ with st.expander("Session management"):
216
+ st.markdown("""
217
+ - Datasets persist across all pages during a session
218
+ - Use *Export → Save Session* to preserve your work between restarts
219
+ - Saved sessions can be reloaded from the same page
220
+ """)
221
+ with st.expander("Performance"):
222
+ st.markdown("""
223
+ - Files >50 MB or >10k rows get a downsampled preview automatically
224
+ - Interactive Plotly plots are slower than Matplotlib for large data
225
+ - Clear plot history periodically to free memory
226
+ """)
227
+ with col2:
228
+ with st.expander("Choosing a plot type"):
229
+ st.markdown("""
230
+ - **Histogram** — single-variable distributions
231
+ - **Scatter** — relationship between two variables
232
+ - **Line** — time series or ordered data
233
+ - **Box / Violin** — compare distributions across groups
234
+ - **Heatmap** — 2D matrix or correlation data
235
+ - **Ternary** — 3-component compositions
236
+ """)
237
+ with st.expander("Publication figures"):
238
+ st.markdown("""
239
+ - Apply *Publication Style* in the style sidebar for paper-ready figures
240
+ - Export at 300 DPI from *Export*
241
+ - Use SVG export for vector graphics in manuscripts
242
+ """)
243
+
244
+ st.markdown("## About")
245
+ st.markdown(f"""
246
+ **EaglePlot** is developed at North Carolina Central University for research and teaching
247
+ in computational science.
248
+ Built with Streamlit · NumPy · Pandas · Matplotlib · Seaborn · Plotly · SciPy.
249
+ Version {__version__}
250
+ """)
251
+
252
+
253
+ # ── Page objects ──────────────────────────────────────────────────────────────
254
+
255
+ home_pg = st.Page(_home_page, title="Home", default=True)
256
+ data_pg = st.Page(
257
+ str(_PAGES_DIR / "1_Data_Upload.py"),
258
+ title="Data Upload",
259
+ url_path="data-upload",
260
+ )
261
+
262
+ # Plot group
263
+ basic_pg = st.Page(
264
+ str(_PAGES_DIR / "2_Quick_Plot.py"),
265
+ title="Quick Plot",
266
+ url_path="plot-basic",
267
+ )
268
+ multi_pg = st.Page(
269
+ str(_PAGES_DIR / "4_Multi_Plot_Dashboard.py"),
270
+ title="Dashboard",
271
+ url_path="plot-multiplot",
272
+ )
273
+ adv_plt_pg = st.Page(
274
+ str(_PAGES_DIR / "5_Advanced_Plotting.py"),
275
+ title="Advanced Plotting",
276
+ url_path="plot-advanced",
277
+ )
278
+ spec_pg = st.Page(
279
+ str(_PAGES_DIR / "9_Spectroscopy.py"),
280
+ title="Spectroscopy",
281
+ url_path="plot-spectroscopy",
282
+ )
283
+ molvis_pg = st.Page(
284
+ str(_PAGES_DIR / "10_Molecular_Viz.py"),
285
+ title="Molecular Viz",
286
+ url_path="plot-molecular-viz",
287
+ )
288
+
289
+ # Analyze group
290
+ single_pg = st.Page(
291
+ str(_PAGES_DIR / "3_Analysis_Tools.py"),
292
+ title="Analysis Tools",
293
+ url_path="analyze-single",
294
+ )
295
+ batch_pg = st.Page(
296
+ str(_PAGES_DIR / "11_Batch_Analysis.py"),
297
+ title="Batch Analysis",
298
+ url_path="analyze-batch",
299
+ )
300
+
301
+ # Top-level pages
302
+ export_pg = st.Page(
303
+ str(_PAGES_DIR / "6_Export_Results.py"),
304
+ title="Export",
305
+ url_path="export",
306
+ )
307
+ gallery_pg = st.Page(
308
+ str(_PAGES_DIR / "7_Gallery.py"),
309
+ title="Gallery",
310
+ url_path="gallery",
311
+ )
312
+ help_pg = st.Page(
313
+ str(_PAGES_DIR / "12_Help.py"),
314
+ title="Help",
315
+ url_path="help",
316
+ )
317
+ settings_pg = st.Page(
318
+ str(_PAGES_DIR / "13_Settings.py"),
319
+ title="Settings",
320
+ url_path="settings",
321
+ )
322
+
323
+ datatools_pg = st.Page(
324
+ str(_PAGES_DIR / "8_Data_Tools.py"),
325
+ title="Data Tools",
326
+ url_path="analyze-data-tools",
327
+ )
328
+
329
+ # ── Navigation (position="hidden" — custom sidebar built below) ───────────────
330
+
331
+ _PLOT_PAGES = {basic_pg, multi_pg, adv_plt_pg, spec_pg, molvis_pg}
332
+ _ANALYZE_PAGES = {single_pg, batch_pg, datatools_pg}
333
+
334
+ pg = st.navigation(
335
+ [
336
+ home_pg,
337
+ data_pg,
338
+ basic_pg,
339
+ multi_pg,
340
+ adv_plt_pg,
341
+ spec_pg,
342
+ molvis_pg,
343
+ single_pg,
344
+ batch_pg,
345
+ datatools_pg,
346
+ export_pg,
347
+ gallery_pg,
348
+ help_pg,
349
+ settings_pg,
350
+ ],
351
+ position="hidden",
352
+ )
353
+
354
+
355
+ # ── Custom sidebar navigation ─────────────────────────────────────────────────
356
+
357
+ _ADV_HEADER = "<p class='eagleplot-section-label'>Advanced</p>"
358
+
359
+ with st.sidebar:
360
+ if _LOGO_PNG.exists():
361
+ _sb_logo_b64 = base64.b64encode(_LOGO_PNG.read_bytes()).decode()
362
+ st.markdown(
363
+ f'<img src="data:image/png;base64,{_sb_logo_b64}" alt="EaglePlot logo"'
364
+ ' style="width:72px;height:auto;display:block;margin:0.5rem auto 1rem;">',
365
+ unsafe_allow_html=True,
366
+ )
367
+ st.page_link(home_pg, label="Home")
368
+ st.page_link(data_pg, label="Data Upload")
369
+
370
+ with st.expander("**Plot**", expanded=pg in _PLOT_PAGES):
371
+ st.page_link(basic_pg, label="Quick Plot")
372
+ st.page_link(multi_pg, label="Dashboard")
373
+ st.markdown(_ADV_HEADER, unsafe_allow_html=True)
374
+ st.page_link(adv_plt_pg, label="↳ Advanced Plotting")
375
+ st.page_link(spec_pg, label="↳ Spectroscopy")
376
+ st.page_link(molvis_pg, label="↳ Molecular Viz")
377
+
378
+ with st.expander("**Analyze**", expanded=pg in _ANALYZE_PAGES):
379
+ st.page_link(single_pg, label="Analysis Tools")
380
+ st.page_link(batch_pg, label="Batch Analysis")
381
+ st.page_link(datatools_pg, label="Data Tools")
382
+
383
+ st.divider()
384
+ st.page_link(export_pg, label="Export")
385
+ st.page_link(gallery_pg, label="Gallery")
386
+ st.page_link(help_pg, label="Help")
387
+ st.page_link(settings_pg, label="Settings")
388
+
389
+ st.divider()
390
+ with st.expander("About", expanded=False):
391
+ if _NCCU_HORIZ.exists():
392
+ _horiz_b64 = base64.b64encode(_NCCU_HORIZ.read_bytes()).decode()
393
+ st.markdown(
394
+ f'<img src="data:image/png;base64,{_horiz_b64}"'
395
+ ' alt="North Carolina Central University"'
396
+ ' style="width:100%;height:auto;margin-bottom:0.4rem;">',
397
+ unsafe_allow_html=True,
398
+ )
399
+ if _NCCU_WINGS.exists():
400
+ _wings_b64 = base64.b64encode(_NCCU_WINGS.read_bytes()).decode()
401
+ st.markdown(
402
+ f'<img src="data:image/png;base64,{_wings_b64}"'
403
+ ' alt="NCCU Eagle Wings emblem"'
404
+ ' style="width:60%;height:auto;display:block;margin:0 auto 0.4rem;">',
405
+ unsafe_allow_html=True,
406
+ )
407
+ st.markdown(
408
+ "**EaglePlot** is a scientific data visualization and analysis "
409
+ "toolkit developed for research and teaching in computational "
410
+ "science at North Carolina Central University."
411
+ )
412
+ st.markdown("[NCCU Schultz Lab on GitHub](https://github.com/The-Schultz-Lab)")
413
+ if runtime.IS_PYODIDE and not runtime.IS_DESKTOP:
414
+ st.caption(
415
+ f"EaglePlot v{__version__}, running in your browser: your data stays on "
416
+ "this computer. To load whole folders, fetch spectra from NIST or work "
417
+ "faster, [install EaglePlot]"
418
+ "(https://the-schultz-lab.github.io/eagleplot/#install)."
419
+ )
420
+
421
+
422
+ # ── Execute the active page ───────────────────────────────────────────────────
423
+
424
+
425
+ def _wait_for_deferred_packages() -> None:
426
+ """Browser build: tell the user the page is waiting, and rerun once it can load."""
427
+ st.info("Loading the science libraries for this page (only once per visit)…")
428
+
429
+ @st.fragment(run_every=0.5)
430
+ def _poll() -> None:
431
+ if not runtime.missing_deferred_packages():
432
+ st.rerun(scope="app")
433
+
434
+ _poll()
435
+
436
+
437
+ if runtime.IS_PYODIDE:
438
+ # The browser build starts without scipy (roadmap D4.1): load it now, while the
439
+ # student looks at Home, and hold other pages until it is there.
440
+ runtime.start_deferred_loads()
441
+ # …and import pandas and the plotting stack while the student reads Home.
442
+ runtime.warm_imports_in_background()
443
+ # Home and Quick Plot don't import them; scipy-based plot types import it when used.
444
+ if pg not in (home_pg, basic_pg) and runtime.missing_deferred_packages():
445
+ _wait_for_deferred_packages()
446
+ st.stop()
447
+
448
+ pg.run()