From f0448fac0ff598984d31e237b193cd342ba13b73 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Mon, 6 May 2024 18:46:27 +0200 Subject: [PATCH 1/7] cut data at x and y --- src/gui_qt/_py/basewindow.py | 21 ++++++++++++++++----- src/gui_qt/data/container.py | 28 +++++++++++++++++----------- src/gui_qt/main/mainwindow.py | 3 ++- src/gui_qt/main/management.py | 13 ++++++++++--- src/nmreval/data/points.py | 29 ++++++++++++++++++++--------- src/resources/_ui/basewindow.ui | 31 +++++++++++++++++++++++++------ 6 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/gui_qt/_py/basewindow.py b/src/gui_qt/_py/basewindow.py index d916548..8d23235 100644 --- a/src/gui_qt/_py/basewindow.py +++ b/src/gui_qt/_py/basewindow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'src/resources/_ui/basewindow.ui' +# Form implementation generated from reading ui file './nmreval/src/resources/_ui/basewindow.ui' # # Created by: PyQt5 UI code generator 5.15.10 # @@ -87,6 +87,8 @@ class Ui_BaseWindow(object): self.menuSave.setObjectName("menuSave") self.menuData = QtWidgets.QMenu(self.menubar) self.menuData.setObjectName("menuData") + self.menuCut_to_visible_range = QtWidgets.QMenu(self.menuData) + self.menuCut_to_visible_range.setObjectName("menuCut_to_visible_range") self.menuHelp = QtWidgets.QMenu(self.menubar) self.menuHelp.setObjectName("menuHelp") self.menuExtra = QtWidgets.QMenu(self.menubar) @@ -304,8 +306,6 @@ class Ui_BaseWindow(object): self.actionDerivation.setObjectName("actionDerivation") self.actionIntegration = QtWidgets.QAction(BaseWindow) self.actionIntegration.setObjectName("actionIntegration") - self.action_cut = QtWidgets.QAction(BaseWindow) - self.action_cut.setObjectName("action_cut") self.actionMove_between_plots = QtWidgets.QAction(BaseWindow) self.actionMove_between_plots.setObjectName("actionMove_between_plots") self.actionBaseline = QtWidgets.QAction(BaseWindow) @@ -368,6 +368,10 @@ class Ui_BaseWindow(object): self.actionExclude_region = QtWidgets.QAction(BaseWindow) self.actionExclude_region.setCheckable(True) self.actionExclude_region.setObjectName("actionExclude_region") + self.action_cut_xaxis = QtWidgets.QAction(BaseWindow) + self.action_cut_xaxis.setObjectName("action_cut_xaxis") + self.action_cut_yaxis = QtWidgets.QAction(BaseWindow) + self.action_cut_yaxis.setObjectName("action_cut_yaxis") self.menuSave.addAction(self.actionSave) self.menuSave.addAction(self.actionExportGraphic) self.menuSave.addAction(self.action_save_fit_parameter) @@ -380,6 +384,9 @@ class Ui_BaseWindow(object): self.menuFile.addSeparator() self.menuFile.addAction(self.action_close) self.menuFile.addSeparator() + self.menuCut_to_visible_range.addSeparator() + self.menuCut_to_visible_range.addAction(self.action_cut_xaxis) + self.menuCut_to_visible_range.addAction(self.action_cut_yaxis) self.menuData.addAction(self.action_new_set) self.menuData.addAction(self.action_delete_sets) self.menuData.addAction(self.actionMove_between_plots) @@ -389,7 +396,7 @@ class Ui_BaseWindow(object): self.menuData.addAction(self.action_sort_pts) self.menuData.addAction(self.actionSkip_points) self.menuData.addSeparator() - self.menuData.addAction(self.action_cut) + self.menuData.addAction(self.menuCut_to_visible_range.menuAction()) self.menuData.addSeparator() self.menuData.addAction(self.actionChange_datatypes) self.menuHelp.addAction(self.actionShow_error_log) @@ -515,6 +522,7 @@ class Ui_BaseWindow(object): self.menuFile.setTitle(_translate("BaseWindow", "&File")) self.menuSave.setTitle(_translate("BaseWindow", "&Save...")) self.menuData.setTitle(_translate("BaseWindow", "&Data")) + self.menuCut_to_visible_range.setTitle(_translate("BaseWindow", "Cut to visible range")) self.menuHelp.setTitle(_translate("BaseWindow", "&Help")) self.menuExtra.setTitle(_translate("BaseWindow", "Math")) self.menuNormalize.setTitle(_translate("BaseWindow", "&Normalize")) @@ -608,7 +616,6 @@ class Ui_BaseWindow(object): self.actionIntegrate.setText(_translate("BaseWindow", "Integrate")) self.actionDerivation.setText(_translate("BaseWindow", "Differentiation...")) self.actionIntegration.setText(_translate("BaseWindow", "Integration...")) - self.action_cut.setText(_translate("BaseWindow", "Cut to visible range")) self.actionMove_between_plots.setText(_translate("BaseWindow", "Move sets...")) self.actionBaseline.setText(_translate("BaseWindow", "Baseline...")) self.actionCalculateT1.setText(_translate("BaseWindow", "Calculate relaxation...")) @@ -636,6 +643,10 @@ class Ui_BaseWindow(object): self.actionBinning.setText(_translate("BaseWindow", "Binning...")) self.actionTNMH.setText(_translate("BaseWindow", "TNMH...")) self.actionExclude_region.setText(_translate("BaseWindow", "Exclude region")) + self.action_cut_xaxis.setText(_translate("BaseWindow", "x axis")) + self.action_cut_xaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible x range.")) + self.action_cut_yaxis.setText(_translate("BaseWindow", "y axis")) + self.action_cut_yaxis.setToolTip(_translate("BaseWindow", "Remove data points outside visible y range. Uses real part of points.")) from ..data.datawidget.datawidget import DataWidget from ..data.integral_widget import IntegralWidget from ..data.point_select import PointSelectWidget diff --git a/src/gui_qt/data/container.py b/src/gui_qt/data/container.py index 71bf9f4..7364579 100644 --- a/src/gui_qt/data/container.py +++ b/src/gui_qt/data/container.py @@ -300,10 +300,12 @@ class ExperimentContainer(QtCore.QObject): self._relations.pop(relation_type) def _update_actions(self): - self.actions.update({'sort': self._data.sort, - 'cut': self._data.cut, - 'norm': self._data.normalize, - 'center': self.center}) + self.actions.update({ + 'sort': self._data.sort, + 'cut': self._data.cut, + 'norm': self._data.normalize, + 'center': self.center, + }) @plot_update def update(self, opts: dict): @@ -311,9 +313,11 @@ class ExperimentContainer(QtCore.QObject): def get_properties(self) -> dict: props = OrderedDict() - props['General'] = OrderedDict([('Name', self.name), - ('Value', str(self.value)), - ('Group', str(self.group))]) + props['General'] = OrderedDict([ + ('Name', self.name), + ('Value', str(self.value)), + ('Group', str(self.group)), + ]) props['Symbol'] = OrderedDict() props['Line'] = OrderedDict() @@ -480,10 +484,12 @@ class ExperimentContainer(QtCore.QObject): else: prefix = f'g[{i}].s[{j}].' - namespace = {prefix + 'x': (self.x, 'x values'), - prefix + 'y': [self.y, 'y values'], - prefix + 'y_err': (self.y_err, 'y error values'), - prefix + 'value': (self.value, str(self.value))} + namespace = { + prefix + 'x': (self.x, 'x values'), + prefix + 'y': [self.y, 'y values'], + prefix + 'y_err': (self.y_err, 'y error values'), + prefix + 'value': (self.value, str(self.value)), + } if len(self._fits) == 1: namespace.update({ diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 7f6123b..a71ce3a 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -233,7 +233,8 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.action_norm_first.triggered.connect(lambda: self.management.apply('norm', ('first',))) self.action_norm_last.triggered.connect(lambda: self.management.apply('norm', ('last',))) self.action_norm_area.triggered.connect(lambda: self.management.apply('norm', ('area',))) - self.action_cut.triggered.connect(lambda: self.management.cut()) + self.action_cut_xaxis.triggered.connect(lambda: self.management.cut(True, False)) + self.action_cut_yaxis.triggered.connect(lambda: self.management.cut(False, True)) self.actionConcatenate_sets.triggered.connect(lambda: self.management.cat()) diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index 56c387a..f47362b 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -450,10 +450,17 @@ class UpperManagement(QtCore.QObject): self.undostack.push(single_undo) self.undostack.endMacro() - def cut(self): + def cut(self, x: bool = False, y: bool = False) -> None: if self.current_graph: - xlim, _ = self.graphs[self.current_graph].ranges - self.apply('cut', xlim) + xlim, ylim = self.graphs[self.current_graph].ranges + + if x is False: + xlim = (None, None) + + if y is False: + ylim = (None, None) + + self.apply('cut', (*xlim, *ylim)) @QtCore.pyqtSlot() def unmask(self): diff --git a/src/nmreval/data/points.py b/src/nmreval/data/points.py index 8332084..1f891ae 100644 --- a/src/nmreval/data/points.py +++ b/src/nmreval/data/points.py @@ -540,26 +540,37 @@ class Points: return self - def cut(self, low_lim: float = None, high_lim: float = None): + def cut(self, x_low: float = None, x_high: float = None, y_low: float = None, y_high: float = None): """ Cut Args: - low_lim: - high_lim: + x_low: Lower limit + x_high: Upper limit for x values + y_low: Lower limit + y_high: Upper limit for x valuew Returns: """ - if low_lim is None and high_lim is None: + + if x_low is None and x_high is None and y_low is None and y_high is None: return self - if low_lim is None: - low_lim = np.min(self._x) + if x_low is None: + x_low = np.min(self._x)-1 - if high_lim is None: - high_lim = np.max(self._x) + if x_high is None: + x_high = np.max(self._x)+1 - _mask = np.ma.masked_inside(self._x, low_lim, high_lim).mask + if y_low is None: + y_low = np.min(self._y.real)-1 + + if y_high is None: + y_high = np.max(self._y.real)+1 + + x_mask = (self._x >= x_low) & (self._x <= x_high) + y_mask = (self._y.real >= y_low) & (self._y.real <= y_high) + _mask = x_mask & y_mask self._x = self._x[_mask] self._y = self._y[_mask] diff --git a/src/resources/_ui/basewindow.ui b/src/resources/_ui/basewindow.ui index 01987b8..85c1f68 100644 --- a/src/resources/_ui/basewindow.ui +++ b/src/resources/_ui/basewindow.ui @@ -172,6 +172,14 @@ &Data + + + Cut to visible range + + + + + @@ -181,7 +189,7 @@ - + @@ -862,11 +870,6 @@ Integration... - - - Cut to visible range - - Move sets... @@ -1030,6 +1033,22 @@ Exclude region + + + x axis + + + Remove data points outside visible x range. + + + + + y axis + + + Remove data points outside visible y range. Uses real part of points. + + -- 2.39.5 From ef66cf584a79c4b843e404e24739f9764617ebb2 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Mon, 6 May 2024 18:52:20 +0200 Subject: [PATCH 2/7] increase precision --- src/gui_qt/data/valueeditwidget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui_qt/data/valueeditwidget.py b/src/gui_qt/data/valueeditwidget.py index ae95a5f..de2376b 100644 --- a/src/gui_qt/data/valueeditwidget.py +++ b/src/gui_qt/data/valueeditwidget.py @@ -385,6 +385,6 @@ class ValueModel(QtCore.QAbstractTableModel): @staticmethod def as_string(value) -> str: if isinstance(value, complex): - return f'{value.real:.8g}{value.imag:+.8g}j' + return f'{value.real:.13g}{value.imag:+.13g}j' else: - return f'{value:.8g}' + return f'{value:.13g}' -- 2.39.5 From cc7572fe145aecd00d609a99de6ff4737d0c03d2 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Wed, 15 May 2024 17:15:09 +0200 Subject: [PATCH 3/7] retain settings in FC reader dialog --- src/gui_qt/io/fcbatchreader.py | 8 ++++++++ src/gui_qt/main/mainwindow.py | 16 +++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/gui_qt/io/fcbatchreader.py b/src/gui_qt/io/fcbatchreader.py index c4bb80b..a4444e6 100644 --- a/src/gui_qt/io/fcbatchreader.py +++ b/src/gui_qt/io/fcbatchreader.py @@ -28,6 +28,12 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog): self.listWidget.installEventFilter(self) + def __call__(self, path=None): + if path is None: + path = pathlib.Path().home() + self.path = path + self.listWidget.clear() + def eventFilter(self, src: QtCore.QObject, evt: QtCore.QEvent) -> bool: # intercept key press in listwidget to allow deletion with Del if evt.type() == QtCore.QEvent.Type.KeyPress: @@ -82,6 +88,7 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog): def accept(self): items = [self.listWidget.item(i).text() for i in range(self.listWidget.count())] + print(items) if items: with busy_cursor(): self.read(items) @@ -116,6 +123,7 @@ class QFCReader(QtWidgets.QDialog, Ui_FCEval_dialog): ret_vals = [] ret_vals.extend(fc_eval.get_parameter(path=self.label.text(), kind='temp', parameter=save_variables)) + print(ret_vals) grp = '' if not self.graph_checkbox.isChecked(): diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 7f6123b..5143964 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -62,6 +62,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.fitresult_dialog = None self.eval = None self.editor = None + self.fc_reader = None self.logtext = QTextHandler(self) logger.addHandler(self.logtext) @@ -264,14 +265,15 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): @QtCore.pyqtSlot(name='on_actionOpen_FC_triggered') def read_fc(self): - reader = QFCReader(path=self.path, parent=self) - reader.add_graphs(self.management.graphs.list()) - reader.data_read.connect(self.management.add_new_data) - reader.exec() + if self.fc_reader is None: + self.fc_reader = QFCReader(path=self.path, parent=self) + self.fc_reader.data_read.connect(self.management.add_new_data) + else: + self.fc_reader(path=self.path) + self.fc_reader.add_graphs(self.management.graphs.list()) + self.fc_reader.exec() - self.path = reader.path - - del reader + self.path = self.fc_reader.path @QtCore.pyqtSlot(name='on_actionPrint_triggered') def print(self): -- 2.39.5 From e87c6bf2c16f4b057940b91cd67fc7a8f914519b Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Mon, 27 May 2024 18:05:43 +0200 Subject: [PATCH 4/7] retain settings in interpolation dialog --- src/gui_qt/_py/interpol_dialog.py | 8 ++-- src/gui_qt/main/mainwindow.py | 12 ++++-- src/gui_qt/math/interpol.py | 60 +++++++++++++++++++++------- src/resources/_ui/interpol_dialog.ui | 37 +---------------- 4 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/gui_qt/_py/interpol_dialog.py b/src/gui_qt/_py/interpol_dialog.py index c3b0827..c0ef858 100644 --- a/src/gui_qt/_py/interpol_dialog.py +++ b/src/gui_qt/_py/interpol_dialog.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'src/resources/_ui/interpol_dialog.ui' +# Form implementation generated from reading ui file './nmreval/src/resources/_ui/interpol_dialog.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -55,7 +55,7 @@ class Ui_Dialog(object): self.gridLayout.addWidget(self.interp_comboBox, 4, 1, 1, 1) self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.gridLayout.addWidget(self.buttonBox, 12, 0, 1, 2) self.line = QtWidgets.QFrame(Dialog) @@ -132,8 +132,6 @@ class Ui_Dialog(object): self.label_8.setBuddy(self.dest_combobox) self.retranslateUi(Dialog) - self.buttonBox.accepted.connect(Dialog.accept) # type: ignore - self.buttonBox.rejected.connect(Dialog.reject) # type: ignore QtCore.QMetaObject.connectSlotsByName(Dialog) Dialog.setTabOrder(self.listWidget, self.ylog_checkBox) Dialog.setTabOrder(self.ylog_checkBox, self.interp_comboBox) diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 7f6123b..7505089 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -62,6 +62,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.fitresult_dialog = None self.eval = None self.editor = None + self._interpol_dialog = None self.logtext = QTextHandler(self) logger.addHandler(self.logtext) @@ -701,10 +702,13 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): return gnames = self.management.graphs.tree() - dialog = InterpolDialog(parent=self) - dialog.set_data(gnames, self.current_graph_widget.id) - dialog.new_data.connect(self.management.interpolate_data) - dialog.show() + if self._interpol_dialog is None: + self._interpol_dialog = InterpolDialog(parent=self) + self._interpol_dialog.new_data.connect(self.management.interpolate_data) + else: + self._interpol_dialog() + self._interpol_dialog.set_data(gnames, self.current_graph_widget.id) + self._interpol_dialog.show() @QtCore.pyqtSlot(name='on_action_calc_triggered') def open_eval_dialog(self): diff --git a/src/gui_qt/math/interpol.py b/src/gui_qt/math/interpol.py index c58d03c..046a7bc 100644 --- a/src/gui_qt/math/interpol.py +++ b/src/gui_qt/math/interpol.py @@ -16,6 +16,12 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog): self.step_lineEdit.setValidator(QtGui.QIntValidator()) self._data = {} + self._src_id = None + self._dest_graph = '' + + def __call__(self): + self.listWidget.clear() + self._data = {} @QtCore.pyqtSlot(int, name='on_xaxis_comboBox_currentIndexChanged') def change_x_source(self, idx: int): @@ -25,29 +31,41 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog): def set_data(self, data, current_gid): self.graph_combobox.blockSignals(True) self._data = {} + dest_idx = 0 for (gid, graph_name), sets in data.items(): self.graph_combobox.addItem(graph_name, userData=gid) self.dest_combobox.addItem(graph_name, userData=gid) + if self._dest_graph == gid: + dest_idx = self.dest_combobox.currentIndex() if gid == current_gid: self.make_list(sets) self._data[gid] = sets self.graph_combobox.blockSignals(False) - self.change_graph(0) + self.change_graph(dest_idx) def make_list(self, current_sets): for sid, set_name in current_sets: item = QtWidgets.QListWidgetItem(set_name) - item.setData(QtCore.Qt.UserRole, sid) - item.setCheckState(QtCore.Qt.Checked) + item.setData(QtCore.Qt.ItemDataRole.UserRole, sid) + item.setCheckState(QtCore.Qt.CheckState.Checked) self.listWidget.addItem(item) @QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged') def change_graph(self, idx: int): self.set_combobox.clear() - gid = self.graph_combobox.itemData(idx, QtCore.Qt.UserRole) + gid = self.graph_combobox.itemData(idx, QtCore.Qt.ItemDataRole.UserRole) + set_idx = -1 if gid is not None: - for set_key, set_name in self._data[gid]: + for i, (set_key, set_name) in enumerate(self._data[gid]): + print(self._src_id, set_key, set_name, i) self.set_combobox.addItem(set_name, userData=set_key) + print(self.set_combobox.currentIndex()) + if self._src_id == set_key: + set_idx = i + + print(set_idx) + if set_idx > -1: + self.set_combobox.setCurrentIndex(set_idx) def collect_parameter(self): xlog = self.xlog_checkBox.isChecked() @@ -71,21 +89,35 @@ class InterpolDialog(QtWidgets.QDialog, Ui_Dialog): x_src = (start, stop, step, loggy) else: - x_src = (self.set_combobox.currentData(QtCore.Qt.UserRole),) + self._src_id = self.set_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole) + x_src = (self._src_id,) - dest_graph = self.dest_combobox.currentData(QtCore.Qt.UserRole) + self._dest_graph = self.dest_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole) use_data = [] for i in range(self.listWidget.count()): item = self.listWidget.item(i) - if item.checkState() == QtCore.Qt.Checked: - use_data.append(item.data(QtCore.Qt.UserRole)) + if item.checkState() == QtCore.Qt.CheckState.Checked: + use_data.append(item.data(QtCore.Qt.ItemDataRole.UserRole)) - self.new_data.emit(use_data, mode, xlog, ylog, x_src, dest_graph) + self.new_data.emit(use_data, mode, xlog, ylog, x_src, self._dest_graph) return True - def accept(self): - success = self.collect_parameter() - if success: - super().accept() + def _save_state(self): + self._src_id = self.set_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole) + self._dest_graph = self.dest_combobox.currentData(QtCore.Qt.ItemDataRole.UserRole) + + @QtCore.pyqtSlot(QtWidgets.QAbstractButton, name='on_buttonBox_clicked') + def check_next_actions(self, bttn: QtWidgets.QAbstractButton): + role = self.buttonBox.buttonRole(bttn) + self._save_state() + + if role == self.buttonBox.ButtonRole.RejectRole: + self.close() + else: + success = self.collect_parameter() + + if success and role == self.buttonBox.ButtonRole.AcceptRole: + self.close() + diff --git a/src/resources/_ui/interpol_dialog.ui b/src/resources/_ui/interpol_dialog.ui index 28fa858..865f70b 100644 --- a/src/resources/_ui/interpol_dialog.ui +++ b/src/resources/_ui/interpol_dialog.ui @@ -119,7 +119,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -300,38 +300,5 @@ dest_combobox - - - buttonBox - accepted() - Dialog - accept() - - - 251 - 490 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 319 - 490 - - - 286 - 274 - - - - + -- 2.39.5 From 03cdc225caa2cace9deb3c18f69895c3dab8ece2 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Mon, 24 Jun 2024 17:53:45 +0200 Subject: [PATCH 5/7] add sinc function --- src/nmreval/models/basic.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/nmreval/models/basic.py b/src/nmreval/models/basic.py index 411e218..ce4308d 100644 --- a/src/nmreval/models/basic.py +++ b/src/nmreval/models/basic.py @@ -191,6 +191,18 @@ class PowerLawCross: return ret_val +class Sinc: + type = 'Basic' + name = 'Sinc' + equation = 'C * sinc((x-x_{0})/w)' + params = ['C', 'x_{0}', 'w'] + + @staticmethod + def func(x, c: float, x0: float, w: float): + # numpy sinc is defined as sin(pi*x)/(pi*x) + return c * np.sinc(((x-x0)/w)/np.pi) + + class Sine: """ Wavy sine function -- 2.39.5 From a8fcd658d90c3ed17311431a1a326eae773ac682 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Mon, 15 Jul 2024 17:42:28 +0200 Subject: [PATCH 6/7] set fit toolbar a name; fixes #281 --- src/gui_qt/fit/fit_toolbar.py | 2 +- src/nmreval/distributions/energy.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui_qt/fit/fit_toolbar.py b/src/gui_qt/fit/fit_toolbar.py index 283c335..74ed526 100644 --- a/src/gui_qt/fit/fit_toolbar.py +++ b/src/gui_qt/fit/fit_toolbar.py @@ -10,7 +10,7 @@ class FitToolbar(QtWidgets.QToolBar): limit_menu: QtWidgets.QMenu, parent=None, ): - super().__init__(parent=parent) + super().__init__('Fit', parent=parent) self.fit_action = fitaction self.region = RegionItem() diff --git a/src/nmreval/distributions/energy.py b/src/nmreval/distributions/energy.py index 40f4c7f..059b2a5 100644 --- a/src/nmreval/distributions/energy.py +++ b/src/nmreval/distributions/energy.py @@ -3,7 +3,12 @@ from ctypes import c_double, cast, pointer, c_void_p import numpy as np from scipy import LowLevelCallable -from scipy.integrate import quad, simps as simpson + +from scipy.integrate import quad +try: + from scipy.integrate import simps as simpson +except ImportError: + from scipy.integrate import simpson from .base import Distribution from ..lib.utils import ArrayLike -- 2.39.5 From e0c287d8a9e13e61433a6bde1759e143683fbd17 Mon Sep 17 00:00:00 2001 From: Dominik Demuth Date: Tue, 16 Jul 2024 18:58:31 +0200 Subject: [PATCH 7/7] plot sub-functions; fixes #282 --- src/gui_qt/data/datawidget/datawidget.py | 13 ++++++++++--- src/gui_qt/main/mainwindow.py | 10 +++++++--- src/gui_qt/main/management.py | 14 +++++++++----- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/gui_qt/data/datawidget/datawidget.py b/src/gui_qt/data/datawidget/datawidget.py index e24c10d..23c1451 100644 --- a/src/gui_qt/data/datawidget/datawidget.py +++ b/src/gui_qt/data/datawidget/datawidget.py @@ -17,7 +17,7 @@ class DataTree(QtWidgets.QTreeWidget): moveItem = QtCore.pyqtSignal(list, str, str, int) # items, from, to, new row copyItem = QtCore.pyqtSignal(list, str) saveFits = QtCore.pyqtSignal(list) - extendFits = QtCore.pyqtSignal(list) + extendFits = QtCore.pyqtSignal(list, bool) # noinspection PyUnresolvedReferences def __init__(self, parent=None): @@ -465,7 +465,7 @@ class DataTree(QtWidgets.QTreeWidget): del_action = menu.addAction('Exterminate sets') cp_action = menu.addAction('Replicate sets') cat_action = menu.addAction('Join us!') - plt_action = save_action = extend_action = None + plt_action = save_action = extend_action = subfit_action = None menu.addSeparator() col_menu = menu.addMenu('Color cycle') for c in available_cycles.keys(): @@ -497,6 +497,7 @@ class DataTree(QtWidgets.QTreeWidget): plt_action = menu.addAction('Plot fit parameter') save_action = menu.addAction('Save fit parameter') extend_action = menu.addAction('Extrapolate fit') + subfit_action = menu.addAction('Plot partial functions') action = menu.exec(evt.globalPos()) @@ -504,6 +505,9 @@ class DataTree(QtWidgets.QTreeWidget): for gid, sets in idx.items(): s.extend(sets) + if action is None: + return + if action == del_action: self.management.delete_sets(s) @@ -521,7 +525,10 @@ class DataTree(QtWidgets.QTreeWidget): self.saveFits.emit(s) elif action == extend_action: - self.extendFits.emit(s) + self.extendFits.emit(s, False) + + elif action == subfit_action: + self.extendFits.emit(s, True) elif action.parent() == col_menu: self.management.set_cycle(s, action.text()) diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 34e94ba..6558524 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -991,15 +991,19 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.editor.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) self.editor.show() - @QtCore.pyqtSlot(list) - def extend_fit(self, sets: list): + @QtCore.pyqtSlot(list, bool) + def extend_fit(self, sets: list, only_subplots: bool): + if only_subplots: + self.management.extend_fits(sets, None, True) + return + w = FitExtension(self) res = w.exec() if res: p = w.values spacefunc = geomspace if p[3] else linspace x = spacefunc(p[0], p[1], num=p[2]) - self.management.extend_fits(sets, x) + self.management.extend_fits(sets, x, False) @QtCore.pyqtSlot(name='on_action_create_fit_function_triggered') def open_fitmodel_wizard(self): diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index f47362b..21715fa 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -711,17 +711,21 @@ class UpperManagement(QtCore.QObject): self.newData.emit(f_id_list, gid) - def extend_fits(self, set_id: list, x_range: np.ndarray): + def extend_fits(self, set_id: list, x_range: np.ndarray | None, only_subplots: bool): graphs = {} for sid in set_id: - data = self[sid] - fit = data.copy(full=True, keep_color=True) - fit.data = fit.data.with_new_x(x_range) + data = fit = self[sid] graph_id = data.graph if graph_id not in graphs: graphs[graph_id] = [] - graphs[graph_id].append(self.add(fit)) + + if not only_subplots: + fit = data.copy(full=True, keep_color=True) + if x_range is not None: + fit.data = fit.data.with_new_x(x_range) + + graphs[graph_id].append(self.add(fit)) color_scheme = available_cycles['colorblind'] for subfunc, col in zip(fit.data.sub(fit.x), cycle(color_scheme)): -- 2.39.5