dev #284

Merged
dominik merged 18 commits from dev into master 2024-09-07 17:25:02 +00:00
51 changed files with 1587 additions and 976 deletions
Showing only changes of commit f30ff3b758 - Show all commits

View File

@ -17,7 +17,7 @@ body:
description: For which version have you observed this behavior? description: For which version have you observed this behavior?
placeholder: You find the program version in "Help/About" placeholder: You find the program version in "Help/About"
validations: validations:
required: true required: false
- type: textarea - type: textarea
attributes: attributes:
label: Expected behavior label: Expected behavior

View File

@ -29,10 +29,10 @@ jobs:
env: env:
GPG_KEYGRIP: ${{ vars.GPG_KEYGRIP }} GPG_KEYGRIP: ${{ vars.GPG_KEYGRIP }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
GO_PIPELINE_LABEL: ${{ env.YEAR }}.${{ gitea.run_number }}_${{ env.SHA_SHORT }}_bookworm GO_PIPELINE_LABEL: ${{ env.YEAR }}.${{ gitea.run_number }}_${{ env.SHA_SHORT }}
- name: Upload AppImage - name: Upload AppImage
run: ./tools/upload_gitea.sh run: ./tools/upload_gitea.sh
env: env:
GO_PIPELINE_LABEL: ${{ env.YEAR }}.${{ gitea.run_number }}_${{ env.SHA_SHORT }}_bookworm GO_PIPELINE_LABEL: ${{ env.YEAR }}.${{ gitea.run_number }}_${{ env.SHA_SHORT }}
UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }} UPLOAD_TOKEN: ${{ secrets.UPLOAD_TOKEN }}
UPLOAD_USER: ${{ vars.UPLOAD_USER }} UPLOAD_USER: ${{ vars.UPLOAD_USER }}

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/basewindow.ui' # Form implementation generated from reading ui file './nmreval/src/resources/_ui/basewindow.ui'
# #
# Created by: PyQt5 UI code generator 5.15.10 # Created by: PyQt5 UI code generator 5.15.10
# #
@ -87,6 +87,8 @@ class Ui_BaseWindow(object):
self.menuSave.setObjectName("menuSave") self.menuSave.setObjectName("menuSave")
self.menuData = QtWidgets.QMenu(self.menubar) self.menuData = QtWidgets.QMenu(self.menubar)
self.menuData.setObjectName("menuData") self.menuData.setObjectName("menuData")
self.menuCut_to_visible_range = QtWidgets.QMenu(self.menuData)
self.menuCut_to_visible_range.setObjectName("menuCut_to_visible_range")
self.menuHelp = QtWidgets.QMenu(self.menubar) self.menuHelp = QtWidgets.QMenu(self.menubar)
self.menuHelp.setObjectName("menuHelp") self.menuHelp.setObjectName("menuHelp")
self.menuExtra = QtWidgets.QMenu(self.menubar) self.menuExtra = QtWidgets.QMenu(self.menubar)
@ -153,15 +155,6 @@ class Ui_BaseWindow(object):
self.toolBar_nmr.setIconSize(QtCore.QSize(24, 24)) self.toolBar_nmr.setIconSize(QtCore.QSize(24, 24))
self.toolBar_nmr.setObjectName("toolBar_nmr") self.toolBar_nmr.setObjectName("toolBar_nmr")
BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_nmr) BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_nmr)
self.toolBar_fit = QtWidgets.QToolBar(BaseWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.toolBar_fit.sizePolicy().hasHeightForWidth())
self.toolBar_fit.setSizePolicy(sizePolicy)
self.toolBar_fit.setIconSize(QtCore.QSize(24, 24))
self.toolBar_fit.setObjectName("toolBar_fit")
BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_fit)
self.toolBar_spectrum = QtWidgets.QToolBar(BaseWindow) self.toolBar_spectrum = QtWidgets.QToolBar(BaseWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
@ -313,8 +306,6 @@ class Ui_BaseWindow(object):
self.actionDerivation.setObjectName("actionDerivation") self.actionDerivation.setObjectName("actionDerivation")
self.actionIntegration = QtWidgets.QAction(BaseWindow) self.actionIntegration = QtWidgets.QAction(BaseWindow)
self.actionIntegration.setObjectName("actionIntegration") self.actionIntegration.setObjectName("actionIntegration")
self.action_cut = QtWidgets.QAction(BaseWindow)
self.action_cut.setObjectName("action_cut")
self.actionMove_between_plots = QtWidgets.QAction(BaseWindow) self.actionMove_between_plots = QtWidgets.QAction(BaseWindow)
self.actionMove_between_plots.setObjectName("actionMove_between_plots") self.actionMove_between_plots.setObjectName("actionMove_between_plots")
self.actionBaseline = QtWidgets.QAction(BaseWindow) self.actionBaseline = QtWidgets.QAction(BaseWindow)
@ -374,6 +365,13 @@ class Ui_BaseWindow(object):
self.actionBinning.setObjectName("actionBinning") self.actionBinning.setObjectName("actionBinning")
self.actionTNMH = QtWidgets.QAction(BaseWindow) self.actionTNMH = QtWidgets.QAction(BaseWindow)
self.actionTNMH.setObjectName("actionTNMH") self.actionTNMH.setObjectName("actionTNMH")
self.actionExclude_region = QtWidgets.QAction(BaseWindow)
self.actionExclude_region.setCheckable(True)
self.actionExclude_region.setObjectName("actionExclude_region")
self.action_cut_xaxis = QtWidgets.QAction(BaseWindow)
self.action_cut_xaxis.setObjectName("action_cut_xaxis")
self.action_cut_yaxis = QtWidgets.QAction(BaseWindow)
self.action_cut_yaxis.setObjectName("action_cut_yaxis")
self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionSave)
self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.actionExportGraphic)
self.menuSave.addAction(self.action_save_fit_parameter) self.menuSave.addAction(self.action_save_fit_parameter)
@ -386,6 +384,9 @@ class Ui_BaseWindow(object):
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuFile.addAction(self.action_close) self.menuFile.addAction(self.action_close)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuCut_to_visible_range.addSeparator()
self.menuCut_to_visible_range.addAction(self.action_cut_xaxis)
self.menuCut_to_visible_range.addAction(self.action_cut_yaxis)
self.menuData.addAction(self.action_new_set) self.menuData.addAction(self.action_new_set)
self.menuData.addAction(self.action_delete_sets) self.menuData.addAction(self.action_delete_sets)
self.menuData.addAction(self.actionMove_between_plots) self.menuData.addAction(self.actionMove_between_plots)
@ -395,7 +396,7 @@ class Ui_BaseWindow(object):
self.menuData.addAction(self.action_sort_pts) self.menuData.addAction(self.action_sort_pts)
self.menuData.addAction(self.actionSkip_points) self.menuData.addAction(self.actionSkip_points)
self.menuData.addSeparator() self.menuData.addSeparator()
self.menuData.addAction(self.action_cut) self.menuData.addAction(self.menuCut_to_visible_range.menuAction())
self.menuData.addSeparator() self.menuData.addSeparator()
self.menuData.addAction(self.actionChange_datatypes) self.menuData.addAction(self.actionChange_datatypes)
self.menuHelp.addAction(self.actionShow_error_log) self.menuHelp.addAction(self.actionShow_error_log)
@ -428,6 +429,7 @@ class Ui_BaseWindow(object):
self.menuLimits.addAction(self.action_no_range) self.menuLimits.addAction(self.action_no_range)
self.menuLimits.addAction(self.action_x_range) self.menuLimits.addAction(self.action_x_range)
self.menuLimits.addAction(self.action_custom_range) self.menuLimits.addAction(self.action_custom_range)
self.menuLimits.addAction(self.actionExclude_region)
self.menuFit.addAction(self.action_FitWidget) self.menuFit.addAction(self.action_FitWidget)
self.menuFit.addSeparator() self.menuFit.addSeparator()
self.menuFit.addAction(self.action_create_fit_function) self.menuFit.addAction(self.action_create_fit_function)
@ -496,7 +498,6 @@ class Ui_BaseWindow(object):
self.toolbar_edit.addAction(self.actionShift) self.toolbar_edit.addAction(self.actionShift)
self.toolBar_nmr.addAction(self.t1action) self.toolBar_nmr.addAction(self.t1action)
self.toolBar_nmr.addAction(self.actionCalculateT1) self.toolBar_nmr.addAction(self.actionCalculateT1)
self.toolBar_fit.addAction(self.action_FitWidget)
self.toolBar_spectrum.addAction(self.action_edit) self.toolBar_spectrum.addAction(self.action_edit)
self.toolBar_spectrum.addAction(self.actionPick_position) self.toolBar_spectrum.addAction(self.actionPick_position)
self.toolBar_data.addAction(self.actionConcatenate_sets) self.toolBar_data.addAction(self.actionConcatenate_sets)
@ -521,6 +522,7 @@ class Ui_BaseWindow(object):
self.menuFile.setTitle(_translate("BaseWindow", "&File")) self.menuFile.setTitle(_translate("BaseWindow", "&File"))
self.menuSave.setTitle(_translate("BaseWindow", "&Save...")) self.menuSave.setTitle(_translate("BaseWindow", "&Save..."))
self.menuData.setTitle(_translate("BaseWindow", "&Data")) self.menuData.setTitle(_translate("BaseWindow", "&Data"))
self.menuCut_to_visible_range.setTitle(_translate("BaseWindow", "Cut to visible range"))
self.menuHelp.setTitle(_translate("BaseWindow", "&Help")) self.menuHelp.setTitle(_translate("BaseWindow", "&Help"))
self.menuExtra.setTitle(_translate("BaseWindow", "Math")) self.menuExtra.setTitle(_translate("BaseWindow", "Math"))
self.menuNormalize.setTitle(_translate("BaseWindow", "&Normalize")) self.menuNormalize.setTitle(_translate("BaseWindow", "&Normalize"))
@ -537,7 +539,6 @@ class Ui_BaseWindow(object):
self.toolBar.setWindowTitle(_translate("BaseWindow", "Main")) self.toolBar.setWindowTitle(_translate("BaseWindow", "Main"))
self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math")) self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math"))
self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR")) self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR"))
self.toolBar_fit.setWindowTitle(_translate("BaseWindow", "Fit"))
self.toolBar_spectrum.setWindowTitle(_translate("BaseWindow", "Spectrum")) self.toolBar_spectrum.setWindowTitle(_translate("BaseWindow", "Spectrum"))
self.toolBar_data.setWindowTitle(_translate("BaseWindow", "Data")) self.toolBar_data.setWindowTitle(_translate("BaseWindow", "Data"))
self.action_close.setText(_translate("BaseWindow", "&Quit")) self.action_close.setText(_translate("BaseWindow", "&Quit"))
@ -615,7 +616,6 @@ class Ui_BaseWindow(object):
self.actionIntegrate.setText(_translate("BaseWindow", "Integrate")) self.actionIntegrate.setText(_translate("BaseWindow", "Integrate"))
self.actionDerivation.setText(_translate("BaseWindow", "Differentiation...")) self.actionDerivation.setText(_translate("BaseWindow", "Differentiation..."))
self.actionIntegration.setText(_translate("BaseWindow", "Integration...")) self.actionIntegration.setText(_translate("BaseWindow", "Integration..."))
self.action_cut.setText(_translate("BaseWindow", "Cut to visible range"))
self.actionMove_between_plots.setText(_translate("BaseWindow", "Move sets...")) self.actionMove_between_plots.setText(_translate("BaseWindow", "Move sets..."))
self.actionBaseline.setText(_translate("BaseWindow", "Baseline...")) self.actionBaseline.setText(_translate("BaseWindow", "Baseline..."))
self.actionCalculateT1.setText(_translate("BaseWindow", "Calculate relaxation...")) self.actionCalculateT1.setText(_translate("BaseWindow", "Calculate relaxation..."))
@ -642,6 +642,11 @@ class Ui_BaseWindow(object):
self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,")) self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,"))
self.actionBinning.setText(_translate("BaseWindow", "Binning...")) self.actionBinning.setText(_translate("BaseWindow", "Binning..."))
self.actionTNMH.setText(_translate("BaseWindow", "TNMH...")) self.actionTNMH.setText(_translate("BaseWindow", "TNMH..."))
self.actionExclude_region.setText(_translate("BaseWindow", "Exclude region"))
self.action_cut_xaxis.setText(_translate("BaseWindow", "x axis"))
self.action_cut_xaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible x range."))
self.action_cut_yaxis.setText(_translate("BaseWindow", "y axis"))
self.action_cut_yaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible y range. Uses real part of points."))
from ..data.datawidget.datawidget import DataWidget from ..data.datawidget.datawidget import DataWidget
from ..data.integral_widget import IntegralWidget from ..data.integral_widget import IntegralWidget
from ..data.point_select import PointSelectWidget from ..data.point_select import PointSelectWidget

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/fcreader.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fcreader.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# 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
@ -47,6 +48,7 @@ class Ui_FCEval_dialog(object):
self.verticalLayout.addWidget(self.input_box) self.verticalLayout.addWidget(self.input_box)
self.region_box = QtWidgets.QGroupBox(FCEval_dialog) self.region_box = QtWidgets.QGroupBox(FCEval_dialog)
self.region_box.setCheckable(True) self.region_box.setCheckable(True)
self.region_box.setChecked(False)
self.region_box.setObjectName("region_box") self.region_box.setObjectName("region_box")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.region_box) self.horizontalLayout = QtWidgets.QHBoxLayout(self.region_box)
self.horizontalLayout.setContentsMargins(3, 3, 3, 3) self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
@ -139,6 +141,7 @@ class Ui_FCEval_dialog(object):
self.line.setObjectName("line") self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 2, 0, 1, 2) self.gridLayout.addWidget(self.line, 2, 0, 1, 2)
self.graph_comboBox = QtWidgets.QComboBox(self.out_box) self.graph_comboBox = QtWidgets.QComboBox(self.out_box)
self.graph_comboBox.setEnabled(False)
self.graph_comboBox.setObjectName("graph_comboBox") self.graph_comboBox.setObjectName("graph_comboBox")
self.gridLayout.addWidget(self.graph_comboBox, 3, 1, 1, 1) self.gridLayout.addWidget(self.graph_comboBox, 3, 1, 1, 1)
self.graph_checkbox = QtWidgets.QCheckBox(self.out_box) self.graph_checkbox = QtWidgets.QCheckBox(self.out_box)
@ -167,8 +170,8 @@ class Ui_FCEval_dialog(object):
self.label_6.setBuddy(self.m0_cb) self.label_6.setBuddy(self.m0_cb)
self.retranslateUi(FCEval_dialog) self.retranslateUi(FCEval_dialog)
self.buttonBox.accepted.connect(FCEval_dialog.accept) self.buttonBox.accepted.connect(FCEval_dialog.accept) # type: ignore
self.buttonBox.rejected.connect(FCEval_dialog.reject) self.buttonBox.rejected.connect(FCEval_dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(FCEval_dialog) QtCore.QMetaObject.connectSlotsByName(FCEval_dialog)
def retranslateUi(self, FCEval_dialog): def retranslateUi(self, FCEval_dialog):
@ -178,7 +181,7 @@ class Ui_FCEval_dialog(object):
self.file_pushbutton.setText(_translate("FCEval_dialog", "Add HDF files...")) self.file_pushbutton.setText(_translate("FCEval_dialog", "Add HDF files..."))
self.dir_pushbutton.setText(_translate("FCEval_dialog", "Add directory...")) self.dir_pushbutton.setText(_translate("FCEval_dialog", "Add directory..."))
self.overwrite_cb.setText(_translate("FCEval_dialog", "Overwrite prev. data")) self.overwrite_cb.setText(_translate("FCEval_dialog", "Overwrite prev. data"))
self.region_box.setTitle(_translate("FCEval_dialog", "Evaluate region (empty values default to start/end)")) self.region_box.setTitle(_translate("FCEval_dialog", "Evaluate region (empty values default to values of the script)"))
self.start_lineedit.setPlaceholderText(_translate("FCEval_dialog", "start pos in µs")) self.start_lineedit.setPlaceholderText(_translate("FCEval_dialog", "start pos in µs"))
self.stop_lineedit.setPlaceholderText(_translate("FCEval_dialog", "end pos in µs")) self.stop_lineedit.setPlaceholderText(_translate("FCEval_dialog", "end pos in µs"))
self.fit_box.setTitle(_translate("FCEval_dialog", "Fit equation")) self.fit_box.setTitle(_translate("FCEval_dialog", "Fit equation"))

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# 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.
@ -42,6 +42,7 @@ class Ui_FitParameter(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
self.parameter_line.setSizePolicy(sizePolicy) self.parameter_line.setSizePolicy(sizePolicy)
self.parameter_line.setMaximumSize(QtCore.QSize(160, 16777215))
self.parameter_line.setText("") self.parameter_line.setText("")
self.parameter_line.setObjectName("parameter_line") self.parameter_line.setObjectName("parameter_line")
self.horizontalLayout_2.addWidget(self.parameter_line) self.horizontalLayout_2.addWidget(self.parameter_line)
@ -51,6 +52,9 @@ class Ui_FitParameter(object):
self.global_checkbox = QtWidgets.QCheckBox(FitParameter) self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
self.global_checkbox.setObjectName("global_checkbox") self.global_checkbox.setObjectName("global_checkbox")
self.horizontalLayout_2.addWidget(self.global_checkbox) self.horizontalLayout_2.addWidget(self.global_checkbox)
self.reset_button = QtWidgets.QPushButton(FitParameter)
self.reset_button.setObjectName("reset_button")
self.horizontalLayout_2.addWidget(self.reset_button)
self.verticalLayout.addLayout(self.horizontalLayout_2) self.verticalLayout.addLayout(self.horizontalLayout_2)
self.frame = QtWidgets.QFrame(FitParameter) self.frame = QtWidgets.QFrame(FitParameter)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
@ -82,6 +86,7 @@ class Ui_FitParameter(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
self.lineEdit.setSizePolicy(sizePolicy) self.lineEdit.setSizePolicy(sizePolicy)
self.lineEdit.setMaximumSize(QtCore.QSize(100, 16777215))
self.lineEdit.setText("") self.lineEdit.setText("")
self.lineEdit.setFrame(True) self.lineEdit.setFrame(True)
self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
@ -100,6 +105,7 @@ class Ui_FitParameter(object):
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth())
self.lineEdit_2.setSizePolicy(sizePolicy) self.lineEdit_2.setSizePolicy(sizePolicy)
self.lineEdit_2.setMaximumSize(QtCore.QSize(100, 16777215))
self.lineEdit_2.setText("") self.lineEdit_2.setText("")
self.lineEdit_2.setFrame(True) self.lineEdit_2.setFrame(True)
self.lineEdit_2.setObjectName("lineEdit_2") self.lineEdit_2.setObjectName("lineEdit_2")
@ -122,6 +128,7 @@ class Ui_FitParameter(object):
self.parameter_line.setPlaceholderText(_translate("FitParameter", "0")) self.parameter_line.setPlaceholderText(_translate("FitParameter", "0"))
self.fixed_check.setText(_translate("FitParameter", "Fix")) self.fixed_check.setText(_translate("FitParameter", "Fix"))
self.global_checkbox.setText(_translate("FitParameter", "Global")) self.global_checkbox.setText(_translate("FitParameter", "Global"))
self.reset_button.setText(_translate("FitParameter", "Use global"))
self.lineEdit.setToolTip(_translate("FitParameter", "<html><head/><body><p>Lower bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>")) self.lineEdit.setToolTip(_translate("FitParameter", "<html><head/><body><p>Lower bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>"))
self.label_3.setText(_translate("FitParameter", "Textlabel")) self.label_3.setText(_translate("FitParameter", "Textlabel"))
self.lineEdit_2.setToolTip(_translate("FitParameter", "<html><head/><body><p>Upper bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>")) self.lineEdit_2.setToolTip(_translate("FitParameter", "<html><head/><body><p>Upper bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>"))

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/fitresult.ui' # Form implementation generated from reading ui file 'src/resources/_ui/fitresult.ui'
# #
# Created by: PyQt5 UI code generator 5.15.10 # Created by: PyQt5 UI code generator 5.15.10
# #
@ -157,67 +157,91 @@ class Ui_Dialog(object):
self.gridLayout_2.setContentsMargins(3, 3, 3, 3) self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
self.gridLayout_2.setSpacing(3) self.gridLayout_2.setSpacing(3)
self.gridLayout_2.setObjectName("gridLayout_2") self.gridLayout_2.setObjectName("gridLayout_2")
self.extrapolate_box = QtWidgets.QCheckBox(self.groupBox)
self.extrapolate_box.setObjectName("extrapolate_box")
self.gridLayout_2.addWidget(self.extrapolate_box, 1, 0, 1, 1)
self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox)
self.parameter_checkbox.setObjectName("parameter_checkbox")
self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 5, 1, 1)
self.graph_comboBox = QtWidgets.QComboBox(self.groupBox) self.graph_comboBox = QtWidgets.QComboBox(self.groupBox)
self.graph_comboBox.setEnabled(False) self.graph_comboBox.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_comboBox.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.graph_comboBox.sizePolicy().hasHeightForWidth())
self.graph_comboBox.setSizePolicy(sizePolicy) self.graph_comboBox.setSizePolicy(sizePolicy)
self.graph_comboBox.setObjectName("graph_comboBox") self.graph_comboBox.setObjectName("graph_comboBox")
self.gridLayout_2.addWidget(self.graph_comboBox, 1, 6, 1, 1) self.gridLayout_2.addWidget(self.graph_comboBox, 1, 7, 1, 1)
self.graph_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_checkBox.sizePolicy().hasHeightForWidth())
self.graph_checkBox.setSizePolicy(sizePolicy)
self.graph_checkBox.setChecked(True)
self.graph_checkBox.setObjectName("graph_checkBox")
self.gridLayout_2.addWidget(self.graph_checkBox, 1, 5, 1, 1)
self.minx_line = QtWidgets.QLineEdit(self.groupBox) self.minx_line = QtWidgets.QLineEdit(self.groupBox)
self.minx_line.setEnabled(False) self.minx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.minx_line.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.minx_line.sizePolicy().hasHeightForWidth())
self.minx_line.setSizePolicy(sizePolicy) self.minx_line.setSizePolicy(sizePolicy)
self.minx_line.setObjectName("minx_line") self.minx_line.setObjectName("minx_line")
self.gridLayout_2.addWidget(self.minx_line, 1, 1, 1, 1) self.gridLayout_2.addWidget(self.minx_line, 1, 1, 1, 1)
self.line_2 = QtWidgets.QFrame(self.groupBox) self.extrapolate_box = QtWidgets.QCheckBox(self.groupBox)
self.line_2.setFrameShape(QtWidgets.QFrame.VLine) self.extrapolate_box.setObjectName("extrapolate_box")
self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) self.gridLayout_2.addWidget(self.extrapolate_box, 1, 0, 1, 1)
self.line_2.setObjectName("line_2") self.numx_line = QtWidgets.QLineEdit(self.groupBox)
self.gridLayout_2.addWidget(self.line_2, 0, 4, 2, 1) self.numx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.numx_line.sizePolicy().hasHeightForWidth())
self.numx_line.setSizePolicy(sizePolicy)
self.numx_line.setObjectName("numx_line")
self.gridLayout_2.addWidget(self.numx_line, 1, 3, 1, 1)
self.graph_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.graph_checkBox.sizePolicy().hasHeightForWidth())
self.graph_checkBox.setSizePolicy(sizePolicy)
self.graph_checkBox.setChecked(True)
self.graph_checkBox.setObjectName("graph_checkBox")
self.gridLayout_2.addWidget(self.graph_checkBox, 1, 6, 1, 1)
self.maxx_line = QtWidgets.QLineEdit(self.groupBox) self.maxx_line = QtWidgets.QLineEdit(self.groupBox)
self.maxx_line.setEnabled(False) self.maxx_line.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0) sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0) sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.maxx_line.sizePolicy().hasHeightForWidth()) sizePolicy.setHeightForWidth(self.maxx_line.sizePolicy().hasHeightForWidth())
self.maxx_line.setSizePolicy(sizePolicy) self.maxx_line.setSizePolicy(sizePolicy)
self.maxx_line.setObjectName("maxx_line") self.maxx_line.setObjectName("maxx_line")
self.gridLayout_2.addWidget(self.maxx_line, 1, 2, 1, 1) self.gridLayout_2.addWidget(self.maxx_line, 1, 2, 1, 1)
self.numx_line = QtWidgets.QLineEdit(self.groupBox) self.line_2 = QtWidgets.QFrame(self.groupBox)
self.numx_line.setEnabled(False) self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
self.numx_line.setObjectName("numx_line") self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
self.gridLayout_2.addWidget(self.numx_line, 1, 3, 1, 1) self.line_2.setObjectName("line_2")
self.gridLayout_2.addWidget(self.line_2, 0, 5, 2, 1)
self.newx_log_checkbox = QtWidgets.QCheckBox(self.groupBox)
self.newx_log_checkbox.setEnabled(False)
self.newx_log_checkbox.setObjectName("newx_log_checkbox")
self.gridLayout_2.addWidget(self.newx_log_checkbox, 1, 4, 1, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox) self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.curve_checkbox.sizePolicy().hasHeightForWidth())
self.curve_checkbox.setSizePolicy(sizePolicy)
self.curve_checkbox.setChecked(True) self.curve_checkbox.setChecked(True)
self.curve_checkbox.setObjectName("curve_checkbox") self.curve_checkbox.setObjectName("curve_checkbox")
self.horizontalLayout.addWidget(self.curve_checkbox) self.horizontalLayout.addWidget(self.curve_checkbox)
self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox) self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.partial_checkBox.sizePolicy().hasHeightForWidth())
self.partial_checkBox.setSizePolicy(sizePolicy)
self.partial_checkBox.setObjectName("partial_checkBox") self.partial_checkBox.setObjectName("partial_checkBox")
self.horizontalLayout.addWidget(self.partial_checkBox) self.horizontalLayout.addWidget(self.partial_checkBox)
self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 4) self.gridLayout_2.addLayout(self.horizontalLayout, 0, 0, 1, 5)
self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.parameter_checkbox.sizePolicy().hasHeightForWidth())
self.parameter_checkbox.setSizePolicy(sizePolicy)
self.parameter_checkbox.setObjectName("parameter_checkbox")
self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 6, 1, 2)
self.gridLayout.addWidget(self.groupBox, 7, 0, 1, 2) self.gridLayout.addWidget(self.groupBox, 7, 0, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry)
@ -253,16 +277,17 @@ class Ui_Dialog(object):
self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits of this set")) self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits of this set"))
self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit")) self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit"))
self.groupBox.setTitle(_translate("Dialog", "Output")) self.groupBox.setTitle(_translate("Dialog", "Output"))
self.extrapolate_box.setToolTip(_translate("Dialog", "Extrapolates only main function"))
self.extrapolate_box.setText(_translate("Dialog", "Extrapolate curves"))
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
self.graph_checkBox.setText(_translate("Dialog", "New graph for parameter"))
self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point")) self.minx_line.setToolTip(_translate("Dialog", "Leave empty to start at lowest point"))
self.minx_line.setPlaceholderText(_translate("Dialog", "min x")) self.minx_line.setPlaceholderText(_translate("Dialog", "min x"))
self.extrapolate_box.setToolTip(_translate("Dialog", "Extrapolates only main function"))
self.extrapolate_box.setText(_translate("Dialog", "Extrapolate curves"))
self.numx_line.setPlaceholderText(_translate("Dialog", "# pts"))
self.graph_checkBox.setText(_translate("Dialog", "New graph for parameter"))
self.maxx_line.setToolTip(_translate("Dialog", "Leave empty to start at highest point")) self.maxx_line.setToolTip(_translate("Dialog", "Leave empty to start at highest point"))
self.maxx_line.setPlaceholderText(_translate("Dialog", "max x")) self.maxx_line.setPlaceholderText(_translate("Dialog", "max x"))
self.numx_line.setPlaceholderText(_translate("Dialog", "# pts")) self.newx_log_checkbox.setText(_translate("Dialog", "log-spaced?"))
self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve")) self.curve_checkbox.setText(_translate("Dialog", "Plot fit curve"))
self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions")) self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions"))
self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter"))
from ..lib.forms import ElideComboBox from ..lib.forms import ElideComboBox
from pyqtgraph import GraphicsLayoutWidget from pyqtgraph import GraphicsLayoutWidget

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'src/resources/_ui/interpol_dialog.ui' # Form implementation generated from reading ui file './nmreval/src/resources/_ui/interpol_dialog.ui'
# #
# Created by: PyQt5 UI code generator 5.15.9 # Created by: PyQt5 UI code generator 5.15.10
# #
# 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.
@ -55,7 +55,7 @@ class Ui_Dialog(object):
self.gridLayout.addWidget(self.interp_comboBox, 4, 1, 1, 1) self.gridLayout.addWidget(self.interp_comboBox, 4, 1, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox") self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 12, 0, 1, 2) self.gridLayout.addWidget(self.buttonBox, 12, 0, 1, 2)
self.line = QtWidgets.QFrame(Dialog) self.line = QtWidgets.QFrame(Dialog)
@ -132,8 +132,6 @@ class Ui_Dialog(object):
self.label_8.setBuddy(self.dest_combobox) self.label_8.setBuddy(self.dest_combobox)
self.retranslateUi(Dialog) self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog)
Dialog.setTabOrder(self.listWidget, self.ylog_checkBox) Dialog.setTabOrder(self.listWidget, self.ylog_checkBox)
Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox) Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox)

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'resources/_ui/smoothdialog.ui' # Form implementation generated from reading ui file 'src/resources/_ui/smoothdialog.ui'
# #
# Created by: PyQt5 UI code generator 5.12.3 # Created by: PyQt5 UI code generator 5.15.10
# #
# 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
@ -17,9 +18,37 @@ class Ui_SmoothDialog(object):
self.gridLayout = QtWidgets.QGridLayout(SmoothDialog) self.gridLayout = QtWidgets.QGridLayout(SmoothDialog)
self.gridLayout.setSpacing(3) self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout") self.gridLayout.setObjectName("gridLayout")
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.gridLayout.addItem(spacerItem, 7, 0, 1, 1)
self.frac_label = QtWidgets.QLabel(SmoothDialog) self.frac_label = QtWidgets.QLabel(SmoothDialog)
self.frac_label.setObjectName("frac_label") self.frac_label.setObjectName("frac_label")
self.gridLayout.addWidget(self.frac_label, 1, 0, 1, 1) self.gridLayout.addWidget(self.frac_label, 2, 0, 1, 1)
self.line = QtWidgets.QFrame(SmoothDialog)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 5, 0, 1, 2)
self.widget = QtWidgets.QWidget(SmoothDialog)
self.widget.setObjectName("widget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label = QtWidgets.QLabel(self.widget)
self.label.setObjectName("label")
self.horizontalLayout_2.addWidget(self.label)
self.polynom_spinBox = QtWidgets.QSpinBox(self.widget)
self.polynom_spinBox.setMinimum(1)
self.polynom_spinBox.setMaximum(3)
self.polynom_spinBox.setObjectName("polynom_spinBox")
self.horizontalLayout_2.addWidget(self.polynom_spinBox)
self.gridLayout.addWidget(self.widget, 3, 0, 1, 2)
self.y_checkBox = QtWidgets.QCheckBox(SmoothDialog)
self.y_checkBox.setObjectName("y_checkBox")
self.gridLayout.addWidget(self.y_checkBox, 6, 1, 1, 1)
self.x_checkBox = QtWidgets.QCheckBox(SmoothDialog)
self.x_checkBox.setObjectName("x_checkBox")
self.gridLayout.addWidget(self.x_checkBox, 6, 0, 1, 1)
self.widget_2 = QtWidgets.QWidget(SmoothDialog) self.widget_2 = QtWidgets.QWidget(SmoothDialog)
self.widget_2.setObjectName("widget_2") self.widget_2.setObjectName("widget_2")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_2) self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_2)
@ -35,37 +64,17 @@ class Ui_SmoothDialog(object):
self.iter_spinBox.setProperty("value", 1) self.iter_spinBox.setProperty("value", 1)
self.iter_spinBox.setObjectName("iter_spinBox") self.iter_spinBox.setObjectName("iter_spinBox")
self.horizontalLayout_3.addWidget(self.iter_spinBox) self.horizontalLayout_3.addWidget(self.iter_spinBox)
self.gridLayout.addWidget(self.widget_2, 3, 0, 1, 2) self.gridLayout.addWidget(self.widget_2, 4, 0, 1, 2)
self.line = QtWidgets.QFrame(SmoothDialog)
self.line.setFrameShape(QtWidgets.QFrame.HLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.gridLayout.addWidget(self.line, 4, 0, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox(SmoothDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 7, 0, 1, 2)
self.widget = QtWidgets.QWidget(SmoothDialog)
self.widget.setObjectName("widget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label = QtWidgets.QLabel(self.widget)
self.label.setObjectName("label")
self.horizontalLayout_2.addWidget(self.label)
self.polynom_spinBox = QtWidgets.QSpinBox(self.widget)
self.polynom_spinBox.setMinimum(1)
self.polynom_spinBox.setMaximum(3)
self.polynom_spinBox.setObjectName("polynom_spinBox")
self.horizontalLayout_2.addWidget(self.polynom_spinBox)
self.gridLayout.addWidget(self.widget, 2, 0, 1, 2)
self.frac_spinBox = QtWidgets.QSpinBox(SmoothDialog) self.frac_spinBox = QtWidgets.QSpinBox(SmoothDialog)
self.frac_spinBox.setMinimum(1) self.frac_spinBox.setMinimum(1)
self.frac_spinBox.setMaximum(999) self.frac_spinBox.setMaximum(999)
self.frac_spinBox.setObjectName("frac_spinBox") self.frac_spinBox.setObjectName("frac_spinBox")
self.gridLayout.addWidget(self.frac_spinBox, 1, 1, 1, 1) self.gridLayout.addWidget(self.frac_spinBox, 2, 1, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(SmoothDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.gridLayout.addWidget(self.buttonBox, 8, 0, 1, 2)
self.comboBox = QtWidgets.QComboBox(SmoothDialog) self.comboBox = QtWidgets.QComboBox(SmoothDialog)
self.comboBox.setObjectName("comboBox") self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("") self.comboBox.addItem("")
@ -77,22 +86,17 @@ class Ui_SmoothDialog(object):
self.comboBox.addItem("") self.comboBox.addItem("")
self.comboBox.addItem("") self.comboBox.addItem("")
self.comboBox.addItem("") self.comboBox.addItem("")
self.gridLayout.addWidget(self.comboBox, 0, 0, 1, 2) self.gridLayout.addWidget(self.comboBox, 1, 0, 1, 2)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.label_2 = QtWidgets.QLabel(SmoothDialog)
self.gridLayout.addItem(spacerItem, 6, 0, 1, 1) self.label_2.setObjectName("label_2")
self.y_checkBox = QtWidgets.QCheckBox(SmoothDialog) self.gridLayout.addWidget(self.label_2, 0, 0, 1, 2)
self.y_checkBox.setObjectName("y_checkBox")
self.gridLayout.addWidget(self.y_checkBox, 5, 1, 1, 1)
self.x_checkBox = QtWidgets.QCheckBox(SmoothDialog)
self.x_checkBox.setObjectName("x_checkBox")
self.gridLayout.addWidget(self.x_checkBox, 5, 0, 1, 1)
self.frac_label.setBuddy(self.frac_spinBox) self.frac_label.setBuddy(self.frac_spinBox)
self.label_3.setBuddy(self.iter_spinBox)
self.label.setBuddy(self.polynom_spinBox) self.label.setBuddy(self.polynom_spinBox)
self.label_3.setBuddy(self.iter_spinBox)
self.retranslateUi(SmoothDialog) self.retranslateUi(SmoothDialog)
self.buttonBox.accepted.connect(SmoothDialog.accept) self.buttonBox.accepted.connect(SmoothDialog.accept) # type: ignore
self.buttonBox.rejected.connect(SmoothDialog.reject) self.buttonBox.rejected.connect(SmoothDialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(SmoothDialog) QtCore.QMetaObject.connectSlotsByName(SmoothDialog)
SmoothDialog.setTabOrder(self.comboBox, self.frac_spinBox) SmoothDialog.setTabOrder(self.comboBox, self.frac_spinBox)
SmoothDialog.setTabOrder(self.frac_spinBox, self.polynom_spinBox) SmoothDialog.setTabOrder(self.frac_spinBox, self.polynom_spinBox)
@ -104,9 +108,11 @@ class Ui_SmoothDialog(object):
_translate = QtCore.QCoreApplication.translate _translate = QtCore.QCoreApplication.translate
SmoothDialog.setWindowTitle(_translate("SmoothDialog", "1D smoothing filter")) SmoothDialog.setWindowTitle(_translate("SmoothDialog", "1D smoothing filter"))
self.frac_label.setText(_translate("SmoothDialog", "Window length")) self.frac_label.setText(_translate("SmoothDialog", "Window length"))
self.label_3.setText(_translate("SmoothDialog", "Iterations"))
self.label.setText(_translate("SmoothDialog", "Polynomial degree")) self.label.setText(_translate("SmoothDialog", "Polynomial degree"))
self.polynom_spinBox.setToolTip(_translate("SmoothDialog", "Deg")) self.polynom_spinBox.setToolTip(_translate("SmoothDialog", "Deg"))
self.y_checkBox.setText(_translate("SmoothDialog", "y log-spaced?"))
self.x_checkBox.setText(_translate("SmoothDialog", "x log-spaced?"))
self.label_3.setText(_translate("SmoothDialog", "Iterations"))
self.frac_spinBox.setToolTip(_translate("SmoothDialog", "<html><head/><body><p>Number of data points used as smoothing window.</p></body></html>")) self.frac_spinBox.setToolTip(_translate("SmoothDialog", "<html><head/><body><p>Number of data points used as smoothing window.</p></body></html>"))
self.comboBox.setItemText(0, _translate("SmoothDialog", "Moving mean")) self.comboBox.setItemText(0, _translate("SmoothDialog", "Moving mean"))
self.comboBox.setItemText(1, _translate("SmoothDialog", "Savitzky-Golay")) self.comboBox.setItemText(1, _translate("SmoothDialog", "Savitzky-Golay"))
@ -117,5 +123,4 @@ class Ui_SmoothDialog(object):
self.comboBox.setItemText(6, _translate("SmoothDialog", "Moving maximum")) self.comboBox.setItemText(6, _translate("SmoothDialog", "Moving maximum"))
self.comboBox.setItemText(7, _translate("SmoothDialog", "Moving minimum")) self.comboBox.setItemText(7, _translate("SmoothDialog", "Moving minimum"))
self.comboBox.setItemText(8, _translate("SmoothDialog", "Moving sum")) self.comboBox.setItemText(8, _translate("SmoothDialog", "Moving sum"))
self.y_checkBox.setText(_translate("SmoothDialog", "y log-spaced?")) self.label_2.setText(_translate("SmoothDialog", "<html><head/><body><p><span style=\" font-weight:600;\">Note:</span> Sets must be sorted for correct results</p></body></html>"))
self.x_checkBox.setText(_translate("SmoothDialog", "x log-spaced?"))

View File

@ -300,10 +300,12 @@ class ExperimentContainer(QtCore.QObject):
self._relations.pop(relation_type) self._relations.pop(relation_type)
def _update_actions(self): def _update_actions(self):
self.actions.update({'sort': self._data.sort, self.actions.update({
'cut': self._data.cut, 'sort': self._data.sort,
'norm': self._data.normalize, 'cut': self._data.cut,
'center': self.center}) 'norm': self._data.normalize,
'center': self.center,
})
@plot_update @plot_update
def update(self, opts: dict): def update(self, opts: dict):
@ -311,9 +313,11 @@ class ExperimentContainer(QtCore.QObject):
def get_properties(self) -> dict: def get_properties(self) -> dict:
props = OrderedDict() props = OrderedDict()
props['General'] = OrderedDict([('Name', self.name), props['General'] = OrderedDict([
('Value', str(self.value)), ('Name', self.name),
('Group', str(self.group))]) ('Value', str(self.value)),
('Group', str(self.group)),
])
props['Symbol'] = OrderedDict() props['Symbol'] = OrderedDict()
props['Line'] = OrderedDict() props['Line'] = OrderedDict()
@ -480,10 +484,12 @@ class ExperimentContainer(QtCore.QObject):
else: else:
prefix = f'g[{i}].s[{j}].' prefix = f'g[{i}].s[{j}].'
namespace = {prefix + 'x': (self.x, 'x values'), namespace = {
prefix + 'y': [self.y, 'y values'], prefix + 'x': (self.x, 'x values'),
prefix + 'y_err': (self.y_err, 'y error values'), prefix + 'y': [self.y, 'y values'],
prefix + 'value': (self.value, str(self.value))} prefix + 'y_err': (self.y_err, 'y error values'),
prefix + 'value': (self.value, str(self.value)),
}
if len(self._fits) == 1: if len(self._fits) == 1:
namespace.update({ namespace.update({

View File

@ -17,7 +17,7 @@ class DataTree(QtWidgets.QTreeWidget):
moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row
copyItem = QtCore.pyqtSignal(list, str) copyItem = QtCore.pyqtSignal(list, str)
saveFits = QtCore.pyqtSignal(list) saveFits = QtCore.pyqtSignal(list)
extendFits = QtCore.pyqtSignal(list) extendFits = QtCore.pyqtSignal(list, bool)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
def __init__(self, parent=None): def __init__(self, parent=None):
@ -49,11 +49,16 @@ class DataTree(QtWidgets.QTreeWidget):
def add_graph(self, idd: str, name: str): def add_graph(self, idd: str, name: str):
item = QtWidgets.QTreeWidgetItem() item = QtWidgets.QTreeWidgetItem()
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEditable | item.setFlags(
QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsDropEnabled |
QtCore.Qt.ItemFlag.ItemIsEditable |
QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
item.setText(0, name) item.setText(0, name)
item.setData(0, QtCore.Qt.UserRole, idd) item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd)
item.setCheckState(0, QtCore.Qt.Checked) item.setCheckState(0, QtCore.Qt.CheckState.Checked)
self.addTopLevelItem(item) self.addTopLevelItem(item)
self._checked_graphs.add(idd) self._checked_graphs.add(idd)
@ -67,14 +72,19 @@ class DataTree(QtWidgets.QTreeWidget):
for row in range(self.invisibleRootItem().childCount()): for row in range(self.invisibleRootItem().childCount()):
graph = self.invisibleRootItem().child(row) graph = self.invisibleRootItem().child(row)
if graph.data(0, QtCore.Qt.UserRole) == gid: if graph.data(0, QtCore.Qt.ItemDataRole.UserRole) == gid:
for (idd, name, value) in items: for (idd, name, value) in items:
item = QtWidgets.QTreeWidgetItem([name]) item = QtWidgets.QTreeWidgetItem([name])
item.setToolTip(0, f'Value: {value}') item.setToolTip(0, f'Value: {value}')
item.setData(0, QtCore.Qt.UserRole, idd) item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd)
item.setCheckState(0, QtCore.Qt.Checked) item.setCheckState(0, QtCore.Qt.CheckState.Checked)
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable | item.setFlags(
QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsDragEnabled |
QtCore.Qt.ItemFlag.ItemIsEditable |
QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
graph.addChild(item) graph.addChild(item)
self._checked_sets.add(idd) self._checked_sets.add(idd)
@ -85,8 +95,8 @@ class DataTree(QtWidgets.QTreeWidget):
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]: def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]:
idd = item.data(0, QtCore.Qt.UserRole) idd = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
is_selected = item.checkState(0) == QtCore.Qt.Checked is_selected = item.checkState(0) == QtCore.Qt.CheckState.Checked
to_be_hidden = set() to_be_hidden = set()
to_be_shown = set() to_be_shown = set()
@ -104,9 +114,9 @@ class DataTree(QtWidgets.QTreeWidget):
self.blockSignals(True) self.blockSignals(True)
for i in range(item.childCount()): for i in range(item.childCount()):
child = item.child(i) child = item.child(i)
child.setCheckState(0, QtCore.Qt.Checked) child.setCheckState(0, QtCore.Qt.CheckState.Checked)
to_be_shown.add(child.data(0, QtCore.Qt.UserRole)) to_be_shown.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
self._checked_sets.add(child.data(0, QtCore.Qt.UserRole)) self._checked_sets.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
self.blockSignals(False) self.blockSignals(False)
# check state change to unchecked # check state change to unchecked
@ -115,10 +125,10 @@ class DataTree(QtWidgets.QTreeWidget):
self.blockSignals(True) self.blockSignals(True)
for i in range(item.childCount()): for i in range(item.childCount()):
child = item.child(i) child = item.child(i)
child.setCheckState(0, QtCore.Qt.Unchecked) child.setCheckState(0, QtCore.Qt.CheckState.Unchecked)
to_be_hidden.add(child.data(0, QtCore.Qt.UserRole)) to_be_hidden.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
try: try:
self._checked_sets.remove(child.data(0, QtCore.Qt.UserRole)) self._checked_sets.remove(child.data(0, QtCore.Qt.ItemDataRole.UserRole))
except KeyError: except KeyError:
pass pass
self.blockSignals(False) self.blockSignals(False)
@ -153,7 +163,7 @@ class DataTree(QtWidgets.QTreeWidget):
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem)
def new_selection(self, item: QtWidgets.QTreeWidgetItem): def new_selection(self, item: QtWidgets.QTreeWidgetItem):
if item.parent() is None: if item.parent() is None:
self.management.select_window(item.data(0, QtCore.Qt.UserRole)) self.management.select_window(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
def dropEvent(self, evt: QtGui.QDropEvent): def dropEvent(self, evt: QtGui.QDropEvent):
dropped_index = self.indexAt(evt.pos()) dropped_index = self.indexAt(evt.pos())
@ -179,7 +189,7 @@ class DataTree(QtWidgets.QTreeWidget):
from_parent.removeChild(it) from_parent.removeChild(it)
tobemoved.append(it) tobemoved.append(it)
take_from.append(from_parent.data(0, QtCore.Qt.UserRole)) take_from.append(from_parent.data(0, QtCore.Qt.ItemDataRole.UserRole))
pos = QtCore.QModelIndex(persistent_drop) pos = QtCore.QModelIndex(persistent_drop)
if self.dropIndicatorPosition() == QtWidgets.QAbstractItemView.BelowItem: if self.dropIndicatorPosition() == QtWidgets.QAbstractItemView.BelowItem:
@ -191,8 +201,8 @@ class DataTree(QtWidgets.QTreeWidget):
else: else:
to_parent.insertChildren(row, tobemoved) to_parent.insertChildren(row, tobemoved)
self.management.move_sets([it.data(0, QtCore.Qt.UserRole) for it in tobemoved], self.management.move_sets([it.data(0, QtCore.Qt.ItemDataRole.UserRole) for it in tobemoved],
to_parent.data(0, QtCore.Qt.UserRole), take_from, to_parent.data(0, QtCore.Qt.ItemDataRole.UserRole), take_from,
pos=-1 if append else row) pos=-1 if append else row)
self.update_indexes() self.update_indexes()
@ -207,7 +217,7 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
data = item.data(0, QtCore.Qt.UserRole) data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if data == gid_out: if data == gid_out:
from_parent = item from_parent = item
@ -231,7 +241,7 @@ class DataTree(QtWidgets.QTreeWidget):
self.blockSignals(False) self.blockSignals(False)
def sort(self, graph_item: QtWidgets.QTreeWidgetItem, mode: str = 'value'): def sort(self, graph_item: QtWidgets.QTreeWidgetItem, mode: str = 'value'):
graph_id = graph_item.data(0, QtCore.Qt.UserRole) graph_id = graph_item.data(0, QtCore.Qt.ItemDataRole.UserRole)
sets = self.management.get_attributes(graph_id, mode) sets = self.management.get_attributes(graph_id, mode)
sets = [el[0] for el in sorted(sets.items(), key=lambda x: x[1])] sets = [el[0] for el in sorted(sets.items(), key=lambda x: x[1])]
@ -243,7 +253,7 @@ class DataTree(QtWidgets.QTreeWidget):
for s in sets: for s in sets:
for c in children: for c in children:
if c.data(0, QtCore.Qt.UserRole) == s: if c.data(0, QtCore.Qt.ItemDataRole.UserRole) == s:
graph_item.addChild(c) graph_item.addChild(c)
self.update_indexes() self.update_indexes()
@ -276,7 +286,7 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
data = item.data(0, QtCore.Qt.UserRole) data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if data == sid: if data == sid:
if name != item.text(0): if name != item.text(0):
item.setText(0, name) item.setText(0, name)
@ -285,7 +295,7 @@ class DataTree(QtWidgets.QTreeWidget):
iterator += 1 iterator += 1
def keyPressEvent(self, evt: QtGui.QKeyEvent): def keyPressEvent(self, evt: QtGui.QKeyEvent):
if evt.key() == QtCore.Qt.Key_Delete: if evt.key() == QtCore.Qt.Key.Key_Delete:
rm_sets = [] rm_sets = []
rm_graphs = [] rm_graphs = []
for idx in self.selectedIndexes(): for idx in self.selectedIndexes():
@ -296,20 +306,20 @@ class DataTree(QtWidgets.QTreeWidget):
if item.parent() is None: if item.parent() is None:
for c_i in range(item.childCount()): for c_i in range(item.childCount()):
# add sets inside graph to removal # add sets inside graph to removal
child_data = item.child(c_i).data(0, QtCore.Qt.UserRole) child_data = item.child(c_i).data(0, QtCore.Qt.ItemDataRole.UserRole)
if child_data not in rm_sets: if child_data not in rm_sets:
rm_sets.append(child_data) rm_sets.append(child_data)
rm_graphs.append(item.data(0, QtCore.Qt.UserRole)) rm_graphs.append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
else: else:
item_data = item.data(0, QtCore.Qt.UserRole) item_data = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if item_data not in rm_sets: if item_data not in rm_sets:
rm_sets.append(item_data) rm_sets.append(item_data)
# self.deleteItem.emit(rm_sets+rm_graphs) # self.deleteItem.emit(rm_sets+rm_graphs)
self.management.delete_sets(rm_sets+rm_graphs) self.management.delete_sets(rm_sets+rm_graphs)
elif evt.key() == QtCore.Qt.Key_Space: elif evt.key() == QtCore.Qt.Key.Key_Space:
sets = [] sets = []
from_parent = [] from_parent = []
@ -329,7 +339,7 @@ class DataTree(QtWidgets.QTreeWidget):
for it in sets: for it in sets:
if it in from_parent: if it in from_parent:
continue continue
it.setCheckState(0, QtCore.Qt.Unchecked if it.checkState(0) == QtCore.Qt.Checked else QtCore.Qt.Checked) it.setCheckState(0, QtCore.Qt.CheckState.Unchecked if it.checkState(0) == QtCore.Qt.CheckState.Checked else QtCore.Qt.CheckState.Checked)
s1, s2 = self.data_change(it, emit=False) s1, s2 = self.data_change(it, emit=False)
to_be_hidden |= s2 to_be_hidden |= s2
to_be_shown |= s1 to_be_shown |= s1
@ -353,7 +363,7 @@ class DataTree(QtWidgets.QTreeWidget):
# find all items that have to be removed # find all items that have to be removed
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
_id = item.data(0, QtCore.Qt.UserRole) _id = item.data(0, QtCore.Qt.ItemDataRole.UserRole)
if _id in ids: if _id in ids:
try: try:
item_parent = item.parent() item_parent = item.parent()
@ -431,7 +441,7 @@ class DataTree(QtWidgets.QTreeWidget):
if i.column() == 0: if i.column() == 0:
continue continue
items.append(self.itemFromIndex(i)) items.append(self.itemFromIndex(i))
graphs.append(self.itemFromIndex(i).data(0, QtCore.Qt.UserRole)) graphs.append(self.itemFromIndex(i).data(0, QtCore.Qt.ItemDataRole.UserRole))
if action == del_action: if action == del_action:
for gid in graphs: for gid in graphs:
@ -455,7 +465,7 @@ class DataTree(QtWidgets.QTreeWidget):
del_action = menu.addAction('Exterminate sets') del_action = menu.addAction('Exterminate sets')
cp_action = menu.addAction('Replicate sets') cp_action = menu.addAction('Replicate sets')
cat_action = menu.addAction('Join us!') cat_action = menu.addAction('Join us!')
plt_action = save_action = extend_action = None plt_action = save_action = extend_action = subfit_action = None
menu.addSeparator() menu.addSeparator()
col_menu = menu.addMenu('Color cycle') col_menu = menu.addMenu('Color cycle')
for c in available_cycles.keys(): for c in available_cycles.keys():
@ -473,12 +483,12 @@ class DataTree(QtWidgets.QTreeWidget):
continue continue
else: else:
graph_id = parent.data(0, QtCore.Qt.UserRole) graph_id = parent.data(0, QtCore.Qt.ItemDataRole.UserRole)
if graph_id not in idx: if graph_id not in idx:
idx[graph_id] = [] idx[graph_id] = []
# collect sets in their graph # collect sets in their graph
idx[graph_id].append(item.data(0, QtCore.Qt.UserRole)) idx[graph_id].append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
data = self.management[item.data(0, QtCore.Qt.UserRole)] data = self.management[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
if data.mode == 'fit': if data.mode == 'fit':
has_fits = True has_fits = True
@ -487,6 +497,7 @@ class DataTree(QtWidgets.QTreeWidget):
plt_action = menu.addAction('Plot fit parameter') plt_action = menu.addAction('Plot fit parameter')
save_action = menu.addAction('Save fit parameter') save_action = menu.addAction('Save fit parameter')
extend_action = menu.addAction('Extrapolate fit') extend_action = menu.addAction('Extrapolate fit')
subfit_action = menu.addAction('Plot partial functions')
action = menu.exec(evt.globalPos()) action = menu.exec(evt.globalPos())
@ -494,6 +505,9 @@ class DataTree(QtWidgets.QTreeWidget):
for gid, sets in idx.items(): for gid, sets in idx.items():
s.extend(sets) s.extend(sets)
if action is None:
return
if action == del_action: if action == del_action:
self.management.delete_sets(s) self.management.delete_sets(s)
@ -511,7 +525,10 @@ class DataTree(QtWidgets.QTreeWidget):
self.saveFits.emit(s) self.saveFits.emit(s)
elif action == extend_action: elif action == extend_action:
self.extendFits.emit(s) self.extendFits.emit(s, False)
elif action == subfit_action:
self.extendFits.emit(s, True)
elif action.parent() == col_menu: elif action.parent() == col_menu:
self.management.set_cycle(s, action.text()) self.management.set_cycle(s, action.text())
@ -523,7 +540,7 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
if item.data(0, QtCore.Qt.UserRole) == gid: if item.data(0, QtCore.Qt.ItemDataRole.UserRole) == gid:
item.setBackground(0, QtGui.QBrush(QtGui.QColor('gray'))) item.setBackground(0, QtGui.QBrush(QtGui.QColor('gray')))
else: else:
item.setBackground(0, QtGui.QBrush()) item.setBackground(0, QtGui.QBrush())
@ -536,10 +553,10 @@ class DataTree(QtWidgets.QTreeWidget):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item is not None: if item is not None:
if item.data(0, QtCore.Qt.UserRole) in sets: if item.data(0, QtCore.Qt.ItemDataRole.UserRole) in sets:
item.setCheckState(0, QtCore.Qt.Unchecked) item.setCheckState(0, QtCore.Qt.CheckState.Unchecked)
else: else:
self._checked_sets.add(item.data(0, QtCore.Qt.UserRole)) self._checked_sets.add(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
iterator += 1 iterator += 1
self.blockSignals(False) self.blockSignals(False)
@ -594,7 +611,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
sid = [] sid = []
for i in self.tree.selectedIndexes(): for i in self.tree.selectedIndexes():
if i.column() == 0: if i.column() == 0:
sid.append(i.data(role=QtCore.Qt.UserRole)) sid.append(i.data(role=QtCore.Qt.ItemDataRole.UserRole))
self.startShowProperty.emit(sid) self.startShowProperty.emit(sid)
@ -603,15 +620,23 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget):
self.proptable.populate(props) self.proptable.populate(props)
def change_property(self, key1, key2, value): def change_property(self, key1, key2, value):
ids = [item.data(0, QtCore.Qt.UserRole) for item in self.tree.selectedItems()]
if key2 == 'Value': if key2 == 'Value':
try: try:
value = float(value) value = float(value)
except ValueError: except ValueError:
QtWidgets.QMessageBox.warning(self, 'Invalid entry', QtWidgets.QMessageBox.warning(
'Value %r is not a valid number for `value`.' % value) self,
'Invalid entry',
f'Value {value!r} is not a valid number for `value`.')
return return
ids = []
for item in self.tree.selectedItems():
ids.append(item.data(0, QtCore.Qt.ItemDataRole.UserRole))
item.setToolTip(0, str(value))
else:
ids = [item.data(0, QtCore.Qt.ItemDataRole.UserRole) for item in self.tree.selectedItems()]
self.propertyChanged.emit(ids, key1, key2, value) self.propertyChanged.emit(ids, key1, key2, value)
def uncheck_sets(self, sets: list[str]): def uncheck_sets(self, sets: list[str]):

View File

@ -41,7 +41,7 @@ class PropWidget(QtWidgets.QWidget):
idx = table.indexFromItem(item) idx = table.indexFromItem(item)
self.propertyChanged.emit(self.tab.tabText(tab_idx), self.propertyChanged.emit(self.tab.tabText(tab_idx),
table.item(idx.row(), idx.column()-1).text(), table.item(idx.row(), idx.column()-1).text(),
item.data(QtCore.Qt.DisplayRole)) item.data(QtCore.Qt.ItemDataRole.DisplayRole))
@QtCore.pyqtSlot(int) @QtCore.pyqtSlot(int)
def tab_change(self, idx: int): def tab_change(self, idx: int):
@ -66,10 +66,10 @@ class PropTable(QtWidgets.QTableWidget):
self.blockSignals(True) self.blockSignals(True)
for k, v in prop.items(): for k, v in prop.items():
value_item = QtWidgets.QTableWidgetItem('') value_item = QtWidgets.QTableWidgetItem('')
value_item.setData(QtCore.Qt.DisplayRole, v) value_item.setData(QtCore.Qt.ItemDataRole.DisplayRole, v)
key_item = QtWidgets.QTableWidgetItem(k) key_item = QtWidgets.QTableWidgetItem(k)
key_item.setFlags(QtCore.Qt.NoItemFlags) key_item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags)
key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0))) key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
self.setRowCount(self.rowCount()+1) self.setRowCount(self.rowCount()+1)

View File

@ -413,12 +413,12 @@ class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
vb = self.time_graph.getPlotItem().getViewBox() vb = self.time_graph.getPlotItem().getViewBox()
vb.disableAutoRange(axis=vb.YAxis) vb.disableAutoRange(axis=vb.YAxis)
if self._all_time is not None:
self.zerofill_box.setVisible(self._all_time) self.zerofill_box.setVisible(self._all_time)
self.apod_box.setVisible(self._all_time) self.apod_box.setVisible(self._all_time)
self.shift_box.setVisible(self._all_time) self.shift_box.setVisible(self._all_time)
self.time_graph.setVisible(self._all_time) self.time_graph.setVisible(self._all_time)
self.logtime_widget.setVisible(self._all_time) self.logtime_widget.setVisible(self._all_time)
self._temp_baseline = self._temp_baseline_time if self._all_time else self._temp_baseline_freq self._temp_baseline = self._temp_baseline_time if self._all_time else self._temp_baseline_freq
self._temp_fft = self._temp_fft_time if self._all_time else self._temp_fft_freq self._temp_fft = self._temp_fft_time if self._all_time else self._temp_fft_freq

View File

@ -3,10 +3,10 @@ from __future__ import annotations
from typing import Any from typing import Any
from numpy import ndarray, iscomplexobj, asarray from numpy import ndarray, iscomplexobj, asarray
from pyqtgraph import PlotDataItem
from ..Qt import QtGui, QtCore, QtWidgets from ..Qt import QtGui, QtCore, QtWidgets
from .._py.valueeditor import Ui_MaskDialog from .._py.valueeditor import Ui_MaskDialog
from ..lib.pg_objects import PlotItem
class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
@ -35,13 +35,13 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
self.tableView.setModel(self.model) self.tableView.setModel(self.model)
self.tableView.setSelectionModel(self.selection_model) self.tableView.setSelectionModel(self.selection_model)
self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tableView.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.tableView.customContextMenuRequested.connect(self.ctx) self.tableView.customContextMenuRequested.connect(self.ctx)
self.selection_real = PlotDataItem(x=[], y=[], symbolSize=25, symbol='x', self.selection_real = PlotItem(x=[], y=[], symbolSize=25, symbol='x',
pen=None, symbolPen='#c9308e', symbolBrush='#c9308e') pen=None, symbolPen='#c9308e', symbolBrush='#c9308e')
self.selection_imag = PlotDataItem(x=[], y=[], symbolSize=25, symbol='+', self.selection_imag = PlotItem(x=[], y=[], symbolSize=25, symbol='+',
pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc') pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc')
def __call__(self, items: dict): def __call__(self, items: dict):
self.items = items self.items = items
@ -133,7 +133,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog):
def keyPressEvent(self, evt): def keyPressEvent(self, evt):
if evt.matches(QtGui.QKeySequence.Copy): if evt.matches(QtGui.QKeySequence.Copy):
self.copy_selection() self.copy_selection()
elif evt.key() == QtCore.Qt.Key_Delete: elif evt.key() == QtCore.Qt.Key.Key_Delete:
self.delete_item() self.delete_item()
else: else:
super().keyPressEvent(evt) super().keyPressEvent(evt)
@ -229,7 +229,7 @@ class ValueModel(QtCore.QAbstractTableModel):
""" """
itemChanged = QtCore.pyqtSignal(int, int, str) itemChanged = QtCore.pyqtSignal(int, int, str)
load_number = 20 load_number = 20
maskRole = QtCore.Qt.UserRole+321 maskRole = QtCore.Qt.ItemDataRole.UserRole+321
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
@ -240,7 +240,7 @@ class ValueModel(QtCore.QAbstractTableModel):
self.mask = None self.mask = None
self.headers = ['x', 'y', '\u0394y'] self.headers = ['x', 'y', '\u0394y']
for i, hd in enumerate(self.headers): for i, hd in enumerate(self.headers):
self.setHeaderData(i, QtCore.Qt.Horizontal, hd) self.setHeaderData(i, QtCore.Qt.Orientation.Horizontal, hd)
def rowCount(self, *args, **kwargs) -> int: def rowCount(self, *args, **kwargs) -> int:
return self.total_rows return self.total_rows
@ -258,25 +258,28 @@ class ValueModel(QtCore.QAbstractTableModel):
self.mask = mask.tolist() self.mask = mask.tolist()
self.endResetModel() self.endResetModel()
self.dataChanged.emit(self.index(0, 0), self.index(0, 1), [QtCore.Qt.DisplayRole]) self.dataChanged.emit(
self.index(0, 0),
self.index(0, 1), [QtCore.Qt.ItemDataRole.DisplayRole]
)
def data(self, idx: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> Any: def data(self, idx: QtCore.QModelIndex, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any:
if not idx.isValid(): if not idx.isValid():
return return
row = idx.row() row = idx.row()
if role in [QtCore.Qt.DisplayRole, QtCore.Qt.EditRole]: if role in [QtCore.Qt.ItemDataRole.DisplayRole, QtCore.Qt.ItemDataRole.EditRole]:
val = self._data[row][idx.column()] val = self._data[row][idx.column()]
return self.as_string(val) return self.as_string(val)
elif role == QtCore.Qt.BackgroundRole: elif role == QtCore.Qt.ItemDataRole.BackgroundRole:
pal = QtGui.QGuiApplication.palette() pal = QtGui.QGuiApplication.palette()
if not self.mask[row]: if not self.mask[row]:
return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Base) return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Base)
else: else:
return pal.color(QtGui.QPalette.Base) return pal.color(QtGui.QPalette.Base)
elif role == QtCore.Qt.ForegroundRole: elif role == QtCore.Qt.ItemDataRole.ForegroundRole:
pal = QtGui.QGuiApplication.palette() pal = QtGui.QGuiApplication.palette()
if not self.mask[row]: if not self.mask[row]:
return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Text) return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Text)
@ -289,7 +292,7 @@ class ValueModel(QtCore.QAbstractTableModel):
else: else:
return return
def setData(self, idx: QtCore.QModelIndex, value: str | bool, role=QtCore.Qt.DisplayRole) -> Any: def setData(self, idx: QtCore.QModelIndex, value: str | bool, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any:
col, row = idx.column(), idx.row() col, row = idx.column(), idx.row()
if role == ValueModel.maskRole: if role == ValueModel.maskRole:
@ -299,7 +302,7 @@ class ValueModel(QtCore.QAbstractTableModel):
return True return True
if value: if value:
if role == QtCore.Qt.EditRole: if role == QtCore.Qt.ItemDataRole.EditRole:
if value == self.as_string(self._data[row][col]): if value == self.as_string(self._data[row][col]):
return True return True
@ -322,9 +325,9 @@ class ValueModel(QtCore.QAbstractTableModel):
else: else:
return False return False
def headerData(self, section: int, orientation, role=QtCore.Qt.DisplayRole) -> Any: def headerData(self, section: int, orientation, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any:
if role == QtCore.Qt.DisplayRole: if role == QtCore.Qt.ItemDataRole.DisplayRole:
if orientation == QtCore.Qt.Horizontal: if orientation == QtCore.Qt.Orientation.Horizontal:
return self.headers[section] return self.headers[section]
else: else:
return str(section+1) return str(section+1)
@ -346,7 +349,7 @@ class ValueModel(QtCore.QAbstractTableModel):
self.endInsertRows() self.endInsertRows()
def flags(self, idx: QtCore.QModelIndex) -> QtCore.Qt.ItemFlag: def flags(self, idx: QtCore.QModelIndex) -> QtCore.Qt.ItemFlag:
return QtCore.QAbstractTableModel.flags(self, idx) | QtCore.Qt.ItemIsEditable return QtCore.QAbstractTableModel.flags(self, idx) | QtCore.Qt.ItemFlag.ItemIsEditable
def removeRows(self, pos: int, rows: int, parent=None, *args, **kwargs) -> bool: def removeRows(self, pos: int, rows: int, parent=None, *args, **kwargs) -> bool:
self.beginRemoveRows(parent, pos, pos+rows-1) self.beginRemoveRows(parent, pos, pos+rows-1)
@ -382,6 +385,6 @@ class ValueModel(QtCore.QAbstractTableModel):
@staticmethod @staticmethod
def as_string(value) -> str: def as_string(value) -> str:
if isinstance(value, complex): if isinstance(value, complex):
return f'{value.real:.8g}{value.imag:+.8g}j' return f'{value.real:.13g}{value.imag:+.13g}j'
else: else:
return f'{value:.8g}' return f'{value:.13g}'

View File

@ -136,8 +136,8 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
max_x = max(max_x, data.x.max()) max_x = max(max_x, data.x.max())
item = QtWidgets.QListWidgetItem(name) item = QtWidgets.QListWidgetItem(name)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
item.setData(QtCore.Qt.UserRole, key) item.setData(QtCore.Qt.ItemDataRole.UserRole, key)
item.setForeground(mkBrush(c.rgb())) item.setForeground(mkBrush(c.rgb()))
self.listWidget.addItem(item) self.listWidget.addItem(item)
@ -191,10 +191,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
plot = self._plots[key] plot = self._plots[key]
data, _ = self._dsc[key] data, _ = self._dsc[key]
@ -214,7 +214,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
tree_item = QtWidgets.QTreeWidgetItem([item.text()]) tree_item = QtWidgets.QTreeWidgetItem([item.text()])
values = self._tg_value.get(item.data(QtCore.Qt.UserRole)) values = self._tg_value.get(item.data(QtCore.Qt.ItemDataRole.UserRole))
if values is not None: if values is not None:
for name, pos in values.items(): for name, pos in values.items():
@ -223,7 +223,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
self.tg_tree.addTopLevelItem(tree_item) self.tg_tree.addTopLevelItem(tree_item)
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
plot = self._plots[key] plot = self._plots[key]
data, _ = self._dsc[key] data, _ = self._dsc[key]
@ -251,7 +251,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem) @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def change_visibility(self, item: QtWidgets.QListWidgetItem): def change_visibility(self, item: QtWidgets.QListWidgetItem):
is_checked = bool(item.checkState()) is_checked = bool(item.checkState())
plot = self._plots[item.data(QtCore.Qt.UserRole)] plot = self._plots[item.data(QtCore.Qt.ItemDataRole.UserRole)]
for val in plot: for val in plot:
val.setVisible(is_checked) val.setVisible(is_checked)
@ -275,10 +275,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
self.tnmh_tree.clear() self.tnmh_tree.clear()
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
data = self.get_fictive(key, baselines) data = self.get_fictive(key, baselines)
@ -292,7 +292,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
tree_item = QtWidgets.QTreeWidgetItem([item.text()]) tree_item = QtWidgets.QTreeWidgetItem([item.text()])
values = self._fit.get(item.data(QtCore.Qt.UserRole)) values = self._fit.get(item.data(QtCore.Qt.ItemDataRole.UserRole))
if values is not None: if values is not None:
child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()]) child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()])
@ -305,10 +305,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
ret_dic = {} ret_dic = {}
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
cp = None cp = None
if self.fictive_export_check.isChecked(): if self.fictive_export_check.isChecked():
@ -332,10 +332,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog):
m = [] m = []
for idx in range(self.listWidget.count()): for idx in range(self.listWidget.count()):
item = self.listWidget.item(idx) item = self.listWidget.item(idx)
if item.checkState() == QtCore.Qt.Unchecked: if item.checkState() == QtCore.Qt.CheckState.Unchecked:
continue continue
key = item.data(QtCore.Qt.UserRole) key = item.data(QtCore.Qt.ItemDataRole.UserRole)
data, _ = self._dsc[key] data, _ = self._dsc[key]
try: try:
tg_value = self._tg_value[key][tg_type][0] tg_value = self._tg_value[key][tg_type][0]

View File

@ -1,138 +1,11 @@
from __future__ import annotations from __future__ import annotations
from nmreval.utils.text import convert
from ..Qt import QtCore, QtWidgets, QtGui from ..Qt import QtCore, QtWidgets, QtGui
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 from ..lib.tables import TableWidget
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
"""
Widget to show a global parameter
"""
value_requested = QtCore.pyqtSignal(object)
value_changed = QtCore.pyqtSignal(str)
state_changed = QtCore.pyqtSignal()
replace_single_value = QtCore.pyqtSignal(object)
def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False):
super().__init__(parent)
self.setupUi(self)
self.name = label
self.parametername.setText(convert(label) + ' ')
self.parameter_line.setText('1')
self.parameter_line.setMaximumWidth(160)
self.lineEdit.setMaximumWidth(100)
self.lineEdit_2.setMaximumWidth(100)
self.label_3.setText(f'&lt; {convert(label)} &lt;')
self.checkBox.stateChanged.connect(self.enableBounds)
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
self.parameter_line.editingFinished.connect(self.update_parameter)
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.fixed_check.toggled.connect(self.set_fixed)
if fixed:
self.fixed_check.hide()
self.parameter_pos = None
self.func_idx = None
self._linetext = '1'
self.menu = QtWidgets.QMenu(self)
def set_parameter_string(self, p: str):
self.parameter_line.setText(p)
self.parameter_line.setToolTip(p)
def set_bounds(self, lb: float, ub: float, cbox: bool = True):
self.checkBox.setCheckState(QtCore.Qt.Checked if cbox else QtCore.Qt.Unchecked)
for val, bds_line in [(lb, self.lineEdit), (ub, self.lineEdit_2)]:
if val is not None:
bds_line.setText(str(val))
else:
bds_line.setText('')
def enableBounds(self, value: int):
self.lineEdit.setEnabled(value == 2)
self.lineEdit_2.setEnabled(value == 2)
def set_parameter(self, p: float | None, bds: tuple[float, float, bool] = None,
fixed: bool = None, glob: bool = None):
ptext = f'{p:.4g}'
self.set_parameter_string(ptext)
if bds is not None:
self.set_bounds(*bds)
if fixed is not None:
self.fixed_check.setCheckState(QtCore.Qt.CheckState.Unchecked if fixed else QtCore.Qt.CheckState.Checked)
if glob is not None:
self.global_checkbox.setCheckState(QtCore.Qt.CheckState.Checked if glob else QtCore.Qt.CheckState.Unchecked)
def get_parameter(self):
try:
p = float(self.parameter_line.text().replace(',', '.'))
except ValueError:
p = self.parameter_line.text().replace(',', '.')
if self.checkBox.isChecked():
lb_text = self.lineEdit.text()
lb = None
if lb_text:
try:
lb = float(lb_text.replace(',', '.'))
except ValueError:
lb = lb_text
ub_text = self.lineEdit_2.text()
rb = None
if ub_text:
try:
rb = float(ub_text.replace(',', '.'))
except ValueError:
rb = ub_text
else:
lb = rb = None
bounds = (lb, rb)
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked()
@QtCore.pyqtSlot(bool)
def set_fixed(self, state: bool):
# self.global_checkbox.setVisible(not state)
self.frame.setVisible(not state)
@QtCore.pyqtSlot()
def update_parameter(self):
new_value = self.parameter_line.text()
if not new_value:
self.parameter_line.setText('1')
try:
float(new_value)
is_text = False
except ValueError:
is_text = True
self.global_checkbox.setCheckState(False)
self.set_fixed(is_text or self.fixed_check.isChecked())
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog): class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
def __init__(self, types=None, parent=None): def __init__(self, types=None, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
@ -172,30 +45,37 @@ class FitModelTree(QtWidgets.QTreeWidget):
treeChanged = QtCore.pyqtSignal() treeChanged = QtCore.pyqtSignal()
itemRemoved = QtCore.pyqtSignal(int) itemRemoved = QtCore.pyqtSignal(int)
counterRole = QtCore.Qt.UserRole + 1 counterRole = QtCore.Qt.ItemDataRole.UserRole + 1
operatorRole = QtCore.Qt.UserRole + 2 operatorRole = QtCore.Qt.ItemDataRole.UserRole + 2
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.setHeaderHidden(True) self.setHeaderHidden(True)
self.setDragEnabled(True) self.setDragEnabled(True)
self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove) self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove)
self.setDefaultDropAction(QtCore.Qt.MoveAction) self.setDefaultDropAction(QtCore.Qt.DropAction.MoveAction)
self.itemSelectionChanged.connect(lambda: self.treeChanged.emit()) self.itemSelectionChanged.connect(lambda: self.treeChanged.emit())
def keyPressEvent(self, evt): def keyPressEvent(self, evt):
operators = [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Asterisk, operators = [
QtCore.Qt.Key_Minus, QtCore.Qt.Key_Slash] QtCore.Qt.Key.Key_Plus,
QtCore.Qt.Key.Key_Asterisk,
QtCore.Qt.Key.Key_Minus,
QtCore.Qt.Key.Key_Slash
]
if evt.key() == QtCore.Qt.Key_Delete: if evt.key() == QtCore.Qt.Key.Key_Delete:
for item in self.selectedItems(): for item in self.selectedItems():
self.remove_function(item) self.remove_function(item)
elif evt.key() == QtCore.Qt.Key_Space: elif evt.key() == QtCore.Qt.Key.Key_Space:
for item in self.treeWidget.selectedItems(): for item in self.selectedItems():
item.setCheckState(0, QtCore.Qt.Checked) if item.checkState( cs = item.checkState(0)
0) == QtCore.Qt.Unchecked else item.setCheckState(0, QtCore.Qt.Unchecked) if cs == QtCore.Qt.CheckState.Unchecked:
item.setCheckState(0, QtCore.Qt.CheckState.Checked)
else:
item.setCheckState(0, QtCore.Qt.CheckState.Unchecked)
elif evt.key() in operators: elif evt.key() in operators:
idx = operators.index(evt.key()) idx = operators.index(evt.key())
@ -246,7 +126,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
color = QtGui.QColor(color) color = QtGui.QColor(color)
it = QtWidgets.QTreeWidgetItem() it = QtWidgets.QTreeWidgetItem()
it.setData(0, QtCore.Qt.UserRole, idx) it.setData(0, QtCore.Qt.ItemDataRole.UserRole, idx)
it.setData(0, self.counterRole, cnt) it.setData(0, self.counterRole, cnt)
it.setData(0, self.operatorRole, op) it.setData(0, self.operatorRole, op)
it.setText(0, name) it.setText(0, name)
@ -257,7 +137,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
it.setForeground(0, QtGui.QBrush(color)) it.setForeground(0, QtGui.QBrush(color))
it.setIcon(0, get_icon(self.icons[op])) it.setIcon(0, get_icon(self.icons[op]))
it.setCheckState(0, QtCore.Qt.Checked if active else QtCore.Qt.Unchecked) it.setCheckState(0, QtCore.Qt.CheckState.Checked if active else QtCore.Qt.CheckState.Unchecked)
if parent is None: if parent is None:
self.addTopLevelItem(it) self.addTopLevelItem(it)
@ -277,7 +157,7 @@ class FitModelTree(QtWidgets.QTreeWidget):
def get_selected(self): def get_selected(self):
try: try:
it = self.selectedItems()[0] it = self.selectedItems()[0]
function_nr = it.data(0, QtCore.Qt.UserRole) function_nr = it.data(0, QtCore.Qt.ItemDataRole.UserRole)
idx = it.data(0, self.counterRole) idx = it.data(0, self.counterRole)
except IndexError: except IndexError:
@ -300,10 +180,10 @@ class FitModelTree(QtWidgets.QTreeWidget):
it = parent.child(i) it = parent.child(i)
child = { child = {
'idx': it.data(0, QtCore.Qt.UserRole), 'idx': it.data(0, QtCore.Qt.ItemDataRole.UserRole),
'op': it.data(0, self.operatorRole), 'op': it.data(0, self.operatorRole),
'pos': pos, 'pos': pos,
'active': (it.checkState(0) == QtCore.Qt.Checked), 'active': (it.checkState(0) == QtCore.Qt.CheckState.Checked),
'children': [] 'children': []
} }
@ -371,8 +251,8 @@ class FitTableWidget(TableWidget):
for (sid, name) in set_ids: for (sid, name) in set_ids:
item = QtWidgets.QTableWidgetItem(name) item = QtWidgets.QTableWidgetItem(name)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
item.setData(QtCore.Qt.UserRole+1, sid) item.setData(QtCore.Qt.ItemDataRole.UserRole+1, sid)
row = self.rowCount() row = self.rowCount()
self.setRowCount(row+1) self.setRowCount(row+1)
self.setItem(row, 0, item) self.setItem(row, 0, item)
@ -390,15 +270,15 @@ class FitTableWidget(TableWidget):
for i in range(self.rowCount()): for i in range(self.rowCount()):
item = self.item(i, 0) item = self.item(i, 0)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.CheckState.Checked:
mod = self.cellWidget(i, 1).currentData() mod = self.cellWidget(i, 1).currentData()
if mod is None: if mod is None:
mod = default mod = default
if include_name: if include_name:
arg = (item.data(QtCore.Qt.UserRole+1), item.text()) arg = (item.data(QtCore.Qt.ItemDataRole.UserRole+1), item.text())
else: else:
arg = item.data(QtCore.Qt.UserRole+1) arg = item.data(QtCore.Qt.ItemDataRole.UserRole+1)
if mod not in data: if mod not in data:
data[mod] = [] data[mod] = []
@ -411,8 +291,8 @@ class FitTableWidget(TableWidget):
for i in range(self.rowCount()): for i in range(self.rowCount()):
item = self.item(i, 0) item = self.item(i, 0)
if include_name: if include_name:
ret_val.append((item.data(QtCore.Qt.UserRole+1), item.text())) ret_val.append((item.data(QtCore.Qt.ItemDataRole.UserRole+1), item.text()))
else: else:
ret_val.append(item.data(QtCore.Qt.UserRole+1)) ret_val.append(item.data(QtCore.Qt.ItemDataRole.UserRole+1))
return ret_val return ret_val

View File

@ -1,14 +1,12 @@
from __future__ import annotations from __future__ import annotations
from typing import Optional
from nmreval.fit.parameter import Parameter from nmreval.fit.parameter import Parameter
from nmreval.utils.text import convert from nmreval.utils.text import convert
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
from .._py.fitfuncwidget import Ui_FormFit from .._py.fitfuncwidget import Ui_FormFit
from .._py.fitmodelwidget import Ui_FitParameter
from ..lib.forms import SelectionWidget from ..lib.forms import SelectionWidget
from .fit_forms import FitModelWidget
class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
@ -30,16 +28,15 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
self.scrollwidget2.setLayout(QtWidgets.QVBoxLayout()) self.scrollwidget2.setLayout(QtWidgets.QVBoxLayout())
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent): def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent):
modifiers = QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier
if isinstance(evt, QtGui.QKeyEvent): if isinstance(evt, QtGui.QKeyEvent):
if (evt.key() == QtCore.Qt.Key_Right) and \ if (evt.key() == QtCore.Qt.Key.Key_Right) and (evt.modifiers() == modifiers):
(evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier):
self.change_single_parameter(src.value, sender=src) self.change_single_parameter(src.value, sender=src)
self.select_next_preview(1) self.select_next_preview(1)
return True return True
elif (evt.key() == QtCore.Qt.Key_Left) and \ elif (evt.key() == QtCore.Qt.Key.Key_Left) and (evt.modifiers() == modifiers):
(evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier):
self.change_single_parameter(src.value, sender=src) self.change_single_parameter(src.value, sender=src)
self.select_next_preview(-1) self.select_next_preview(-1)
@ -65,7 +62,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
self.glob_values = [1] * len(func.params) self.glob_values = [1] * len(func.params)
for k, v in enumerate(func.params): for k, v in enumerate(func.params):
widgt = FitModelWidget(label=v, parent=self.scrollwidget) widgt = ParameterGlobalWidget(name=v, parent=self.scrollwidget)
widgt.parameter_pos = k widgt.parameter_pos = k
widgt.func_idx = idx widgt.func_idx = idx
try: try:
@ -95,7 +92,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
for w1, w2 in zip(self.global_parameter, self.data_parameter): for w1, w2 in zip(self.global_parameter, self.data_parameter):
w1.parametername.setFixedSize(self.max_width) w1.parametername.setFixedSize(self.max_width)
w1.checkBox.setFixedSize(self.max_width) w1.checkBox.setFixedSize(self.max_width)
w2.label.setFixedSize(self.max_width) w2.parametername.setFixedSize(self.max_width)
if hasattr(func, 'choices') and func.choices is not None: if hasattr(func, 'choices') and func.choices is not None:
cbox = func.choices cbox = func.choices
@ -175,7 +172,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
# disable single parameter if it is set global, enable if global is unset # disable single parameter if it is set global, enable if global is unset
widget = self.sender() widget = self.sender()
idx = self.global_parameter.index(widget) idx = self.global_parameter.index(widget)
enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked) enable = (widget.global_checkbox.checkState() == QtCore.Qt.CheckState.Unchecked)
self.data_parameter[idx].setEnabled(enable) self.data_parameter[idx].setEnabled(enable)
def select_next_preview(self, direction): def select_next_preview(self, direction):
@ -215,7 +212,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
param_general = [] param_general = []
for g in self.global_parameter: for g in self.global_parameter:
if isinstance(g, FitModelWidget): if isinstance(g, ParameterGlobalWidget):
p_i, bds_i, fixed_i, global_i = g.get_parameter() p_i, bds_i, fixed_i, global_i = g.get_parameter()
parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i) parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i)
param_general.append(parameter_i) param_general.append(parameter_i)
@ -236,7 +233,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
p = [] p = []
for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)): for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)):
if isinstance(g, FitModelWidget): if isinstance(g, ParameterGlobalWidget):
if (p_i is None) or is_global[i]: if (p_i is None) or is_global[i]:
# set has no oen value # set has no oen value
p.append(param_general[i].copy()) p.append(param_general[i].copy())
@ -303,8 +300,8 @@ class ParameterSingleWidget(QtWidgets.QWidget):
self._init_ui() self._init_ui()
self.name = name self.name = name
self.label.setText(convert(name)) self.parametername.setText(convert(name))
self.label.setToolTip('If this is bold then this parameter is only for this data. ' self.parametername.setToolTip('If this is bold then this parameter is only for this data. '
'Otherwise, the general parameter is used and displayed') 'Otherwise, the general parameter is used and displayed')
# self.value_line.setValidator(QtGui.QDoubleValidator()) # self.value_line.setValidator(QtGui.QDoubleValidator())
@ -316,8 +313,8 @@ class ParameterSingleWidget(QtWidgets.QWidget):
layout.setContentsMargins(2, 2, 2, 2) layout.setContentsMargins(2, 2, 2, 2)
layout.setSpacing(2) layout.setSpacing(2)
self.label = QtWidgets.QLabel(self) self.parametername = QtWidgets.QLabel(self)
layout.addWidget(self.label) layout.addWidget(self.parametername)
layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum))
@ -347,6 +344,133 @@ class ParameterSingleWidget(QtWidgets.QWidget):
def show_as_local_parameter(self, is_local: bool): def show_as_local_parameter(self, is_local: bool):
if is_local: if is_local:
self.label.setStyleSheet('font-weight: bold;') self.parametername.setStyleSheet('font-weight: bold;')
else: else:
self.label.setStyleSheet('') self.parametername.setStyleSheet('')
class ParameterGlobalWidget(QtWidgets.QWidget, Ui_FitParameter):
"""
Widget to show a global parameter
"""
value_requested = QtCore.pyqtSignal(object)
value_changed = QtCore.pyqtSignal(str)
state_changed = QtCore.pyqtSignal()
replace_single_value = QtCore.pyqtSignal(object)
def __init__(self, name: str = 'Fitparameter', parent=None, fixed: bool = False):
super().__init__(parent)
self.setupUi(self)
self.name = name
self.reset_button.setVisible(False)
self.parametername.setText(convert(name) + ' ')
self.parameter_line.setText('1')
self.parameter_line.setMaximumWidth(160)
self.lineEdit.setMaximumWidth(100)
self.lineEdit_2.setMaximumWidth(100)
self.label_3.setText(f'&lt; {convert(name)} &lt;')
self.checkBox.stateChanged.connect(self.enableBounds)
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
self.parameter_line.editingFinished.connect(self.update_parameter)
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.fixed_check.toggled.connect(self.set_fixed)
if fixed:
self.fixed_check.hide()
self.reset_button.setVisible(False)
self.parameter_pos = None
self.func_idx = None
self._linetext = '1'
self.menu = QtWidgets.QMenu(self)
def set_parameter_string(self, p: str):
self.parameter_line.setText(p)
self.parameter_line.setToolTip(p)
def set_bounds(self, lb: float, ub: float, cbox: bool = True):
self.checkBox.setCheckState(QtCore.Qt.CheckState.Checked if cbox else QtCore.Qt.CheckState.Unchecked)
for val, bds_line in [(lb, self.lineEdit), (ub, self.lineEdit_2)]:
if val is not None:
bds_line.setText(str(val))
else:
bds_line.setText('')
def enableBounds(self, value: int):
self.lineEdit.setEnabled(value == 2)
self.lineEdit_2.setEnabled(value == 2)
def set_parameter(self, p: float | None, bds: tuple[float, float, bool] = None,
fixed: bool = None, glob: bool = None):
ptext = f'{p:.4g}'
self.set_parameter_string(ptext)
if bds is not None:
self.set_bounds(*bds)
if fixed is not None:
self.fixed_check.setCheckState(QtCore.Qt.CheckState.Unchecked if fixed else QtCore.Qt.CheckState.Checked)
if glob is not None:
self.global_checkbox.setCheckState(QtCore.Qt.CheckState.Checked if glob else QtCore.Qt.CheckState.Unchecked)
def get_parameter(self):
try:
p = float(self.parameter_line.text().replace(',', '.'))
except ValueError:
p = self.parameter_line.text().replace(',', '.')
if self.checkBox.isChecked():
lb_text = self.lineEdit.text()
lb = None
if lb_text:
try:
lb = float(lb_text.replace(',', '.'))
except ValueError:
lb = lb_text
ub_text = self.lineEdit_2.text()
rb = None
if ub_text:
try:
rb = float(ub_text.replace(',', '.'))
except ValueError:
rb = ub_text
else:
lb = rb = None
bounds = (lb, rb)
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked()
@QtCore.pyqtSlot(bool)
def set_fixed(self, state: bool):
# self.global_checkbox.setVisible(not state)
self.frame.setVisible(not state)
@QtCore.pyqtSlot()
def update_parameter(self):
new_value = self.parameter_line.text()
if not new_value:
self.parameter_line.setText('1')
try:
float(new_value)
is_text = False
except ValueError:
is_text = True
self.global_checkbox.setCheckState(False)
self.set_fixed(is_text or self.fixed_check.isChecked())

View File

@ -0,0 +1,99 @@
from ..Qt import QtWidgets, QtGui, QtCore
from ..lib.iconloading import get_icon
from ..lib.pg_objects import RegionItem
class FitToolbar(QtWidgets.QToolBar):
def __init__(
self,
fitaction: QtWidgets.QAction,
limit_menu: QtWidgets.QMenu,
parent=None,
):
super().__init__('Fit', parent=parent)
self.fit_action = fitaction
self.region = RegionItem()
self.addAction(fitaction)
self.fitlim_button = QtWidgets.QToolButton(self)
self.fitlim_button.setMenu(limit_menu)
self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup)
self.fitlim_button.setIcon(get_icon('fit_region'))
self.addWidget(self.fitlim_button)
self.label = QtWidgets.QLabel(self)
self.label.setText('L: ')
self.addWidget(self.label)
self.label.setEnabled(False)
self.lineedit = QtWidgets.QLineEdit(self)
self.lineedit.setValidator(QtGui.QDoubleValidator())
self.lineedit.setMaximumWidth(92)
self.addWidget(self.lineedit)
self.lineedit.setEnabled(False)
self.label2 = QtWidgets.QLabel(self)
self.label2.setText(' R: ')
self.addWidget(self.label2)
self.label2.setEnabled(False)
self.lineedit2 = QtWidgets.QLineEdit(self)
self.lineedit2.setValidator(QtGui.QDoubleValidator())
self.addWidget(self.lineedit2)
self.lineedit2.setMaximumWidth(92)
self.lineedit2.setEnabled(False)
self.limit_group = QtWidgets.QActionGroup(self)
for ac in limit_menu.actions():
self.limit_group.addAction(ac)
self.limit_group.triggered.connect(self.change_limit_type)
self.region.sigRegionChanged.connect(self.change_labels)
self.change_labels()
self.lineedit.textChanged.connect(self.move_region)
self.lineedit2.textChanged.connect(self.move_region)
@QtCore.pyqtSlot(QtWidgets.QAction)
def change_limit_type(self, action: QtWidgets.QAction):
is_custom = (action.text() in ['Custom', 'Exclude region'])
print(is_custom)
for w in [self.label, self.label2, self.lineedit, self.lineedit2]:
w.setEnabled(is_custom)
def change_labels(self):
r = self.region.getRegion()
self.lineedit.blockSignals(True)
self.lineedit.setText(f'{r[0]:.4g}')
self.lineedit.blockSignals(False)
self.lineedit2.blockSignals(True)
self.lineedit2.setText(f'{r[1]:.4g}')
self.lineedit2.blockSignals(False)
def move_region(self):
try:
r_min = float(self.lineedit.text())
except ValueError:
r_min = None
try:
r_max = float(self.lineedit2.text())
except ValueError:
r_max = None
if r_min is not None and r_max is not None:
self.region.setRegion((r_min, r_max), use_log=True)
def get_limit(self):
action_text = self.limit_group.checkedAction().text()
return {
'None': 'none',
'Visible x range': 'x',
'Custom': ('in', self.region.getRegion()),
'Exclude region': ('out', self.region.getRegion()),
}[action_text]

View File

@ -165,7 +165,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
self.iscomplex = False self.iscomplex = False
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
f = self.functions[item.data(0, QtCore.Qt.UserRole)] f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
if hasattr(f, 'iscomplex') and f.iscomplex: if hasattr(f, 'iscomplex') and f.iscomplex:
self.iscomplex = True self.iscomplex = True
break break
@ -226,7 +226,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
iterator = QtWidgets.QTreeWidgetItemIterator(self.functree) iterator = QtWidgets.QTreeWidgetItemIterator(self.functree)
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
f = self.functions[item.data(0, QtCore.Qt.UserRole)] f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
cnt = item.data(0, self.functree.counterRole) cnt = item.data(0, self.functree.counterRole)
all_parameters[f'{f.name}_{cnt}'] = [(convert(pp, new='str'), (cnt, i)) for i, pp in enumerate(f.params)] all_parameters[f'{f.name}_{cnt}'] = [(convert(pp, new='str'), (cnt, i)) for i, pp in enumerate(f.params)]
@ -240,7 +240,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
while iterator.value(): while iterator.value():
item = iterator.value() item = iterator.value()
if item.checkState(0) != QtCore.Qt.CheckState.Unchecked: if item.checkState(0) != QtCore.Qt.CheckState.Unchecked:
f = self.functions[item.data(0, QtCore.Qt.UserRole)] f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)]
if hasattr(f, 'iscomplex') and f.iscomplex: if hasattr(f, 'iscomplex') and f.iscomplex:
iscomplex = True iscomplex = True
break break

View File

@ -9,7 +9,6 @@ import numpy as np
from pyqtgraph import mkPen from pyqtgraph import mkPen
from nmreval.fit._meta import MultiModel, ModelFactory from nmreval.fit._meta import MultiModel, ModelFactory
from nmreval.fit.data import Data
from nmreval.fit.model import Model from nmreval.fit.model import Model
from nmreval.fit.parameter import Parameters from nmreval.fit.parameter import Parameters
from nmreval.fit.result import FitResult from nmreval.fit.result import FitResult
@ -42,8 +41,8 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self._management = mgmt self._management = mgmt
self._current_model = next(QFitDialog.model_cnt) self._current_model = next(QFitDialog.model_cnt)
self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole) self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.ItemDataRole.UserRole)
self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole) self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.ItemDataRole.UserRole)
self.data_table = FitTableWidget(self.data_widget) self.data_table = FitTableWidget(self.data_widget)
self.data_widget.addWidget(self.data_table) self.data_widget.addWidget(self.data_table)
@ -150,9 +149,9 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
# deselect all fit sets # deselect all fit sets
for i in range(self.data_table.rowCount()): for i in range(self.data_table.rowCount()):
data_id = self.data_table.item(i, 0).data(QtCore.Qt.UserRole+1) data_id = self.data_table.item(i, 0).data(QtCore.Qt.ItemDataRole.UserRole+1)
if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf): if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf):
self.data_table.item(i, 0).setCheckState(QtCore.Qt.Unchecked) self.data_table.item(i, 0).setCheckState(QtCore.Qt.CheckState.Unchecked)
if self.models: if self.models:
for m in self.models.keys(): for m in self.models.keys():
@ -176,7 +175,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self.default_combobox.addItem('Model '+idx, userData=idx) self.default_combobox.addItem('Model '+idx, userData=idx)
self.show_combobox.addItem('Model '+idx, userData=idx) self.show_combobox.addItem('Model '+idx, userData=idx)
self.show_combobox.setItemData(self.show_combobox.count()-1, idx, QtCore.Qt.UserRole) self.show_combobox.setItemData(self.show_combobox.count()-1, idx, QtCore.Qt.ItemDataRole.UserRole)
self.show_combobox.setCurrentIndex(self.show_combobox.count()-1) self.show_combobox.setCurrentIndex(self.show_combobox.count()-1)
self._current_model = idx self._current_model = idx
@ -190,7 +189,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
self.get_functions() self.get_functions()
self.functionwidget.clear() self.functionwidget.clear()
self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.UserRole) self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
if self._current_model in self.models and len(self.models[self._current_model]): if self._current_model in self.models and len(self.models[self._current_model]):
for el in self.models[self._current_model]: for el in self.models[self._current_model]:
self.functionwidget.add_function(**el) self.functionwidget.add_function(**el)
@ -220,8 +219,14 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if len(self.models) == 1: if len(self.models) == 1:
self.model_frame.hide() self.model_frame.hide()
def _prepare(self, model: list, function_use: list = None, def _prepare(
parameter: dict = None, add_idx: bool = False, cnt: int = 0) -> tuple[dict, int]: self,
model: list,
function_use: list = None,
parameter: dict = None,
add_idx: bool = False,
cnt: int = 0,
) -> tuple[dict, int]:
if parameter is None: if parameter is None:
parameter = { parameter = {
@ -264,7 +269,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if f['children']: if f['children']:
# recurse for children # recurse for children
_, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt) _, cnt = self._prepare(f['children'], parameter=parameter, function_use=function_use, add_idx=add_idx, cnt=cnt)
return parameter, cnt return parameter, cnt
@ -276,7 +281,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
func_dict = {} func_dict = {}
for model_name, model_parameter in self.models.items(): for model_name, model_parameter in self.models.items():
func, order, param_len = ModelFactory.create_from_list(model_parameter) func, order, param_len, _ = ModelFactory.create_from_list(model_parameter)
multiple_funcs = isinstance(func, MultiModel) multiple_funcs = isinstance(func, MultiModel)
if func is None: if func is None:
continue continue
@ -388,7 +393,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
func_dict = {} func_dict = {}
for k, mod in self.models.items(): for k, mod in self.models.items():
func, order, param_len = ModelFactory.create_from_list(mod) func, order, param_len, _ = ModelFactory.create_from_list(mod)
multiple_funcs = isinstance(func, MultiModel) multiple_funcs = isinstance(func, MultiModel)
if k in data: if k in data:
@ -496,7 +501,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
if model_p['active']: if model_p['active']:
cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:]) cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:])
if model_p['children']: if model_p['children']:
cnt += self.set_parameter_iter(fit_id, param, model_p['children'], cnt=cnt) cnt = self.set_parameter_iter(fit_id, param, model_p['children'], cnt=cnt)
return cnt return cnt

View File

@ -1,6 +1,8 @@
from __future__ import annotations
from math import isnan from math import isnan
from pyqtgraph import mkBrush, mkPen from pyqtgraph import mkBrush, mkPen, mkColor
from numpy import abs as np_abs, isfinite as np_isfinite from numpy import abs as np_abs, isfinite as np_isfinite
from nmreval.utils.text import convert from nmreval.utils.text import convert
@ -16,7 +18,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
closed = QtCore.pyqtSignal(dict, list, str, bool, bool, list) closed = QtCore.pyqtSignal(dict, list, str, bool, bool, list)
redoFit = QtCore.pyqtSignal(dict) redoFit = QtCore.pyqtSignal(dict)
def __init__(self, results: list, management, parent=None): def __init__(self, results: list, sub_colors: dict, management, parent=None):
super().__init__(parent=parent) super().__init__(parent=parent)
self.setupUi(self) self.setupUi(self)
@ -28,12 +30,14 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.extrapolate_box.stateChanged.connect(lambda x: self.maxx_line.setEnabled(x)) self.extrapolate_box.stateChanged.connect(lambda x: self.maxx_line.setEnabled(x))
self.extrapolate_box.stateChanged.connect(lambda x: self.minx_line.setEnabled(x)) self.extrapolate_box.stateChanged.connect(lambda x: self.minx_line.setEnabled(x))
self.extrapolate_box.stateChanged.connect(lambda x: self.numx_line.setEnabled(x)) self.extrapolate_box.stateChanged.connect(lambda x: self.numx_line.setEnabled(x))
self.extrapolate_box.stateChanged.connect(lambda x: self.newx_log_checkbox.setEnabled(x))
self._previous_fits = {} self._previous_fits = {}
self._opts = [] self._opts = []
self._results = {} self._results = {}
self.graph_opts = {} self.graph_opts = {}
self.last_idx = None self.last_idx = None
self.func_colors = sub_colors
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') self.resid_plot = self.graphicsView.addPlot(row=0, col=0, title='Residual')
@ -45,21 +49,29 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
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)
self.resid_graph = PlotItem(x=[], y=[], self.resid_graph = PlotItem(
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)), x=[], y=[],
pen=None) symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
self.resid_graph_imag = PlotItem(x=[], y=[], pen=None
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)), )
pen=None) self.resid_graph_imag = PlotItem(
x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None
)
self.resid_plot.addItem(self.resid_graph) self.resid_plot.addItem(self.resid_graph)
self.resid_plot.addItem(self.resid_graph_imag) self.resid_plot.addItem(self.resid_graph_imag)
self.data_graph = PlotItem(x=[], y=[], self.data_graph = PlotItem(
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)), x=[], y=[],
pen=None) symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
self.data_graph_imag = PlotItem(x=[], y=[], pen=None
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)), )
pen=None) self.data_graph_imag = PlotItem(
x=[], y=[],
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
pen=None
)
self.fit_plot.addItem(self.data_graph) self.fit_plot.addItem(self.data_graph)
self.fit_plot.addItem(self.data_graph_imag) self.fit_plot.addItem(self.data_graph_imag)
@ -82,13 +94,14 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.set_results(results) self.set_results(results)
def __call__(self, results: list): def __call__(self, results: list, sub_colors: dict):
self._previous_fits = {} self._previous_fits = {}
self.sets_comboBox.blockSignals(True) self.sets_comboBox.blockSignals(True)
self.sets_comboBox.clear() self.sets_comboBox.clear()
self.sets_comboBox.blockSignals(False) self.sets_comboBox.blockSignals(False)
self._results = {} self._results = {}
self._opts = {} self._opts = {}
self.func_colors = sub_colors
self.set_results(results) self.set_results(results)
@ -190,10 +203,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.fit_graph.setData(x=res.x, y=res.y.real) self.fit_graph.setData(x=res.x, y=res.y.real)
self.fit_graph_imag.setData(x=res.x, y=res.y.imag) self.fit_graph_imag.setData(x=res.x, y=res.y.imag)
for i, f in enumerate(sub_funcs): for f, c in zip(sub_funcs, self.func_colors[idx]):
item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': i, 'style': 2})) col = mkColor(*[c_i*255 for c_i in c])
item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': col, 'style': 2}))
self.fit_plot.addItem(item) self.fit_plot.addItem(item)
item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': i, 'style': 2})) item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': col, 'style': 2}))
self.fit_plot.addItem(item) self.fit_plot.addItem(item)
else: else:
@ -202,8 +216,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.fit_graph.setData(x=res.x, y=res.y) self.fit_graph.setData(x=res.x, y=res.y)
self.fit_graph_imag.setData(x=[], y=[]) self.fit_graph_imag.setData(x=[], y=[])
for i, f in enumerate(sub_funcs): for f, c in zip(sub_funcs, self.func_colors[idx]):
item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': i, 'style': 2})) item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': mkColor(*[c_i*255 for c_i in c]), 'style': 2}))
self.fit_plot.addItem(item) self.fit_plot.addItem(item)
self._plot_residuals(idx) self._plot_residuals(idx)
@ -217,8 +231,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
if idx in self.graph_opts: if idx in self.graph_opts:
view_range, logx, logy = self.graph_opts[idx] view_range, logx, logy = self.graph_opts[idx]
self.fit_plot.setLogMode(x=logx, y=logy)
self.fit_plot.setRange(xRange=view_range[0], yRange=view_range[1], padding=0) self.fit_plot.setRange(xRange=view_range[0], yRange=view_range[1], padding=0)
self.fit_plot.setLogMode(x=logx, y=logy)
self.logx_box.blockSignals(True) self.logx_box.blockSignals(True)
self.logx_box.setChecked(logx) self.logx_box.setChecked(logx)
self.logx_box.blockSignals(False) self.logx_box.blockSignals(False)
@ -229,20 +243,16 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
self.fit_plot.enableAutoRange() self.fit_plot.enableAutoRange()
def _plot_residuals(self, idx: str = None): def _plot_residuals(self, idx: str = None):
print(idx)
if idx is None or isinstance(idx, QtWidgets.QAbstractButton): if idx is None or isinstance(idx, QtWidgets.QAbstractButton):
idx = self.sets_comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole) idx = self.sets_comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole)
res = self._results[idx] res = self._results[idx]
if res.iscomplex: if res.iscomplex:
if self.rel_dev_button.isChecked(): if self.rel_dev_button.isChecked():
print('rel')
self.resid_graph.setData(x=res.x_data, y=res.residual.real/np_abs(res.y_data.real)) self.resid_graph.setData(x=res.x_data, y=res.residual.real/np_abs(res.y_data.real))
print(res.y_data.imag)
if all(np_isfinite(res.y_data.imag)): if all(np_isfinite(res.y_data.imag)):
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag/np_abs(res.y_data.imag)) self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag/np_abs(res.y_data.imag))
else: else:
print('abs')
self.resid_graph.setData(x=res.x_data, y=res.residual.real) self.resid_graph.setData(x=res.x_data, y=res.residual.real)
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag) self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag)
@ -356,7 +366,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
parts = self.partial_checkBox.checkState() == QtCore.Qt.CheckState.Checked parts = self.partial_checkBox.checkState() == QtCore.Qt.CheckState.Checked
extrapolate = [None, None, None] extrapolate = [None, None, None, None]
error = [] error = []
if self.extrapolate_box.isChecked(): if self.extrapolate_box.isChecked():
try: try:
@ -372,6 +382,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
except (TypeError, ValueError): except (TypeError, ValueError):
error.append('Number of points is missing') error.append('Number of points is missing')
extrapolate[3] = self.newx_log_checkbox.isChecked()
if error: if error:
msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error)) msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error))
return return
@ -409,10 +421,13 @@ class FitExtension(QtWidgets.QDialog):
self.num_pts.setValidator(QtGui.QIntValidator()) self.num_pts.setValidator(QtGui.QIntValidator())
gridLayout.addWidget(self.num_pts, 2, 1, 1, 1) gridLayout.addWidget(self.num_pts, 2, 1, 1, 1)
self.logx_checkbox = QtWidgets.QCheckBox('Log-spaced?')
gridLayout.addWidget(self.logx_checkbox, 3, 0, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox() self.buttonBox = QtWidgets.QDialogButtonBox()
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2) gridLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
self.setLayout(gridLayout) self.setLayout(gridLayout)
@ -420,12 +435,13 @@ class FitExtension(QtWidgets.QDialog):
self.buttonBox.rejected.connect(self.reject) self.buttonBox.rejected.connect(self.reject)
@property @property
def values(self): def values(self) -> tuple[float, float, int, bool] | None:
try: try:
xmin = float(self.min_line.text()) xmin = float(self.min_line.text())
xmax = float(self.max_line.text()) xmax = float(self.max_line.text())
nums = int(self.num_pts.text()) nums = int(self.num_pts.text())
logx = self.logx_checkbox.isChecked()
except TypeError: except TypeError:
return None return None
return xmin, xmax, nums return xmin, xmax, nums, logx

View File

@ -670,11 +670,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
else: else:
if os.path.exists(outfile): if os.path.exists(outfile):
if QtWidgets.QMessageBox.warning(self, 'Export graphic', if QtWidgets.QMessageBox.warning(
f'{os.path.split(outfile)[1]} already exists.\n' self,
f'Do you REALLY want to replace it?', 'Export graphic',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, f'{os.path.split(outfile)[1]} already exists.\n'
QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.No: f'Do you REALLY want to replace it?',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.No
) == QtWidgets.QMessageBox.No:
return return
bg_color = self._bgcolor bg_color = self._bgcolor
@ -716,16 +719,20 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
logger.exception(f'{item} could not exported because {e.args}') logger.exception(f'{item} could not exported because {e.args}')
continue continue
if len(item) == 2:
# plot can show errorbars
item_dic['yerr'] = item[1].opts['topData']
if item_dic: if item_dic:
if len(item) == 2:
# plot can show errorbars
if len(item_dic['x']):
item_dic['yerr'] = item[1].opts['topData']
else:
item_dic['yerr'] = []
dic['items'].append(item_dic) dic['items'].append(item_dic)
for item in self._external_items: for item in self._external_items:
try: try:
dic['items'].append(item.get_data_opts()) item_dic = item.get_data_opts()
if item_dic:
dic['items'].append(item_dic)
except Exception as e: except Exception as e:
logger.exception(f'{item} could not be exported because {e.args}') logger.exception(f'{item} could not be exported because {e.args}')
continue continue

View File

@ -38,7 +38,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.changestaggeredrange(0) self.changestaggeredrange(0)
self.ascii_table.contextMenuEvent = self.ctx_table self.ascii_table.contextMenuEvent = self.ctx_table
self.ascii_table.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.ascii_table.horizontalHeader().setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table) self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table)
self.skip = False self.skip = False
@ -65,7 +65,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.set_column_names(1) self.set_column_names(1)
self.skippy_checkbox.blockSignals(True) self.skippy_checkbox.blockSignals(True)
self.skippy_checkbox.setCheckState(QtCore.Qt.Unchecked) self.skippy_checkbox.setCheckState(QtCore.Qt.CheckState.Unchecked)
self.skippy_checkbox.blockSignals(False) self.skippy_checkbox.blockSignals(False)
return self return self
@ -132,7 +132,10 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1))) self.ascii_table.setHorizontalHeaderLabels(map(str, range(1, self.ascii_table.columnCount() + 1)))
if self.column_checkBox.isChecked() and self.line_spinBox.isEnabled(): if self.column_checkBox.isChecked() and self.line_spinBox.isEnabled():
header_line = self.reader.header[self.line_spinBox.value()-1] header_line = self.reader.header[self.line_spinBox.value()-1]
self.ascii_table.setHorizontalHeaderLabels(header_line.split()) header_line = header_line.strip('\n\t\r, ')
header_line = re.sub(r'[\t ;,]+', ';', header_line)
self.ascii_table.setHorizontalHeaderLabels(header_line.split(';'))
@QtCore.pyqtSlot(int, name='on_staggered_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_staggered_checkBox_stateChanged')
def changestaggeredrange(self, state: int): def changestaggeredrange(self, state: int):
@ -178,11 +181,12 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def accept(self): def accept(self):
if self.apply(): if self.apply():
self.close() super().accept()
def apply(self): def apply(self):
# default row for x is the first row, it will be superseded if an integer number is given. # default row for x is the first row, it will be superseded if an integer number is given.
x = self.x_lineedit.text() x = self.x_lineedit.text()
is_valid = True
if x: if x:
try: try:
x = int(x)-1 x = int(x)-1
@ -191,16 +195,35 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
else: else:
x = None x = None
if not self.check_column_numbers(x, max(self.reader.width)):
_ = QtWidgets.QMessageBox.information(self, 'Improper input',
f'Input for x axis is invalid')
return False
try: try:
y = [int(t)-1 for t in self.y_lineedit.text().split(' ')] y = [int(t)-1 for t in self.y_lineedit.text().split(' ')]
except ValueError: except ValueError:
y = None y = None
if not self.check_column_numbers(y, max(self.reader.width)):
_ = QtWidgets.QMessageBox.information(self, 'Improper input',
f'Input for y axis is invalid')
return False
try: try:
y_err = [int(t)-1 for t in self.deltay_lineEdit.text().split(' ')] y_err = [int(t)-1 for t in self.deltay_lineEdit.text().split(' ')]
except ValueError: except ValueError:
y_err = None y_err = None
mode = self.buttonGroup.checkedButton().text()
if mode != 'Points':
y_err = None
if not self.check_column_numbers(y, max(self.reader.width)):
_ = QtWidgets.QMessageBox.information(self, 'Improper input',
f'Input for y_err axis is invalid')
return False
col_header = None col_header = None
if self.column_checkBox.isChecked(): if self.column_checkBox.isChecked():
col_header = [] col_header = []
@ -218,21 +241,22 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
x=x, x=x,
y=y, y=y,
yerr=y_err, yerr=y_err,
mode=self.buttonGroup.checkedButton().text(), mode=mode,
col_names=col_header, col_names=col_header,
num_value=self.get_numerical_value(), num_value=self.get_numerical_value(),
) )
self.data_read.emit(ret_dic) self.data_read.emit(ret_dic)
except ImportError as e: except Exception as e:
_ = QtWidgets.QMessageBox.information(self, 'Reading failed', _ = QtWidgets.QMessageBox.information(self, 'Reading failed',
f'Import data failed with {e.args}') f'Import data failed with\n {e.args[0]}')
return False
return True return True
@QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked') @QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked')
def show_error(self, val: int): def show_error(self, val: int):
self.deltay_lineEdit.setEnabled(val == -3) self.deltay_lineEdit.setEnabled(val == -2)
@QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged') @QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged')
def skip_next_dial(self, _: int): def skip_next_dial(self, _: int):
@ -281,7 +305,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
else: else:
self.label_8.setText(fname) self.label_8.setText(fname)
def get_numerical_value(self): def get_numerical_value(self) -> float:
val = 0 val = 0
if self.re_button.isChecked() and self._matches: if self.re_button.isChecked() and self._matches:
m = self._matches[self.re_match_index.value()-1] m = self._matches[self.re_match_index.value()-1]
@ -292,3 +316,21 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader):
val = float(self.custom_input.text()) val = float(self.custom_input.text())
return val return val
def check_column_numbers(self, values: int | list[int] | str | None, num_column: int) -> bool:
is_valid = False
if values is None:
is_valid = True
elif values == 'index':
is_valid = True
elif isinstance(values, int):
is_valid = values < num_column
elif isinstance(values, list):
try:
is_valid = all(v < num_column for v in values)
except TypeError:
is_valid = False
return is_valid

View File

@ -78,14 +78,22 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
for opts in self.sample.steps: for opts in self.sample.steps:
item = QtWidgets.QListWidgetItem() item = QtWidgets.QListWidgetItem()
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) item.setFlags(
item.setCheckState(QtCore.Qt.Unchecked) QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
item.setCheckState(QtCore.Qt.CheckState.Unchecked)
if opts[0] == 'i': if opts[0] == 'i':
item.setFlags(QtCore.Qt.NoItemFlags) item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags)
item.setText(f'{opts[1]:.2f} K for {opts[2] / 60:.0f} min') item.setText(f'{opts[1]:.2f} K for {opts[2] / 60:.0f} min')
else: else:
item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) item.setFlags(
QtCore.Qt.ItemFlag.ItemIsEnabled |
QtCore.Qt.ItemFlag.ItemIsSelectable |
QtCore.Qt.ItemFlag.ItemIsUserCheckable
)
item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min') item.setText(f'{opts[2]:.2f} K to {opts[3]:.2f} K with {opts[1]} K/min')
self.step_listWidget.addItem(item) self.step_listWidget.addItem(item)
@ -97,7 +105,12 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
if empty: if empty:
self.empty = self.calibrator.set_measurement(empty, mode='empty') self.empty = self.calibrator.set_measurement(empty, mode='empty')
self.empty_label.setText('~/' + str(self.empty.fname.relative_to(Path.home())))
# avoid ValueError breaking data update
if self.empty.fname.is_relative_to(Path.home()):
self.empty_label.setText('~/' + str(self.empty.fname.relative_to(Path.home())))
else:
self.empty_label.setText(str(self.empty.fname))
self.update_plots() self.update_plots()
@ -118,8 +131,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
self.references.append(ref) self.references.append(ref)
item = QtWidgets.QTableWidgetItem(str(ref.fname.name)) item = QtWidgets.QTableWidgetItem(str(ref.fname.name))
item.setData(QtCore.Qt.UserRole, ref.fname) item.setData(QtCore.Qt.ItemDataRole.UserRole, ref.fname)
item.setFlags(QtCore.Qt.ItemIsEnabled) item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled)
rowcnt = self.reference_tableWidget.rowCount() rowcnt = self.reference_tableWidget.rowCount()
self.reference_tableWidget.setRowCount(rowcnt+1) self.reference_tableWidget.setRowCount(rowcnt+1)
@ -132,7 +145,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
@QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked') @QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked')
def remove_reference(self): def remove_reference(self):
idx = self.reference_tableWidget.currentRow() idx = self.reference_tableWidget.currentRow()
self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.UserRole)) self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.ItemDataRole.UserRole))
self.reference_tableWidget.removeRow(idx) self.reference_tableWidget.removeRow(idx)
self.update_plots() self.update_plots()
@ -145,10 +158,10 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
for row in range(self.step_listWidget.count()): for row in range(self.step_listWidget.count()):
if idx == row: if idx == row:
continue continue
self.step_listWidget.item(row).setCheckState(QtCore.Qt.Unchecked) self.step_listWidget.item(row).setCheckState(QtCore.Qt.CheckState.Unchecked)
self.step_listWidget.blockSignals(False) self.step_listWidget.blockSignals(False)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.CheckState.Checked:
mode, rate, _, _ = self.sample.steps[idx] mode, rate, _, _ = self.sample.steps[idx]
self.current_run = (rate, mode) self.current_run = (rate, mode)
self.sample_idx = idx self.sample_idx = idx
@ -217,6 +230,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
if empty_data is not None: if empty_data is not None:
self.empty_sample.setData(x=empty_data[0], y=empty_data[1]) self.empty_sample.setData(x=empty_data[0], y=empty_data[1])
else:
self.empty_sample.setData(x=[], y=[])
self.calib_graph.clear() self.calib_graph.clear()
@ -249,11 +264,16 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog):
except TypeError: except TypeError:
return return
if self.cp_checkBox.isChecked() and self.references:
y_label = 'cp'
else:
y_label = 'q'
rate, mode = self.current_run rate, mode = self.current_run
new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate} ({mode})') new_val = DSC(sample_data[0], sample_data[1], value=rate, name=f'{self.fname.stem} {rate}K-min ({mode}, {y_label})')
if filesave: if filesave:
new_val.savetxt(self.fname.with_name(f'{self.fname.stem} {rate}K-min {mode}.dat'.replace(' ', '_'))) new_val.savetxt(self.fname.with_name(f'{self.fname.stem}_{rate}K-min_{y_label}{mode}.dat'.replace(' ', '_')))
close_after = False close_after = False
else: else:
self.data_read.emit([new_val]) self.data_read.emit([new_val])

View File

@ -22,14 +22,22 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
self.start_lineedit.setValidator(QtGui.QDoubleValidator()) self.start_lineedit.setValidator(QtGui.QDoubleValidator())
self.stop_lineedit.setValidator(QtGui.QDoubleValidator()) self.stop_lineedit.setValidator(QtGui.QDoubleValidator())
self.graph_checkbox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(not bool(x))) self.graph_checkbox.stateChanged.connect(
lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.CheckState.Unchecked)
)
self.listWidget.installEventFilter(self) self.listWidget.installEventFilter(self)
def __call__(self, path=None):
if path is None:
path = pathlib.Path().home()
self.path = path
self.listWidget.clear()
def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool: def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool:
# intercept key press in listwidget to allow deletion with Del # intercept key press in listwidget to allow deletion with Del
if evt.type() == QtCore.QEvent.KeyPress: if evt.type() == QtCore.QEvent.Type.KeyPress:
if evt.key() == QtCore.Qt.Key_Delete: if evt.key() == QtCore.Qt.Key.Key_Delete:
self.listWidget.takeItem(self.listWidget.currentRow()) self.listWidget.takeItem(self.listWidget.currentRow())
return True return True
@ -41,21 +49,25 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
@QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_region_checkBox_stateChanged')
def use_region(self, state: int): def use_region(self, state: int):
self.start_lineedit.setEnabled(state == QtCore.Qt.Checked) self.start_lineedit.setEnabled(state == QtCore.Qt.CheckState.Checked)
self.stop_lineedit.setEnabled(state == QtCore.Qt.Checked) self.stop_lineedit.setEnabled(state == QtCore.Qt.CheckState.Checked)
@QtCore.pyqtSlot(name='on_file_pushbutton_clicked') @QtCore.pyqtSlot(name='on_file_pushbutton_clicked')
@QtCore.pyqtSlot(name='on_dir_pushbutton_clicked') @QtCore.pyqtSlot(name='on_dir_pushbutton_clicked')
def get_input(self): def get_input(self):
if self.sender() == self.file_pushbutton: if self.sender() == self.file_pushbutton:
infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(caption='Select HDF files', infiles, _ = QtWidgets.QFileDialog.getOpenFileNames(
directory=str(self.path), caption='Select HDF files',
filter='HDF files (*.h5)') directory=str(self.path),
filter='HDF files (*.h5)',
)
else: else:
infiles = QtWidgets.QFileDialog.getExistingDirectory(caption='Select input directory', infiles = QtWidgets.QFileDialog.getExistingDirectory(
directory=str(self.path), caption='Select input directory',
options=QtWidgets.QFileDialog.ShowDirsOnly) directory=str(self.path),
options=QtWidgets.QFileDialog.ShowDirsOnly,
)
infiles = [infiles] if infiles else infiles infiles = [infiles] if infiles else infiles
if infiles: if infiles:
@ -65,14 +77,18 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
@QtCore.pyqtSlot(name='on_savebutton_clicked') @QtCore.pyqtSlot(name='on_savebutton_clicked')
def save_path(self): def save_path(self):
outfile = QtWidgets.QFileDialog.getExistingDirectory(self, caption='Select directory', outfile = QtWidgets.QFileDialog.getExistingDirectory(
directory=self.label.text(), self,
options=QtWidgets.QFileDialog.ShowDirsOnly) caption='Select directory',
directory=self.label.text(),
options=QtWidgets.QFileDialog.ShowDirsOnly,
)
if outfile: if outfile:
self.label.setText(outfile) self.label.setText(outfile)
def accept(self): def accept(self):
items = [self.listWidget.item(i).text() for i in range(self.listWidget.count())] items = [self.listWidget.item(i).text() for i in range(self.listWidget.count())]
print(items)
if items: if items:
with busy_cursor(): with busy_cursor():
self.read(items) self.read(items)
@ -107,9 +123,10 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog):
ret_vals = [] ret_vals = []
ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables)) ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables))
print(ret_vals)
grp = '' grp = ''
if not self.graph_checkbox.isChecked(): if not self.graph_checkbox.isChecked():
grp = self.graph_comboBox.currentData(QtCore.Qt.UserRole) grp = self.graph_comboBox.currentData(QtCore.Qt.ItemDataRole.UserRole)
self.data_read.emit(ret_vals, grp) self.data_read.emit(ret_vals, grp)

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
import struct import struct
from ..Qt import QtCore from ..Qt import QtCore, QtWidgets
from .asciireader import QAsciiReader from .asciireader import QAsciiReader
from .hdfreader import QHdfViewer from .hdfreader import QHdfViewer
from .bdsreader import QBDSReader from .bdsreader import QBDSReader
@ -26,8 +26,12 @@ class QFileReader(QtCore.QObject):
self.reader = {} self.reader = {}
for ext, reader in [ for ext, reader in [
('txt', QAsciiReader), ('dsc', QDSCReader), ('agr', QGraceReader), ('txt', QAsciiReader),
('bds', QBDSReader), ('hdf', QHdfViewer), ('nmr', QNMRReader) ('dsc', QDSCReader),
('agr', QGraceReader),
('bds', QBDSReader),
('hdf', QHdfViewer),
('nmr', QNMRReader),
]: ]:
self.register(ext, reader) self.register(ext, reader)
@ -47,6 +51,7 @@ class QFileReader(QtCore.QObject):
if not isinstance(fname, list): if not isinstance(fname, list):
fname = [fname] fname = [fname]
status = QtWidgets.QDialog.Accepted
for f in fname: for f in fname:
f = Path(f) f = Path(f)
dtype = self.guess_type(f) dtype = self.guess_type(f)
@ -57,7 +62,10 @@ class QFileReader(QtCore.QObject):
try: try:
# If QAsciiReader.skip = True it accepts automatically and returns None # If QAsciiReader.skip = True it accepts automatically and returns None
r(f).exec() if status == QtWidgets.QDialog.DialogCode.Rejected and isinstance(r, QAsciiReader) and self.reader['txt'].skip:
break
status = r(f).exec()
except AttributeError: except AttributeError:
pass pass

View File

@ -1,5 +1,8 @@
import logging
from pathlib import Path from pathlib import Path
from PyQt5 import QtWidgets
from .codeeditor import _make_textformats from .codeeditor import _make_textformats
from ..Qt import QtWidgets, QtCore, QtGui from ..Qt import QtWidgets, QtCore, QtGui
from nmreval.configs import config_paths from nmreval.configs import config_paths
@ -113,3 +116,28 @@ class QLog(QtWidgets.QDialog):
for lines in text[-100:]: for lines in text[-100:]:
self.plainTextEdit.appendPlainText(lines[:-1]) self.plainTextEdit.appendPlainText(lines[:-1])
class ConsoleDock(QtWidgets.QDockWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.code = QtWidgets.QPlainTextEdit(parent)
self.code.highlight = LogHighlighter(self.code.document())
self.code.setReadOnly(True)
self.code.setMaximumBlockCount(50)
self.setWidget(self.code)
class QTextHandler(logging.Handler):
def __init__(self, parent):
super().__init__()
self.console = ConsoleDock(parent)
self.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
self.setLevel(logging.WARNING)
def emit(self, record):
msg = self.format(record)
self.console.code.appendPlainText(msg)
self.console.show()

View File

@ -22,51 +22,59 @@ class Namespace:
if basic: if basic:
self.add_namespace( self.add_namespace(
{'x': (None, 'x values'), {
'y': (None, 'x values'), 'x': (None, 'x values'),
'y_err': (None, 'y error values'), 'y': (None, 'x values'),
'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'), 'y_err': (None, 'y error values'),
'np': (np, 'numpy module'), 'fit': (None, 'dictionary of fit parameter', 'fit["PIKA"]'),
'np': (np, 'numpy module'),
}, },
parents=('Basic', 'General'), parents=('Basic', 'General'),
) )
self.add_namespace( self.add_namespace(
{'sin': (np.sin, 'Sine', 'sin(PIKA)'), {
'cos': (np.cos, 'Cosine', 'cos(PIKA)'), 'sin': (np.sin, 'Sine', 'sin(PIKA)'),
'tan': (np.tan, 'Tangens', 'tan(PIKA)'), 'cos': (np.cos, 'Cosine', 'cos(PIKA)'),
'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'), 'tan': (np.tan, 'Tangens', 'tan(PIKA)'),
'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'), 'ln': (np.log, 'Natural Logarithm', 'ln(PIKA)'),
'exp': (np.exp, 'Exponential', 'exp(PIKA)'), 'log': (np.log10, 'Logarithm (base 10)', 'log(PIKA)'),
'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'), 'exp': (np.exp, 'Exponential', 'exp(PIKA)'),
'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', 'lin_range(start, stop, N)'), 'sqrt': (np.sqrt, 'Root', 'sqrt(PIKA)'),
'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', 'lin_range(start, stop, N)'), 'lin_range': (np.linspace, 'N evenly spaced over interval [start, stop]', 'lin_range(start, stop, N)'),
}, 'log_range': (np.geomspace, 'N evenly spaced (log-scale) over interval [start, stop]', 'lin_range(start, stop, N)'),
},
parents=('Basic', 'Functions')) parents=('Basic', 'Functions'))
self.add_namespace( self.add_namespace(
{'max': (np.max, 'Maximum value', 'max(PIKA)'), {
'min': (np.min, 'Minimum value', 'min(PIKA)'), 'max': (np.max, 'Maximum value', 'max(PIKA)'),
'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'), 'min': (np.min, 'Minimum value', 'min(PIKA)'),
'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'), 'argmax': (np.argmax, 'Index of maximum value', 'argmax(PIKA)'),
}, 'argmin': (np.argmax, 'Index of minimum value', 'argmin(PIKA)'),
},
parents=('Basic', 'Values')), parents=('Basic', 'Values')),
if const: if const:
self.add_namespace( self.add_namespace(
{'e': (constants.e, 'e / As'), {
'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'), 'e': (constants.e, 'e / As'),
'Eu': (constants.Eu,), 'h': (constants.h, 'h / eVs'), 'eps0': (constants.epsilon0, 'epsilon0 / As/Vm'),
'hbar': (constants.hbar, 'hbar / eVs'), 'kB': (constants.kB, 'kB / eV/K'), 'Eu': (constants.Eu,),
'mu0': (constants.mu0, 'mu0 / Vs/Am'), 'NA': (constants.NA, 'NA / 1/mol'), 'h': (constants.h, 'h / eVs'),
'pi': (constants.pi,), 'R': (constants.R, 'R / eV'), 'hbar': (constants.hbar, 'hbar / eVs'),
}, 'kB': (constants.kB, 'kB / eV/K'),
'mu0': (constants.mu0, 'mu0 / Vs/Am'),
'NA': (constants.NA, 'NA / 1/mol'),
'pi': (constants.pi,),
'R': (constants.R, 'R / eV'),
},
parents=('Constants', 'Maybe useful'), parents=('Constants', 'Maybe useful'),
) )
self.add_namespace( self.add_namespace(
{f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()}, {f'gamma["{k}"]': (v, k, f'gamma["{k}"]') for k, v in constants.gamma.items()},
parents=('Constants', 'Magnetogyric ratios (in 1/(sT))') parents=('Constants', 'Gyromagnetic ratios (in 1/(sT))')
) )
if fitfuncs: if fitfuncs:
@ -199,7 +207,7 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
for entry in subspace: for entry in subspace:
key_item = QtWidgets.QTableWidgetItem(entry) key_item = QtWidgets.QTableWidgetItem(entry)
key_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) key_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)
vals = self.namespace.namespace[entry] vals = self.namespace.namespace[entry]
@ -214,12 +222,12 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
display = vals[1] display = vals[1]
value_item = QtWidgets.QTableWidgetItem(display) value_item = QtWidgets.QTableWidgetItem(display)
value_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) value_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)
key_item.setData(QtCore.Qt.UserRole, alias) key_item.setData(QtCore.Qt.ItemDataRole.UserRole, alias)
key_item.setData(QtCore.Qt.UserRole+1, entry) key_item.setData(QtCore.Qt.ItemDataRole.UserRole+1, entry)
value_item.setData(QtCore.Qt.UserRole, alias) value_item.setData(QtCore.Qt.ItemDataRole.UserRole, alias)
value_item.setData(QtCore.Qt.UserRole+1, entry) value_item.setData(QtCore.Qt.ItemDataRole.UserRole+1, entry)
row = self.namespace_table.rowCount() row = self.namespace_table.rowCount()
self.namespace_table.setRowCount(row+1) self.namespace_table.setRowCount(row+1)
@ -241,5 +249,5 @@ class QNamespaceWidget(QtWidgets.QWidget, Ui_Form):
@QtCore.pyqtSlot(QtWidgets.QTableWidgetItem, name='on_namespace_table_itemDoubleClicked') @QtCore.pyqtSlot(QtWidgets.QTableWidgetItem, name='on_namespace_table_itemDoubleClicked')
def item_selected(self, item: QtWidgets.QTableWidgetItem): def item_selected(self, item: QtWidgets.QTableWidgetItem):
self.selected.emit(item.data(QtCore.Qt.UserRole)) self.selected.emit(item.data(QtCore.Qt.ItemDataRole.UserRole))
self.sendKey.emit(item.data(QtCore.Qt.UserRole+1)) self.sendKey.emit(item.data(QtCore.Qt.ItemDataRole.UserRole+1))

View File

@ -183,6 +183,8 @@ class PlotItem(PlotDataItem):
brush = self.opts['symbolBrush'] brush = self.opts['symbolBrush']
if isinstance(brush, tuple): if isinstance(brush, tuple):
self.opts['symbolcolor'] = brush self.opts['symbolcolor'] = brush
elif isinstance(brush, str):
self.opts['symbolcolor'] = int(f'0x{brush[1:3]}', 16), int(f'0x{brush[3:5]}', 16), int(f'0x{brush[5:7]}', 16)
else: else:
c = brush.color() c = brush.color()
self.opts['symbolcolor'] = c.red(), c.green(), c.blue() self.opts['symbolcolor'] = c.red(), c.green(), c.blue()
@ -340,7 +342,8 @@ class PlotItem(PlotDataItem):
opts = self.opts opts = self.opts
item_dic = { item_dic = {
'x': x, 'y': y, 'x': x,
'y': y,
'name': opts.get('name', ''), 'name': opts.get('name', ''),
'symbolsize': opts['symbolSize'], 'symbolsize': opts['symbolSize'],
} }

View File

@ -10,7 +10,7 @@ from ..Qt import QtGui, QtWidgets, QtCore
@contextmanager @contextmanager
def busy_cursor(): def busy_cursor():
try: try:
cursor = QtGui.QCursor(QtCore.Qt.ForbiddenCursor) cursor = QtGui.QCursor(QtCore.Qt.CursorShape.ForbiddenCursor)
QtWidgets.QApplication.setOverrideCursor(cursor) QtWidgets.QApplication.setOverrideCursor(cursor)
yield yield

View File

@ -0,0 +1,3 @@

View File

@ -2,7 +2,6 @@ from __future__ import annotations
import os import os
import re import re
import time
from pathlib import Path from pathlib import Path
from numpy import geomspace, linspace from numpy import geomspace, linspace
@ -13,17 +12,18 @@ from nmreval.lib.logger import logger
from nmreval.io.sessionwriter import NMRWriter from nmreval.io.sessionwriter import NMRWriter
from .management import UpperManagement from .management import UpperManagement
from ..lib.logger import QTextHandler
from ..Qt import QtGui, QtPrintSupport from ..Qt import QtGui, QtPrintSupport
from ..data.shift_graphs import QShift from ..data.shift_graphs import QShift
from ..data.signaledit import QPreviewDialog, QBaselineDialog from ..data.signaledit import QPreviewDialog, QBaselineDialog
from ..dsc.glass_dialog import TgCalculator from ..dsc.glass_dialog import TgCalculator
from ..fit.fit_toolbar import FitToolbar
from ..fit.result import FitExtension, QFitResult from ..fit.result import FitExtension, QFitResult
from ..graphs.graphwindow import QGraphWindow from ..graphs.graphwindow import QGraphWindow
from ..graphs.movedialog import QMover from ..graphs.movedialog import QMover
from ..io.fcbatchreader import QFCReader from ..io.fcbatchreader import QFCReader
from ..io.filedialog import * from ..io.filedialog import *
from ..lib.iconloading import make_action_icons, get_icon from ..lib.iconloading import make_action_icons, get_icon
from ..lib.pg_objects import RegionItem
from ..lib.starter import make_starter from ..lib.starter import make_starter
from ..math.binning import BinningWindow from ..math.binning import BinningWindow
from ..math.evaluation import QEvalDialog from ..math.evaluation import QEvalDialog
@ -62,6 +62,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.fitresult_dialog = None self.fitresult_dialog = None
self.eval = None self.eval = None
self.editor = None self.editor = None
self._interpol_dialog = None
self.fc_reader = None
self.logtext = QTextHandler(self)
logger.addHandler(self.logtext)
self.addDockWidget(QtCore.Qt.DockWidgetArea.BottomDockWidgetArea, self.logtext.console)
self.logtext.console.hide()
self.movedialog = QMover(self) self.movedialog = QMover(self)
@ -97,12 +104,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.norm_toolbutton.setIcon(get_icon('normal')) self.norm_toolbutton.setIcon(get_icon('normal'))
self.toolbar_edit.addWidget(self.norm_toolbutton) self.toolbar_edit.addWidget(self.norm_toolbutton)
self.fitlim_button = QtWidgets.QToolButton(self)
self.fitlim_button.setMenu(self.menuLimits)
self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup)
self.fitlim_button.setIcon(get_icon('fit_region'))
self.toolBar_fit.addWidget(self.fitlim_button)
while self.tabWidget.count() > 2: while self.tabWidget.count() > 2:
self.tabWidget.removeTab(self.tabWidget.count()-1) self.tabWidget.removeTab(self.tabWidget.count()-1)
@ -120,9 +121,11 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.statusBar.addWidget(self.mousepos) self.statusBar.addWidget(self.mousepos)
self.fitregion = RegionItem()
self._fit_plot_id = None self._fit_plot_id = None
self.fit_toolbar = FitToolbar(self.action_FitWidget, self.menuLimits, self)
self.addToolBar(self.fit_toolbar)
self.setGeometry(QtWidgets.QStyle.alignedRect( self.setGeometry(QtWidgets.QStyle.alignedRect(
QtCore.Qt.LayoutDirection.LeftToRight, QtCore.Qt.LayoutDirection.LeftToRight,
QtCore.Qt.AlignmentFlag.AlignCenter, QtCore.Qt.AlignmentFlag.AlignCenter,
@ -138,11 +141,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.ac_group.addAction(self.action_nm_fit) self.ac_group.addAction(self.action_nm_fit)
self.ac_group.addAction(self.action_odr_fit) self.ac_group.addAction(self.action_odr_fit)
self.ac_group2 = QtWidgets.QActionGroup(self)
self.ac_group2.addAction(self.action_no_range)
self.ac_group2.addAction(self.action_x_range)
self.ac_group2.addAction(self.action_custom_range)
def _init_signals(self): def _init_signals(self):
self.actionRedo = self.management.undostack.createRedoAction(self) self.actionRedo = self.management.undostack.createRedoAction(self)
icon = QtGui.QIcon.fromTheme("edit-redo") icon = QtGui.QIcon.fromTheme("edit-redo")
@ -158,7 +156,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter) self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.ac_group2.triggered.connect(self.change_fit_limits) self.fit_toolbar.limit_group.triggered.connect(self.change_fit_limits)
self.t1action.triggered.connect(lambda: self._show_tab('t1_temp')) self.t1action.triggered.connect(lambda: self._show_tab('t1_temp'))
self.action_edit.triggered.connect(self.do_preview) self.action_edit.triggered.connect(self.do_preview)
@ -237,7 +235,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.action_norm_first.triggered.connect(lambda: self.management.apply('norm', ('first',))) self.action_norm_first.triggered.connect(lambda: self.management.apply('norm', ('first',)))
self.action_norm_last.triggered.connect(lambda: self.management.apply('norm', ('last',))) self.action_norm_last.triggered.connect(lambda: self.management.apply('norm', ('last',)))
self.action_norm_area.triggered.connect(lambda: self.management.apply('norm', ('area',))) self.action_norm_area.triggered.connect(lambda: self.management.apply('norm', ('area',)))
self.action_cut.triggered.connect(lambda: self.management.cut()) self.action_cut_xaxis.triggered.connect(lambda: self.management.cut(True, False))
self.action_cut_yaxis.triggered.connect(lambda: self.management.cut(False, True))
self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat()) self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat())
@ -268,14 +267,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
@QtCore.pyqtSlot(name='on_actionOpen_FC_triggered') @QtCore.pyqtSlot(name='on_actionOpen_FC_triggered')
def read_fc(self): def read_fc(self):
reader = QFCReader(path=self.path, parent=self) if self.fc_reader is None:
reader.add_graphs(self.management.graphs.list()) self.fc_reader = QFCReader(path=self.path, parent=self)
reader.data_read.connect(self.management.add_new_data) self.fc_reader.data_read.connect(self.management.add_new_data)
reader.exec() else:
self.fc_reader(path=self.path)
self.fc_reader.add_graphs(self.management.graphs.list())
self.fc_reader.exec()
self.path = reader.path self.path = self.fc_reader.path
del reader
@QtCore.pyqtSlot(name='on_actionPrint_triggered') @QtCore.pyqtSlot(name='on_actionPrint_triggered')
def print(self): def print(self):
@ -705,10 +705,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
return return
gnames = self.management.graphs.tree() gnames = self.management.graphs.tree()
dialog = InterpolDialog(parent=self) if self._interpol_dialog is None:
dialog.set_data(gnames, self.current_graph_widget.id) self._interpol_dialog = InterpolDialog(parent=self)
dialog.new_data.connect(self.management.interpolate_data) self._interpol_dialog.new_data.connect(self.management.interpolate_data)
dialog.show() else:
self._interpol_dialog()
self._interpol_dialog.set_data(gnames, self.current_graph_widget.id)
self._interpol_dialog.show()
@QtCore.pyqtSlot(name='on_action_calc_triggered') @QtCore.pyqtSlot(name='on_action_calc_triggered')
def open_eval_dialog(self): def open_eval_dialog(self):
@ -898,30 +901,29 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.fit_dialog.load(self.management.active_sets) self.fit_dialog.load(self.management.active_sets)
for item in self.fit_dialog.preview_lines: for item in self.fit_dialog.preview_lines:
self.current_graph_widget.add_external(item) self.current_graph_widget.add_external(item)
if self.action_custom_range.isChecked(): if self.action_custom_range.isChecked() or self.actionExclude_region.isChecked():
self.current_graph_widget.add_external(self.fitregion) self.current_graph_widget.add_external(self.fit_toolbar.region)
block_window = True block_window = True
else: else:
for item in self.fit_dialog.preview_lines: for item in self.fit_dialog.preview_lines:
self.current_graph_widget.remove_external(item) self.current_graph_widget.remove_external(item)
self.current_graph_widget.remove_external(self.fitregion) self.current_graph_widget.remove_external(self.fit_toolbar.region)
return block_window return block_window
@QtCore.pyqtSlot(QtWidgets.QAction) @QtCore.pyqtSlot(QtWidgets.QAction)
def change_fit_limits(self, action: QtWidgets.QAction): def change_fit_limits(self, action: QtWidgets.QAction):
if action == self.action_custom_range and self.fit_dialog.isVisible(): if self.current_graph_widget is None:
self.current_graph_widget.add_external(self.fitregion) return
if action in [self.action_custom_range, self.actionExclude_region] and self.fit_dialog.isVisible():
self.current_graph_widget.add_external(self.fit_toolbar.region)
else: else:
self.current_graph_widget.remove_external(self.fitregion) self.current_graph_widget.remove_external(self.fit_toolbar.region)
def start_fit(self, parameter, links, fit_options): def start_fit(self, parameter, links, fit_options):
fit_options['limits'] = { fit_options['limits'] = self.fit_toolbar.get_limit()
self.action_no_range: 'none',
self.action_x_range: 'x',
self.action_custom_range: self.fitregion.getRegion()
}[self.ac_group2.checkedAction()]
fit_options['fit_mode'] = { fit_options['fit_mode'] = {
self.action_lm_fit: 'lsq', self.action_lm_fit: 'lsq',
@ -957,19 +959,19 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
for item in self.fit_dialog.preview_lines: for item in self.fit_dialog.preview_lines:
g.add_external(item) g.add_external(item)
@QtCore.pyqtSlot(list) @QtCore.pyqtSlot(list, dict)
def show_fit_results(self, results: list): def show_fit_results(self, results: list, sub_colors: dict[str, tuple[float, float, float]]):
self.fit_dialog.fit_button.setEnabled(True) self.fit_dialog.fit_button.setEnabled(True)
self.fit_timer.stop() self.fit_timer.stop()
self.status.setText('') self.status.setText('')
if results: if results:
if self.fitresult_dialog is None: if self.fitresult_dialog is None:
self.fitresult_dialog = QFitResult(results, self.management, parent=self) self.fitresult_dialog = QFitResult(results, sub_colors, self.management, parent=self)
self.fitresult_dialog.add_graphs(self.management.graphs.list()) self.fitresult_dialog.add_graphs(self.management.graphs.list())
self.fitresult_dialog.closed.connect(self.accepts_fit) self.fitresult_dialog.closed.connect(self.accepts_fit)
self.fitresult_dialog.redoFit.connect(self.management.redo_fits) self.fitresult_dialog.redoFit.connect(self.management.redo_fits)
else: else:
self.fitresult_dialog(results) self.fitresult_dialog(results, sub_colors)
self.fitresult_dialog.add_graphs(self.management.graphs.list()) self.fitresult_dialog.add_graphs(self.management.graphs.list())
self.fitresult_dialog.show() self.fitresult_dialog.show()
@ -989,14 +991,19 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.editor.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) self.editor.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
self.editor.show() self.editor.show()
@QtCore.pyqtSlot(list) @QtCore.pyqtSlot(list, bool)
def extend_fit(self, sets: list): def extend_fit(self, sets: list, only_subplots: bool):
if only_subplots:
self.management.extend_fits(sets, None, True)
return
w = FitExtension(self) w = FitExtension(self)
res = w.exec() res = w.exec()
if res: if res:
p = w.values p = w.values
x = linspace(p[0], p[1], num=p[2]) spacefunc = geomspace if p[3] else linspace
self.management.extend_fits(sets, x) x = spacefunc(p[0], p[1], num=p[2])
self.management.extend_fits(sets, x, False)
@QtCore.pyqtSlot(name='on_action_create_fit_function_triggered') @QtCore.pyqtSlot(name='on_action_create_fit_function_triggered')
def open_fitmodel_wizard(self): def open_fitmodel_wizard(self):

View File

@ -85,7 +85,7 @@ class UpperManagement(QtCore.QObject):
newData = QtCore.pyqtSignal([list, str], [list, str, bool]) newData = QtCore.pyqtSignal([list, str], [list, str, bool])
deleteData = QtCore.pyqtSignal(list) deleteData = QtCore.pyqtSignal(list)
dataChanged = QtCore.pyqtSignal(str) dataChanged = QtCore.pyqtSignal(str)
fitFinished = QtCore.pyqtSignal(list) fitFinished = QtCore.pyqtSignal(list, dict)
stopFit = QtCore.pyqtSignal() stopFit = QtCore.pyqtSignal()
properties_collected = QtCore.pyqtSignal(dict) properties_collected = QtCore.pyqtSignal(dict)
unset_state = QtCore.pyqtSignal(list) unset_state = QtCore.pyqtSignal(list)
@ -428,6 +428,7 @@ class UpperManagement(QtCore.QObject):
self.graphs[d.graph].update_legend(identifier, name) self.graphs[d.graph].update_legend(identifier, name)
elif identifier in self.graphs: elif identifier in self.graphs:
self.graphs[identifier].title = name self.graphs[identifier].title = name
self.graphs.valueChanged.emit()
else: else:
raise KeyError('Unknown ID ' + str(identifier)) raise KeyError('Unknown ID ' + str(identifier))
@ -449,10 +450,17 @@ class UpperManagement(QtCore.QObject):
self.undostack.push(single_undo) self.undostack.push(single_undo)
self.undostack.endMacro() self.undostack.endMacro()
def cut(self): def cut(self, x: bool = False, y: bool = False) -> None:
if self.current_graph: if self.current_graph:
xlim, _ = self.graphs[self.current_graph].ranges xlim, ylim = self.graphs[self.current_graph].ranges
self.apply('cut', xlim)
if x is False:
xlim = (None, None)
if y is False:
ylim = (None, None)
self.apply('cut', (*xlim, *ylim))
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def unmask(self): def unmask(self):
@ -503,29 +511,46 @@ class UpperManagement(QtCore.QObject):
we = we_option we = we_option
if m_complex is None or m_complex == 1: if m_complex is None or m_complex == 1:
# model is not complex: m_complex = None
# model is complex, fit real part: m_complex = 1
_y = data_i.y.real _y = data_i.y.real
elif m_complex == 2 and np.iscomplexobj(data_i.y): data_complex = 1
_y = data_i.y.imag elif m_complex == 2:
# model is complex, fit imag part: m_complex = 2
if np.iscomplexobj(data_i.y):
# data is complex, use imag part
_y = data_i.y.imag
data_complex = 2
else:
# data is real
_y = data_i.y
data_complex = 1
else: else:
# model is complex, fit complex: m_complex = 0
# use data as given (complex or not)
_y = data_i.y _y = data_i.y
data_complex = 0
_x = data_i.x _x = data_i.x
# options for fit limits 'none', 'x', ('in', custom region), ('out', excluded region)
if fit_limits == 'none': if fit_limits == 'none':
inside = slice(None) inside = slice(None)
elif fit_limits == 'x': elif fit_limits == 'x':
x_lim, _ = self.graphs[self.current_graph].ranges x_lim, _ = self.graphs[self.current_graph].ranges
inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1])) inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1]))
elif fit_limits[0] == 'in':
inside = np.where((_x >= fit_limits[1][0]) & (_x <= fit_limits[1][1]))
else: else:
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1])) inside = np.where((_x < fit_limits[1][0]) | (_x > fit_limits[1][1]))
try: try:
if isinstance(we, str): if isinstance(we, str):
d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id) d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id, complex_type=data_complex)
else: else:
d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id) d = fit_d.Data(_x[inside], _y[inside], we=we[inside], idx=set_id, complex_type=data_complex)
except Exception as e: except Exception as e:
raise Exception(f'Setting data failed for {set_id}') raise Exception(f'Setting data failed for {data_i.name}') from e
d.set_model(m) d.set_model(m)
try: try:
@ -541,7 +566,7 @@ class UpperManagement(QtCore.QObject):
return True return True
except Exception as e: except Exception as e:
logger.error('Fit preparation failed', *e.args) logger.error(f'Fit preparation failed with error: {e.args}')
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), QtWidgets.QMessageBox.warning(QtWidgets.QWidget(),
'Fit prep failed', 'Fit prep failed',
f'Fit preparation failed:\n' f'Fit preparation failed:\n'
@ -569,13 +594,23 @@ class UpperManagement(QtCore.QObject):
def end_fit(self, result: list, success: bool): def end_fit(self, result: list, success: bool):
if success: if success:
logger.info('Successful fit') logger.info('Successful fit')
self.fitFinished.emit(result)
sub_colors = {}
for k, v in self.__fit_options[0].items():
sub_colors.update({set_id: v['color'] for set_id in v['data_parameter']})
self.fitFinished.emit(result, sub_colors)
else: else:
e = result[0] e = result[0]
logger.exception(e, exc_info=True) logger.exception(e, exc_info=True)
QtWidgets.QMessageBox.warning(QtWidgets.QWidget(), 'Fit failed', QtWidgets.QMessageBox.warning(
f'Fit kaput with exception: \n\n{e!r}') QtWidgets.QWidget(),
self.fitFinished.emit([]) 'Fit failed',
f'Fit kaput with exception: \n\n{e!r}'
)
self.fitFinished.emit([], {})
self._fit_active = False self._fit_active = False
@QtCore.pyqtSlot(dict) @QtCore.pyqtSlot(dict)
@ -613,7 +648,7 @@ class UpperManagement(QtCore.QObject):
continue continue
if not all(e is None for e in extrapolate): if not all(e is None for e in extrapolate):
spacefunc = np.geomspace if fit.islog else np.linspace spacefunc = np.geomspace if extrapolate[3] else np.linspace
xmin = fit.x.min() xmin = fit.x.min()
xmax = fit.x.max() xmax = fit.x.max()
@ -676,17 +711,21 @@ class UpperManagement(QtCore.QObject):
self.newData.emit(f_id_list, gid) self.newData.emit(f_id_list, gid)
def extend_fits(self, set_id: list, x_range: np.ndarray): def extend_fits(self, set_id: list, x_range: np.ndarray | None, only_subplots: bool):
graphs = {} graphs = {}
for sid in set_id: for sid in set_id:
data = self[sid] data = fit = self[sid]
fit = data.copy(full=True, keep_color=True)
fit.data = fit.data.with_new_x(x_range)
graph_id = data.graph graph_id = data.graph
if graph_id not in graphs: if graph_id not in graphs:
graphs[graph_id] = [] graphs[graph_id] = []
graphs[graph_id].append(self.add(fit))
if not only_subplots:
fit = data.copy(full=True, keep_color=True)
if x_range is not None:
fit.data = fit.data.with_new_x(x_range)
graphs[graph_id].append(self.add(fit))
color_scheme = available_cycles['colorblind'] color_scheme = available_cycles['colorblind']
for subfunc, col in zip(fit.data.sub(fit.x), cycle(color_scheme)): for subfunc, col in zip(fit.data.sub(fit.x), cycle(color_scheme)):

View File

@ -16,6 +16,12 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
self.step_lineEdit.setValidator(QtGui.QIntValidator()) self.step_lineEdit.setValidator(QtGui.QIntValidator())
self._data = {} self._data = {}
self._src_id = None
self._dest_graph = ''
def __call__(self):
self.listWidget.clear()
self._data = {}
@QtCore.pyqtSlot(int, name='on_xaxis_comboBox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_xaxis_comboBox_currentIndexChanged')
def change_x_source(self, idx: int): def change_x_source(self, idx: int):
@ -25,29 +31,41 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
def set_data(self, data, current_gid): def set_data(self, data, current_gid):
self.graph_combobox.blockSignals(True) self.graph_combobox.blockSignals(True)
self._data = {} self._data = {}
dest_idx = 0
for (gid, graph_name), sets in data.items(): for (gid, graph_name), sets in data.items():
self.graph_combobox.addItem(graph_name, userData=gid) self.graph_combobox.addItem(graph_name, userData=gid)
self.dest_combobox.addItem(graph_name, userData=gid) self.dest_combobox.addItem(graph_name, userData=gid)
if self._dest_graph == gid:
dest_idx = self.dest_combobox.currentIndex()
if gid == current_gid: if gid == current_gid:
self.make_list(sets) self.make_list(sets)
self._data[gid] = sets self._data[gid] = sets
self.graph_combobox.blockSignals(False) self.graph_combobox.blockSignals(False)
self.change_graph(0) self.change_graph(dest_idx)
def make_list(self, current_sets): def make_list(self, current_sets):
for sid, set_name in current_sets: for sid, set_name in current_sets:
item = QtWidgets.QListWidgetItem(set_name) item = QtWidgets.QListWidgetItem(set_name)
item.setData(QtCore.Qt.UserRole, sid) item.setData(QtCore.Qt.ItemDataRole.UserRole, sid)
item.setCheckState(QtCore.Qt.Checked) item.setCheckState(QtCore.Qt.CheckState.Checked)
self.listWidget.addItem(item) self.listWidget.addItem(item)
@QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged')
def change_graph(self, idx: int): def change_graph(self, idx: int):
self.set_combobox.clear() self.set_combobox.clear()
gid = self.graph_combobox.itemData(idx, QtCore.Qt.UserRole) gid = self.graph_combobox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole)
set_idx = -1
if gid is not None: if gid is not None:
for set_key, set_name in self._data[gid]: for i, (set_key, set_name) in enumerate(self._data[gid]):
print(self._src_id, set_key, set_name, i)
self.set_combobox.addItem(set_name, userData=set_key) self.set_combobox.addItem(set_name, userData=set_key)
print(self.set_combobox.currentIndex())
if self._src_id == set_key:
set_idx = i
print(set_idx)
if set_idx > -1:
self.set_combobox.setCurrentIndex(set_idx)
def collect_parameter(self): def collect_parameter(self):
xlog = self.xlog_checkBox.isChecked() xlog = self.xlog_checkBox.isChecked()
@ -71,21 +89,35 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog):
x_src = (start, stop, step, loggy) x_src = (start, stop, step, loggy)
else: else:
x_src = (self.set_combobox.currentData(QtCore.Qt.UserRole),) self._src_id = self.set_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
x_src = (self._src_id,)
dest_graph = self.dest_combobox.currentData(QtCore.Qt.UserRole) self._dest_graph = self.dest_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
use_data = [] use_data = []
for i in range(self.listWidget.count()): for i in range(self.listWidget.count()):
item = self.listWidget.item(i) item = self.listWidget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.CheckState.Checked:
use_data.append(item.data(QtCore.Qt.UserRole)) use_data.append(item.data(QtCore.Qt.ItemDataRole.UserRole))
self.new_data.emit(use_data, mode, xlog, ylog, x_src, dest_graph) self.new_data.emit(use_data, mode, xlog, ylog, x_src, self._dest_graph)
return True return True
def accept(self): def _save_state(self):
success = self.collect_parameter() self._src_id = self.set_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
if success: self._dest_graph = self.dest_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole)
super().accept()
@QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked')
def check_next_actions(self, bttn: QtWidgets.QAbstractButton):
role = self.buttonBox.buttonRole(bttn)
self._save_state()
if role == self.buttonBox.ButtonRole.RejectRole:
self.close()
else:
success = self.collect_parameter()
if success and role == self.buttonBox.ButtonRole.AcceptRole:
self.close()

View File

@ -12,10 +12,10 @@ class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog):
@QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged') @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged')
def change_mode(self, idx: int): def change_mode(self, idx: int):
if idx == 2: if idx == 1:
self.widget.show() self.widget.show()
self.widget_2.hide() self.widget_2.hide()
elif idx == 3: elif idx == 2:
self.widget.show() self.widget.show()
self.widget_2.show() self.widget_2.show()
else: else:
@ -29,12 +29,24 @@ class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog):
idx = self.comboBox.currentIndex() idx = self.comboBox.currentIndex()
# this order must match the combobox # this order must match the combobox
para['mode'] = ['mean', 'savgol', 'loess', 'median', 'std', 'var', 'max', 'min', 'sum'][idx] para['mode'] = [
'mean',
'savgol',
'loess',
'median',
'std',
'var',
'max',
'min',
'sum',
][idx]
if idx == 2: # Savitzky-Golay needs also polynomial degree
if idx == 1:
para['deg'] = self.polynom_spinBox.value() para['deg'] = self.polynom_spinBox.value()
if idx == 3: # LOESS needs also polynomial degree and number of iterations
if idx == 2:
para['deg'] = self.polynom_spinBox.value() para['deg'] = self.polynom_spinBox.value()
para['it'] = self.iter_spinBox.value() para['it'] = self.iter_spinBox.value()

View File

@ -320,7 +320,11 @@ class Points:
pts = [] pts = []
_tmp_x = self._x[self.mask] _tmp_x = self._x[self.mask]
_tmp_y = self._y[self.mask] x_order = np.argsort(_tmp_x)
_tmp_x = _tmp_x[x_order]
_tmp_y = self._y[self.mask][x_order]
_tmp_yerr = self._y_err[self.mask][x_order]
if idx is not None: if idx is not None:
for idx_i in idx: for idx_i in idx:
if isinstance(idx_i, tuple): if isinstance(idx_i, tuple):
@ -338,7 +342,7 @@ class Points:
right_b = int(min(len(self), x_idx + avg_range[1] + 1)) right_b = int(min(len(self), x_idx + avg_range[1] + 1))
if left_b < right_b: if left_b < right_b:
pts.append([_tmp_x[x_idx], *self._average(avg_mode, x_idx, left_b, right_b)]) pts.append([_tmp_x[x_idx], *self._average(_tmp_x, _tmp_y, _tmp_yerr, avg_mode, x_idx, left_b, right_b)])
else: else:
pts.append([_tmp_x[x_idx], _tmp_y[x_idx], self._y_err[x_idx]]) pts.append([_tmp_x[x_idx], _tmp_y[x_idx], self._y_err[x_idx]])
@ -358,28 +362,37 @@ class Points:
left_b = int(max(0, x_idx - avg_range[0])) left_b = int(max(0, x_idx - avg_range[0]))
right_b = int(min(len(self), x_idx + avg_range[1] + 1)) right_b = int(min(len(self), x_idx + avg_range[1] + 1))
pts.append([_tmp_x[x_idx], *self._average(avg_mode, x_idx, left_b, right_b)]) pts.append([_tmp_x[x_idx], *self._average(_tmp_x, _tmp_y, _tmp_yerr, avg_mode, x_idx, left_b, right_b)])
return pts return pts
def _average(self, mode: str, idx, left: int, right: int) -> tuple[float, float]: @staticmethod
def _average(
x: np.ndarray,
y: np.ndarray,
y_err: np.ndarray,
mode: str,
idx: int,
left: int,
right: int,
) -> tuple[float, float]:
if mode == 'mean': if mode == 'mean':
y_mean = np.mean(self._y[self.mask][left:right].real) y_mean = np.mean(y[left:right].real)
y_err = np.linalg.norm(self._y_err[self.mask][left:right]) / (right - left) y_err_mean = np.linalg.norm(y_err[left:right]) / (right - left)
elif mode == 'sum': elif mode == 'sum':
y_mean = np.sum(self._y[self.mask][left:right].real) y_mean = np.sum(y[left:right].real)
y_err = np.linalg.norm(self._y_err[self.mask][left:right]) y_err_mean = np.linalg.norm(y_err[left:right])
elif mode == 'integral': elif mode == 'integral':
y_mean = simpson(self._y[self.mask][left:right].real, x=self._x[left:right]) y_mean = simpson(y[left:right].real, x=x[left:right])
y_err = np.linalg.norm(cumulative_trapezoid(self._y_err[self.mask][left:right].real, x=self._x[left:right])) y_err_mean = np.linalg.norm(cumulative_trapezoid(y_err[left:right].real, x=x[left:right]))
else: else:
y_mean = self._y[self.mask][idx].real y_mean = y[idx].real
y_err = self._y_err[self.mask][idx] y_err_mean = y_err[idx]
return y_mean, y_err return y_mean, y_err_mean
def concatenate(self, other): def concatenate(self, other):
""" """
@ -527,26 +540,37 @@ class Points:
return self return self
def cut(self, low_lim: float = None, high_lim: float = None): def cut(self, x_low: float = None, x_high: float = None, y_low: float = None, y_high: float = None):
""" """
Cut Cut
Args: Args:
low_lim: x_low: Lower limit
high_lim: x_high: Upper limit for x values
y_low: Lower limit
y_high: Upper limit for x valuew
Returns: Returns:
""" """
if low_lim is None and high_lim is None:
if x_low is None and x_high is None and y_low is None and y_high is None:
return self return self
if low_lim is None: if x_low is None:
low_lim = np.min(self._x) x_low = np.min(self._x)-1
if high_lim is None: if x_high is None:
high_lim = np.max(self._x) x_high = np.max(self._x)+1
_mask = np.ma.masked_inside(self._x, low_lim, high_lim).mask if y_low is None:
y_low = np.min(self._y.real)-1
if y_high is None:
y_high = np.max(self._y.real)+1
x_mask = (self._x >= x_low) & (self._x <= x_high)
y_mask = (self._y.real >= y_low) & (self._y.real <= y_high)
_mask = x_mask & y_mask
self._x = self._x[_mask] self._x = self._x[_mask]
self._y = self._y[_mask] self._y = self._y[_mask]

View File

@ -3,7 +3,12 @@ from ctypes import c_double, cast, pointer, c_void_p
import numpy as np import numpy as np
from scipy import LowLevelCallable from scipy import LowLevelCallable
from scipy.integrate import quad, simps as simpson
from scipy.integrate import quad
try:
from scipy.integrate import simps as simpson
except ImportError:
from scipy.integrate import simpson
from .base import Distribution from .base import Distribution
from ..lib.utils import ArrayLike from ..lib.utils import ArrayLike

View File

@ -9,7 +9,13 @@ from inspect import signature, Parameter
class ModelFactory: class ModelFactory:
@staticmethod @staticmethod
def create_from_list(funcs: list, left=None, func_order=None, param_len=None, left_cnt=None): def create_from_list(
funcs: list,
left=None,
func_order: list[int] = None,
param_len: list[int] = None,
left_cnt: int = 0,
):
if func_order is None: if func_order is None:
func_order = [] func_order = []
@ -20,32 +26,50 @@ class ModelFactory:
if not func['active']: if not func['active']:
continue continue
func_order.append(func['cnt'])
param_len.append(len(func['func'].params))
if func['children']: if func['children']:
right, _, _ = ModelFactory.create_from_list(func['children'], left_cnt=func['pos'], f = func.copy()
func_order=func_order, param_len=param_len) f['children'] = []
right_cnt = None right, _, _, right_cnt = ModelFactory.create_from_list(
right = MultiModel(func['func'], right, func['children'][0]['op'], left_idx=func['cnt'], right_idx=None) [f] + func['children'],
left_cnt=func['pos'],
func_order=func_order,
param_len=param_len,
)
else: else:
right = func['func'] right = func['func']
right_cnt = func['cnt'] right_cnt = func['cnt']
func_order.append(func['cnt'])
param_len.append(len(func['func'].params))
if left is None: if left is None:
left = right left = right
left_cnt = right_cnt left_cnt = right_cnt
else: else:
left = MultiModel(left, right, func['op'], left = MultiModel(left, right, func['op'], left_idx=left_cnt, right_idx=right_cnt)
left_idx=left_cnt, right_idx=right_cnt)
return left, func_order, param_len return left, func_order, param_len, left_cnt
class MultiModel: class MultiModel:
op_repr = {operator.add: ' + ', operator.mul: ' * ', operator.sub: ' - ', operator.truediv: ' / '} op_repr = {
str_op = {'+': operator.add, '*': operator.mul, '-': operator.sub, '/': operator.truediv} operator.add: ' + ',
int_op = {0: operator.add, 1: operator.mul, 2: operator.sub, 3: operator.truediv} 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,
}
def __init__(self, def __init__(self,
left: Any, left: Any,
@ -69,6 +93,9 @@ class MultiModel:
if self._op is None: if self._op is None:
raise ValueError('Invalid binary operator.') raise ValueError('Invalid binary operator.')
if right_idx is None:
right_idx = left_idx + 1
self.name = '(' self.name = '('
self.params = [] self.params = []
self.bounds = [] self.bounds = []

View File

@ -6,10 +6,13 @@ from .model import Model
from .parameter import Parameters, Parameter from .parameter import Parameters, Parameter
class Data(object): class Data:
def __init__(self, x, y, we=None, idx=None): def __init__(self, x, y, we=None, idx=None, complex_type: int = 0):
self.x = np.asarray(x) self.x = np.asarray(x)
self.y = np.asarray(y) self.y = np.asarray(y)
if self.x.size == 0 or self.y.size == 0:
raise ValueError("Data is empty")
if self.y.shape[0] != self.x.shape[0]: if self.y.shape[0] != self.x.shape[0]:
raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}') raise ValueError(f'x and y have different lengths {self.x.shape[0]} and {self.y.shape[0]}')
@ -20,6 +23,7 @@ class Data(object):
self.parameter = Parameters() self.parameter = Parameters()
self.para_keys: list = [] self.para_keys: list = []
self.fun_kwargs = {} self.fun_kwargs = {}
self.complex_type = complex_type
def __len__(self): def __len__(self):
return self.y.shape[0] return self.y.shape[0]

View File

@ -361,7 +361,7 @@ class FitRoutine(object):
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0)) res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0))
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape) err, corr, partial_corr = _calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
self.make_results(data, res.x, var, data.para_keys, res.jac.shape, self.make_results(data, res.x, var, data.para_keys, res.jac.shape,
err=err, corr=corr, partial_corr=partial_corr) err=err, corr=corr, partial_corr=partial_corr)
@ -375,7 +375,7 @@ class FitRoutine(object):
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0)) res = optimize.least_squares(cost, p0, bounds=(lb, ub), max_nfev=500 * len(p0))
err, corr, partial_corr = self._calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape) err, corr, partial_corr = _calc_error(res.jac, np.sum(res.fun**2), *res.jac.shape)
for v, var_pars_k in zip(data, data_pars): for v, var_pars_k in zip(data, data_pars):
self.make_results(v, res.x, var, var_pars_k, res.jac.shape, self.make_results(v, res.x, var, var_pars_k, res.jac.shape,
err=err, corr=corr, partial_corr=partial_corr) err=err, corr=corr, partial_corr=partial_corr)
@ -458,9 +458,17 @@ class FitRoutine(object):
self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)), self.make_results(v, res.beta, var, var_pars_k, (sum(len(d) for d in data), len(p0)),
err=res.sd_beta, corr=corr, partial_corr=partial_corr) err=res.sd_beta, corr=corr, partial_corr=partial_corr)
def make_results(self, data, p, var_pars, used_pars, shape, def make_results(
err=None, corr=None, partial_corr=None): self,
data: Data,
p: list[float],
var_pars: list[str],
used_pars: list[str],
shape: tuple[int, int],
err: list[float] = None,
corr: np.ndarray = None,
partial_corr: np.ndarray = None,
):
if err is None: if err is None:
err = [0] * len(p) err = [0] * len(p)
@ -498,52 +506,54 @@ class FitRoutine(object):
model = data.get_model() model = data.get_model()
self.result[idx] = FitResultCreator.make_with_model( self.result[idx] = FitResultCreator.make_with_model(
model, model=model,
data.x, x_orig=data.x,
data.y, y_orig=data.y,
actual_parameters, p=actual_parameters,
data.fun_kwargs, fun_kwargs=data.fun_kwargs,
data.we_string, we=data.we_string,
data.idx, idx=data.idx,
*shape, nobs=shape[0],
nvar=shape[1],
corr=actual_corr, corr=actual_corr,
pcorr=actual_pcorr, pcorr=actual_pcorr,
data_mode=data.complex_type,
) )
return self.result return self.result
@staticmethod
def _calc_error(jac, chi, nobs, nvars):
# copy of scipy.curve_fit to calculate covariance
# noinspection PyTupleAssignmentBalance
try:
_, s, vt = la.svd(jac, full_matrices=False)
except ValueError as e:
# this may be issue #39: On entry to DGESSD parameter had an illegal value
# catch this exception and ignore error calculation
logger.error(f'Error calculation failed with {e.args}')
pcov = None
else:
threshold = EPS * max(jac.shape) * s[0]
s = s[s > threshold]
vt = vt[:s.size]
pcov = np.dot(vt.T / s**2, vt) * chi / (nobs - nvars)
if pcov is None: def _calc_error(jac, chi, nobs, nvars):
_err = np.zeros(nvars) # copy of scipy.curve_fit to calculate covariance
corr = np.zeros((nvars, nvars)) # noinspection PyTupleAssignmentBalance
else: try:
_err = np.sqrt(np.diag(pcov)) _, s, vt = la.svd(jac, full_matrices=False)
corr = pcov / (_err[:, None] * _err[None, :]) except ValueError as e:
# this may be issue #39: On entry to DGESSD parameter had an illegal value
# catch this exception and ignore error calculation
logger.error(f'Error calculation failed with {e.args}')
pcov = None
else:
threshold = EPS * max(jac.shape) * s[0]
s = s[s > threshold]
vt = vt[:s.size]
pcov = np.dot(vt.T / s**2, vt) * chi / (nobs - nvars)
corr = corr.astype(np.float64) if pcov is None:
try: _err = np.zeros(nvars)
corr_inv = np.linalg.inv(corr) corr = np.zeros((nvars, nvars))
corr_inv_diag = np.diag(np.sqrt(1 / np.diag(corr_inv))) else:
partial_corr = -1. * np.dot(np.dot(corr_inv_diag, corr_inv), corr_inv_diag) # Partial correlation matrix _err = np.sqrt(np.diag(pcov))
partial_corr[np.diag_indices_from(partial_corr)] = 1. corr = pcov / (_err[:, None] * _err[None, :])
except np.linalg.LinAlgError:
partial_corr = corr
return _err, corr, partial_corr corr = corr.astype(np.float64)
try:
corr_inv = np.linalg.inv(corr)
corr_inv_diag = np.diag(np.sqrt(1 / np.diag(corr_inv)))
partial_corr = -1. * np.dot(np.dot(corr_inv_diag, corr_inv), corr_inv_diag) # Partial correlation matrix
partial_corr[np.diag_indices_from(partial_corr)] = 1.
except np.linalg.LinAlgError:
partial_corr = corr
return _err, corr, partial_corr

View File

@ -9,7 +9,7 @@ from ._meta import MultiModel
from .parameter import Parameters, Parameter from .parameter import Parameters, Parameter
class Model(object): class Model:
def __init__(self, model, *args, **kwargs): def __init__(self, model, *args, **kwargs):
self.idx = kwargs.pop('idx', None) self.idx = kwargs.pop('idx', None)

View File

@ -11,6 +11,7 @@ from scipy.stats import f as fdist
from scipy.interpolate import interp1d from scipy.interpolate import interp1d
from ._meta import MultiModel from ._meta import MultiModel
from .model import Model
from .parameter import Parameter from .parameter import Parameter
from ..data.points import Points from ..data.points import Points
from ..data.signals import Signal from ..data.signals import Signal
@ -36,17 +37,30 @@ class FitResultCreator:
else: else:
resid = kwargs['y'] - y_orig resid = kwargs['y'] - y_orig
stats = FitResultCreator.calc_statistics(resid, _y) stats = calc_statistics(resid, _y)
return FitResult(kwargs['x'], kwargs['y'], x_orig, y_orig, params, dict(kwargs['choice']), resid, 0, 0, return FitResult(
kwargs['name'], stats, idx) x=kwargs['x'],
y=kwargs['y'],
x_data=x_orig,
y_data=y_orig,
params=params,
fun_kwargs=dict(kwargs['choice']),
resid=resid,
nobs=0,
nvar=0,
we='',
name=kwargs['name'],
stats=stats,
idx=idx,
)
@staticmethod @staticmethod
def make_with_model( def make_with_model(
model: 'Model', model: 'Model',
x_orig: np.ndarray, x_orig: np.ndarray,
y_orig: np.ndarray, y_orig: np.ndarray,
p: 'Parameters', p: list,
fun_kwargs: dict, fun_kwargs: dict,
we: str, we: str,
idx: str | None, idx: str | None,
@ -54,6 +68,7 @@ class FitResultCreator:
nvar: int, nvar: int,
corr: np.ndarray, corr: np.ndarray,
pcorr: np.ndarray, pcorr: np.ndarray,
data_mode: int,
) -> FitResult: ) -> FitResult:
if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)): if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)):
islog = True islog = True
@ -83,17 +98,11 @@ class FitResultCreator:
actual_mode = fun_kwargs['complex_mode'] actual_mode = fun_kwargs['complex_mode']
fun_kwargs['complex_mode'] = 0 fun_kwargs['complex_mode'] = 0
_y = model.func(p_final, _x, **fun_kwargs) _y = check_complex(model.func(p_final, _x, **fun_kwargs), actual_mode, data_mode)
if not actual_mode < 0: fun_kwargs['complex_mode'] = actual_mode
if actual_mode == 1:
_y.imag = 0
elif actual_mode == 2:
_y.real = 0
fun_kwargs['complex_mode'] = actual_mode stats = calc_statistics(_y, resid, nobs, nvar)
stats = FitResultCreator.calc_statistics(_y, resid, nobs, nvar)
varied = [p.var for p in parameters.values()] varied = [p.var for p in parameters.values()]
if corr is None: if corr is None:
@ -134,38 +143,9 @@ class FitResultCreator:
pcorr=partial_correlation, pcorr=partial_correlation,
islog=islog, islog=islog,
func=model, func=model,
data_complex=data_mode,
) )
@staticmethod
def calc_statistics(y, residual, nobs=None, nvar=None):
chi = (residual**2).sum()
try:
r = 1 - chi/((y-np.mean(y))**2).sum()
except RuntimeWarning:
r = -9999
if nobs is None:
nobs = 1
if nvar is None:
nvar = 0
dof = nobs - nvar
loglikehood = nobs * np.log(chi / nobs)
stats = {
'chi^2': chi,
'R^2': r,
'AIC': loglikehood + 2 * nvar,
'BIC': loglikehood + np.log(nobs) * nvar,
'adj. R^2': 1 - (nobs-1) / (dof+1e-13) * (1-r),
'red. chi^2': chi / (dof + 1e-13),
}
stats['AICc'] = stats['AIC'] + 2*(nvar+1)*nvar / (dof - 1 + 1e-13)
return stats
class FitResult(Points): class FitResult(Points):
@ -188,7 +168,8 @@ class FitResult(Points):
pcorr: np.ndarray = None, pcorr: np.ndarray = None,
islog: bool = False, islog: bool = False,
func=None, func=None,
**kwargs data_complex: int = 1,
**kwargs,
): ):
self.parameter, name = self._prepare_names(params, name) self.parameter, name = self._prepare_names(params, name)
@ -210,6 +191,7 @@ class FitResult(Points):
self.y_data = y_data self.y_data = y_data
self._model_name = name self._model_name = name
self._func = func self._func = func
self._data_complex = data_complex
@staticmethod @staticmethod
def _prepare_names(parameter: dict, modelname: str): def _prepare_names(parameter: dict, modelname: str):
@ -418,20 +400,12 @@ class FitResult(Points):
if self.func is None: if self.func is None:
raise ValueError('no fit function available to calculate new y values') raise ValueError('no fit function available to calculate new y values')
actual_mode = -1
if 'complex_mode' in self.fun_kwargs:
actual_mode = self.fun_kwargs['complex_mode']
self.fun_kwargs['complex_mode'] = 0
new_fit = self.copy() new_fit = self.copy()
y_values = self.func.func(self.p_final, x_values, **self.fun_kwargs) fun_kwargs = {k: v for k, v in self.fun_kwargs.items()}
if not actual_mode < 0: if self.fun_kwargs.get('complex_mode', -1) == -1:
if actual_mode == 1: fun_kwargs.pop('complex_mode', None)
y_values.imag = 0 y_values = self.func.func(self.p_final, x_values, **fun_kwargs)
elif actual_mode == 2: y_values = check_complex(y_values, self.fun_kwargs.get('complex_mode', -1), self._data_complex)
y_values.real = 0
self.fun_kwargs['complex_mode'] = actual_mode
new_fit.set_data(x_values, y_values, y_err=0.0) new_fit.set_data(x_values, y_values, y_err=0.0)
@ -442,20 +416,16 @@ class FitResult(Points):
raise ValueError('no fit function available to calculate new y values') raise ValueError('no fit function available to calculate new y values')
part_functions = [] part_functions = []
actual_mode = -1 actual_mode = self.fun_kwargs.get('complex_mode', -1)
if 'complex_mode' in self.fun_kwargs: fun_kwargs = {k: v for k, v in self.fun_kwargs.items()}
actual_mode = self.fun_kwargs['complex_mode'] if self.fun_kwargs.get('complex_mode', -1) == -1:
self.fun_kwargs['complex_mode'] = 0 fun_kwargs.pop('complex_mode', None)
for sub_name, sub_y in zip(self.func.sub_name(), self.func.sub(self.p_final, x_values, **self.fun_kwargs)): for sub_name, sub_y in zip(self.func.sub_name(), self.func.sub(self.p_final, x_values, **self.fun_kwargs)):
if not actual_mode < 0: sub_y = check_complex(sub_y, actual_mode, self._data_complex)
if actual_mode == 1:
sub_y.imag = 0
elif actual_mode == 2:
sub_y.real = 0
if np.iscomplexobj(sub_y):
part_functions.append(Signal(x_values, sub_y, name=sub_name)) part_functions.append(Signal(x_values, sub_y, name=sub_name))
else: else:
part_functions.append(Points(x_values, sub_y, name=sub_name)) part_functions.append(Points(x_values, sub_y, name=sub_name))
@ -463,3 +433,49 @@ class FitResult(Points):
self.fun_kwargs['complex_mode'] = actual_mode self.fun_kwargs['complex_mode'] = actual_mode
return part_functions return part_functions
def check_complex(y, model_complex, data_complex):
if not np.iscomplexobj(y):
return y
if model_complex == 1:
y.imag = 0
if data_complex == 1:
y = y.real
elif model_complex == 2:
y.real = 0
if data_complex == 1:
y = y.imag
return y
def calc_statistics(y, residual, nobs=None, nvar=None):
chi = (residual**2).sum()
try:
r = 1 - chi/((y-np.mean(y))**2).sum()
except RuntimeWarning:
r = -9999
if nobs is None:
nobs = 1
if nvar is None:
nvar = 0
dof = nobs - nvar
loglikehood = nobs * np.log(chi / nobs)
stats = {
'chi^2': chi,
'R^2': r,
'AIC': loglikehood + 2 * nvar,
'BIC': loglikehood + np.log(nobs) * nvar,
'adj. R^2': 1 - (nobs-1) / (dof+1e-13) * (1-r),
'red. chi^2': chi / (dof + 1e-13),
}
stats['AICc'] = stats['AIC'] + 2*(nvar+1)*nvar / (dof - 1 + 1e-13)
return stats

View File

@ -49,7 +49,7 @@ 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.strip('\n\t\r, ') line = line.strip('\n\t\r, ')
line = re.sub(r'[\t ;,] *', ';', line) line = re.sub(r'[\t ;,]+', ';', line)
line = line.split(';') line = line.split(';')
try: try:
@ -183,7 +183,13 @@ class AsciiReader:
single_len = 2 single_len = 2
stepsize = 2 stepsize = 2
cls = {'points': Points, 'fid': FID, 'spectrum': Spectrum, 'bds': BDS, 'dsc': DSC}[mode] cls = {
'points': Points,
'fid': FID,
'spectrum': Spectrum,
'bds': BDS,
'dsc': DSC,
}[mode]
for j in range(1, num_y+1, stepsize): for j in range(1, num_y+1, stepsize):
if col_names is not None: if col_names is not None:
@ -191,7 +197,7 @@ class AsciiReader:
kwargs['name'] = col_names[j-1] kwargs['name'] = col_names[j-1]
elif num_y > single_len: elif num_y > single_len:
# more than one axis, append column number # more than one axis, append column number
kwargs['name'] = filename + '_' + str(y[j-1]) kwargs['name'] = f'{filename}_{y[j-1]+1}'
if j+num_y < raw_data.shape[2]: if j+num_y < raw_data.shape[2]:
kwargs['y_err'] = raw_data[i, :, j+num_y] kwargs['y_err'] = raw_data[i, :, j+num_y]

View File

@ -11,7 +11,8 @@ try:
from scipy.integrate import simpson from scipy.integrate import simpson
except ImportError: except ImportError:
from scipy.integrate import simps as simpson from scipy.integrate import simps as simpson
from scipy.interpolate import interp1d from scipy.interpolate import CubicSpline
ReferenceValue = namedtuple('Reference', ['name', 'transitions']) ReferenceValue = namedtuple('Reference', ['name', 'transitions'])
Cyclohexane = ReferenceValue('Cyclohexane', [(-87.06+273.15, 79.58), (6.54+273.15, None)]) Cyclohexane = ReferenceValue('Cyclohexane', [(-87.06+273.15, 79.58), (6.54+273.15, None)])
@ -38,7 +39,7 @@ class DSCSample:
def read_file(self, fname: str | Path) -> None: def read_file(self, fname: str | Path) -> None:
fname = Path(fname) fname = Path(fname)
# file contains weird deg C character in stupiod ISO encoding # file contains weird deg C character in stupid ISO encoding
with fname.open('r', encoding='iso-8859-15') as f: with fname.open('r', encoding='iso-8859-15') as f:
ii = 1 ii = 1
for line in f: for line in f:
@ -144,9 +145,12 @@ class DSCCalibrator:
self.reference = [] self.reference = []
self.ref_list = [] self.ref_list = []
def set_measurement(self, def set_measurement(
fname: str | Path | DSCSample, mode: str = 'sample', self: DSCCalibrator,
reference: ReferenceValue = Cyclohexane): fname: str | Path | DSCSample,
mode: str = 'sample',
reference: ReferenceValue = Cyclohexane
):
if mode not in ['sample', 'empty', 'reference']: if mode not in ['sample', 'empty', 'reference']:
raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"') raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"')
if mode == 'reference' and not isinstance(reference, ReferenceValue): if mode == 'reference' and not isinstance(reference, ReferenceValue):
@ -266,7 +270,12 @@ class DSCCalibrator:
return sol return sol
def get_data(self, idx: int, slope: str = 'iso', limits: tuple[float, float] = None): def get_data(
self: DSCCalibrator,
idx: int,
slope: str = 'iso',
limits: tuple[float, float] = None
) -> tuple[np.ndarray, np.ndarray, np.ndarray,np.ndarray | None, np.ndarray]:
if self.sample.steps[idx][0] == 'i': if self.sample.steps[idx][0] == 'i':
raise ValueError('baseline correction is not implemented for isotherms') raise ValueError('baseline correction is not implemented for isotherms')
@ -292,7 +301,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[2, 0], empty_data[1], fill_value='extrapolate')(sample_data[2, 0]) empty_y = CubicSpline(empty_data[2]-empty_data[2, 0], empty_data[1], extrapolate=True)(sample_data[2] - 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), :]
@ -346,9 +355,10 @@ class DSCCalibrator:
offset = region[0, 0] offset = region[0, 0]
sample_data[1] -= m * (sample_data[2] - region[1, 0]) + offset sample_data[1] -= m * (sample_data[2] - region[1, 0]) + offset
line = np.array([[sample_data[2, 0], sample_data[2, -1]], line = np.array([
[m * (sample_data[2, 0] - region[1, 0]) + offset, [sample_data[2, 0], sample_data[2, -1]],
m * (sample_data[2, -1] - region[1, 0]) + offset]]) [m * (sample_data[2, 0] - region[1, 0]) + offset, m * (sample_data[2, -1] - region[1, 0]) + offset]
])
else: else:
line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]]) line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]])

View File

@ -3,14 +3,21 @@ import numpy.polynomial.polynomial as poly
from scipy import signal as signal from scipy import signal as signal
__all__ = ['smooth', 'loess', 'savgol', __all__ = [
'running_max', 'running_min', 'smooth',
'running_var', 'running_std', 'loess',
'running_median', 'running_mean', 'savgol',
'running_sum'] 'running_max',
'running_min',
'running_var',
'running_std',
'running_median',
'running_mean',
'running_sum',
]
def loess(x, y, window_size, it=0, deg=2): def loess(x, y, window_size: int, it: int = 0, deg: int = 2):
# ULTRA LANGSAM !!! # ULTRA LANGSAM !!!
it = max(it, 0) it = max(it, 0)
@ -81,19 +88,19 @@ def savgol(x, y, window_size: int, deg: int = 2, mode: str = 'mirror'):
return new_y return new_y
def running_mean(x, y, window_size): def running_mean(x, y, window_size: int):
return _running_func(np.nanmean, x, y, window_size) return _running_func(np.nanmean, x, y, window_size)
def running_median(x, y, window_size): def running_median(x, y, window_size: int):
return _running_func(np.nanmedian, x, y, window_size) return _running_func(np.nanmedian, x, y, window_size)
def running_std(x, y, window_size): def running_std(x, y, window_size: int):
return _running_func(np.nanstd, x, y, window_size) return _running_func(np.nanstd, x, y, window_size)
def running_var(x, y, window_size): def running_var(x, y, window_size: int):
return _running_func(np.nanvar, x, y, window_size) return _running_func(np.nanvar, x, y, window_size)
@ -132,11 +139,27 @@ def _moving_window(arr, nn):
return np.lib.stride_tricks.as_strided(arr, shapes, strides) return np.lib.stride_tricks.as_strided(arr, shapes, strides)
_funcs = {'loess': loess, 'savgol': savgol, 'mean': running_mean, 'median': running_median, _funcs = {
'std': running_std, 'var': running_var, 'max': running_max, 'min': running_min, 'sum': running_sum} 'loess': loess,
'savgol': savgol,
'mean': running_mean,
'median': running_median,
'std': running_std,
'var': running_var,
'max': running_max,
'min': running_min,
'sum': running_sum,
}
def smooth(data, window_size, mode='mean', logx=False, logy=False, **kwargs): def smooth(
data: 'Data',
window_size: int,
mode: str = 'mean',
logx: bool = False,
logy: bool = False,
**kwargs
):
try: try:
func = _funcs[mode] func = _funcs[mode]
except KeyError: except KeyError:
@ -162,6 +185,6 @@ def smooth(data, window_size, mode='mean', logx=False, logy=False, **kwargs):
new_y = 10**new_y new_y = 10**new_y
new_data = data.copy() new_data = data.copy()
new_data.set_data(x=new_x, y=new_y, y_err=None) new_data.set_data(x=new_x, y=new_y, y_err=np.zeros_like(new_y))
return new_data return new_data

View File

@ -191,6 +191,18 @@ class PowerLawCross:
return ret_val return ret_val
class Sinc:
type = 'Basic'
name = 'Sinc'
equation = 'C * sinc((x-x_{0})/w)'
params = ['C', 'x_{0}', 'w']
@staticmethod
def func(x, c: float, x0: float, w: float):
# numpy sinc is defined as sin(pi*x)/(pi*x)
return c * np.sinc(((x-x0)/w)/np.pi)
class Sine: class Sine:
""" """
Wavy sine function Wavy sine function

View File

@ -172,6 +172,14 @@
<property name="title"> <property name="title">
<string>&amp;Data</string> <string>&amp;Data</string>
</property> </property>
<widget class="QMenu" name="menuCut_to_visible_range">
<property name="title">
<string>Cut to visible range</string>
</property>
<addaction name="separator"/>
<addaction name="action_cut_xaxis"/>
<addaction name="action_cut_yaxis"/>
</widget>
<addaction name="action_new_set"/> <addaction name="action_new_set"/>
<addaction name="action_delete_sets"/> <addaction name="action_delete_sets"/>
<addaction name="actionMove_between_plots"/> <addaction name="actionMove_between_plots"/>
@ -181,7 +189,7 @@
<addaction name="action_sort_pts"/> <addaction name="action_sort_pts"/>
<addaction name="actionSkip_points"/> <addaction name="actionSkip_points"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_cut"/> <addaction name="menuCut_to_visible_range"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionChange_datatypes"/> <addaction name="actionChange_datatypes"/>
</widget> </widget>
@ -247,6 +255,7 @@
<addaction name="action_no_range"/> <addaction name="action_no_range"/>
<addaction name="action_x_range"/> <addaction name="action_x_range"/>
<addaction name="action_custom_range"/> <addaction name="action_custom_range"/>
<addaction name="actionExclude_region"/>
</widget> </widget>
<addaction name="action_FitWidget"/> <addaction name="action_FitWidget"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -437,30 +446,6 @@
<addaction name="t1action"/> <addaction name="t1action"/>
<addaction name="actionCalculateT1"/> <addaction name="actionCalculateT1"/>
</widget> </widget>
<widget class="QToolBar" name="toolBar_fit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Fit</string>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="action_FitWidget"/>
</widget>
<widget class="QToolBar" name="toolBar_spectrum"> <widget class="QToolBar" name="toolBar_spectrum">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -885,11 +870,6 @@
<string>Integration...</string> <string>Integration...</string>
</property> </property>
</action> </action>
<action name="action_cut">
<property name="text">
<string>Cut to visible range</string>
</property>
</action>
<action name="actionMove_between_plots"> <action name="actionMove_between_plots">
<property name="text"> <property name="text">
<string>Move sets...</string> <string>Move sets...</string>
@ -1045,6 +1025,30 @@
<string>TNMH...</string> <string>TNMH...</string>
</property> </property>
</action> </action>
<action name="actionExclude_region">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Exclude region</string>
</property>
</action>
<action name="action_cut_xaxis">
<property name="text">
<string>x axis</string>
</property>
<property name="toolTip">
<string>Remove data points outside visible x range.</string>
</property>
</action>
<action name="action_cut_yaxis">
<property name="text">
<string>y axis</string>
</property>
<property name="toolTip">
<string>Remove data points outside visible y range. Uses real part of points.</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -96,11 +96,14 @@
<item> <item>
<widget class="QGroupBox" name="region_box"> <widget class="QGroupBox" name="region_box">
<property name="title"> <property name="title">
<string>Evaluate region (empty values default to start/end)</string> <string>Evaluate region (empty values default to values of the script)</string>
</property> </property>
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
@ -311,7 +314,11 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QComboBox" name="graph_comboBox"/> <widget class="QComboBox" name="graph_comboBox">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="graph_checkbox"> <widget class="QCheckBox" name="graph_checkbox">

View File

@ -67,6 +67,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Initial values</string> <string>Initial values</string>
</property> </property>
@ -92,6 +98,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Use global</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -151,6 +164,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lower bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Lower bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
@ -195,6 +214,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Upper bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Upper bound. Same bound is used for all data. Leave empty for no boundary condition.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>

View File

@ -354,59 +354,26 @@
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
</property> </property>
<item row="1" column="0"> <item row="1" column="7">
<widget class="QCheckBox" name="extrapolate_box">
<property name="toolTip">
<string>Extrapolates only main function</string>
</property>
<property name="text">
<string>Extrapolate curves</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QCheckBox" name="parameter_checkbox">
<property name="text">
<string>Plot parameter</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QComboBox" name="graph_comboBox"> <widget class="QComboBox" name="graph_comboBox">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="5">
<widget class="QCheckBox" name="graph_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>New graph for parameter</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="minx_line"> <widget class="QLineEdit" name="minx_line">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@ -419,10 +386,45 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="4" rowspan="2"> <item row="1" column="0">
<widget class="Line" name="line_2"> <widget class="QCheckBox" name="extrapolate_box">
<property name="orientation"> <property name="toolTip">
<enum>Qt::Vertical</enum> <string>Extrapolates only main function</string>
</property>
<property name="text">
<string>Extrapolate curves</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="numx_line">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="placeholderText">
<string># pts</string>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QCheckBox" name="graph_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>New graph for parameter</string>
</property>
<property name="checked">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -432,7 +434,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@ -445,20 +447,33 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="3"> <item row="0" column="5" rowspan="2">
<widget class="QLineEdit" name="numx_line"> <widget class="Line" name="line_2">
<property name="enabled"> <property name="orientation">
<bool>false</bool> <enum>Qt::Vertical</enum>
</property>
<property name="placeholderText">
<string># pts</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="4"> <item row="1" column="4">
<widget class="QCheckBox" name="newx_log_checkbox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>log-spaced?</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QCheckBox" name="curve_checkbox"> <widget class="QCheckBox" name="curve_checkbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Plot fit curve</string> <string>Plot fit curve</string>
</property> </property>
@ -469,6 +484,12 @@
</item> </item>
<item> <item>
<widget class="QCheckBox" name="partial_checkBox"> <widget class="QCheckBox" name="partial_checkBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Plot partial functions</string> <string>Plot partial functions</string>
</property> </property>
@ -476,6 +497,19 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="0" column="6" colspan="2">
<widget class="QCheckBox" name="parameter_checkbox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Plot parameter</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -119,7 +119,7 @@
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -300,38 +300,5 @@
<tabstop>dest_combobox</tabstop> <tabstop>dest_combobox</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections> <connections/>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>251</x>
<y>490</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>319</x>
<y>490</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -17,7 +17,20 @@
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
</property> </property>
<item row="1" column="0"> <item row="7" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="frac_label"> <widget class="QLabel" name="frac_label">
<property name="text"> <property name="text">
<string>Window length</string> <string>Window length</string>
@ -27,68 +40,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Iterations</string>
</property>
<property name="buddy">
<cstring>iter_spinBox</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="iter_spinBox">
<property name="maximum">
<number>3</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="2"> <item row="3" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QWidget" name="widget" native="true"> <widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing"> <property name="spacing">
@ -132,7 +91,65 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="6" column="1">
<widget class="QCheckBox" name="y_checkBox">
<property name="text">
<string>y log-spaced?</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="x_checkBox">
<property name="text">
<string>x log-spaced?</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Iterations</string>
</property>
<property name="buddy">
<cstring>iter_spinBox</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="iter_spinBox">
<property name="maximum">
<number>3</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="frac_spinBox"> <widget class="QSpinBox" name="frac_spinBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of data points used as smoothing window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of data points used as smoothing window.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -145,7 +162,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="2"> <item row="8" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QComboBox" name="comboBox"> <widget class="QComboBox" name="comboBox">
<item> <item>
<property name="text"> <property name="text">
@ -194,30 +221,13 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="0" column="0" colspan="2">
<spacer name="verticalSpacer"> <widget class="QLabel" name="label_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="y_checkBox">
<property name="text"> <property name="text">
<string>y log-spaced?</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; Sets must be sorted for correct results&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> <property name="margin">
</item> <number>3</number>
<item row="5" column="0">
<widget class="QCheckBox" name="x_checkBox">
<property name="text">
<string>x log-spaced?</string>
</property> </property>
</widget> </widget>
</item> </item>