closes #53

Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de>
Reviewed-on: #55
This commit is contained in:
Dominik Demuth 2023-04-30 18:21:16 +00:00
parent 7671a56b6f
commit d8cc99cea4
10 changed files with 626 additions and 1066 deletions

View File

@ -1,29 +1,113 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/dscfile_dialog.ui'
# Form implementation generated from reading ui file 'src/resources/_ui/dscfile_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING! All changes made in this file will be lost!
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(962, 662)
self.gridLayout_2 = QtWidgets.QGridLayout(Dialog)
self.gridLayout_2.setObjectName("gridLayout_2")
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout_2.addWidget(self.buttonBox, 1, 1, 1, 1)
self.gridLayout_4 = QtWidgets.QGridLayout()
self.gridLayout_4.setContentsMargins(-1, 0, 0, -1)
self.gridLayout_4.setSpacing(3)
self.gridLayout_4.setObjectName("gridLayout_4")
self.cp_checkBox = QtWidgets.QCheckBox(Dialog)
Dialog.resize(1341, 799)
self.verticalLayout_5 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.splitter = QtWidgets.QSplitter(Dialog)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.setObjectName("splitter")
self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter)
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout_4.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.groupBox = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.groupBox.setFlat(False)
self.groupBox.setObjectName("groupBox")
self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox)
self.verticalLayout.setContentsMargins(6, 6, 6, 6)
self.verticalLayout.setObjectName("verticalLayout")
self.step_listWidget = QtWidgets.QListWidget(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.step_listWidget.sizePolicy().hasHeightForWidth())
self.step_listWidget.setSizePolicy(sizePolicy)
self.step_listWidget.setMinimumSize(QtCore.QSize(0, 0))
self.step_listWidget.setObjectName("step_listWidget")
self.verticalLayout.addWidget(self.step_listWidget)
self.verticalLayout_4.addWidget(self.groupBox)
self.groupBox_2 = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.groupBox_2.setObjectName("groupBox_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_2)
self.verticalLayout_2.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.groupBox_4 = QtWidgets.QGroupBox(self.groupBox_2)
self.groupBox_4.setObjectName("groupBox_4")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox_4)
self.verticalLayout_6.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.empty_label = QtWidgets.QLabel(self.groupBox_4)
self.empty_label.setObjectName("empty_label")
self.verticalLayout_6.addWidget(self.empty_label)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.loadempty_button = QtWidgets.QPushButton(self.groupBox_4)
self.loadempty_button.setObjectName("loadempty_button")
self.horizontalLayout_2.addWidget(self.loadempty_button)
self.delempty_button = QtWidgets.QPushButton(self.groupBox_4)
self.delempty_button.setObjectName("delempty_button")
self.horizontalLayout_2.addWidget(self.delempty_button)
self.verticalLayout_6.addLayout(self.horizontalLayout_2)
self.verticalLayout_2.addWidget(self.groupBox_4)
self.groupBox_5 = QtWidgets.QGroupBox(self.groupBox_2)
self.groupBox_5.setObjectName("groupBox_5")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.groupBox_5)
self.verticalLayout_7.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_7.setSpacing(3)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.none_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
self.none_radioButton.setObjectName("none_radioButton")
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.none_radioButton)
self.verticalLayout_7.addWidget(self.none_radioButton)
self.isotherm_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
self.isotherm_radioButton.setChecked(True)
self.isotherm_radioButton.setObjectName("isotherm_radioButton")
self.buttonGroup.addButton(self.isotherm_radioButton)
self.verticalLayout_7.addWidget(self.isotherm_radioButton)
self.slope_radioButton = QtWidgets.QRadioButton(self.groupBox_5)
self.slope_radioButton.setObjectName("slope_radioButton")
self.buttonGroup.addButton(self.slope_radioButton)
self.verticalLayout_7.addWidget(self.slope_radioButton)
self.widget = QtWidgets.QWidget(self.groupBox_5)
self.widget.setMinimumSize(QtCore.QSize(0, 33))
self.widget.setObjectName("widget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_3.setContentsMargins(3, 3, 3, 3)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.limit1_lineedit = QtWidgets.QLineEdit(self.widget)
self.limit1_lineedit.setObjectName("limit1_lineedit")
self.horizontalLayout_3.addWidget(self.limit1_lineedit)
self.limit2_lineedit = QtWidgets.QLineEdit(self.widget)
self.limit2_lineedit.setText("")
self.limit2_lineedit.setObjectName("limit2_lineedit")
self.horizontalLayout_3.addWidget(self.limit2_lineedit)
self.verticalLayout_7.addWidget(self.widget)
self.verticalLayout_2.addWidget(self.groupBox_5)
self.verticalLayout_4.addWidget(self.groupBox_2)
self.groupBox_3 = QtWidgets.QGroupBox(self.verticalLayoutWidget)
self.groupBox_3.setObjectName("groupBox_3")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_3)
self.verticalLayout_3.setContentsMargins(6, 6, 6, 6)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.cp_checkBox = QtWidgets.QCheckBox(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -31,36 +115,8 @@ class Ui_Dialog(object):
self.cp_checkBox.setSizePolicy(sizePolicy)
self.cp_checkBox.setChecked(True)
self.cp_checkBox.setObjectName("cp_checkBox")
self.gridLayout_4.addWidget(self.cp_checkBox, 11, 0, 1, 4)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.loadempty_button = QtWidgets.QPushButton(Dialog)
self.loadempty_button.setObjectName("loadempty_button")
self.horizontalLayout_2.addWidget(self.loadempty_button)
self.delempty_button = QtWidgets.QPushButton(Dialog)
self.delempty_button.setObjectName("delempty_button")
self.horizontalLayout_2.addWidget(self.delempty_button)
self.gridLayout_4.addLayout(self.horizontalLayout_2, 5, 0, 1, 4)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout_4.addItem(spacerItem, 12, 0, 1, 1)
self.isotherm_radioButton = QtWidgets.QRadioButton(Dialog)
self.isotherm_radioButton.setChecked(True)
self.isotherm_radioButton.setObjectName("isotherm_radioButton")
self.buttonGroup = QtWidgets.QButtonGroup(Dialog)
self.buttonGroup.setObjectName("buttonGroup")
self.buttonGroup.addButton(self.isotherm_radioButton)
self.gridLayout_4.addWidget(self.isotherm_radioButton, 6, 1, 1, 1)
self.label_4 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
self.label_4.setSizePolicy(sizePolicy)
self.label_4.setStyleSheet("font-weight: bold")
self.label_4.setObjectName("label_4")
self.gridLayout_4.addWidget(self.label_4, 0, 0, 1, 4)
self.reference_tableWidget = QtWidgets.QTableWidget(Dialog)
self.verticalLayout_3.addWidget(self.cp_checkBox)
self.reference_tableWidget = QtWidgets.QTableWidget(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -75,57 +131,11 @@ class Ui_Dialog(object):
self.reference_tableWidget.horizontalHeader().setVisible(False)
self.reference_tableWidget.horizontalHeader().setStretchLastSection(True)
self.reference_tableWidget.verticalHeader().setVisible(False)
self.gridLayout_4.addWidget(self.reference_tableWidget, 9, 0, 1, 4)
self.step_listWidget = QtWidgets.QListWidget(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.step_listWidget.sizePolicy().hasHeightForWidth())
self.step_listWidget.setSizePolicy(sizePolicy)
self.step_listWidget.setMinimumSize(QtCore.QSize(0, 0))
self.step_listWidget.setObjectName("step_listWidget")
self.gridLayout_4.addWidget(self.step_listWidget, 1, 0, 1, 4)
self.label = QtWidgets.QLabel(Dialog)
self.label.setObjectName("label")
self.gridLayout_4.addWidget(self.label, 6, 0, 1, 1)
self.slope_radioButton = QtWidgets.QRadioButton(Dialog)
self.slope_radioButton.setObjectName("slope_radioButton")
self.buttonGroup.addButton(self.slope_radioButton)
self.gridLayout_4.addWidget(self.slope_radioButton, 6, 2, 1, 1)
self.empty_label = QtWidgets.QLabel(Dialog)
self.empty_label.setObjectName("empty_label")
self.gridLayout_4.addWidget(self.empty_label, 4, 0, 1, 4)
self.label_3 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
self.label_3.setSizePolicy(sizePolicy)
self.label_3.setStyleSheet("font-weight: bold")
self.label_3.setObjectName("label_3")
self.gridLayout_4.addWidget(self.label_3, 8, 0, 1, 4)
self.label_2 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
self.label_2.setSizePolicy(sizePolicy)
self.label_2.setStyleSheet("font-weight: bold")
self.label_2.setObjectName("label_2")
self.gridLayout_4.addWidget(self.label_2, 3, 0, 1, 4)
self.line = QtWidgets.QFrame(Dialog)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.gridLayout_4.addWidget(self.line, 7, 0, 1, 4)
self.none_radioButton = QtWidgets.QRadioButton(Dialog)
self.none_radioButton.setObjectName("none_radioButton")
self.buttonGroup.addButton(self.none_radioButton)
self.gridLayout_4.addWidget(self.none_radioButton, 6, 3, 1, 1)
self.verticalLayout_3.addWidget(self.reference_tableWidget)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSpacing(3)
self.horizontalLayout.setObjectName("horizontalLayout")
self.ref_add_pushButton = QtWidgets.QPushButton(Dialog)
self.ref_add_pushButton = QtWidgets.QPushButton(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -133,7 +143,7 @@ class Ui_Dialog(object):
self.ref_add_pushButton.setSizePolicy(sizePolicy)
self.ref_add_pushButton.setObjectName("ref_add_pushButton")
self.horizontalLayout.addWidget(self.ref_add_pushButton)
self.ref_remove_pushButton = QtWidgets.QPushButton(Dialog)
self.ref_remove_pushButton = QtWidgets.QPushButton(self.groupBox_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -141,16 +151,22 @@ class Ui_Dialog(object):
self.ref_remove_pushButton.setSizePolicy(sizePolicy)
self.ref_remove_pushButton.setObjectName("ref_remove_pushButton")
self.horizontalLayout.addWidget(self.ref_remove_pushButton)
self.gridLayout_4.addLayout(self.horizontalLayout, 10, 0, 1, 4)
self.line_2 = QtWidgets.QFrame(Dialog)
self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line_2.setObjectName("line_2")
self.gridLayout_4.addWidget(self.line_2, 2, 0, 1, 4)
self.gridLayout_2.addLayout(self.gridLayout_4, 0, 0, 1, 1)
self.gridLayout = QtWidgets.QGridLayout()
self.verticalLayout_3.addLayout(self.horizontalLayout)
self.verticalLayout_4.addWidget(self.groupBox_3)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_4.addItem(spacerItem)
self.buttonBox = QtWidgets.QDialogButtonBox(self.verticalLayoutWidget)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Save)
self.buttonBox.setCenterButtons(True)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout_4.addWidget(self.buttonBox)
self.widget1 = QtWidgets.QWidget(self.splitter)
self.widget1.setObjectName("widget1")
self.gridLayout = QtWidgets.QGridLayout(self.widget1)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.raw_graph = PlotWidget(Dialog)
self.raw_graph = PlotWidget(self.widget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -159,7 +175,7 @@ class Ui_Dialog(object):
self.raw_graph.setMinimumSize(QtCore.QSize(300, 200))
self.raw_graph.setObjectName("raw_graph")
self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1)
self.calib_graph = PlotWidget(Dialog)
self.calib_graph = PlotWidget(self.widget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -168,7 +184,7 @@ class Ui_Dialog(object):
self.calib_graph.setMinimumSize(QtCore.QSize(300, 200))
self.calib_graph.setObjectName("calib_graph")
self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1)
self.baseline_graph = PlotWidget(Dialog)
self.baseline_graph = PlotWidget(self.widget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -177,7 +193,7 @@ class Ui_Dialog(object):
self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200))
self.baseline_graph.setObjectName("baseline_graph")
self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1)
self.end_graph = PlotWidget(Dialog)
self.end_graph = PlotWidget(self.widget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -186,7 +202,7 @@ class Ui_Dialog(object):
self.end_graph.setMinimumSize(QtCore.QSize(0, 0))
self.end_graph.setObjectName("end_graph")
self.gridLayout.addWidget(self.end_graph, 1, 1, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 1, 1, 1)
self.verticalLayout_5.addWidget(self.splitter)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept)
@ -196,18 +212,20 @@ class Ui_Dialog(object):
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Read DSC file"))
self.cp_checkBox.setText(_translate("Dialog", "Convert to heat capacity"))
self.groupBox.setTitle(_translate("Dialog", "Detected steps"))
self.groupBox_2.setTitle(_translate("Dialog", "Baseline corrections"))
self.groupBox_4.setTitle(_translate("Dialog", "Empty measurement"))
self.empty_label.setText(_translate("Dialog", "No emtpy measurement"))
self.loadempty_button.setText(_translate("Dialog", "Load empty"))
self.delempty_button.setText(_translate("Dialog", "Remove empty"))
self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms"))
self.label_4.setText(_translate("Dialog", "Detected steps"))
self.label.setText(_translate("Dialog", "Slope"))
self.slope_radioButton.setText(_translate("Dialog", "Initial slope"))
self.empty_label.setText(_translate("Dialog", "Empty measurement"))
self.label_3.setText(_translate("Dialog", "Calibration"))
self.label_2.setText(_translate("Dialog", "Baseline"))
self.groupBox_5.setTitle(_translate("Dialog", "Slope correction"))
self.none_radioButton.setText(_translate("Dialog", "None"))
self.isotherm_radioButton.setText(_translate("Dialog", "Isotherms"))
self.slope_radioButton.setText(_translate("Dialog", "Initial slope"))
self.limit1_lineedit.setPlaceholderText(_translate("Dialog", "start (in min)"))
self.limit2_lineedit.setPlaceholderText(_translate("Dialog", "stop (in min)"))
self.groupBox_3.setTitle(_translate("Dialog", "References"))
self.cp_checkBox.setText(_translate("Dialog", "Use reference to convert to heat capacity"))
self.ref_add_pushButton.setText(_translate("Dialog", "Add reference"))
self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference"))
from pyqtgraph import PlotWidget

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'src/resources/_ui/eval_expr_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/fitdialog.ui'
# Form implementation generated from reading ui file 'src/resources/_ui/fitdialog.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING! All changes made in this file will be lost!
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/autohome/dominik/nmreval-gitea/src/resources/_ui/fitresult.ui'
# Form implementation generated from reading ui file 'src/resources/_ui/fitresult.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.

View File

@ -97,7 +97,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
if empty:
self.empty = self.calibrator.set_measurement(empty, mode='empty')
self.empty_label.setText(str(self.empty.fname.name))
self.empty_label.setText('~/' + str(self.empty.fname.relative_to(Path.home())))
self.update_plots()
@ -158,20 +158,28 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
self.sample_idx = None
self.clear_plots()
def get_data(self, ):
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()]
slope_type = {
self.none_radioButton: None,
self.isotherm_radioButton: 'iso',
self.slope_radioButton: 'curve',
}[self.buttonGroup.checkedButton()]
limit = None
if slope_type == 'curve':
try:
limit = float(self.limit1_lineedit.text())*60, float(self.limit2_lineedit.text())*60
except ValueError:
limit = None
try:
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx,
slope=slope_type)
raw_sample, drift_value, sample_data, empty_data, slope = self.calibrator.get_data(self.sample_idx, slope=slope_type, limits=limit)
except ValueError as e:
_msg = QtWidgets.QMessageBox.warning(self, 'No rate found', e.args[0])
_msg = QtWidgets.QMessageBox.warning(self, f'Data collection with error', e.args[0])
return
self.calibrator.ref_list = []
@ -194,6 +202,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonGroup_buttonClicked')
@QtCore.pyqtSlot(int, name='on_cp_checkBox_stateChanged')
@QtCore.pyqtSlot(str, name='on_limit1_lineedit_textChanged')
@QtCore.pyqtSlot(str, name='on_limit2_lineedit_textChanged')
def update_plots(self, _=None):
res = self.get_data()
if res is None:
@ -254,7 +264,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
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)
self.export_data(filesave=bttn_value == self.buttonBox.Save, close_after=bttn_value == self.buttonBox.Ok)
else:
super().close()

View File

@ -911,7 +911,7 @@ class UpperManagement(QtCore.QObject):
self.data[new_id[0]].data = new_data
except Exception as e:
failures.append((data_i, e))
print(str(data_i) + ' failed with Exception: ' + ''.join(e.args))
logger.warning(str(data_i) + ' failed with Exception: ' + ''.join(e.args))
continue
if overwrite:
@ -946,9 +946,9 @@ class UpperManagement(QtCore.QObject):
self.newData.emit([s_id], graph)
except Exception as err:
print('Creation failed with error: ' + ', '.join(err.args))
logger.exception('Creation failed with error: ' + ', '.join(err.args))
err_msg = QtWidgets.QMessageBox(parent=self.sender())
err_msg.setText('One or more errors occured during evaluation.')
err_msg.setText('One or more errors occurred during evaluation.')
err_msg.setDetailedText('Creation failed with error: ' + ', '.join(err.args))
err_msg.exec()

View File

@ -1,289 +0,0 @@
import os
import argparse
import sys
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate
from scipy.integrate import simps
from ..io.dsc import DSCSample, Cyclohexane
parser = argparse.ArgumentParser(description='Calibrate DSC data')
parser.add_argument('sample', type=str, help='filename of DSC sample')
parser.add_argument('empty', type=str, help='filename of empty pan')
parser.add_argument('reference', help='filename of reference', type=str)
parser.add_argument('--cooling', help='Show figure of found cooling rates', action='store_true')
def evaluate(sample, empty, reference, ref_points=Cyclohexane, show_cooling=False):
sample = DSCSample(sample)
empty = DSCSample(empty)
reference = DSCSample(reference)
if show_cooling:
fig, ax = plt.subplots()
print('\n')
for k, v in sample.cooling.items():
print('Plot run {} with cooling rate {} K/min'.format(k, v))
c = sample.flow_data(v, mode='c')
ax.plot(c[0], c[1], label=str(v)+' K/min')
ax.set_xlabel('T / K')
plt.legend()
plt.show()
return
run_list = []
if len(sample.heating) > 1:
run = None
print('\nMultiple heat rates found:')
for k, v in sample.heating.items():
print(' run {}: {} K/min'.format(k, v))
while run not in sample.heating:
# choose your own adventure!!!
value = input('\nPlease select a run (press Enter for all heat rates): ')
if value == '':
run_list = list(sample.heating.keys())
break
else:
run = int(value)
run_list = [run]
else:
run_list = list(sample.heating.keys())
for run in run_list:
rate = sample.heating[run]
print('\nProcessing heat rate {} K/min'.format(rate))
print('Load data of heating data')
len_sample = sample.length(run)
# sanity checks
try:
reference_data = reference.flow_data(rate)
except IndexError:
print('ERROR: Reference measurement has no heat rate {} K/min'.format(rate))
print('Stop evaluation')
sys.exit()
try:
run_baseline = empty.get_run(rate)
except ValueError:
print('ERROR: Empty measurement has no heat rate {} K/min'.format(rate))
print('Stop evaluation')
sys.exit()
len_baseline = empty.length(run_baseline)
if len_baseline != len_sample:
print('WARNING: measurements differ by {} points'.format(abs(len_baseline - len_sample)))
# max_length = min(len_baseline, len_sample)
sample_data = sample.flow_data(rate, length=None)
empty_data = empty.flow_data(rate, length=None)
# plot input data
fig1, ax1 = plt.subplots(2, 3, **{'figsize': (10, 6)})
ax1[0, 0].set_title('raw data')
ax1[0, 0].set_xlabel('T / K')
ax1[0, 0].plot(sample_data[0], sample_data[1], 'k-', label='Sample')
ax1[0, 0].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
ax1[0, 0].plot(reference_data[0], reference_data[1], 'r-', label='Reference')
ax1[0, 0].legend()
print('Substract empty data\n')
sample_baseline = sample_data.copy()
empty_y = empty_data[1]
if len_sample != len_baseline:
with np.errstate(all='ignore'):
empty_y = scipy.interpolate.interp1d(empty_data[0], empty_data[1],
fill_value='extrapolate')(sample_data[0])
sample_baseline[1] = sample_data[1] - empty_y
# plot baseline correction
ax1[0, 1].set_title('baseline correction')
ax1[0, 1].set_xlabel('T / K')
ax1[0, 1].plot(sample_data[0], sample_data[1], 'k--', label='Raw')
ax1[0, 1].plot(sample_baseline[0], sample_baseline[1], 'k-', label='Baseline corrected')
ax1[0, 1].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
ax1[0, 1].legend()
print('Load isothermal data around heat rate')
mean_isotherms = []
for offset, where, ls in [(-1, 'low', '-'), (1, 'high', '--')]:
# read isotherms and baseline correct
len_baseline = empty.length(run_baseline+offset)
len_sample = sample.length(run+offset)
if len_baseline != len_sample:
print('WARNING: {} T isotherms differ by {} points'.format(where, abs(len_baseline-len_sample)))
max_length = min(len_baseline, len_sample)
isotherm_sample = sample.isotherm_data(run_baseline+offset, length=max_length)
isotherm_empty = empty.isotherm_data(run+offset, length=max_length)
isotherm_sample[1] -= isotherm_empty[1]
# get mean isotherm value
m = np.polyfit(isotherm_sample[0, 200:-200], isotherm_sample[1, 200:-200], 0)[0]
mean_isotherms.append(m)
print('Calculated {} heat flow: {:.4} mW'.format(where, m))
ax1[0, 2].plot(isotherm_sample[0], isotherm_sample[1], 'k--')
# calculate slope from difference between isotherms
slope = (mean_isotherms[1]-mean_isotherms[0]) / (sample_data[2, -1] - empty_data[2, 0])
print('Heat flow slope from isotherms: {:.4} per minute'.format(slope*60))
# calculate mean slope of heat flow at points in the beginning
slope_baseline = np.gradient(sample_baseline[1, int(4000/rate):int(9000/rate)],
sample_baseline[2, 300]-sample_baseline[2, 299]).mean()
print('Heat flow slope from initial heating: {:.4f} per minute\n'.format(slope_baseline*60))
drift_corrected = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope
drift_from_slope = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope_baseline
# plot
ax1[0, 2].axhline(mean_isotherms[0], linestyle=':')
ax1[0, 2].axhline(mean_isotherms[1], linestyle=':')
ax1[0, 2].plot(sample_baseline[2], sample_baseline[1], 'k-', label='Baseline corrected')
ax1[0, 2].plot(sample_baseline[2], drift_corrected, 'g-', label='Corrected (isotherm)')
ax1[0, 2].plot(sample_baseline[2], drift_from_slope, 'b-', label='Corrected (heating)')
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + (sample_baseline[2]-empty_data[2, 0])*slope, 'g--')
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + slope_baseline*(sample_baseline[2]-empty_data[2, 0]),
'b--')
ax1[0, 2].plot(sample_baseline[2, int(4000/rate):int(9000/rate)],
sample_baseline[1, int(4000/rate):int(9000/rate)], 'r--')
ax1[0, 2].set_title('time dependence')
ax1[0, 2].set_xlabel('t / s')
ax1[0, 2].legend()
melts = []
for i, (ref_temp, enthalpy) in enumerate(ref_points.transitions):
# region around reference peaks
t_low_lim = ref_temp - 15
t_high_lim = ref_temp + 15
low_border = np.argmin(np.abs(reference_data[0]-t_low_lim))
high_border = np.argmin(np.abs(reference_data[0]-t_high_lim))
ref_zoom = reference_data[:, low_border:high_border]
x_val = np.array([[ref_zoom[0, 0], 1],
[ref_zoom[0, -1], 1]])
y_val = np.array([ref_zoom[1, 0],
ref_zoom[1, -1]])
print('Baseline correct reference of %.2f transition' % ref_temp)
sol = np.linalg.solve(x_val, y_val)
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
peak_max = ref_zoom[:, np.argmax(ref_zoom[1])]
integration_limit = np.argmin(abs(ref_zoom[0]-peak_max[0]+3)), np.argmin(abs(ref_zoom[0]-peak_max[0]-3))
# substract baseline around reference peaks
x_val = np.array([[ref_zoom[0, integration_limit[0]], 1],
[ref_zoom[0, integration_limit[1]], 1]])
y_val = np.array([ref_zoom[1, integration_limit[0]],
ref_zoom[1, integration_limit[1]]])
print('Baseline correct reference of %.2f transition' % ref_temp)
sol = np.linalg.solve(x_val, y_val)
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
# calculate onset slope (use points at position of maximum gradient +/- 100/rate)
ref_grad = np.gradient(ref_zoom[1])
max_grad = np.argmax(ref_grad)
x_val = np.array([[ref_zoom[0, max_grad-int(100/rate)], 1],
[ref_zoom[0, max_grad+int(100/rate)+1], 1]])
y_val = np.array([ref_zoom[1, max_grad-int(100/rate)],
ref_zoom[1, max_grad+int(100/rate)+1]])
sol = np.linalg.solve(x_val, y_val)
onset = sol[0]*ref_zoom[0] + sol[1]
melts.append(-sol[1]/sol[0])
# plot
ax1[1, i].set_title(f'reference: {ref_temp:.2f}')
ax1[1, i].set_xlabel('T / K')
ax1[1, i].plot(reference_data[0], reference_data[1], 'r-')
ax1[1, i].plot(ref_zoom[0, max_grad], ref_zoom[0, max_grad], 'kx')
ax1[1, i].plot(ref_zoom[0], onset, 'k--')
ax1[1, i].axhline(0, color='k', linestyle='--')
ax1[1, i].set_xlim(ref_zoom[0, integration_limit[0]], ref_zoom[0, integration_limit[1]])
ax1[1, i].set_ylim(-max(ref_zoom[1])/10, max(ref_zoom[1])*1.1)
print('Onset of transition: %.2f K found at %.2f' % (ref_temp, melts[-1]))
if enthalpy is not None:
# integrate over low temperature peak to calibrate y axis
# delta H in J/g: Integrate Peak over time and divide by weight
area = 1e-3 * simps(ref_zoom[1, integration_limit[0]:integration_limit[1]],
ref_zoom[2, integration_limit[0]:integration_limit[1]],
even='avg')
calib_y_axis = enthalpy / (area / reference.weight)
print("Calibration factor of peak: %f\n" % calib_y_axis)
sample_baseline[1] *= calib_y_axis
fig1.delaxes(ax1[1, 2])
fig1.tight_layout()
plt.show()
# give a choice how to compensate for long-time drift
mode = None
while mode not in ['i', 'h']:
mode = input('\nUse [i]sotherms or initial [h]eating for long-time correction? (Default: i) ')
if mode == '':
mode = 'i'
if mode == 'h':
print('\nCorrect slope from initial heating')
sample_baseline[1] = drift_from_slope
else:
print('\nCorrect slope from isotherm')
sample_baseline[1] = drift_corrected
# calibrate T axis
print('\nCalibrate temperature')
real_trans = np.array([temp for (temp, _) in ref_points.transitions])
t_vals = np.array([[melts[0], 1],
[melts[1], 1]])
calibration_temp = np.linalg.solve(t_vals, real_trans)
print('T_real = {:.4f} * T_meas {:+.4f}'.format(*calibration_temp))
sample_baseline[0] = calibration_temp[0] * sample_baseline[0] + calibration_temp[1]
print('Convert to capacity')
cp = sample_baseline[1] * 60. / rate / sample.weight / 1000.
if sample.weight is None:
raise ValueError('No sample weight given')
# plot final results in separate figure
fig2, ax2 = plt.subplots()
ax2.set_title('{} K/min: Heat flow vs. heat capacity (close to cont.)'.format(rate))
ax2.set_xlabel('Temperature / K')
ax2.plot(sample_baseline[0], sample_baseline[1], label='heat flow')
ax2.plot(sample_baseline[0], cp, label='heat capacity')
plt.legend()
plt.show()
outname = os.path.splitext(sample.name)[0] + '_' + str(rate) + 'K-min.dat'
header = 'Made with version: {}\n'.format(__version__)
header += 'T/K\tCp/J/(gK)\theat flow/mW'
print()
print('Save to', outname)
np.savetxt(outname, np.c_[sample_baseline[0], cp, sample_baseline[1]], header=header)
if __name__ == '__main__':
args = parser.parse_args()
evaluate(args.sample, args.empty, args.reference, show_cooling=args.cooling)

View File

@ -1,292 +0,0 @@
from __future__ import annotations
__version__ = '0.1.2'
import os
from argparse import ArgumentParser
from pathlib import Path
import sys
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import simps
from nmreval.io.dsc import DSCReader, Cyclohexane, ReferenceValue
parser = ArgumentParser(description='Calibrate DSC data')
parser.add_argument('sample', type=str, help='filename of DSC sample')
parser.add_argument('empty', type=str, help='filename of empty pan')
parser.add_argument('reference', help='filename of reference', type=str)
parser.add_argument('--cooling', help='Plot found cooling rates', action='store_true')
def evaluate(sample: str|Path, empty: str|Path, reference: str|Path,
ref_points: ReferenceValue = Cyclohexane, show_cooling: bool = False):
sample = DSCReader(sample)
empty = DSCReader(empty)
reference = DSCReader(reference)
print(sample)
if show_cooling:
fig, ax = plt.subplots()
print('\n')
for k, v in sample.cooling.items():
print('Plot run {} with cooling rate {} K/min'.format(k, v))
c = sample.flow_data(v, mode='c')
ax.plot(c[0], c[1], label=str(v)+' K/min')
ax.set_xlabel('T / K')
plt.legend()
plt.show()
return
run_list = []
if len(sample.heating) > 1:
run = None
print('\nMultiple heat rates found:')
for k, v in sample.heating.items():
print(' run {}: {} K/min'.format(k, v))
while run not in sample.heating:
# choose your own adventure!!!
value = input('\nPlease select a run (press Enter for all heat rates): ')
if value == '':
run_list = list(sample.heating.keys())
break
else:
run = int(value)
run_list = [run]
else:
run_list = list(sample.heating.keys())
for run in run_list:
rate = sample.heating[run]
print('\nProcessing heat rate {} K/min'.format(rate))
print('Load data of heating data')
len_sample = sample.length(run)
# sanity checks
try:
reference_data = reference.flow_data(rate)
except IndexError:
print('ERROR: Reference measurement has no heat rate {} K/min'.format(rate))
print('Stop evaluation')
sys.exit()
try:
run_baseline = empty.get_run(rate)
except ValueError:
print('ERROR: Empty measurement has no heat rate {} K/min'.format(rate))
print('Stop evaluation')
sys.exit()
len_baseline = empty.length(run_baseline)
max_length = None
if len_baseline != len_sample:
print('WARNING: measurements differ by {} points'.format(abs(len_baseline - len_sample)))
max_length = min(len_baseline, len_sample)
sample_data = sample.flow_data(rate, length=max_length)
empty_data = empty.flow_data(rate, length=max_length)
# plot input data
fig1, ax1 = plt.subplots(2, 3, **{'figsize': (10, 6)})
ax1[0, 0].set_title('raw data')
ax1[0, 0].set_xlabel('T / K')
ax1[0, 0].plot(sample_data[0], sample_data[1], 'k-', label='Sample')
ax1[0, 0].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
ax1[0, 0].plot(reference_data[0], reference_data[1], 'r-', label='Reference')
ax1[0, 0].legend()
print('Substract empty data')
sample_baseline = sample_data.copy()
sample_baseline[1] = sample_data[1] - empty_data[1]
# plot baseline correction
ax1[0, 1].set_title('baseline correction')
ax1[0, 1].set_xlabel('T / K')
ax1[0, 1].plot(sample_data[0], sample_data[1], 'k--', label='Raw')
ax1[0, 1].plot(sample_baseline[0], sample_baseline[1], 'k-', label='Baseline corrected')
ax1[0, 1].plot(empty_data[0], empty_data[1], 'b-', label='Empty')
ax1[0, 1].legend()
print('Load isothermal data around heat rate')
mean_isotherms = []
for offset, where, ls in [(-1, 'low', '-'), (1, 'high', '--')]:
# read isotherms and baseline correct
len_baseline = empty.length(run_baseline+offset)
len_sample = sample.length(run+offset)
if len_baseline != len_sample:
print('WARNING: {} T isotherms differ by {} points'.format(where, abs(len_baseline-len_sample)))
max_length = min(len_baseline, len_sample)
isotherm_sample = sample.isotherm_data(run_baseline+offset, length=max_length)
isotherm_empty = empty.isotherm_data(run+offset, length=max_length)
isotherm_sample[1] -= isotherm_empty[1]
# get mean isotherm value
m = np.polyfit(isotherm_sample[0, 200:-200], isotherm_sample[1, 200:-200], 0)[0]
mean_isotherms.append(m)
print('Calculated {} heat flow: {} mW'.format(where, m))
ax1[0, 2].plot(isotherm_sample[0], isotherm_sample[1], 'k--')
# calculate slope from difference between isotherms
slope = (mean_isotherms[1]-mean_isotherms[0]) / (sample_data[2, -1] - empty_data[2, 0])
print('Heat flow slope from isotherms: {} per minute'.format(slope*60))
# calculate mean slope of heat flow at points in the beginning
slope_baseline = np.gradient(sample_baseline[1, int(4000/rate):int(9000/rate)],
sample_baseline[2, 300]-sample_baseline[2, 299]).mean()
print('Heat flow slope from initial heating: {} per minute'.format(slope_baseline*60))
drift_corrected = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope
drift_from_slope = sample_baseline[1] - mean_isotherms[0] - (sample_baseline[2]-empty_data[2, 0])*slope_baseline
# plot
ax1[0, 2].axhline(mean_isotherms[0], linestyle=':')
ax1[0, 2].axhline(mean_isotherms[1], linestyle=':')
ax1[0, 2].plot(sample_baseline[2], sample_baseline[1], 'k-', label='Baseline corrected')
ax1[0, 2].plot(sample_baseline[2], drift_corrected, 'g-', label='Corrected (isotherm)')
ax1[0, 2].plot(sample_baseline[2], drift_from_slope, 'b-', label='Corrected (heating)')
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + (sample_baseline[2]-empty_data[2, 0])*slope, 'g--')
ax1[0, 2].plot(sample_baseline[2], mean_isotherms[0] + slope_baseline*(sample_baseline[2]-empty_data[2, 0]),
'b--')
ax1[0, 2].plot(sample_baseline[2, int(4000/rate):int(9000/rate)],
sample_baseline[1, int(4000/rate):int(9000/rate)], 'r--')
ax1[0, 2].set_title('time dependence')
ax1[0, 2].set_xlabel('t / s')
ax1[0, 2].legend()
melts = []
for i, (trans_temp, enthalpy) in enumerate(ref_points.transitions):
print(trans_temp, enthalpy)
# region around reference peaks
# NOTE: limits are hard coded for cyclohexane, other references need other limits
low_border = np.argmin(abs(reference_data[0]-(trans_temp-15)))
high_border = np.argmin(abs(reference_data[0]-(trans_temp+15)))
ref_zoom = reference_data[:, low_border:high_border]
x_val = np.array([[ref_zoom[0, 0], 1],
[ref_zoom[0, -1], 1]])
y_val = np.array([ref_zoom[1, 0],
ref_zoom[1, -1]])
print('Baseline correct reference of {} transition'.format(trans_temp))
sol = np.linalg.solve(x_val, y_val)
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
peak_max = ref_zoom[:, np.argmax(ref_zoom[1])]
integration_limit = np.argmin(abs(ref_zoom[0]-peak_max[0]+3)), np.argmin(abs(ref_zoom[0]-peak_max[0]-3))
# substract baseline around reference peaks
x_val = np.array([[ref_zoom[0, integration_limit[0]], 1],
[ref_zoom[0, integration_limit[1]], 1]])
y_val = np.array([ref_zoom[1, integration_limit[0]],
ref_zoom[1, integration_limit[1]]])
print('Baseline correct reference of {} transition'.format(trans_temp))
sol = np.linalg.solve(x_val, y_val)
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
# calculate onset slope (use points at position of maximum gradient +/- 100/rate)
ref_grad = np.gradient(ref_zoom[1])
max_grad = np.argmax(ref_grad)
x_val = np.array([[ref_zoom[0, max_grad-int(100/rate)], 1],
[ref_zoom[0, max_grad+int(100/rate)+1], 1]])
y_val = np.array([ref_zoom[1, max_grad-int(100/rate)],
ref_zoom[1, max_grad+int(100/rate)+1]])
sol = np.linalg.solve(x_val, y_val)
onset = sol[0]*ref_zoom[0] + sol[1]
melts.append(-sol[1]/sol[0])
# plot
ax1[1, i].set_title('reference: {:.2f} K'.format(trans_temp))
ax1[1, i].set_xlabel('T / K')
ax1[1, i].plot(reference_data[0], reference_data[1], 'r-')
ax1[1, i].plot(ref_zoom[0, max_grad], ref_zoom[0, max_grad], 'kx')
ax1[1, i].plot(ref_zoom[0], onset, 'k--')
ax1[1, i].axhline(0, color='k', linestyle='--')
ax1[1, i].set_xlim(ref_zoom[0, integration_limit[0]], ref_zoom[0, integration_limit[1]])
ax1[1, i].set_ylim(-max(ref_zoom[1])/10, max(ref_zoom[1])*1.1)
print('Onset of transition: {:.2f} K, should be at {:.2f}'.format(melts[-1], trans_temp))
if enthalpy is not None:
# integrate over low temperature peak to calibrate y axis
# NOTE: again, this is only valid for cyclohexane
# delta H in J/g: Integrate Peak over time and divide by weight
area = 1e-3 * simps(ref_zoom[1, integration_limit[0]:integration_limit[1]],
ref_zoom[2, integration_limit[0]:integration_limit[1]],
even='avg')
calib_y_axis = enthalpy / (area / reference.weight)
print("Calibration factor of peak: {}".format(calib_y_axis))
sample_baseline[1] *= calib_y_axis
fig1.delaxes(ax1[1, 2])
fig1.tight_layout()
plt.show()
# give a choice how to compensate for long-time drift
mode = None
while mode not in ['i', 'h']:
mode = input('\nUse [i]sotherms or initial [h]eating for long-time correction? (Default: i) ')
if mode == '':
mode = 'i'
if mode == 'h':
print('\nCorrect slope from initial heating')
sample_baseline[1] = drift_from_slope
else:
print('\nCorrect slope from isotherm')
sample_baseline[1] = drift_corrected
# calibrate T axis
print('\nCalibrate temperature')
real_trans = np.array([ref_points.transition1, ref_points.transition2])
t_vals = np.array([[melts[0], 1],
[melts[1], 1]])
calibration_temp = np.linalg.solve(t_vals, real_trans)
print('T_real = {:.4f} * T_meas {:+.4f}'.format(*calibration_temp))
sample_baseline[0] = calibration_temp[0] * sample_baseline[0] + calibration_temp[1]
print('Convert to capacity')
cp = sample_baseline[1] * 60. / rate / sample.weight / 1000.
if sample.weight is None:
raise ValueError('No sample weight given')
# plot final results in separate figure
fig2, ax2 = plt.subplots()
ax2.set_title('{} K/min: Heat flow vs. heat capacity (close to cont.)'.format(rate))
ax2.set_xlabel('Temperature / K')
ax2.plot(sample_baseline[0], sample_baseline[1], label='heat flow')
ax2.plot(sample_baseline[0], cp, label='heat capacity')
plt.legend()
plt.show()
outname = os.path.splitext(sample.name)[0] + '_' + str(rate) + 'K-min.dat'
header = 'Made with version: {}\n'.format(__version__)
header += 'T/K\tCp/J/(gK)\theat flow/mW'
print()
print('Save to', outname)
np.savetxt(outname, np.c_[sample_baseline[0], cp, sample_baseline[1]], header=header)
if __name__ == '__main__':
args = parser.parse_args()
evaluate(args.sample, args.empty, args.reference, show_cooling=args.cooling)

View File

@ -33,7 +33,7 @@ class DSCSample:
self.read_file(fname)
def read_file(self, fname: str | Path):
def read_file(self, fname: str | Path) -> None:
fname = Path(fname)
# file contains weird deg C character in stupiod ISO encoding
@ -141,7 +141,7 @@ class DSCCalibrator:
def __init__(self):
self.sample = None
self.empty = None
self.reference =[]
self.reference = []
self.ref_list = []
def set_measurement(self,
@ -207,11 +207,8 @@ class DSCCalibrator:
integ_limit = (np.argmin(np.abs(ref_zoom[0] - peak_max[0] + 3)),
np.argmin(np.abs(ref_zoom[0] - peak_max[0] - 3)))
# substract baseline around reference peaks
x_val = np.array([[ref_zoom[0, integ_limit[0]], 1], [ref_zoom[0, integ_limit[1]], 1]])
y_val = np.array([ref_zoom[1, integ_limit[0]], ref_zoom[1, integ_limit[1]]])
sol = np.linalg.solve(x_val, y_val)
# subtract baseline around reference peak
sol = self.solve_linear_eq(integ_limit, ref_zoom)
ref_zoom[1] -= (ref_zoom[0] * sol[0] + sol[1])
# calculate onset slope (use points at position of maximum gradient - 100/rate (+50/rate))
@ -220,11 +217,7 @@ class DSCCalibrator:
grad_pos = max_grad-int(50 / rate), max_grad
x_val = np.array([[ref_zoom[0, grad_pos[0]], 1],
[ref_zoom[0, grad_pos[1]], 1]])
y_val = np.array([ref_zoom[1, grad_pos[0]],
ref_zoom[1,grad_pos[1]]])
sol = np.linalg.solve(x_val, y_val)
sol = self.solve_linear_eq(grad_pos, ref_zoom)
onset = sol[0] * ref_zoom[0] + sol[1]
melts.append(-sol[1] / sol[0])
@ -254,7 +247,15 @@ class DSCCalibrator:
return calib_x, calib_y, results
def get_data(self, idx, slope='iso'):
@staticmethod
def solve_linear_eq(limits: tuple, ref_zoom: np.ndarray) -> np.ndarray:
x_val = np.array([[ref_zoom[0, limits[0]], 1], [ref_zoom[0, limits[1]], 1]])
y_val = np.array([ref_zoom[1, limits[0]], ref_zoom[1, limits[1]]])
sol = np.linalg.solve(x_val, y_val)
return sol
def get_data(self, idx: int, slope: str = 'iso', limits: tuple[float, float] = None):
if self.sample.steps[idx][0] == 'i':
raise ValueError('baseline correction is not implemented for isotherms')
@ -304,21 +305,36 @@ class DSCCalibrator:
drift_value = np.c_[drift_value, isotherm_sample]
if slope is not None:
offset = sample_data[1, 200]
if slope == 'iso':
# calculate slope from difference between isotherms
m = (mean_isotherms[1] - mean_isotherms[0]) / (sample_data[2, -1] - sample_data[2, 0])
offset = sample_data[1, 200]
else:
# calculate mean slope of heat flow from points in the beginning
offset = sample_data[1, 200]
grad = np.gradient(sample_data[1, :], sample_data[2, :])
region = None
if limits is not None:
if len(limits) != 2:
raise ValueError(f'limits must be tuple of len 2, not {limits!r}')
min_lim, max_lim = min(limits), max(limits)
window = (sample_data[2, :] >= min_lim) * (sample_data[2, :] <= max_lim)
region = sample_data[1:, window]
if region.shape[1] <= 2:
# raise ValueError(f'No data inside selected time window {min_lim/60} min and {max_lim/60} min')
region = None
if region is None:
# if no limits, use all
region = sample_data[1:, :]
grad = np.gradient(region[0, :], region[1, :])
grad = grad[~np.isnan(grad)]
m = grad[(grad < grad.mean()+grad.std()/5)*(grad > grad.mean()-grad.std()/5)].mean()
sample_data[1] -= m * (sample_data[2] - sample_data[2, 200]) + offset
line = np.array([[sample_data[2, 0], sample_data[2, -1]],
[m * (sample_data[2, 200] - sample_data[2, 200]) + offset,
[m * (sample_data[2, 0] - sample_data[2, 200]) + offset,
m * (sample_data[2, -1] - sample_data[2, 200]) + offset]])
else:

View File

@ -6,349 +6,445 @@
<rect>
<x>0</x>
<y>0</y>
<width>962</width>
<height>662</height>
<width>1341</width>
<height>799</height>
</rect>
</property>
<property name="windowTitle">
<string>Read DSC file</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Save</set>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Detected steps</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QListWidget" name="step_listWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Baseline corrections</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Empty measurement</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="empty_label">
<property name="text">
<string>No emtpy measurement</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QPushButton" name="loadempty_button">
<property name="text">
<string>Load empty</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="delempty_button">
<property name="text">
<string>Remove empty</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Slope correction</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QRadioButton" name="none_radioButton">
<property name="text">
<string>None</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="isotherm_radioButton">
<property name="text">
<string>Isotherms</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="slope_radioButton">
<property name="text">
<string>Initial slope</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>33</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QLineEdit" name="limit1_lineedit">
<property name="placeholderText">
<string>start (in min)</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="limit2_lineedit">
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>stop (in min)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>References</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QCheckBox" name="cp_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use reference to convert to heat capacity</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="reference_tableWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column/>
<column/>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QPushButton" name="ref_add_pushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add reference</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ref_remove_pushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remove reference</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Save</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="PlotWidget" name="raw_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="PlotWidget" name="calib_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="PlotWidget" name="baseline_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="PlotWidget" name="end_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_4">
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="11" column="0" colspan="4">
<widget class="QCheckBox" name="cp_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Convert to heat capacity</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QPushButton" name="loadempty_button">
<property name="text">
<string>Load empty</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="delempty_button">
<property name="text">
<string>Remove empty</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="12" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="1">
<widget class="QRadioButton" name="isotherm_radioButton">
<property name="text">
<string>Isotherms</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-weight: bold</string>
</property>
<property name="text">
<string>Detected steps</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="4">
<widget class="QTableWidget" name="reference_tableWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column/>
<column/>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="QListWidget" name="step_listWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Slope</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QRadioButton" name="slope_radioButton">
<property name="text">
<string>Initial slope</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item row="4" column="0" colspan="4">
<widget class="QLabel" name="empty_label">
<property name="text">
<string>Empty measurement</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="4">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-weight: bold</string>
</property>
<property name="text">
<string>Calibration</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">font-weight: bold</string>
</property>
<property name="text">
<string>Baseline</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QRadioButton" name="none_radioButton">
<property name="text">
<string>None</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item row="10" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<widget class="QPushButton" name="ref_add_pushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add reference</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ref_remove_pushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remove reference</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="4">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="PlotWidget" name="raw_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="PlotWidget" name="calib_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="PlotWidget" name="baseline_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="PlotWidget" name="end_graph">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>