from __future__ import annotations import pathlib import re from pathlib import Path from numpy import geomspace, linspace from pyqtgraph import ViewBox, PlotDataItem from nmreval.configs import * from .management import UpperManagement from ..Qt import QtCore, QtGui, QtPrintSupport, QtWidgets from ..data.shift_graphs import QShift from ..data.signaledit import QApodDialog, QBaselineDialog, QPhasedialog from ..fit.result import QFitResult from ..graphs.graphwindow import QGraphWindow from ..graphs.movedialog import QMover from ..io.fcbatchreader import QFCReader from ..io.filedialog import OpenFileDialog, SaveDirectoryDialog from ..lib import get_icon, make_action_icons from ..lib.pg_objects import RegionItem from ..math.evaluation import QEvalDialog from ..math.interpol import InterpolDialog from ..math.mean_dialog import QMeanTimes from ..math.smooth import QSmooth from ..nmr.coupling_calc import QCoupCalcDialog from ..nmr.t1_from_tau import QRelaxCalc from .._py.basewindow import Ui_BaseWindow class NMRMainWindow(QtWidgets.QMainWindow, Ui_BaseWindow): closeSignal = QtCore.pyqtSignal() openpoints = QtCore.pyqtSignal(dict) save_ses_sig = QtCore.pyqtSignal(str) rest_ses_sig = QtCore.pyqtSignal(str) def __init__(self, parents=None, path=None): super().__init__(parent=parents) if path is None: self.path = Path.home() else: self.path = Path(path) self.read_state() self.management = UpperManagement(self) self.fitlimitvalues = [None, None] self.fitpreview = [] self._fit_plot_id = None self.savefitdialog = None self.eval = None self.editor = None self.movedialog = QMover(self) self.current_graph_widget = None self.current_plotitem = None self._block_window_change = False self.fname = None self.tim = QtCore.QTimer() self.settings = QtCore.QSettings('NMREVal', 'settings') self._init_gui() self._init_signals() def _init_gui(self): self.setupUi(self) make_action_icons(self) self.setWindowIcon(get_icon('logo')) self.norm_toolbutton = QtWidgets.QToolButton(self) self.norm_toolbutton.setMenu(self.menuNormalize) self.norm_toolbutton.setPopupMode(self.norm_toolbutton.InstantPopup) self.norm_toolbutton.setIcon(get_icon('normal')) self.toolbar_edit.addWidget(self.norm_toolbutton) self.fitlim_button = QtWidgets.QToolButton(self) self.fitlim_button.setMenu(self.menuLimits) self.fitlim_button.setPopupMode(self.fitlim_button.InstantPopup) self.fitlim_button.setIcon(get_icon('fit_region')) self.toolBar_fit.addWidget(self.fitlim_button) self.area.dragEnterEvent = self.dragEnterEvent while self.tabWidget.count() > 2: self.tabWidget.removeTab(self.tabWidget.count()-1) # Prevent closing "data" and "values" for i in [0, 1]: self.tabWidget.tabBar().tabButton(i, QtWidgets.QTabBar.ButtonPosition.RightSide).resize(0, 0) self.setAcceptDrops(True) self.mousepos = QtWidgets.QLabel('') self.status = QtWidgets.QLabel('') self.statusBar.addWidget(self.status) 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, self.size(), QtWidgets.qApp.desktop().availableGeometry())) self.datawidget.management = self.management self.ac_group = QtWidgets.QActionGroup(self) self.ac_group.addAction(self.action_lm_fit) self.ac_group.addAction(self.action_nm_fit) self.ac_group.addAction(self.action_odr_fit) self.ac_group2 = QtWidgets.QActionGroup(self) self.ac_group2.addAction(self.action_no_range) self.ac_group2.addAction(self.action_x_range) self.ac_group2.addAction(self.action_custom_range) def _init_signals(self): self.actionRedo = self.management.undostack.createRedoAction(self) icon = QtGui.QIcon.fromTheme("edit-redo") self.actionRedo.setIcon(icon) self.actionRedo.setShortcuts(QtGui.QKeySequence.Redo) self.menuData.insertAction(self.action_new_set, self.actionRedo) self.actionUndo = self.management.undostack.createUndoAction(self) self.actionUndo.setShortcuts(QtGui.QKeySequence.Undo) icon = QtGui.QIcon.fromTheme("edit-undo") self.actionUndo.setIcon(icon) self.menuData.insertAction(self.actionRedo, self.actionUndo) # # self.actionSave.triggered.connect(lambda: self.management.save('/autohome/dominik/nmreval/testdata/test.nmr', '')) # self.actionSave.triggered.connect(self.save) self.action_save_fit_parameter.triggered.connect(self.save_fit_parameter) 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.actionPick_position.triggered.connect(lambda: self._show_tab('pick')) self.actionIntegrate.triggered.connect(lambda: self._show_tab('integrate')) self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit')) self.action_new_set.triggered.connect(self.management.create_empty) self.datawidget.keyChanged.connect(self.management.change_keys) self.datawidget.tree.deleteItem.connect(self.management.delete_sets) self.datawidget.tree.moveItem.connect(self.management.move_sets) self.datawidget.tree.copyItem.connect(self.management.copy_sets) self.datawidget.graph_toolButton.clicked.connect(self.new_graph) self.datawidget.empty_toolButton.clicked.connect(self.management.create_empty) self.datawidget.func_toolButton.clicked.connect(self.make_data_from_function) self.datawidget.tree.stateChanged.connect(self.management.change_visibility) self.datawidget.startShowProperty.connect(self.management.get_properties) self.datawidget.propertyChanged.connect(self.management.update_property) self.datawidget.tree.saveFits.connect(self.save_fit_parameter) self.management.newData.connect(self.show_new_data) self.management.newGraph.connect(self.new_graph) self.management.dataChanged.connect(self.update_data) self.management.deleteData.connect(self.delete_data) self.management.deleteGraph.connect(self.remove_graph) self.management.restoreGraph.connect(self.set_graph) self.management.properties_collected.connect(self.datawidget.set_properties) self.management.unset_state.connect(lambda x: self.datawidget.uncheck_sets(x)) self.management.fitFinished.connect(self.show_fit_results) self.fit_dialog._management = self.management self.fit_dialog.preview_emit.connect(self.show_fit_preview) self.fit_dialog.fitStartSig.connect(self.start_fit) self.fit_dialog.abortFit.connect(lambda : self.management.stopFit.emit()) self.movedialog.moveData.connect(self.move_sets) self.movedialog.copyData.connect(self.management.copy_sets) self.ptsselectwidget.points_selected.connect(self.management.extract_points) 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.action_sort_pts.triggered.connect(lambda: self.management.apply('sort', ())) self.action_calc_eps_derivative.triggered.connect(self.management.bds_deriv) self.action_magnitude.triggered.connect(self.management.calc_magn) self.actionCenterMax.triggered.connect(lambda: self.management.apply('center', ())) self.valuewidget.requestData.connect(self.show_data_values) self.valuewidget.itemChanged.connect(self.management.set_values) self.valuewidget.itemDeleted.connect(self.management.remove_values) self.valuewidget.itemAdded.connect(self.management.append) self.valuewidget.maskSignal.connect(self.management.mask_value) self.valuewidget.values_selected.connect(self.plot_selected_values) self.valuewidget.split_signal.connect(self.management.split_set) self.actionMaximize.triggered.connect(lambda: self.current_graph_widget.showMaximized()) self.actionNext_window.triggered.connect(lambda: self.area.activateNextSubWindow()) self.actionPrevious.triggered.connect(lambda: self.area.activatePreviousSubWindow()) self.closeSignal.connect(self.close) self.action_norm_max.triggered.connect(lambda: self.management.apply('norm', ('max',))) self.action_norm_max_abs.triggered.connect(lambda: self.management.apply('norm', ('maxabs',))) 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.actionConcatenate_sets.triggered.connect(lambda : self.management.cat()) @QtCore.pyqtSlot(name='on_action_open_triggered') def open(self): filedialog = OpenFileDialog(directory=self.path, caption='Open files', filters='All files (*.*);;' 'Program session (*.nmr);;' 'HDF files (*.h5);;' 'Text files (*.txt *.dat);;' 'Novocontrol Alpha (*.EPS);;' 'TecMag files (*.tnt);;' 'Grace files (*.agr)') filedialog.set_graphs(self.management.graphs.list()) filedialog.exec() fname = filedialog.selectedFiles() if fname: self.path = Path(fname[0]).parent self.management.load_files(fname, new_plot=filedialog.add_to_graph) @QtCore.pyqtSlot(name='on_actionOpen_FC_triggered') def read_fc(self): reader = QFCReader(path=self.path, parent=self) reader.data_read.connect(self.management.add_new_data) reader.exec() del reader @QtCore.pyqtSlot(name='on_actionPrint_triggered') def print(self): QtPrintSupport.QPrintDialog().exec() @QtCore.pyqtSlot(name='on_actionExportData_triggered') @QtCore.pyqtSlot(name='on_actionSave_triggered') def save(self): save_dialog = SaveDirectoryDialog( directory=str(self.path), parent=self, ) mode = save_dialog.exec() if mode == QtWidgets.QDialog.Accepted: savefile = save_dialog.save_file() selected_filter = save_dialog.selectedNameFilter() if savefile is not None: use_underscore = save_dialog.checkBox.isChecked() self.management.save(savefile, selected_filter, strip_spaces=use_underscore) param_outfile = re.sub('[_\s-]?<label>[_\s-]?', '', savefile.stem) if save_dialog.agr_cb.isChecked(): self.current_graph_widget.export(savefile.with_name(param_outfile + '.agr')) if save_dialog.fit_cb.isChecked(): self.management.save_fit_parameter(savefile.with_name(param_outfile + '.dat')) @QtCore.pyqtSlot() @QtCore.pyqtSlot(list) def save_fit_parameter(self, fit_sets: list[str] = None): fname, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save fit parameter', directory=str(self.path), filter='All files(*, *);;Text files(*.dat *.txt)') if fname: self.management.save_fit_parameter(fname, fit_sets=fit_sets) @QtCore.pyqtSlot(name='on_actionExportGraphic_triggered') def export_graphic(self): self.current_graph_widget.export_dialog() @QtCore.pyqtSlot(name='on_actionNew_window_triggered') def new_graph(self): w = QGraphWindow() self.management.graphs[w.id] = w self.set_graph(w.id) if self.eval is not None: self.eval.set_graphs(self.management.graphs.list()) return w.id @QtCore.pyqtSlot(list, str) def show_new_data(self, sets: list, graph: str): if len(sets) == 0: return if graph == '': graph = self.new_graph() self.management.plots_to_graph(sets, graph) for idd in sets: new_item = self.management[idd] self.datawidget.blockSignals(True) self.datawidget.add_item(new_item.id, new_item.name, graph) self.datawidget.blockSignals(False) if graph == self.fit_dialog.connected_figure: self.fit_dialog.load(self.management.graphs.active(graph)) if self.valuewidget.isVisible(): self.valuewidget(self.management.graphs.tree()) @QtCore.pyqtSlot(name='on_actionDelete_window_triggered') def delete_windows(self): self.management.delete_sets() @QtCore.pyqtSlot(str) def remove_graph(self, gid: str): self.datawidget.remove_item(gid) val_figure = self.valuewidget.connected_figure self.valuewidget.remove_graph() w = None for w in self.area.subWindowList(): wdgt = w.widget() if wdgt.id == gid: wdgt.disconnect() wdgt.scene.disconnect() if wdgt == self.current_graph_widget: if self.ptsselectwidget.connected_figure == gid: self.ptsselectwidget.connected_figure = None self.tabWidget.removeTab(self.tabWidget.indexOf(self.ptsselectwidget)) if self.t1tauwidget.connected_figure == gid: self.t1tauwidget.connected_figure = None self.tabWidget.removeTab(self.tabWidget.indexOf(self.t1tauwidget)) if self.fit_dialog.connected_figure == gid: self.fit_dialog.connected_figure = None for item in self.fit_dialog.preview_lines: self.current_graph_widget.remove_external(item) if val_figure == gid: self.tabWidget.setCurrentIndex(0) self.current_graph_widget.enable_picking(False) self.current_graph_widget = None self.management.current_graph = '' self.current_plotitem = None wdgt.setParent(None) try: wdgt.deleteLater() except AttributeError: pass break if w is not None: self.area.removeSubWindow(w) w.close() try: w.deleteLater() except AttributeError: pass if self.current_graph_widget is None: if self.area.subWindowList(): self.area.activateNextSubWindow() @QtCore.pyqtSlot(str) def set_graph(self, key: str): w = self.management.graphs[key] subwindow = self.area.addSubWindow(w) subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandMove, True) subwindow.setOption(QtWidgets.QMdiSubWindow.RubberBandResize, True) subwindow.setMinimumHeight(400) subwindow.setMinimumWidth(600) self.datawidget.blockSignals(True) self.datawidget.tree.blockSignals(True) self.datawidget.add_graph(w.id, w.title) self.datawidget.tree.blockSignals(False) self.datawidget.blockSignals(False) w.mousePositionChanged.connect(self.mousemoved) w.aboutToClose.connect(self.delete_windows) w.positionClicked.connect(self.point_selected) w.show() graph_list = self.management.graphs.list() self.t1tauwidget.set_graphs(graph_list) self.ptsselectwidget.set_graphs(graph_list) @QtCore.pyqtSlot(QtWidgets.QMdiSubWindow, name='on_area_subWindowActivated') def change_window(self, wd): """ Called every time focus moves from or to a subwindow. Returns None if current focus is not on a subwindow""" if wd is not None: if self.current_graph_widget is not None: self.current_graph_widget.closable = True if self.ptsselectwidget.isVisible(): self._select_ptswidget(False, False, False) if self.fit_dialog.isVisible(): self._select_fitwidget(False, False) self.current_graph_widget = wd.widget() self.management.current_graph = wd.widget().id self.current_plotitem = self.current_graph_widget.graphic self.change_mouse_mode(self.actionMouse_behaviour.isChecked()) pick = False block = False if self.ptsselectwidget.isVisible(): pick, block = self._select_ptswidget(True, pick, block) if self.fit_dialog.isVisible(): block = self._select_fitwidget(True, block) self._set_pick_block(pick, block) self.datawidget.tree.blockSignals(True) self.datawidget.tree.highlight(self.management.current_graph) self.datawidget.tree.blockSignals(False) @QtCore.pyqtSlot(name='on_actionCascade_windows_triggered') @QtCore.pyqtSlot(name='on_actionTile_triggered') def change_window_size(self): if self.sender() == self.actionCascade_windows: self.area.cascadeSubWindows() elif self.sender() == self.actionTile: self.area.tileSubWindows() @QtCore.pyqtSlot(name='on_actionChange_datatypes_triggered') def type_change_dialog(self): from ..data.conversion import ConversionDialog dialog = ConversionDialog(self) dialog.set_graphs(self.management.graphs.tree()) dialog.convertSets.connect(self.management.convert_sets) _ = dialog.exec() dialog.disconnect() def _set_pick_block(self, pick: bool, block: bool): self.current_graph_widget.enable_picking(pick) self.current_graph_widget.closable = not block @QtCore.pyqtSlot(int, name='on_tabWidget_currentChanged') def toggle_tabs(self, idx: int): widget = self.tabWidget.widget(idx) if self.current_graph_widget is None: if self.tabWidget.currentIndex() > 1: self.tabWidget.removeTab(self.tabWidget.indexOf(widget)) self.tabWidget.setCurrentIndex(0) return if self.current_graph_widget is not None: self.current_graph_widget.enable_picking(False) pick_required, block_window = self._select_ptswidget(widget == self.ptsselectwidget, False, False) self._select_valuewidget(widget == self.valuewidget) pick_required, block_window = self._select_t1tauwidget(widget == self.t1tauwidget, pick_required, block_window) block_window = self._select_fitwidget(widget == self.fit_dialog, block_window) self._set_pick_block(pick_required, block_window) def _select_ptswidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]: if self.current_graph_widget is None: return pick_required, block_window if onoff: # point selection for line in self.ptsselectwidget.pts_lines: self.current_graph_widget.add_external(line) self.ptsselectwidget.point_removed.connect(self.current_graph_widget.remove_external) self.ptsselectwidget.connected_figure = self.management.current_graph pick_required = True else: if self.ptsselectwidget.connected_figure: g = self.management.graphs[self.ptsselectwidget.connected_figure] for line in self.ptsselectwidget.pts_lines: g.remove_external(line) # self.ptsselectwidget.clear() return pick_required, block_window @QtCore.pyqtSlot(int, name='on_tabWidget_tabCloseRequested') def close_tab(self, idx: int): if idx == 0: pass else: self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(idx) def _show_tab(self, mode: str): widget, name = { 't1_temp': (self.t1tauwidget, 'T1 mininmon'), 'signal': (self.editsignalwidget, 'Signals'), 'pick': (self.ptsselectwidget, 'Pick points'), 'fit': (self.fit_dialog, 'Fit') }[mode] for idx in range(self.tabWidget.count()): if self.tabWidget.widget(idx) == widget: self.tabWidget.setCurrentIndex(idx) return self.tabWidget.addTab(widget, name) self.tabWidget.setCurrentIndex(self.tabWidget.count()-1) 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) else: if self.valuewidget.connected_figure is not None: self.management.graphs[self.valuewidget.connected_figure].remove_external(self._values_plot) def _select_integralwidget(self, onoff: bool, pick_required: bool): if self.current_graph_widget is None: return pick_required if onoff: self.integralwidget(self.current_graph_widget.title, self.management.graphs.current_sets(self.management.current_graph)) self.integralwidget.item_deleted.connect(self.current_graph_widget.remove_external) self.integralwidget.connected_figure = self.management.current_graph pick_required = True else: if self.integralwidget.connected_figure: g = self.management.graphs[self.integralwidget.connected_figure] for line in self.integralwidget.lines: g.remove_external(line[0]) g.remove_external(line[1]) self.integralwidget.clear() return pick_required def _select_t1tauwidget(self, onoff: bool, pick_required: bool, block_window: bool): if onoff: # tau from t1 if self.current_graph_widget is None: return pick_required, block_window idx = self.tabWidget.indexOf(self.t1tauwidget) if len(self.current_graph_widget) != 1: QtWidgets.QMessageBox.information(self, 'Too many datasets', 'Only one T1 curve can be evaluated at once') self.tabWidget.removeTab(idx) self.tabWidget.setCurrentIndex(0) return pick_required, block_window self.tabWidget.setTabText(idx, 'T1 min (%s)' % {self.current_graph_widget.title}) self.t1tauwidget.set_graphs(self.management.graphs.list()) self.t1tauwidget.set_data(*self.management.get_data(self.current_graph_widget.active[0], xy_only=True), name=self.management[self.current_graph_widget.active[0]].name) self.t1tauwidget.connected_figure = self.management.current_graph self.current_graph_widget.add_external(self.t1tauwidget.min_pos) self.current_graph_widget.add_external(self.t1tauwidget.parabola) pick_required = True block_window = True else: if self.t1tauwidget.connected_figure: g = self.management.graphs[self.t1tauwidget.connected_figure] g.remove_external(self.t1tauwidget.min_pos) g.remove_external(self.t1tauwidget.parabola) return pick_required, block_window @QtCore.pyqtSlot(str) def get_data(self, key: str): self.sender().set_data(self.management[key]) @QtCore.pyqtSlot(name='on_actionCalculateT1_triggered') def show_t1calc_dialog(self): dialog = QRelaxCalc(self) dialog.set_graphs(self.management.graphs.tree(key_only=True)) dialog.newData.connect(self.management.calc_relaxation) dialog.show() @QtCore.pyqtSlot(name='on_actionSkip_points_triggered') def skip_pts_dialog(self): from ..math.skipping import QSkipDialog dial = QSkipDialog(self) dial.exec() self.management.skip_points(**dial.get_arguments()) @QtCore.pyqtSlot(name='on_action_coup_calc_triggered') def coupling_dialog(self): dialog = QCoupCalcDialog(self) dialog.show() @QtCore.pyqtSlot(name='on_action_mean_t1_triggered') def mean_dialog(self): gnames = self.management.graphs.tree(key_only=True) dialog = QMeanTimes(gnames, parent=self) dialog.newValues.connect(self.management.calc_mean) dialog.show() @QtCore.pyqtSlot(name='on_actionRunning_values_triggered') def smooth_dialog(self): dialog = QSmooth(parent=self) dialog.newValues.connect(self.management.smooth_data) dialog.show() @QtCore.pyqtSlot(name='on_actionInterpolation_triggered') def interpol_dialog(self): if self.current_graph_widget is None: 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() @QtCore.pyqtSlot(name='on_action_calc_triggered') def open_eval_dialog(self): if self.eval is None: self.eval = QEvalDialog(parent=self) self.eval.do_eval.connect(self.management.eval_expression) self.eval.do_calc.connect(self.management.create_from_function) self.eval.set_mode('e') self.eval.set_namespace(self.management.get_namespace()) self.eval.add_data(self.management.active_sets) self.eval.set_graphs(self.management.graphs.list()) self.eval.exec() @QtCore.pyqtSlot(name='on_actionAddlines_triggered') def make_data_from_function(self): if self.eval is None: self.eval = QEvalDialog(parent=self) self.eval.do_eval.connect(self.management.eval_expression) self.eval.do_calc.connect(self.management.create_from_function) self.eval.set_mode('c') self.eval.set_namespace(self.management.get_namespace()) self.eval.set_graphs(self.management.graphs.list()) self.eval.exec() @QtCore.pyqtSlot(name='on_actionDerivation_triggered') @QtCore.pyqtSlot(name='on_actionIntegration_triggered') @QtCore.pyqtSlot(name='on_actionFilon_triggered') def int_diff_ft(self): sets = self.management.active_sets mode = {self.actionIntegration: 'int', self.actionDerivation: 'diff', self.actionFilon: 'logft'}[self.sender()] if sets: from ..math.integrate_derive import QDeriveIntegrate dialog = QDeriveIntegrate(mode) dialog.add_graphs(self.management.graphs.list()) dialog.set_sets(sets) res = dialog.exec() if res: options = dialog.get_options() mode = options['mode'] if mode in ['i', 'd']: self.management.integrate(**options) elif mode == 'l': self.management.logft(**options) else: raise ValueError('Unknown mode %s, not `i`, `d`, `l`.' % str(mode)) @QtCore.pyqtSlot(str) def update_data(self, sid: str): if self.valuewidget.shown_set == sid: self.show_data_values(sid) self.datawidget.set_name(sid, self.management[sid].name) @QtCore.pyqtSlot(str) def delete_data(self, sid): if self.valuewidget.shown_set == sid: self.tabWidget.setCurrentIndex(0) self.datawidget.remove_item(sid) @QtCore.pyqtSlot(name='on_actionBaseline_triggered') def baseline_dialog(self): if not self.current_graph_widget: return if len(self.current_graph_widget) != 1: QtWidgets.QMessageBox.information(self, 'Invalid number of sets', 'Baseline correction can only applied to one set at a time.') return for sid in self.current_graph_widget.active: data_mode = self.management[sid].mode if data_mode in ['spectrum']: editor = QBaselineDialog(self) editor.add_data(*self.management.get_data(sid, xy_only=True)) editor.finished.connect(self.management.apply) editor.exec() @QtCore.pyqtSlot(str) def do_preview(self, mode): 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) 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 tobeadded: dialog.add_data(*self.management.get_data(sid, xy_only=True)) if dialog.exec() == QtWidgets.QDialog.Accepted: self.management.apply(mode, dialog.get_value()) @QtCore.pyqtSlot(name='on_actionMove_between_plots_triggered') def move_sets_dialog(self): gnames = self.management.graphs.tree() self.movedialog.setup(gnames) self.movedialog.exec() @QtCore.pyqtSlot(list, str, str) def move_sets(self, sets, dest, src): self.management.move_sets(sets, dest, src) for s in sets: self.datawidget.tree.move_sets(s, dest, src) @QtCore.pyqtSlot(str) def show_data_values(self, sid: str): if sid == '': return 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 @QtCore.pyqtSlot(object, str) def item_to_graph(self, item, graph_id): self.management.graphs[graph_id].add_external(item) @QtCore.pyqtSlot(object, str) def item_from_graph(self, item, graph_id): self.management.graphs[graph_id].remove_external(item) def closeEvent(self, evt): # self._write_settings() self.close() @QtCore.pyqtSlot(int) def request_data(self, idx): idd = self.datawidget.get_indexes(idx=idx-1) try: x = self.management[idd].x y = self.management[idd].y ret_val = (x, y) except KeyError: ret_val = None self.sender().receive_data(ret_val) return ret_val @QtCore.pyqtSlot(tuple, bool) def point_selected(self, pos, double): w = self.tabWidget.currentWidget() if w == self.ptsselectwidget: line = self.ptsselectwidget.add(pos, double) self.current_graph_widget.add_external(line) elif w == self.t1tauwidget: self.t1tauwidget.t1min_picked(pos) def _select_fitwidget(self, onoff: bool, block_window: bool): if self.current_graph_widget is not None: pass if onoff: self.fit_dialog.connected_figure = self.management.current_graph self.fit_dialog.load(self.management.active_sets) for item in self.fit_dialog.preview_lines: self.current_graph_widget.add_external(item) if self.action_custom_range.isChecked(): self.current_graph_widget.add_external(self.fitregion) block_window = True else: for item in self.fit_dialog.preview_lines: self.current_graph_widget.remove_external(item) self.current_graph_widget.remove_external(self.fitregion) return block_window @QtCore.pyqtSlot(QtWidgets.QAction) def change_fit_limits(self, action: QtWidgets.QAction): if action == self.action_custom_range and self.fit_dialog.isVisible(): self.current_graph_widget.add_external(self.fitregion) else: self.current_graph_widget.remove_external(self.fitregion) def start_fit(self, parameter, links, fit_options): fit_options['limits'] = { self.action_no_range: 'none', self.action_x_range: 'x', self.action_custom_range: self.fitregion.getRegion() }[self.ac_group2.checkedAction()] fit_options['fit_mode'] = { self.action_lm_fit: 'lsq', self.action_nm_fit: 'nm', self.action_odr_fit: 'odr' }[self.ac_group.checkedAction()] self.fit_dialog.fit_button.setEnabled(False) self.management.start_fit(parameter, links, fit_options) @QtCore.pyqtSlot(dict, int, bool) def show_fit_preview(self, funcs: dict, num: int, show: bool): if self.fit_dialog.connected_figure is None: return g = self.management.graphs[self.fit_dialog.connected_figure] for item in self.fit_dialog.preview_lines: g.remove_external(item) if show: x_lim, _ = g.ranges space = geomspace if g.log[0] else linspace x = space(1.01*x_lim[0], 0.99*x_lim[1], num=num) self.fit_dialog.make_previews(x, funcs) for item in self.fit_dialog.preview_lines: g.add_external(item) @QtCore.pyqtSlot(list) def show_fit_results(self, results: list): self.fit_dialog.fit_button.setEnabled(True) if results: res_dialog = QFitResult(results, self.management, parent=self) res_dialog.add_graphs(self.management.graphs.list()) res_dialog.closed.connect(self.accepts_fit) res_dialog.redoFit.connect(self.management.redo_fits) res_dialog.show() @QtCore.pyqtSlot(dict, list, str, bool, dict) def accepts_fit(self, res: dict, opts: list, param_graph: str, show_fit: bool, parts: dict) -> None: self.fit_dialog.set_parameter(res) self.management.make_fits(res, opts, param_graph, show_fit, parts) @QtCore.pyqtSlot(name='on_actionFunction_editor_triggered') def edit_models(self): if self.editor is None: from ..lib.usermodeleditor import QUsermodelEditor self.editor = QUsermodelEditor(config_paths() / 'usermodels.py', parent=self) self.editor.modelsChanged.connect(self.update_fitmodels) self.editor.setWindowModality(QtCore.Qt.ApplicationModal) self.editor.show() @QtCore.pyqtSlot(name='on_actionShift_triggered') def shift_dialog(self): s = QShift(self) s.set_graphs(self.management.graphs.list()) for key, name in self.management.active_sets: data = self.management.data[key] s.add_item(key, name, data.x, data.y) s.valuesChanged.connect(self.management.shift_scale) s.show() def update_fitmodels(self): pass @staticmethod @QtCore.pyqtSlot(name='on_actionDocumentation_triggered') def open_doc(): docpath = '/autohome/dominik/auswerteprogramm3/doc/_build/html/index.html' import webbrowser webbrowser.open(docpath) def dropEvent(self, evt): if evt.mimeData().hasUrls(): files = [str(url.toLocalFile()) for url in evt.mimeData().urls()] self.management.load_files(files) def dragEnterEvent(self, evt): evt.accept() @QtCore.pyqtSlot(bool, name='on_actionMouse_behaviour_toggled') def change_mouse_mode(self, is_checked: bool): if is_checked: self.current_plotitem.plotItem.vb.setMouseMode(ViewBox.RectMode) else: self.current_plotitem.plotItem.vb.setMouseMode(ViewBox.PanMode) def mousemoved(self, xpos, ypos): self.mousepos.setText(f'x={xpos:.3g}; y={ypos:.3g}') @QtCore.pyqtSlot(name='on_actionSnake_triggered') @QtCore.pyqtSlot(name='on_actionTetris_triggered') @QtCore.pyqtSlot(name='on_actionLife_triggered') @QtCore.pyqtSlot(name='on_actionMine_triggered') def spannung_spiel_und_spass(self): if self.sender() == self.actionLife: from ..lib.gol import QGameOfLife game = QGameOfLife(parent=self) game.setWindowModality(QtCore.Qt.NonModal) game.show() elif self.sender() == self.actionMine: from ..lib.stuff import QMines game = QMines(parent=self) game.setWindowModality(QtCore.Qt.NonModal) game.show() else: from ..lib.stuff import Game if self.sender() == self.actionSnake: gtype = 'snake' else: gtype = 'tetris' game = Game(gtype, parent=self) game.show() @QtCore.pyqtSlot(name='on_actionConfiguration_triggered') def open_configuration(self): from ..lib.configurations import GeneralConfiguration dialog = GeneralConfiguration(self) dialog.show() @QtCore.pyqtSlot(name='on_action_colorcycle_triggered') def open_color_dialog(self): from ..lib.color_dialog import ColorDialog dialog = ColorDialog(self) dialog.show() def close(self): write_state({'recent_path': str(self.path)}) super().close() def read_state(self): opts = read_state() self.path = pathlib.Path(opts.get('recent_path', Path.home()))