From 13de2714b5ac9f0e34e816b2416cc3a67a376051 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/fit/model.py | 3 +- nmreval/fit/parameter.py | 11 + nmreval/fit/result.py | 9 +- 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/fit/result.py | 46 +++- 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/gui_qt/lib/pg_objects.py | 4 +- nmreval/gui_qt/main/mainwindow.py | 42 +-- nmreval/gui_qt/main/management.py | 63 +++-- nmreval/lib/colors.py | 45 +-- nmreval/utils/text.py | 8 +- resources/_ui/fitresult.ui | 271 ++++++++++--------- resources/icons/normal_light/logo.png | 1 - resources/icons/pokemon_light/logo.png | 1 - 25 files changed, 526 insertions(+), 354 deletions(-) delete mode 100755 __init__.py delete mode 120000 resources/icons/normal_light/logo.png delete mode 120000 resources/icons/pokemon_light/logo.png 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/fit/model.py b/nmreval/fit/model.py index 1df8fbf..f815f03 100644 --- a/nmreval/fit/model.py +++ b/nmreval/fit/model.py @@ -17,7 +17,7 @@ class Model(object): elif inspect.isfunction(model): self._init_from_function(model) else: - raise ValueError(f'No idea how to use datatype {model}.') + raise ValueError(f'No idea how to use datatype {model!r}.') self.lb = [i if i is not None else -inf for i in self.lb] self.ub = [i if i is not None else inf for i in self.ub] @@ -140,7 +140,6 @@ class Model(object): return [self.func(p, x, **kwargs)] else: - print('multi model') if not kwargs: kwargs = self.fun_kwargs diff --git a/nmreval/fit/parameter.py b/nmreval/fit/parameter.py index c23cff1..12f92ed 100644 --- a/nmreval/fit/parameter.py +++ b/nmreval/fit/parameter.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from numbers import Number from itertools import count @@ -115,6 +117,15 @@ class Parameter: else: return start + f'{self.value:} (fixed)' + def __add__(self, other: Parameter | float) -> float: + if isinstance(other, float): + return self.value + other + elif isinstance(other, Parameter): + return self.value + other.value + + def __radd__(self, other: Parameter | float) -> float: + return self.__add__(other) + @property def scaled_value(self): return self.value / self.scale diff --git a/nmreval/fit/result.py b/nmreval/fit/result.py index 97a8a5f..8c0a803 100644 --- a/nmreval/fit/result.py +++ b/nmreval/fit/result.py @@ -94,8 +94,13 @@ class FitResultCreator: correlation = corr partial_correlation = pcorr - return FitResult(_x, _y, x_orig, y_orig, parameters, fun_kwargs, resid, nobs, nvar, model.name, stats, - idx=idx, corr=correlation, pcorr=partial_correlation, islog=islog, iscomplex=model.is_complex), part_functions + return ( + FitResult(_x, _y, x_orig, y_orig, parameters, fun_kwargs, resid, + nobs, nvar, model.name, stats, + idx=idx, corr=correlation, pcorr=partial_correlation, + islog=islog, iscomplex=model.is_complex), + part_functions, + ) @staticmethod def calc_statistics(y, residual, nobs=None, nvar=None): 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/fit/result.py b/nmreval/gui_qt/fit/result.py index 3e9ede7..b4ff9e0 100644 --- a/nmreval/gui_qt/fit/result.py +++ b/nmreval/gui_qt/fit/result.py @@ -11,7 +11,7 @@ from ..lib.pg_objects import PlotItem class QFitResult(QtWidgets.QDialog, Ui_Dialog): - closed = QtCore.pyqtSignal(dict, list) + closed = QtCore.pyqtSignal(dict, list, str, bool, dict) redoFit = QtCore.pyqtSignal(dict) def __init__(self, results: list, management, parent=None): @@ -25,7 +25,6 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): for (res, parts) in results: idx = res.idx - print(parts) data_k = management.data[idx] if res.name not in self._models: @@ -64,7 +63,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.set_parameter(0) self.buttonBox.accepted.connect(self.accept) - self.param_tableWidget.horizontalHeader().sectionClicked.connect(self.show_results) + self.param_tableWidget.itemClicked.connect(self.show_results) + self.param_tableWidget.horizontalHeader().sectionClicked.connect(lambda i: self.show_results(None, idx=i)) + + self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked)) + self.logy_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(y=bool(x))) def add_graphs(self, graphs: list): @@ -72,10 +75,6 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): for (graph_id, graph_name) in graphs: self.graph_comboBox.addItem(graph_name, userData=graph_id) - @QtCore.pyqtSlot(int, name='on_graph_checkBox_stateChanged') - def change_graph(self, state: int): - self.graph_comboBox.setEnabled(state == QtCore.Qt.Unchecked) - @QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged') def set_parameter(self, idx: int): model_name = self.sets_comboBox.itemText(idx) @@ -109,22 +108,32 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.param_tableWidget.resizeColumnsToContents() self.param_tableWidget.selectColumn(0) - self.show_results(0) + self.show_results(None, idx=0) @QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged') @QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged') def change_opts(self, _): - idx = self.sets_comboBox.currentIndex() + idx = self.param_tableWidget.currentIndex().column() self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked, self.del_prev_checkBox.checkState() == QtCore.Qt.Checked) - def show_results(self, idx: int): + def show_results(self, item, idx=None): + if item is not None: + idx = self.param_tableWidget.indexFromItem(item).column() + set_id = self.param_tableWidget.horizontalHeaderItem(idx).data(QtCore.Qt.UserRole) self.set_plot(set_id) self.set_correlation(set_id) self.set_statistics(set_id) + self.reject_fit_checkBox.blockSignals(True) + self.reject_fit_checkBox.setChecked(self._opts[idx][0]) + self.reject_fit_checkBox.blockSignals(False) + self.del_prev_checkBox.blockSignals(True) + self.del_prev_checkBox.setChecked(self._opts[idx][1]) + self.del_prev_checkBox.blockSignals(False) + def set_plot(self, idx: str): res = self._results[idx] iscomplex = res.iscomplex @@ -234,10 +243,19 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog): self.redoFit.emit(self._results) elif button_type == self.buttonBox.Ok: - graph = '' if self.graph_checkBox.checkState() == QtCore.Qt.Checked else self.graph_comboBox.currentData() - subplots = self.partial_checkBox.checkState() == QtCore.Qt.Checked - self._opts.extend([graph, subplots]) - self.closed.emit(self._results, self._opts) + graph = None + if self.parameter_checkbox.isChecked(): + if self.graph_checkBox.checkState() == QtCore.Qt.Checked: + graph = '' + else: + graph = self.graph_comboBox.currentData() + + plot_fits = self.curve_checkbox.isChecked() + + if self.partial_checkBox.checkState() == QtCore.Qt.Checked: + self.closed.emit(self._results, self._opts, graph, plot_fits, self._parts) + else: + self.closed.emit(self._results, self._opts, graph, plot_fits, {}) self.accept() 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/gui_qt/lib/pg_objects.py b/nmreval/gui_qt/lib/pg_objects.py index fb82b8d..52f3955 100644 --- a/nmreval/gui_qt/lib/pg_objects.py +++ b/nmreval/gui_qt/lib/pg_objects.py @@ -4,10 +4,10 @@ from pyqtgraph import ( ErrorBarItem, LinearRegionItem, mkBrush, mkColor, mkPen, - PlotDataItem + PlotDataItem, ) -from ...lib.colors import BaseColor, Colors +from ...lib.colors import BaseColor, Colors, TUColors from ...lib.lines import LineStyle from ...lib.symbols import SymbolStyle diff --git a/nmreval/gui_qt/main/mainwindow.py b/nmreval/gui_qt/main/mainwindow.py index 2a3e899..e5c6600 100644 --- a/nmreval/gui_qt/main/mainwindow.py +++ b/nmreval/gui_qt/main/mainwindow.py @@ -391,6 +391,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.management.current_graph = wd.widget().id self.current_plotitem = self.current_graph_widget.graphic + self.change_mouse_mode(self.actionMouse_behaviour.isChecked()) + pick = False block = False if self.ptsselectwidget.isVisible(): @@ -720,6 +722,10 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): for s in sets: self.datawidget.tree.move_sets(s, dest, src) + @QtCore.pyqtSlot(name='on_action_idx_cut_triggered') + def cut_with_idx(self): + print('schnippschnapp') + @QtCore.pyqtSlot(str) def show_data_values(self, sid: str): if sid == '': @@ -776,19 +782,18 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): pass if onoff: - if self.management.active_sets: - self.fit_dialog.connected_figure = self.management.current_graph - 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(): - self.current_graph_widget.add_external(self.fitregion) + self.fit_dialog.connected_figure = self.management.current_graph + 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(): + self.current_graph_widget.add_external(self.fitregion) - block_window = True - else: - for item in self.fit_dialog.preview_lines: - self.current_graph_widget.remove_external(item) - self.current_graph_widget.remove_external(self.fitregion) + block_window = True + else: + for item in self.fit_dialog.preview_lines: + self.current_graph_widget.remove_external(item) + self.current_graph_widget.remove_external(self.fitregion) return block_window @@ -834,18 +839,21 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): for item in self.fit_dialog.preview_lines: g.add_external(item) - self.raise_() - @QtCore.pyqtSlot(list) def show_fit_results(self, results: list): self.fit_dialog.fit_button.setEnabled(True) if results: res_dialog = QFitResult(results, self.management, parent=self) res_dialog.add_graphs(self.management.graphs.list()) - res_dialog.closed.connect(self.management.make_fits) + res_dialog.closed.connect(self.accepts_fit) res_dialog.redoFit.connect(self.management.redo_fits) res_dialog.show() + @QtCore.pyqtSlot(dict, list, str, bool, dict) + def accepts_fit(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: dict) -> None: + self.fit_dialog.set_parameter(res) + self.management.make_fits(res, opts, param_graph, show_fit, parts) + @QtCore.pyqtSlot(name='on_actionFunction_editor_triggered') def edit_models(self): if self.editor is None: @@ -885,14 +893,14 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): evt.accept() @QtCore.pyqtSlot(bool, name='on_actionMouse_behaviour_toggled') - def change_mouse_mode(self, is_checked): + def change_mouse_mode(self, is_checked: bool): if is_checked: self.current_plotitem.plotItem.vb.setMouseMode(ViewBox.RectMode) else: self.current_plotitem.plotItem.vb.setMouseMode(ViewBox.PanMode) def mousemoved(self, xpos, ypos): - self.mousepos.setText('x={:.3g}; y={:.3g}'.format(xpos, ypos)) + self.mousepos.setText(f'x={xpos:.3g}; y={ypos:.3g}') @QtCore.pyqtSlot(name='on_actionSnake_triggered') @QtCore.pyqtSlot(name='on_actionTetris_triggered') diff --git a/nmreval/gui_qt/main/management.py b/nmreval/gui_qt/main/management.py index 9fc83d7..d10200b 100644 --- a/nmreval/gui_qt/main/management.py +++ b/nmreval/gui_qt/main/management.py @@ -9,6 +9,7 @@ from ...fit import data as fit_d from ...fit.model import Model from ...fit.result import FitResult from ...fit.minimizer import FitRoutine +from ...lib.colors import TUColorsC from ...math.interpol import interpolate from ...math.logfourier import logft from ...math.smooth import smooth @@ -78,8 +79,7 @@ class UpperManagement(QtCore.QObject): properties_collected = QtCore.pyqtSignal(dict) unset_state = QtCore.pyqtSignal(list) - - _colors = cycle(Colors) + _colors = cycle(TUColors) _actions = { 'ls': (ShiftCommand, 'Left shift'), @@ -238,6 +238,11 @@ class UpperManagement(QtCore.QObject): # move to correct position self.graphs[dest].move_sets(sets, pos) + def select_window(self, gid: str): + for key, plot in self.graphs.items(): + if key == gid: + self.window.area.setActiveSubWindow(plot.parent()) + @QtCore.pyqtSlot() @QtCore.pyqtSlot(list, str) def copy_sets(self, sets: list = None, src: str = None): @@ -466,14 +471,22 @@ class UpperManagement(QtCore.QObject): parameter[set_id] = (new_values, set_parameter[1]) self.start_fit(*self.__fit_options) - @QtCore.pyqtSlot(dict, list) - def make_fits(self, res: dict, opts: list): + def make_fits(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: dict) -> None: + """ + + Args: + res: key is that of original data, value is FitResult + opts: (ignore this fits, delete previous fits) + param_graph: None if no parameter to plot, '' for new graph, or id of existig graph + show_fit: plot fit curve? + parts: key is that of original data, value is list of subplots + + """ f_id_list = [] gid = '' - subplots = opts.pop(-1) - param_graph = opts.pop(-1) tobedeleted = [] + accepted = [] for i, (k, fit) in enumerate(res.items()): reject, delete_prev = opts[i] if reject: @@ -493,19 +506,31 @@ class UpperManagement(QtCore.QObject): fit.value = data_k.value fit.group = data_k.group - f_id = self.add(fit, color=color, src=k) + accepted.append(fit) + + if show_fit: + f_id = self.add(fit, color=color, src=k) + + f_id_list.append(f_id) + data_k.set_fits(f_id) - f_id_list.append(f_id) - data_k.set_fits(f_id) gid = data_k.graph + if k in parts and show_fit: + for subfunc, col in zip(parts[k], TUColorsC): + sub_f_id = self.add(subfunc, color=col, linestyle=LineStyle.Dashed, symbol=SymbolStyle.No) + subfunc.value = data_k.value + subfunc.group = data_k.group + f_id_list.append(sub_f_id) + self.delete_sets(tobedeleted) - if f_id_list: - self.newData.emit(f_id_list, gid) - self.make_fit_parameter(f_id_list, graph_id=param_graph) + if accepted and param_graph is not None: + self.make_fit_parameter(accepted, graph_id=param_graph) - def make_fit_parameter(self, fit_sets: List[str], graph_id: str = None): + self.newData.emit(f_id_list, gid) + + def make_fit_parameter(self, fit_sets: List[str | FitResult], graph_id: str = None): fit_dict = self._collect_fit_parameter(fit_sets) if fit_dict: @@ -530,12 +555,17 @@ class UpperManagement(QtCore.QObject): data.data.save_parameter(fname) - def _collect_fit_parameter(self, fit_sets: List[str]) -> dict: + def _collect_fit_parameter(self, fit_sets: List[str | FitResult]) -> dict: fit_dict = {} for set_id in fit_sets: - data = self.data[set_id] - if data.mode != 'fit': + if isinstance(set_id, str): + data = self.data[set_id] + if data.mode != 'fit': + continue + elif isinstance(set_id, FitResult): + data = set_id + else: continue for key, pvalue in data.parameter.items(): @@ -811,7 +841,6 @@ class UpperManagement(QtCore.QObject): err_msg.exec() self.sender().success = not failures - self.sender().add_data(self.active_sets) @QtCore.pyqtSlot(list, dict) 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/nmreval/utils/text.py b/nmreval/utils/text.py index 1840abe..57ff30a 100644 --- a/nmreval/utils/text.py +++ b/nmreval/utils/text.py @@ -31,12 +31,12 @@ big_greek = [ 'Nu Xi Omicron Pi Rho Sigma Tau Ypsilon Phi Chi Psi Omega', ] special_chars = [ - r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar', - '\u221e \u222b \u2211 \u27e8 \u27e9 \u00b1 \u27c2 \u2225 \u21d0 \u21d2 \u21d4 \u00b7 \u0127', - r'\f{Symbol}¥\f{} \f{Symbol}ò\f{} \f{Symbol}å\f{} \f{Symbol}á\f{} \f{Symbol}ñ\f{} \f{Symbol}±\f{} ' + r'\infty \int \sum \langle \rangle \pm \perp \para \leftarrow \rightarrow \leftrightarrow \cdot \hbar \n', + '\u221e \u222b \u2211 \u27e8 \u27e9 \u00b1 \u27c2 \u2225 \u21d0 \u21d2 \u21d4 \u00b7 \u0127
', + r'\f{Symbol}¥\f{} \f{Symbol}ò\f{} \f{Symbol}å\f{} \f{Symbol}á\f{} \f{Symbol}ñ\f{} \f{Symbol}±\f{} \n' r'\f{Symbol}^\f{} \f{Symbol}||\f{} \f{Symbol}¬\f{} \f{Symbol}®\f{} \f{Symbol}«\f{} \f{Symbol}×\f{Symbol} ' r'h\h{-0.6}\v{0.3}-\v{-0.3}\h{0.3}', - r'infty int sum < > \+- perp para <- -> <-> \* hbar', + r'infty int sum < > \+- perp para <- -> <-> \* hbar \s', ] funcs = [ r'\exp \log \ln \sin \cos \tan', 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 - - - diff --git a/resources/icons/normal_light/logo.png b/resources/icons/normal_light/logo.png deleted file mode 120000 index 7a3d63f..0000000 --- a/resources/icons/normal_light/logo.png +++ /dev/null @@ -1 +0,0 @@ -/autohome/dominik/nmreval/resources/icons/logo.png \ No newline at end of file diff --git a/resources/icons/pokemon_light/logo.png b/resources/icons/pokemon_light/logo.png deleted file mode 120000 index 7a3d63f..0000000 --- a/resources/icons/pokemon_light/logo.png +++ /dev/null @@ -1 +0,0 @@ -/autohome/dominik/nmreval/resources/icons/logo.png \ No newline at end of file