forked from IPKM/nmreval
bugfixes (#188)
Bugfixes for several issues: closes #163, closes #151, closes #176, closes #138, closes #104 Co-authored-by: Dominik Demuth <dominik.demuth@physik.tu-darmstadt.de> Reviewed-on: IPKM/nmreval#188
This commit is contained in:
parent
9815c0df40
commit
92a3933ed4
@ -10,6 +10,10 @@ from ..lib.tables import TableWidget
|
|||||||
|
|
||||||
|
|
||||||
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
||||||
|
"""
|
||||||
|
Widget to show a global parameter
|
||||||
|
"""
|
||||||
|
|
||||||
value_requested = QtCore.pyqtSignal(object)
|
value_requested = QtCore.pyqtSignal(object)
|
||||||
value_changed = QtCore.pyqtSignal(str)
|
value_changed = QtCore.pyqtSignal(str)
|
||||||
state_changed = QtCore.pyqtSignal()
|
state_changed = QtCore.pyqtSignal()
|
||||||
@ -73,11 +77,9 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
|||||||
if bds is not None:
|
if bds is not None:
|
||||||
self.set_bounds(*bds)
|
self.set_bounds(*bds)
|
||||||
|
|
||||||
if fixed is not None:
|
self.fixed_check.setCheckState(QtCore.Qt.CheckState.Unchecked if fixed else QtCore.Qt.CheckState.Checked)
|
||||||
self.fixed_check.setCheckState(QtCore.Qt.Unchecked if fixed else QtCore.Qt.Checked)
|
|
||||||
|
|
||||||
if glob is not None:
|
self.global_checkbox.setCheckState(QtCore.Qt.CheckState.Checked if glob else QtCore.Qt.CheckState.Unchecked)
|
||||||
self.global_checkbox.setCheckState(QtCore.Qt.Checked if glob else QtCore.Qt.Unchecked)
|
|
||||||
|
|
||||||
def get_parameter(self):
|
def get_parameter(self):
|
||||||
try:
|
try:
|
||||||
@ -126,7 +128,7 @@ class FitModelWidget(QtWidgets.QWidget, Ui_FitParameter):
|
|||||||
is_text = True
|
is_text = True
|
||||||
self.global_checkbox.setCheckState(False)
|
self.global_checkbox.setCheckState(False)
|
||||||
|
|
||||||
self.set_fixed(is_text)
|
self.set_fixed(is_text or self.fixed_check.isChecked())
|
||||||
|
|
||||||
|
|
||||||
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
|
class QSaveModelDialog(QtWidgets.QDialog, Ui_SaveDialog):
|
||||||
|
@ -27,6 +27,7 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
mouseDoubleClicked = QtCore.pyqtSignal()
|
mouseDoubleClicked = QtCore.pyqtSignal()
|
||||||
positionClicked = QtCore.pyqtSignal(tuple, bool)
|
positionClicked = QtCore.pyqtSignal(tuple, bool)
|
||||||
aboutToClose = QtCore.pyqtSignal(list)
|
aboutToClose = QtCore.pyqtSignal(list)
|
||||||
|
newData = QtCore.pyqtSignal(list, str)
|
||||||
|
|
||||||
counter = itertools.count()
|
counter = itertools.count()
|
||||||
|
|
||||||
@ -87,6 +88,9 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
|
|
||||||
self.bwbutton.toggled.connect(self.change_background)
|
self.bwbutton.toggled.connect(self.change_background)
|
||||||
|
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
self.graphic.installEventFilter(self)
|
||||||
|
|
||||||
def _init_gui(self):
|
def _init_gui(self):
|
||||||
self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
|
self.setWindowTitle('Graph ' + str(next(QGraphWindow.counter)))
|
||||||
|
|
||||||
@ -119,6 +123,34 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
for lineedit in [self.xmin_lineedit, self.xmax_lineedit, self.ymin_lineedit, self.ymax_lineedit]:
|
for lineedit in [self.xmin_lineedit, self.xmax_lineedit, self.ymin_lineedit, self.ymax_lineedit]:
|
||||||
lineedit.setValidator(QtGui.QDoubleValidator())
|
lineedit.setValidator(QtGui.QDoubleValidator())
|
||||||
|
|
||||||
|
def eventFilter(self, obj: QtCore.QObject, evt: QtCore.QEvent):
|
||||||
|
"""
|
||||||
|
Catch drag and Drop to prevent anything inside self.graphic to accept the events.
|
||||||
|
Without event filter, we cannot process it here and start file reading
|
||||||
|
"""
|
||||||
|
if evt.type() == QtCore.QEvent.Type.DragEnter:
|
||||||
|
evt.accept()
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif evt.type() == QtCore.QEvent.Type.Drop:
|
||||||
|
return self._handle_drop(evt)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def dropEvent(self, evt: QtGui.QDropEvent):
|
||||||
|
return self._handle_drop(evt)
|
||||||
|
|
||||||
|
def _handle_drop(self, evt: QtGui.QDropEvent):
|
||||||
|
if evt.mimeData().hasUrls():
|
||||||
|
files = [str(url.toLocalFile()) for url in evt.mimeData().urls()]
|
||||||
|
self.newData.emit(files, self.id)
|
||||||
|
|
||||||
|
evt.accept()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def __contains__(self, item: str):
|
def __contains__(self, item: str):
|
||||||
return item in self.sets
|
return item in self.sets
|
||||||
|
|
||||||
@ -304,6 +336,8 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow):
|
|||||||
if item in self.graphic.items():
|
if item in self.graphic.items():
|
||||||
self.graphic.removeItem(item)
|
self.graphic.removeItem(item)
|
||||||
|
|
||||||
|
self.show_legend()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool, name='on_imag_button_toggled')
|
@QtCore.pyqtSlot(bool, name='on_imag_button_toggled')
|
||||||
@QtCore.pyqtSlot(bool, name='on_real_button_toggled')
|
@QtCore.pyqtSlot(bool, name='on_real_button_toggled')
|
||||||
def set_imag_visible(self, visible: bool):
|
def set_imag_visible(self, visible: bool):
|
||||||
|
@ -7,8 +7,11 @@ from ..graphs.graphwindow import QGraphWindow
|
|||||||
|
|
||||||
|
|
||||||
class MdiAreaTile(QtWidgets.QMdiArea):
|
class MdiAreaTile(QtWidgets.QMdiArea):
|
||||||
|
newData = QtCore.pyqtSignal(list)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
|
||||||
def tileSubWindowsVertically(self):
|
def tileSubWindowsVertically(self):
|
||||||
window_list = self.subWindowList()
|
window_list = self.subWindowList()
|
||||||
@ -47,3 +50,8 @@ class MdiAreaTile(QtWidgets.QMdiArea):
|
|||||||
if isinstance(wdgt, QGraphWindow) and wdgt.id == key:
|
if isinstance(wdgt, QGraphWindow) and wdgt.id == key:
|
||||||
self.setActiveSubWindow(win)
|
self.setActiveSubWindow(win)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def dropEvent(self, evt):
|
||||||
|
if evt.mimeData().hasUrls():
|
||||||
|
files = [str(url.toLocalFile()) for url in evt.mimeData().urls()]
|
||||||
|
self.newData.emit(files)
|
||||||
|
@ -108,7 +108,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.fitlim_button.setIcon(get_icon('fit_region'))
|
self.fitlim_button.setIcon(get_icon('fit_region'))
|
||||||
self.toolBar_fit.addWidget(self.fitlim_button)
|
self.toolBar_fit.addWidget(self.fitlim_button)
|
||||||
|
|
||||||
self.area.dragEnterEvent = self.dragEnterEvent
|
# self.area.dragEnterEvent = self.dragEnterEvent
|
||||||
|
|
||||||
while self.tabWidget.count() > 2:
|
while self.tabWidget.count() > 2:
|
||||||
self.tabWidget.removeTab(self.tabWidget.count()-1)
|
self.tabWidget.removeTab(self.tabWidget.count()-1)
|
||||||
@ -201,6 +201,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
self.management.unset_state.connect(lambda x: self.datawidget.uncheck_sets(x))
|
self.management.unset_state.connect(lambda x: self.datawidget.uncheck_sets(x))
|
||||||
self.management.fitFinished.connect(self.show_fit_results)
|
self.management.fitFinished.connect(self.show_fit_results)
|
||||||
|
|
||||||
|
self.area.newData.connect(lambda x: self.management.load_files(x))
|
||||||
|
|
||||||
self.fit_dialog._management = self.management
|
self.fit_dialog._management = self.management
|
||||||
self.fit_dialog.preview_emit.connect(self.show_fit_preview)
|
self.fit_dialog.preview_emit.connect(self.show_fit_preview)
|
||||||
self.fit_dialog.fitStartSig.connect(self.start_fit)
|
self.fit_dialog.fitStartSig.connect(self.start_fit)
|
||||||
@ -469,6 +471,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
|
|||||||
w.mousePositionChanged.connect(self.mousemoved)
|
w.mousePositionChanged.connect(self.mousemoved)
|
||||||
w.aboutToClose.connect(self.management.delete_sets)
|
w.aboutToClose.connect(self.management.delete_sets)
|
||||||
w.positionClicked.connect(self.point_selected)
|
w.positionClicked.connect(self.point_selected)
|
||||||
|
w.newData.connect(lambda x, y: self.management.load_files(x, new_plot=y))
|
||||||
w.show()
|
w.show()
|
||||||
|
|
||||||
graph_list = self.management.graphs.list()
|
graph_list = self.management.graphs.list()
|
||||||
|
@ -234,6 +234,10 @@ class FitRoutine(object):
|
|||||||
# get variable parameter for fitter
|
# get variable parameter for fitter
|
||||||
p0_k, lb_k, ub_k, var_pars_k = self._prep_data(data)
|
p0_k, lb_k, ub_k, var_pars_k = self._prep_data(data)
|
||||||
|
|
||||||
|
if p0_k is None:
|
||||||
|
self.make_results(data, data.para_keys, var_pars_k, data.para_keys, (len(data.para_keys), len(data.para_keys)),
|
||||||
|
err=None, corr=None, partial_corr=None)
|
||||||
|
else:
|
||||||
if mode == 'lsq':
|
if mode == 'lsq':
|
||||||
self._least_squares_single(data, p0_k, lb_k, ub_k, var_pars_k)
|
self._least_squares_single(data, p0_k, lb_k, ub_k, var_pars_k)
|
||||||
|
|
||||||
@ -247,6 +251,12 @@ class FitRoutine(object):
|
|||||||
else:
|
else:
|
||||||
data_pars, p0, lb, ub, var_pars = self._prep_global(data_groups, linked_parameter)
|
data_pars, p0, lb, ub, var_pars = self._prep_global(data_groups, linked_parameter)
|
||||||
|
|
||||||
|
if not p0:
|
||||||
|
for data_k, p_k in zip(data_groups, data_pars):
|
||||||
|
self.make_results(data_k, p_k, [], p_k, (len(p_k), len(p_k)),
|
||||||
|
err=None, corr=None, partial_corr=None)
|
||||||
|
|
||||||
|
else:
|
||||||
if mode == 'lsq':
|
if mode == 'lsq':
|
||||||
self._least_squares_global(data_groups, p0, lb, ub, var_pars, data_pars)
|
self._least_squares_global(data_groups, p0, lb, ub, var_pars, data_pars)
|
||||||
|
|
||||||
@ -291,7 +301,10 @@ class FitRoutine(object):
|
|||||||
vals.append([v_k.scaled_value, v_k.lb / v_k.scale, v_k.ub / v_k.scale])
|
vals.append([v_k.scaled_value, v_k.lb / v_k.scale, v_k.ub / v_k.scale])
|
||||||
var_pars.append(p_k)
|
var_pars.append(p_k)
|
||||||
|
|
||||||
|
if vals:
|
||||||
pp, lb, ub = zip(*vals)
|
pp, lb, ub = zip(*vals)
|
||||||
|
else:
|
||||||
|
pp = lb = ub = None
|
||||||
|
|
||||||
return pp, lb, ub, var_pars
|
return pp, lb, ub, var_pars
|
||||||
|
|
||||||
|
@ -115,6 +115,7 @@ class GraceEditor:
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def parse(self, filename: str | pathlib.Path):
|
def parse(self, filename: str | pathlib.Path):
|
||||||
|
self.clear()
|
||||||
self.file = pathlib.Path(filename)
|
self.file = pathlib.Path(filename)
|
||||||
|
|
||||||
# we start always with the header
|
# we start always with the header
|
||||||
@ -215,9 +216,8 @@ class GraceEditor:
|
|||||||
def _make_graph(self, line: str):
|
def _make_graph(self, line: str):
|
||||||
m = self._RE_GRAPH_START.match(line)
|
m = self._RE_GRAPH_START.match(line)
|
||||||
g_idx = int(m.group(1))
|
g_idx = int(m.group(1))
|
||||||
if g_idx < len(self.graphs):
|
while g_idx < len(self.graphs):
|
||||||
# this assumes that graphs are ordered in agr file even if missing, e.g. we read gß, g1, g3, ...
|
# this assumes that graphs are ordered in agr file even if missing, e.g. we read gß, g1, g3, ...
|
||||||
while g_idx != len(self.graphs):
|
|
||||||
self.graphs.append(GraceGraph(len(self.graphs)))
|
self.graphs.append(GraceGraph(len(self.graphs)))
|
||||||
|
|
||||||
self.graphs.append(GraceGraph(g_idx))
|
self.graphs.append(GraceGraph(g_idx))
|
||||||
@ -354,8 +354,8 @@ class GraceHeader(list):
|
|||||||
class GraceProperties(list):
|
class GraceProperties(list):
|
||||||
_RE_ENTRY = re.compile(r'(?!.*(on|off)$)' # ignore lines that end with on or off
|
_RE_ENTRY = re.compile(r'(?!.*(on|off)$)' # ignore lines that end with on or off
|
||||||
r'@\s*(?P<graph>[gs]\d+)?\s*' # @ maybe followed by g0 or s12
|
r'@\s*(?P<graph>[gs]\d+)?\s*' # @ maybe followed by g0 or s12
|
||||||
r'(?P<key>[\w\s]*)\s+' # key: stops at last space unless comma-separated values
|
r'(?P<key>[\w\s]*\w)*\s+' # key: stops at last space unless comma-separated values
|
||||||
r'(?P<val>(?:\s*[\d\w.+-]+\s*,)*\s*[\\{}\"\w.+\- ]+)', # one value, maybe more with commas
|
r'(?P<val>(?<!^)(?:\s*[\d\w.+-]+\s*,)*\s*[\S ]+)', # one value, maybe more with commas
|
||||||
re.IGNORECASE | re.VERBOSE)
|
re.IGNORECASE | re.VERBOSE)
|
||||||
_RE_ONOFF = re.compile(r'@\s(?P<graph>[gs]\d+)*\s*(?P<val>[\w\s]*)\s+(on|off)')
|
_RE_ONOFF = re.compile(r'@\s(?P<graph>[gs]\d+)*\s*(?P<val>[\w\s]*)\s+(on|off)')
|
||||||
|
|
||||||
@ -730,7 +730,11 @@ class GraceRegion(list):
|
|||||||
|
|
||||||
|
|
||||||
def _convert_to_value(parse_string):
|
def _convert_to_value(parse_string):
|
||||||
|
if re.match(r'\".*\"', parse_string):
|
||||||
|
tuples = [parse_string]
|
||||||
|
else:
|
||||||
tuples = parse_string.split(',')
|
tuples = parse_string.split(',')
|
||||||
|
|
||||||
for i, v in enumerate(tuples):
|
for i, v in enumerate(tuples):
|
||||||
v = v.strip()
|
v = v.strip()
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ UNSIGNED_NUMBER_RE = re.compile(r'[-+]?\d*[+p.]?\d+([eE][-+]?\d+)?', re.MULTILIN
|
|||||||
|
|
||||||
|
|
||||||
def numbers_from_string(any_string: str) -> list[float]:
|
def numbers_from_string(any_string: str) -> list[float]:
|
||||||
print(any_string)
|
|
||||||
matches = []
|
matches = []
|
||||||
for m in NUMBER_RE.finditer(any_string):
|
for m in NUMBER_RE.finditer(any_string):
|
||||||
matches.append(float(m.group().replace('p', '.')))
|
matches.append(float(m.group().replace('p', '.')))
|
||||||
|
Loading…
Reference in New Issue
Block a user