diff --git a/src/gui_qt/_py/apod_dialog.py b/src/gui_qt/_py/apod_dialog.py
index 6a40632..1f64573 100644
--- a/src/gui_qt/_py/apod_dialog.py
+++ b/src/gui_qt/_py/apod_dialog.py
@@ -14,54 +14,167 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ApodEdit(object):
def setupUi(self, ApodEdit):
ApodEdit.setObjectName("ApodEdit")
- ApodEdit.resize(784, 484)
+ ApodEdit.resize(1144, 655)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(ApodEdit.sizePolicy().hasHeightForWidth())
ApodEdit.setSizePolicy(sizePolicy)
self.gridLayout = QtWidgets.QGridLayout(ApodEdit)
- self.gridLayout.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout.setContentsMargins(9, 9, 9, 9)
self.gridLayout.setSpacing(3)
self.gridLayout.setObjectName("gridLayout")
- self.graphicsView = NMRPlotWidget(ApodEdit)
+ self.time_graph = NMRPlotWidget(ApodEdit)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
- self.graphicsView.setSizePolicy(sizePolicy)
- self.graphicsView.setObjectName("graphicsView")
- self.gridLayout.addWidget(self.graphicsView, 2, 0, 1, 1)
- self.graphicsView_2 = NMRPlotWidget(ApodEdit)
- self.graphicsView_2.setObjectName("graphicsView_2")
- self.gridLayout.addWidget(self.graphicsView_2, 2, 1, 1, 1)
- self.apodcombobox = QtWidgets.QComboBox(ApodEdit)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.apodcombobox.sizePolicy().hasHeightForWidth())
- self.apodcombobox.setSizePolicy(sizePolicy)
- self.apodcombobox.setObjectName("apodcombobox")
- self.gridLayout.addWidget(self.apodcombobox, 0, 0, 1, 1)
- self.widget_layout = QtWidgets.QHBoxLayout()
- self.widget_layout.setContentsMargins(-1, 6, -1, -1)
- self.widget_layout.setSpacing(20)
- self.widget_layout.setObjectName("widget_layout")
- self.gridLayout.addLayout(self.widget_layout, 1, 0, 1, 2)
+ sizePolicy.setHeightForWidth(self.time_graph.sizePolicy().hasHeightForWidth())
+ self.time_graph.setSizePolicy(sizePolicy)
+ self.time_graph.setObjectName("time_graph")
+ self.gridLayout.addWidget(self.time_graph, 2, 1, 1, 1)
+ self.freq_graph = NMRPlotWidget(ApodEdit)
+ self.freq_graph.setObjectName("freq_graph")
+ self.gridLayout.addWidget(self.freq_graph, 2, 2, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(ApodEdit)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
- self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
- self.eqn_label = QtWidgets.QLabel(ApodEdit)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+ self.gridLayout.addWidget(self.buttonBox, 4, 1, 1, 2)
+ self.widget = QtWidgets.QWidget(ApodEdit)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.eqn_label.sizePolicy().hasHeightForWidth())
- self.eqn_label.setSizePolicy(sizePolicy)
+ sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
+ self.widget.setSizePolicy(sizePolicy)
+ self.widget.setObjectName("widget")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.baseline_box = QtWidgets.QCheckBox(self.widget)
+ self.baseline_box.setObjectName("baseline_box")
+ self.verticalLayout.addWidget(self.baseline_box)
+ self.shift_box = QtWidgets.QGroupBox(self.widget)
+ self.shift_box.setFlat(True)
+ self.shift_box.setCheckable(True)
+ self.shift_box.setChecked(False)
+ self.shift_box.setObjectName("shift_box")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.shift_box)
+ self.gridLayout_4.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_4.setSpacing(3)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.ls_lineedit = QtWidgets.QLineEdit(self.shift_box)
+ self.ls_lineedit.setObjectName("ls_lineedit")
+ self.gridLayout_4.addWidget(self.ls_lineedit, 1, 1, 1, 1)
+ self.ls_spinbox = QtWidgets.QSpinBox(self.shift_box)
+ self.ls_spinbox.setMaximum(999999)
+ self.ls_spinbox.setObjectName("ls_spinbox")
+ self.gridLayout_4.addWidget(self.ls_spinbox, 0, 1, 1, 1)
+ self.ls_combobox = QtWidgets.QComboBox(self.shift_box)
+ self.ls_combobox.setObjectName("ls_combobox")
+ self.ls_combobox.addItem("")
+ self.ls_combobox.addItem("")
+ self.gridLayout_4.addWidget(self.ls_combobox, 0, 0, 2, 1)
+ self.verticalLayout.addWidget(self.shift_box)
+ self.apod_box = QtWidgets.QGroupBox(self.widget)
+ self.apod_box.setFlat(True)
+ self.apod_box.setCheckable(True)
+ self.apod_box.setChecked(False)
+ self.apod_box.setObjectName("apod_box")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.apod_box)
+ self.verticalLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_2.setSpacing(3)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.apodcombobox = QtWidgets.QComboBox(self.apod_box)
+ self.apodcombobox.setObjectName("apodcombobox")
+ self.verticalLayout_2.addWidget(self.apodcombobox)
+ self.eqn_label = QtWidgets.QLabel(self.apod_box)
self.eqn_label.setIndent(3)
self.eqn_label.setObjectName("eqn_label")
- self.gridLayout.addWidget(self.eqn_label, 0, 1, 1, 1)
+ self.verticalLayout_2.addWidget(self.eqn_label)
+ self.widget_layout = QtWidgets.QHBoxLayout()
+ self.widget_layout.setContentsMargins(-1, 6, -1, -1)
+ self.widget_layout.setSpacing(20)
+ self.widget_layout.setObjectName("widget_layout")
+ self.verticalLayout_2.addLayout(self.widget_layout)
+ self.verticalLayout.addWidget(self.apod_box)
+ self.zerofill_box = QtWidgets.QGroupBox(self.widget)
+ self.zerofill_box.setFlat(True)
+ self.zerofill_box.setCheckable(True)
+ self.zerofill_box.setChecked(False)
+ self.zerofill_box.setObjectName("zerofill_box")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.zerofill_box)
+ self.horizontalLayout.setContentsMargins(3, 3, 3, 3)
+ self.horizontalLayout.setSpacing(3)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(self.zerofill_box)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.zf_spinbox = QtWidgets.QSpinBox(self.zerofill_box)
+ self.zf_spinbox.setMinimum(1)
+ self.zf_spinbox.setMaximum(3)
+ self.zf_spinbox.setObjectName("zf_spinbox")
+ self.horizontalLayout.addWidget(self.zf_spinbox)
+ self.verticalLayout.addWidget(self.zerofill_box)
+ self.phase_box = QtWidgets.QGroupBox(self.widget)
+ self.phase_box.setFlat(True)
+ self.phase_box.setCheckable(True)
+ self.phase_box.setChecked(False)
+ self.phase_box.setObjectName("phase_box")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.phase_box)
+ self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
+ self.gridLayout_2.setSpacing(3)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.label_3 = QtWidgets.QLabel(self.phase_box)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.phase_box)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
+ self.ph0_spinbox = QtWidgets.QDoubleSpinBox(self.phase_box)
+ self.ph0_spinbox.setWrapping(True)
+ self.ph0_spinbox.setDecimals(1)
+ self.ph0_spinbox.setMinimum(-180.0)
+ self.ph0_spinbox.setMaximum(180.0)
+ self.ph0_spinbox.setSingleStep(0.5)
+ self.ph0_spinbox.setObjectName("ph0_spinbox")
+ self.gridLayout_2.addWidget(self.ph0_spinbox, 0, 1, 1, 1)
+ self.pivot_lineedit = QtWidgets.QLineEdit(self.phase_box)
+ self.pivot_lineedit.setObjectName("pivot_lineedit")
+ self.gridLayout_2.addWidget(self.pivot_lineedit, 2, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.phase_box)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
+ self.ph1_spinbox = QtWidgets.QDoubleSpinBox(self.phase_box)
+ self.ph1_spinbox.setWrapping(True)
+ self.ph1_spinbox.setDecimals(2)
+ self.ph1_spinbox.setMinimum(-720.0)
+ self.ph1_spinbox.setMaximum(720.0)
+ self.ph1_spinbox.setSingleStep(0.05)
+ self.ph1_spinbox.setObjectName("ph1_spinbox")
+ self.gridLayout_2.addWidget(self.ph1_spinbox, 1, 1, 1, 1)
+ self.verticalLayout.addWidget(self.phase_box)
+ self.ft_box = QtWidgets.QGroupBox(self.widget)
+ self.ft_box.setCheckable(True)
+ self.ft_box.setChecked(False)
+ self.ft_box.setObjectName("ft_box")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.ft_box)
+ self.verticalLayout_3.setContentsMargins(3, 3, 3, 3)
+ self.verticalLayout_3.setSpacing(3)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.phase_before_button = QtWidgets.QRadioButton(self.ft_box)
+ self.phase_before_button.setChecked(True)
+ self.phase_before_button.setObjectName("phase_before_button")
+ self.buttonGroup = QtWidgets.QButtonGroup(ApodEdit)
+ self.buttonGroup.setObjectName("buttonGroup")
+ self.buttonGroup.addButton(self.phase_before_button)
+ self.verticalLayout_3.addWidget(self.phase_before_button)
+ self.phase_after_button = QtWidgets.QRadioButton(self.ft_box)
+ self.phase_after_button.setObjectName("phase_after_button")
+ self.buttonGroup.addButton(self.phase_after_button)
+ self.verticalLayout_3.addWidget(self.phase_after_button)
+ self.verticalLayout.addWidget(self.ft_box)
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem)
+ self.gridLayout.addWidget(self.widget, 2, 0, 1, 1)
self.retranslateUi(ApodEdit)
self.buttonBox.accepted.connect(ApodEdit.accept) # type: ignore
@@ -71,5 +184,22 @@ class Ui_ApodEdit(object):
def retranslateUi(self, ApodEdit):
_translate = QtCore.QCoreApplication.translate
ApodEdit.setWindowTitle(_translate("ApodEdit", "Apodization"))
+ self.baseline_box.setText(_translate("ApodEdit", "Baseline"))
+ self.shift_box.setTitle(_translate("ApodEdit", "Shift"))
+ self.ls_lineedit.setText(_translate("ApodEdit", "0"))
+ self.ls_combobox.setItemText(0, _translate("ApodEdit", "Points"))
+ self.ls_combobox.setItemText(1, _translate("ApodEdit", "Seconds"))
+ self.apod_box.setTitle(_translate("ApodEdit", "Apodization"))
self.eqn_label.setText(_translate("ApodEdit", "TextLabel"))
+ self.zerofill_box.setTitle(_translate("ApodEdit", "Zero fill"))
+ self.label.setText(_translate("ApodEdit", "Double length"))
+ self.zf_spinbox.setSuffix(_translate("ApodEdit", "x"))
+ self.phase_box.setTitle(_translate("ApodEdit", "Phase correction"))
+ self.label_3.setText(_translate("ApodEdit", "Phase 1"))
+ self.label_4.setText(_translate("ApodEdit", "Pivot"))
+ self.pivot_lineedit.setText(_translate("ApodEdit", "0"))
+ self.label_2.setText(_translate("ApodEdit", "Phase 0"))
+ self.ft_box.setTitle(_translate("ApodEdit", "Fourier transform"))
+ self.phase_before_button.setText(_translate("ApodEdit", "before phase correction"))
+ self.phase_after_button.setText(_translate("ApodEdit", "after phase correction"))
from ..lib.graph_items import NMRPlotWidget
diff --git a/src/gui_qt/data/container.py b/src/gui_qt/data/container.py
index 106eee3..ec78c6e 100644
--- a/src/gui_qt/data/container.py
+++ b/src/gui_qt/data/container.py
@@ -749,3 +749,41 @@ class SignalContainer(ExperimentContainer):
self._update_actions()
return self
+
+ @plot_update
+ def edit_signal(
+ self: SignalContainer,
+ baseline: tuple[None | bool],
+ leftshift: tuple[None | float, str],
+ zerofill: tuple[None, int],
+ apod: tuple[None, list[float], type[object]],
+ phase: tuple[None | float, float, float],
+ fourier: tuple[None | bool]
+ ):
+ """
+ Function for EditUndoCommand to call if a timesignal or spectra must be worked on.
+ This avoids to update the plot for every action we do and makes it slightly faster.
+ """
+
+ if baseline[0] is not None:
+ self._data.baseline()
+
+ if leftshift[0] is not None:
+ self._data.shift(*leftshift)
+
+ if zerofill[0] is not None:
+ self._data.zerofill(*zerofill)
+
+ if apod[0] is not None:
+ self._data.apod(*apod)
+
+ if fourier[0] is not None:
+ print(fourier)
+ if fourier[0]:
+ if phase[0] is not None:
+ self._data.manual_phase(*phase)
+ self.fourier()
+ else:
+ self.fourier()
+ if phase[0] is not None:
+ self._data.manual_phase(*phase)
diff --git a/src/gui_qt/data/signaledit/__init__.py b/src/gui_qt/data/signaledit/__init__.py
index b92851f..18efbfc 100644
--- a/src/gui_qt/data/signaledit/__init__.py
+++ b/src/gui_qt/data/signaledit/__init__.py
@@ -1,2 +1,2 @@
-from .phase_dialog import QApodDialog, QPhasedialog
+from .phase_dialog import QPreviewDialog
from .baseline_dialog import QBaselineDialog
diff --git a/src/gui_qt/data/signaledit/phase_dialog.py b/src/gui_qt/data/signaledit/phase_dialog.py
index 5962ed0..69a4d16 100644
--- a/src/gui_qt/data/signaledit/phase_dialog.py
+++ b/src/gui_qt/data/signaledit/phase_dialog.py
@@ -1,119 +1,43 @@
from __future__ import annotations
-import numpy as np
from pyqtgraph import mkPen
-from numpy import inf, linspace
-from numpy.fft import fft, fftfreq, fftshift
+import numpy as np
+from numpy import pi
+from numpy.fft import fft, fftshift, fftfreq
+from nmreval.data import FID, Spectrum
from ...lib.pg_objects import PlotItem, LogInfiniteLine
from nmreval.lib.importer import find_models
from nmreval.math import apodization as apodization
from nmreval.utils.text import convert
-from ...Qt import QtCore, QtWidgets
+from ...Qt import QtCore, QtWidgets, QtGui
from ..._py.apod_dialog import Ui_ApodEdit
-from ..._py.phase_corr_dialog import Ui_SignalEdit
from ...lib.forms import FormWidget
-class QPreviewDialogs(QtWidgets.QDialog):
+class QPreviewDialog(QtWidgets.QDialog, Ui_ApodEdit):
finished = QtCore.pyqtSignal(str, tuple)
def __init__(self, parent=None):
super().__init__(parent=parent)
- self.data = []
- self.graphs = []
-
- self.mode = ''
-
- def setRange(self, xlim: list, ylim: list, logmode: list[bool]):
- self.graphicsView.getPlotItem().setLogMode(x=logmode[0], y=logmode[1])
- if logmode[0]:
- xlim = [np.log10(x) for x in xlim]
- if logmode[1]:
- ylim = [np.log10(y) for y in ylim]
-
- self.graphicsView.setRange(xRange=xlim, yRange=ylim, padding=0, disableAutoRange=True)
-
- def add_data(self, x, y):
- self.data.append((x, y))
- real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b'), )
- imag_plt = PlotItem(x=x, y=y.imag, pen=mkPen('r'))
- self.graphs.append((real_plt, imag_plt))
- self.graphicsView.addItem(real_plt)
- self.graphicsView.addItem(imag_plt)
-
- def done(self, val):
- self.cleanup()
- super().done(val)
-
- def close(self):
- self.cleanup()
- super().close()
-
- def accept(self):
- self.finished.emit(self.mode, self.get_value())
- super().accept()
-
- def get_value(self):
- raise NotImplementedError
-
- def cleanup(self):
- self.blockSignals(True)
-
- for line in self.graphs:
- for g in line:
- self.graphicsView.removeItem(g)
- del g
-
- self.graphicsView.clear()
-
- self.data = []
- self.graphs = []
-
- self.blockSignals(False)
-
-
-class QPhasedialog(QPreviewDialogs, Ui_SignalEdit):
- def __init__(self, parent=None):
- super().__init__(parent=parent)
self.setupUi(self)
- self.mode = 'ph'
+ self.data = []
+ self.graphs = []
+ self._tmp_data_bl = []
+ self._tmp_data_zf = []
+ self._tmp_data_ls = []
+ self._tmp_data_ap = []
+ self._tmp_data_ph = []
self.pvt_line = LogInfiniteLine(pos=0, movable=True)
- self.graphicsView.addItem(self.pvt_line)
+ self.freq_graph.addItem(self.pvt_line)
self.pvt_line.sigPositionChanged.connect(self.move_line)
- @QtCore.pyqtSlot(float, name='on_ph1slider_valueChanged')
- @QtCore.pyqtSlot(float, name='on_ph0slider_valueChanged')
- def _temp_phase(self, *args):
- ph0, ph1, pvt = self.get_value()
- self.pvt_line.setValue(pvt)
+ self.ls_lineedit.hide()
- for i, (x, y) in enumerate(self.data):
- phasecorr = np.exp(-1j * (ph0 + ph1*(x-pvt)/np.max(x))*np.pi/180.)
- _y = y * phasecorr
-
- self.graphs[i][0].setData(x=x, y=_y.real)
- self.graphs[i][1].setData(x=x, y=_y.imag)
-
- def get_value(self):
- return float(self.ph0slider.text()), float(self.ph1slider.text()), float(self.pivot_lineedit.text())
-
- def move_line(self, evt):
- self.pivot_lineedit.setText(f'{evt.value():.5g}')
-
-
-class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
- def __init__(self, parent=None):
- super().__init__(parent=parent)
- self.setupUi(self)
-
- self._limits = (-inf, inf), -inf
-
- self.apods = []
self.apods = find_models(apodization)
self.apodcombobox.blockSignals(True)
@@ -122,72 +46,246 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
self.apodcombobox.blockSignals(False)
self.apod_graph = PlotItem(x=[], y=[])
- self.graphicsView.addItem(self.apod_graph)
+ self.time_graph.addItem(self.apod_graph)
- self.mode = 'ap'
+ for g in [self.freq_graph, self.time_graph]:
+ pl = g.getPlotItem()
+ pl.hideButtons()
+ pl.setMenuEnabled(False)
+
+ self._all_time = None
+ self._all_freq = None
self.change_apodization(0)
- def add_data(self, x, y):
- real_plt = PlotItem(x=x, y=y.real, pen=mkPen('b'))
- # imag_plt = (x=x, y=y.imag, pen=pg.mkPen('r'))
- self.graphicsView.addItem(real_plt)
- # self.graphicsView.addItem(imag_plt)
+ self.shift_box.clicked.connect(self._update_shift)
+ self.ls_spinbox.valueChanged.connect(self._update_shift)
+ self.ls_lineedit.setValidator(QtGui.QDoubleValidator())
+ self.ls_lineedit.textChanged.connect(self._update_shift)
+ self.zerofill_box.clicked.connect(self._update_zf)
+ self.zf_spinbox.valueChanged.connect(self._update_zf)
+
+ self.apod_box.clicked.connect(self._update_apod)
+
+ self.phase_box.clicked.connect(self._update_phase)
+ self.ph0_spinbox.valueChanged.connect(self._update_phase)
+ self.ph1_spinbox.valueChanged.connect(self._update_phase)
+ self.pivot_lineedit.setValidator(QtGui.QDoubleValidator())
+ self.pivot_lineedit.textChanged.connect(self._update_phase)
+ self.pivot_lineedit.textEdited.connect(lambda x: self.pvt_line.setValue(float(x)))
+
+ def add_data(self: QPreviewDialog, data: FID | Spectrum) -> bool:
+
+ if isinstance(data, FID):
+ if self._all_freq:
+ msg = QtWidgets.QMessageBox.warning(self, 'Mixed types',
+ 'Timesignals and spectra cannot be edited at the same time.')
+ return False
+ else:
+ self._all_time = True
+ self._all_freq = False
+
+ elif isinstance(data, Spectrum):
+ if self._all_time:
+ msg = QtWidgets.QMessageBox.warning(self, 'Mixed types',
+ 'Timesignals and spectra cannot be edited at the same time.')
+ return False
+ else:
+ self._all_time = False
+ self._all_freq = True
+
+ fid = data.copy()
+ spec = self._temp_fft(fid.x, fid.y)
+
+ x_len = data.x.size
+ self.zf_spinbox.setMaximum(min(2**17//x_len, 3))
+
+ real_plt = PlotItem(x=fid.x, y=fid.y.real, pen=mkPen('b'))
+ imag_plt = PlotItem(x=fid.x, y=fid.y.imag, pen=mkPen('r'))
+ self.time_graph.addItem(imag_plt)
+ self.time_graph.addItem(real_plt)
+
+ real_plt_fft = PlotItem(x=spec[0], y=spec[1].real, pen=mkPen('b'))
+ imag_plt_fft = PlotItem(x=spec[0], y=spec[1].imag, pen=mkPen('r'))
+ self.freq_graph.addItem(imag_plt_fft)
+ self.freq_graph.addItem(real_plt_fft)
+
+ self.data.append(data)
+ for p in [self._tmp_data_bl, self._tmp_data_ls]:
+ p.append(data.y.copy())
+
+ for p in [self._tmp_data_zf, self._tmp_data_ap]:
+ p.append((data.x, data.y.copy()))
+
+ self._tmp_data_ph.append((data.x, data.y, spec[0], spec[1]))
+
+ self.graphs.append((real_plt, imag_plt, real_plt_fft, imag_plt_fft))
+
+ return True
+
+ @QtCore.pyqtSlot(name='on_baseline_box_clicked')
+ def _update_bl(self):
+ if self.baseline_box.isChecked():
+ for y in self._tmp_data_bl:
+ y -= y[int(-0.12*y.size):].mean()
+ else:
+ for i, d in enumerate(self.data):
+ self._tmp_data_bl[i] = d.y.copy()
+
+ self._update_shift()
+
+ def _update_shift(self):
+ if self.shift_box.isChecked():
+ if self.ls_combobox.currentIndex() == 0:
+ num_points = self.ls_spinbox.value()
+ is_time = False
+ else:
+ num_points = float(self.ls_lineedit.text())
+ is_time = True
+
+ for i, y in enumerate(self._tmp_data_bl):
+ self._tmp_data_ls[i] = self._temp_leftshift(self.data[i].dx, y, num_points, is_time)
+
+ else:
+ for i, y in enumerate(self._tmp_data_bl):
+ self._tmp_data_ls[i] = y
+
+ self._update_zf()
+
+ def _update_zf(self):
+ zf_padding = self.zf_spinbox.value()
+
+ if self.zerofill_box.isChecked():
+ for i, y in enumerate(self._tmp_data_ls):
+ self._tmp_data_zf[i] = self._temp_zerofill(self.data[i].x, y, zf_padding)
+
+ else:
+ for i, y in enumerate(self._tmp_data_ls):
+ self._tmp_data_zf[i] = self.data[i].x, y
+
+ self._update_apod()
+
+ def _update_apod(self):
+ if self.apod_box.isChecked():
+ model = self.apods[self.apodcombobox.currentIndex()]
+ p = self._get_parameter()
+
+ x_limit = np.inf, -np.inf
+ y_limit = -np.inf
+
+ for i, (x, y) in enumerate(self._tmp_data_zf):
+ self._tmp_data_ap[i] = x, y * model.apod(x, *p)
+ y_limit = max(y.real.max(), y_limit)
+ x_limit = min(x_limit[0], x.min()), max(x_limit[1], x.max())
+
+ _x_apod = np.linspace(*x_limit, num=150)
+ _y_apod = model.apod(_x_apod, *p)
+ self.apod_graph.setData(x=_x_apod, y=y_limit * _y_apod)
+ self.apod_graph.show()
+
+ else:
+ for i, (x, y) in enumerate(self._tmp_data_zf):
+ self._tmp_data_ap[i] = x, y
+
+ self.apod_graph.hide()
+
+ self._update_phase()
+
+ def _update_phase(self):
+ if self.phase_box.isChecked():
+ pvt = float(self.pivot_lineedit.text())
+ self.pvt_line.show()
+ ph0 = self.ph0_spinbox.value()
+ ph1 = self.ph1_spinbox.value()
+
+ for i, (x, y) in enumerate(self._tmp_data_ap):
+ x_fft, y_fft = self._temp_fft(x, y)
+
+ if ph0 != 0:
+ y = self._temp_phase(x, y, ph0, 0, 0)
+ y_fft = self._temp_phase(x, y_fft, ph0, ph1, pvt)
+ elif ph1 != 0:
+ y_fft = self._temp_phase(x, y_fft, ph0, ph1, pvt)
+
+ self._tmp_data_ph[i] = x, y, x_fft, y_fft
+
+ else:
+ self.pvt_line.hide()
+ for i, (x, y) in enumerate(self._tmp_data_ap):
+ self._tmp_data_ph[i] = x, y, *self._temp_fft(x, y)
+
+ self._update_plots()
+
+ def _update_plots(self):
+ for i, (x, y, xf, yf) in enumerate(self._tmp_data_ph):
+ self.graphs[i][0].setData(x=x, y=y.real)
+ self.graphs[i][1].setData(x=x, y=y.imag)
+
+ self.graphs[i][2].setData(x=xf, y=yf.real)
+ self.graphs[i][3].setData(x=xf, y=yf.imag)
+
+ @staticmethod
+ def _temp_phase(x: np.ndarray, y: np.ndarray, ph0: float, ph1: float, pvt: float) -> np.ndarray:
+ phase_correction = np.exp(-1j * (ph0 + ph1 * (x - pvt) / x.max()) * pi / 180.)
+ _y = y * phase_correction
+
+ return _y
+
+ @staticmethod
+ def _temp_zerofill(x: np.ndarray, y: np.ndarray, num_padding: int) -> tuple[np.ndarray, np.ndarray]:
+ length = x.size
+ factor = 2**num_padding
+
+ _y = np.r_[y, np.zeros((factor-1) * length)]
+
+ _temp_x = np.arange(1, (factor-1) * length+1) * (x[1]-x[0]) + np.max(x)
+ _x = np.r_[x, _temp_x]
+
+ return _x, _y
+
+ @staticmethod
+ def _temp_leftshift(dx: np.ndarray, y: np.ndarray, points: float | int, is_time: bool) -> np.ndarray:
+ if is_time:
+ points = int(points//dx)
+ _y = np.roll(y, -points)
+ _y[-points-1:] = 0
+
+ return _y
+
+ @staticmethod
+ def _temp_fft(x: np.ndarray, y: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
y_fft = fftshift(fft(y))
x_fft = fftshift(fftfreq(len(x), d=x[1]-x[0]))
- real_plt_fft = PlotItem(x=x_fft, y=y_fft.real, pen=mkPen('b'))
- # imag_plt_fft = pg.PlotDataItem(x=x_fft, y=y_fft.imag, pen=pg.mkPen('b'))
- self.graphicsView_2.addItem(real_plt_fft)
- # self.graphicsView_2.addItem(imag_plt_fft)
- self.graphs.append((real_plt, real_plt_fft))
- self.data.append((x, y, x_fft))
+ return x_fft, y_fft
- xlimits = (max(x.min(), self._limits[0][0]), min(x.max(), self._limits[0][1]))
- ylimit = max(self._limits[1], y.real.max())
- self._limits = xlimits, ylimit
+ def move_line(self, evt):
+ self.pivot_lineedit.setText(f'{evt.value():.5g}')
@QtCore.pyqtSlot(int, name='on_apodcombobox_currentIndexChanged')
- def change_apodization(self, index):
+ def change_apodization(self, index: int) -> None:
# delete old widgets
self.eqn_label.setText(convert(self.apods[index].equation))
while self.widget_layout.count():
item = self.widget_layout.takeAt(0)
+ if isinstance(item, FormWidget):
+ item.disconnect()
+
try:
item.widget().deleteLater()
except AttributeError:
pass
# set up parameter widgets for new model
- for k, v in enumerate(self.apods[index]().params):
- widgt = FormWidget(name=v)
- widgt.valueChanged.connect(self._temp_apod)
- self.widget_layout.addWidget(widgt)
+ for k, v in enumerate(self.apods[index].params):
+ widget = FormWidget(name=v)
+ widget.value = 1
+ widget.valueChanged.connect(self._update_apod)
+ self.widget_layout.addWidget(widget)
self.widget_layout.addStretch()
- self._temp_apod()
-
- def _temp_apod(self):
- apodmodel = self.apods[self.apodcombobox.currentIndex()]
- p = self._get_parameter()
-
- if self.data:
- for i, (x, y, x_fft) in enumerate(self.data):
- y2 = apodmodel.apod(x, *p)
- _y = y2 * y
- self.graphs[i][0].setData(x=x, y=_y.real)
- # self.graphs[i][1].setData(y=_y.imag)
- y_fft = fftshift(fft(_y))
- self.graphs[i][1].setData(x=x_fft, y=y_fft.real)
- # self.graphs[i][3].setData(y=y_fft.imag)
-
- _x_apod = linspace(self._limits[0][0], self._limits[0][1])
- try:
- _y_apod = apodmodel.apod(_x_apod, *p)
- self.apod_graph.setData(x=_x_apod, y=self._limits[1]*_y_apod)
- except IndexError:
- pass
+ self._update_apod()
def _get_parameter(self):
p = []
@@ -201,8 +299,78 @@ class QApodDialog(QPreviewDialogs, Ui_ApodEdit):
return p
- def get_value(self):
- apodmodel = self.apods[self.apodcombobox.currentIndex()]
- p = self._get_parameter()
+ @QtCore.pyqtSlot(int, name='on_ls_combobox_currentIndexChanged')
+ def change_ls(self, idx: int) -> None:
+ self.ls_lineedit.setVisible(bool(idx))
+ self.ls_spinbox.setVisible(not bool(idx))
- return p, apodmodel
+ @QtCore.pyqtSlot(bool, name='on_ft_checkbox_stateChanged')
+ def change_ft(self, state: bool):
+ self.ph1_spinbox.setEnabled(state)
+ self.pivot_lineedit.setEnabled(state)
+
+ def cleanup(self):
+ self.blockSignals(True)
+
+ for line in self.graphs:
+ for g in line:
+ self.time_graph.removeItem(g)
+ self.freq_graph.removeItem(g)
+ del g
+
+ self.time_graph.clear()
+ self.freq_graph.clear()
+
+ self._tmp_data_ap = []
+ self._tmp_data_bl = []
+ self._tmp_data_ls = []
+ self._tmp_data_ph = []
+ self._tmp_data_zf = []
+
+ self.data = []
+ self.graphs = []
+
+ self.blockSignals(False)
+
+ def get_value(self):
+ edits = [(None,), (None,), (None,), (None,), (None,), (None,)]
+
+ if self.baseline_box.isChecked():
+ edits[0] = (True,)
+
+ if self.zerofill_box.isChecked():
+ edits[2] = (self.zf_spinbox.value(),)
+
+ if self.shift_box.isChecked():
+ if self.ls_combobox.currentIndex() == 0:
+ edits[1] = (self.ls_spinbox.value(), 'pts')
+ else:
+ edits[1] = (float(self.ls_lineedit.text()), 'time')
+
+ if self.apod_box.isChecked():
+ edits[3] = (self._get_parameter(), self.apods[self.apodcombobox.currentIndex()])
+
+ if self.phase_box.isChecked():
+ edits[4] = (self.ph0_spinbox.value(), self.ph1_spinbox.value(), float(self.pivot_lineedit.text()))
+
+ if self.ft_box.isChecked():
+ edits[5] = (self.phase_before_button.isChecked(),)
+
+ return edits
+
+ def exec(self):
+ self._prepare_ui()
+ return super().exec()
+
+ def _prepare_ui(self):
+ """Stuff we have to do before showing the window but after all the data was added"""
+
+ vb = self.freq_graph.getPlotItem().getViewBox()
+ vb.disableAutoRange(axis=vb.YAxis)
+
+ vb = self.time_graph.getPlotItem().getViewBox()
+ vb.disableAutoRange(axis=vb.YAxis)
+
+ self.zerofill_box.setVisible(self._all_time)
+ self.apod_box.setVisible(self._all_time)
+ self.shift_box.setVisible(self._all_time)
diff --git a/src/gui_qt/lib/undos.py b/src/gui_qt/lib/undos.py
index 25f642a..683dedf 100644
--- a/src/gui_qt/lib/undos.py
+++ b/src/gui_qt/lib/undos.py
@@ -87,6 +87,21 @@ class ShiftCommand(QtWidgets.QUndoCommand):
self.__data.apply('ls', self.__args)
+class EditCommand(QtWidgets.QUndoCommand):
+ def __init__(self, data, *args):
+ super().__init__('Edit signal')
+
+ self.__data = data
+ self.__arguments = args
+ self.__original = copy.deepcopy(self.__data.data)
+
+ def undo(self):
+ self.__data.data = copy.deepcopy(self.__original)
+
+ def redo(self):
+ self.__data.edit_signal(*self.__arguments)
+
+
class NormCommand(QtWidgets.QUndoCommand):
def __init__(self, data, mode):
super().__init__('Normalize')
diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py
index e20ae11..19af6cc 100644
--- a/src/gui_qt/main/mainwindow.py
+++ b/src/gui_qt/main/mainwindow.py
@@ -15,7 +15,7 @@ from nmreval.io.sessionwriter import NMRWriter
from .management import UpperManagement
from ..Qt import QtGui, QtPrintSupport
from ..data.shift_graphs import QShift
-from ..data.signaledit import QApodDialog, QBaselineDialog, QPhasedialog
+from ..data.signaledit import QPreviewDialog, QBaselineDialog
from ..dsc.glass_dialog import TgCalculator
from ..fit.result import FitExtension, QFitResult
from ..graphs.graphwindow import QGraphWindow
@@ -164,7 +164,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.ac_group2.triggered.connect(self.change_fit_limits)
self.t1action.triggered.connect(lambda: self._show_tab('t1_temp'))
- self.action_edit.triggered.connect(lambda: self._show_tab('signal'))
+ self.action_edit.triggered.connect(self.do_preview)
self.actionPick_position.triggered.connect(lambda: self._show_tab('pick'))
self.actionIntegration.triggered.connect(lambda: self._show_tab('integrate'))
self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit'))
@@ -214,7 +214,7 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
self.t1tauwidget.newData.connect(self.management.add_new_data)
self.editsignalwidget.do_something.connect(self.management.apply)
- self.editsignalwidget.preview_triggered.connect(self.do_preview)
+ # self.editsignalwidget.preview_triggered.connect(self.do_preview)
self.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ()))
self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv)
@@ -784,29 +784,22 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow):
editor.finished.connect(self.management.apply)
editor.exec()
- @QtCore.pyqtSlot(str)
- def do_preview(self, mode):
+ @QtCore.pyqtSlot()
+ def do_preview(self):
+ dialog = QPreviewDialog(self)
- if mode == 'ap':
- dialog = QApodDialog(parent=self)
- elif mode == 'ph':
- dialog = QPhasedialog(parent=self)
- else:
- raise ValueError('Unknown preview mode %s' % str(mode))
-
- dialog.setRange(*self.current_graph_widget.ranges, self.current_graph_widget.log)
+ success = True
for sid in self.current_graph_widget.active:
data_mode = self.management[sid].mode
- tobeadded = False
- if (data_mode == 'fid') or (data_mode == 'spectrum' and mode == 'ph'):
- tobeadded = True
+ if data_mode in ('fid', 'spectrum'):
+ success = dialog.add_data(self.management[sid].data)
- if tobeadded:
- dialog.add_data(*self.management.get_data(sid, xy_only=True))
+ if not success:
+ break
- if dialog.exec() == QtWidgets.QDialog.Accepted:
- self.management.apply(mode, dialog.get_value())
+ if success and dialog.exec() == QtWidgets.QDialog.Accepted:
+ self.management.edit_signals(dialog.get_value())
@QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered')
def move_sets_dialog(self):
diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py
index 13e536e..07ee695 100644
--- a/src/gui_qt/main/management.py
+++ b/src/gui_qt/main/management.py
@@ -418,6 +418,13 @@ class UpperManagement(QtCore.QObject):
self.undostack.push(single_undo)
self.undostack.endMacro()
+ def edit_signals(self: UpperManagement, args: list[tuple]) -> None:
+ self.undostack.beginMacro('Edit signals')
+ for sid in self.graphs[self.current_graph]:
+ single_undo = EditCommand(self.data[sid], *args)
+ self.undostack.push(single_undo)
+ self.undostack.endMacro()
+
def cut(self):
if self.current_graph:
xlim, _ = self.graphs[self.current_graph].ranges
diff --git a/src/nmreval/data/nmr.py b/src/nmreval/data/nmr.py
index 8f5229a..cd5d339 100644
--- a/src/nmreval/data/nmr.py
+++ b/src/nmreval/data/nmr.py
@@ -62,6 +62,10 @@ class FID(Signal):
return self
+ def manual_phase(self, ph0: float = 0., ph1: float = 0., pvt: float = 0):
+ """FID knows only how to phase correct in zeroth order"""
+ super().manual_phase(ph0=ph0)
+
def fourier(self) -> 'Spectrum':
ft = np.fft.fftshift(np.fft.fft(self._y)) / self.dx
freq = np.fft.fftshift(np.fft.fftfreq(self.length, self.dx))
diff --git a/src/nmreval/data/points.py b/src/nmreval/data/points.py
index bf9a438..a9d8cba 100644
--- a/src/nmreval/data/points.py
+++ b/src/nmreval/data/points.py
@@ -319,20 +319,21 @@ class Points:
if pts is None:
pts = []
- for x in idx:
- if isinstance(x, tuple):
- x_idx = np.argmin(np.abs(self._x[self.mask] - (x[0]+x[1])/2))
- left_b = np.argmin(np.abs(self._x[self.mask] - x[0]))
- right_b = np.argmin(np.abs(self._x[self.mask] - x[1]))
- else:
- x_idx = np.argmin(np.abs(self._x[self.mask]-x))
- left_b = int(max(0, x_idx - avg_range[0]))
- right_b = int(min(len(self), x_idx + avg_range[1] + 1))
+ if idx is not None:
+ for x in idx:
+ if isinstance(x, tuple):
+ x_idx = np.argmin(np.abs(self._x[self.mask] - (x[0]+x[1])/2))
+ left_b = np.argmin(np.abs(self._x[self.mask] - x[0]))
+ right_b = np.argmin(np.abs(self._x[self.mask] - x[1]))
+ else:
+ x_idx = np.argmin(np.abs(self._x[self.mask]-x))
+ left_b = int(max(0, x_idx - avg_range[0]))
+ right_b = int(min(len(self), x_idx + avg_range[1] + 1))
- if left_b < right_b:
- pts.append([self._x[x_idx], *self._average(avg_mode, x_idx, left_b, right_b)])
- else:
- pts.append([self._x[x_idx], self._y[x_idx], self._y_err[x_idx]])
+ if left_b < right_b:
+ pts.append([self._x[x_idx], *self._average(avg_mode, x_idx, left_b, right_b)])
+ else:
+ pts.append([self._x[x_idx], self._y[x_idx], self._y_err[x_idx]])
if special is not None:
if special not in ['max', 'min', 'absmax', 'absmin']:
diff --git a/src/resources/_ui/apod_dialog.ui b/src/resources/_ui/apod_dialog.ui
index 5585d7b..e5ee569 100644
--- a/src/resources/_ui/apod_dialog.ui
+++ b/src/resources/_ui/apod_dialog.ui
@@ -6,8 +6,8 @@
0
0
- 784
- 484
+ 1144
+ 655
@@ -21,22 +21,22 @@
- 3
+ 9
- 3
+ 9
- 3
+ 9
- 3
+ 9
3
- -
-
+
-
+
0
@@ -45,30 +45,10 @@
- -
-
+
-
+
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- 20
-
-
- 6
-
-
-
- -
+
-
Qt::Horizontal
@@ -78,20 +58,361 @@
- -
-
+
-
+
-
+
0
0
-
- TextLabel
-
-
- 3
-
+
+
-
+
+
+ Baseline
+
+
+
+ -
+
+
+ Shift
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
-
+
+
+ 0
+
+
+
+ -
+
+
+ 999999
+
+
+
+ -
+
+
-
+
+ Points
+
+
+ -
+
+ Seconds
+
+
+
+
+
+
+
+ -
+
+
+ Apodization
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
-
+
+
+ -
+
+
+ TextLabel
+
+
+ 3
+
+
+
+ -
+
+
+ 20
+
+
+ 6
+
+
+
+
+
+
+ -
+
+
+ Zero fill
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
-
+
+
+ Double length
+
+
+
+ -
+
+
+ x
+
+
+ 1
+
+
+ 3
+
+
+
+
+
+
+ -
+
+
+ Phase correction
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
-
+
+
+ Phase 1
+
+
+
+ -
+
+
+ Pivot
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+ -180.000000000000000
+
+
+ 180.000000000000000
+
+
+ 0.500000000000000
+
+
+
+ -
+
+
+ 0
+
+
+
+ -
+
+
+ Phase 0
+
+
+
+ -
+
+
+ true
+
+
+ 2
+
+
+ -720.000000000000000
+
+
+ 720.000000000000000
+
+
+ 0.050000000000000
+
+
+
+
+
+
+ -
+
+
+ Fourier transform
+
+
+ true
+
+
+ false
+
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
+ 3
+
+
-
+
+
+ before phase correction
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+ after phase correction
+
+
+ buttonGroup
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 20
+ 40
+
+
+
+
+
@@ -138,4 +459,7 @@
+
+
+