diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index 5c0bff4..3b24795 100644 --- a/src/gui_qt/_py/basewindow.py +++ b/src/gui_qt/_py/basewindow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'resources/_ui/basewindow.ui' +# Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui' # # Created by: PyQt5 UI code generator 5.15.10 # @@ -153,15 +153,6 @@ class Ui_BaseWindow(object): self.toolBar_nmr.setIconSize(QtCore.QSize(24, 24)) self.toolBar_nmr.setObjectName("toolBar_nmr") BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_nmr) - self.toolBar_fit = QtWidgets.QToolBar(BaseWindow) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.toolBar_fit.sizePolicy().hasHeightForWidth()) - self.toolBar_fit.setSizePolicy(sizePolicy) - self.toolBar_fit.setIconSize(QtCore.QSize(24, 24)) - self.toolBar_fit.setObjectName("toolBar_fit") - BaseWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar_fit) self.toolBar_spectrum = QtWidgets.QToolBar(BaseWindow) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -496,7 +487,6 @@ class Ui_BaseWindow(object): self.toolbar_edit.addAction(self.actionShift) self.toolBar_nmr.addAction(self.t1action) self.toolBar_nmr.addAction(self.actionCalculateT1) - self.toolBar_fit.addAction(self.action_FitWidget) self.toolBar_spectrum.addAction(self.action_edit) self.toolBar_spectrum.addAction(self.actionPick_position) self.toolBar_data.addAction(self.actionConcatenate_sets) @@ -537,7 +527,6 @@ class Ui_BaseWindow(object): self.toolBar.setWindowTitle(_translate("BaseWindow", "Main")) self.toolbar_edit.setWindowTitle(_translate("BaseWindow", "Math")) self.toolBar_nmr.setWindowTitle(_translate("BaseWindow", "NMR")) - self.toolBar_fit.setWindowTitle(_translate("BaseWindow", "Fit")) self.toolBar_spectrum.setWindowTitle(_translate("BaseWindow", "Spectrum")) self.toolBar_data.setWindowTitle(_translate("BaseWindow", "Data")) self.action_close.setText(_translate("BaseWindow", "&Quit")) diff --git a/src/gui_qt/_py/fitmodelwidget.py b/src/gui_qt/_py/fitmodelwidget.py index f183f36..8664646 100644 --- a/src/gui_qt/_py/fitmodelwidget.py +++ b/src/gui_qt/_py/fitmodelwidget.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -42,6 +42,7 @@ class Ui_FitParameter(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth()) self.parameter_line.setSizePolicy(sizePolicy) + self.parameter_line.setMaximumSize(QtCore.QSize(160, 16777215)) self.parameter_line.setText("") self.parameter_line.setObjectName("parameter_line") self.horizontalLayout_2.addWidget(self.parameter_line) @@ -51,6 +52,9 @@ class Ui_FitParameter(object): self.global_checkbox = QtWidgets.QCheckBox(FitParameter) self.global_checkbox.setObjectName("global_checkbox") self.horizontalLayout_2.addWidget(self.global_checkbox) + self.reset_button = QtWidgets.QPushButton(FitParameter) + self.reset_button.setObjectName("reset_button") + self.horizontalLayout_2.addWidget(self.reset_button) self.verticalLayout.addLayout(self.horizontalLayout_2) self.frame = QtWidgets.QFrame(FitParameter) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) @@ -82,6 +86,7 @@ class Ui_FitParameter(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth()) self.lineEdit.setSizePolicy(sizePolicy) + self.lineEdit.setMaximumSize(QtCore.QSize(100, 16777215)) self.lineEdit.setText("") self.lineEdit.setFrame(True) self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) @@ -100,6 +105,7 @@ class Ui_FitParameter(object): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lineEdit_2.sizePolicy().hasHeightForWidth()) self.lineEdit_2.setSizePolicy(sizePolicy) + self.lineEdit_2.setMaximumSize(QtCore.QSize(100, 16777215)) self.lineEdit_2.setText("") self.lineEdit_2.setFrame(True) self.lineEdit_2.setObjectName("lineEdit_2") @@ -122,6 +128,7 @@ class Ui_FitParameter(object): self.parameter_line.setPlaceholderText(_translate("FitParameter", "0")) self.fixed_check.setText(_translate("FitParameter", "Fix")) self.global_checkbox.setText(_translate("FitParameter", "Global")) + self.reset_button.setText(_translate("FitParameter", "Use global")) self.lineEdit.setToolTip(_translate("FitParameter", "

Lower bound. Same bound is used for all data. Leave empty for no boundary condition.

")) self.label_3.setText(_translate("FitParameter", "Textlabel")) self.lineEdit_2.setToolTip(_translate("FitParameter", "

Upper bound. Same bound is used for all data. Leave empty for no boundary condition.

")) diff --git a/src/gui_qt/fit/fit_forms.py b/src/gui_qt/fit/fit_forms.py index 4b696c5..60c3191 100644 --- a/src/gui_qt/fit/fit_forms.py +++ b/src/gui_qt/fit/fit_forms.py @@ -1,138 +1,11 @@ from __future__ import annotations -from nmreval.utils.text import convert - from ..Qt import QtCore, QtWidgets, QtGui -from .._py.fitmodelwidget import Ui_FitParameter from .._py.save_fitmodel_dialog import Ui_SaveDialog from ..lib.iconloading import get_icon from ..lib.tables import TableWidget -class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter): - """ - Widget to show a global parameter - """ - - value_requested = QtCore.pyqtSignal(object) - value_changed = QtCore.pyqtSignal(str) - state_changed = QtCore.pyqtSignal() - replace_single_value = QtCore.pyqtSignal(object) - - def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False): - super().__init__(parent) - self.setupUi(self) - - self.name = label - - self.parametername.setText(convert(label) + ' ') - - self.parameter_line.setText('1') - self.parameter_line.setMaximumWidth(160) - self.lineEdit.setMaximumWidth(100) - self.lineEdit_2.setMaximumWidth(100) - - self.label_3.setText(f'< {convert(label)} <') - - self.checkBox.stateChanged.connect(self.enableBounds) - self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit()) - self.parameter_line.editingFinished.connect(self.update_parameter) - self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self)) - self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None)) - self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text())) - self.fixed_check.toggled.connect(self.set_fixed) - - if fixed: - self.fixed_check.hide() - - self.parameter_pos = None - self.func_idx = None - - self._linetext = '1' - - self.menu = QtWidgets.QMenu(self) - - def set_parameter_string(self, p: str): - self.parameter_line.setText(p) - self.parameter_line.setToolTip(p) - - def set_bounds(self, lb: float, ub: float, cbox: bool = True): - self.checkBox.setCheckState(QtCore.Qt.Checked if cbox else QtCore.Qt.Unchecked) - for val, bds_line in [(lb, self.lineEdit), (ub, self.lineEdit_2)]: - if val is not None: - bds_line.setText(str(val)) - else: - bds_line.setText('') - - def enableBounds(self, value: int): - self.lineEdit.setEnabled(value == 2) - self.lineEdit_2.setEnabled(value == 2) - - def set_parameter(self, p: float | None, bds: tuple[float, float, bool] = None, - fixed: bool = None, glob: bool = None): - ptext = f'{p:.4g}' - - self.set_parameter_string(ptext) - - if bds is not None: - self.set_bounds(*bds) - - if fixed is not None: - self.fixed_check.setCheckState(QtCore.Qt.CheckState.Unchecked if fixed else QtCore.Qt.CheckState.Checked) - - if glob is not None: - self.global_checkbox.setCheckState(QtCore.Qt.CheckState.Checked if glob else QtCore.Qt.CheckState.Unchecked) - - def get_parameter(self): - try: - p = float(self.parameter_line.text().replace(',', '.')) - except ValueError: - p = self.parameter_line.text().replace(',', '.') - - if self.checkBox.isChecked(): - lb_text = self.lineEdit.text() - lb = None - if lb_text: - try: - lb = float(lb_text.replace(',', '.')) - except ValueError: - lb = lb_text - - ub_text = self.lineEdit_2.text() - rb = None - if ub_text: - try: - rb = float(ub_text.replace(',', '.')) - except ValueError: - rb = ub_text - else: - lb = rb = None - - bounds = (lb, rb) - - return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked() - - @QtCore.pyqtSlot(bool) - def set_fixed(self, state: bool): - # self.global_checkbox.setVisible(not state) - self.frame.setVisible(not state) - - @QtCore.pyqtSlot() - def update_parameter(self): - new_value = self.parameter_line.text() - if not new_value: - self.parameter_line.setText('1') - - try: - float(new_value) - is_text = False - except ValueError: - is_text = True - self.global_checkbox.setCheckState(False) - - self.set_fixed(is_text or self.fixed_check.isChecked()) - - class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog): def __init__(self, types=None, parent=None): super().__init__(parent=parent) @@ -172,30 +45,37 @@ class FitModelTree(QtWidgets.QTreeWidget): treeChanged = QtCore.pyqtSignal() itemRemoved = QtCore.pyqtSignal(int) - counterRole = QtCore.Qt.UserRole + 1 - operatorRole = QtCore.Qt.UserRole + 2 + counterRole = QtCore.Qt.ItemDataRole.UserRole + 1 + operatorRole = QtCore.Qt.ItemDataRole.UserRole + 2 def __init__(self, parent=None): super().__init__(parent=parent) self.setHeaderHidden(True) self.setDragEnabled(True) self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove) - self.setDefaultDropAction(QtCore.Qt.MoveAction) + self.setDefaultDropAction(QtCore.Qt.DropAction.MoveAction) self.itemSelectionChanged.connect(lambda: self.treeChanged.emit()) def keyPressEvent(self, evt): - operators = [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Asterisk, - QtCore.Qt.Key_Minus, QtCore.Qt.Key_Slash] + operators = [ + QtCore.Qt.Key.Key_Plus, + QtCore.Qt.Key.Key_Asterisk, + QtCore.Qt.Key.Key_Minus, + QtCore.Qt.Key.Key_Slash + ] - if evt.key() == QtCore.Qt.Key_Delete: + if evt.key() == QtCore.Qt.Key.Key_Delete: for item in self.selectedItems(): self.remove_function(item) - elif evt.key() == QtCore.Qt.Key_Space: + elif evt.key() == QtCore.Qt.Key.Key_Space: for item in self.treeWidget.selectedItems(): - item.setCheckState(0, QtCore.Qt.Checked) if item.checkState( - 0) == QtCore.Qt.Unchecked else item.setCheckState(0, QtCore.Qt.Unchecked) + cs = item.checkState(0) + if cs == QtCore.Qt.CheckState.Unchecked: + item.setCheckState(0, QtCore.Qt.CheckState.Checked) + else: + item.setCheckState(0, QtCore.Qt.CheckState.Unchecked) elif evt.key() in operators: idx = operators.index(evt.key()) @@ -246,7 +126,7 @@ class FitModelTree(QtWidgets.QTreeWidget): color = QtGui.QColor(color) it = QtWidgets.QTreeWidgetItem() - it.setData(0, QtCore.Qt.UserRole, idx) + it.setData(0, QtCore.Qt.ItemDataRole.UserRole, idx) it.setData(0, self.counterRole, cnt) it.setData(0, self.operatorRole, op) it.setText(0, name) @@ -257,7 +137,7 @@ class FitModelTree(QtWidgets.QTreeWidget): it.setForeground(0, QtGui.QBrush(color)) it.setIcon(0, get_icon(self.icons[op])) - it.setCheckState(0, QtCore.Qt.Checked if active else QtCore.Qt.Unchecked) + it.setCheckState(0, QtCore.Qt.CheckState.Checked if active else QtCore.Qt.CheckState.Unchecked) if parent is None: self.addTopLevelItem(it) @@ -277,7 +157,7 @@ class FitModelTree(QtWidgets.QTreeWidget): def get_selected(self): try: it = self.selectedItems()[0] - function_nr = it.data(0, QtCore.Qt.UserRole) + function_nr = it.data(0, QtCore.Qt.ItemDataRole.UserRole) idx = it.data(0, self.counterRole) except IndexError: @@ -300,10 +180,10 @@ class FitModelTree(QtWidgets.QTreeWidget): it = parent.child(i) child = { - 'idx': it.data(0, QtCore.Qt.UserRole), + 'idx': it.data(0, QtCore.Qt.ItemDataRole.UserRole), 'op': it.data(0, self.operatorRole), 'pos': pos, - 'active': (it.checkState(0) == QtCore.Qt.Checked), + 'active': (it.checkState(0) == QtCore.Qt.CheckState.Checked), 'children': [] } @@ -371,8 +251,8 @@ class FitTableWidget(TableWidget): for (sid, name) in set_ids: item = QtWidgets.QTableWidgetItem(name) - item.setCheckState(QtCore.Qt.Checked) - item.setData(QtCore.Qt.UserRole+1, sid) + item.setCheckState(QtCore.Qt.CheckState.Checked) + item.setData(QtCore.Qt.ItemDataRole.UserRole+1, sid) row = self.rowCount() self.setRowCount(row+1) self.setItem(row, 0, item) @@ -390,15 +270,15 @@ class FitTableWidget(TableWidget): for i in range(self.rowCount()): item = self.item(i, 0) - if item.checkState() == QtCore.Qt.Checked: + if item.checkState() == QtCore.Qt.CheckState.Checked: mod = self.cellWidget(i, 1).currentData() if mod is None: mod = default if include_name: - arg = (item.data(QtCore.Qt.UserRole+1), item.text()) + arg = (item.data(QtCore.Qt.ItemDataRole.UserRole+1), item.text()) else: - arg = item.data(QtCore.Qt.UserRole+1) + arg = item.data(QtCore.Qt.ItemDataRole.UserRole+1) if mod not in data: data[mod] = [] @@ -411,8 +291,8 @@ class FitTableWidget(TableWidget): for i in range(self.rowCount()): item = self.item(i, 0) if include_name: - ret_val.append((item.data(QtCore.Qt.UserRole+1), item.text())) + ret_val.append((item.data(QtCore.Qt.ItemDataRole.UserRole+1), item.text())) else: - ret_val.append(item.data(QtCore.Qt.UserRole+1)) + ret_val.append(item.data(QtCore.Qt.ItemDataRole.UserRole+1)) return ret_val diff --git a/src/gui_qt/fit/fit_parameter.py b/src/gui_qt/fit/fit_parameter.py index e4a72d2..7fd58e7 100644 --- a/src/gui_qt/fit/fit_parameter.py +++ b/src/gui_qt/fit/fit_parameter.py @@ -1,14 +1,12 @@ from __future__ import annotations -from typing import Optional - from nmreval.fit.parameter import Parameter from nmreval.utils.text import convert from ..Qt import QtWidgets, QtCore, QtGui from .._py.fitfuncwidget import Ui_FormFit +from .._py.fitmodelwidget import Ui_FitParameter from ..lib.forms import SelectionWidget -from .fit_forms import FitModelWidget class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): @@ -30,16 +28,15 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): self.scrollwidget2.setLayout(QtWidgets.QVBoxLayout()) def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent): + modifiers = QtCore.Qt.KeyboardModifier.ControlModifier | QtCore.Qt.KeyboardModifier.ShiftModifier if isinstance(evt, QtGui.QKeyEvent): - if (evt.key() == QtCore.Qt.Key_Right) and \ - (evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier): + if (evt.key() == QtCore.Qt.Key.Key_Right) and (evt.modifiers() == modifiers): self.change_single_parameter(src.value, sender=src) self.select_next_preview(1) return True - elif (evt.key() == QtCore.Qt.Key_Left) and \ - (evt.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier): + elif (evt.key() == QtCore.Qt.Key.Key_Left) and (evt.modifiers() == modifiers): self.change_single_parameter(src.value, sender=src) self.select_next_preview(-1) @@ -65,7 +62,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): self.glob_values = [1] * len(func.params) for k, v in enumerate(func.params): - widgt = FitModelWidget(label=v, parent=self.scrollwidget) + widgt = ParameterGlobalWidget(label=v, parent=self.scrollwidget) widgt.parameter_pos = k widgt.func_idx = idx try: @@ -95,7 +92,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): for w1, w2 in zip(self.global_parameter, self.data_parameter): w1.parametername.setFixedSize(self.max_width) w1.checkBox.setFixedSize(self.max_width) - w2.label.setFixedSize(self.max_width) + w2.parametername.setFixedSize(self.max_width) if hasattr(func, 'choices') and func.choices is not None: cbox = func.choices @@ -175,7 +172,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): # disable single parameter if it is set global, enable if global is unset widget = self.sender() idx = self.global_parameter.index(widget) - enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked) + enable = (widget.global_checkbox.checkState() == QtCore.Qt.CheckState.Unchecked) self.data_parameter[idx].setEnabled(enable) def select_next_preview(self, direction): @@ -215,7 +212,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): param_general = [] for g in self.global_parameter: - if isinstance(g, FitModelWidget): + if isinstance(g, ParameterGlobalWidget): p_i, bds_i, fixed_i, global_i = g.get_parameter() parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i) param_general.append(parameter_i) @@ -236,7 +233,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): p = [] for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)): - if isinstance(g, FitModelWidget): + if isinstance(g, ParameterGlobalWidget): if (p_i is None) or is_global[i]: # set has no oen value p.append(param_general[i].copy()) @@ -293,60 +290,166 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): return param_len -class ParameterSingleWidget(QtWidgets.QWidget): +class ParameterSingleWidget(QtWidgets.QWidget, Ui_FitParameter): valueChanged = QtCore.pyqtSignal(object) removeSingleValue = QtCore.pyqtSignal() def __init__(self, name: str, parent=None): super().__init__(parent=parent) - - self._init_ui() + self.setupUi(self) self.name = name - self.label.setText(convert(name)) - self.label.setToolTip('If this is bold then this parameter is only for this data. ' - 'Otherwise, the general parameter is used and displayed') + self.parametername.setText(convert(name)) + self.parametername.setToolTip('If this is bold then this parameter is only for this data. ' + 'Otherwise, the general parameter is used and displayed') - # self.value_line.setValidator(QtGui.QDoubleValidator()) - self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0) + self.parameter_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0) self.reset_button.clicked.connect(lambda x: self.removeSingleValue.emit()) - def _init_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(2, 2, 2, 2) - layout.setSpacing(2) - - self.label = QtWidgets.QLabel(self) - layout.addWidget(self.label) - - layout.addSpacerItem(QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) - - self.value_line = QtWidgets.QLineEdit(self) - self.value_line.textEdited.connect(lambda x: self.show_as_local_parameter(True)) - layout.addWidget(self.value_line) - - self.reset_button = QtWidgets.QToolButton(self) - self.reset_button.setText('Use global') - self.reset_button.clicked.connect(lambda: self.show_as_local_parameter(False)) - layout.addWidget(self.reset_button) - - self.setLayout(layout) + self.global_checkbox.setVisible(False) @property def value(self) -> float: try: - return float(self.value_line.text().replace(',', '.')) + return float(self.parameter_line.text().replace(',', '.')) except ValueError: return 0.0 @value.setter def value(self, val): # self.value_line.setText(f'{float(val):.5g}') - self.value_line.setText(f'{val}') - self.value_line.setCursorPosition(0) + self.parameter_line.setText(f'{val}') + self.parameter_line.setCursorPosition(0) def show_as_local_parameter(self, is_local: bool): if is_local: - self.label.setStyleSheet('font-weight: bold;') + self.parametername.setStyleSheet('font-weight: bold;') else: - self.label.setStyleSheet('') + self.parametername.setStyleSheet('') + + +class ParameterGlobalWidget(QtWidgets.QWidget, Ui_FitParameter): + """ + Widget to show a global parameter + """ + + value_requested = QtCore.pyqtSignal(object) + value_changed = QtCore.pyqtSignal(str) + state_changed = QtCore.pyqtSignal() + replace_single_value = QtCore.pyqtSignal(object) + + def __init__(self, label: str = 'Fitparameter', parent=None, fixed: bool = False): + super().__init__(parent) + self.setupUi(self) + + self.name = label + self.reset_button.setVisible(False) + + self.parametername.setText(convert(label) + ' ') + + self.parameter_line.setText('1') + self.parameter_line.setMaximumWidth(160) + self.lineEdit.setMaximumWidth(100) + self.lineEdit_2.setMaximumWidth(100) + + self.label_3.setText(f'< {convert(label)} <') + + self.checkBox.stateChanged.connect(self.enableBounds) + self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit()) + self.parameter_line.editingFinished.connect(self.update_parameter) + self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self)) + self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None)) + self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text())) + self.fixed_check.toggled.connect(self.set_fixed) + + if fixed: + self.fixed_check.hide() + + self.reset_button.setVisible(False) + + self.parameter_pos = None + self.func_idx = None + + self._linetext = '1' + + self.menu = QtWidgets.QMenu(self) + + def set_parameter_string(self, p: str): + self.parameter_line.setText(p) + self.parameter_line.setToolTip(p) + + def set_bounds(self, lb: float, ub: float, cbox: bool = True): + self.checkBox.setCheckState(QtCore.Qt.CheckState.Checked if cbox else QtCore.Qt.CheckState.Unchecked) + for val, bds_line in [(lb, self.lineEdit), (ub, self.lineEdit_2)]: + if val is not None: + bds_line.setText(str(val)) + else: + bds_line.setText('') + + def enableBounds(self, value: int): + self.lineEdit.setEnabled(value == 2) + self.lineEdit_2.setEnabled(value == 2) + + def set_parameter(self, p: float | None, bds: tuple[float, float, bool] = None, + fixed: bool = None, glob: bool = None): + ptext = f'{p:.4g}' + + self.set_parameter_string(ptext) + + if bds is not None: + self.set_bounds(*bds) + + if fixed is not None: + self.fixed_check.setCheckState(QtCore.Qt.CheckState.Unchecked if fixed else QtCore.Qt.CheckState.Checked) + + if glob is not None: + self.global_checkbox.setCheckState(QtCore.Qt.CheckState.Checked if glob else QtCore.Qt.CheckState.Unchecked) + + def get_parameter(self): + try: + p = float(self.parameter_line.text().replace(',', '.')) + except ValueError: + p = self.parameter_line.text().replace(',', '.') + + if self.checkBox.isChecked(): + lb_text = self.lineEdit.text() + lb = None + if lb_text: + try: + lb = float(lb_text.replace(',', '.')) + except ValueError: + lb = lb_text + + ub_text = self.lineEdit_2.text() + rb = None + if ub_text: + try: + rb = float(ub_text.replace(',', '.')) + except ValueError: + rb = ub_text + else: + lb = rb = None + + bounds = (lb, rb) + + return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked() + + @QtCore.pyqtSlot(bool) + def set_fixed(self, state: bool): + # self.global_checkbox.setVisible(not state) + self.frame.setVisible(not state) + + @QtCore.pyqtSlot() + def update_parameter(self): + new_value = self.parameter_line.text() + if not new_value: + self.parameter_line.setText('1') + + try: + float(new_value) + is_text = False + except ValueError: + is_text = True + self.global_checkbox.setCheckState(False) + + self.set_fixed(is_text or self.fixed_check.isChecked()) diff --git a/src/gui_qt/fit/fit_toolbar.py b/src/gui_qt/fit/fit_toolbar.py new file mode 100644 index 0000000..89d13ff --- /dev/null +++ b/src/gui_qt/fit/fit_toolbar.py @@ -0,0 +1,78 @@ +from ..Qt import QtWidgets, QtGui, QtCore +from ..lib.iconloading import get_icon + + +class FitToolbar(QtWidgets.QToolBar): + def __init__( + self, + fitaction: QtWidgets.QAction, + limit_menu: QtWidgets.QMenu, + region, + parent=None, + ): + super().__init__(parent=parent) + + self.fit_action = fitaction + self.region = region + self.addAction(fitaction) + + self.fitlim_button = QtWidgets.QToolButton(self) + self.fitlim_button.setMenu(limit_menu) + self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup) + self.fitlim_button.setIcon(get_icon('fit_region')) + self.addWidget(self.fitlim_button) + + self.label = QtWidgets.QLabel(self) + self.label.setText('L: ') + self.addWidget(self.label) + self.label.setEnabled(False) + + self.lineedit = QtWidgets.QLineEdit(self) + self.lineedit.setValidator(QtGui.QDoubleValidator()) + self.lineedit.setMaximumWidth(92) + self.addWidget(self.lineedit) + self.lineedit.setEnabled(False) + + self.label2 = QtWidgets.QLabel(self) + self.label2.setText(' R: ') + self.addWidget(self.label2) + self.label2.setEnabled(False) + + self.lineedit2 = QtWidgets.QLineEdit(self) + self.lineedit2.setValidator(QtGui.QDoubleValidator()) + self.addWidget(self.lineedit2) + self.lineedit2.setMaximumWidth(92) + self.lineedit2.setEnabled(False) + + self.limit_group = QtWidgets.QActionGroup(self) + for ac in limit_menu.actions(): + self.limit_group.addAction(ac) + + self.limit_group.triggered.connect(self.change_limit_type) + + self.region.sigRegionChanged.connect(self.change_labels) + + self.lineedit.textChanged.connect(self.move_region) + self.lineedit2.textChanged.connect(self.move_region) + + @QtCore.pyqtSlot(QtWidgets.QAction) + def change_limit_type(self, action: QtWidgets.QAction): + is_custom = action.text() == 'Custom' + + for w in [self.label, self.label2, self.lineedit, self.lineedit2]: + w.setEnabled(is_custom) + + def change_labels(self): + r = self.region.getRegion() + self.lineedit.blockSignals(True) + self.lineedit.setText(f'{r[0]:.4g}') + self.lineedit.blockSignals(False) + + self.lineedit2.blockSignals(True) + self.lineedit2.setText(f'{r[1]:.4g}') + self.lineedit2.blockSignals(False) + + def move_region(self): + r_min = self.lineedit.text() + r_max = self.lineedit2.text() + self.region.setRegion((float(r_min), float(r_max)), use_log=True) diff --git a/src/gui_qt/fit/fitfunction.py b/src/gui_qt/fit/fitfunction.py index 54ce567..b524521 100644 --- a/src/gui_qt/fit/fitfunction.py +++ b/src/gui_qt/fit/fitfunction.py @@ -165,7 +165,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form): self.iscomplex = False while iterator.value(): item = iterator.value() - f = self.functions[item.data(0, QtCore.Qt.UserRole)] + f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)] if hasattr(f, 'iscomplex') and f.iscomplex: self.iscomplex = True break @@ -226,7 +226,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form): iterator = QtWidgets.QTreeWidgetItemIterator(self.functree) while iterator.value(): item = iterator.value() - f = self.functions[item.data(0, QtCore.Qt.UserRole)] + f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)] cnt = item.data(0, self.functree.counterRole) all_parameters[f'{f.name}_{cnt}'] = [(convert(pp, new='str'), (cnt, i)) for i, pp in enumerate(f.params)] @@ -240,7 +240,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form): while iterator.value(): item = iterator.value() if item.checkState(0) != QtCore.Qt.CheckState.Unchecked: - f = self.functions[item.data(0, QtCore.Qt.UserRole)] + f = self.functions[item.data(0, QtCore.Qt.ItemDataRole.UserRole)] if hasattr(f, 'iscomplex') and f.iscomplex: iscomplex = True break diff --git a/src/gui_qt/fit/fitwindow.py b/src/gui_qt/fit/fitwindow.py index ac1f11e..e71e2be 100644 --- a/src/gui_qt/fit/fitwindow.py +++ b/src/gui_qt/fit/fitwindow.py @@ -9,7 +9,6 @@ import numpy as np from pyqtgraph import mkPen from nmreval.fit._meta import MultiModel, ModelFactory -from nmreval.fit.data import Data from nmreval.fit.model import Model from nmreval.fit.parameter import Parameters from nmreval.fit.result import FitResult @@ -42,8 +41,8 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): self._management = mgmt self._current_model = next(QFitDialog.model_cnt) - self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole) - self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.UserRole) + self.show_combobox.setItemData(0, self._current_model, QtCore.Qt.ItemDataRole.UserRole) + self.default_combobox.setItemData(0, self._current_model, QtCore.Qt.ItemDataRole.UserRole) self.data_table = FitTableWidget(self.data_widget) self.data_widget.addWidget(self.data_table) @@ -150,9 +149,9 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): # deselect all fit sets for i in range(self.data_table.rowCount()): - data_id = self.data_table.item(i, 0).data(QtCore.Qt.UserRole+1) + data_id = self.data_table.item(i, 0).data(QtCore.Qt.ItemDataRole.UserRole+1) if self._management[data_id].mode == 'fit' or self._management[data_id].has_relation(Relations.isFitPartOf): - self.data_table.item(i, 0).setCheckState(QtCore.Qt.Unchecked) + self.data_table.item(i, 0).setCheckState(QtCore.Qt.CheckState.Unchecked) if self.models: for m in self.models.keys(): @@ -176,7 +175,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): self.default_combobox.addItem('Model '+idx, userData=idx) self.show_combobox.addItem('Model '+idx, userData=idx) - self.show_combobox.setItemData(self.show_combobox.count()-1, idx, QtCore.Qt.UserRole) + self.show_combobox.setItemData(self.show_combobox.count()-1, idx, QtCore.Qt.ItemDataRole.UserRole) self.show_combobox.setCurrentIndex(self.show_combobox.count()-1) self._current_model = idx @@ -190,7 +189,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): self.get_functions() self.functionwidget.clear() - self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.UserRole) + self._current_model = self.show_combobox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole) if self._current_model in self.models and len(self.models[self._current_model]): for el in self.models[self._current_model]: self.functionwidget.add_function(**el) diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 39fdbea..6091d9e 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -17,6 +17,7 @@ from ..Qt import QtGui, QtPrintSupport from ..data.shift_graphs import QShift from ..data.signaledit import QPreviewDialog, QBaselineDialog from ..dsc.glass_dialog import TgCalculator +from ..fit.fit_toolbar import FitToolbar from ..fit.result import FitExtension, QFitResult from ..graphs.graphwindow import QGraphWindow from ..graphs.movedialog import QMover @@ -97,12 +98,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.norm_toolbutton.setIcon(get_icon('normal')) self.toolbar_edit.addWidget(self.norm_toolbutton) - self.fitlim_button = QtWidgets.QToolButton(self) - self.fitlim_button.setMenu(self.menuLimits) - self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup) - self.fitlim_button.setIcon(get_icon('fit_region')) - self.toolBar_fit.addWidget(self.fitlim_button) - while self.tabWidget.count() > 2: self.tabWidget.removeTab(self.tabWidget.count()-1) @@ -123,6 +118,9 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.fitregion = RegionItem() self._fit_plot_id = None + self.fit_toolbar = FitToolbar(self.action_FitWidget, self.menuLimits, self.fitregion, self) + self.addToolBar(self.fit_toolbar) + self.setGeometry(QtWidgets.QStyle.alignedRect( QtCore.Qt.LayoutDirection.LeftToRight, QtCore.Qt.AlignmentFlag.AlignCenter, @@ -138,11 +136,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.ac_group.addAction(self.action_nm_fit) self.ac_group.addAction(self.action_odr_fit) - self.ac_group2 = QtWidgets.QActionGroup(self) - self.ac_group2.addAction(self.action_no_range) - self.ac_group2.addAction(self.action_x_range) - self.ac_group2.addAction(self.action_custom_range) - def _init_signals(self): self.actionRedo = self.management.undostack.createRedoAction(self) icon = QtGui.QIcon.fromTheme("edit-redo") @@ -158,7 +151,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter) # noinspection PyUnresolvedReferences - self.ac_group2.triggered.connect(self.change_fit_limits) + self.fit_toolbar.limit_group.triggered.connect(self.change_fit_limits) self.t1action.triggered.connect(lambda: self._show_tab('t1_temp')) self.action_edit.triggered.connect(self.do_preview) diff --git a/src/resources/_ui/basewindow.ui b/src/resources/_ui/basewindow.ui index 8a5efaf..bf3de79 100644 --- a/src/resources/_ui/basewindow.ui +++ b/src/resources/_ui/basewindow.ui @@ -437,30 +437,6 @@ - - - - 0 - 0 - - - - Fit - - - - 24 - 24 - - - - TopToolBarArea - - - false - - - diff --git a/src/resources/_ui/fitmodelwidget.ui b/src/resources/_ui/fitmodelwidget.ui index ffc0b93..d36a6b9 100755 --- a/src/resources/_ui/fitmodelwidget.ui +++ b/src/resources/_ui/fitmodelwidget.ui @@ -67,6 +67,12 @@ 0 + + + 160 + 16777215 + + Initial values @@ -92,6 +98,13 @@ + + + + Use global + + + @@ -151,6 +164,12 @@ 0 + + + 100 + 16777215 + + <html><head/><body><p>Lower bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html> @@ -195,6 +214,12 @@ 0 + + + 100 + 16777215 + + <html><head/><body><p>Upper bound. Same bound is used for all data. Leave empty for no boundary condition.</p></body></html>