From 2c4f2f288ea12210839b2221a2326a8b07dc2913 Mon Sep 17 00:00:00 2001 From: dominik Date: Mon, 28 Mar 2022 16:26:10 +0200 Subject: [PATCH] Parameter are new initial values after fit --- __init__.py | 2 - doc/source/conf.py | 1 - nmreval/fit/_meta.py | 4 +- nmreval/gui_qt/_py/fitresult.py | 140 +++++----- nmreval/gui_qt/data/container.py | 38 +-- nmreval/gui_qt/data/datawidget/datawidget.py | 8 +- nmreval/gui_qt/fit/fit_forms.py | 31 ++- nmreval/gui_qt/fit/fit_parameter.py | 36 ++- nmreval/gui_qt/fit/fitwindow.py | 82 ++++-- nmreval/gui_qt/io/asciireader.py | 5 + nmreval/gui_qt/lib/__init__.py | 6 +- nmreval/gui_qt/lib/delegates.py | 6 +- nmreval/gui_qt/lib/forms.py | 17 +- nmreval/lib/colors.py | 45 +-- resources/_ui/fitresult.ui | 271 ++++++++++--------- 15 files changed, 398 insertions(+), 294 deletions(-) delete mode 100755 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100755 index 6860c71..0000000 --- a/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -from .version import __version__, __releasename__ diff --git a/doc/source/conf.py b/doc/source/conf.py index e516330..a23eb00 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -12,7 +12,6 @@ # import os import sys -import sphinx_bootstrap_theme sys.path.append('/autohome/dominik/nmreval') import nmreval diff --git a/nmreval/fit/_meta.py b/nmreval/fit/_meta.py index 42e1d3f..ad8ad9e 100644 --- a/nmreval/fit/_meta.py +++ b/nmreval/fit/_meta.py @@ -100,12 +100,12 @@ class MultiModel: if v.default is not Parameter.empty} for k, v in temp_dic.items(): - key_ = '%s_%d' % (k, idx) + key_ = f'{k}_{idx}' kw_dict[key_] = v self._fun_kwargs[key_] = v self._ext_int_kw[key_] = k - strcnt = '(%d)' % idx + strcnt = f'({idx})' self.params += [pp+strcnt for pp in func.params] self.name += func.name + strcnt diff --git a/nmreval/gui_qt/_py/fitresult.py b/nmreval/gui_qt/_py/fitresult.py index 386e538..bb2e98a 100644 --- a/nmreval/gui_qt/_py/fitresult.py +++ b/nmreval/gui_qt/_py/fitresult.py @@ -27,6 +27,69 @@ class Ui_Dialog(object): self.sets_comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToMinimumContentsLength) self.sets_comboBox.setObjectName("sets_comboBox") self.gridLayout.addWidget(self.sets_comboBox, 0, 0, 1, 1) + self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 2) + self.param_tableWidget = QtWidgets.QTableWidget(Dialog) + self.param_tableWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.param_tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) + self.param_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + self.param_tableWidget.setAlternatingRowColors(True) + self.param_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) + self.param_tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectColumns) + self.param_tableWidget.setShowGrid(False) + self.param_tableWidget.setColumnCount(0) + self.param_tableWidget.setObjectName("param_tableWidget") + self.param_tableWidget.setRowCount(0) + self.param_tableWidget.horizontalHeader().setStretchLastSection(False) + self.gridLayout.addWidget(self.param_tableWidget, 1, 0, 1, 1) + self.groupBox = QtWidgets.QGroupBox(Dialog) + self.groupBox.setObjectName("groupBox") + self.gridLayout_2 = QtWidgets.QGridLayout(self.groupBox) + self.gridLayout_2.setContentsMargins(3, 3, 3, 3) + self.gridLayout_2.setSpacing(3) + self.gridLayout_2.setObjectName("gridLayout_2") + 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, 1, 1, 1) + self.graph_comboBox = QtWidgets.QComboBox(self.groupBox) + self.graph_comboBox.setEnabled(False) + self.graph_comboBox.setObjectName("graph_comboBox") + self.gridLayout_2.addWidget(self.graph_comboBox, 1, 2, 1, 1) + self.curve_checkbox = QtWidgets.QCheckBox(self.groupBox) + self.curve_checkbox.setChecked(True) + self.curve_checkbox.setObjectName("curve_checkbox") + self.gridLayout_2.addWidget(self.curve_checkbox, 0, 0, 1, 1) + self.partial_checkBox = QtWidgets.QCheckBox(self.groupBox) + self.partial_checkBox.setObjectName("partial_checkBox") + self.gridLayout_2.addWidget(self.partial_checkBox, 1, 0, 1, 1) + self.parameter_checkbox = QtWidgets.QCheckBox(self.groupBox) + self.parameter_checkbox.setChecked(True) + self.parameter_checkbox.setObjectName("parameter_checkbox") + self.gridLayout_2.addWidget(self.parameter_checkbox, 0, 1, 1, 1) + self.gridLayout.addWidget(self.groupBox, 5, 0, 1, 2) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setSpacing(3) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog) + self.reject_fit_checkBox.setObjectName("reject_fit_checkBox") + self.horizontalLayout_2.addWidget(self.reject_fit_checkBox) + self.del_prev_checkBox = QtWidgets.QCheckBox(Dialog) + self.del_prev_checkBox.setObjectName("del_prev_checkBox") + self.horizontalLayout_2.addWidget(self.del_prev_checkBox) + self.gridLayout.addLayout(self.horizontalLayout_2, 2, 0, 1, 1) + self.line = QtWidgets.QFrame(Dialog) + self.line.setFrameShape(QtWidgets.QFrame.HLine) + self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line.setObjectName("line") + self.gridLayout.addWidget(self.line, 3, 0, 1, 2) self.stack = QtWidgets.QToolBox(Dialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -35,7 +98,7 @@ class Ui_Dialog(object): self.stack.setSizePolicy(sizePolicy) self.stack.setObjectName("stack") self.page = QtWidgets.QWidget() - self.page.setGeometry(QtCore.QRect(0, 0, 399, 414)) + self.page.setGeometry(QtCore.QRect(0, 0, 399, 346)) self.page.setObjectName("page") self.verticalLayout = QtWidgets.QVBoxLayout(self.page) self.verticalLayout.setContentsMargins(3, 3, 3, 3) @@ -50,7 +113,7 @@ class Ui_Dialog(object): self.verticalLayout.addWidget(self.logy_box) self.stack.addItem(self.page, "") self.page_2 = QtWidgets.QWidget() - self.page_2.setGeometry(QtCore.QRect(0, 0, 399, 414)) + self.page_2.setGeometry(QtCore.QRect(0, 0, 399, 346)) self.page_2.setObjectName("page_2") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.page_2) self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) @@ -67,7 +130,7 @@ class Ui_Dialog(object): self.verticalLayout_2.addWidget(self.stats_tableWidget) self.stack.addItem(self.page_2, "") self.page_3 = QtWidgets.QWidget() - self.page_3.setGeometry(QtCore.QRect(0, 0, 399, 414)) + self.page_3.setGeometry(QtCore.QRect(0, 0, 399, 346)) self.page_3.setObjectName("page_3") self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.page_3) self.verticalLayout_3.setContentsMargins(3, 3, 3, 3) @@ -92,64 +155,7 @@ class Ui_Dialog(object): self.corr_tableWidget.verticalHeader().setVisible(False) self.verticalLayout_3.addWidget(self.corr_tableWidget) self.stack.addItem(self.page_3, "") - self.gridLayout.addWidget(self.stack, 0, 1, 4, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setSpacing(3) - self.horizontalLayout.setObjectName("horizontalLayout") - self.partial_checkBox = QtWidgets.QCheckBox(Dialog) - self.partial_checkBox.setObjectName("partial_checkBox") - self.horizontalLayout.addWidget(self.partial_checkBox) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem) - self.label_2 = QtWidgets.QLabel(Dialog) - self.label_2.setObjectName("label_2") - self.horizontalLayout.addWidget(self.label_2) - self.graph_checkBox = QtWidgets.QCheckBox(Dialog) - 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.horizontalLayout.addWidget(self.graph_checkBox) - self.graph_comboBox = QtWidgets.QComboBox(Dialog) - self.graph_comboBox.setEnabled(False) - self.graph_comboBox.setObjectName("graph_comboBox") - self.horizontalLayout.addWidget(self.graph_comboBox) - self.gridLayout.addLayout(self.horizontalLayout, 5, 0, 1, 2) - self.line_2 = QtWidgets.QFrame(Dialog) - self.line_2.setFrameShape(QtWidgets.QFrame.HLine) - self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) - self.line_2.setObjectName("line_2") - self.gridLayout.addWidget(self.line_2, 3, 0, 1, 1) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setSpacing(3) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.reject_fit_checkBox = QtWidgets.QCheckBox(Dialog) - self.reject_fit_checkBox.setObjectName("reject_fit_checkBox") - self.horizontalLayout_2.addWidget(self.reject_fit_checkBox) - self.del_prev_checkBox = QtWidgets.QCheckBox(Dialog) - self.del_prev_checkBox.setObjectName("del_prev_checkBox") - self.horizontalLayout_2.addWidget(self.del_prev_checkBox) - self.gridLayout.addLayout(self.horizontalLayout_2, 2, 0, 1, 1) - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry) - self.buttonBox.setObjectName("buttonBox") - self.gridLayout.addWidget(self.buttonBox, 6, 0, 1, 2) - self.param_tableWidget = QtWidgets.QTableWidget(Dialog) - self.param_tableWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) - self.param_tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) - self.param_tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) - self.param_tableWidget.setAlternatingRowColors(True) - self.param_tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) - self.param_tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectColumns) - self.param_tableWidget.setShowGrid(False) - self.param_tableWidget.setColumnCount(0) - self.param_tableWidget.setObjectName("param_tableWidget") - self.param_tableWidget.setRowCount(0) - self.param_tableWidget.horizontalHeader().setStretchLastSection(False) - self.gridLayout.addWidget(self.param_tableWidget, 1, 0, 1, 1) + self.gridLayout.addWidget(self.stack, 0, 1, 3, 1) self.retranslateUi(Dialog) self.stack.setCurrentIndex(0) @@ -159,6 +165,13 @@ class Ui_Dialog(object): def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Fit results")) + self.groupBox.setTitle(_translate("Dialog", "Output")) + self.graph_checkBox.setText(_translate("Dialog", "New graph")) + 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")) + self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit")) + self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits")) self.logy_box.setText(_translate("Dialog", "logarithmic y axis")) self.stack.setItemText(self.stack.indexOf(self.page), _translate("Dialog", "Plot")) self.stack.setItemText(self.stack.indexOf(self.page_2), _translate("Dialog", "Statistics")) @@ -171,10 +184,5 @@ class Ui_Dialog(object): item = self.corr_tableWidget.horizontalHeaderItem(3) item.setText(_translate("Dialog", "Partial Corr.")) self.stack.setItemText(self.stack.indexOf(self.page_3), _translate("Dialog", "Correlations")) - self.partial_checkBox.setText(_translate("Dialog", "Plot partial functions")) - self.label_2.setText(_translate("Dialog", "Location of parameters:")) - self.graph_checkBox.setText(_translate("Dialog", "New graph")) - self.reject_fit_checkBox.setText(_translate("Dialog", "Reject this fit")) - self.del_prev_checkBox.setText(_translate("Dialog", "Delete previous fits")) from ..lib.forms import ElideComboBox from pyqtgraph import GraphicsLayoutWidget diff --git a/nmreval/gui_qt/data/container.py b/nmreval/gui_qt/data/container.py index e9302e7..3aae696 100644 --- a/nmreval/gui_qt/data/container.py +++ b/nmreval/gui_qt/data/container.py @@ -9,7 +9,7 @@ from ...data.points import Points from ...data.signals import Signal from ...utils.text import convert from ...data.bds import BDS -from ...lib.colors import Colors +from ...lib.colors import BaseColor, TUColors from ...lib.lines import LineStyle from ...lib.symbols import SymbolStyle, symbolcycle from ...data.nmr import Spectrum, FID @@ -24,7 +24,7 @@ class ExperimentContainer(QtCore.QObject): dataChanged = QtCore.pyqtSignal(str) labelChanged = QtCore.pyqtSignal(str, str) groupChanged = QtCore.pyqtSignal(str, str) - colors = cycle(Colors) + colors = cycle(TUColors) def __init__(self, identifier, data, **kwargs): super().__init__() @@ -63,25 +63,27 @@ class ExperimentContainer(QtCore.QObject): def __len__(self): return len(self._data) - def copy(self, full: bool = False): + def copy(self, full: bool = False, keep_color: bool = True): if full: - # pen_dict = { - # 'symbol': self.plot_real.symbol, - # 'symbolcolor': self.plot_real.symbolcolor, - # 'symbolsize': self.plot_real.symbolsize, - # 'linestyle': self.plot_real.linestyle, - # 'linecolor': self.plot_real.linecolor, - # 'linewidth': self.plot_real.linewidth, - # } + pen_dict = {} + if keep_color: + pen_dict = { + 'symbol': self.plot_real.symbol, + 'symbolcolor': self.plot_real.symbolcolor, + 'symbolsize': self.plot_real.symbolsize, + 'linestyle': self.plot_real.linestyle, + 'linecolor': self.plot_real.linecolor, + 'linewidth': self.plot_real.linewidth, + } - new_data = type(self)(str(self.id), self._data.copy(), manager=self._manager) + new_data = type(self)(str(self.id), self._data.copy(), manager=self._manager, **pen_dict) new_data.mode = self.mode - # if self.plot_imag is not None: - # new_data.plot_imag.set_symbol(symbol=self.plot_imag.symbol, size=self.plot_imag.symbolsize, - # color=self.plot_imag.symbolcolor) - # new_data.plot_imag.set_line(style=self.plot_imag.linestyle, width=self.plot_imag.linewidth, - # color=self.plot_imag.linecolor) + if keep_color and self.plot_imag is not None: + new_data.plot_imag.set_symbol(symbol=self.plot_imag.symbol, size=self.plot_imag.symbolsize, + color=self.plot_imag.symbolcolor) + new_data.plot_imag.set_line(style=self.plot_imag.linestyle, width=self.plot_imag.linewidth, + color=self.plot_imag.linecolor) return new_data @@ -548,7 +550,7 @@ class FitContainer(ExperimentContainer): def _init_plot(self, **kwargs): color = kwargs.get('color', (0, 0, 0)) - if isinstance(color, Colors): + if isinstance(color, BaseColor): color = color.rgb() self.plot_real = PlotItem(x=self._data.x, y=self._data.y.real, name=self.name, diff --git a/nmreval/gui_qt/data/datawidget/datawidget.py b/nmreval/gui_qt/data/datawidget/datawidget.py index 98c8440..68b5ec3 100644 --- a/nmreval/gui_qt/data/datawidget/datawidget.py +++ b/nmreval/gui_qt/data/datawidget/datawidget.py @@ -23,6 +23,7 @@ class DataTree(QtWidgets.QTreeWidget): self.invisibleRootItem().setFlags(self.invisibleRootItem().flags() ^ QtCore.Qt.ItemIsDropEnabled) self.itemChanged.connect(self.data_change) + self.itemClicked.connect(self.new_selection) self.setColumnCount(2) @@ -76,7 +77,7 @@ class DataTree(QtWidgets.QTreeWidget): break @QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem) - def data_change(self, item: QtWidgets.QTreeWidgetItem) -> (list, list): + def data_change(self, item: QtWidgets.QTreeWidgetItem) -> tuple[set, set]: idd = item.data(0, QtCore.Qt.UserRole) is_selected = item.checkState(0) == QtCore.Qt.Checked to_be_hidden = set() @@ -140,6 +141,11 @@ class DataTree(QtWidgets.QTreeWidget): return to_be_shown, to_be_hidden + @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)) + def dropEvent(self, evt: QtGui.QDropEvent): dropped_index = self.indexAt(evt.pos()) if not dropped_index.isValid(): diff --git a/nmreval/gui_qt/fit/fit_forms.py b/nmreval/gui_qt/fit/fit_forms.py index 958e883..6d8f25d 100644 --- a/nmreval/gui_qt/fit/fit_forms.py +++ b/nmreval/gui_qt/fit/fit_forms.py @@ -1,4 +1,6 @@ -from typing import Tuple, Union +from __future__ import annotations + +from typing import List, Tuple, Union from ...utils.text import convert from ..Qt import QtCore, QtWidgets, QtGui @@ -51,8 +53,8 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter): return convert(self.parametername.text().strip(), old='html', new='str') def set_parameter_string(self, p: str): - self.parameter_line.setText(str(p)) - self.parameter_line.setToolTip(str(p)) + 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) @@ -66,21 +68,25 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter): self.lineEdit.setEnabled(value == 2) self.lineEdit_2.setEnabled(value == 2) - def set_parameter(self, p: list, bds: Tuple[float, float, bool] = (None, None, False), - fixed: bool = False, glob: bool = False): + def set_parameter(self, p: float | None, bds: Tuple[float, float, bool] = None, + fixed: bool = None, glob: bool = None): if p is None: # bad hack: linked parameter return (None, linked parameter) # if p is None -> parameter is linked to argument given by bds self.link_parameter(linkto=bds) else: - ptext = ' '.join([f'{pp:.4g}' for pp in p]) + ptext = f'{p:.4g}' self.set_parameter_string(ptext) - self.set_bounds(*bds) + if bds is not None: + self.set_bounds(*bds) - self.fixed_check.setCheckState(QtCore.Qt.Unchecked if fixed else QtCore.Qt.Checked) - self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked) + if fixed is not None: + self.fixed_check.setCheckState(QtCore.Qt.Unchecked if fixed else QtCore.Qt.Checked) + + if glob is not None: + self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked) def get_parameter(self): if self.is_linked: @@ -323,7 +329,7 @@ class FitModelTree(QtWidgets.QTreeWidget): return function_nr, idx - def get_functions(self, full=True, parent=None, pos=-1, return_pos=False): + def get_functions(self, full: bool = True, pos: int = -1, return_pos: bool = False, parent=None): """ Create nested list of functions in tree. Parameters saved are idx (Index of function in list of all functions), cnt (counter of number to associate with functione values), ops (+, -, *, /), and maybe children. @@ -397,7 +403,7 @@ class FitTableWidget(QtWidgets.QTableWidget): self.hideColumn(1) self.resizeColumnToContents(0) - def load(self, set_ids: list): + def load(self, set_ids: List[str]): self.blockSignals(True) while self.rowCount(): @@ -419,8 +425,7 @@ class FitTableWidget(QtWidgets.QTableWidget): self.blockSignals(False) - def collect_data(self, default=None, include_name=False): - + def collect_data(self, default: str = None, include_name: bool = False) -> dict: data = {} for i in range(self.rowCount()): diff --git a/nmreval/gui_qt/fit/fit_parameter.py b/nmreval/gui_qt/fit/fit_parameter.py index e8f03c0..db57269 100644 --- a/nmreval/gui_qt/fit/fit_parameter.py +++ b/nmreval/gui_qt/fit/fit_parameter.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import List + from ...utils.text import convert from ..Qt import QtWidgets, QtCore, QtGui from .._py.fitfuncwidget import Ui_FormFit @@ -50,6 +54,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): self.comboBox.addItem(name, userData=sid) self._make_parameter(sid) self.comboBox.blockSignals(False) + self.change_data(0) def set_function(self, func, idx): self.func = func @@ -71,7 +76,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): if self.max_width.width() < size.width(): self.max_width = size - widgt.state_changed.connect(self.set_global) + widgt.state_changed.connect(self.make_global) widgt.value_requested.connect(self.look_for_value) widgt.value_changed.connect(self.change_global_parameter) @@ -115,12 +120,14 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): w.add_links(parameter) @QtCore.pyqtSlot(str) - def change_global_parameter(self, value: str): - idx = self.global_parameter.index(self.sender()) + def change_global_parameter(self, value: str, idx: int = None): + if idx is None: + idx = self.global_parameter.index(self.sender()) + self.glob_values[idx] = float(value) if self.data_values[self.comboBox.currentData()][idx] is None: self.data_parameter[idx].blockSignals(True) - self.data_parameter[idx].value = value + self.data_parameter[idx].value = float(value) self.data_parameter[idx].blockSignals(False) @QtCore.pyqtSlot(str, object) @@ -138,7 +145,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): idx = self.data_parameter.index(sender) self.data_values[self.comboBox.currentData()][idx] = value - def change_single_choice(self, argname, value, sender=None): + def change_single_choice(self, _, value, sender=None): if sender is None: sender = self.sender() idx = self.data_parameter.index(sender) @@ -149,7 +156,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): self.value_requested.emit(self.global_parameter.index(sender)) @QtCore.pyqtSlot() - def set_global(self): + def make_global(self): # disable single parameter if it is set global, enable if global is unset widget = self.sender() idx = self.global_parameter.index(widget) @@ -233,6 +240,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})') except TypeError: pass + try: if p[i] < lb[i]: raise ValueError(f'Parameter {g.name} is outside bounds ({lb[i]}, {ub[i]})') @@ -250,3 +258,19 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit): data_parameter[sid] = (p, kw_p) return data_parameter, lb, ub, is_fixed, global_p, is_linked + + def set_parameter(self, set_id: str | None, parameter: List[float]) -> int: + if set_id is None: + for val, g in zip(parameter, self.global_parameter): + if isinstance(g, SelectionWidget): + continue + g.set_parameter(val) + + else: + new_param = self.data_values[set_id] + for i in range(len(new_param)): + new_param[i] = parameter[i] + + self.change_data(self.comboBox.currentIndex()) + + return len(self.global_parameter) diff --git a/nmreval/gui_qt/fit/fitwindow.py b/nmreval/gui_qt/fit/fitwindow.py index c895c60..320e83b 100644 --- a/nmreval/gui_qt/fit/fitwindow.py +++ b/nmreval/gui_qt/fit/fitwindow.py @@ -1,5 +1,10 @@ +from __future__ import annotations + +from functools import reduce from itertools import count, cycle +from operator import add from string import ascii_letters +from typing import Dict, List, Tuple from pyqtgraph import mkPen @@ -9,6 +14,7 @@ from ..lib.pg_objects import PlotItem from ..Qt import QtGui, QtCore, QtWidgets from .._py.fitdialog import Ui_FitDialog from ...fit._meta import MultiModel, ModelFactory +from ...fit.result import FitResult class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): @@ -27,7 +33,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): self.parameters = {} self.preview_lines = [] self._current_function = None - self.function_widgets = {} + self.param_widgets = {} self._management = mgmt self._current_model = next(QFitDialog.model_cnt) @@ -63,10 +69,10 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): """ Remove function and children from tree and dictionary """ - w = self.function_widgets[idx] + w = self.param_widgets[idx] self.stackedWidget.removeWidget(w) w.deleteLater() - del self.function_widgets[idx] + del self.param_widgets[idx] if len(self.functionwidget) == 0: # empty model @@ -82,8 +88,8 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): """ Display parameter associated with selected function. """ - if function_id in self.function_widgets: - dialog = self.function_widgets[function_id] + if function_id in self.param_widgets: + dialog = self.param_widgets[function_id] else: # create new widget for function @@ -103,7 +109,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): dialog.value_requested.connect(self.look_value) self.stackedWidget.addWidget(dialog) - self.function_widgets[function_id] = dialog + self.param_widgets[function_id] = dialog self.stackedWidget.setCurrentWidget(dialog) @@ -114,13 +120,13 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): # show same tab (general parameter/Data parameter) tab_idx = 0 if self._current_function is not None: - tab_idx = self.function_widgets[self._current_function].tabWidget.currentIndex() + tab_idx = self.param_widgets[self._current_function].tabWidget.currentIndex() dialog.tabWidget.setCurrentIndex(tab_idx) self._current_function = function_id - def look_value(self, idx): - func_widget = self.function_widgets[self._current_function] + def look_value(self, idx: int): + func_widget = self.param_widgets[self._current_function] set_ids = [func_widget.comboBox.itemData(i) for i in range(func_widget.comboBox.count())] for s in set_ids: func_widget.data_values[s][idx] = self._management[s].value @@ -132,7 +138,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): self._complex[self._current_model] = self.functionwidget.get_complex_state() self._func_list[self._current_model] = self.functionwidget.get_parameter_list() - def load(self, ids: list): + def load(self, ids: List[str]): """ Add name and id of dataset to list. """ @@ -143,7 +149,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): else: self.data_table.add_model(self._current_model) - for dialog in self.function_widgets.values(): + for dialog in self.param_widgets.values(): dialog.load(ids) @QtCore.pyqtSlot(name='on_newmodel_button_clicked') @@ -190,9 +196,9 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): for m in self.models[model_id]: func_id = m['cnt'] - self.stackedWidget.removeWidget(self.function_widgets[func_id]) + self.stackedWidget.removeWidget(self.param_widgets[func_id]) - self.function_widgets.pop(func_id) + self.param_widgets.pop(func_id) self._complex.pop(model_id) self._func_list.pop(model_id) @@ -203,7 +209,8 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): if len(self.models) == 1: self.model_frame.hide() - def _prepare(self, model: list, function_use=None, parameter=None, add_idx=False, cnt=0): + def _prepare(self, model: list, function_use: list = None, + parameter: dict = None, add_idx: bool = False, cnt: int = 0) -> Tuple[dict, int]: if parameter is None: parameter = {'parameter': {}, 'lb': (), 'ub': (), 'var': [], 'glob': {'idx': [], 'p': [], 'var': [], 'lb': [], 'ub': []}, @@ -212,12 +219,13 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): for i, f in enumerate(model): if not f['active']: continue + try: - p, lb, ub, var, glob, links = self.function_widgets[f['cnt']].get_parameter(function_use) + p, lb, ub, var, glob, links = self.param_widgets[f['cnt']].get_parameter(function_use) except ValueError as e: _ = QtWidgets.QMessageBox().warning(self, 'Invalid value', str(e), QtWidgets.QMessageBox.Ok) - return None, -1 + return {}, -1 p_len = len(parameter['lb']) @@ -337,7 +345,6 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): @QtCore.pyqtSlot(int, name='on_preview_checkbox_stateChanged') def show_preview(self, state: int): - print('state', state) if state: self.preview_button.show() self.preview_checkbox.setText('') @@ -405,13 +412,9 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): y = f.func(x, *p, **kwargs) if is_complex is None: self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3))) - - elif is_complex == 0: + if is_complex in [0, 1]: self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3))) - self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3))) - elif is_complex == 1: - self.preview_lines.append(PlotItem(x=x, y=y.real, pen=mkPen(width=3))) - else: + if is_complex in [0, 2]: self.preview_lines.append(PlotItem(x=x, y=y.imag, pen=mkPen(width=3))) if isinstance(f, MultiModel): @@ -419,16 +422,39 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog): pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i])) if is_complex is None: self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i)) - elif is_complex == 0: + if is_complex in [0, 1]: self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i)) - self.preview_lines.append(PlotItem(x=x, y=s.imag, pen=pen_i)) - elif is_complex == 1: - self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i)) - else: + if is_complex in [0, 2]: self.preview_lines.append(PlotItem(x=x, y=s.imag, pen=pen_i)) return self.preview_lines + def set_parameter(self, parameter: Dict[str, FitResult]): + # which data uses which model + data = self.data_table.collect_data(default=self.default_combobox.currentData()) + + glob_fit_parameter = [] + + for fitted_model, fitted_data in data.items(): + for fit_id, fit_curve in parameter.items(): + if fit_id in fitted_data: + fit_parameter = list(fit_curve.parameter.values()) + glob_fit_parameter.append(fit_parameter) + + self.set_parameter_iter(fit_id, [p.value for p in fit_parameter], self.models[fitted_model]) + + mean_parameter = [reduce(add, p)/len(p) for p in zip(*glob_fit_parameter)] + + self.set_parameter_iter(None, mean_parameter, self.models[fitted_model]) + + def set_parameter_iter(self, fit_id: str | None, param: List[float], functions: List, cnt: int = 0): + for model_p in functions: + cnt += self.param_widgets[model_p['cnt']].set_parameter(fit_id, param[cnt:]) + if model_p['children']: + cnt += self.set_parameter_iter(fit_id, param, model_p['children'], cnt=cnt) + + return cnt + def closeEvent(self, evt: QtGui.QCloseEvent): self.preview_emit.emit({}, -1, False) self.preview_lines = [] diff --git a/nmreval/gui_qt/io/asciireader.py b/nmreval/gui_qt/io/asciireader.py index fd6472a..43a6a9f 100644 --- a/nmreval/gui_qt/io/asciireader.py +++ b/nmreval/gui_qt/io/asciireader.py @@ -15,6 +15,11 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): self.reader = AsciiReader(fname) + pal = QtWidgets.QApplication.instance().palette() + rgb = pal.color(pal.Base).getRgb()[:3] + rgb2 = pal.color(pal.Text).getRgb()[:3] + self.plainTextEdit_2.setStyleSheet(f'QPlainTextEdit {{ background-color: rgb{rgb} ; color: rgb{rgb2}; }}') + self.ascii_table.horizontalHeader().setStretchLastSection(True) self.buttonbox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply) self.buttonbox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.accept) diff --git a/nmreval/gui_qt/lib/__init__.py b/nmreval/gui_qt/lib/__init__.py index f273183..36d53d0 100644 --- a/nmreval/gui_qt/lib/__init__.py +++ b/nmreval/gui_qt/lib/__init__.py @@ -61,7 +61,11 @@ def get_icon(icon_name): global HAS_IMPORTLIB_RESOURCE - dirname = 'resources.icons.%s_light' % icon_type + if icon_name != 'logo': + dirname = f'resources.icons.{icon_type}_light' + else: + dirname = 'resources.icons' + if HAS_IMPORTLIB_RESOURCE: with path(dirname, icon_name+'.png') as imgpath: icon = QtGui.QIcon() diff --git a/nmreval/gui_qt/lib/delegates.py b/nmreval/gui_qt/lib/delegates.py index 50f79eb..5054e47 100644 --- a/nmreval/gui_qt/lib/delegates.py +++ b/nmreval/gui_qt/lib/delegates.py @@ -2,7 +2,7 @@ import re from ..Qt import QtWidgets, QtGui, QtCore -from ...lib.colors import BaseColor, Colors +from ...lib.colors import BaseColor, TUColors from ...lib.lines import LineStyle from ...lib.symbols import SymbolStyle, make_symbol_pixmap @@ -101,12 +101,12 @@ class ColorListEditor(QtWidgets.QComboBox): @value.setter def value(self, val): for i in range(self.count()): - if val == self.itemData(i): + if val.name == self.itemData(i).name: self.setCurrentIndex(i) break def populateList(self): - for i, colorName in enumerate(Colors): + for i, colorName in enumerate(TUColors): color = QtGui.QColor(*colorName.value) self.insertItem(i, colorName.name) self.setItemData(i, colorName) diff --git a/nmreval/gui_qt/lib/forms.py b/nmreval/gui_qt/lib/forms.py index 9c1faaf..0d22f06 100644 --- a/nmreval/gui_qt/lib/forms.py +++ b/nmreval/gui_qt/lib/forms.py @@ -56,7 +56,7 @@ class LineEdit(QtWidgets.QLineEdit): def contextMenuEvent(self, evt): menu = self.createStandardContextMenu() - request_action = menu.addAction('Use value of set(s)') + request_action = menu.addAction('Use value of sets') action = menu.exec(evt.globalPos()) @@ -89,9 +89,11 @@ class LineEditPost(QtWidgets.QLineEdit): class FormWidget(QtWidgets.QWidget): - types = {'float': (float, QtGui.QDoubleValidator), - 'int': (int, QtGui.QIntValidator), - 'str': (str, lambda: 0)} + types = { + 'float': (float, QtGui.QDoubleValidator), + 'int': (int, QtGui.QIntValidator), + 'str': (str, lambda: 0), + } valueChanged = QtCore.pyqtSignal(object) stateChanged = QtCore.pyqtSignal(bool) @@ -140,7 +142,12 @@ class FormWidget(QtWidgets.QWidget): @value.setter def value(self, val): - self.vals.setText(str(val)) + if self._type == 'str': + self.vals.setText(val) + elif self._type == 'int': + self.vals.setText(f'{val:.0f}') + else: + self.vals.setText(f'{val:.5g}') def setChecked(self, enable): if self._checkable: diff --git a/nmreval/lib/colors.py b/nmreval/lib/colors.py index 1dbd839..9bdb6f6 100644 --- a/nmreval/lib/colors.py +++ b/nmreval/lib/colors.py @@ -147,7 +147,7 @@ class BaseColor(enum.Enum): return cls((r, g, b)) -class TuColorsA(BaseColor): +class TUColorsA(BaseColor): TuDa1a = (93, 133, 195) TuDa2a = (0, 156, 218) TuDa3a = (80, 182, 149) @@ -161,7 +161,7 @@ class TuColorsA(BaseColor): TuDa11a = (128, 69, 151) -class TuColorsB(BaseColor): +class TUColorsB(BaseColor): TuDa1b = (0, 90, 169) TuDa2b = (0, 131, 204) TuDa3b = (0, 157, 129) @@ -175,7 +175,7 @@ class TuColorsB(BaseColor): TuDa11b = (114, 16, 133) -class TuColorsC(BaseColor): +class TUColorsC(BaseColor): TuDa1c = (0, 78, 138) TuDa2c = (0, 104, 157) TuDa3c = (0, 136, 119) @@ -243,28 +243,26 @@ class Tab20(BaseColor): TabBlue = (31, 119, 180) TabBlue2 = (174, 199, 232) TabOrange = (255, 127, 14) - TabOrange2 = (255, 127, 14) + TabOrange2 = (255, 187, 120) TabGreen = (44, 160, 44) - TabGreen2 = (44, 160, 44) + TabGreen2 = (152, 223, 138) TabRed = (214, 39, 40) - TabRed2 = (214, 39, 40) + TabRed2 = (255, 152, 150) TabPurple = (148, 103, 189) - TabPurple2 = (148, 103, 189) + TabPurple2 = (197, 176, 213) TabBrown = (140, 86, 75) - TabBrown2 = (140, 86, 75) + TabBrown2 = (196, 156, 148) TabRose = (227, 119, 194) - TabRose2 = (227, 119, 194) + TabRose2 = (247, 182, 210) TabGrey = (220, 220, 220) - TabGrey2 = (220, 220, 220) + TabGrey2 = (199, 199, 199) TabChartreuse = (188, 189, 34) - TabChartreuse2 = (188, 189, 34) + TabChartreuse2 = (219, 219, 141) TabTurquoise = (23, 190, 207) - TabTurquoise2 = (23, 190, 207) + TabTurquoise2 = (158, 218, 229) class GraceColors(BaseColor): - White = (255, 255, 255) - Black = (0, 0, 0) Red = (255, 0, 0) Green = (0, 255, 0) Blue = (0, 0, 255) @@ -281,25 +279,30 @@ class GraceColors(BaseColor): Green4 = (0, 139, 0) +class BlackWhite(BaseColor): + White = (255, 255, 255) + Black = (0, 0, 0) + + TUColors = enum.Enum( value='TUColors', - names={member.name: member.value for palette in [TuColorsA, TuColorsB, TuColorsC, TUColorsD, TUGrays] for member in palette}, - type=BaseColor + names={member.name: member.value for palette in [TUColorsA, TUColorsB, TUColorsC, TUColorsD] for member in palette}, + type=BaseColor, ) Colors = enum.Enum( value='Colors', - names={member.name: member.value for palette in [TUColors, GraceColors, Tab10] for member in palette}, - type=BaseColor + names={member.name: member.value for palette in [TUColors, TUGrays, GraceColors, Tab10, BlackWhite] for member in palette}, + type=BaseColor, ) def get_palettes(): palettes = { 'Full': Colors, - 'TuDa:a': TuColorsA, - 'TuDa:b': TuColorsB, - 'TuDa:c': TuColorsC, + 'TuDa:a': TUColorsA, + 'TuDa:b': TUColorsB, + 'TuDa:c': TUColorsC, 'TuDa:d': TUColorsD, 'Tab10': Tab10, 'Grace': GraceColors diff --git a/resources/_ui/fitresult.ui b/resources/_ui/fitresult.ui index 06dc1d2..0675b76 100644 --- a/resources/_ui/fitresult.ui +++ b/resources/_ui/fitresult.ui @@ -39,7 +39,147 @@ - + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry + + + + + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectColumns + + + false + + + 0 + + + false + + + + + + + Output + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 0 + + + + New graph + + + true + + + + + + + false + + + + + + + Plot fit curve + + + true + + + + + + + Plot partial functions + + + + + + + Plot parameter + + + true + + + + + + + + + + 3 + + + + + Reject this fit + + + + + + + Delete previous fits + + + + + + + + + Qt::Horizontal + + + + @@ -59,7 +199,7 @@ 0 0 399 - 414 + 346 @@ -102,7 +242,7 @@ 0 0 399 - 414 + 346 @@ -152,7 +292,7 @@ 0 0 399 - 414 + 346 @@ -217,129 +357,6 @@ - - - - 3 - - - - - Plot partial functions - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Location of parameters: - - - - - - - - 0 - 0 - - - - New graph - - - true - - - - - - - false - - - - - - - - - Qt::Horizontal - - - - - - - 3 - - - - - Reject this fit - - - - - - - Delete previous fits - - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry - - - - - - - Qt::ScrollBarAsNeeded - - - QAbstractScrollArea::AdjustIgnored - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectColumns - - - false - - - 0 - - - false - - -