Source code for pylorenzmie.lmtool.ProfileWidget

import numpy as np
from numpy.typing import NDArray
import pyqtgraph as pg
from pyqtgraph.Qt.QtCore import (Qt, pyqtProperty)

from pylorenzmie.theory import LorenzMie


[docs] class ProfileWidget(pg.PlotWidget): '''Radial-profile plot showing experimental data and model prediction.''' def __init__(self, *args, model: LorenzMie | None = None, radius: int = 100, **kwargs) -> None: super().__init__(*args, **kwargs) self._configurePlot() self.model = LorenzMie() if model is None else model self._data = None self._stdev = None self.radius = radius def _configurePlot(self) -> None: self.setBackground('w') self.showGrid(True, True, 0.2) opts = {'font-size': '14pt', 'color': 'gray'} self.setLabel('bottom', 'r [pixels]', **opts) self.setLabel('left', 'b(r)', **opts) pen = pg.mkPen('k', width=3, style=Qt.PenStyle.DashLine) self.addLine(y=1, pen=pen) pen = pg.mkPen('k', width=3) self.getAxis('bottom').setPen(pen) self.getAxis('left').setPen(pen) self.theory = pg.PlotCurveItem(pen=pg.mkPen('r', width=3)) self.experiment = pg.PlotCurveItem(pen=pg.mkPen('k', width=3)) pen = pg.mkPen('k', width=1, style=Qt.PenStyle.DashLine) self.upper = pg.PlotCurveItem(pen=pen) self.lower = pg.PlotCurveItem(pen=pen) brush = pg.mkBrush(255, 165, 0, 128) self.region = pg.FillBetweenItem(self.upper, self.lower, brush) self.addItem(self.theory) self.addItem(self.experiment) self.addItem(self.upper) self.addItem(self.lower) self.addItem(self.region) @pyqtProperty(dict) def properties(self) -> dict[str, LorenzMie.Property]: return self.model.properties @properties.setter def properties(self, properties: dict[str, LorenzMie.Property]) -> None: # Profile is always centred at the origin, so x_p/y_p are irrelevant. properties = {k: v for k, v in properties.items() if k not in ('x_p', 'y_p')} if len(properties) == 0: return self.model.properties = properties self.plotTheory() @pyqtProperty(LorenzMie) def model(self) -> LorenzMie: return self._model @model.setter def model(self, model: LorenzMie) -> None: # Profile is always computed at the origin; zero the position in-place. model.x_p = 0.0 model.y_p = 0.0 self._model = model @pyqtProperty(tuple) def data(self) -> tuple[NDArray[float], NDArray[float]]: return (self._data, self._stdev) @data.setter def data(self, data: tuple[NDArray[float], NDArray[float]]) -> None: self._data, self._stdev = data self.plotData() @pyqtProperty(int) def radius(self) -> int: return self._radius @radius.setter def radius(self, radius: int) -> None: self._radius = radius self.model.coordinates = np.arange(radius) self.plotData() self.plotTheory()
[docs] def plotTheory(self) -> None: self.theory.setData(self.model.hologram())
[docs] def plotData(self) -> None: if self._data is None: return radius = min(self.radius, len(self._data)) data = self._data[0:radius] stdev = self._stdev[0:radius] self.experiment.setData(data) self.upper.setData(data + stdev) self.lower.setData(data - stdev)
[docs] @classmethod def example(cls) -> None: from pylorenzmie.theory import AberratedLorenzMie as model app = pg.mkQApp() widget = cls() widget.model = model() widget.radius = 150 widget.show() app.exec()
if __name__ == '__main__': # pragma: no cover ProfileWidget.example()