nmreval/src/gui_qt/main/mainwindow.py

1022 lines
40 KiB
Python
Raw Normal View History

2022-10-20 15:23:15 +00:00
from __future__ import annotations
import os
2022-03-08 09:27:40 +00:00
import pathlib
2022-03-24 16:35:10 +00:00
import re
2022-03-08 09:27:40 +00:00
from pathlib import Path
from numpy import geomspace, linspace
from pyqtgraph import ViewBox
2022-03-08 09:27:40 +00:00
2022-10-20 15:23:15 +00:00
from nmreval.configs import *
2022-03-08 09:27:40 +00:00
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
from ..lib.utils import UpdateDialog, open_bug_report, Updater
2022-03-08 09:27:40 +00:00
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()
if os.getenv('APPIMAGE') is not None:
if Updater.get_update_information(os.getenv('APPIMAGE'))[0]:
self.look_for_update()
2022-03-08 09:27:40 +00:00
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._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.integralwidget.management = self.management
# self.drawingswidget.graphs = self.management.graphs
2022-03-08 09:27:40 +00:00
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)
self.action_draw_object.setVisible(False)
2022-03-08 09:27:40 +00:00
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.actionIntegration.triggered.connect(lambda: self._show_tab('integrate'))
2022-03-08 09:27:40 +00:00
self.action_FitWidget.triggered.connect(lambda: self._show_tab('fit'))
self.action_draw_object.triggered.connect(lambda: self._show_tab('drawing'))
2022-03-08 09:27:40 +00:00
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)
2022-04-03 14:42:44 +00:00
self.valuewidget.split_signal.connect(self.management.split_set)
2022-03-08 09:27:40 +00:00
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',
filter='All files (*.*);;'
'Program session (*.nmr);;'
'HDF files (*.h5);;'
'Text files (*.txt *.dat);;'
'Novocontrol Alpha (*.EPS);;'
'TecMag files (*.tnt);;'
'Grace files (*.agr)')
2022-03-08 09:27:40 +00:00
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)
2022-03-08 09:27:40 +00:00
mode = save_dialog.exec()
if mode == QtWidgets.QDialog.Accepted:
2022-03-24 16:35:10 +00:00
savefile = save_dialog.save_file()
2022-03-08 09:27:40 +00:00
selected_filter = save_dialog.selectedNameFilter()
2022-03-24 16:35:10 +00:00
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'))
2022-03-08 09:27:40 +00:00
@QtCore.pyqtSlot()
@QtCore.pyqtSlot(list)
2022-10-20 15:23:15 +00:00
def save_fit_parameter(self, fit_sets: list[str] = None):
save_dialog = SaveDirectoryDialog(parent=self, caption='Save fit parameter', directory=str(self.path),
filter='All files(*, *);;Text files(*.dat *.txt)')
2022-03-08 09:27:40 +00:00
mode = save_dialog.exec()
if mode == QtWidgets.QDialog.Accepted:
savefile = save_dialog.save_file()
if savefile:
self.management.save_fit_parameter(savefile, fit_sets=fit_sets)
2022-03-08 09:27:40 +00:00
@QtCore.pyqtSlot(name='on_actionExportGraphic_triggered')
def export_graphic(self):
2022-03-24 16:35:10 +00:00
self.current_graph_widget.export_dialog()
2022-03-08 09:27:40 +00:00
@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))
2022-04-03 14:42:44 +00:00
if self.valuewidget.isVisible():
self.valuewidget(self.management.graphs.tree())
2022-03-08 09:27:40 +00:00
@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()
2022-03-08 09:27:40 +00:00
w = None
for w in self.area.subWindowList():
wdgt = w.widget()
if wdgt.id == gid:
wdgt.disconnect()
wdgt.scene.disconnect()
2022-03-08 09:27:40 +00:00
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:
2022-03-08 09:27:40 +00:00
self.tabWidget.setCurrentIndex(0)
self.current_graph_widget.enable_picking(False)
2022-03-08 09:27:40 +00:00
self.current_graph_widget = None
self.management.current_graph = ''
self.current_plotitem = None
wdgt.setParent(None)
try:
wdgt.deleteLater()
except AttributeError:
pass
2022-03-08 09:27:40 +00:00
break
2022-03-08 09:27:40 +00:00
if w is not None:
self.area.removeSubWindow(w)
2022-03-08 09:27:40 +00:00
w.close()
try:
w.deleteLater()
except AttributeError:
pass
2022-03-08 09:27:40 +00:00
if self.current_graph_widget is None:
if self.area.subWindowList():
self.area.activateNextSubWindow()
2022-03-08 09:27:40 +00:00
@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())
2022-03-08 09:27:40 +00:00
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
2022-10-30 17:45:43 +00:00
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'),
'drawing': (self.drawingswidget, 'Draw'),
'integrate': (self.integralwidget, 'Integrate'),
2022-10-30 17:45:43 +00:00
}[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)
@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)
2022-03-08 09:27:40 +00:00
@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._select_drawingswidget(widget == self.drawingswidget)
pick_required = self._select_integralwidget(widget == self.integralwidget, pick_required, block_window)
2022-03-08 09:27:40 +00:00
self._set_pick_block(pick_required, block_window)
2022-10-20 15:23:15 +00:00
def _select_ptswidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]:
2022-03-08 09:27:40 +00:00
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
def _select_valuewidget(self, onoff: bool):
if onoff: # Values
self.valuewidget(self.management.graphs.tree())
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)
2022-03-08 09:27:40 +00:00
else:
if self.valuewidget.connected_figure is not None:
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)
2022-03-08 09:27:40 +00:00
def _select_integralwidget(self, onoff: bool, pick_required: bool, block_window: bool) -> tuple[bool, bool]:
2022-03-08 09:27:40 +00:00
if self.current_graph_widget is None:
return pick_required, block_window
2022-03-08 09:27:40 +00:00
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
block_window = True
2022-03-08 09:27:40 +00:00
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, block_window
2022-03-08 09:27:40 +00:00
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
2022-03-08 09:27:40 +00:00
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
2022-03-08 09:27:40 +00:00
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
def _select_drawingswidget(self, onoff):
if onoff:
self.drawingswidget(self.management.graphs.list())
self.drawingswidget.connected_figure = self.management.current_graph
else:
self.drawingswidget.clear()
2022-03-08 09:27:40 +00:00
@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())
2022-03-22 19:07:59 +00:00
@QtCore.pyqtSlot(name='on_action_coup_calc_triggered')
2022-03-08 09:27:40 +00:00
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')
2022-03-08 09:27:40 +00:00
@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()]
2022-03-08 09:27:40 +00:00
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):
2022-03-08 09:27:40 +00:00
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)
2022-03-08 09:27:40 +00:00
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, 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)
2022-03-08 09:27:40 +00:00
@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)
elif w == self.integralwidget:
region, integral_plot = self.integralwidget.add(pos)
self.current_graph_widget.add_external(region)
self.current_graph_widget.add_external(integral_plot)
2022-03-08 09:27:40 +00:00
def _select_fitwidget(self, onoff: bool, block_window: bool):
if self.current_graph_widget is not None:
2022-03-24 16:35:10 +00:00
pass
2022-03-08 09:27:40 +00:00
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)
2022-03-08 09:27:40 +00:00
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)
2022-03-08 09:27:40 +00:00
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)
2022-03-08 09:27:40 +00:00
@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(lambda: self.fit_dialog.read_and_load_functions())
2022-03-08 09:27:40 +00:00
self.editor.setWindowModality(QtCore.Qt.ApplicationModal)
self.editor.show()
2023-01-08 18:30:15 +00:00
@QtCore.pyqtSlot(name='on_action_create_fit_function_triggered')
def open_fitmodel_wizard(self):
from ..fit.function_creation_dialog import QUserFitCreator
helper = QUserFitCreator(config_paths() / 'usermodels.py', parent=self)
helper.classCreated.connect(lambda: self.fit_dialog.read_and_load_functions())
helper.show()
2022-03-08 09:27:40 +00:00
@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()
@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):
2022-03-08 09:27:40 +00:00
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}')
2022-03-08 09:27:40 +00:00
@QtCore.pyqtSlot(name='on_actionSnake_triggered')
@QtCore.pyqtSlot(name='on_actionTetris_triggered')
@QtCore.pyqtSlot(name='on_actionLife_triggered')
@QtCore.pyqtSlot(name='on_actionMine_triggered')
2022-03-08 09:27:40 +00:00
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()
2022-03-08 09:27:40 +00:00
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()
2022-03-08 09:27:40 +00:00
def close(self):
write_state({'History': {'recent path': str(self.path)}})
2022-03-08 09:27:40 +00:00
super().close()
def read_state(self):
opts = read_state()
self.path = pathlib.Path(opts['History'].get('recent path', Path.home()))
@QtCore.pyqtSlot(name='on_actionBugs_triggered')
def report_bug(self):
open_bug_report()
@QtCore.pyqtSlot(name='on_actionUpdate_triggered')
def look_for_update(self):
w = UpdateDialog(parent=self)
w.show()