diff --git a/src/gui_qt/_py/tnmh_dialog.py b/src/gui_qt/_py/tnmh_dialog.py index dee2ee0..9ca5174 100644 --- a/src/gui_qt/_py/tnmh_dialog.py +++ b/src/gui_qt/_py/tnmh_dialog.py @@ -17,36 +17,70 @@ class Ui_Dialog(object): Dialog.resize(1042, 683) self.gridLayout = QtWidgets.QGridLayout(Dialog) self.gridLayout.setObjectName("gridLayout") - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.label = QtWidgets.QLabel(Dialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) + self.stackedWidget = QtWidgets.QStackedWidget(Dialog) + self.stackedWidget.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.stackedWidget.setFrameShadow(QtWidgets.QFrame.Raised) + self.stackedWidget.setObjectName("stackedWidget") + self.stackedWidgetPage1 = QtWidgets.QWidget() + self.stackedWidgetPage1.setObjectName("stackedWidgetPage1") + self.gridLayout_3 = QtWidgets.QGridLayout(self.stackedWidgetPage1) + self.gridLayout_3.setObjectName("gridLayout_3") + self.checkBox = QtWidgets.QCheckBox(self.stackedWidgetPage1) + self.checkBox.setObjectName("checkBox") + self.gridLayout_3.addWidget(self.checkBox, 1, 1, 1, 1) + self.checkBox_2 = QtWidgets.QCheckBox(self.stackedWidgetPage1) + self.checkBox_2.setText("") + self.checkBox_2.setObjectName("checkBox_2") + self.gridLayout_3.addWidget(self.checkBox_2, 2, 1, 1, 1) + self.pushButton = QtWidgets.QPushButton(self.stackedWidgetPage1) + self.pushButton.setObjectName("pushButton") + self.gridLayout_3.addWidget(self.pushButton, 0, 1, 1, 1) + self.comboBox_2 = CheckCombobox(self.stackedWidgetPage1) + self.comboBox_2.setObjectName("comboBox_2") + self.gridLayout_3.addWidget(self.comboBox_2, 3, 1, 1, 1) + self.graphicsView_3 = PlotWidget(self.stackedWidgetPage1) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) - self.label.setSizePolicy(sizePolicy) - self.label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.label.setObjectName("label") - self.verticalLayout_2.addWidget(self.label) - 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.setText("") - self.label_3.setObjectName("label_3") - self.verticalLayout_2.addWidget(self.label_3) - self.label_4 = QtWidgets.QLabel(Dialog) - self.label_4.setObjectName("label_4") - self.verticalLayout_2.addWidget(self.label_4) - self.label_5 = QtWidgets.QLabel(Dialog) - self.label_5.setText("") - self.label_5.setObjectName("label_5") - self.verticalLayout_2.addWidget(self.label_5) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_2.addItem(spacerItem) - self.gridLayout.addLayout(self.verticalLayout_2, 0, 3, 2, 1) + sizePolicy.setHeightForWidth(self.graphicsView_3.sizePolicy().hasHeightForWidth()) + self.graphicsView_3.setSizePolicy(sizePolicy) + self.graphicsView_3.setObjectName("graphicsView_3") + self.gridLayout_3.addWidget(self.graphicsView_3, 0, 0, 4, 1) + self.stackedWidget.addWidget(self.stackedWidgetPage1) + self.page = QtWidgets.QWidget() + self.page.setObjectName("page") + self.gridLayout_2 = QtWidgets.QGridLayout(self.page) + self.gridLayout_2.setObjectName("gridLayout_2") + self.pushButton_2 = QtWidgets.QPushButton(self.page) + self.pushButton_2.setObjectName("pushButton_2") + self.gridLayout_2.addWidget(self.pushButton_2, 0, 1, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_2 = QtWidgets.QLabel(self.page) + self.label_2.setObjectName("label_2") + self.horizontalLayout.addWidget(self.label_2) + self.comboBox = QtWidgets.QComboBox(self.page) + self.comboBox.setObjectName("comboBox") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.comboBox.addItem("") + self.horizontalLayout.addWidget(self.comboBox) + self.gridLayout_2.addLayout(self.horizontalLayout, 1, 1, 1, 1) + self.pushButton_3 = QtWidgets.QPushButton(self.page) + self.pushButton_3.setObjectName("pushButton_3") + self.gridLayout_2.addWidget(self.pushButton_3, 2, 1, 1, 1) + self.graphicsView_2 = PlotWidget(self.page) + self.graphicsView_2.setObjectName("graphicsView_2") + self.gridLayout_2.addWidget(self.graphicsView_2, 0, 0, 3, 1) + self.stackedWidget.addWidget(self.page) + self.gridLayout.addWidget(self.stackedWidget, 1, 1, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 2, 1, 1, 1) self.graphicsView = PlotWidget(Dialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) @@ -54,10 +88,7 @@ class Ui_Dialog(object): sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth()) self.graphicsView.setSizePolicy(sizePolicy) self.graphicsView.setObjectName("graphicsView") - self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1) - self.graphicsView_2 = PlotWidget(Dialog) - self.graphicsView_2.setObjectName("graphicsView_2") - self.gridLayout.addWidget(self.graphicsView_2, 1, 2, 1, 1) + self.gridLayout.addWidget(self.graphicsView, 0, 1, 1, 1) self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") self.listWidget = QtWidgets.QListWidget(Dialog) @@ -68,37 +99,29 @@ class Ui_Dialog(object): self.listWidget.setSizePolicy(sizePolicy) self.listWidget.setObjectName("listWidget") self.verticalLayout.addWidget(self.listWidget) - self.pushButton = QtWidgets.QPushButton(Dialog) - self.pushButton.setObjectName("pushButton") - self.verticalLayout.addWidget(self.pushButton) - self.pushButton_2 = QtWidgets.QPushButton(Dialog) - self.pushButton_2.setObjectName("pushButton_2") - self.verticalLayout.addWidget(self.pushButton_2) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.label_2 = QtWidgets.QLabel(Dialog) - self.label_2.setObjectName("label_2") - self.horizontalLayout.addWidget(self.label_2) - self.comboBox = QtWidgets.QComboBox(Dialog) - self.comboBox.setObjectName("comboBox") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.comboBox.addItem("") - self.horizontalLayout.addWidget(self.comboBox) - self.verticalLayout.addLayout(self.horizontalLayout) - self.pushButton_3 = QtWidgets.QPushButton(Dialog) - self.pushButton_3.setObjectName("pushButton_3") - self.verticalLayout.addWidget(self.pushButton_3) - self.gridLayout.addLayout(self.verticalLayout, 0, 0, 3, 1) - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.gridLayout.addWidget(self.buttonBox, 3, 2, 1, 2) + self.label = QtWidgets.QLabel(Dialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth()) + self.label.setSizePolicy(sizePolicy) + self.label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) + self.label.setObjectName("label") + self.verticalLayout.addWidget(self.label) + self.label_3 = QtWidgets.QLabel(Dialog) + self.label_3.setObjectName("label_3") + self.verticalLayout.addWidget(self.label_3) + self.label_4 = QtWidgets.QLabel(Dialog) + self.label_4.setObjectName("label_4") + self.verticalLayout.addWidget(self.label_4) + self.label_5 = QtWidgets.QLabel(Dialog) + self.label_5.setText("") + self.label_5.setObjectName("label_5") + self.verticalLayout.addWidget(self.label_5) + self.gridLayout.addLayout(self.verticalLayout, 0, 0, 2, 1) self.retranslateUi(Dialog) + self.stackedWidget.setCurrentIndex(0) self.buttonBox.accepted.connect(Dialog.accept) # type: ignore self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) @@ -106,8 +129,7 @@ class Ui_Dialog(object): def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "SImba, everything the light touches is our kingdom")) - self.label.setText(_translate("Dialog", "
Tg values:
")) - self.label_4.setText(_translate("Dialog", "TNMH parameter:
")) + self.checkBox.setText(_translate("Dialog", "Export lines")) self.pushButton.setText(_translate("Dialog", "Calculate Tg")) self.pushButton_2.setText(_translate("Dialog", "Fictive Cp")) self.label_2.setText(_translate("Dialog", "Tg for TNMH")) @@ -117,4 +139,8 @@ class Ui_Dialog(object): self.comboBox.setItemText(3, _translate("Dialog", "End")) self.comboBox.setItemText(4, _translate("Dialog", "Inflection")) self.pushButton_3.setText(_translate("Dialog", "Fit TNMH model")) + self.label.setText(_translate("Dialog", "Tg values:
")) + self.label_3.setText(_translate("Dialog", "TextLabel")) + self.label_4.setText(_translate("Dialog", "TNMH parameter:
")) +from ..lib.forms import CheckCombobox from pyqtgraph import PlotWidget diff --git a/src/gui_qt/dsc/glass_dialog.py b/src/gui_qt/dsc/glass_dialog.py index b5fc2e0..28bcc75 100644 --- a/src/gui_qt/dsc/glass_dialog.py +++ b/src/gui_qt/dsc/glass_dialog.py @@ -1,8 +1,9 @@ from itertools import cycle -from numpy import array, nan -from pyqtgraph import mkPen, mkBrush +from numpy import array, nan, isnan +from pyqtgraph import mkPen, mkBrush, LegendItem +from nmreval.dsc.hodge import tau_hodge from nmreval.lib.colors import Tab10 from ..Qt import QtWidgets, QtCore from .._py.tnmh_dialog import Ui_Dialog @@ -23,6 +24,16 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): self._plots = {} self._tg_value = {} self._fit = {} + self._hodge_plots = { + 'onset': PlotItem(x=[], y=[], pen=None, symbol='o', symbolBrush=Tab10.TabBlue.rgb(), name='Onset'), + 'mid': PlotItem(x=[], y=[], pen=None, symbol='s', symbolBrush=Tab10.TabOrange.rgb(), name='Midpoint'), + 'end': PlotItem(x=[], y=[], pen=None, symbol='t', symbolBrush=Tab10.TabGreen.rgb(), name='End'), + 'inflection': PlotItem(x=[], y=[], pen=None, symbol='d', symbolBrush=Tab10.TabRed.rgb(), name='Inflection'), + 'fictive': PlotItem(x=[], y=[], pen=None, symbol='t1', symbolBrush=Tab10.TabPurple.rgb(), name='Fictive'), + # 'TNMH': PlotItem(x=[], y=[], pen=None, symbol='star', symbolBrush=Tab10.TabPurple.rgb(), name='TNMH'), + } + for plt in self._hodge_plots.values(): + self.graphicsView_3.addItem(plt) self.limits = RegionItem(), RegionItem() for lim in self.limits: @@ -33,6 +44,9 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): self.listWidget.itemClicked.connect(self.show_tg_values) + for x in 'abc': + self.comboBox_2.addItem(x) + def __call__(self): self.clear() self.add_sets() @@ -44,6 +58,9 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): self.graphicsView.removeItem(val) self.graphicsView_2.removeItem(val) + for plt in self._hodge_plots.values(): + plt.setData(x=[], y=[]) + self._dsc = {} self._plots = {} self._tg_value = {} @@ -98,8 +115,8 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): if self._limitless: dist = max_x - min_x - self.limits[0].setRegion((min_x, min_x+0.1*dist)) - self.limits[1].setRegion((max_x-0.1*dist, max_x)) + self.limits[0].setRegion((min_x, min_x+min(0.1*dist, 5))) + self.limits[1].setRegion((max_x-min(5, 0.1*dist), max_x)) self._limitless = False @QtCore.pyqtSlot(name='on_pushButton_clicked') @@ -124,7 +141,7 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): self._tg_value[key].update(tg_results) - plot[1].setData(array(list(self._tg_value[key].values()))) + self._update_tg_plots() def show_tg_values(self, item): values = self._tg_value.get(item.data(QtCore.Qt.UserRole)) @@ -137,6 +154,18 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): if fit is not None: self.label_5.setText(fit._parameter_string()) + def _update_tg_plots(self): + for idx in range(self.listWidget.count()): + item = self.listWidget.item(idx) + + key = item.data(QtCore.Qt.UserRole) + plot = self._plots[key] + data, _ = self._dsc[key] + + plot[1].setData(array(list(self._tg_value[key].values()))) + + self.hodge() + @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name='on_listWidget_itemChanged') def change_visibility(self, item: QtWidgets.QListWidgetItem): is_checked = bool(item.checkState()) @@ -166,7 +195,7 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): self._tg_value[key]['fictive'] = (tg, 0) - plot[1].setData(array(list(self._tg_value[key].values()))) + self._update_tg_plots() @QtCore.pyqtSlot(name='on_pushButton_3_clicked') def make_tnmh(self): @@ -190,6 +219,25 @@ class TgCalculator(QtWidgets.QDialog, Ui_Dialog): self._fit[key] = res plot[-1].setData(res.x, res.y) + def hodge(self): + for tg_type, plot in self._hodge_plots.items(): + m = [] + for idx in range(self.listWidget.count()): + item = self.listWidget.item(idx) + + key = item.data(QtCore.Qt.UserRole) + data, _ = self._dsc[key] + + tg_value = self._tg_value[key][tg_type][0] + if isnan(tg_value): + continue + + m.append([tg_value, data.value]) + + if len(m) > 1: + r = tau_hodge(*array(m).T) + plot.setData(r.x, r.y) + def close(self) -> bool: self.clear() return super().close() diff --git a/src/gui_qt/lib/delegates.py b/src/gui_qt/lib/delegates.py index d6f0c9a..6990276 100644 --- a/src/gui_qt/lib/delegates.py +++ b/src/gui_qt/lib/delegates.py @@ -30,7 +30,7 @@ class PropertyDelegate(QtWidgets.QStyledItemDelegate): rect = options.rect rect.adjust(5, 0, -5, 0) - mid = (rect.bottom()+rect.top()) / 2 + mid = int((rect.bottom()+rect.top()) / 2) painter.drawLine(rect.left(), mid, rect.right(), mid) painter.restore() diff --git a/src/gui_qt/lib/forms.py b/src/gui_qt/lib/forms.py index 6a51c3a..c0b73aa 100644 --- a/src/gui_qt/lib/forms.py +++ b/src/gui_qt/lib/forms.py @@ -1,3 +1,5 @@ +from typing import Any + from numpy import inf from nmreval.utils.text import convert @@ -404,3 +406,21 @@ class ElideComboBox(QtWidgets.QComboBox): opt.currentText = painter.fontMetrics().elidedText(opt.currentText, QtCore.Qt.ElideRight, rect.width()) painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt) + + +class CheckCombobox(QtWidgets.QComboBox): + + def addItem(self, text: str, userData: Any=None) -> None: + super().addItem(text, userData=userData) + + item = self.model().item(self.count()-1) + item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) + item.setCheckState(QtCore.Qt.Checked) + + def addItems(self, text): + for text_i in text: + self.addItem(text_i) + + def isChecked(self, idx: int) -> bool: + return bool(self.model().item(idx).checkState()) + diff --git a/src/nmreval/data/dsc.py b/src/nmreval/data/dsc.py index 32f1fb1..383a2e0 100644 --- a/src/nmreval/data/dsc.py +++ b/src/nmreval/data/dsc.py @@ -40,11 +40,16 @@ class DSC(Points): regress = linregress(region.x[glass_regime], region.y[glass_regime]) glass_extrapolation = regress.slope * region.x + regress.intercept - region.y -= glass_extrapolation + import matplotlib.pyplot as plt + + plt.plot(region.x, glass_extrapolation) liquid_regime = (min_liquid < region.x) & (region.x < max_liquid) regress2 = linregress(region.x[liquid_regime], region.y[liquid_regime]) + region.y -= glass_extrapolation + + plt.plot(region.x, regress2.slope * region.x + regress2.intercept) real_area = cumulative_trapezoid(region.y, region.x, initial=0) real_area -= real_area[-1] diff --git a/src/nmreval/dsc/hodge.py b/src/nmreval/dsc/hodge.py new file mode 100644 index 0000000..b2dc3ba --- /dev/null +++ b/src/nmreval/dsc/hodge.py @@ -0,0 +1,26 @@ +import numpy as np +from scipy.stats import linregress + +from nmreval.data import Points +from nmreval.fit.minimizer import FitRoutine +from nmreval.lib.utils import ArrayLike +from nmreval.models import Arrhenius +from nmreval.utils import kB + + +def tau_hodge(tg: ArrayLike, rate: ArrayLike) -> Points: + rate = np.asanyarray(rate) / 60 + tg = np.asanyarray(tg) + fitter = FitRoutine() + fitter.set_model(Arrhenius) + d = fitter.add_data(tg, rate) + + init = linregress(1/tg, np.log(rate)) + + d.set_parameter([np.exp(init.intercept), init.slope*kB], fun_kwargs={'invt': None}) + + res = fitter.run()[0] + de = res.parameter['E_{A}'] + tau = kB*tg**2/np.abs(de.value)/rate + + return Points(x=1000/tg, y=tau, y_err=tau*de.error/np.abs(de.value), name='Hodge') diff --git a/src/nmreval/fit/parameter.py b/src/nmreval/fit/parameter.py index cc18c85..fcaa284 100644 --- a/src/nmreval/fit/parameter.py +++ b/src/nmreval/fit/parameter.py @@ -88,6 +88,7 @@ class Parameter: if self.lb <= value <= self.ub: self.value = value else: + print(value, self.lb, self.ub) raise ValueError('Value of parameter is outside bounds') self.init_val = value diff --git a/src/resources/_ui/tnmh_dialog.ui b/src/resources/_ui/tnmh_dialog.ui index 9e218cb..6d43351 100644 --- a/src/resources/_ui/tnmh_dialog.ui +++ b/src/resources/_ui/tnmh_dialog.ui @@ -14,8 +14,150 @@