interactive integration; new user-defined fit functions reloads model list; fixed requirements.txt
This commit is contained in:
@ -2,6 +2,10 @@ from itertools import cycle
|
||||
|
||||
import pyqtgraph as pg
|
||||
from numpy import nanmax, nanmin, inf, argsort, where
|
||||
|
||||
from nmreval.lib.colors import Tab10
|
||||
from ..lib.pg_objects import PlotItem, RegionItem
|
||||
|
||||
try:
|
||||
# numpy > 1.19 renamed some integration functions
|
||||
from scipy.integrate import cumulative_trapezoid
|
||||
@ -13,31 +17,33 @@ from .._py.integral_widget import Ui_Form
|
||||
|
||||
|
||||
class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
colors = cycle(['red', 'green', 'blue', 'cyan', 'magenta',
|
||||
'darkRed', 'darkGreen', 'darkBlue', 'darkCyan', 'darkMagenta'])
|
||||
colors = cycle(Tab10)
|
||||
|
||||
requestData = QtCore.pyqtSignal(str)
|
||||
item_deleted = QtCore.pyqtSignal(pg.GraphicsObject)
|
||||
newData = QtCore.pyqtSignal(str, list)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.connected_figure = ''
|
||||
self.management = None
|
||||
|
||||
self.graph_shown = None
|
||||
self.shown_set = None
|
||||
|
||||
self._data = None
|
||||
self.ranges = []
|
||||
self.lines = []
|
||||
self.areas = []
|
||||
|
||||
self._x = None
|
||||
self._y = None
|
||||
self.max_area = 0
|
||||
self.max_y = inf
|
||||
self.min_y = -inf
|
||||
|
||||
def __call__(self, graph_name, items):
|
||||
self.label_2.setText(f'Connected to <b>{graph_name}</b>')
|
||||
self.label_2.setText(f'Connected to {graph_name}\nChanging tab will remove all integration limits.')
|
||||
|
||||
self.clear()
|
||||
|
||||
@ -50,7 +56,7 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
self.set_combobox.blockSignals(False)
|
||||
|
||||
self.set_combobox.setCurrentIndex(0)
|
||||
self.set_combobox.currentIndexChanged.emit(0)
|
||||
self.change_set(0)
|
||||
|
||||
return self
|
||||
|
||||
@ -61,26 +67,29 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
super().keyPressEvent(e)
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_set_combobox_currentIndexChanged')
|
||||
def change_set(self, idx: int):
|
||||
key = self.set_combobox.itemData(idx)
|
||||
self.requestData.emit(key)
|
||||
def change_set(self, _: int):
|
||||
# key = self.set_combobox.itemData(idx)
|
||||
key = self.set_combobox.currentData()
|
||||
|
||||
def set_data(self, ptr):
|
||||
self._data = ptr
|
||||
self._data = self.management[key]
|
||||
self.max_y = nanmax(self._data.y.real)
|
||||
self.min_y = nanmin(self._data.y.real)
|
||||
for idx, rnge in enumerate(self.ranges):
|
||||
self._update_values(idx, rnge)
|
||||
|
||||
|
||||
def add(self, pos):
|
||||
x = pos[0]
|
||||
self.ranges.append((x, x * 1.1))
|
||||
self.ranges.append((x, x*1.1))
|
||||
|
||||
c = next(IntegralWidget.colors)
|
||||
qc = QtGui.QColor(c)
|
||||
qc = QtGui.QColor(c.hex())
|
||||
qc.setAlpha(40)
|
||||
region = pg.LinearRegionItem(values=[x, x*1.1], brush=QtGui.QBrush(qc), pen=pg.mkPen(QtGui.QColor(c)))
|
||||
integral_plot = pg.PlotDataItem(x=[], y=[])
|
||||
pen = pg.mkPen({f'color': c.rgb()})
|
||||
|
||||
region = RegionItem(values=[x, x*1.1], mode='mid', brush=QtGui.QBrush(qc), pen=pen)
|
||||
integral_plot = PlotItem(x=[], y=[], pen=pen)
|
||||
|
||||
region.sigRegionChanged.connect(self._update_integral)
|
||||
|
||||
self.lines.append((region, integral_plot))
|
||||
@ -93,7 +102,7 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
item = QtWidgets.QTreeWidgetItem()
|
||||
item.setText(0, f'Integral {len(self.ranges)}')
|
||||
|
||||
item.setForeground(0, QtGui.QBrush(QtGui.QColor(c)))
|
||||
item.setForeground(0, QtGui.QBrush(QtGui.QColor(c.hex())))
|
||||
|
||||
pts_i = self.ranges[-1]
|
||||
item_list = []
|
||||
@ -127,7 +136,8 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
|
||||
def _update_values(self, idx, new_range):
|
||||
self.ranges[idx] = new_range
|
||||
area = self.calc_integral(idx, *new_range)
|
||||
|
||||
area = self.make_integral(idx, *new_range)
|
||||
|
||||
item = self.treeWidget.topLevelItem(idx)
|
||||
item.child(0).setText(0, f'Start: {new_range[0]:.5g}')
|
||||
@ -157,18 +167,18 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
scale = (self.max_y - self.min_y) / y_i[-1] * (area_i / max_value)
|
||||
integral_line.setData(x=x_i, y=y_i * scale)
|
||||
|
||||
def calc_integral(self, idx, x_min, x_max):
|
||||
int_range = where((self._data.x >= x_min) & (self._data.x <= x_max))[0]
|
||||
if len(int_range) > 1:
|
||||
x_int = self._data.x[int_range]
|
||||
y_int = self._data.y[int_range].real
|
||||
order = argsort(x_int)
|
||||
def make_integral(self, idx, x_min, x_max):
|
||||
if self._data is None:
|
||||
self.change_set(0)
|
||||
|
||||
integral = cumulative_trapezoid(y=y_int[order], x=x_int[order], initial=0)
|
||||
scale = (self.max_y-self.min_y) / integral[-1]
|
||||
self.lines[idx][1].setData(x=x_int[order], y=integral*scale + self.min_y)
|
||||
integral = self._data.data.integrate(limits=(x_min, x_max), asarray=True)
|
||||
|
||||
return integral[-1]
|
||||
if integral.size != 0:
|
||||
area = integral[-1, 1]
|
||||
scale = (self.max_y-self.min_y) / area
|
||||
self.lines[idx][1].setData(x=integral[:, 0], y=integral[:, 1]*scale + self.min_y)
|
||||
|
||||
return area
|
||||
|
||||
else:
|
||||
self.lines[idx][1].setData(x=[], y=[])
|
||||
@ -179,24 +189,36 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
for item in self.treeWidget.selectedItems():
|
||||
idx = root.indexOfChild(item)
|
||||
|
||||
self.ranges.pop(idx)
|
||||
self.item_deleted.emit(self.lines[idx][0])
|
||||
self.item_deleted.emit(self.lines[idx][1])
|
||||
|
||||
self.ranges.pop(idx)
|
||||
self.lines.pop(idx)
|
||||
self.areas.pop(idx)
|
||||
|
||||
self.treeWidget.takeTopLevelItem(idx)
|
||||
|
||||
@QtCore.pyqtSlot(name='on_pushButton_clicked')
|
||||
def convert_to_datasets(self):
|
||||
set_id = self.set_combobox.currentData()
|
||||
values = []
|
||||
for i in range(len(self.ranges)):
|
||||
x_i, y_i = self.lines[i][1].getData()
|
||||
start_i, stop_i = self.ranges[i]
|
||||
area_i = self.areas[i]
|
||||
values.append((x_i, y_i, start_i, stop_i, area_i))
|
||||
set_values = []
|
||||
areas = []
|
||||
|
||||
self.newData.emit(set_id, values)
|
||||
new_sets = True
|
||||
|
||||
for r in self.ranges:
|
||||
area_i = []
|
||||
for idx in range(self.set_combobox.count()):
|
||||
set_id = self.set_combobox.itemData(idx)
|
||||
d = self.management[set_id]
|
||||
if new_sets:
|
||||
set_values.append(d.value)
|
||||
integration = d.data.integrate(limits=r, asarray=True)
|
||||
area_i.append(integration[-1, 1])
|
||||
|
||||
areas.append(area_i)
|
||||
new_sets = False
|
||||
|
||||
self.management.integral_datasets(self.ranges, set_values, areas)
|
||||
|
||||
def clear(self):
|
||||
self.connected_figure = ''
|
||||
@ -204,6 +226,7 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
self.shown_set = None
|
||||
|
||||
self._data = None
|
||||
self.areas = []
|
||||
self.ranges = []
|
||||
self.lines = []
|
||||
|
||||
@ -211,3 +234,6 @@ class IntegralWidget(QtWidgets.QWidget, Ui_Form):
|
||||
self.max_y = inf
|
||||
self.min_y = -inf
|
||||
|
||||
self.treeWidget.clear()
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user