From 4ca8f434aa31b326f37c774312ef4ff29a60e52f Mon Sep 17 00:00:00 2001 From: dominik Date: Sat, 19 Nov 2022 17:59:35 +0100 Subject: [PATCH] value tab plots complex selection; graphwindows shows last path for export; filedialog uses call not init --- src/gui_qt/_py/asciidialog.py | 87 +++++++++++++------------- src/gui_qt/data/valueeditwidget.py | 33 +++++++--- src/gui_qt/graphs/graphwindow.py | 99 ++++++++++++++++-------------- src/gui_qt/io/asciireader.py | 35 +++++------ src/gui_qt/io/exporters.py | 4 +- src/gui_qt/io/filedialog.py | 22 ++++--- src/gui_qt/main/mainwindow.py | 25 ++++---- src/gui_qt/main/management.py | 15 +++-- src/nmreval/io/asciireader.py | 1 + src/nmreval/io/graceeditor.py | 4 +- src/resources/_ui/asciidialog.ui | 38 +++++++++--- 11 files changed, 206 insertions(+), 157 deletions(-) diff --git a/src/gui_qt/_py/asciidialog.py b/src/gui_qt/_py/asciidialog.py index 523ad06..0b2e0a2 100644 --- a/src/gui_qt/_py/asciidialog.py +++ b/src/gui_qt/_py/asciidialog.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file '_ui/asciidialog.ui' +# Form implementation generated from reading ui file '/autohome/dominik/nmreval/src/resources/_ui/asciidialog.ui' # -# Created by: PyQt5 UI code generator 5.12.3 +# Created by: PyQt5 UI code generator 5.15.4 # -# 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 @@ -23,11 +24,11 @@ class Ui_ascii_reader(object): self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tabWidgetPage1) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.verticalLayout_3.setObjectName("verticalLayout_3") - self.plainTextEdit_2 = QtWidgets.QPlainTextEdit(self.tabWidgetPage1) - self.plainTextEdit_2.setEnabled(False) - self.plainTextEdit_2.setMaximumSize(QtCore.QSize(16777215, 180)) - self.plainTextEdit_2.setObjectName("plainTextEdit_2") - self.verticalLayout_3.addWidget(self.plainTextEdit_2) + self.comment_textfield = QtWidgets.QPlainTextEdit(self.tabWidgetPage1) + self.comment_textfield.setEnabled(False) + self.comment_textfield.setMaximumSize(QtCore.QSize(16777215, 180)) + self.comment_textfield.setObjectName("comment_textfield") + self.verticalLayout_3.addWidget(self.comment_textfield) self.ascii_table = QtWidgets.QTableWidget(self.tabWidgetPage1) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -49,9 +50,9 @@ class Ui_ascii_reader(object): self.tabWidgetPage2.setObjectName("tabWidgetPage2") self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tabWidgetPage2) self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.plainTextEdit = QtWidgets.QPlainTextEdit(self.tabWidgetPage2) - self.plainTextEdit.setObjectName("plainTextEdit") - self.horizontalLayout_3.addWidget(self.plainTextEdit) + self.delay_textfield = QtWidgets.QPlainTextEdit(self.tabWidgetPage2) + self.delay_textfield.setObjectName("delay_textfield") + self.horizontalLayout_3.addWidget(self.delay_textfield) self.formLayout = QtWidgets.QFormLayout() self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow) self.formLayout.setContentsMargins(0, -1, -1, -1) @@ -79,18 +80,18 @@ class Ui_ascii_reader(object): self.end_lineedit = QtWidgets.QLineEdit(self.tabWidgetPage2) self.end_lineedit.setObjectName("end_lineedit") self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.end_lineedit) - self.checkBox = QtWidgets.QCheckBox(self.tabWidgetPage2) - self.checkBox.setLayoutDirection(QtCore.Qt.LeftToRight) - self.checkBox.setObjectName("checkBox") - self.formLayout.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.checkBox) - self.checkBox_2 = QtWidgets.QCheckBox(self.tabWidgetPage2) + self.log_checkBox = QtWidgets.QCheckBox(self.tabWidgetPage2) + self.log_checkBox.setLayoutDirection(QtCore.Qt.LeftToRight) + self.log_checkBox.setObjectName("log_checkBox") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.log_checkBox) + self.staggered_checkBox = QtWidgets.QCheckBox(self.tabWidgetPage2) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.checkBox_2.sizePolicy().hasHeightForWidth()) - self.checkBox_2.setSizePolicy(sizePolicy) - self.checkBox_2.setObjectName("checkBox_2") - self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.checkBox_2) + sizePolicy.setHeightForWidth(self.staggered_checkBox.sizePolicy().hasHeightForWidth()) + self.staggered_checkBox.setSizePolicy(sizePolicy) + self.staggered_checkBox.setObjectName("staggered_checkBox") + self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.staggered_checkBox) self.stag_lineEdit = QtWidgets.QLineEdit(self.tabWidgetPage2) self.stag_lineEdit.setEnabled(True) self.stag_lineEdit.setObjectName("stag_lineEdit") @@ -139,9 +140,9 @@ class Ui_ascii_reader(object): self.label_5 = QtWidgets.QLabel(self.widget) self.label_5.setObjectName("label_5") self.horizontalLayout_4.addWidget(self.label_5) - self.lineEdit = QtWidgets.QLineEdit(self.widget) - self.lineEdit.setObjectName("lineEdit") - self.horizontalLayout_4.addWidget(self.lineEdit) + self.deltay_lineEdit = QtWidgets.QLineEdit(self.widget) + self.deltay_lineEdit.setObjectName("deltay_lineEdit") + self.horizontalLayout_4.addWidget(self.deltay_lineEdit) self.horizontalLayout.addWidget(self.widget) self.verticalLayout.addLayout(self.horizontalLayout) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() @@ -152,23 +153,23 @@ class Ui_ascii_reader(object): self.horizontalLayout_2.addWidget(self.skippy_checkbox) spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_2.addItem(spacerItem3) - self.radioButton = QtWidgets.QRadioButton(ascii_reader) - self.radioButton.setChecked(True) - self.radioButton.setAutoExclusive(True) - self.radioButton.setObjectName("radioButton") + self.pts_radioButton = QtWidgets.QRadioButton(ascii_reader) + self.pts_radioButton.setChecked(True) + self.pts_radioButton.setAutoExclusive(True) + self.pts_radioButton.setObjectName("pts_radioButton") self.buttonGroup = QtWidgets.QButtonGroup(ascii_reader) self.buttonGroup.setObjectName("buttonGroup") - self.buttonGroup.addButton(self.radioButton) - self.horizontalLayout_2.addWidget(self.radioButton) - self.radioButton_2 = QtWidgets.QRadioButton(ascii_reader) - self.radioButton_2.setAutoExclusive(True) - self.radioButton_2.setObjectName("radioButton_2") - self.buttonGroup.addButton(self.radioButton_2) - self.horizontalLayout_2.addWidget(self.radioButton_2) - self.radioButton_3 = QtWidgets.QRadioButton(ascii_reader) - self.radioButton_3.setObjectName("radioButton_3") - self.buttonGroup.addButton(self.radioButton_3) - self.horizontalLayout_2.addWidget(self.radioButton_3) + self.buttonGroup.addButton(self.pts_radioButton) + self.horizontalLayout_2.addWidget(self.pts_radioButton) + self.FID_radioButton = QtWidgets.QRadioButton(ascii_reader) + self.FID_radioButton.setAutoExclusive(True) + self.FID_radioButton.setObjectName("FID_radioButton") + self.buttonGroup.addButton(self.FID_radioButton) + self.horizontalLayout_2.addWidget(self.FID_radioButton) + self.spectrum_radioButton = QtWidgets.QRadioButton(ascii_reader) + self.spectrum_radioButton.setObjectName("spectrum_radioButton") + self.buttonGroup.addButton(self.spectrum_radioButton) + self.horizontalLayout_2.addWidget(self.spectrum_radioButton) self.verticalLayout.addLayout(self.horizontalLayout_2) self.buttonbox = QtWidgets.QDialogButtonBox(ascii_reader) self.buttonbox.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) @@ -187,8 +188,8 @@ class Ui_ascii_reader(object): self.label_2.setText(_translate("ascii_reader", "Number of delays")) self.label_3.setText(_translate("ascii_reader", "Start value")) self.label_4.setText(_translate("ascii_reader", "End value")) - self.checkBox.setText(_translate("ascii_reader", "Logarithmic scale")) - self.checkBox_2.setText(_translate("ascii_reader", "Staggered range")) + self.log_checkBox.setText(_translate("ascii_reader", "Logarithmic scale")) + self.staggered_checkBox.setText(_translate("ascii_reader", "Staggered range")) self.pushButton.setText(_translate("ascii_reader", "Calculate delays")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabWidgetPage2), _translate("ascii_reader", "Delays")) self.x_label.setText(_translate("ascii_reader", "x")) @@ -198,6 +199,6 @@ class Ui_ascii_reader(object): self.label_5.setText(_translate("ascii_reader", "

Δy

")) self.skippy_checkbox.setToolTip(_translate("ascii_reader", "Use selection for next files. Deletes possible delay values.")) self.skippy_checkbox.setText(_translate("ascii_reader", "Skip next dialogues?")) - self.radioButton.setText(_translate("ascii_reader", "Points")) - self.radioButton_2.setText(_translate("ascii_reader", "FID")) - self.radioButton_3.setText(_translate("ascii_reader", "Spectrum")) + self.pts_radioButton.setText(_translate("ascii_reader", "Points")) + self.FID_radioButton.setText(_translate("ascii_reader", "FID")) + self.spectrum_radioButton.setText(_translate("ascii_reader", "Spectrum")) diff --git a/src/gui_qt/data/valueeditwidget.py b/src/gui_qt/data/valueeditwidget.py index 4d9f4b2..bb94965 100644 --- a/src/gui_qt/data/valueeditwidget.py +++ b/src/gui_qt/data/valueeditwidget.py @@ -2,7 +2,8 @@ from __future__ import annotations from typing import Any -import numpy as np +from numpy import ndarray, iscomplexobj, asarray +from pyqtgraph import PlotDataItem from ..Qt import QtGui, QtCore, QtWidgets from .._py.valueeditor import Ui_MaskDialog @@ -14,7 +15,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): itemChanged = QtCore.pyqtSignal(str, tuple, object) itemDeleted = QtCore.pyqtSignal(str, list) itemAdded = QtCore.pyqtSignal(str) - values_selected = QtCore.pyqtSignal(str, list, list) + values_selected = QtCore.pyqtSignal(str, str) split_signal = QtCore.pyqtSignal(str, int) def __init__(self, parent=None): @@ -37,8 +38,10 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tableView.customContextMenuRequested.connect(self.ctx) - self.graph_combobox.currentIndexChanged.connect(self._populate_sets) - self.set_combobox.currentIndexChanged.connect(self._populate_table) + self.selection_real = PlotDataItem(x=[], y=[], symbolSize=25, symbol='x', + pen=None, symbolPen='#c9308e', symbolBrush='#c9308e') + self.selection_imag = PlotDataItem(x=[], y=[], symbolSize=25, symbol='+', + pen=None, symbolPen='#dcdcdc', symbolBrush='#dcdcdc') def __call__(self, items: dict): self.items = items @@ -58,7 +61,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): return self - @QtCore.pyqtSlot(int) + @QtCore.pyqtSlot(int, name='on_graph_combobox_currentIndexChanged') def _populate_sets(self, idx: int): if idx == -1: self.graph_combobox.setCurrentIndex(0) @@ -66,6 +69,7 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.set_combobox.blockSignals(True) self.set_combobox.clear() + old_figure = self.connected_figure self.connected_figure = self.graph_combobox.currentData() if self.items: @@ -80,13 +84,15 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): self.set_combobox.setCurrentIndex(sidx) self.set_combobox.currentIndexChanged.emit(sidx) - @QtCore.pyqtSlot(int) + self.values_selected.emit(old_figure, self.connected_figure) + + @QtCore.pyqtSlot(int, name='on_set_combobox_currentIndexChanged') def _populate_table(self, idx): self.selection_model.clearSelection() self.shown_set = self.set_combobox.itemData(idx) self.requestData.emit(self.set_combobox.itemData(idx)) - def set_data(self, data: list, mask: np.ndarray): + def set_data(self, data: list, mask: ndarray): self.selection_model.clearSelection() self.model.loadData(data, mask) self.spinBox.setMaximum(self.model.rowCount()) @@ -193,9 +199,16 @@ class ValueEditWidget(QtWidgets.QWidget, Ui_MaskDialog): try: yvals.append(float(self.model.data(idx.sibling(idx.row(), 1)))) except ValueError: - yvals.append(complex(self.model.data(idx.sibling(idx.row(), 1))).real) + yvals.append(complex(self.model.data(idx.sibling(idx.row(), 1)))) - self.values_selected.emit(self.connected_figure, xvals, yvals) + yvals = asarray(yvals) + + if iscomplexobj(yvals): + self.selection_real.setData(x=xvals, y=yvals.real) + self.selection_imag.setData(x=xvals, y=yvals.imag) + else: + self.selection_real.setData(x=xvals, y=yvals) + self.selection_imag.setData(x=[], y=[]) @QtCore.pyqtSlot(name='on_toolButton_clicked') def goto(self): @@ -227,7 +240,7 @@ class ValueModel(QtCore.QAbstractTableModel): def columnCount(self, *args, **kwargs) -> int: return len(self.headers) - def loadData(self, data: list[np.ndarray], mask: np.ndarray): + def loadData(self, data: list[ndarray], mask: ndarray): self.beginResetModel() self._data = [] for x, y, y_err in zip(*data): diff --git a/src/gui_qt/graphs/graphwindow.py b/src/gui_qt/graphs/graphwindow.py index dea112c..f1040da 100644 --- a/src/gui_qt/graphs/graphwindow.py +++ b/src/gui_qt/graphs/graphwindow.py @@ -5,11 +5,13 @@ import os import uuid from math import isnan +from pathlib import Path from numpy import errstate, floor, log10 from pyqtgraph import GraphicsObject, getConfigOption, mkColor from nmreval.utils.text import convert +from ..io.filedialog import FileDialog from ..lib.pg_objects import LegendItemBlock, RegionItem from ..Qt import QtCore, QtWidgets, QtGui @@ -524,64 +526,67 @@ class QGraphWindow(QtWidgets.QGraphicsView, Ui_GraphWindow): str_format = imgformat.data().decode('utf-8') filters += ';;' + str_format.upper() + ' (*.' + str_format + ')' - outfile, _ = QtWidgets.QFileDialog.getSaveFileName(self, caption='Export graphic', filter=filters, - options=QtWidgets.QFileDialog.DontConfirmOverwrite) + outfile = None + f = FileDialog(caption='Export graphic', filters=filters) + mode = f.exec() + if mode == QtWidgets.QDialog.Accepted: + outfile = f.save_file() if outfile: self.export(outfile) - def export(self, outfile: str): - _, suffix = os.path.splitext(outfile) - if suffix == '': - QtWidgets.QMessageBox.warning(self, 'No file extension', - 'No file extension found, graphic was not saved.') - return + def export(self, outfile: Path): + suffix = outfile.suffix + if suffix == '': + QtWidgets.QMessageBox.warning(self, 'No file extension', + 'No file extension found, graphic was not saved.') + return - if suffix == '.agr': - res = 0 - if os.path.exists(outfile): - res = GraceMsgBox(outfile, parent=self).exec() - if res == -1: - return + if suffix == '.agr': + res = 0 + if outfile.exists(): + res = GraceMsgBox(outfile, parent=self).exec() + if res == -1: + return - opts = self.export_graphics() + opts = self.export_graphics() - from ..io.exporters import GraceExporter - if res == 0: - mode = 'w' - elif res == 1: - mode = 'a' - else: - mode = res-2 + from ..io.exporters import GraceExporter + if res == 0: + mode = 'w' + elif res == 1: + mode = 'a' + else: + mode = res-2 - GraceExporter(opts).export(outfile, mode=mode) + GraceExporter(opts).export(outfile, mode=mode) + + else: + if os.path.exists(outfile): + if QtWidgets.QMessageBox.warning(self, 'Export graphic', + f'{os.path.split(outfile)[1]} already exists.\n' + f'Do you REALLY want to replace it?', + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, + QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.No: + return + + bg_color = self._bgcolor + fg_color = self._fgcolor + self.set_color(foreground='k', background='w') + + if suffix == '.pdf': + from ..io.exporters import PDFPrintExporter + PDFPrintExporter(self.graphic).export(outfile) + + elif suffix == '.svg': + from pyqtgraph.exporters import SVGExporter + SVGExporter(self.scene).export(outfile) else: - if os.path.exists(outfile): - if QtWidgets.QMessageBox.warning(self, 'Export graphic', - f'{os.path.split(outfile)[1]} already exists.\n' - f'Do you REALLY want to replace it?', - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.No) == QtWidgets.QMessageBox.No: - return + from pyqtgraph.exporters import ImageExporter - bg_color = self._bgcolor - fg_color = self._fgcolor - self.set_color(foreground='k', background='w') + ImageExporter(self.scene).export(outfile) - if suffix == '.pdf': - from ..io.exporters import PDFPrintExporter - PDFPrintExporter(self.graphic).export(outfile) - - elif suffix == '.svg': - from pyqtgraph.exporters import SVGExporter - SVGExporter(self.scene).export(outfile) - - else: - from pyqtgraph.exporters import ImageExporter - - ImageExporter(self.scene).export(outfile) - - self.set_color(foreground=fg_color, background=bg_color) + self.set_color(foreground=fg_color, background=bg_color) def export_graphics(self) -> dict: dic = self.get_state() diff --git a/src/gui_qt/io/asciireader.py b/src/gui_qt/io/asciireader.py index f609407..01dd012 100644 --- a/src/gui_qt/io/asciireader.py +++ b/src/gui_qt/io/asciireader.py @@ -18,7 +18,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): pal = QtWidgets.QApplication.instance().palette() rgb = pal.color(pal.Base).getRgb()[:3] rgb2 = pal.color(pal.Text).getRgb()[:3] - self.plainTextEdit_2.setStyleSheet(f'QPlainTextEdit {{ background-color: rgb{rgb} ; color: rgb{rgb2}; }}') + self.comment_textfield.setStyleSheet(f'Qdelay_textfield {{ background-color: rgb{rgb} ; color: rgb{rgb2}; }}') self.ascii_table.horizontalHeader().setStretchLastSection(True) self.buttonbox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply) @@ -31,13 +31,12 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): self.ascii_table.horizontalHeader().customContextMenuRequested.connect(self.ctx_table) def __call__(self, fname, *args, **kwargs): - for i in [self.stag_lineEdit, self.start_lineedit, self.end_lineedit, - self.plainTextEdit_2, self.ascii_table, self.delay_lineedit]: + self.comment_textfield, self.ascii_table, self.delay_lineedit]: i.clear() - self.checkBox_2.setChecked(False) - self.checkBox.setChecked(False) - self.plainTextEdit_2.show() + self.staggered_checkBox.setChecked(False) + self.log_checkBox.setChecked(False) + self.comment_textfield.show() self.reader = AsciiReader(fname) self.set_gui() @@ -53,7 +52,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): def set_gui(self): for text in self.reader.header: - self.plainTextEdit_2.appendPlainText(text) + self.comment_textfield.appendPlainText(text) self.ascii_table.setRowCount(self.reader.shape[0]) self.ascii_table.setColumnCount(self.reader.shape[1]) @@ -62,7 +61,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): if len(last_header_line) == self.reader.shape[1]: self.ascii_table.setHorizontalHeaderLabels(last_header_line) except IndexError: - self.plainTextEdit_2.hide() + self.comment_textfield.hide() for i, line in enumerate(self.reader.data): for j, field in enumerate(line): @@ -73,7 +72,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): if self.reader.delays is not None: set_string = ''.join(str(d) + '\n' for d in self.reader.delays) - self.plainTextEdit.setPlainText(set_string) + self.delay_textfield.setPlainText(set_string) self.delay_lineedit.setText(str(len(self.reader.delays))) def ctx_table(self, _): @@ -92,7 +91,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): def set_columns(self, mode): cols = ' '.join([str(s.column()+1) for s in self.ascii_table.selectionModel().selectedColumns()]) try: - lineedit = {'x': self.x_lineedit, 'y': self.y_lineedit, 'yerr': self.lineEdit}[mode] + lineedit = {'x': self.x_lineedit, 'y': self.y_lineedit, 'yerr': self.deltay_lineEdit}[mode] lineedit.setText(cols) except KeyError: pass @@ -109,19 +108,19 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): @QtCore.pyqtSlot(name='on_pushButton_clicked') def calc_delays(self): self.reader.calc_delays(float(self.start_lineedit.text()), float(self.end_lineedit.text()), - int(self.delay_lineedit.text()), log=self.checkBox.isChecked(), - stagg=self.checkBox_2.isChecked(), stag_size=int(self.stag_lineEdit.text())) + int(self.delay_lineedit.text()), log=self.log_checkBox.isChecked(), + stagg=self.staggered_checkBox.isChecked(), stag_size=int(self.stag_lineEdit.text())) set_string = ''.join(str(d) + '\n' for d in self.reader.delays) - self.plainTextEdit.setPlainText(set_string) + self.delay_textfield.setPlainText(set_string) def update_header(self, text, comment='#'): text = text.replace(comment, '').lstrip().rstrip() - self.plainTextEdit_2.appendPlainText(text) + self.comment_textfield.appendPlainText(text) @QtCore.pyqtSlot() - def on_plainTextEdit_textChanged(self): - new_delays = str(self.plainTextEdit.toPlainText()).rstrip('\n').split('\n') + def on_delay_textfield_textChanged(self): + new_delays = str(self.delay_textfield.toPlainText()).rstrip('\n').split('\n') self.delay_lineedit.setText(str(len(new_delays))) if new_delays[0] == '': new_delays = None @@ -150,7 +149,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): except ValueError: y = None try: - y_err = [int(t)-1 for t in str(self.lineEdit.text()).split(' ')] + y_err = [int(t)-1 for t in str(self.deltay_lineEdit.text()).split(' ')] except ValueError: y_err = None @@ -179,7 +178,7 @@ class QAsciiReader(QtWidgets.QDialog, Ui_ascii_reader): if val == -2: self.widget.show() else: - self.lineEdit.setText('') + self.deltay_lineEdit.setText('') self.widget.hide() @QtCore.pyqtSlot(int, name='on_skippy_checkbox_stateChanged') diff --git a/src/gui_qt/io/exporters.py b/src/gui_qt/io/exporters.py index 363fdd5..e089991 100644 --- a/src/gui_qt/io/exporters.py +++ b/src/gui_qt/io/exporters.py @@ -1,5 +1,7 @@ from __future__ import annotations +from pathlib import Path + from numpy import c_ from nmreval.io.graceeditor import GraceEditor @@ -13,7 +15,7 @@ class GraceExporter: self.__agr = None self.__opts = kwargs - def export(self, outfile: str, mode: int | str = 'w'): + def export(self, outfile: str | Path, mode: int | str = 'w'): if mode == 'w': self.__agr = GraceEditor() else: diff --git a/src/gui_qt/io/filedialog.py b/src/gui_qt/io/filedialog.py index 9f1f532..c9b9bf8 100644 --- a/src/gui_qt/io/filedialog.py +++ b/src/gui_qt/io/filedialog.py @@ -5,14 +5,17 @@ import pathlib from ..Qt import QtWidgets, QtCore -class _FileDialog(QtWidgets.QFileDialog): +class FileDialog(QtWidgets.QFileDialog): + last_path = None + def __init__(self, directory=None, caption=None, filters='', parent=None): super().__init__(parent=parent) self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True) self.setWindowTitle(caption) - self.setDirectory(str(directory)) + if directory is not None: + self.setDirectory(str(directory)) self.setNameFilters(filters.split(';;')) file_tree = self.findChild(QtWidgets.QTreeView, 'treeView') @@ -29,8 +32,14 @@ class _FileDialog(QtWidgets.QFileDialog): line.setFrameShadow(line.Sunken) self.layout().addWidget(line, self.layout().rowCount(), 0, 1, self.layout().columnCount()) + def save_file(self) -> pathlib.Path | None: + outfile = self.selectedFiles() + if outfile: + return pathlib.Path(outfile[0]) + return -class OpenFileDialog(_FileDialog): + +class OpenFileDialog(FileDialog): def __init__(self, directory=None, caption=None, filters='', parent=None): super().__init__(directory=directory, caption=caption, filters=filters, parent=parent) @@ -71,7 +80,7 @@ class OpenFileDialog(_FileDialog): self.add_to_graph = self.comboBox.itemData(idx) -class SaveDirectoryDialog(_FileDialog): +class SaveDirectoryDialog(FileDialog): def __init__(self, directory=None, filters='', parent=None): super().__init__(directory=directory, filters=filters, parent=parent) @@ -115,8 +124,3 @@ class SaveDirectoryDialog(_FileDialog): self.setNameFilters(['All files (*.*)', 'Session file (*.nmr)', 'Text file (*.dat)', 'HDF file (*.h5)', 'Grace files (*.agr)']) - def save_file(self) -> pathlib.Path | None: - outfile = self.selectedFiles() - if outfile: - return pathlib.Path(outfile[0]) - return diff --git a/src/gui_qt/main/mainwindow.py b/src/gui_qt/main/mainwindow.py index 425d90c..e8b58a2 100644 --- a/src/gui_qt/main/mainwindow.py +++ b/src/gui_qt/main/mainwindow.py @@ -101,9 +101,6 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): self.statusBar.addWidget(self.mousepos) self.fitregion = RegionItem() - self._values_plot = PlotDataItem(x=[], y=[], symbolSize=30, symbol='x', - pen=None, symbolPen='#d526b5', symbolBrush='#d526b5') - self._fit_plot_id = None self.setGeometry(QtWidgets.QStyle.alignedRect(QtCore.Qt.LeftToRight, QtCore.Qt.AlignCenter, @@ -530,12 +527,14 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): def _select_valuewidget(self, onoff: bool): if onoff: # Values self.valuewidget(self.management.graphs.tree()) - self.valuewidget.connected_figure = self.management.current_graph - if self.valuewidget.connected_figure is not None: - self.management.graphs[self.valuewidget.connected_figure].add_external(self._values_plot) + current_graph = self.valuewidget.connected_figure + if current_graph is not None: + self.management.graphs[current_graph].add_external(self.valuewidget.selection_real) + self.management.graphs[current_graph].add_external(self.valuewidget.selection_imag) else: if self.valuewidget.connected_figure is not None: - self.management.graphs[self.valuewidget.connected_figure].remove_external(self._values_plot) + self.management.graphs[self.valuewidget.connected_figure].remove_external(self.valuewidget.selection_real) + self.management.graphs[self.valuewidget.connected_figure].remove_external(self.valuewidget.selection_imag) def _select_integralwidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]: if self.current_graph_widget is None: @@ -777,12 +776,12 @@ class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): data, mask = self.management.get_data(sid) self.valuewidget.set_data(data, mask) - def plot_selected_values(self, gid: str, x: list, y: list): - self._values_plot.setData(x=x, y=y) - if gid != self.valuewidget.connected_figure and self.valuewidget.connected_figure is not None: - self.management.graphs[self.valuewidget.connected_figure].remove_external(self._values_plot) - self.management.graphs[gid].add_external(self._values_plot) - self.valuewidget.connected_figure = gid + def plot_selected_values(self, old_gid: str, new_gid: str): + for pts in (self.valuewidget.selection_real, self.valuewidget.selection_imag): + if old_gid: + self.management.graphs[old_gid].remove_external(pts) + if new_gid: + self.management.graphs[new_gid].add_external(pts) @QtCore.pyqtSlot(object, str) def item_to_graph(self, item, graph_id): diff --git a/src/gui_qt/main/management.py b/src/gui_qt/main/management.py index 4eaa869..1ae337f 100644 --- a/src/gui_qt/main/management.py +++ b/src/gui_qt/main/management.py @@ -9,12 +9,13 @@ from nmreval.fit import data as fit_d from nmreval.fit.model import Model from nmreval.fit.result import FitResult from nmreval.fit.minimizer import FitRoutine -from nmreval.lib.colors import TUColorsC, available_cycles +from nmreval.lib.colors import available_cycles from nmreval.math.interpol import interpolate from nmreval.math.logfourier import logft from nmreval.math.smooth import smooth from nmreval.nmr.relaxation import Relaxation +from ..Qt import QtCore, QtWidgets from ..lib.undos import * from ..data.container import * from ..io.filereaders import QFileReader @@ -112,6 +113,8 @@ class UpperManagement(QtCore.QObject): self.undostack = QtWidgets.QUndoStack() self.deleteData.connect(self.plot_from_graph) + self._filereader = None + def __setitem__(self, key: str, value, **kwargs): if isinstance(value, ExperimentContainer): item = value @@ -148,7 +151,9 @@ class UpperManagement(QtCore.QObject): return _id def load_files(self, fname: List[str], new_plot: str = None): - ret_dic = QFileReader(manager=self).readfiles(fname) + if self._filereader is None: + self._filereader = QFileReader(manager=self) + ret_dic = self._filereader.readfiles(fname) self.add_new_data(ret_dic, new_plot) def _load_session(self, sets: dict, graphs: dict): @@ -1000,9 +1005,9 @@ class UpperManagement(QtCore.QObject): if opts['axis1'] in ['t', 'invt1000']: t_p = opts['t_param'] if len(t_p) == 2: - from ...models import Arrhenius as Func + from nmreval.models import Arrhenius as Func else: - from ...models import VFT as Func + from nmreval.models import VFT as Func _x = Func.func(x1, *t_p, invt=opts['axis1']) @@ -1078,7 +1083,7 @@ class UpperManagement(QtCore.QObject): raise ValueError('No file extension detected') if suffix == '.nmr': - from ...io.sessionwriter import NMRWriter + from nmreval.io.sessionwriter import NMRWriter NMRWriter(self.graphs, self.data).export(path) return diff --git a/src/nmreval/io/asciireader.py b/src/nmreval/io/asciireader.py index 77cd90c..7f29bb9 100644 --- a/src/nmreval/io/asciireader.py +++ b/src/nmreval/io/asciireader.py @@ -105,6 +105,7 @@ class AsciiReader: if mode == 'Points': try: + # TODO read errors correctly for k in range(1, raw_data.shape[2]): if raw_data.shape[2] < 3: name = only_f diff --git a/src/nmreval/io/graceeditor.py b/src/nmreval/io/graceeditor.py index 0f6cb23..6734362 100644 --- a/src/nmreval/io/graceeditor.py +++ b/src/nmreval/io/graceeditor.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pathlib import re from io import StringIO @@ -93,7 +95,7 @@ class GraceEditor: return s - def parse(self, filename): + def parse(self, filename: str | pathlib.Path): self.file = pathlib.Path(filename) # we start always with the header diff --git a/src/resources/_ui/asciidialog.ui b/src/resources/_ui/asciidialog.ui index 88fb2d2..bd54180 100644 --- a/src/resources/_ui/asciidialog.ui +++ b/src/resources/_ui/asciidialog.ui @@ -24,11 +24,20 @@ Data - + + 0 + + + 0 + + + 0 + + 0 - + false @@ -76,7 +85,7 @@ - + @@ -123,7 +132,7 @@ - + Qt::LeftToRight @@ -133,7 +142,7 @@ - + 0 @@ -243,7 +252,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -267,7 +285,7 @@ - + @@ -303,7 +321,7 @@ - + Points @@ -319,7 +337,7 @@ - + FID @@ -332,7 +350,7 @@ - + Spectrum