1
0
forked from IPKM/nmreval
nmreval/nmreval/gui_qt/io/dscreader.py
2022-07-04 20:54:11 +02:00

267 lines
10 KiB
Python

from __future__ import annotations
from pathlib import Path
import numpy as np
from pyqtgraph import PlotDataItem
from ..Qt import QtWidgets, QtCore
from .._py.dscfile_dialog import Ui_Dialog
from ...data.points import Points
from ...io.dsc import Cyclohexane, DSCCalibrator, DSCSample
class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
data_read = QtCore.pyqtSignal(list)
file_ext = ['.txt', '.dsc']
def __init__(self, sample=None, empty=None, reference=None, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self.calibrator = DSCCalibrator()
self.current_run = (-1, 'h')
self.sample_idx = None
self.fname = None
self.raw_graph.setLabel('bottom', text='T', units='K')
self.raw_graph.setTitle('Raw data')
self.raw_graph.addLegend()
self.raw_sample = PlotDataItem(x=[], y=[], name='Sample')
self.empty_sample = PlotDataItem(x=[], y=[], pen={'dash': (5, 5)}, name='Empty')
self.raw_graph.addItem(self.raw_sample)
self.raw_graph.addItem(self.empty_sample)
self.end_graph.setTitle('End result')
self.end_graph.setLabel('bottom', text='T', units='K')
self.baseline_sample = PlotDataItem(x=[], y=[])
self.end_graph.addItem(self.baseline_sample)
self.baseline_graph.setTitle('Time dependence')
self.baseline_graph.setLabel('bottom', text='t', units='min')
self.drift_sample = PlotDataItem(x=[], y=[])
self.slope_graph = PlotDataItem(x=[], y=[], pen={'color': 'r', 'dash': (5, 5)})
self.baseline_graph.addItem(self.drift_sample)
self.baseline_graph.addItem(self.slope_graph)
self.calib_graph.setTitle('Calibration')
self.calib_graph.setLabel('bottom', text='T', units='K')
self.ref_plotitems = []
self.sample = None
if sample is not None:
self.add_sample(sample)
self.empty = None
if empty is not None:
self.add_empty(empty=empty)
self.references = []
if reference is not None:
if isinstance(reference, (str, Path, DSCSample)):
reference = [reference]
for r in reference:
self.add_reference(ref=r)
def __call__(self, fname: Path | str):
self.clear_plots()
self.add_sample(fname)
return self
def add_sample(self, fname: Path | str):
self.sample = self.calibrator.set_measurement(fname, mode='sample')
self.fname = self.sample.fname
self.step_listWidget.clear()
for opts in self.sample.steps:
item = QtWidgets.QListWidgetItem()
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
item.setCheckState(QtCore.Qt.Unchecked)
if opts[0] == 'i':
item.setFlags(QtCore.Qt.NoItemFlags)
item.setText(f'{opts[1]:.2f} K for {opts[1] / 60:.0f} min')
else:
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable)
item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min')
self.step_listWidget.addItem(item)
@QtCore.pyqtSlot(name='on_loadempty_button_clicked')
def add_empty(self, empty=None):
if empty is None:
empty, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self.fname.parent))
if empty:
self.empty = self.calibrator.set_measurement(empty, mode='empty')
self.empty_label.setText(str(self.empty.fname.name))
self.update_plots()
@QtCore.pyqtSlot(name='on_delempty_button_clicked')
def remove_empty(self):
self.empty_label.setText('No empty measurement')
self.calibrator.empty = None
self.update_plots()
@QtCore.pyqtSlot(name='on_ref_add_pushButton_clicked')
def add_reference(self, ref: str | Path = None):
if ref is None:
ref, _ = QtWidgets.QFileDialog.getOpenFileName(directory=str(self.fname.parent))
if ref:
ref = self.calibrator.set_measurement(ref, mode='reference')
self.references.append(ref)
item = QtWidgets.QTableWidgetItem(str(ref.fname.name))
item.setData(QtCore.Qt.UserRole, ref.fname)
item.setFlags(QtCore.Qt.ItemIsEnabled)
rowcnt = self.reference_tableWidget.rowCount()
self.reference_tableWidget.setRowCount(rowcnt+1)
self.reference_tableWidget.setItem(rowcnt, 0, item)
self.reference_tableWidget.setCellWidget(rowcnt, 1, ReferenceComboBox())
self.reference_tableWidget.resizeColumnsToContents()
self.update_plots()
@QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked')
def remove_reference(self):
idx = self.reference_tableWidget.currentRow()
self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.UserRole))
self.reference_tableWidget.removeRow(idx)
self.update_plots()
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name='on_step_listWidget_itemChanged')
def select_run(self, item: QtWidgets.QListWidgetItem):
idx = self.step_listWidget.indexFromItem(item).row()
self.step_listWidget.blockSignals(True)
for row in range(self.step_listWidget.count()):
if idx == row:
continue
self.step_listWidget.item(row).setCheckState(QtCore.Qt.Unchecked)
self.step_listWidget.blockSignals(False)
if item.checkState() == QtCore.Qt.Checked:
mode, rate, _, _ = self.sample.steps[idx]
self.current_run = (rate, mode)
self.sample_idx = idx
self.update_plots()
else:
self.current_run = (-1, 'h')
self.sample_idx = None
self.clear_plots()
def get_data(self, ):
if self.sample_idx is None:
return
rate = self.current_run[0]
slope_type = {self.none_radioButton: None,
self.isotherm_radioButton: 'iso',
self.slope_radioButton: 'curve'}[self.buttonGroup.checkedButton()]
try:
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx,
slope=slope_type)
except ValueError as e:
_msg = QtWidgets.QMessageBox.warning(self, 'No rate found', e.args[0])
return
self.calibrator.ref_list = []
for row in range(self.reference_tableWidget.rowCount()):
self.calibrator.ref_list.append(self.reference_tableWidget.cellWidget(row, 1).get_reference())
calib_x, calib_y, regions = self.calibrator.get_calibration(rate)
drift_value[0] /= 60.
slope[0] /= 60.
if calib_x is not None:
sample_data[0] *= calib_x[0]
sample_data[0] += calib_x[1]
if self.cp_checkBox.isChecked():
sample_data[1] *= calib_y
return sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
@QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged')
def update_plots(self, _=None):
sample_data, raw_sample, empty_data, drift_value, slope, calib_x, calib_y, regions = self.get_data()
self.raw_sample.setData(x=raw_sample[0], y=raw_sample[1])
self.drift_sample.setData(x=drift_value[0], y=drift_value[1])
self.slope_graph.setData(x=slope[0], y=slope[1])
if empty_data is not None:
self.empty_sample.setData(x=empty_data[0], y=empty_data[1])
self.calib_graph.clear()
if calib_x is not None:
for ref_zoom, onset, grad_points in regions:
ref_plot = PlotDataItem(x=ref_zoom[0], y=ref_zoom[1])
self.calib_graph.addItem(ref_plot)
self.calib_graph.addItem(PlotDataItem(x=grad_points[0], y=grad_points[1],
symbol='x'))
view_limits = -np.max(ref_zoom[1])/10, np.max(ref_zoom[1]) * 1.1
cut_idx, = np.where((onset < view_limits[1]) & (onset > view_limits[0]))
self.calib_graph.addItem(PlotDataItem(x=ref_zoom[0, cut_idx], y=onset[cut_idx],
pen={'color': 'r', 'dash': (2, 5)}))
self.calib_graph.addItem(PlotDataItem(x=[ref_zoom[0, 0], ref_zoom[0, -1]], y=[0, 0],
pen={'color': 'r', 'dash': (2, 5)}))
self.baseline_sample.setData(x=sample_data[0], y=sample_data[1])
def clear_plots(self):
for plot in [self.raw_sample, self.baseline_sample, self.empty_sample, self.drift_sample, self.slope_graph]:
plot.setData(x=[], y=[])
self.calib_graph.clear()
def export_data(self, filesave=False, close_after=True):
try:
sample_data = self.get_data()[0]
except TypeError:
return
rate, mode = self.current_run
new_val = Points(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})')
if filesave:
new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_')))
else:
self.data_read.emit([new_val])
if close_after:
super().accept()
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked')
def button_clicked(self, bttn: QtWidgets.QAbstractButton):
bttn_value = self.buttonBox.standardButton(bttn)
if bttn_value in (self.buttonBox.Ok, self.buttonBox.Apply, self.buttonBox.Save):
self.export_data(filesave=bttn_value==self.buttonBox.Save, close_after=bttn_value==self.buttonBox.Ok)
else:
super().close()
class ReferenceComboBox(QtWidgets.QComboBox):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.references = [Cyclohexane]
for ref in self.references:
self.addItem(ref.name)
def get_reference(self):
return self.references[self.currentIndex()]