Merge branch 'main' into fit_constraints

# Conflicts:
#	src/gui_qt/fit/fit_forms.py
#	src/gui_qt/main/management.py
#	src/nmreval/fit/minimizer.py
This commit is contained in:
Dominik Demuth 2023-09-11 18:18:30 +02:00
commit 869901596b
46 changed files with 428 additions and 216 deletions

View File

@ -32,7 +32,7 @@ AppDir:
arch: amd64 arch: amd64
allow_unauthenticated: true allow_unauthenticated: true
sources: sources:
- sourceline: 'deb [arch=amd64] http://mirror.infra.pkm/ bullseye main contrib non-free' - sourceline: 'deb [arch=amd64] http://ftp.uni-mainz.de/debian bullseye main contrib non-free'
include: include:
# for /usr/bin/env # for /usr/bin/env
@ -42,7 +42,7 @@ AppDir:
# - hicolor-icon-theme # - hicolor-icon-theme
- libatlas3-base - libatlas3-base
- gnuplot-nox - gnuplot-nox
- python3.9-minimal - python3-minimal
- python3-numpy - python3-numpy
- python3-scipy - python3-scipy
- python3-bsddb3 - python3-bsddb3

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '_ui/apod_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/apod_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# 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 from PyQt5 import QtCore, QtGui, QtWidgets
@ -23,7 +24,7 @@ class Ui_ApodEdit(object):
self.gridLayout.setContentsMargins(3, 3, 3, 3) self.gridLayout.setContentsMargins(3, 3, 3, 3)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.graphicsView = PlotWidget(ApodEdit) self.graphicsView = NMRPlotWidget(ApodEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -31,7 +32,7 @@ class Ui_ApodEdit(object):
self.graphicsView.setSizePolicy(sizePolicy) self.graphicsView.setSizePolicy(sizePolicy)
self.graphicsView.setObjectName("graphicsView") self.graphicsView.setObjectName("graphicsView")
self.gridLayout.addWidget(self.graphicsView, 2, 0, 1, 1) self.gridLayout.addWidget(self.graphicsView, 2, 0, 1, 1)
self.graphicsView_2 = PlotWidget(ApodEdit) self.graphicsView_2 = NMRPlotWidget(ApodEdit)
self.graphicsView_2.setObjectName("graphicsView_2") self.graphicsView_2.setObjectName("graphicsView_2")
self.gridLayout.addWidget(self.graphicsView_2, 2, 1, 1, 1) self.gridLayout.addWidget(self.graphicsView_2, 2, 1, 1, 1)
self.apodcombobox = QtWidgets.QComboBox(ApodEdit) self.apodcombobox = QtWidgets.QComboBox(ApodEdit)
@ -63,12 +64,12 @@ class Ui_ApodEdit(object):
self.gridLayout.addWidget(self.eqn_label, 0, 1, 1, 1) self.gridLayout.addWidget(self.eqn_label, 0, 1, 1, 1)
self.retranslateUi(ApodEdit) self.retranslateUi(ApodEdit)
self.buttonBox.accepted.connect(ApodEdit.accept) self.buttonBox.accepted.connect(ApodEdit.accept) # type: ignore
self.buttonBox.rejected.connect(ApodEdit.close) self.buttonBox.rejected.connect(ApodEdit.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(ApodEdit) QtCore.QMetaObject.connectSlotsByName(ApodEdit)
def retranslateUi(self, ApodEdit): def retranslateUi(self, ApodEdit):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization")) ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization"))
self.eqn_label.setText(_translate("ApodEdit", "TextLabel")) self.eqn_label.setText(_translate("ApodEdit", "TextLabel"))
from pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '_ui/baseline_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/baseline_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# 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 from PyQt5 import QtCore, QtGui, QtWidgets
@ -44,7 +45,7 @@ class Ui_SignalEdit(object):
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3) self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 3)
self.graphicsView = PlotWidget(SignalEdit) self.graphicsView = NMRPlotWidget(SignalEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -54,11 +55,11 @@ class Ui_SignalEdit(object):
self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1) self.gridLayout.addWidget(self.graphicsView, 0, 2, 1, 1)
self.retranslateUi(SignalEdit) self.retranslateUi(SignalEdit)
self.buttonBox.accepted.connect(SignalEdit.accept) self.buttonBox.accepted.connect(SignalEdit.accept) # type: ignore
self.buttonBox.rejected.connect(SignalEdit.close) self.buttonBox.rejected.connect(SignalEdit.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(SignalEdit) QtCore.QMetaObject.connectSlotsByName(SignalEdit)
def retranslateUi(self, SignalEdit): def retranslateUi(self, SignalEdit):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog")) SignalEdit.setWindowTitle(_translate("SignalEdit", "Dialog"))
from pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget

View File

@ -166,7 +166,7 @@ class Ui_Dialog(object):
self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget) self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.raw_graph = PlotWidget(self.layoutWidget) self.raw_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -175,7 +175,7 @@ class Ui_Dialog(object):
self.raw_graph.setMinimumSize(QtCore.QSize(300, 200)) self.raw_graph.setMinimumSize(QtCore.QSize(300, 200))
self.raw_graph.setObjectName("raw_graph") self.raw_graph.setObjectName("raw_graph")
self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1) self.gridLayout.addWidget(self.raw_graph, 0, 0, 1, 1)
self.calib_graph = PlotWidget(self.layoutWidget) self.calib_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -184,7 +184,7 @@ class Ui_Dialog(object):
self.calib_graph.setMinimumSize(QtCore.QSize(300, 200)) self.calib_graph.setMinimumSize(QtCore.QSize(300, 200))
self.calib_graph.setObjectName("calib_graph") self.calib_graph.setObjectName("calib_graph")
self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1) self.gridLayout.addWidget(self.calib_graph, 1, 0, 1, 1)
self.baseline_graph = PlotWidget(self.layoutWidget) self.baseline_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -193,7 +193,7 @@ class Ui_Dialog(object):
self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200)) self.baseline_graph.setMinimumSize(QtCore.QSize(300, 200))
self.baseline_graph.setObjectName("baseline_graph") self.baseline_graph.setObjectName("baseline_graph")
self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1) self.gridLayout.addWidget(self.baseline_graph, 0, 1, 1, 1)
self.end_graph = PlotWidget(self.layoutWidget) self.end_graph = NMRPlotWidget(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -228,4 +228,4 @@ class Ui_Dialog(object):
self.cp_checkBox.setText(_translate("Dialog", "Use reference to convert to heat capacity")) self.cp_checkBox.setText(_translate("Dialog", "Use reference to convert to heat capacity"))
self.ref_add_pushButton.setText(_translate("Dialog", "Add reference")) self.ref_add_pushButton.setText(_translate("Dialog", "Add reference"))
self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference")) self.ref_remove_pushButton.setText(_translate("Dialog", "Remove reference"))
from pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget

View File

@ -38,7 +38,6 @@ class Ui_FitDialog(object):
self.weight_combobox.addItem("") self.weight_combobox.addItem("")
self.weight_combobox.addItem("") self.weight_combobox.addItem("")
self.weight_combobox.addItem("") self.weight_combobox.addItem("")
self.weight_combobox.addItem("")
self.gridLayout_2.addWidget(self.weight_combobox, 6, 1, 1, 1) self.gridLayout_2.addWidget(self.weight_combobox, 6, 1, 1, 1)
self.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2) self.newmodel_button = QtWidgets.QPushButton(self.scrollAreaWidgetContents_2)
self.newmodel_button.setEnabled(False) self.newmodel_button.setEnabled(False)
@ -144,7 +143,6 @@ class Ui_FitDialog(object):
self.weight_combobox.setItemText(1, _translate("FitDialog", "y")) self.weight_combobox.setItemText(1, _translate("FitDialog", "y"))
self.weight_combobox.setItemText(2, _translate("FitDialog", "")) self.weight_combobox.setItemText(2, _translate("FitDialog", ""))
self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy")) self.weight_combobox.setItemText(3, _translate("FitDialog", "Δy"))
self.weight_combobox.setItemText(4, _translate("FitDialog", "log(y)"))
self.newmodel_button.setText(_translate("FitDialog", "New model")) self.newmodel_button.setText(_translate("FitDialog", "New model"))
self.deletemodel_button.setText(_translate("FitDialog", "Delete model")) self.deletemodel_button.setText(_translate("FitDialog", "Delete model"))
self.label_3.setText(_translate("FitDialog", "Weight")) self.label_3.setText(_translate("FitDialog", "Weight"))

View File

@ -210,7 +210,7 @@ class Ui_GraphWindow(object):
self.checkBox.setChecked(True) self.checkBox.setChecked(True)
self.checkBox.setObjectName("checkBox") self.checkBox.setObjectName("checkBox")
self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1) self.gridLayout.addWidget(self.checkBox, 0, 1, 1, 1)
self.graphic = PlotWidget(GraphWindow) self.graphic = NMRPlotWidget(GraphWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -274,5 +274,5 @@ class Ui_GraphWindow(object):
self.label_6.setText(_translate("GraphWindow", "X Axis")) self.label_6.setText(_translate("GraphWindow", "X Axis"))
self.label_7.setText(_translate("GraphWindow", "Y Axis")) self.label_7.setText(_translate("GraphWindow", "Y Axis"))
self.checkBox.setText(_translate("GraphWindow", "Show legend")) self.checkBox.setText(_translate("GraphWindow", "Show legend"))
from ..lib.graph_items import NMRPlotWidget
from ..lib.listwidget import QListWidgetSelect from ..lib.listwidget import QListWidgetSelect
from pyqtgraph import PlotWidget

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/phase_corr_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/phase_corr_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.15.4 # Created by: PyQt5 UI code generator 5.15.9
# #
# WARNING: Any manual changes made to this file will be lost when pyuic5 is # 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. # run again. Do not edit this file unless you know what you are doing.
@ -24,7 +24,7 @@ class Ui_SignalEdit(object):
self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setContentsMargins(6, 6, 6, 6)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
self.graphicsView = PlotWidget(SignalEdit) self.graphicsView = NMRPlotWidget(SignalEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -83,8 +83,8 @@ class Ui_SignalEdit(object):
self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1) self.gridLayout.addItem(spacerItem1, 1, 5, 1, 1)
self.retranslateUi(SignalEdit) self.retranslateUi(SignalEdit)
self.buttonBox.accepted.connect(SignalEdit.accept) self.buttonBox.accepted.connect(SignalEdit.accept) # type: ignore
self.buttonBox.rejected.connect(SignalEdit.close) self.buttonBox.rejected.connect(SignalEdit.close) # type: ignore
QtCore.QMetaObject.connectSlotsByName(SignalEdit) QtCore.QMetaObject.connectSlotsByName(SignalEdit)
def retranslateUi(self, SignalEdit): def retranslateUi(self, SignalEdit):
@ -94,4 +94,4 @@ class Ui_SignalEdit(object):
self.label_8.setText(_translate("SignalEdit", "Pivot")) self.label_8.setText(_translate("SignalEdit", "Pivot"))
self.label_6.setText(_translate("SignalEdit", "Phase 1")) self.label_6.setText(_translate("SignalEdit", "Phase 1"))
self.label.setText(_translate("SignalEdit", "Phase 0")) self.label.setText(_translate("SignalEdit", "Phase 0"))
from pyqtgraph import PlotWidget from ..lib.graph_items import NMRPlotWidget

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/shift_scale_dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/shift_scale_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# 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 from PyQt5 import QtCore, QtGui, QtWidgets
@ -162,7 +163,7 @@ class Ui_shift_dialog(object):
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2) self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalFrame_2)
self.verticalLayout_2.setSpacing(3) self.verticalLayout_2.setSpacing(3)
self.verticalLayout_2.setObjectName("verticalLayout_2") self.verticalLayout_2.setObjectName("verticalLayout_2")
self.graphicsView = PlotWidget(self.verticalFrame_2) self.graphicsView = NMRPlotWidget(self.verticalFrame_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -267,8 +268,8 @@ class Ui_shift_dialog(object):
self.retranslateUi(shift_dialog) self.retranslateUi(shift_dialog)
self.tabWidget.setCurrentIndex(0) self.tabWidget.setCurrentIndex(0)
self.buttonBox.accepted.connect(shift_dialog.accept) self.buttonBox.accepted.connect(shift_dialog.accept) # type: ignore
self.buttonBox.rejected.connect(shift_dialog.reject) self.buttonBox.rejected.connect(shift_dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(shift_dialog) QtCore.QMetaObject.connectSlotsByName(shift_dialog)
shift_dialog.setTabOrder(self.tabWidget, self.shift_table) shift_dialog.setTabOrder(self.tabWidget, self.shift_table)
shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox) shift_dialog.setTabOrder(self.shift_table, self.x_shift_spinbox)
@ -310,5 +311,5 @@ class Ui_shift_dialog(object):
self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data")) self.overwrite_checkbox.setText(_translate("shift_dialog", "Overwrite data"))
self.data_newgraph.setText(_translate("shift_dialog", "New graph")) self.data_newgraph.setText(_translate("shift_dialog", "New graph"))
self.values_newgraph.setText(_translate("shift_dialog", "New graph")) self.values_newgraph.setText(_translate("shift_dialog", "New graph"))
from ..lib.graph_items import NMRPlotWidget
from ..lib.spinboxes import SciSpinBox from ..lib.spinboxes import SciSpinBox
from pyqtgraph import PlotWidget

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/t1dialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/t1dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.9
# #
# 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 from PyQt5 import QtCore, QtGui, QtWidgets
@ -160,6 +161,7 @@ class Ui_t1dialog(object):
self.tau_combox.addItem("") self.tau_combox.addItem("")
self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2) self.gridLayout_4.addWidget(self.tau_combox, 1, 0, 1, 2)
self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3) self.checkBox_interpol = QtWidgets.QCheckBox(self.groupBox_3)
self.checkBox_interpol.setEnabled(False)
self.checkBox_interpol.setObjectName("checkBox_interpol") self.checkBox_interpol.setObjectName("checkBox_interpol")
self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2) self.gridLayout_4.addWidget(self.checkBox_interpol, 2, 0, 1, 2)
self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3) self.graph_checkbox = QtWidgets.QCheckBox(self.groupBox_3)

View File

@ -59,7 +59,7 @@ class Ui_DSCEvalDialog(object):
self.tg_tree.headerItem().setText(0, "1") self.tg_tree.headerItem().setText(0, "1")
self.tg_tree.header().setVisible(False) self.tg_tree.header().setVisible(False)
self.gridLayout_2.addWidget(self.tg_tree, 2, 0, 1, 1) self.gridLayout_2.addWidget(self.tg_tree, 2, 0, 1, 1)
self.dsc_plot = PlotWidget(self.page) self.dsc_plot = NMRPlotWidget(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -89,10 +89,10 @@ class Ui_DSCEvalDialog(object):
self.label_4 = QtWidgets.QLabel(self.page_2) self.label_4 = QtWidgets.QLabel(self.page_2)
self.label_4.setObjectName("label_4") self.label_4.setObjectName("label_4")
self.gridLayout_3.addWidget(self.label_4, 2, 0, 1, 1) self.gridLayout_3.addWidget(self.label_4, 2, 0, 1, 1)
self.tghodge_graph = PlotWidget(self.page_2) self.tghodge_graph = NMRPlotWidget(self.page_2)
self.tghodge_graph.setObjectName("tghodge_graph") self.tghodge_graph.setObjectName("tghodge_graph")
self.gridLayout_3.addWidget(self.tghodge_graph, 1, 0, 1, 1) self.gridLayout_3.addWidget(self.tghodge_graph, 1, 0, 1, 1)
self.tau_plot = PlotWidget(self.page_2) self.tau_plot = NMRPlotWidget(self.page_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
@ -155,7 +155,7 @@ class Ui_DSCEvalDialog(object):
self.page_3.setObjectName("page_3") self.page_3.setObjectName("page_3")
self.gridLayout_6 = QtWidgets.QGridLayout(self.page_3) self.gridLayout_6 = QtWidgets.QGridLayout(self.page_3)
self.gridLayout_6.setObjectName("gridLayout_6") self.gridLayout_6.setObjectName("gridLayout_6")
self.tnmh_graphics = PlotWidget(self.page_3) self.tnmh_graphics = NMRPlotWidget(self.page_3)
self.tnmh_graphics.setObjectName("tnmh_graphics") self.tnmh_graphics.setObjectName("tnmh_graphics")
self.gridLayout_6.addWidget(self.tnmh_graphics, 1, 0, 1, 2) self.gridLayout_6.addWidget(self.tnmh_graphics, 1, 0, 1, 2)
self.tnmh_tree = QtWidgets.QTreeWidget(self.page_3) self.tnmh_tree = QtWidgets.QTreeWidget(self.page_3)
@ -249,5 +249,5 @@ class Ui_DSCEvalDialog(object):
self.back_button.setText(_translate("DSCEvalDialog", "Back")) self.back_button.setText(_translate("DSCEvalDialog", "Back"))
self.next_button.setText(_translate("DSCEvalDialog", "Next")) self.next_button.setText(_translate("DSCEvalDialog", "Next"))
self.close_button.setText(_translate("DSCEvalDialog", "Close")) self.close_button.setText(_translate("DSCEvalDialog", "Close"))
from ..lib.graph_items import NMRPlotWidget
from ..lib.listwidget import QListWidgetSelect from ..lib.listwidget import QListWidgetSelect
from pyqtgraph import PlotWidget

View File

@ -463,6 +463,16 @@ class ExperimentContainer(QtCore.QObject):
return offset return offset
@plot_update
def shift_scale(self, shift_factor: tuple[float, float], scaling_factor: tuple[float, float]):
scale_x, scale_y = scaling_factor
shift_x, shift_y = shift_factor
self.data.x = self.data.x * scale_x + shift_x
self.data.y = self.data.y * scale_y + shift_y
self.data.y_err = self.data.y_err * scale_y
self.update({'shift': scaling_factor, 'scale': shift_factor})
def get_namespace(self, i: int = None, j: int = None) -> dict: def get_namespace(self, i: int = None, j: int = None) -> dict:
if (i is None) and (j is None): if (i is None) and (j is None):
prefix = '' prefix = ''

View File

@ -1,7 +1,7 @@
import numpy as np import numpy as np
from itertools import cycle from itertools import cycle
from pyqtgraph import mkColor, mkPen from pyqtgraph import mkColor, mkPen, mkBrush
from nmreval.lib.colors import Tab10 from nmreval.lib.colors import Tab10
@ -42,11 +42,17 @@ class QShift(QtWidgets.QDialog, Ui_shift_dialog):
def add_item(self, idx, name, x, y): def add_item(self, idx, name, x, y):
color = mkColor(next(self._colors).rgb()) color = mkColor(next(self._colors).rgb())
if np.iscomplexobj(y):
pl = [PlotItem(x=x, y=y.real, name=name, pen=mkPen(color=color)), if len(y) == 1:
PlotItem(x=x, y=y.imag, name=name, pen=mkPen(color=color))] sym_kwds = {'symbol': 'o', 'symbolBrush': mkBrush(color=color), 'symbolPen': mkPen(color=color)}
else: else:
pl = [PlotItem(x=x, y=y, name=name, pen=mkPen(color=color))] sym_kwds = {'symbol': None, 'symbolBrush': mkBrush(color=color), 'symbolPen': mkPen(color=color)}
if np.iscomplexobj(y):
pl = [PlotItem(x=x, y=y.real, name=name, pen=mkPen(color=color), **sym_kwds),
PlotItem(x=x, y=y.imag, name=name, pen=mkPen(color=color), **sym_kwds)]
else:
pl = [PlotItem(x=x, y=y, name=name, pen=mkPen(color=color), **sym_kwds)]
self.data[idx] = (pl, x, y) self.data[idx] = (pl, x, y)

View File

@ -6,12 +6,14 @@ from ..Qt import QtCore, QtWidgets, QtGui
from .._py.fitmodelwidget import Ui_FitParameter from .._py.fitmodelwidget import Ui_FitParameter
from .._py.save_fitmodel_dialog import Ui_SaveDialog from .._py.save_fitmodel_dialog import Ui_SaveDialog
from ..lib.iconloading import get_icon from ..lib.iconloading import get_icon
from ..lib.tables import TableWidget
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter): class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
value_requested = QtCore.pyqtSignal(object) value_requested = QtCore.pyqtSignal(object)
value_changed = QtCore.pyqtSignal(str) value_changed = QtCore.pyqtSignal(str)
state_changed = QtCore.pyqtSignal() state_changed = QtCore.pyqtSignal()
replace_single_value = QtCore.pyqtSignal(object)
def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False): def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
super().__init__(parent) super().__init__(parent)
@ -30,6 +32,7 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit()) self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
self.parameter_line.editingFinished.connect(self.update_parameter) self.parameter_line.editingFinished.connect(self.update_parameter)
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self)) self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None))
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text())) self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
self.fixed_check.toggled.connect(self.set_fixed) self.fixed_check.toggled.connect(self.set_fixed)
@ -311,7 +314,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
return funcs return funcs
class FitTableWidget(QtWidgets.QTableWidget): class FitTableWidget(TableWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)

View File

@ -78,6 +78,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
widgt.state_changed.connect(self.make_global) widgt.state_changed.connect(self.make_global)
widgt.value_requested.connect(self.look_for_value) widgt.value_requested.connect(self.look_for_value)
widgt.value_changed.connect(self.change_global_parameter) widgt.value_changed.connect(self.change_global_parameter)
widgt.replace_single_value.connect(self.delete_single_parameter)
self.global_parameter.append(widgt) self.global_parameter.append(widgt)
self.scrollwidget.layout().addWidget(widgt) self.scrollwidget.layout().addWidget(widgt)
@ -150,6 +151,13 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
if value is None: if value is None:
self.change_data(self.comboBox.currentIndex()) self.change_data(self.comboBox.currentIndex())
def delete_single_parameter(self):
idx = self.global_parameter.index(self.sender())
for i in range(self.comboBox.count()):
set_id = self.comboBox.itemData(i)
self.data_values[set_id][idx] = None
self.change_data(self.comboBox.currentIndex())
def change_single_choice(self, _, value, sender=None): def change_single_choice(self, _, value, sender=None):
if sender is None: if sender is None:
sender = self.sender() sender = self.sender()
@ -298,7 +306,8 @@ class ParameterSingleWidget(QtWidgets.QWidget):
self._name = name self._name = name
self.label.setText(convert(name)) self.label.setText(convert(name))
self.label.setToolTip('IIf this is bold then this parameter is only for this data. otherwise the general parameter is used and displayed') self.label.setToolTip('If this is bold then this parameter is only for this data. '
'Otherwise, the general parameter is used and displayed')
# self.value_line.setValidator(QtGui.QDoubleValidator()) # self.value_line.setValidator(QtGui.QDoubleValidator())
self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0) self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)

View File

@ -14,7 +14,7 @@ from gui_qt.lib.namespace import QNamespaceWidget
__all__ = ['QUserFitCreator'] __all__ = ['QUserFitCreator']
validator = QtGui.QRegExpValidator(QtCore.QRegExp('[A-Za-z]\S*')) validator = QtGui.QRegExpValidator(QtCore.QRegExp('[_A-Za-z][_A-Za-z0-9]*'))
pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE) pattern = re.compile(r'def func\(.*\):', flags=re.MULTILINE)
@ -145,6 +145,7 @@ class QUserFitCreator(QtWidgets.QDialog, Ui_Dialog):
self.classCreated.emit() self.classCreated.emit()
super().accept() super().accept()
class KwargsWidget(QtWidgets.QWidget): class KwargsWidget(QtWidgets.QWidget):
Changed = QtCore.pyqtSignal() Changed = QtCore.pyqtSignal()
@ -209,7 +210,7 @@ class KwargsWidget(QtWidgets.QWidget):
def get_strings(self) -> str: def get_strings(self) -> str:
kwargs = [] kwargs = []
if self.use_nuclei.isChecked(): if self.use_nuclei.isChecked():
kwargs.append("(r'\gamma', 'nucleus', gamma)") kwargs.append(r"(r'\gamma', 'nucleus', gamma)")
for i in range(self.choices.count()): for i in range(self.choices.count()):
kwargs.append(self.choices.widget(i).get_strings()) kwargs.append(self.choices.widget(i).get_strings())
@ -300,7 +301,7 @@ class ChoiceWidget(QtWidgets.QWidget):
def get_strings(self) -> str: def get_strings(self) -> str:
opts = [] opts = []
for i in range(self.table.rowCount()): for i in range(self.table.rowCount()):
name = self.table.item(i, 0).text() name = self.table.cellWidget(i, 0).text()
val = self._make_value(i) val = self._make_value(i)
opts.append(f'{name!r}: {val!r}') opts.append(f'{name!r}: {val!r}')

View File

@ -3,6 +3,7 @@ from math import isnan
from pyqtgraph import mkBrush, mkPen from pyqtgraph import mkBrush, mkPen
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ..lib.graph_items import logTickValues
from ..lib.utils import RdBuCMap from ..lib.utils import RdBuCMap
from ..Qt import QtWidgets, QtGui, QtCore from ..Qt import QtWidgets, QtGui, QtCore
@ -33,8 +34,12 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.graph_opts = {} self.graph_opts = {}
self.last_idx = None self.last_idx = None
self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual')
self.fit_plot = self.graphicsView.addPlot(row=1, col=0, title='Fit') self.fit_plot = self.graphicsView.addPlot(row=1, col=0, title='Fit')
self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual')
for orient in ['top', 'bottom', 'left', 'right']:
self.fit_plot.getAxis(orient).logTickValues = logTickValues
self.resid_plot.getAxis(orient).logTickValues = logTickValues
self.graphicsView.ci.layout.setRowStretchFactor(0, 1) self.graphicsView.ci.layout.setRowStretchFactor(0, 1)
self.graphicsView.ci.layout.setRowStretchFactor(1, 2) self.graphicsView.ci.layout.setRowStretchFactor(1, 2)

View File

@ -4,7 +4,7 @@ import itertools
import os import os
import uuid import uuid
from math import isnan from math import isfinite
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
@ -45,7 +45,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.id = str(uuid.uuid4()) self.id = str(uuid.uuid4())
self.sets = [] self.sets = []
self.active = [] self._active = []
self.real_plots = {} self.real_plots = {}
self.imag_plots = {} self.imag_plots = {}
@ -75,6 +75,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
self.scene.contextMenu[0].disconnect() self.scene.contextMenu[0].disconnect()
self.scene.contextMenu[0].triggered.connect(self.export_dialog) self.scene.contextMenu[0].triggered.connect(self.export_dialog)
self.bwbutton.toggled.connect(self.change_background)
def _init_gui(self): def _init_gui(self):
self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter))) self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
@ -114,11 +116,11 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return iter(self.active) return iter(self.active)
def __len__(self): def __len__(self):
return len(self.active) return len(self._active)
def curves(self) -> tuple: def curves(self) -> tuple:
for set_id in self.sets: for set_id in self.sets:
if set_id in self.active: if set_id in self._active:
if self.real_button.isChecked(): if self.real_button.isChecked():
if self.error_plots[set_id] is not None: if self.error_plots[set_id] is not None:
yield self.real_plots[set_id], self.error_plots[set_id] yield self.real_plots[set_id], self.error_plots[set_id]
@ -144,7 +146,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
tmp = [np.nan, np.nan] tmp = [np.nan, np.nan]
for j, x in enumerate(r[i]): for j, x in enumerate(r[i]):
try: try:
tmp[j] = 10**x tmp[j] = 10**min(x, 199)
except OverflowError: except OverflowError:
pass pass
r[i] = tuple(tmp) r[i] = tuple(tmp)
@ -153,6 +155,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return tuple(r) return tuple(r)
@property
def active(self) -> list:
return [set_id for set_id in self.sets if set_id in self._active]
@active.setter
def active(self, value: list):
self._active = value
def block(self, state: bool): def block(self, state: bool):
self._block = state self._block = state
@ -201,8 +211,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
for plot in [self.real_plots, self.imag_plots, self.error_plots]: for plot in [self.real_plots, self.imag_plots, self.error_plots]:
self.graphic.removeItem(plot[n]) self.graphic.removeItem(plot[n])
if n in self.active: if n in self._active:
self.active.remove(n) self._active.remove(n)
# remove from label list # remove from label list
self.listWidget.blockSignals(True) self.listWidget.blockSignals(True)
@ -246,8 +256,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return return
for a in idlist: for a in idlist:
if a not in self.active: if a not in self._active:
self.active.append(a) self._active.append(a)
for (bttn, plot_dic) in [ for (bttn, plot_dic) in [
(self.real_button, self.real_plots), (self.real_button, self.real_plots),
@ -266,8 +276,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return return
for r in idlist: for r in idlist:
if r in self.active: if r in self._active:
self.active.remove(r) self._active.remove(r)
for plt in [self.real_plots, self.imag_plots, self.error_plots]: for plt in [self.real_plots, self.imag_plots, self.error_plots]:
item = plt[r] item = plt[r]
@ -289,7 +299,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
else: else:
func = self.graphic.removeItem func = self.graphic.removeItem
for a in self.active: for a in self._active:
item = plots[a] item = plots[a]
if item is not None: if item is not None:
func(item) func(item)
@ -306,12 +316,12 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
return return
if visible: if visible:
for a in self.active: for a in self._active:
item = self.error_plots[a] item = self.error_plots[a]
if (item is not None) and (item not in self.graphic.items()): if (item is not None) and (item not in self.graphic.items()):
self.graphic.addItem(item) self.graphic.addItem(item)
else: else:
for a in self.active: for a in self._active:
item = self.error_plots[a] item = self.error_plots[a]
if (item is not None) and (item in self.graphic.items()): if (item is not None) and (item in self.graphic.items()):
self.graphic.removeItem(item) self.graphic.removeItem(item)
@ -423,6 +433,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
def set_logmode(self, xmode: bool = None, ymode: bool = None): def set_logmode(self, xmode: bool = None, ymode: bool = None):
r = self.ranges r = self.ranges
self.plotItem.setXRange(*r[0])
self.plotItem.setYRange(*r[1])
if xmode is None: if xmode is None:
xmode = self.plotItem.ctrl.logXCheck.isChecked() xmode = self.plotItem.ctrl.logXCheck.isChecked()
else: else:
@ -439,6 +452,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
item.logmode[0] = self.log[:] item.logmode[0] = self.log[:]
self.plotItem.updateLogMode() self.plotItem.updateLogMode()
self.set_range(x=r[0], y=r[1])
self.plotItem.enableAutoRange() self.plotItem.enableAutoRange()
@ -481,9 +495,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
with errstate(all='ignore'): with errstate(all='ignore'):
xy = [log10(val) for val in xy] xy = [log10(val) for val in xy]
if isnan(xy[1]): if not isfinite(xy[1]):
xy = [-1, 1] xy = [-1, 1]
elif isnan(xy[0]): elif not isfinite(xy[0]):
xy[0] = xy[1]-4 xy[0] = xy[1]-4
func(xy[0], xy[1], padding=0) func(xy[0], xy[1], padding=0)
@ -669,7 +683,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
'legend': self.legend.isVisible(), 'legend': self.legend.isVisible(),
'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()), 'plots': (self.real_button.isChecked(), self.imag_button.isChecked(), self.error_button.isChecked()),
'children': self.sets, 'children': self.sets,
'active': self.active, 'active': self._active,
} }
in_legend = [] in_legend = []
@ -760,8 +774,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
if y is not None: if y is not None:
self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()}) self.plotItem.setLabel('left', y, **{'font-size': '10pt', 'color': self._fgcolor.name()})
@QtCore.pyqtSlot(bool, name='on_bwbutton_toggled')
def change_background(self, _): def change_background(self, _):
temp = self._fgcolor, self._bgcolor temp = self._fgcolor, self._bgcolor
self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1]) self.set_color(foreground=self._prev_colors[0], background=self._prev_colors[1])
self._prev_colors = temp self._prev_colors = temp

View File

@ -4,6 +4,7 @@ from nmreval.lib.lines import LineStyle
from nmreval.lib.symbols import SymbolStyle from nmreval.lib.symbols import SymbolStyle
from nmreval.data.points import Points from nmreval.data.points import Points
from nmreval.io.graceeditor import GraceEditor from nmreval.io.graceeditor import GraceEditor
from nmreval.utils.text import convert
from ..Qt import QtCore, QtWidgets, QtGui from ..Qt import QtCore, QtWidgets, QtGui
from .._py.gracereader import Ui_Dialog from .._py.gracereader import Ui_Dialog
@ -55,7 +56,7 @@ class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
if ds is None: if ds is None:
continue continue
item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {gset.get_property("legend")}, ' item_2 = QtWidgets.QTreeWidgetItem([f'Set {gset.idx} (Label: {convert(gset.get_property("legend"), old="agr", new="str")}, '
f'shape: {ds.shape})']) f'shape: {ds.shape})'])
item_2.setCheckState(0, QtCore.Qt.Checked) item_2.setCheckState(0, QtCore.Qt.Checked)
item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx)) item_2.setData(0, QtCore.Qt.UserRole, (graphs.idx, gset.idx))
@ -94,6 +95,7 @@ class QGraceReader(QtWidgets.QDialog, Ui_Dialog):
label = '' label = ''
else: else:
label = label.replace('"', '') label = label.replace('"', '')
label = convert(label, old='agr', new='str')
sd = s.data sd = s.data
sd = np.atleast_2d(sd) sd = np.atleast_2d(sd)
if s.type == 'xydy': if s.type == 'xydy':

View File

@ -237,7 +237,7 @@ class CodeEditor(QtWidgets.QPlainTextEdit):
if block.isVisible() and (bottom >= evt.rect().top()): if block.isVisible() and (bottom >= evt.rect().top()):
number = str(block_number + 1) number = str(block_number + 1)
painter.setPen(QtCore.Qt.black) painter.setPen(QtCore.Qt.black)
painter.drawText(0, top, self.current_linenumber.width() - 3, height, painter.drawText(0, int(top), self.current_linenumber.width() - 3, height,
QtCore.Qt.AlignRight, number) QtCore.Qt.AlignRight, number)
block = block.next() block = block.next()

View File

@ -52,19 +52,24 @@ class QDelayWidget(QtWidgets.QWidget):
class LineEdit(QtWidgets.QLineEdit): class LineEdit(QtWidgets.QLineEdit):
values_requested = QtCore.pyqtSignal() values_requested = QtCore.pyqtSignal()
replace_single_values = QtCore.pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
def contextMenuEvent(self, evt): def contextMenuEvent(self, evt):
menu = self.createStandardContextMenu() menu = self.createStandardContextMenu()
request_action = menu.addAction('Use value of sets') request_action = menu.addAction('Use numeric value of sets')
set_value_action = menu.addAction('Replace single set values')
action = menu.exec(evt.globalPos()) action = menu.exec(evt.globalPos())
if action == request_action: if action == request_action:
self.values_requested.emit() self.values_requested.emit()
elif action == set_value_action:
self.replace_single_values.emit()
class LineEditPost(QtWidgets.QLineEdit): class LineEditPost(QtWidgets.QLineEdit):
values_requested = QtCore.pyqtSignal() values_requested = QtCore.pyqtSignal()

View File

@ -0,0 +1,53 @@
from numpy import log10, arange, floor, ceil
from pyqtgraph import PlotWidget, PlotItem
__all__ = ['NMRPlotWidget', 'logTickValues']
class NMRPlotWidget(PlotWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for orient in ['top', 'bottom', 'left', 'right']:
# BAD HACK!!! but seems to work, see function for explanation
self.plotItem.getAxis(orient).logTickValues = logTickValues
class NMRPlotItem(PlotItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for orient in ['top', 'bottom', 'left', 'right']:
self.plotItem.getAxis(orient).logTickValues = logTickValues
def logTickValues(minVal, maxVal, size, stdTicks):
# TODO FIND A BETTER SOLUTION!!!
# Sometimes minVal and maxVal are not log-scaled values and the loop from v1 to v2 is humongous,
# The minor list then fills the RAM completely and freezes everything
# Until there is a better solution, we overwrite this function for every AxesItem
# and do not draw minor ticks at all if there are too many
# start with the tick spacing given by tickValues().
# Any level whose spacing is < 1 needs to be converted to log scale
ticks = []
for (spacing, t) in stdTicks:
if spacing >= 1.0:
ticks.append((spacing, t))
if len(ticks) < 3:
v1 = int(floor(minVal))
v2 = int(ceil(maxVal))
# major = list(range(v1+1, v2))
minor = []
if v2 - v1 < 400:
for v in range(v1, v2):
minor.extend(v + log10(arange(1, 10)))
minor = [x for x in minor if minVal < x < maxVal]
ticks.append((None, minor))
return ticks

View File

@ -375,9 +375,6 @@ class PlotItem(PlotDataItem):
class RegionItem(LinearRegionItem): class RegionItem(LinearRegionItem):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.mode = kwargs.pop('mode', 'half') self.mode = kwargs.pop('mode', 'half')
print(args, kwargs)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.logmode = False self.logmode = False
@ -475,11 +472,18 @@ class LegendItemBlock(LegendItem):
def mouseDragEvent(self, ev): def mouseDragEvent(self, ev):
if ev.button() == QtCore.Qt.LeftButton: if ev.button() == QtCore.Qt.LeftButton:
ev.accept() ev.accept()
dpos = ev.pos() - ev.lastPos() dpos = ev.pos() - ev.lastPos()
upper_left = self.pos()
lower_right = self.pos()
lower_right.setX(lower_right.x() + self.width())
lower_right.setY(lower_right.y() + self.height())
vb_rect = self.parentItem().rect() vb_rect = self.parentItem().rect()
pos = self.pos()
# upper left corner and a point a little more to the bottom right must be inside # upper left and lower right corner must be inside viewbox
if vb_rect.contains(pos+dpos) and vb_rect.contains(pos+dpos+QtCore.QPointF(20., 20.)): if vb_rect.contains(upper_left + dpos) and vb_rect.contains(lower_right + dpos):
self.autoAnchor(pos + dpos) self.autoAnchor(upper_left + dpos)
else: else:
self.autoAnchor(pos) self.autoAnchor(upper_left)

View File

@ -28,4 +28,17 @@ class TreeWidget(QtWidgets.QTreeWidget):
continue continue
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked) it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked)
else: else:
super().keyPressEvent(evt) super().keyPressEvent(evt)
class TableWidget(QtWidgets.QTableWidget):
def keyPressEvent(self, evt: QtGui.QKeyEvent):
if evt.key() == QtCore.Qt.Key.Key_Space:
for idx in self.selectedIndexes():
item = self.itemFromIndex(idx)
cs = item.checkState()
item.setCheckState(QtCore.Qt.CheckState.Unchecked if cs == QtCore.Qt.CheckState.Checked
else QtCore.Qt.CheckState.Checked)
else:
super().keyPressEvent(evt)

View File

@ -211,7 +211,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.ptsselectwidget.points_selected.connect(self.management.extract_points) self.ptsselectwidget.points_selected.connect(self.management.extract_points)
self.t1tauwidget.newData.connect(self.management.add_new_data)
self.t1tauwidget.newData.connect(self.management.add_new_data) self.t1tauwidget.newData.connect(self.management.add_new_data)
self.editsignalwidget.do_something.connect(self.management.apply) self.editsignalwidget.do_something.connect(self.management.apply)
@ -917,10 +916,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.action_odr_fit: 'odr' self.action_odr_fit: 'odr'
}[self.ac_group.checkedAction()] }[self.ac_group.checkedAction()]
self.fit_dialog.fit_button.setEnabled(False) fit_is_ready = self.management.prepare_fit(parameter, links, fit_options)
self.management.start_fit(parameter, links, fit_options) if fit_is_ready:
self.status.setText('Fit running...'.format(self.management.fitter.step)) self.management.start_fit()
self.fit_timer.start(500) self.fit_dialog.fit_button.setEnabled(False)
self.status.setText('Fit running...'.format(self.management.fitter.step))
self.fit_timer.start(500)
@QtCore.pyqtSlot(dict, int, bool) @QtCore.pyqtSlot(dict, int, bool)
def show_fit_preview(self, funcs: dict, num: int, show: bool): def show_fit_preview(self, funcs: dict, num: int, show: bool):

View File

@ -58,11 +58,18 @@ class GraphDict(OrderedDict):
def list(self): def list(self):
return [(k, v.title) for k, v in self.items()] return [(k, v.title) for k, v in self.items()]
def active(self, key: str): def active(self, key: str, return_val: str = 'both'):
if key: if not key:
return [(self._data[i].id, self._data[i].name) for i in self[key]]
else:
return [] return []
else:
if return_val == 'both':
return [(self._data[i].id, self._data[i].name) for i in self[key]]
elif return_val == 'id':
return [self._data[i].id for i in self[key]]
elif return_val == 'name':
return [self._data[i].name for i in self[key]]
else:
raise ValueError(f'return_val got wrong value {return_val!r}')
def current_sets(self, key: str): def current_sets(self, key: str):
if key: if key:
@ -148,6 +155,10 @@ class UpperManagement(QtCore.QObject):
def active_sets(self): def active_sets(self):
return self.graphs.active(self.current_graph) return self.graphs.active(self.current_graph)
@property
def active_id(self):
return self.graphs.active(self.current_graph, return_val='id')
def get_attributes(self, graph_id: str, attr: str) -> dict[str, Any]: def get_attributes(self, graph_id: str, attr: str) -> dict[str, Any]:
return {self.data[i].id: getattr(self.data[i], attr) for i in self.graphs[graph_id].sets} return {self.data[i].id: getattr(self.data[i], attr) for i in self.graphs[graph_id].sets}
@ -413,9 +424,9 @@ class UpperManagement(QtCore.QObject):
for d in self.data.values(): for d in self.data.values():
d.mask = np.ones_like(d.mask, dtype=bool) d.mask = np.ones_like(d.mask, dtype=bool)
def start_fit(self, parameter: dict, links: list, fit_options: dict): def prepare_fit(self, parameter: dict, links: list, fit_options: dict) -> bool:
if self._fit_active: if self._fit_active:
return return False
self.__fit_options = (parameter, links, fit_options) self.__fit_options = (parameter, links, fit_options)
@ -423,60 +434,84 @@ class UpperManagement(QtCore.QObject):
models = {} models = {}
fit_limits = fit_options['limits'] fit_limits = fit_options['limits']
fit_mode = fit_options['fit_mode'] fit_mode = fit_options['fit_mode']
we = fit_options['we'] we_option = fit_options['we']
for model_id, model_p in parameter.items(): self.fitter.fitmethod = fit_mode
m = Model(model_p['func'])
models[model_id] = m
m_complex = model_p['complex'] # all-encompassing error catch
try:
for model_id, model_p in parameter.items():
m = Model(model_p['func'])
models[model_id] = m
for set_id, set_params in model_p['parameter'].items(): m_complex = model_p['complex']
data_i = self.data[set_id]
if we.lower() == 'deltay':
we = data_i.y_err**2
if m_complex is None or m_complex == 1: # sets are not in active order but in order they first appeared in fit dialog
_y = data_i.y.real # iterate over order of set id in active order and access parameter inside loop
elif m_complex == 2 and np.iscomplexobj(data_i.y): # instead of directly looping
_y = data_i.y.imag list_ids = list(model_p['parameter'].keys())
else: set_order = [self.active_id.index(i) for i in list_ids]
_y = data_i.y for pos in set_order:
set_id = list_ids[pos]
_x = data_i.x data_i = self.data[set_id]
set_params = model_p['parameter'][set_id]
if fit_limits == 'none': if we_option.lower() == 'deltay':
inside = slice(None) we = data_i.y_err**2
elif fit_limits == 'x': else:
x_lim, _ = self.graphs[self.current_graph].ranges we = we_option
inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1]))
else:
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1]))
if isinstance(we, str): if m_complex is None or m_complex == 1:
d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id) _y = data_i.y.real
else: elif m_complex == 2 and np.iscomplexobj(data_i.y):
d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id) _y = data_i.y.imag
else:
_y = data_i.y
d.set_model(m) _x = data_i.x
d.set_parameter(set_params[0], var=model_p['var'],
lb=model_p['lb'], ub=model_p['ub'],
fun_kwargs=set_params[1])
self.fitter.add_data(d) if fit_limits == 'none':
inside = slice(None)
elif fit_limits == 'x':
x_lim, _ = self.graphs[self.current_graph].ranges
inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1]))
else:
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1]))
model_globs = model_p['glob'] if isinstance(we, str):
if model_globs: d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id)
for parameter_args in zip(*model_globs.values()): else:
m.set_global_parameter(**{k: v for k, v in zip(model_globs.keys(), parameter_args)}) d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id)
# m.set_global_parameter(**model_p['glob'])
for links_i in links: d.set_model(m)
self.fitter.set_link_parameter((models[links_i[0]], links_i[1]), d.set_parameter(set_params[0], var=model_p['var'],
(models[links_i[2]], links_i[3])) lb=model_p['lb'], ub=model_p['ub'],
fun_kwargs=set_params[1])
self.fitter.add_data(d)
model_globs = model_p['glob']
if model_globs:
for parameter_args in zip(*model_globs.values()):
m.set_global_parameter(**{k: v for k, v in zip(model_globs.keys(), parameter_args)})
# m.set_global_parameter(**model_p['glob'])
for links_i in links:
self.fitter.set_link_parameter((models[links_i[0]], links_i[1]),
(models[links_i[2]], links_i[3]))
return True
except Exception as e:
logger.error('Fit preparation failed', *e.args)
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(),
'Fit prep failed',
f'Fit preparation failed with message\n{e.args}')
return False
def start_fit(self):
with busy_cursor(): with busy_cursor():
self.fit_worker = FitWorker(self.fitter, fit_mode) self.fit_worker = FitWorker(self.fitter)
self.fit_thread = QtCore.QThread() self.fit_thread = QtCore.QThread()
self.fit_worker.moveToThread(self.fit_thread) self.fit_worker.moveToThread(self.fit_thread)
@ -512,7 +547,8 @@ class UpperManagement(QtCore.QObject):
for set_id, set_parameter in parameter.items(): for set_id, set_parameter in parameter.items():
new_values = [v.value for v in res[set_id].parameter.values()] new_values = [v.value for v in res[set_id].parameter.values()]
parameter[set_id] = (new_values, set_parameter[1]) parameter[set_id] = (new_values, set_parameter[1])
self.start_fit(*self.__fit_options) if self.prepare_fit(*self.__fit_options):
self.start_fit()
def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: bool, extrapolate: list) -> None: def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: bool, extrapolate: list) -> None:
""" """
@ -637,7 +673,7 @@ class UpperManagement(QtCore.QObject):
def save_fit_parameter(self, fname: str | pathlib.Path, fit_sets: list[str] = None): def save_fit_parameter(self, fname: str | pathlib.Path, fit_sets: list[str] = None):
if fit_sets is None: if fit_sets is None:
fit_sets = [s for (s, _) in self.active_sets] fit_sets = [s for s in self.active_id]
for set_id in fit_sets: for set_id in fit_sets:
data = self.data[set_id] data = self.data[set_id]
@ -843,13 +879,10 @@ class UpperManagement(QtCore.QObject):
d_k = self.data[k] d_k = self.data[k]
if copy_data is None: if copy_data is None:
d_k.x = d_k.x*v[1][0] + v[0][0] d_k.shift_scale(v[0], v[1])
d_k.y = d_k.y*v[1][1] + v[0][1]
else: else:
new_data = d_k.copy(full=True) new_data = d_k.copy(full=True)
new_data.update({'shift': v[0], 'scale': v[1]}) new_data.shift_scale(v[0], v[1])
new_data.data.x = new_data.x*v[1][0] + v[0][0]
new_data.y = new_data.y*v[1][1] + v[0][1]
sid = self.add(new_data) sid = self.add(new_data)
sid_list.append(sid) sid_list.append(sid)
@ -1009,7 +1042,7 @@ class UpperManagement(QtCore.QObject):
def show_statistics(self, mode): def show_statistics(self, mode):
x, y, = [], [] x, y, = [], []
for i, _ in self.active_sets: for i in self.active_id:
_temp = self.data[i] _temp = self.data[i]
try: try:
x.append(float(_temp.name)) x.append(float(_temp.name))
@ -1020,7 +1053,7 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def calc_magn(self): def calc_magn(self):
new_id = [] new_id = []
for k, _ in self.active_sets: for k in self.active_id:
dataset = self.data[k] dataset = self.data[k]
if isinstance(dataset, SignalContainer): if isinstance(dataset, SignalContainer):
new_value = dataset.copy(full=True) new_value = dataset.copy(full=True)
@ -1032,7 +1065,7 @@ class UpperManagement(QtCore.QObject):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def center(self): def center(self):
new_id = [] new_id = []
for k, _ in self.active_sets: for k in self.active_id:
new_value = self.data[k].copy(full=True) new_value = self.data[k].copy(full=True)
new_value.x -= new_value.x[np.argmax(new_value.y.real)] new_value.x -= new_value.x[np.argmax(new_value.y.real)]
new_id.append(self.add(new_value)) new_id.append(self.add(new_value))
@ -1071,7 +1104,7 @@ class UpperManagement(QtCore.QObject):
def bds_deriv(self): def bds_deriv(self):
new_sets = [] new_sets = []
for (set_id, _) in self.active_sets: for set_id in self.active_id:
data_i = self.data[set_id] data_i = self.data[set_id]
diff = data_i.data.diff(log=True) diff = data_i.data.diff(log=True)
new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real) new_data = Points(x=diff.x, y=-np.pi/2*diff.y.real)
@ -1098,7 +1131,7 @@ class UpperManagement(QtCore.QObject):
self.newData.emit(new_sets, kwargs['graph']) self.newData.emit(new_sets, kwargs['graph'])
def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False): def skip_points(self, offset: int, step: int, invert: bool = False, copy: bool = False):
for k, _ in self.active_sets: for k in self.active_id:
src = self.data[k] src = self.data[k]
if invert: if invert:
mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0 mask = np.mod(np.arange(offset, src.x.size+offset), step) != 0
@ -1253,16 +1286,15 @@ class UpperManagement(QtCore.QObject):
class FitWorker(QtCore.QObject): class FitWorker(QtCore.QObject):
finished = QtCore.pyqtSignal(list, bool) finished = QtCore.pyqtSignal(list, bool)
def __init__(self, fitter, mode): def __init__(self, fitter):
super().__init__() super().__init__()
self.fitter = fitter self.fitter = fitter
self.mode = mode
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def run(self): def run(self):
try: try:
res = self.fitter.run(mode=self.mode) res = self.fitter.run()
success = True success = True
except Exception as e: except Exception as e:
res = [e] res = [e]

View File

@ -19,7 +19,7 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
self.graphs = {} self.graphs = {}
self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW] self.specdens = [ColeCole, ColeDavidson, HavriliakNegami, KWW, LogGaussian]
self.coupling = [Quadrupolar, HomoDipolar, Czjzek] self.coupling = [Quadrupolar, HomoDipolar, Czjzek]
self.tau_parameter = [] self.tau_parameter = []
@ -199,3 +199,9 @@ class QRelaxCalc(QtWidgets.QDialog, Ui_Dialog):
def accept(self): def accept(self):
self.calc_relaxation() self.calc_relaxation()
super().accept() super().accept()
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
def on_buttonBox_clicked(self, button: QtWidgets.QAbstractButton):
role = self.buttonBox.buttonRole(button)
if role == self.buttonBox.ApplyRole:
self.calc_relaxation()

View File

@ -61,6 +61,8 @@ class QT1Widget(QtWidgets.QDialog, Ui_t1dialog):
self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model()) self.freq_combox.currentIndexChanged.connect(lambda x: self.update_model())
self.freq_spinbox.valueChanged.connect(lambda x: self.update_model()) self.freq_spinbox.valueChanged.connect(lambda x: self.update_model())
self.checkBox_interpol.setVisible(False)
self.update_specdens(0) self.update_specdens(0)
self.update_coupling(0) self.update_coupling(0)

View File

@ -54,6 +54,9 @@ class ColeCole(Distribution):
tau (array_like): tau (array_like):
alpha (float): alpha (float):
""" """
if alpha == 1:
return tau / (1 + omega**2 * tau**2)
omtau = (omega*tau)**alpha omtau = (omega*tau)**alpha
return np.sin(alpha*np.pi/2) * omtau / (1 + omtau**2 + 2*np.cos(alpha*np.pi/2)*omtau) / omega return np.sin(alpha*np.pi/2) * omtau / (1 + omtau**2 + 2*np.cos(alpha*np.pi/2)*omtau) / omega

View File

@ -5,6 +5,7 @@ from typing import Callable
import numpy as np import numpy as np
from scipy import LowLevelCallable from scipy import LowLevelCallable
from scipy.special import erf
from nmreval.lib.utils import ArrayLike from nmreval.lib.utils import ArrayLike
@ -32,7 +33,7 @@ class LogGaussian(Distribution):
return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma return np.exp(-0.5*(np.log(tau/tau0)/sigma)**2)/np.sqrt(2*np.pi)/sigma
@staticmethod @staticmethod
def correlation(t, tau0, sigma: float): def correlation(t: ArrayLike, tau0: ArrayLike, sigma: float):
_t = np.atleast_1d(t) _t = np.atleast_1d(t)
_tau = np.atleast_1d(tau0) _tau = np.atleast_1d(tau0)
@ -44,7 +45,7 @@ class LogGaussian(Distribution):
return res.squeeze() return res.squeeze()
@staticmethod @staticmethod
def susceptibility(omega, tau0, sigma: float): def susceptibility(omega: ArrayLike, tau0: ArrayLike, sigma: float):
_omega = np.atleast_1d(omega) _omega = np.atleast_1d(omega)
_tau = np.atleast_1d(tau0) _tau = np.atleast_1d(tau0)
@ -68,6 +69,7 @@ class LogGaussian(Distribution):
ret_val = _integration_parallel(_omega, _tau, sigma, _integrate_process_imag) ret_val = _integration_parallel(_omega, _tau, sigma, _integrate_process_imag)
ret_val /= _omega[:, None] ret_val /= _omega[:, None]
ret_val[_omega == 0, :] = tau[None, :] * np.exp(sigma**2 / 2)
return ret_val.squeeze() return ret_val.squeeze()
@ -113,18 +115,16 @@ def _integrate_susc_c(lowfunc, highfunc, omega, tau, sigma):
return res return res
def _integrate_process_imag(args): def _integrate_process_imag(omega, tau, sigma):
omega_i, tau_j, sigma = args area = quad(_integrand_freq_imag_high, 0, 50, args=(omega, tau, sigma), epsabs=1e-12, epsrel=1e-12)[0]
area = quad(_integrand_freq_imag_high, 0, 50, args=(omega_i, tau_j, sigma), epsabs=1e-12, epsrel=1e-12)[0] area += quad(_integrand_freq_imag_low, -50, 0, args=(omega, tau, sigma), epsabs=1e-12, epsrel=1e-12)[0]
area += quad(_integrand_freq_imag_low, -50, 0, args=(omega_i, tau_j, sigma), epsabs=1e-12, epsrel=1e-12)[0]
return area return area
def _integrate_process_real(args): def _integrate_process_real(omega: float, tau: float, sigma: float):
omega_i, tau_j, sigma = args area = quad(_integrand_freq_real_high, 0, 50, args=(omega, tau, sigma))[0]
area = quad(_integrand_freq_real_high, 0, 50, args=(omega_i, tau_j, sigma))[0] area += quad(_integrand_freq_real_low, -50, 0, args=(omega, tau, sigma))[0]
area += quad(_integrand_freq_real_low, -50, 0, args=(omega_i, tau_j, sigma))[0]
return area return area
@ -145,9 +145,8 @@ def _integrate_correlation_c(t, tau, sigma):
return res return res
def _integrate_process_time(args): def _integrate_process_time(omega, tau, sigma):
omega_i, tau_j, sigma = args return quad(_integrand_time, -50, 50, args=(omega, tau, sigma), epsabs=1e-12, epsrel=1e-12)[0]
return quad(_integrand_time, -50, 50, args=(omega_i, tau_j, sigma), epsabs=1e-12, epsrel=1e-12)[0]
def _integrand_time(u, t, tau, sigma): def _integrand_time(u, t, tau, sigma):

View File

@ -24,9 +24,10 @@ class ModelFactory:
param_len.append(len(func['func'].params)) param_len.append(len(func['func'].params))
if func['children']: if func['children']:
right, _, _ = ModelFactory.create_from_list(func['children'], left=func['func'], left_cnt=func['pos'], right, _, _ = ModelFactory.create_from_list(func['children'], left_cnt=func['pos'],
func_order=func_order, param_len=param_len) func_order=func_order, param_len=param_len)
right_cnt = None right_cnt = None
right = MultiModel(func['func'], right, func['children'][0]['op'], left_idx=func['cnt'], right_idx=None)
else: else:
right = func['func'] right = func['func']
right_cnt = func['cnt'] right_cnt = func['cnt']
@ -46,7 +47,13 @@ class MultiModel:
str_op = {'+': operator.add, '*': operator.mul, '-': operator.sub, '/': operator.truediv} str_op = {'+': operator.add, '*': operator.mul, '-': operator.sub, '/': operator.truediv}
int_op = {0: operator.add, 1: operator.mul, 2: operator.sub, 3: operator.truediv} int_op = {0: operator.add, 1: operator.mul, 2: operator.sub, 3: operator.truediv}
def __init__(self, left: Any, right: Any, op: str | Callable | int = '+', left_idx=0, right_idx=1): def __init__(self,
left: Any,
right: Any,
op: str | Callable | int = '+',
left_idx: int | None = 0,
right_idx: int | None = 1,
):
self._left = left self._left = left
self._right = right self._right = right

View File

@ -82,7 +82,7 @@ def _update_parameter(data: Data, varied_keys: list[str], parameter: list[float]
class FitRoutine(object): class FitRoutine(object):
def __init__(self, mode='lsq'): def __init__(self, mode='lsq'):
self._fitmethod = mode self.fitmethod = mode
self.data = [] self.data = []
self.fit_model = None self.fit_model = None
self._no_own_model = [] self._no_own_model = []
@ -220,9 +220,12 @@ class FitRoutine(object):
logger.info('Fit aborted by user') logger.info('Fit aborted by user')
self._abort = True self._abort = True
def run(self, mode: str = 'lsq'): def run(self, mode: str=None):
self._abort = False self._abort = False
if mode is None:
mode = self.fitmethod
fit_groups, linked_parameter = self.prepare_links() fit_groups, linked_parameter = self.prepare_links()
for data_groups in fit_groups: for data_groups in fit_groups:
if len(data_groups) == 1 and not self.linked: if len(data_groups) == 1 and not self.linked:

View File

@ -15,7 +15,7 @@ NUMBERRE = re.compile(r'[0-9]\.*[0-9]*[Ee]*[+-]*[0-9]*')
class AsciiReader: class AsciiReader:
delimiters = ['\t', ' ', ','] # delimiters = ['\t', ' ', ',']
def __init__(self, fname): def __init__(self, fname):
self.fname = None self.fname = None
@ -49,7 +49,8 @@ class AsciiReader:
with self.fname.open('r') as f: with self.fname.open('r') as f:
for i, line in enumerate(islice(f, len(self.header)+len(self.lines), num_lines)): for i, line in enumerate(islice(f, len(self.header)+len(self.lines), num_lines)):
line = line.rstrip('\n\t\r, ') line = line.rstrip('\n\t\r, ')
line = re.split(r'[\s,;]', line) line = re.sub(r'[\t ;,] *', ';', line)
line = line.split(';')
try: try:
comment_start = line.index('#') comment_start = line.index('#')

View File

@ -292,7 +292,7 @@ class DSCCalibrator:
empty_y = empty_data[1] empty_y = empty_data[1]
if self.sample.length(idx) != self.empty.length(idx_empty): if self.sample.length(idx) != self.empty.length(idx_empty):
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
empty_y = interp1d(empty_data[2], empty_data[1], fill_value='extrapolate')(sample_data[2]) empty_y = interp1d(empty_data[2]-empty_data[2, 0], empty_data[1], fill_value='extrapolate')(sample_data[2, 0])
sample_data[1] -= empty_y sample_data[1] -= empty_y
drift_value = sample_data.copy()[(2, 1), :] drift_value = sample_data.copy()[(2, 1), :]

View File

@ -24,8 +24,8 @@ def interpolate(data, new_x, xlog=False, ylog=False, kind='cubic', extrapolate=T
new_y = f(new_x) new_y = f(new_x)
if ylog: if ylog:
ret_val.set_data(x=new_x, y=10**new_y, y_err=None) ret_val.set_data(x=new_x, y=10**new_y, y_err=0)
else: else:
ret_val.set_data(x=new_x, y=new_y, y_err=None) ret_val.set_data(x=new_x, y=new_y, y_err=0)
return ret_val return ret_val

View File

@ -54,14 +54,35 @@ class HavriliakNegamiBDS(_AbstractBDS):
name = 'Havriliak-Negami' name = 'Havriliak-Negami'
equation = r'\Delta\epsilon / [1-(i\omega\tau)^{\gamma}]^{\alpha}' equation = r'\Delta\epsilon / [1-(i\omega\tau)^{\gamma}]^{\alpha}'
params = _AbstractBDS.params + [r'\alpha', r'\gamma'] params = _AbstractBDS.params + [r'\alpha', r'\gamma']
bounds = _AbstractBDS.bounds + [(0, 1), (0, 1)] bounds = _AbstractBDS.bounds + [(0, 1), (0, None)]
susceptibility = HavriliakNegami.susceptibility susceptibility = HavriliakNegami.susceptibility
class HavriliakNegamiAlphaGammaBDS:
type = 'Dielectric Spectroscopy'
name = 'Havriliak-Negami (ind. slopes)'
equation = r'\Delta\epsilon / [1-(i\omega\tau)^{\gamma}]^{\alpha}'
params = [r'\Delta\epsilon', r'\tau_{0}', r'\alpha', r'\alpha\gamma']
bounds = [(0, None), (0, None), (0, 1), (0, 1)]
iscomplex = True
@staticmethod
def func(x, deps, tau, alpha, alphagamma, complex_mode: int = 0, **kwargs):
chi = deps * HavriliakNegami.susceptibility(2*np.pi*x, tau, alpha, alphagamma/alpha, **kwargs)
if complex_mode == 0:
return chi
elif complex_mode == 1:
return chi.real
elif complex_mode == 2:
return chi.imag
else:
raise ValueError(f'{complex_mode!r} is not 0, 1, 2')
class KWWBDS(_AbstractBDS): class KWWBDS(_AbstractBDS):
name = 'KWW' name = 'KWW'
params = _AbstractBDS.params + [r'\beta'] params = _AbstractBDS.params + [r'\beta']
bounds = _AbstractBDS.bounds + [(0, 1)] bounds = _AbstractBDS.bounds + [(0.1, 1)]
susceptibility = KWW.susceptibility susceptibility = KWW.susceptibility

View File

@ -149,7 +149,6 @@ class DiffusionGradients:
else: else:
tm = t2 tm = t2
tp = x tp = x
# T2 decay happens twice
q2 = (g1**2 - g2**2) * (nucleus * tp)**2 q2 = (g1**2 - g2**2) * (nucleus * tp)**2
t_eff = (2 * tp) / 3 + tm t_eff = (2 * tp) / 3 + tm

View File

@ -66,7 +66,7 @@ class HavriliakNegamiFC(_AbstractFC):
class KWWFC(_AbstractFC): class KWWFC(_AbstractFC):
name = 'KWW' name = 'KWW'
params = _AbstractFC.params + [r'\beta'] params = _AbstractFC.params + [r'\beta']
bounds = _AbstractFC.bounds + [(0, 1)] bounds = _AbstractFC.bounds + [(0.1, 1)]
relax = Relaxation(distribution=KWW) relax = Relaxation(distribution=KWW)

View File

@ -80,6 +80,10 @@ def _replace_delims(text, src, dest):
return text return text
def _replace_agr_controls(text: str):
return re.sub(r'\\[hvzfx]\{(\d*.?\d+.?)?\}', '', text)
def convert(text: str, old: str = 'tex', new: str = 'html', brackets: bool = True): def convert(text: str, old: str = 'tex', new: str = 'html', brackets: bool = True):
t = {'latex': 0, 'tex': 0, 'html': 1, t = {'latex': 0, 'tex': 0, 'html': 1,
'agr': 2, 'plain': 3, 'str': 3} 'agr': 2, 'plain': 3, 'str': 3}
@ -101,5 +105,8 @@ def convert(text: str, old: str = 'tex', new: str = 'html', brackets: bool = Tru
if idx_out == 3 and not brackets: if idx_out == 3 and not brackets:
text = text.replace('{', '').replace('}', '') text = text.replace('{', '').replace('}', '')
if idx_in == 2:
text = _replace_agr_controls(text)
return text return text

View File

@ -36,7 +36,7 @@
<number>3</number> <number>3</number>
</property> </property>
<item row="2" column="0"> <item row="2" column="0">
<widget class="PlotWidget" name="graphicsView"> <widget class="NMRPlotWidget" name="graphicsView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -46,7 +46,7 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="PlotWidget" name="graphicsView_2"/> <widget class="NMRPlotWidget" name="graphicsView_2"/>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QComboBox" name="apodcombobox"> <widget class="QComboBox" name="apodcombobox">
@ -98,9 +98,9 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>PlotWidget</class> <class>NMRPlotWidget</class>
<extends>QGraphicsView</extends> <extends>QGraphicsView</extends>
<header>pyqtgraph</header> <header>..lib.graph_items</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -53,7 +53,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="0" column="2">
<widget class="PlotWidget" name="graphicsView"> <widget class="NMRPlotWidget" name="graphicsView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -66,9 +66,9 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>PlotWidget</class> <class>NMRPlotWidget</class>
<extends>QGraphicsView</extends> <extends>QGraphicsView</extends>
<header>pyqtgraph</header> <header>..lib.graph_items</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -378,7 +378,7 @@
<widget class="QWidget" name="layoutWidget"> <widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="PlotWidget" name="raw_graph"> <widget class="NMRPlotWidget" name="raw_graph">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -394,7 +394,7 @@
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="PlotWidget" name="calib_graph"> <widget class="NMRPlotWidget" name="calib_graph">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -410,7 +410,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="PlotWidget" name="baseline_graph"> <widget class="NMRPlotWidget" name="baseline_graph">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -426,7 +426,7 @@
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="PlotWidget" name="end_graph"> <widget class="NMRPlotWidget" name="end_graph">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -449,9 +449,9 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>PlotWidget</class> <class>NMRPlotWidget</class>
<extends>QGraphicsView</extends> <extends>QGraphicsView</extends>
<header>pyqtgraph</header> <header>..lib.graph_items</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -538,7 +538,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="0" rowspan="2"> <item row="0" column="0" rowspan="2">
<widget class="PlotWidget" name="graphic"> <widget class="NMRPlotWidget" name="graphic">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -552,16 +552,16 @@
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>PlotWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph</header>
</customwidget>
<customwidget> <customwidget>
<class>QListWidgetSelect</class> <class>QListWidgetSelect</class>
<extends>QListWidget</extends> <extends>QListWidget</extends>
<header>..lib.listwidget</header> <header>..lib.listwidget</header>
</customwidget> </customwidget>
<customwidget>
<class>NMRPlotWidget</class>
<extends>QGraphicsView</extends>
<header>..lib.graph_items</header>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>logx_button</tabstop> <tabstop>logx_button</tabstop>

View File

@ -36,7 +36,7 @@
<number>3</number> <number>3</number>
</property> </property>
<item row="0" column="0" colspan="8"> <item row="0" column="0" colspan="8">
<widget class="PlotWidget" name="graphicsView"> <widget class="NMRPlotWidget" name="graphicsView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -160,9 +160,9 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>PlotWidget</class> <class>NMRPlotWidget</class>
<extends>QGraphicsView</extends> <extends>QGraphicsView</extends>
<header>pyqtgraph</header> <header>..lib.graph_items</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -317,7 +317,7 @@
<number>3</number> <number>3</number>
</property> </property>
<item> <item>
<widget class="PlotWidget" name="graphicsView"> <widget class="NMRPlotWidget" name="graphicsView">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -503,9 +503,9 @@
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>PlotWidget</class> <class>NMRPlotWidget</class>
<extends>QGraphicsView</extends> <extends>QGraphicsView</extends>
<header>pyqtgraph</header> <header>..lib.graph_items</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>SciSpinBox</class> <class>SciSpinBox</class>

View File

@ -451,6 +451,9 @@
</item> </item>
<item row="2" column="0" colspan="2"> <item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox_interpol"> <widget class="QCheckBox" name="checkBox_interpol">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Use minimon interpolation</string> <string>Use minimon interpolation</string>
</property> </property>

View File

@ -100,7 +100,7 @@
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="PlotWidget" name="dsc_plot"> <widget class="NMRPlotWidget" name="dsc_plot">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -145,10 +145,10 @@
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="PlotWidget" name="tghodge_graph"/> <widget class="NMRPlotWidget" name="tghodge_graph"/>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="PlotWidget" name="tau_plot"> <widget class="NMRPlotWidget" name="tau_plot">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -277,7 +277,7 @@
<widget class="QWidget" name="page_3"> <widget class="QWidget" name="page_3">
<layout class="QGridLayout" name="gridLayout_6"> <layout class="QGridLayout" name="gridLayout_6">
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="PlotWidget" name="tnmh_graphics"/> <widget class="NMRPlotWidget" name="tnmh_graphics"/>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QTreeWidget" name="tnmh_tree"> <widget class="QTreeWidget" name="tnmh_tree">
@ -427,16 +427,16 @@
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>PlotWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph</header>
</customwidget>
<customwidget> <customwidget>
<class>QListWidgetSelect</class> <class>QListWidgetSelect</class>
<extends>QListWidget</extends> <extends>QListWidget</extends>
<header>..lib.listwidget</header> <header>..lib.listwidget</header>
</customwidget> </customwidget>
<customwidget>
<class>NMRPlotWidget</class>
<extends>QGraphicsView</extends>
<header>..lib.graph_items</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections> <connections>