forked from IPKM/nmreval
Merge branch 'fit_constraints'
# Conflicts: # src/gui_qt/main/management.py
This commit is contained in:
@ -1,10 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'resources/_ui/fitmodelwidget.ui'
|
||||
# Form implementation generated from reading ui file 'src/resources/_ui/fitmodelwidget.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.12.3
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
@ -13,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_FitParameter(object):
|
||||
def setupUi(self, FitParameter):
|
||||
FitParameter.setObjectName("FitParameter")
|
||||
FitParameter.resize(365, 78)
|
||||
FitParameter.resize(365, 66)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
@ -36,7 +37,7 @@ class Ui_FitParameter(object):
|
||||
self.parametername.setObjectName("parametername")
|
||||
self.horizontalLayout_2.addWidget(self.parametername)
|
||||
self.parameter_line = LineEdit(FitParameter)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.parameter_line.sizePolicy().hasHeightForWidth())
|
||||
@ -44,20 +45,12 @@ class Ui_FitParameter(object):
|
||||
self.parameter_line.setText("")
|
||||
self.parameter_line.setObjectName("parameter_line")
|
||||
self.horizontalLayout_2.addWidget(self.parameter_line)
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_2.addItem(spacerItem)
|
||||
self.fixed_check = QtWidgets.QCheckBox(FitParameter)
|
||||
self.fixed_check.setObjectName("fixed_check")
|
||||
self.horizontalLayout_2.addWidget(self.fixed_check)
|
||||
self.global_checkbox = QtWidgets.QCheckBox(FitParameter)
|
||||
self.global_checkbox.setObjectName("global_checkbox")
|
||||
self.horizontalLayout_2.addWidget(self.global_checkbox)
|
||||
self.toolButton = QtWidgets.QToolButton(FitParameter)
|
||||
self.toolButton.setText("")
|
||||
self.toolButton.setPopupMode(QtWidgets.QToolButton.InstantPopup)
|
||||
self.toolButton.setArrowType(QtCore.Qt.RightArrow)
|
||||
self.toolButton.setObjectName("toolButton")
|
||||
self.horizontalLayout_2.addWidget(self.toolButton)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
||||
self.frame = QtWidgets.QFrame(FitParameter)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
|
||||
|
@ -8,6 +8,7 @@ from pyqtgraph import mkPen
|
||||
|
||||
from nmreval.data.points import Points
|
||||
from nmreval.data.signals import Signal
|
||||
from nmreval.lib.logger import logger
|
||||
from nmreval.utils.text import convert
|
||||
from nmreval.data.bds import BDS
|
||||
from nmreval.data.dsc import DSC
|
||||
@ -356,7 +357,7 @@ class ExperimentContainer(QtCore.QObject):
|
||||
elif mode in ['imag', 'all'] and self.plot_imag is not None:
|
||||
self.plot_imag.set_symbol(symbol=symbol, size=size, color=color)
|
||||
else:
|
||||
print('Updating symbol failed for ' + str(self.id))
|
||||
logger.warning(f'Updating symbol failed for {self.id}')
|
||||
|
||||
def setLine(self, *, width=None, style=None, color=None, mode='real'):
|
||||
if mode in ['real', 'all']:
|
||||
@ -368,7 +369,7 @@ class ExperimentContainer(QtCore.QObject):
|
||||
elif mode in ['imag', 'all'] and self.plot_imag is not None:
|
||||
self.plot_imag.set_line(width=width, style=style, color=color)
|
||||
else:
|
||||
print('Updating line failed for ' + str(self.id))
|
||||
logger.warning(f'Updating line failed for {self.id}')
|
||||
|
||||
def update_property(self, key1: str, key2: str, value: Any):
|
||||
keykey = key2.split()
|
||||
|
@ -1,3 +1,4 @@
|
||||
from nmreval.lib.logger import logger
|
||||
from nmreval.math import apodization
|
||||
from nmreval.lib.importer import find_models
|
||||
from nmreval.utils.text import convert
|
||||
@ -67,7 +68,7 @@ class EditSignalWidget(QtWidgets.QWidget, Ui_Form):
|
||||
self.do_something.emit(sender, (ph0, ph1, pvt))
|
||||
|
||||
else:
|
||||
print('You should never reach this by accident.')
|
||||
logger.warning(f'You should never reach this by accident, invalid sender {sender!r}')
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
|
||||
def change_apodization(self, index):
|
||||
|
@ -19,19 +19,20 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.parametername.setText(label + ' ')
|
||||
self.name = label
|
||||
|
||||
self.parametername.setText(convert(label) + ' ')
|
||||
|
||||
validator = QtGui.QDoubleValidator()
|
||||
self.parameter_line.setValidator(validator)
|
||||
self.parameter_line.setText('1')
|
||||
self.parameter_line.setMaximumWidth(240)
|
||||
self.lineEdit.setMaximumWidth(60)
|
||||
self.lineEdit_2.setMaximumWidth(60)
|
||||
self.parameter_line.setMaximumWidth(160)
|
||||
self.lineEdit.setMaximumWidth(100)
|
||||
self.lineEdit_2.setMaximumWidth(100)
|
||||
|
||||
self.label_3.setText(f'< {label} <')
|
||||
self.label_3.setText(f'< {convert(label)} <')
|
||||
|
||||
self.checkBox.stateChanged.connect(self.enableBounds)
|
||||
self.global_checkbox.stateChanged.connect(lambda: self.state_changed.emit())
|
||||
self.parameter_line.editingFinished.connect(self.update_parameter)
|
||||
self.parameter_line.values_requested.connect(lambda: self.value_requested.emit(self))
|
||||
self.parameter_line.replace_single_values.connect(lambda: self.replace_single_value.emit(None))
|
||||
self.parameter_line.editingFinished.connect(lambda: self.value_changed.emit(self.parameter_line.text()))
|
||||
@ -40,18 +41,12 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
||||
if fixed:
|
||||
self.fixed_check.hide()
|
||||
|
||||
self.menu = QtWidgets.QMenu(self)
|
||||
self.add_links()
|
||||
|
||||
self.is_linked = None
|
||||
self.parameter_pos = None
|
||||
self.func_idx = None
|
||||
|
||||
self._linetext = '1'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return convert(self.parametername.text().strip(), old='html', new='str')
|
||||
self.menu = QtWidgets.QMenu(self)
|
||||
|
||||
def set_parameter_string(self, p: str):
|
||||
self.parameter_line.setText(p)
|
||||
@ -71,38 +66,24 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
||||
|
||||
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 = f'{p:.4g}'
|
||||
ptext = f'{p:.4g}'
|
||||
|
||||
self.set_parameter_string(ptext)
|
||||
self.set_parameter_string(ptext)
|
||||
|
||||
if bds is not None:
|
||||
self.set_bounds(*bds)
|
||||
if bds is not None:
|
||||
self.set_bounds(*bds)
|
||||
|
||||
if fixed is not None:
|
||||
self.fixed_check.setCheckState(QtCore.Qt.Unchecked if fixed else QtCore.Qt.Checked)
|
||||
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)
|
||||
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:
|
||||
try:
|
||||
p = float(self._linetext)
|
||||
except ValueError:
|
||||
p = 1.0
|
||||
else:
|
||||
try:
|
||||
p = float(self.parameter_line.text().replace(',', '.'))
|
||||
except ValueError:
|
||||
_ = QtWidgets.QMessageBox().warning(self, 'Invalid value',
|
||||
f'{self.parametername.text()} contains invalid values',
|
||||
QtWidgets.QMessageBox.Cancel)
|
||||
return None
|
||||
try:
|
||||
p = float(self.parameter_line.text().replace(',', '.'))
|
||||
except ValueError:
|
||||
p = self.parameter_line.text().replace(',', '.')
|
||||
|
||||
if self.checkBox.isChecked():
|
||||
try:
|
||||
@ -119,75 +100,27 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
||||
|
||||
bounds = (lb, rb)
|
||||
|
||||
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked(), self.is_linked
|
||||
return p, bounds, not self.fixed_check.isChecked(), self.global_checkbox.isChecked()
|
||||
|
||||
@QtCore.pyqtSlot(bool)
|
||||
def set_fixed(self, state: bool):
|
||||
# self.global_checkbox.setVisible(not state)
|
||||
self.frame.setVisible(not state)
|
||||
|
||||
def add_links(self, parameter: dict = None):
|
||||
if parameter is None:
|
||||
parameter = {}
|
||||
self.menu.clear()
|
||||
|
||||
ac = QtWidgets.QAction('Link to...', self)
|
||||
ac.triggered.connect(self.link_parameter)
|
||||
self.menu.addAction(ac)
|
||||
|
||||
for model_key, model_funcs in parameter.items():
|
||||
m = QtWidgets.QMenu('Model ' + model_key, self)
|
||||
for func_name, func_params in model_funcs.items():
|
||||
m2 = QtWidgets.QMenu(func_name, m)
|
||||
for p_name, idx in func_params:
|
||||
ac = QtWidgets.QAction(p_name, m2)
|
||||
ac.setData((model_key, *idx))
|
||||
ac.triggered.connect(self.link_parameter)
|
||||
m2.addAction(ac)
|
||||
m.addMenu(m2)
|
||||
self.menu.addMenu(m)
|
||||
|
||||
self.toolButton.setMenu(self.menu)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def link_parameter(self, linkto=None):
|
||||
if linkto is None:
|
||||
action = self.sender()
|
||||
else:
|
||||
action = False
|
||||
for m in self.menu.actions():
|
||||
if m.menu():
|
||||
for a in m.menu().actions():
|
||||
if a.data() == linkto:
|
||||
action = a
|
||||
break
|
||||
if action:
|
||||
break
|
||||
|
||||
if (self.func_idx, self.parameter_pos) == action.data():
|
||||
return
|
||||
def update_parameter(self):
|
||||
new_value = self.parameter_line.text()
|
||||
if not new_value:
|
||||
self.parameter_line.setText('1')
|
||||
|
||||
try:
|
||||
new_text = f'Linked to {action.parentWidget().title()}.{action.text()}'
|
||||
self._linetext = self.parameter_line.text()
|
||||
self.parameter_line.setText(new_text)
|
||||
self.parameter_line.setEnabled(False)
|
||||
self.global_checkbox.hide()
|
||||
self.global_checkbox.blockSignals(True)
|
||||
self.global_checkbox.setCheckState(QtCore.Qt.Checked)
|
||||
self.global_checkbox.blockSignals(False)
|
||||
self.frame.hide()
|
||||
self.is_linked = action.data()
|
||||
float(new_value)
|
||||
is_text = False
|
||||
except ValueError:
|
||||
is_text = True
|
||||
self.global_checkbox.setCheckState(False)
|
||||
|
||||
except AttributeError:
|
||||
self.parameter_line.setText(self._linetext)
|
||||
self.parameter_line.setEnabled(True)
|
||||
if self.fixed_check.isEnabled():
|
||||
self.global_checkbox.show()
|
||||
self.frame.show()
|
||||
self.is_linked = None
|
||||
|
||||
self.state_changed.emit()
|
||||
self.set_fixed(is_text)
|
||||
|
||||
|
||||
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
|
||||
@ -282,8 +215,17 @@ class FitModelTree(QtWidgets.QTreeWidget):
|
||||
idx = item.data(0, self.counterRole)
|
||||
self.itemRemoved.emit(idx)
|
||||
|
||||
def add_function(self, idx: int, cnt: int, op: int, name: str, color: QtGui.QColor | str | tuple,
|
||||
parent: QtWidgets.QTreeWidgetItem = None, children: list = None, active: bool = True, **kwargs):
|
||||
def add_function(self,
|
||||
idx: int,
|
||||
cnt: int,
|
||||
op: int,
|
||||
name: str,
|
||||
color: QtGui.QColor | str | tuple,
|
||||
parent: QtWidgets.QTreeWidgetItem = None,
|
||||
children: list = None,
|
||||
active: bool = True,
|
||||
param_names: list[str] = None,
|
||||
**kwargs):
|
||||
"""
|
||||
Add function to tree and dictionary of functions.
|
||||
"""
|
||||
@ -298,6 +240,10 @@ class FitModelTree(QtWidgets.QTreeWidget):
|
||||
it.setData(0, self.counterRole, cnt)
|
||||
it.setData(0, self.operatorRole, op)
|
||||
it.setText(0, name)
|
||||
if param_names is not None:
|
||||
it.setToolTip(0,
|
||||
'Parameter names:\n' +
|
||||
'\n'.join(f'{pn}({cnt})' for pn in param_names))
|
||||
it.setForeground(0, QtGui.QBrush(color))
|
||||
|
||||
it.setIcon(0, get_icon(self.icons[op]))
|
||||
|
@ -1,5 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from nmreval.fit.parameter import Parameter
|
||||
from nmreval.utils.text import convert
|
||||
|
||||
from ..Qt import QtWidgets, QtCore, QtGui
|
||||
@ -62,8 +65,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
||||
self.glob_values = [1] * len(func.params)
|
||||
|
||||
for k, v in enumerate(func.params):
|
||||
name = convert(v)
|
||||
widgt = FitModelWidget(label=name, parent=self.scrollwidget)
|
||||
widgt = FitModelWidget(label=v, parent=self.scrollwidget)
|
||||
widgt.parameter_pos = k
|
||||
widgt.func_idx = idx
|
||||
try:
|
||||
@ -83,7 +85,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
||||
self.global_parameter.append(widgt)
|
||||
self.scrollwidget.layout().addWidget(widgt)
|
||||
|
||||
widgt2 = ParameterSingleWidget(name=name, parent=self.scrollwidget2)
|
||||
widgt2 = ParameterSingleWidget(name=v, parent=self.scrollwidget2)
|
||||
widgt2.valueChanged.connect(self.change_single_parameter)
|
||||
widgt2.removeSingleValue.connect(self.change_single_parameter)
|
||||
widgt2.installEventFilter(self)
|
||||
@ -115,20 +117,22 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
||||
self.scrollwidget.layout().addStretch(1)
|
||||
self.scrollwidget2.layout().addStretch(1)
|
||||
|
||||
def set_links(self, parameter):
|
||||
for w in self.global_parameter:
|
||||
if isinstance(w, FitModelWidget):
|
||||
w.add_links(parameter)
|
||||
# def set_links(self, parameter):
|
||||
# for w in self.global_parameter:
|
||||
# if isinstance(w, FitModelWidget):
|
||||
# w.add_links(parameter)
|
||||
|
||||
@QtCore.pyqtSlot(str)
|
||||
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)
|
||||
# self.glob_values[idx] = float(value)
|
||||
self.glob_values[idx] = value
|
||||
if self.data_values[self.comboBox.currentData()][idx] is None:
|
||||
self.data_parameter[idx].blockSignals(True)
|
||||
self.data_parameter[idx].value = float(value)
|
||||
# self.data_parameter[idx].value = float(value)
|
||||
self.data_parameter[idx].value = value
|
||||
self.data_parameter[idx].blockSignals(False)
|
||||
|
||||
@QtCore.pyqtSlot(str, object)
|
||||
@ -171,7 +175,7 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
||||
# disable single parameter if it is set global, enable if global is unset
|
||||
widget = self.sender()
|
||||
idx = self.global_parameter.index(widget)
|
||||
enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked) and (widget.is_linked is None)
|
||||
enable = (widget.global_checkbox.checkState() == QtCore.Qt.Unchecked)
|
||||
self.data_parameter[idx].setEnabled(enable)
|
||||
|
||||
def select_next_preview(self, direction):
|
||||
@ -204,64 +208,50 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
||||
if sid not in self.data_values:
|
||||
self.data_values[sid] = [None] * len(self.data_parameter)
|
||||
|
||||
def get_parameter(self, use_func=None):
|
||||
def get_parameter(self, use_func=None) -> tuple[dict, list]:
|
||||
bds = []
|
||||
is_global = []
|
||||
is_fixed = []
|
||||
globs = []
|
||||
is_linked = []
|
||||
param_general = []
|
||||
|
||||
for g in self.global_parameter:
|
||||
if isinstance(g, FitModelWidget):
|
||||
p_i, bds_i, fixed_i, global_i, link_i = g.get_parameter()
|
||||
p_i, bds_i, fixed_i, global_i = g.get_parameter()
|
||||
parameter_i = Parameter(name=g.name, value=p_i, lb=bds_i[0], ub=bds_i[1], var=fixed_i)
|
||||
param_general.append(parameter_i)
|
||||
|
||||
globs.append(p_i)
|
||||
bds.append(bds_i)
|
||||
is_fixed.append(fixed_i)
|
||||
is_global.append(global_i)
|
||||
is_linked.append(link_i)
|
||||
|
||||
lb, ub = list(zip(*bds))
|
||||
|
||||
data_parameter = {}
|
||||
if use_func is None:
|
||||
use_func = list(self.data_values.keys())
|
||||
|
||||
global_p = None
|
||||
for sid, parameter in self.data_values.items():
|
||||
if sid not in use_func:
|
||||
continue
|
||||
|
||||
kw_p = {}
|
||||
p = []
|
||||
if global_p is None:
|
||||
global_p = {'p': [], 'idx': [], 'var': [], 'ub': [], 'lb': []}
|
||||
|
||||
for i, (p_i, g) in enumerate(zip(parameter, self.global_parameter)):
|
||||
if isinstance(g, FitModelWidget):
|
||||
if (p_i is None) or is_global[i]:
|
||||
p.append(globs[i])
|
||||
if is_global[i]:
|
||||
if i not in global_p['idx']:
|
||||
global_p['p'].append(globs[i])
|
||||
global_p['idx'].append(i)
|
||||
global_p['var'].append(is_fixed[i])
|
||||
global_p['ub'].append(ub[i])
|
||||
global_p['lb'].append(lb[i])
|
||||
# set has no oen value
|
||||
p.append(param_general[i].copy())
|
||||
else:
|
||||
p.append(p_i)
|
||||
lb, ub = bds[i]
|
||||
try:
|
||||
if not (lb < p_i < ub):
|
||||
raise ValueError(f'Parameter {g.name} is outside bounds ({lb}, {ub})')
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if p[i] > ub[i]:
|
||||
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]})')
|
||||
except TypeError:
|
||||
pass
|
||||
# create Parameter
|
||||
p.append(
|
||||
Parameter(name=g.name, value=p_i, lb=lb, ub=ub, var=is_fixed[i])
|
||||
)
|
||||
|
||||
else:
|
||||
if p_i is None:
|
||||
@ -273,7 +263,15 @@ class QFitParameterWidget(QtWidgets.QWidget, Ui_FormFit):
|
||||
|
||||
data_parameter[sid] = (p, kw_p)
|
||||
|
||||
return data_parameter, lb, ub, is_fixed, global_p, is_linked
|
||||
global_parameter = []
|
||||
for param, global_flag in zip(param_general, is_global):
|
||||
if global_flag:
|
||||
param.is_global = True
|
||||
global_parameter.append(param)
|
||||
else:
|
||||
global_parameter.append(None)
|
||||
|
||||
return data_parameter, global_parameter
|
||||
|
||||
def set_parameter(self, set_id: str | None, parameter: list[float]) -> int:
|
||||
num_parameter = list(filter(lambda g: not isinstance(g, SelectionWidget), self.global_parameter))
|
||||
@ -304,12 +302,12 @@ class ParameterSingleWidget(QtWidgets.QWidget):
|
||||
|
||||
self._init_ui()
|
||||
|
||||
self._name = name
|
||||
self.name = name
|
||||
self.label.setText(convert(name))
|
||||
self.label.setToolTip('If this is bold then this parameter is only for this data. '
|
||||
'Otherwise, the general parameter is used and displayed')
|
||||
|
||||
self.value_line.setValidator(QtGui.QDoubleValidator())
|
||||
# self.value_line.setValidator(QtGui.QDoubleValidator())
|
||||
self.value_line.textChanged.connect(lambda: self.valueChanged.emit(self.value) if self.value is not None else 0)
|
||||
self.reset_button.clicked.connect(lambda x: self.removeSingleValue.emit())
|
||||
|
||||
@ -343,9 +341,10 @@ class ParameterSingleWidget(QtWidgets.QWidget):
|
||||
|
||||
@value.setter
|
||||
def value(self, val):
|
||||
self.value_line.setText(f'{float(val):.5g}')
|
||||
# self.value_line.setText(f'{float(val):.5g}')
|
||||
self.value_line.setText(f'{val}')
|
||||
|
||||
def show_as_local_parameter(self, is_local):
|
||||
def show_as_local_parameter(self, is_local: bool):
|
||||
if is_local:
|
||||
self.label.setStyleSheet('font-weight: bold;')
|
||||
else:
|
||||
|
@ -128,7 +128,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
|
||||
|
||||
self.newFunction.emit(idx, cnt)
|
||||
|
||||
self.add_function(idx, cnt, op, name, col)
|
||||
self.add_function(idx, cnt, op, name, col, param_names=self.functions[idx].params)
|
||||
|
||||
def add_function(self, idx: int, cnt: int, op: int,
|
||||
name: str, color: str | tuple[float, float, float] | BaseColor, **kwargs):
|
||||
@ -141,6 +141,7 @@ class QFunctionWidget(QtWidgets.QWidget, Ui_Form):
|
||||
qcolor = QtGui.QColor.fromRgbF(*color)
|
||||
else:
|
||||
qcolor = QtGui.QColor(color)
|
||||
|
||||
self.functree.add_function(idx, cnt, op, name, qcolor, **kwargs)
|
||||
|
||||
f = self.functions[idx]
|
||||
|
@ -9,6 +9,9 @@ import numpy as np
|
||||
from pyqtgraph import mkPen
|
||||
|
||||
from nmreval.fit._meta import MultiModel, ModelFactory
|
||||
from nmreval.fit.data import Data
|
||||
from nmreval.fit.model import Model
|
||||
from nmreval.fit.parameter import Parameters
|
||||
from nmreval.fit.result import FitResult
|
||||
|
||||
from .fit_forms import FitTableWidget
|
||||
@ -116,7 +119,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
|
||||
# collect parameter names etc. to allow linkage
|
||||
self._func_list[self._current_model] = self.functionwidget.get_parameter_list()
|
||||
dialog.set_links(self._func_list)
|
||||
# dialog.set_links(self._func_list)
|
||||
|
||||
# show same tab (general parameter/Data parameter)
|
||||
tab_idx = 0
|
||||
@ -219,57 +222,49 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
|
||||
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': []},
|
||||
'links': [], 'color': []}
|
||||
parameter = {
|
||||
'data_parameter': {},
|
||||
'global_parameter': [],
|
||||
'links': [],
|
||||
'color': [],
|
||||
}
|
||||
|
||||
for i, f in enumerate(model):
|
||||
if not f['active']:
|
||||
continue
|
||||
|
||||
try:
|
||||
p, lb, ub, var, glob, links = self.param_widgets[f['cnt']].get_parameter(function_use)
|
||||
p, glob = 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 {}, -1
|
||||
|
||||
p_len = len(parameter['lb'])
|
||||
|
||||
parameter['lb'] += lb
|
||||
parameter['ub'] += ub
|
||||
parameter['var'] += var
|
||||
parameter['links'] += links
|
||||
parameter['color'] += [f['color']]
|
||||
parameter['color'].append(f['color'])
|
||||
parameter['global_parameter'].extend(glob)
|
||||
|
||||
cnt = f['cnt']
|
||||
|
||||
for p_k, v_k in p.items():
|
||||
if add_idx:
|
||||
kw_k = {f'{k}_{cnt}': v for k, v in v_k[1].items()}
|
||||
else:
|
||||
kw_k = v_k[1]
|
||||
|
||||
if p_k in parameter['parameter']:
|
||||
params, kw = parameter['parameter'][p_k]
|
||||
if p_k in parameter['data_parameter']:
|
||||
params, kw = parameter['data_parameter'][p_k]
|
||||
params += v_k[0]
|
||||
kw.update(kw_k)
|
||||
else:
|
||||
parameter['parameter'][p_k] = (v_k[0], kw_k)
|
||||
|
||||
for g_k, g_v in glob.items():
|
||||
if g_k != 'idx':
|
||||
parameter['glob'][g_k] += g_v
|
||||
else:
|
||||
parameter['glob']['idx'] += [idx_i + p_len for idx_i in g_v]
|
||||
parameter['data_parameter'][p_k] = (v_k[0], kw_k)
|
||||
|
||||
if add_idx:
|
||||
cnt += 1
|
||||
|
||||
if f['children']:
|
||||
# recurse for children
|
||||
child_parameter, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt)
|
||||
_, cnt = self._prepare(f['children'], parameter=parameter, add_idx=add_idx, cnt=cnt)
|
||||
|
||||
return parameter, cnt
|
||||
|
||||
@ -280,30 +275,43 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
data = self.data_table.collect_data(default=self.default_combobox.currentData())
|
||||
|
||||
func_dict = {}
|
||||
for k, mod in self.models.items():
|
||||
func, order, param_len = ModelFactory.create_from_list(mod)
|
||||
for model_name, model_parameter in self.models.items():
|
||||
func, order, param_len = ModelFactory.create_from_list(model_parameter)
|
||||
|
||||
if func is None:
|
||||
continue
|
||||
|
||||
if k in data:
|
||||
parameter, _ = self._prepare(mod, function_use=data[k], add_idx=isinstance(func, MultiModel))
|
||||
func = Model(func)
|
||||
|
||||
if model_name in data:
|
||||
parameter, _ = self._prepare(model_parameter, function_use=data[model_name], add_idx=isinstance(func, MultiModel))
|
||||
|
||||
if parameter is None:
|
||||
return
|
||||
|
||||
for (data_parameter, _) in parameter['data_parameter'].values():
|
||||
for pname, param in zip(func.params, data_parameter):
|
||||
param.name = pname
|
||||
|
||||
if self._complex[model_name] is not None:
|
||||
for p_k, p_v in parameter['data_parameter'].items():
|
||||
p_v[1].update({'complex_mode': self._complex[model_name]})
|
||||
parameter['data_parameter'][p_k] = p_v[0], p_v[1]
|
||||
|
||||
for pname, param_value in zip(func.params, parameter['global_parameter']):
|
||||
if param_value is not None:
|
||||
param_value.name = pname
|
||||
func.set_global_parameter(param_value)
|
||||
|
||||
parameter['func'] = func
|
||||
parameter['order'] = order
|
||||
parameter['len'] = param_len
|
||||
parameter['complex'] = self._complex[k]
|
||||
if self._complex[k] is not None:
|
||||
for p_k, p_v in parameter['parameter'].items():
|
||||
p_v[1].update({'complex_mode': self._complex[k]})
|
||||
parameter['parameter'][p_k] = p_v[0], p_v[1]
|
||||
parameter['complex'] = self._complex[model_name]
|
||||
|
||||
func_dict[k] = parameter
|
||||
func_dict[model_name] = parameter
|
||||
|
||||
replaceable = []
|
||||
for k, v in func_dict.items():
|
||||
for model_name, v in func_dict.items():
|
||||
for i, link_i in enumerate(v['links']):
|
||||
if link_i is None:
|
||||
continue
|
||||
@ -334,7 +342,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
QtWidgets.QMessageBox.Ok)
|
||||
return
|
||||
|
||||
replaceable.append((k, i, rep_model, repl_idx))
|
||||
replaceable.append((model_name, i, rep_model, repl_idx))
|
||||
|
||||
replace_value = None
|
||||
for p_k in f['parameter'].values():
|
||||
@ -412,31 +420,37 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
def make_previews(self, x, models_parameters: dict):
|
||||
self.preview_lines = []
|
||||
|
||||
# needed to create namespace
|
||||
param_dict = Parameters()
|
||||
|
||||
cnt = 0
|
||||
for model in models_parameters.values():
|
||||
f = model['func']
|
||||
for parameter_list in model['data_parameter'].values():
|
||||
for i, p_value in enumerate(parameter_list[0]):
|
||||
p_value.name = f.params[i]
|
||||
param_dict.add_parameter(f'a{cnt}', p_value)
|
||||
cnt += 1
|
||||
|
||||
for k, model in models_parameters.items():
|
||||
f = model['func']
|
||||
is_complex = self._complex[k]
|
||||
|
||||
parameters = model['parameter']
|
||||
parameters = model['data_parameter']
|
||||
color = model['color']
|
||||
|
||||
seen_parameter = []
|
||||
|
||||
for p, kwargs in parameters.values():
|
||||
if (p, kwargs) in seen_parameter:
|
||||
# plot only previews with different parameter
|
||||
continue
|
||||
|
||||
seen_parameter.append((p, kwargs))
|
||||
p_value = [pp.value for pp in p]
|
||||
|
||||
if is_complex is not None:
|
||||
y = f.func(x, *p, complex_mode=is_complex, **kwargs)
|
||||
y = f.func(x, *p_value, complex_mode=is_complex, **kwargs)
|
||||
if np.iscomplexobj(y):
|
||||
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)))
|
||||
else:
|
||||
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
|
||||
else:
|
||||
y = f.func(x, *p, **kwargs)
|
||||
y = f.func(x, *p_value, **kwargs)
|
||||
self.preview_lines.append(PlotItem(x=x, y=y, pen=mkPen(width=3)))
|
||||
|
||||
if isinstance(f, MultiModel):
|
||||
@ -444,7 +458,7 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
if is_complex is not None:
|
||||
sub_kwargs.update({'complex_mode': is_complex})
|
||||
|
||||
for i, s in enumerate(f.subs(x, *p, **sub_kwargs)):
|
||||
for i, s in enumerate(f.subs(x, *p_value, **sub_kwargs)):
|
||||
pen_i = mkPen(QtGui.QColor.fromRgbF(*color[i]))
|
||||
if np.iscomplexobj(s):
|
||||
self.preview_lines.append(PlotItem(x=x, y=s.real, pen=pen_i))
|
||||
@ -452,15 +466,17 @@ class QFitDialog(QtWidgets.QWidget, Ui_FitDialog):
|
||||
else:
|
||||
self.preview_lines.append(PlotItem(x=x, y=s, pen=pen_i))
|
||||
|
||||
param_dict.clear()
|
||||
|
||||
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():
|
||||
glob_fit_parameter = []
|
||||
|
||||
for fit_id, fit_curve in parameter.items():
|
||||
if fit_id in fitted_data:
|
||||
fit_parameter = list(fit_curve.parameter.values())
|
||||
|
@ -138,9 +138,7 @@ class DrawingsWidget(QtWidgets.QWidget, Ui_Form):
|
||||
graph_id = self.graph_comboBox.currentData()
|
||||
current_lines = self.lines[graph_id]
|
||||
|
||||
print(remove_rows)
|
||||
for i in reversed(remove_rows):
|
||||
print(i)
|
||||
self.tableWidget.removeRow(i)
|
||||
self.line_deleted.emit(current_lines[i], graph_id)
|
||||
|
||||
|
@ -27,7 +27,6 @@ class MdiAreaTile(QtWidgets.QMdiArea):
|
||||
pos = QtCore.QPoint(0, 0)
|
||||
|
||||
for win in window_list:
|
||||
print(win.minimumSize())
|
||||
win.setGeometry(rect)
|
||||
win.move(pos)
|
||||
|
||||
|
@ -1,110 +0,0 @@
|
||||
import os.path
|
||||
import json
|
||||
import urllib.request
|
||||
import webbrowser
|
||||
import random
|
||||
|
||||
from ..Qt import QtGui, QtCore, QtWidgets
|
||||
from .._py.pokemon import Ui_Dialog
|
||||
|
||||
random.seed()
|
||||
|
||||
|
||||
class QPokemon(QtWidgets.QDialog, Ui_Dialog):
|
||||
def __init__(self, number=None, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self._js = json.load(open(os.path.join(path_to_module, 'utils', 'pokemon.json'), 'r'), encoding='UTF-8')
|
||||
self._id = 0
|
||||
|
||||
if number is not None and number in range(1, len(self._js)+1):
|
||||
poke_nr = f'{number:03d}'
|
||||
self._id = number
|
||||
else:
|
||||
poke_nr = f'{random.randint(1, len(self._js)):03d}'
|
||||
self._id = int(poke_nr)
|
||||
|
||||
self._pokemon = None
|
||||
self.show_pokemon(poke_nr)
|
||||
self.label_15.linkActivated.connect(lambda x: webbrowser.open(x))
|
||||
|
||||
self.buttonBox.clicked.connect(self.randomize)
|
||||
self.next_button.clicked.connect(self.show_next)
|
||||
self.prev_button.clicked.connect(self.show_prev)
|
||||
|
||||
def show_pokemon(self, nr):
|
||||
self._pokemon = self._js[nr]
|
||||
self.setWindowTitle('Pokémon: ' + self._pokemon['Deutsch'])
|
||||
|
||||
for i in range(self.tabWidget.count(), -1, -1):
|
||||
print('i', self.tabWidget.count(), i)
|
||||
try:
|
||||
self.tabWidget.widget(i).deleteLater()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
for n, img in self._pokemon['Bilder']:
|
||||
w = QtWidgets.QWidget()
|
||||
vl = QtWidgets.QVBoxLayout()
|
||||
l = QtWidgets.QLabel(self)
|
||||
l.setAlignment(QtCore.Qt.AlignHCenter)
|
||||
pixmap = QtGui.QPixmap()
|
||||
|
||||
try:
|
||||
pixmap.loadFromData(urllib.request.urlopen(img, timeout=0.5).read())
|
||||
except IOError:
|
||||
l.setText(n)
|
||||
else:
|
||||
sc_pixmap = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
|
||||
l.setPixmap(sc_pixmap)
|
||||
|
||||
vl.addWidget(l)
|
||||
w.setLayout(vl)
|
||||
self.tabWidget.addTab(w, n)
|
||||
|
||||
if len(self._pokemon['Bilder']) <= 1:
|
||||
self.tabWidget.tabBar().setVisible(False)
|
||||
else:
|
||||
self.tabWidget.tabBar().setVisible(True)
|
||||
self.tabWidget.adjustSize()
|
||||
|
||||
self.name.clear()
|
||||
keys = ['National-Dex', 'Kategorie', 'Typ', 'Größe', 'Gewicht', 'Farbe', 'Link']
|
||||
label_list = [self.pokedex_nr, self.category, self.poketype, self.weight, self.height, self.color, self.info]
|
||||
for (k, label) in zip(keys, label_list):
|
||||
v = self._pokemon[k]
|
||||
if isinstance(v, list):
|
||||
v = os.path.join('', *v)
|
||||
|
||||
if k == 'Link':
|
||||
v = '<a href={}>{}</a>'.format(v, v)
|
||||
|
||||
label.setText(v)
|
||||
|
||||
for k in ['Deutsch', 'Japanisch', 'Englisch', 'Französisch']:
|
||||
v = self._pokemon[k]
|
||||
self.name.addItem(k + ': ' + v)
|
||||
|
||||
self.adjustSize()
|
||||
|
||||
def randomize(self, idd):
|
||||
if idd.text() == 'Retry':
|
||||
new_number = f'{random.randint(1, len(self._js)):03d}'
|
||||
self._id = int(new_number)
|
||||
self.show_pokemon(new_number)
|
||||
else:
|
||||
self.close()
|
||||
|
||||
def show_next(self):
|
||||
new_number = self._id + 1
|
||||
if new_number > len(self._js):
|
||||
new_number = 1
|
||||
self._id = new_number
|
||||
self.show_pokemon(f'{new_number:03d}')
|
||||
|
||||
def show_prev(self):
|
||||
new_number = self._id - 1
|
||||
if new_number == 0:
|
||||
new_number = len(self._js)
|
||||
self._id = new_number
|
||||
self.show_pokemon(f'{new_number:03d}')
|
@ -441,7 +441,7 @@ class UpperManagement(QtCore.QObject):
|
||||
# all-encompassing error catch
|
||||
try:
|
||||
for model_id, model_p in parameter.items():
|
||||
m = Model(model_p['func'])
|
||||
m = model_p['func']
|
||||
models[model_id] = m
|
||||
|
||||
m_complex = model_p['complex']
|
||||
@ -450,13 +450,16 @@ class UpperManagement(QtCore.QObject):
|
||||
# iterate over order of set id in active order and access parameter inside loop
|
||||
# instead of directly looping
|
||||
try:
|
||||
list_ids = list(model_p['parameter'].keys())
|
||||
list_ids = list(model_p['data_parameter'].keys())
|
||||
set_order = [self.active_id.index(i) for i in list_ids]
|
||||
except ValueError as e:
|
||||
raise Exception('Getting order failed') from e
|
||||
|
||||
for pos in set_order:
|
||||
set_id = list_ids[pos]
|
||||
|
||||
data_i = self.data[set_id]
|
||||
set_params = model_p['data_parameter'][set_id]
|
||||
try:
|
||||
data_i = self.data[set_id]
|
||||
except KeyError as e:
|
||||
@ -488,7 +491,7 @@ class UpperManagement(QtCore.QObject):
|
||||
inside = np.where((_x >= x_lim[0]) & (_x <= x_lim[1]))
|
||||
else:
|
||||
inside = np.where((_x >= fit_limits[0]) & (_x <= fit_limits[1]))
|
||||
|
||||
|
||||
try:
|
||||
if isinstance(we, str):
|
||||
d = fit_d.Data(_x[inside], _y[inside], we=we, idx=set_id)
|
||||
@ -499,18 +502,12 @@ class UpperManagement(QtCore.QObject):
|
||||
|
||||
d.set_model(m)
|
||||
try:
|
||||
d.set_parameter(set_params[0], var=model_p['var'],
|
||||
lb=model_p['lb'], ub=model_p['ub'],
|
||||
fun_kwargs=set_params[1])
|
||||
d.set_parameter(set_params[0], fun_kwargs=set_params[1])
|
||||
except Exception as e:
|
||||
raise Exception('Setting parameter failed') from e
|
||||
|
||||
self.fitter.add_data(d)
|
||||
|
||||
model_globs = model_p['glob']
|
||||
if model_globs:
|
||||
m.set_global_parameter(**model_p['glob'])
|
||||
|
||||
for links_i in links:
|
||||
self.fitter.set_link_parameter((models[links_i[0]], links_i[1]),
|
||||
(models[links_i[2]], links_i[3]))
|
||||
@ -1170,7 +1167,6 @@ class UpperManagement(QtCore.QObject):
|
||||
|
||||
@QtCore.pyqtSlot(dict)
|
||||
def calc_relaxation(self, opts: dict):
|
||||
|
||||
params = opts['pts']
|
||||
if len(params) == 4:
|
||||
if params[3]:
|
||||
|
Reference in New Issue
Block a user