Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6eea368
Determine bounds of plot data via main UI for plotting simulated data
AntoineTUE Mar 30, 2026
ef084f3
Implement MoleculeCheckBox for molecular band selection and refactor …
AntoineTUE Mar 30, 2026
91ae920
Use lazy loading for scipy in molecules.py
AntoineTUE Mar 30, 2026
0a23db8
Remove leftover imports of scipy by function in molecules.py
AntoineTUE Mar 30, 2026
538c480
Refactor plot labels for clarity and consistency in molecule module
AntoineTUE Mar 30, 2026
4de3b65
fit_spec: mask NaNs to avoid fit problems, cleanup unused code and cl…
AntoineTUE Mar 31, 2026
3d0d0e6
Remove unused 'fit_func', seems to have not been in use before
AntoineTUE Mar 31, 2026
65e6285
Make previewing spectra with show_spec more consistent
AntoineTUE Mar 31, 2026
ff14eca
Decouple MoleculeFitter from UI state, to avoid UI changes affecting …
AntoineTUE Apr 8, 2026
9fdf154
Determine bounds of plot data via main UI for plotting simulated data
AntoineTUE Mar 30, 2026
6e77f88
Implement MoleculeCheckBox for molecular band selection and refactor …
AntoineTUE Mar 30, 2026
13e6813
Use lazy loading for scipy in molecules.py
AntoineTUE Mar 30, 2026
6039dbf
Remove leftover imports of scipy by function in molecules.py
AntoineTUE Mar 30, 2026
7912e70
Refactor plot labels for clarity and consistency in molecule module
AntoineTUE Mar 30, 2026
1fedd9d
fit_spec: mask NaNs to avoid fit problems, cleanup unused code and cl…
AntoineTUE Mar 31, 2026
6a96120
Remove unused 'fit_func', seems to have not been in use before
AntoineTUE Mar 31, 2026
d514567
Make previewing spectra with show_spec more consistent
AntoineTUE Mar 31, 2026
e3523f1
Decouple MoleculeFitter from UI state, to avoid UI changes affecting …
AntoineTUE Apr 8, 2026
688b367
Merge branch 'refactor-molecule-fitting' of https://github.com/Antoin…
AntoineTUE Apr 20, 2026
5a2e004
Rely on lmfit.minimize and Moose for model fitting, construct model p…
AntoineTUE Apr 20, 2026
e746036
Rework right-click menu interaction of fit result table based on rece…
AntoineTUE Apr 20, 2026
4767e8a
Add missing pyqtgraph import to molecules.py
AntoineTUE Apr 20, 2026
f540fe0
Add support for padding to MoleculeCheckBox.get_db
AntoineTUE Apr 20, 2026
aaf9e1f
Bump Moose dependency to v0.3.0, add lmfit as dependency. removed unu…
AntoineTUE Apr 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions OES_toolbox/Widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
import pyqtgraph as pg
import qtawesome as qta

import Moose

from OES_toolbox.file_handling import FileLoader, SpectraDataset
from OES_toolbox.logger import Logger
from OES_toolbox.lazy_import import lazy_import

pd = lazy_import("pandas")

from typing import TYPE_CHECKING
from collections.abc import Callable
Expand Down Expand Up @@ -410,3 +415,67 @@ def _populate_with_data(self, dataset:SpectraDataset, label=None):
self.is_content = True # needed to force non-nested files to plot their data, without adding a child node.
self.set_spectrum(x,y,shift=shift,bg=dataset.background,name=None,bg_is_internal=dataset.has_background)
self.set_background(None)

class MoleculeCheckBox(QCheckBox):
"""A checkbox that enables a user to select molecular bands to show or fit.

Handles both static spectra from LIFBASE simulation, as well as database files for simulation/fitting.

Only loads data when needed, caching it to the `_db` attribute.

A subset of the data (in wavelength range), can be obtained using the `get_db` method.
"""

def __init__(self, ident: str, label: str = None, src: str = "LIFBASE", parent=None):
super().__init__(parent=parent)
self.ident = ident
self.label = ident if label is None else label
self.src = src
self.can_fit = self.src == "mOES"
self.setText(self.label)
self._db = None

@property
def db(self):
if self._db is None:
self._load_database()
return self._db

def _load_database(self):
"""Load a database or sample LIFBASE simulation and cache it on the instance.

The database/file is only loaded if the `_db` attribute is (still) `None`.

No filtering/slicing is applied to the database, so it can be cached and re-used effectively.
"""
if self.can_fit and self._db is None:
with pg.ProgressDialog(f"Loading line-by-line database: {self.label}",cancelText=None,wait=0,busyCursor=True) as _diag:
self._db = Moose.query_DB(self.ident)
elif not self.can_fit and self.src=="LIFBASE" and self._db is None:
with pg.ProgressDialog(f"Loading LIFBASE simulation: {self.label}",cancelText=None, wait=0) as _diag:
# Read the LIFBASE output files, which use lots of whitespace padding and must thus be coerced to float rather than string
# FileLoader._read_generic_text fails correctly detecting separator/delimiter because of whitespacing.
file_path = f"{Path(__file__).parent}/data/mol_spec/{self.ident}.mod"
data = pd.read_csv(file_path,header=None, sep=",",decimal='.',dtype=float,names=["wl","I"])
data.wl=data.iloc[:,0]/10 # Angstrom to nm
self._db = data

def get_db(self, wavelength_interval:tuple[float,float]|None = None, wl_pad:float|int = 10):
"""Return a slice of a database, or LIFBASE spectrum, within the specified `wavelength_interval`.

If no interval is provided (the default), it will lookup the active bounds from the main window.

If no data(base) has been loaded yet, calling this method will trigger a `load_database` call, which will cache the data.

To avoid edge effects, it will look up a slightly wider slice of data on both sides of the interval, based on the `wl_pad` argument (default=10).

Note: for automatic lookup of the bounds to work correctly, the widget instance must have a parent that is part of the OESToolbox main window.
"""
colname = 'wl' if self.src.upper()=="LIFBASE" else 'air_wavelength'
if wavelength_interval is None:
wl_min, wl_max, *_= self.window().get_bounds()
else:
wl_min,wl_max = wavelength_interval
data = self.db[self.db[colname].between(wl_min - wl_pad, wl_max + wl_pad)]
return data

14 changes: 2 additions & 12 deletions OES_toolbox/ident.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,8 @@ def update_spec_ident(self):
self.mw.ident_table.setRowCount(0)

# find wavelength range from file spec
for plot_item in self.mw.specplot.listDataItems():
if "file" in plot_item.name():
x0, x1 = plot_item.dataBounds(0)
if lim_unset:
min_x = x0
max_x = x1
max_y = plot_item.dataBounds(1)[1]
lim_unset = False

min_x = min(min_x, x0)
max_x = max(max_x, x1)

min_x, max_x, min_y, max_y = self.mw.get_bounds()

# fetch options
if self.mw.ident_int_cbox.currentIndex() == 1:
Te = self.mw.ident_Te.value()
Expand Down
Loading