use tree colors in fit result for sub-funcs #253
| @@ -17,7 +17,7 @@ body: | ||||
|       description: For which version have you observed this behavior? | ||||
|       placeholder: You find the program version in "Help/About" | ||||
|     validations: | ||||
|       required: true | ||||
|       required: false | ||||
|   - type: textarea | ||||
|     attributes: | ||||
|       label: Expected behavior | ||||
|   | ||||
| @@ -365,6 +365,9 @@ class Ui_BaseWindow(object): | ||||
|         self.actionBinning.setObjectName("actionBinning") | ||||
|         self.actionTNMH = QtWidgets.QAction(BaseWindow) | ||||
|         self.actionTNMH.setObjectName("actionTNMH") | ||||
|         self.actionExclude_region = QtWidgets.QAction(BaseWindow) | ||||
|         self.actionExclude_region.setCheckable(True) | ||||
|         self.actionExclude_region.setObjectName("actionExclude_region") | ||||
|         self.menuSave.addAction(self.actionSave) | ||||
|         self.menuSave.addAction(self.actionExportGraphic) | ||||
|         self.menuSave.addAction(self.action_save_fit_parameter) | ||||
| @@ -419,6 +422,7 @@ class Ui_BaseWindow(object): | ||||
|         self.menuLimits.addAction(self.action_no_range) | ||||
|         self.menuLimits.addAction(self.action_x_range) | ||||
|         self.menuLimits.addAction(self.action_custom_range) | ||||
|         self.menuLimits.addAction(self.actionExclude_region) | ||||
|         self.menuFit.addAction(self.action_FitWidget) | ||||
|         self.menuFit.addSeparator() | ||||
|         self.menuFit.addAction(self.action_create_fit_function) | ||||
| @@ -631,6 +635,7 @@ class Ui_BaseWindow(object): | ||||
|         self.actionTNMH_model.setText(_translate("BaseWindow", "Tg , Hodge, TNMH,,,")) | ||||
|         self.actionBinning.setText(_translate("BaseWindow", "Binning...")) | ||||
|         self.actionTNMH.setText(_translate("BaseWindow", "TNMH...")) | ||||
|         self.actionExclude_region.setText(_translate("BaseWindow", "Exclude region")) | ||||
| from ..data.datawidget.datawidget import DataWidget | ||||
| from ..data.integral_widget import IntegralWidget | ||||
| from ..data.point_select import PointSelectWidget | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # -*- 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 | ||||
| # | ||||
| @@ -157,67 +157,91 @@ class Ui_Dialog(object): | ||||
|         self.gridLayout_2.setContentsMargins(3, 3, 3, 3) | ||||
|         self.gridLayout_2.setSpacing(3) | ||||
|         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.setEnabled(False) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.graph_comboBox.sizePolicy().hasHeightForWidth()) | ||||
|         self.graph_comboBox.setSizePolicy(sizePolicy) | ||||
|         self.graph_comboBox.setObjectName("graph_comboBox") | ||||
|         self.gridLayout_2.addWidget(self.graph_comboBox, 1, 6, 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.gridLayout_2.addWidget(self.graph_comboBox, 1, 7, 1, 1) | ||||
|         self.minx_line = QtWidgets.QLineEdit(self.groupBox) | ||||
|         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.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.minx_line.sizePolicy().hasHeightForWidth()) | ||||
|         self.minx_line.setSizePolicy(sizePolicy) | ||||
|         self.minx_line.setObjectName("minx_line") | ||||
|         self.gridLayout_2.addWidget(self.minx_line, 1, 1, 1, 1) | ||||
|         self.line_2 = QtWidgets.QFrame(self.groupBox) | ||||
|         self.line_2.setFrameShape(QtWidgets.QFrame.VLine) | ||||
|         self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) | ||||
|         self.line_2.setObjectName("line_2") | ||||
|         self.gridLayout_2.addWidget(self.line_2, 0, 4, 2, 1) | ||||
|         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.numx_line = QtWidgets.QLineEdit(self.groupBox) | ||||
|         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.setEnabled(False) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) | ||||
|         sizePolicy.setHorizontalStretch(0) | ||||
|         sizePolicy.setVerticalStretch(0) | ||||
|         sizePolicy.setHeightForWidth(self.maxx_line.sizePolicy().hasHeightForWidth()) | ||||
|         self.maxx_line.setSizePolicy(sizePolicy) | ||||
|         self.maxx_line.setObjectName("maxx_line") | ||||
|         self.gridLayout_2.addWidget(self.maxx_line, 1, 2, 1, 1) | ||||
|         self.numx_line = QtWidgets.QLineEdit(self.groupBox) | ||||
|         self.numx_line.setEnabled(False) | ||||
|         self.numx_line.setObjectName("numx_line") | ||||
|         self.gridLayout_2.addWidget(self.numx_line, 1, 3, 1, 1) | ||||
|         self.line_2 = QtWidgets.QFrame(self.groupBox) | ||||
|         self.line_2.setFrameShape(QtWidgets.QFrame.VLine) | ||||
|         self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) | ||||
|         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.setObjectName("horizontalLayout") | ||||
|         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.setObjectName("curve_checkbox") | ||||
|         self.horizontalLayout.addWidget(self.curve_checkbox) | ||||
|         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.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.buttonBox = QtWidgets.QDialogButtonBox(Dialog) | ||||
|         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.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit")) | ||||
|         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.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.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.partial_checkBox.setText(_translate("Dialog", "Plot partial functions")) | ||||
|         self.parameter_checkbox.setText(_translate("Dialog", "Plot parameter")) | ||||
| from ..lib.forms import ElideComboBox | ||||
| from pyqtgraph import GraphicsLayoutWidget | ||||
|   | ||||
| @@ -49,11 +49,16 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|  | ||||
|     def add_graph(self, idd: str, name: str): | ||||
|         item = QtWidgets.QTreeWidgetItem() | ||||
|         item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEditable | | ||||
|                       QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) | ||||
|         item.setFlags( | ||||
|             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.setData(0, QtCore.Qt.UserRole, idd) | ||||
|         item.setCheckState(0, QtCore.Qt.Checked) | ||||
|         item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd) | ||||
|         item.setCheckState(0, QtCore.Qt.CheckState.Checked) | ||||
|  | ||||
|         self.addTopLevelItem(item) | ||||
|         self._checked_graphs.add(idd) | ||||
| @@ -67,14 +72,19 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|  | ||||
|         for row in range(self.invisibleRootItem().childCount()): | ||||
|             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: | ||||
|                     item = QtWidgets.QTreeWidgetItem([name]) | ||||
|                     item.setToolTip(0, f'Value: {value}') | ||||
|                     item.setData(0, QtCore.Qt.UserRole, idd) | ||||
|                     item.setCheckState(0, QtCore.Qt.Checked) | ||||
|                     item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsEditable | | ||||
|                                   QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable) | ||||
|                     item.setData(0, QtCore.Qt.ItemDataRole.UserRole, idd) | ||||
|                     item.setCheckState(0, QtCore.Qt.CheckState.Checked) | ||||
|                     item.setFlags( | ||||
|                         QtCore.Qt.ItemFlag.ItemIsSelectable | | ||||
|                         QtCore.Qt.ItemFlag.ItemIsDragEnabled | | ||||
|                         QtCore.Qt.ItemFlag.ItemIsEditable | | ||||
|                         QtCore.Qt.ItemFlag.ItemIsEnabled | | ||||
|                         QtCore.Qt.ItemFlag.ItemIsUserCheckable | ||||
|                     ) | ||||
|                     graph.addChild(item) | ||||
|                     self._checked_sets.add(idd) | ||||
|  | ||||
| @@ -85,8 +95,8 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|  | ||||
|     @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) | ||||
|     def data_change(self, item: QtWidgets.QTreeWidgetItem, emit: bool = True) -> tuple[set, set]: | ||||
|         idd = item.data(0, QtCore.Qt.UserRole) | ||||
|         is_selected = item.checkState(0) == QtCore.Qt.Checked | ||||
|         idd = item.data(0, QtCore.Qt.ItemDataRole.UserRole) | ||||
|         is_selected = item.checkState(0) == QtCore.Qt.CheckState.Checked | ||||
|         to_be_hidden = set() | ||||
|         to_be_shown = set() | ||||
|  | ||||
| @@ -104,9 +114,9 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|                     self.blockSignals(True) | ||||
|                     for i in range(item.childCount()): | ||||
|                         child = item.child(i) | ||||
|                         child.setCheckState(0, QtCore.Qt.Checked) | ||||
|                         to_be_shown.add(child.data(0, QtCore.Qt.UserRole)) | ||||
|                         self._checked_sets.add(child.data(0, QtCore.Qt.UserRole)) | ||||
|                         child.setCheckState(0, QtCore.Qt.CheckState.Checked) | ||||
|                         to_be_shown.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole)) | ||||
|                         self._checked_sets.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole)) | ||||
|                     self.blockSignals(False) | ||||
|  | ||||
|                 # check state change to unchecked | ||||
| @@ -115,10 +125,10 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|                     self.blockSignals(True) | ||||
|                     for i in range(item.childCount()): | ||||
|                         child = item.child(i) | ||||
|                         child.setCheckState(0, QtCore.Qt.Unchecked) | ||||
|                         to_be_hidden.add(child.data(0, QtCore.Qt.UserRole)) | ||||
|                         child.setCheckState(0, QtCore.Qt.CheckState.Unchecked) | ||||
|                         to_be_hidden.add(child.data(0, QtCore.Qt.ItemDataRole.UserRole)) | ||||
|                         try: | ||||
|                             self._checked_sets.remove(child.data(0, QtCore.Qt.UserRole)) | ||||
|                             self._checked_sets.remove(child.data(0, QtCore.Qt.ItemDataRole.UserRole)) | ||||
|                         except KeyError: | ||||
|                             pass | ||||
|                     self.blockSignals(False) | ||||
| @@ -153,7 +163,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|     @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) | ||||
|     def new_selection(self, item: QtWidgets.QTreeWidgetItem): | ||||
|         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): | ||||
|         dropped_index = self.indexAt(evt.pos()) | ||||
| @@ -179,7 +189,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|  | ||||
|             from_parent.removeChild(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) | ||||
|         if self.dropIndicatorPosition() == QtWidgets.QAbstractItemView.BelowItem: | ||||
| @@ -191,8 +201,8 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         else: | ||||
|             to_parent.insertChildren(row, tobemoved) | ||||
|  | ||||
|         self.management.move_sets([it.data(0, QtCore.Qt.UserRole) for it in tobemoved], | ||||
|                                   to_parent.data(0, QtCore.Qt.UserRole), take_from, | ||||
|         self.management.move_sets([it.data(0, QtCore.Qt.ItemDataRole.UserRole) for it in tobemoved], | ||||
|                                   to_parent.data(0, QtCore.Qt.ItemDataRole.UserRole), take_from, | ||||
|                                   pos=-1 if append else row) | ||||
|  | ||||
|         self.update_indexes() | ||||
| @@ -207,7 +217,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         while iterator.value(): | ||||
|             item = iterator.value() | ||||
|             if item is not None: | ||||
|                 data = item.data(0, QtCore.Qt.UserRole) | ||||
|                 data = item.data(0, QtCore.Qt.ItemDataRole.UserRole) | ||||
|                 if data == gid_out: | ||||
|                     from_parent = item | ||||
|  | ||||
| @@ -231,7 +241,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         self.blockSignals(False) | ||||
|  | ||||
|     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 = [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 c in children: | ||||
|                 if c.data(0, QtCore.Qt.UserRole) == s: | ||||
|                 if c.data(0, QtCore.Qt.ItemDataRole.UserRole) == s: | ||||
|                     graph_item.addChild(c) | ||||
|  | ||||
|         self.update_indexes() | ||||
| @@ -276,7 +286,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         while iterator.value(): | ||||
|             item = iterator.value() | ||||
|             if item is not None: | ||||
|                 data = item.data(0, QtCore.Qt.UserRole) | ||||
|                 data = item.data(0, QtCore.Qt.ItemDataRole.UserRole) | ||||
|                 if data == sid: | ||||
|                     if name != item.text(0): | ||||
|                         item.setText(0, name) | ||||
| @@ -285,7 +295,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|                 iterator += 1 | ||||
|  | ||||
|     def keyPressEvent(self, evt: QtGui.QKeyEvent): | ||||
|         if evt.key() == QtCore.Qt.Key_Delete: | ||||
|         if evt.key() == QtCore.Qt.Key.Key_Delete: | ||||
|             rm_sets = [] | ||||
|             rm_graphs = [] | ||||
|             for idx in self.selectedIndexes(): | ||||
| @@ -296,20 +306,20 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|                 if item.parent() is None: | ||||
|                     for c_i in range(item.childCount()): | ||||
|                         # 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: | ||||
|                             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: | ||||
|                     item_data = item.data(0, QtCore.Qt.UserRole) | ||||
|                     item_data = item.data(0, QtCore.Qt.ItemDataRole.UserRole) | ||||
|                     if item_data not in rm_sets: | ||||
|                         rm_sets.append(item_data) | ||||
|  | ||||
|             # self.deleteItem.emit(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 = [] | ||||
|             from_parent = [] | ||||
|  | ||||
| @@ -329,7 +339,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|             for it in sets: | ||||
|                 if it in from_parent: | ||||
|                     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) | ||||
|                 to_be_hidden |= s2 | ||||
|                 to_be_shown |= s1 | ||||
| @@ -353,7 +363,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         # find all items that have to be removed | ||||
|         while iterator.value(): | ||||
|             item = iterator.value() | ||||
|             _id = item.data(0, QtCore.Qt.UserRole) | ||||
|             _id = item.data(0, QtCore.Qt.ItemDataRole.UserRole) | ||||
|             if _id in ids: | ||||
|                 try: | ||||
|                     item_parent = item.parent() | ||||
| @@ -431,7 +441,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|             if i.column() == 0: | ||||
|                 continue | ||||
|             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: | ||||
|             for gid in graphs: | ||||
| @@ -473,12 +483,12 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|                 continue | ||||
|  | ||||
|             else: | ||||
|                 graph_id = parent.data(0, QtCore.Qt.UserRole) | ||||
|                 graph_id = parent.data(0, QtCore.Qt.ItemDataRole.UserRole) | ||||
|                 if graph_id not in idx: | ||||
|                     idx[graph_id] = [] | ||||
|                 # collect sets in their graph | ||||
|                 idx[graph_id].append(item.data(0, QtCore.Qt.UserRole)) | ||||
|                 data = self.management[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.ItemDataRole.UserRole)] | ||||
|                 if data.mode == 'fit': | ||||
|                     has_fits = True | ||||
|  | ||||
| @@ -523,7 +533,7 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         while iterator.value(): | ||||
|             item = iterator.value() | ||||
|             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'))) | ||||
|                 else: | ||||
|                     item.setBackground(0, QtGui.QBrush()) | ||||
| @@ -536,10 +546,10 @@ class DataTree(QtWidgets.QTreeWidget): | ||||
|         while iterator.value(): | ||||
|             item = iterator.value() | ||||
|             if item is not None: | ||||
|                 if item.data(0, QtCore.Qt.UserRole) in sets: | ||||
|                     item.setCheckState(0, QtCore.Qt.Unchecked) | ||||
|                 if item.data(0, QtCore.Qt.ItemDataRole.UserRole) in sets: | ||||
|                     item.setCheckState(0, QtCore.Qt.CheckState.Unchecked) | ||||
|                 else: | ||||
|                     self._checked_sets.add(item.data(0, QtCore.Qt.UserRole)) | ||||
|                     self._checked_sets.add(item.data(0, QtCore.Qt.ItemDataRole.UserRole)) | ||||
|                 iterator += 1 | ||||
|         self.blockSignals(False) | ||||
|  | ||||
| @@ -594,7 +604,7 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget): | ||||
|         sid = [] | ||||
|         for i in self.tree.selectedIndexes(): | ||||
|             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) | ||||
|  | ||||
| @@ -603,15 +613,23 @@ class DataWidget(QtWidgets.QWidget, Ui_DataWidget): | ||||
|         self.proptable.populate(props) | ||||
|  | ||||
|     def change_property(self, key1, key2, value): | ||||
|         ids = [item.data(0, QtCore.Qt.UserRole) for item in self.tree.selectedItems()] | ||||
|         if key2 == 'Value': | ||||
|             try: | ||||
|                 value = float(value) | ||||
|             except ValueError: | ||||
|                 QtWidgets.QMessageBox.warning(self, 'Invalid entry', | ||||
|                                               'Value %r is not a valid number for `value`.' % value) | ||||
|                 QtWidgets.QMessageBox.warning( | ||||
|                     self, | ||||
|                     'Invalid entry', | ||||
|                     f'Value {value!r} is not a valid number for `value`.') | ||||
|                 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) | ||||
|  | ||||
|     def uncheck_sets(self, sets: list[str]): | ||||
|   | ||||
| @@ -41,7 +41,7 @@ class PropWidget(QtWidgets.QWidget): | ||||
|         idx = table.indexFromItem(item) | ||||
|         self.propertyChanged.emit(self.tab.tabText(tab_idx), | ||||
|                                   table.item(idx.row(), idx.column()-1).text(), | ||||
|                                   item.data(QtCore.Qt.DisplayRole)) | ||||
|                                   item.data(QtCore.Qt.ItemDataRole.DisplayRole)) | ||||
|  | ||||
|     @QtCore.pyqtSlot(int) | ||||
|     def tab_change(self, idx: int): | ||||
| @@ -66,10 +66,10 @@ class PropTable(QtWidgets.QTableWidget): | ||||
|         self.blockSignals(True) | ||||
|         for k, v in prop.items(): | ||||
|             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.setFlags(QtCore.Qt.NoItemFlags) | ||||
|             key_item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags) | ||||
|             key_item.setForeground(QtGui.QBrush(QtGui.QColor(0, 0, 0))) | ||||
|  | ||||
|             self.setRowCount(self.rowCount()+1) | ||||
|   | ||||
| @@ -3,10 +3,10 @@ from __future__ import annotations | ||||
| from typing import Any | ||||
|  | ||||
| from numpy import ndarray, iscomplexobj, asarray | ||||
| from pyqtgraph import PlotDataItem | ||||
|  | ||||
| from ..Qt import QtGui, QtCore, QtWidgets | ||||
| from .._py.valueeditor import Ui_MaskDialog | ||||
| from ..lib.pg_objects import PlotItem | ||||
|  | ||||
|  | ||||
| class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): | ||||
| @@ -35,13 +35,13 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): | ||||
|  | ||||
|         self.tableView.setModel(self.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.selection_real = PlotDataItem(x=[], y=[], symbolSize=25, symbol='x', | ||||
|                                            pen=None, symbolPen='#c9308e', symbolBrush='#c9308e') | ||||
|         self.selection_imag = PlotDataItem(x=[], y=[], symbolSize=25, symbol='+', | ||||
|                                            pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc') | ||||
|         self.selection_real = PlotItem(x=[], y=[], symbolSize=25, symbol='x', | ||||
|                                        pen=None, symbolPen='#c9308e', symbolBrush='#c9308e') | ||||
|         self.selection_imag = PlotItem(x=[], y=[], symbolSize=25, symbol='+', | ||||
|                                        pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc') | ||||
|  | ||||
|     def __call__(self, items: dict): | ||||
|         self.items = items | ||||
| @@ -133,7 +133,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): | ||||
|     def keyPressEvent(self, evt): | ||||
|         if evt.matches(QtGui.QKeySequence.Copy): | ||||
|             self.copy_selection() | ||||
|         elif evt.key() == QtCore.Qt.Key_Delete: | ||||
|         elif evt.key() == QtCore.Qt.Key.Key_Delete: | ||||
|             self.delete_item() | ||||
|         else: | ||||
|             super().keyPressEvent(evt) | ||||
| @@ -229,7 +229,7 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|     """ | ||||
|     itemChanged = QtCore.pyqtSignal(int, int, str) | ||||
|     load_number = 20 | ||||
|     maskRole = QtCore.Qt.UserRole+321 | ||||
|     maskRole = QtCore.Qt.ItemDataRole.UserRole+321 | ||||
|  | ||||
|     def __init__(self, parent=None): | ||||
|         super().__init__(parent=parent) | ||||
| @@ -240,7 +240,7 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|         self.mask = None | ||||
|         self.headers = ['x', 'y', '\u0394y'] | ||||
|         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: | ||||
|         return self.total_rows | ||||
| @@ -258,25 +258,28 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|         self.mask = mask.tolist() | ||||
|  | ||||
|         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(): | ||||
|             return | ||||
|  | ||||
|         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()] | ||||
|             return self.as_string(val) | ||||
|  | ||||
|         elif role == QtCore.Qt.BackgroundRole: | ||||
|         elif role == QtCore.Qt.ItemDataRole.BackgroundRole: | ||||
|             pal = QtGui.QGuiApplication.palette() | ||||
|             if not self.mask[row]: | ||||
|                 return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Base) | ||||
|             else: | ||||
|                 return pal.color(QtGui.QPalette.Base) | ||||
|  | ||||
|         elif role == QtCore.Qt.ForegroundRole: | ||||
|         elif role == QtCore.Qt.ItemDataRole.ForegroundRole: | ||||
|             pal = QtGui.QGuiApplication.palette() | ||||
|             if not self.mask[row]: | ||||
|                 return pal.color(QtGui.QPalette.Disabled, QtGui.QPalette.Text) | ||||
| @@ -289,7 +292,7 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if role == ValueModel.maskRole: | ||||
| @@ -299,7 +302,7 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|             return True | ||||
|  | ||||
|         if value: | ||||
|             if role == QtCore.Qt.EditRole: | ||||
|             if role == QtCore.Qt.ItemDataRole.EditRole: | ||||
|                 if value == self.as_string(self._data[row][col]): | ||||
|                     return True | ||||
|  | ||||
| @@ -322,9 +325,9 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|         else: | ||||
|             return False | ||||
|  | ||||
|     def headerData(self, section: int, orientation, role=QtCore.Qt.DisplayRole) -> Any: | ||||
|         if role == QtCore.Qt.DisplayRole: | ||||
|             if orientation == QtCore.Qt.Horizontal: | ||||
|     def headerData(self, section: int, orientation, role=QtCore.Qt.ItemDataRole.DisplayRole) -> Any: | ||||
|         if role == QtCore.Qt.ItemDataRole.DisplayRole: | ||||
|             if orientation == QtCore.Qt.Orientation.Horizontal: | ||||
|                 return self.headers[section] | ||||
|             else: | ||||
|                 return str(section+1) | ||||
| @@ -346,7 +349,7 @@ class ValueModel(QtCore.QAbstractTableModel): | ||||
|         self.endInsertRows() | ||||
|  | ||||
|     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: | ||||
|         self.beginRemoveRows(parent, pos, pos+rows-1) | ||||
|   | ||||
| @@ -136,8 +136,8 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|             max_x = max(max_x, data.x.max()) | ||||
|  | ||||
|             item = QtWidgets.QListWidgetItem(name) | ||||
|             item.setCheckState(QtCore.Qt.Checked) | ||||
|             item.setData(QtCore.Qt.UserRole, key) | ||||
|             item.setCheckState(QtCore.Qt.CheckState.Checked) | ||||
|             item.setData(QtCore.Qt.ItemDataRole.UserRole, key) | ||||
|             item.setForeground(mkBrush(c.rgb())) | ||||
|             self.listWidget.addItem(item) | ||||
|  | ||||
| @@ -191,10 +191,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|  | ||||
|         for idx in range(self.listWidget.count()): | ||||
|             item = self.listWidget.item(idx) | ||||
|             if item.checkState() == QtCore.Qt.Unchecked: | ||||
|             if item.checkState() == QtCore.Qt.CheckState.Unchecked: | ||||
|                 continue | ||||
|  | ||||
|             key = item.data(QtCore.Qt.UserRole) | ||||
|             key = item.data(QtCore.Qt.ItemDataRole.UserRole) | ||||
|             plot = self._plots[key] | ||||
|             data, _ = self._dsc[key] | ||||
|  | ||||
| @@ -214,7 +214,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|             item = self.listWidget.item(idx) | ||||
|  | ||||
|             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: | ||||
|                 for name, pos in values.items(): | ||||
| @@ -223,7 +223,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|  | ||||
|                 self.tg_tree.addTopLevelItem(tree_item) | ||||
|  | ||||
|             key = item.data(QtCore.Qt.UserRole) | ||||
|             key = item.data(QtCore.Qt.ItemDataRole.UserRole) | ||||
|             plot = self._plots[key] | ||||
|             data, _ = self._dsc[key] | ||||
|  | ||||
| @@ -251,7 +251,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|     @QtCore.pyqtSlot(QtWidgets.QListWidgetItem) | ||||
|     def change_visibility(self, item: QtWidgets.QListWidgetItem): | ||||
|         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: | ||||
|             val.setVisible(is_checked) | ||||
|  | ||||
| @@ -275,10 +275,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|         self.tnmh_tree.clear() | ||||
|         for idx in range(self.listWidget.count()): | ||||
|             item = self.listWidget.item(idx) | ||||
|             if item.checkState() == QtCore.Qt.Unchecked: | ||||
|             if item.checkState() == QtCore.Qt.CheckState.Unchecked: | ||||
|                 continue | ||||
|  | ||||
|             key = item.data(QtCore.Qt.UserRole) | ||||
|             key = item.data(QtCore.Qt.ItemDataRole.UserRole) | ||||
|  | ||||
|             data = self.get_fictive(key, baselines) | ||||
|  | ||||
| @@ -292,7 +292,7 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|             item = self.listWidget.item(idx) | ||||
|  | ||||
|             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: | ||||
|                 child_item = QtWidgets.QTreeWidgetItem([values.parameter_string()]) | ||||
| @@ -305,10 +305,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|         ret_dic = {} | ||||
|         for idx in range(self.listWidget.count()): | ||||
|             item = self.listWidget.item(idx) | ||||
|             if item.checkState() == QtCore.Qt.Unchecked: | ||||
|             if item.checkState() == QtCore.Qt.CheckState.Unchecked: | ||||
|                 continue | ||||
|  | ||||
|             key = item.data(QtCore.Qt.UserRole) | ||||
|             key = item.data(QtCore.Qt.ItemDataRole.UserRole) | ||||
|  | ||||
|             cp = None | ||||
|             if self.fictive_export_check.isChecked(): | ||||
| @@ -332,10 +332,10 @@ class TgCalculator(QtWidgets.QWizard, Ui_DSCEvalDialog): | ||||
|             m = [] | ||||
|             for idx in range(self.listWidget.count()): | ||||
|                 item = self.listWidget.item(idx) | ||||
|                 if item.checkState() == QtCore.Qt.Unchecked: | ||||
|                 if item.checkState() == QtCore.Qt.CheckState.Unchecked: | ||||
|                     continue | ||||
|  | ||||
|                 key = item.data(QtCore.Qt.UserRole) | ||||
|                 key = item.data(QtCore.Qt.ItemDataRole.UserRole) | ||||
|                 data, _ = self._dsc[key] | ||||
|                 try: | ||||
|                     tg_value = self._tg_value[key][tg_type][0] | ||||
|   | ||||
| @@ -58,7 +58,8 @@ class FitToolbar(QtWidgets.QToolBar): | ||||
|  | ||||
|     @QtCore.pyqtSlot(QtWidgets.QAction) | ||||
|     def change_limit_type(self, action: QtWidgets.QAction): | ||||
|         is_custom = (action.text() == 'Custom') | ||||
|         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) | ||||
| @@ -93,5 +94,6 @@ class FitToolbar(QtWidgets.QToolBar): | ||||
|         return { | ||||
|             'None': 'none', | ||||
|             'Visible x range': 'x', | ||||
|             'Custom': self.region.getRegion(), | ||||
|             'Custom': ('in', self.region.getRegion()), | ||||
|             'Exclude region': ('out', self.region.getRegion()), | ||||
|         }[action_text] | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| from math import isnan | ||||
|  | ||||
| from pyqtgraph import mkBrush, mkPen, mkColor | ||||
| @@ -28,6 +30,7 @@ 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.minx_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._opts = [] | ||||
| @@ -363,7 +366,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): | ||||
|  | ||||
|             parts = self.partial_checkBox.checkState() == QtCore.Qt.CheckState.Checked | ||||
|  | ||||
|             extrapolate = [None, None, None] | ||||
|             extrapolate = [None, None, None, None] | ||||
|             error = [] | ||||
|             if self.extrapolate_box.isChecked(): | ||||
|                 try: | ||||
| @@ -379,6 +382,8 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): | ||||
|                 except (TypeError, ValueError): | ||||
|                     error.append('Number of points is missing') | ||||
|  | ||||
|                 extrapolate[3] = self.newx_log_checkbox.isChecked() | ||||
|  | ||||
|             if error: | ||||
|                 msg = QtWidgets.QMessageBox.warning(self, 'Error', 'Extrapolation failed because:\n' + '\n'.join(error)) | ||||
|                 return | ||||
| @@ -416,10 +421,13 @@ class FitExtension(QtWidgets.QDialog): | ||||
|         self.num_pts.setValidator(QtGui.QIntValidator()) | ||||
|         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.setOrientation(QtCore.Qt.Orientation.Horizontal) | ||||
|         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) | ||||
|  | ||||
| @@ -427,12 +435,13 @@ class FitExtension(QtWidgets.QDialog): | ||||
|         self.buttonBox.rejected.connect(self.reject) | ||||
|  | ||||
|     @property | ||||
|     def values(self): | ||||
|     def values(self) -> tuple[float, float, int, bool] | None: | ||||
|         try: | ||||
|             xmin = float(self.min_line.text()) | ||||
|             xmax = float(self.max_line.text()) | ||||
|             nums = int(self.num_pts.text()) | ||||
|             logx = self.logx_checkbox.isChecked() | ||||
|         except TypeError: | ||||
|             return None | ||||
|  | ||||
|         return xmin, xmax, nums | ||||
|         return xmin, xmax, nums, logx | ||||
|   | ||||
| @@ -670,11 +670,14 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): | ||||
|  | ||||
|         else: | ||||
|             if os.path.exists(outfile): | ||||
|                 if QtWidgets.QMessageBox.warning(self, 'Export graphic', | ||||
|                                                  f'{os.path.split(outfile)[1]} already exists.\n' | ||||
|                                                  f'Do you REALLY want to replace it?', | ||||
|                                                  QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, | ||||
|                                                  QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.No: | ||||
|                 if QtWidgets.QMessageBox.warning( | ||||
|                         self, | ||||
|                         'Export graphic', | ||||
|                         f'{os.path.split(outfile)[1]} already exists.\n' | ||||
|                         f'Do you REALLY want to replace it?', | ||||
|                         QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, | ||||
|                         QtWidgets.QMessageBox.No | ||||
|                 ) == QtWidgets.QMessageBox.No: | ||||
|                     return | ||||
|  | ||||
|             bg_color = self._bgcolor | ||||
| @@ -716,16 +719,20 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): | ||||
|                 logger.exception(f'{item} could not exported because {e.args}') | ||||
|                 continue | ||||
|  | ||||
|             if len(item) == 2: | ||||
|                 # plot can show errorbars | ||||
|                 item_dic['yerr'] = item[1].opts['topData'] | ||||
|  | ||||
|             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) | ||||
|  | ||||
|         for item in self._external_items: | ||||
|             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: | ||||
|                 logger.exception(f'{item} could not be exported because {e.args}') | ||||
|                 continue | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): | ||||
|         self.changestaggeredrange(0) | ||||
|  | ||||
|         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.skip = False | ||||
| @@ -65,7 +65,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): | ||||
|         self.set_column_names(1) | ||||
|  | ||||
|         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) | ||||
|  | ||||
|         return self | ||||
| @@ -232,7 +232,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): | ||||
|  | ||||
|     @QtCore.pyqtSlot(int, name='on_buttonGroup_buttonClicked') | ||||
|     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') | ||||
|     def skip_next_dial(self, _: int): | ||||
|   | ||||
| @@ -78,14 +78,22 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|  | ||||
|         for opts in self.sample.steps: | ||||
|             item = QtWidgets.QListWidgetItem() | ||||
|             item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable) | ||||
|             item.setCheckState(QtCore.Qt.Unchecked) | ||||
|             item.setFlags( | ||||
|                 QtCore.Qt.ItemFlag.ItemIsEnabled | | ||||
|                 QtCore.Qt.ItemFlag.ItemIsSelectable | | ||||
|                 QtCore.Qt.ItemFlag.ItemIsUserCheckable | ||||
|             ) | ||||
|             item.setCheckState(QtCore.Qt.CheckState.Unchecked) | ||||
|  | ||||
|             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') | ||||
|             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') | ||||
|  | ||||
|             self.step_listWidget.addItem(item) | ||||
| @@ -97,7 +105,12 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|  | ||||
|         if empty: | ||||
|             self.empty = self.calibrator.set_measurement(empty, mode='empty') | ||||
|             self.empty_label.setText('~/' + str(self.empty.fname.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() | ||||
|  | ||||
| @@ -118,8 +131,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|  | ||||
|             self.references.append(ref) | ||||
|             item = QtWidgets.QTableWidgetItem(str(ref.fname.name)) | ||||
|             item.setData(QtCore.Qt.UserRole, ref.fname) | ||||
|             item.setFlags(QtCore.Qt.ItemIsEnabled) | ||||
|             item.setData(QtCore.Qt.ItemDataRole.UserRole, ref.fname) | ||||
|             item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) | ||||
|  | ||||
|             rowcnt = self.reference_tableWidget.rowCount() | ||||
|             self.reference_tableWidget.setRowCount(rowcnt+1) | ||||
| @@ -132,7 +145,7 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|     @QtCore.pyqtSlot(name='on_ref_remove_pushButton_clicked') | ||||
|     def remove_reference(self): | ||||
|         idx = self.reference_tableWidget.currentRow() | ||||
|         self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.UserRole)) | ||||
|         self.calibrator.remove_reference(self.reference_tableWidget.item(idx, 0).data(QtCore.Qt.ItemDataRole.UserRole)) | ||||
|  | ||||
|         self.reference_tableWidget.removeRow(idx) | ||||
|         self.update_plots() | ||||
| @@ -145,10 +158,10 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|         for row in range(self.step_listWidget.count()): | ||||
|             if idx == row: | ||||
|                 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) | ||||
|  | ||||
|         if item.checkState() == QtCore.Qt.Checked: | ||||
|         if item.checkState() == QtCore.Qt.CheckState.Checked: | ||||
|             mode, rate, _, _ = self.sample.steps[idx] | ||||
|             self.current_run = (rate, mode) | ||||
|             self.sample_idx = idx | ||||
| @@ -217,6 +230,8 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|  | ||||
|         if empty_data is not None: | ||||
|             self.empty_sample.setData(x=empty_data[0], y=empty_data[1]) | ||||
|         else: | ||||
|             self.empty_sample.setData(x=[], y=[]) | ||||
|  | ||||
|         self.calib_graph.clear() | ||||
|  | ||||
| @@ -249,11 +264,16 @@ class QDSCReader(QtWidgets.QDialog, Ui_Dialog): | ||||
|         except TypeError: | ||||
|             return | ||||
|  | ||||
|         if self.cp_checkBox.isChecked() and self.references: | ||||
|             y_label = 'cp' | ||||
|         else: | ||||
|             y_label = 'q' | ||||
|  | ||||
|         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: | ||||
|             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 | ||||
|         else: | ||||
|             self.data_read.emit([new_val]) | ||||
|   | ||||
| @@ -1,5 +1,8 @@ | ||||
| import logging | ||||
| from pathlib import Path | ||||
|  | ||||
| from PyQt5 import QtWidgets | ||||
|  | ||||
| from .codeeditor import _make_textformats | ||||
| from ..Qt import QtWidgets, QtCore, QtGui | ||||
| from nmreval.configs import config_paths | ||||
| @@ -113,3 +116,27 @@ class QLog(QtWidgets.QDialog): | ||||
|  | ||||
|         for lines in text[-100:]: | ||||
|             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')) | ||||
|  | ||||
|     def emit(self, record): | ||||
|         msg = self.format(record) | ||||
|         self.console.code.appendPlainText(msg) | ||||
|         self.console.show() | ||||
|   | ||||
| @@ -183,6 +183,8 @@ class PlotItem(PlotDataItem): | ||||
|             brush = self.opts['symbolBrush'] | ||||
|             if isinstance(brush, tuple): | ||||
|                 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: | ||||
|                 c = brush.color() | ||||
|                 self.opts['symbolcolor'] = c.red(), c.green(), c.blue() | ||||
| @@ -340,7 +342,8 @@ class PlotItem(PlotDataItem): | ||||
|  | ||||
|         opts = self.opts | ||||
|         item_dic = { | ||||
|             'x': x, 'y': y, | ||||
|             'x': x, | ||||
|             'y': y, | ||||
|             'name': opts.get('name', ''), | ||||
|             'symbolsize': opts['symbolSize'], | ||||
|         } | ||||
|   | ||||
| @@ -10,7 +10,7 @@ from ..Qt import QtGui, QtWidgets, QtCore | ||||
| @contextmanager | ||||
| def busy_cursor(): | ||||
|     try: | ||||
|         cursor = QtGui.QCursor(QtCore.Qt.ForbiddenCursor) | ||||
|         cursor = QtGui.QCursor(QtCore.Qt.CursorShape.ForbiddenCursor) | ||||
|         QtWidgets.QApplication.setOverrideCursor(cursor) | ||||
|         yield | ||||
|  | ||||
|   | ||||
							
								
								
									
										3
									
								
								src/gui_qt/main/console.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/gui_qt/main/console.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -12,6 +12,7 @@ from nmreval.lib.logger import logger | ||||
| from nmreval.io.sessionwriter import NMRWriter | ||||
|  | ||||
| from .management import UpperManagement | ||||
| from ..lib.logger import ConsoleDock, QTextHandler | ||||
| from ..Qt import QtGui, QtPrintSupport | ||||
| from ..data.shift_graphs import QShift | ||||
| from ..data.signaledit import QPreviewDialog, QBaselineDialog | ||||
| @@ -62,6 +63,11 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): | ||||
|         self.eval = None | ||||
|         self.editor = 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.current_graph_widget = None | ||||
| @@ -888,7 +894,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): | ||||
|             self.fit_dialog.load(self.management.active_sets) | ||||
|             for item in self.fit_dialog.preview_lines: | ||||
|                 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.fit_toolbar.region) | ||||
|  | ||||
|             block_window = True | ||||
| @@ -904,7 +910,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): | ||||
|         if self.current_graph_widget is None: | ||||
|             return | ||||
|  | ||||
|         if action == self.action_custom_range and self.fit_dialog.isVisible(): | ||||
|         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: | ||||
|             self.current_graph_widget.remove_external(self.fit_toolbar.region) | ||||
| @@ -984,7 +990,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): | ||||
|         res = w.exec() | ||||
|         if res: | ||||
|             p = w.values | ||||
|             x = linspace(p[0], p[1], num=p[2]) | ||||
|             spacefunc = geomspace if p[3] else linspace | ||||
|             x = spacefunc(p[0], p[1], num=p[2]) | ||||
|             self.management.extend_fits(sets, x) | ||||
|  | ||||
|     @QtCore.pyqtSlot(name='on_action_create_fit_function_triggered') | ||||
|   | ||||
| @@ -503,27 +503,44 @@ class UpperManagement(QtCore.QObject): | ||||
|                         we = we_option | ||||
|  | ||||
|                     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 | ||||
|                     elif m_complex == 2 and np.iscomplexobj(data_i.y): | ||||
|                         _y = data_i.y.imag | ||||
|                         data_complex = 1 | ||||
|                     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: | ||||
|                         # model is complex, fit complex: m_complex = 0 | ||||
|                         # use data as given (complex or not) | ||||
|                         _y = data_i.y | ||||
|                         data_complex = 0 | ||||
|  | ||||
|                     _x = data_i.x | ||||
|  | ||||
|                     # options for fit limits 'none', 'x', ('in', custom region), ('out', excluded region) | ||||
|                     if fit_limits == 'none': | ||||
|                         inside = slice(None) | ||||
|                     elif fit_limits == 'x': | ||||
|                         x_lim, _ = self.graphs[self.current_graph].ranges | ||||
|                         inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1])) | ||||
|                     elif fit_limits[0] == 'in': | ||||
|                         inside = np.where((_x >= fit_limits[1][0]) & (_x <= fit_limits[1][1])) | ||||
|                     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: | ||||
|                         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: | ||||
|                             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: | ||||
|                         raise Exception(f'Setting data failed for {set_id}') | ||||
|  | ||||
| @@ -623,7 +640,7 @@ class UpperManagement(QtCore.QObject): | ||||
|                 continue | ||||
|  | ||||
|             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() | ||||
|                 xmax = fit.x.max() | ||||
|   | ||||
| @@ -12,10 +12,10 @@ class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog): | ||||
|  | ||||
|     @QtCore.pyqtSlot(int, name='on_comboBox_currentIndexChanged') | ||||
|     def change_mode(self, idx: int): | ||||
|         if idx == 2: | ||||
|         if idx == 1: | ||||
|             self.widget.show() | ||||
|             self.widget_2.hide() | ||||
|         elif idx == 3: | ||||
|         elif idx == 2: | ||||
|             self.widget.show() | ||||
|             self.widget_2.show() | ||||
|         else: | ||||
| @@ -29,12 +29,24 @@ class QSmooth(QtWidgets.QDialog, Ui_SmoothDialog): | ||||
|         idx = self.comboBox.currentIndex() | ||||
|  | ||||
|         # 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() | ||||
|  | ||||
|         if idx == 3: | ||||
|         # LOESS needs also polynomial degree and number of iterations | ||||
|         if idx == 2: | ||||
|             para['deg'] = self.polynom_spinBox.value() | ||||
|             para['it'] = self.iter_spinBox.value() | ||||
|  | ||||
|   | ||||
| @@ -320,7 +320,11 @@ class Points: | ||||
|             pts = [] | ||||
|  | ||||
|         _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: | ||||
|             for idx_i in idx: | ||||
|                 if isinstance(idx_i, tuple): | ||||
| @@ -338,7 +342,7 @@ class Points: | ||||
|                     right_b = int(min(len(self), x_idx + avg_range[1] + 1)) | ||||
|  | ||||
|                 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: | ||||
|                     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])) | ||||
|             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 | ||||
|  | ||||
|     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': | ||||
|             y_mean = np.mean(self._y[self.mask][left:right].real) | ||||
|             y_err = np.linalg.norm(self._y_err[self.mask][left:right]) / (right - left) | ||||
|             y_mean = np.mean(y[left:right].real) | ||||
|             y_err_mean = np.linalg.norm(y_err[left:right]) / (right - left) | ||||
|  | ||||
|         elif mode == 'sum': | ||||
|             y_mean = np.sum(self._y[self.mask][left:right].real) | ||||
|             y_err = np.linalg.norm(self._y_err[self.mask][left:right]) | ||||
|             y_mean = np.sum(y[left:right].real) | ||||
|             y_err_mean = np.linalg.norm(y_err[left:right]) | ||||
|  | ||||
|         elif mode == 'integral': | ||||
|             y_mean = simpson(self._y[self.mask][left:right].real, x=self._x[left:right]) | ||||
|             y_err = np.linalg.norm(cumulative_trapezoid(self._y_err[self.mask][left:right].real, x=self._x[left:right])) | ||||
|             y_mean = simpson(y[left:right].real, x=x[left:right]) | ||||
|             y_err_mean = np.linalg.norm(cumulative_trapezoid(y_err[left:right].real, x=x[left:right])) | ||||
|  | ||||
|         else: | ||||
|             y_mean = self._y[self.mask][idx].real | ||||
|             y_err = self._y_err[self.mask][idx] | ||||
|             y_mean = y[idx].real | ||||
|             y_err_mean = y_err[idx] | ||||
|  | ||||
|         return y_mean, y_err | ||||
|         return y_mean, y_err_mean | ||||
|  | ||||
|     def concatenate(self, other): | ||||
|         """ | ||||
|   | ||||
| @@ -6,8 +6,8 @@ from .model import Model | ||||
| from .parameter import Parameters, Parameter | ||||
|  | ||||
|  | ||||
| class Data(object): | ||||
|     def __init__(self, x, y, we=None, idx=None): | ||||
| class Data: | ||||
|     def __init__(self, x, y, we=None, idx=None, complex_type: int = 0): | ||||
|         self.x = np.asarray(x) | ||||
|         self.y = np.asarray(y) | ||||
|         if self.y.shape[0] != self.x.shape[0]: | ||||
| @@ -20,6 +20,7 @@ class Data(object): | ||||
|         self.parameter = Parameters() | ||||
|         self.para_keys: list = [] | ||||
|         self.fun_kwargs = {} | ||||
|         self.complex_type = complex_type | ||||
|  | ||||
|     def __len__(self): | ||||
|         return self.y.shape[0] | ||||
|   | ||||
| @@ -361,7 +361,7 @@ class FitRoutine(object): | ||||
|         with np.errstate(all='ignore'): | ||||
|             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, | ||||
|                           err=err, corr=corr, partial_corr=partial_corr) | ||||
|  | ||||
| @@ -375,7 +375,7 @@ class FitRoutine(object): | ||||
|         with np.errstate(all='ignore'): | ||||
|             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): | ||||
|             self.make_results(v, res.x, var, var_pars_k, res.jac.shape, | ||||
|                               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)), | ||||
|                               err=res.sd_beta, corr=corr, partial_corr=partial_corr) | ||||
|  | ||||
|     def make_results(self, data, p, var_pars, used_pars, shape, | ||||
|                      err=None, corr=None, partial_corr=None): | ||||
|  | ||||
|     def make_results( | ||||
|             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: | ||||
|             err = [0] * len(p) | ||||
|  | ||||
| @@ -498,52 +506,54 @@ class FitRoutine(object): | ||||
|         model = data.get_model() | ||||
|  | ||||
|         self.result[idx] = FitResultCreator.make_with_model( | ||||
|             model, | ||||
|             data.x, | ||||
|             data.y, | ||||
|             actual_parameters, | ||||
|             data.fun_kwargs, | ||||
|             data.we_string, | ||||
|             data.idx, | ||||
|             *shape, | ||||
|             model=model, | ||||
|             x_orig=data.x, | ||||
|             y_orig=data.y, | ||||
|             p=actual_parameters, | ||||
|             fun_kwargs=data.fun_kwargs, | ||||
|             we=data.we_string, | ||||
|             idx=data.idx, | ||||
|             nobs=shape[0], | ||||
|             nvar=shape[1], | ||||
|             corr=actual_corr, | ||||
|             pcorr=actual_pcorr, | ||||
|             data_mode=data.complex_type, | ||||
|         ) | ||||
|  | ||||
|         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: | ||||
|             _err = np.zeros(nvars) | ||||
|             corr = np.zeros((nvars, nvars)) | ||||
|         else: | ||||
|             _err = np.sqrt(np.diag(pcov)) | ||||
|             corr = pcov / (_err[:, None] * _err[None, :]) | ||||
| 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) | ||||
|  | ||||
|         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 | ||||
|     if pcov is None: | ||||
|         _err = np.zeros(nvars) | ||||
|         corr = np.zeros((nvars, nvars)) | ||||
|     else: | ||||
|         _err = np.sqrt(np.diag(pcov)) | ||||
|         corr = pcov / (_err[:, None] * _err[None, :]) | ||||
|  | ||||
|         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 | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ from ._meta import MultiModel | ||||
| from .parameter import Parameters, Parameter | ||||
|  | ||||
|  | ||||
| class Model(object): | ||||
| class Model: | ||||
|     def __init__(self, model, *args, **kwargs): | ||||
|         self.idx = kwargs.pop('idx', None) | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ from scipy.stats import f as fdist | ||||
| from scipy.interpolate import interp1d | ||||
|  | ||||
| from ._meta import MultiModel | ||||
| from .model import Model | ||||
| from .parameter import Parameter | ||||
| from ..data.points import Points | ||||
| from ..data.signals import Signal | ||||
| @@ -36,17 +37,30 @@ class FitResultCreator: | ||||
|         else: | ||||
|             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, | ||||
|                          kwargs['name'], stats, idx) | ||||
|         return FitResult( | ||||
|             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 | ||||
|     def make_with_model( | ||||
|             model: 'Model', | ||||
|             x_orig: np.ndarray, | ||||
|             y_orig: np.ndarray, | ||||
|             p: 'Parameters', | ||||
|             p: list, | ||||
|             fun_kwargs: dict, | ||||
|             we: str, | ||||
|             idx: str | None, | ||||
| @@ -54,6 +68,7 @@ class FitResultCreator: | ||||
|             nvar: int, | ||||
|             corr:  np.ndarray, | ||||
|             pcorr: np.ndarray, | ||||
|             data_mode: int, | ||||
|     ) -> FitResult: | ||||
|         if np.all(x_orig > 0) and (np.max(x_orig) > 100 * np.min(x_orig)): | ||||
|             islog = True | ||||
| @@ -83,17 +98,11 @@ class FitResultCreator: | ||||
|             actual_mode = fun_kwargs['complex_mode'] | ||||
|             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: | ||||
|             if actual_mode == 1: | ||||
|                 _y.imag = 0 | ||||
|             elif actual_mode == 2: | ||||
|                 _y.real = 0 | ||||
|         fun_kwargs['complex_mode'] = actual_mode | ||||
|  | ||||
|             fun_kwargs['complex_mode'] = actual_mode | ||||
|  | ||||
|         stats = FitResultCreator.calc_statistics(_y, resid, nobs, nvar) | ||||
|         stats = calc_statistics(_y, resid, nobs, nvar) | ||||
|         varied = [p.var for p in parameters.values()] | ||||
|  | ||||
|         if corr is None: | ||||
| @@ -134,38 +143,9 @@ class FitResultCreator: | ||||
|             pcorr=partial_correlation, | ||||
|             islog=islog, | ||||
|             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): | ||||
|  | ||||
| @@ -188,7 +168,8 @@ class FitResult(Points): | ||||
|             pcorr: np.ndarray = None, | ||||
|             islog: bool = False, | ||||
|             func=None, | ||||
|             **kwargs | ||||
|             data_complex: int = 1, | ||||
|             **kwargs, | ||||
|     ): | ||||
|  | ||||
|         self.parameter, name = self._prepare_names(params, name) | ||||
| @@ -210,6 +191,7 @@ class FitResult(Points): | ||||
|         self.y_data = y_data | ||||
|         self._model_name = name | ||||
|         self._func = func | ||||
|         self._data_complex = data_complex | ||||
|  | ||||
|     @staticmethod | ||||
|     def _prepare_names(parameter: dict, modelname: str): | ||||
| @@ -418,20 +400,12 @@ class FitResult(Points): | ||||
|         if self.func is None: | ||||
|             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() | ||||
|         y_values = self.func.func(self.p_final, x_values, **self.fun_kwargs) | ||||
|         if not actual_mode < 0: | ||||
|             if actual_mode == 1: | ||||
|                 y_values.imag = 0 | ||||
|             elif actual_mode == 2: | ||||
|                 y_values.real = 0 | ||||
|  | ||||
|             self.fun_kwargs['complex_mode'] = actual_mode | ||||
|         fun_kwargs = {k: v for k, v in self.fun_kwargs.items()} | ||||
|         if self.fun_kwargs.get('complex_mode', -1) == -1: | ||||
|             fun_kwargs.pop('complex_mode', None) | ||||
|         y_values = self.func.func(self.p_final, x_values, **fun_kwargs) | ||||
|         y_values = check_complex(y_values, self.fun_kwargs.get('complex_mode', -1), self._data_complex) | ||||
|  | ||||
|         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') | ||||
|  | ||||
|         part_functions = [] | ||||
|         actual_mode = -1 | ||||
|         if 'complex_mode' in self.fun_kwargs: | ||||
|             actual_mode = self.fun_kwargs['complex_mode'] | ||||
|             self.fun_kwargs['complex_mode'] = 0 | ||||
|         actual_mode = self.fun_kwargs.get('complex_mode', -1) | ||||
|         fun_kwargs = {k: v for k, v in self.fun_kwargs.items()} | ||||
|         if self.fun_kwargs.get('complex_mode', -1) == -1: | ||||
|             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)): | ||||
|             if not actual_mode < 0: | ||||
|                 if actual_mode == 1: | ||||
|                     sub_y.imag = 0 | ||||
|                 elif actual_mode == 2: | ||||
|                     sub_y.real = 0 | ||||
|             sub_y = check_complex(sub_y, actual_mode, self._data_complex) | ||||
|  | ||||
|             if np.iscomplexobj(sub_y): | ||||
|                 part_functions.append(Signal(x_values, sub_y, name=sub_name)) | ||||
|  | ||||
|             else: | ||||
|                 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 | ||||
|  | ||||
|         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 | ||||
|   | ||||
| @@ -49,7 +49,7 @@ class AsciiReader: | ||||
|         with self.fname.open('r') as f: | ||||
|             for i, line in enumerate(islice(f, len(self.header)+len(self.lines), num_lines)): | ||||
|                 line = line.strip('\n\t\r, ') | ||||
|                 line = re.sub(r'[\t ;,] *', ';', line) | ||||
|                 line = re.sub(r'[\t ;,]+', ';', line) | ||||
|                 line = line.split(';') | ||||
|  | ||||
|                 try: | ||||
| @@ -183,7 +183,13 @@ class AsciiReader: | ||||
|                 single_len = 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): | ||||
|                 if col_names is not None: | ||||
| @@ -191,7 +197,7 @@ class AsciiReader: | ||||
|                     kwargs['name'] = col_names[j-1] | ||||
|                 elif num_y > single_len: | ||||
|                     # 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]: | ||||
|                     kwargs['y_err'] = raw_data[i, :, j+num_y] | ||||
|   | ||||
| @@ -11,7 +11,8 @@ try: | ||||
|     from scipy.integrate import simpson | ||||
| except ImportError: | ||||
|     from scipy.integrate import simps as simpson | ||||
| from scipy.interpolate import interp1d | ||||
| from scipy.interpolate import CubicSpline | ||||
|  | ||||
|  | ||||
| ReferenceValue = namedtuple('Reference', ['name', 'transitions']) | ||||
| 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: | ||||
|         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: | ||||
|             ii = 1 | ||||
|             for line in f: | ||||
| @@ -144,9 +145,12 @@ class DSCCalibrator: | ||||
|         self.reference = [] | ||||
|         self.ref_list = [] | ||||
|  | ||||
|     def set_measurement(self, | ||||
|                         fname: str | Path | DSCSample, mode: str = 'sample', | ||||
|                         reference: ReferenceValue = Cyclohexane): | ||||
|     def set_measurement( | ||||
|             self: DSCCalibrator, | ||||
|             fname: str | Path | DSCSample, | ||||
|             mode: str = 'sample', | ||||
|             reference: ReferenceValue = Cyclohexane | ||||
|     ): | ||||
|         if mode not in ['sample', 'empty', 'reference']: | ||||
|             raise ValueError(f'Unknown mode {mode}, not "sample", "empty", "reference"') | ||||
|         if mode == 'reference' and not isinstance(reference, ReferenceValue): | ||||
| @@ -266,7 +270,12 @@ class DSCCalibrator: | ||||
|  | ||||
|         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': | ||||
|             raise ValueError('baseline correction is not implemented for isotherms') | ||||
|  | ||||
| @@ -292,7 +301,7 @@ class DSCCalibrator: | ||||
|             empty_y = empty_data[1] | ||||
|             if self.sample.length(idx) != self.empty.length(idx_empty): | ||||
|                 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 | ||||
|         drift_value = sample_data.copy()[(2, 1), :] | ||||
| @@ -346,9 +355,10 @@ class DSCCalibrator: | ||||
|  | ||||
|             offset = region[0, 0] | ||||
|             sample_data[1] -= m * (sample_data[2] - region[1, 0]) + offset | ||||
|             line = np.array([[sample_data[2, 0], sample_data[2, -1]], | ||||
|                              [m * (sample_data[2, 0] - region[1, 0]) + offset, | ||||
|                               m * (sample_data[2, -1] - region[1, 0]) + offset]]) | ||||
|             line = np.array([ | ||||
|                 [sample_data[2, 0], sample_data[2, -1]], | ||||
|                 [m * (sample_data[2, 0] - region[1, 0]) + offset, m * (sample_data[2, -1] - region[1, 0]) + offset] | ||||
|             ]) | ||||
|  | ||||
|         else: | ||||
|             line = np.array([[sample_data[2, 0], sample_data[2, -1]], [0, 0]]) | ||||
|   | ||||
| @@ -247,6 +247,7 @@ | ||||
|      <addaction name="action_no_range"/> | ||||
|      <addaction name="action_x_range"/> | ||||
|      <addaction name="action_custom_range"/> | ||||
|      <addaction name="actionExclude_region"/> | ||||
|     </widget> | ||||
|     <addaction name="action_FitWidget"/> | ||||
|     <addaction name="separator"/> | ||||
| @@ -1021,6 +1022,14 @@ | ||||
|     <string>TNMH...</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="actionExclude_region"> | ||||
|    <property name="checkable"> | ||||
|     <bool>true</bool> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Exclude region</string> | ||||
|    </property> | ||||
|   </action> | ||||
|  </widget> | ||||
|  <customwidgets> | ||||
|   <customwidget> | ||||
|   | ||||
| @@ -354,59 +354,26 @@ | ||||
|       <property name="spacing"> | ||||
|        <number>3</number> | ||||
|       </property> | ||||
|       <item row="1" column="0"> | ||||
|        <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"> | ||||
|       <item row="1" column="7"> | ||||
|        <widget class="QComboBox" name="graph_comboBox"> | ||||
|         <property name="enabled"> | ||||
|          <bool>false</bool> | ||||
|         </property> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | ||||
|          <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </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"> | ||||
|        <widget class="QLineEdit" name="minx_line"> | ||||
|         <property name="enabled"> | ||||
|          <bool>false</bool> | ||||
|         </property> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
| @@ -419,10 +386,45 @@ | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item row="0" column="4" rowspan="2"> | ||||
|        <widget class="Line" name="line_2"> | ||||
|         <property name="orientation"> | ||||
|          <enum>Qt::Vertical</enum> | ||||
|       <item row="1" column="0"> | ||||
|        <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="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> | ||||
|        </widget> | ||||
|       </item> | ||||
| @@ -432,7 +434,7 @@ | ||||
|          <bool>false</bool> | ||||
|         </property> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
| @@ -445,20 +447,33 @@ | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item row="1" column="3"> | ||||
|        <widget class="QLineEdit" name="numx_line"> | ||||
|         <property name="enabled"> | ||||
|          <bool>false</bool> | ||||
|         </property> | ||||
|         <property name="placeholderText"> | ||||
|          <string># pts</string> | ||||
|       <item row="0" column="5" rowspan="2"> | ||||
|        <widget class="Line" name="line_2"> | ||||
|         <property name="orientation"> | ||||
|          <enum>Qt::Vertical</enum> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </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"> | ||||
|         <item> | ||||
|          <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"> | ||||
|            <string>Plot fit curve</string> | ||||
|           </property> | ||||
| @@ -469,6 +484,12 @@ | ||||
|         </item> | ||||
|         <item> | ||||
|          <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"> | ||||
|            <string>Plot partial functions</string> | ||||
|           </property> | ||||
| @@ -476,6 +497,19 @@ | ||||
|         </item> | ||||
|        </layout> | ||||
|       </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> | ||||
|     </widget> | ||||
|    </item> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user