nmreval/src/gui_qt/data/point_select.py

203 lines
6.8 KiB
Python

import re
from ..Qt import QtCore, QtWidgets
from .._py.ptstab import Ui_Form
from ..lib.pg_objects import LogInfiniteLine, RegionItem
__all__ = ['PointSelectWidget']
REGION_RE = re.compile(r'(?P<first>[+-]*\d+(?:\.\d*)*(?:[eE][+-]*\d+)*)'
r'(?: ?- ?(?P<second>[+-]*\d+(?:\.\d*)*(?:[eE][+-]*\d+)*))*')
class PointSelectWidget(QtWidgets.QWidget, Ui_Form):
widget_closed = QtCore.pyqtSignal()
points_selected = QtCore.pyqtSignal(dict, str)
point_removed = QtCore.pyqtSignal(LogInfiniteLine)
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self.pts = []
self.pts_lines = []
self.nop = 0
self._prev_pos = ''
self._last_item = None
self.connected_figure = ''
self.okButton.clicked.connect(self.apply)
self.deleteButton.clicked.connect(self.remove_points)
self.peaktable.itemChanged.connect(self.editing_finished)
self.peaktable.itemDoubleClicked.connect(self.editing_started)
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Delete:
self.remove_points()
elif e.key() == QtCore.Qt.Key_F2:
self.editing_started()
else:
super().keyPressEvent(e)
def clear(self):
self.pts = []
self.nop = 0
self.peaktable.clear()
self.pts_lines = []
@QtCore.pyqtSlot(tuple, bool)
def add(self, pos: tuple, double: bool):
x = pos[0]
if double:
self.removepoint(-1)
self.pts.append((x, x*1.1))
item = RegionItem(values=[x, x*1.1], mode='mid')
item.sigRegionChanged.connect(self._update_region)
else:
self.pts.append(x)
item = LogInfiniteLine(pos=x, movable=True)
item.sigPositionChanged.connect(self._update_line)
self.pts_lines.append(item)
self.nop += 1
self._makerow()
return item
def remove_points(self):
for i in sorted(self.peaktable.selectedIndexes(), key=lambda x: x.row(), reverse=True):
self.removepoint(pos=i.row())
def removepoint(self, pos=0):
if pos == -1:
pos = len(self.pts) - 1
try:
self.pts.pop(pos)
self.nop -= 1
item = self.peaktable.takeItem(pos)
del item
del_line = self.pts_lines.pop(pos)
self.point_removed.emit(del_line)
del del_line
except IndexError:
pass
def _makerow(self):
if isinstance(self.pts[-1], tuple):
item = QtWidgets.QListWidgetItem(f'{self.pts[-1][0]:.5g} - {self.pts[-1][1]:.5g}')
else:
item = QtWidgets.QListWidgetItem(f'{self.pts[-1]:.5g}')
item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
self.peaktable.blockSignals(True)
self.peaktable.addItem(item)
self.peaktable.blockSignals(False)
def closeEvent(self, evt):
self.widget_closed.emit()
super().closeEvent(evt)
@QtCore.pyqtSlot()
def apply(self) -> dict:
ret_dic = {'avg_range': [self.left_pt.value(), self.right_pt.value()],
'avg_mode': {0: 'mean', 1: 'sum', 2: 'integral'}[self.average_combobox.currentIndex()],
'special': None, 'idx': None,
'xy': (self.xbutton.isChecked(), self.ybutton.isChecked())}
if self.groupBox_2.isChecked():
ret_dic['special'] = {0: 'max', 1: 'absmax', 2: 'min', 3: 'absmin'}[self.special_comboBox.currentIndex()]
if len(self.pts) != 0:
ret_dic['idx'] = self.pts
if self.graph_checkbox.isChecked():
gid = ''
else:
gid = self.graph_combobox.currentData()
self.points_selected.emit(ret_dic, gid)
return ret_dic
def _update_region(self):
try:
idx = self.pts_lines.index(self.sender())
except ValueError:
return
self.pts[idx] = self.sender().getRegion()
self.peaktable.blockSignals(True)
self.peaktable.item(idx).setText('{:.5g} - {:.5g}'.format(*self.pts[idx]))
self.peaktable.blockSignals(False)
def _update_line(self):
try:
idx = self.pts_lines.index(self.sender())
except ValueError:
return
self.pts[idx] = self.sender().value()
self.peaktable.blockSignals(True)
self.peaktable.item(idx).setText(f'{self.pts[idx]:.5g}')
self.peaktable.blockSignals(False)
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def editing_started(self, item=None):
if item is None:
item = self.peaktable.selectedItems()[0]
self._prev_pos = item.text()
self.peaktable.editItem(item)
@QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
def editing_finished(self, it: QtWidgets.QListWidgetItem):
m = re.match(REGION_RE, it.text()).groupdict()
undo = True
if m:
start, stop = m['first'], m['second']
row = self.peaktable.row(it)
it_pts = self.pts_lines[row]
if ((stop is None) and isinstance(it_pts, RegionItem)) or \
((stop is not None) and isinstance(it_pts, LogInfiniteLine)):
QtWidgets.QMessageBox().information(self, 'Invalid type',
'Conversion between point and region is not possible.')
else:
if stop is None:
it_pts.blockSignals(True)
it_pts.setValue(float(start))
it_pts.blockSignals(False)
self.pts[row] = float(start)
else:
start, stop = float(start), float(stop)
pos = (min(start, stop), max(start, stop))
self.pts[row] = pos
self.peaktable.blockSignals(True)
it.setText(f'{pos[0]:.5g} - {pos[1]:.5g}')
self.peaktable.blockSignals(False)
it_pts.blockSignals(True)
it_pts.setRegion(pos)
it_pts.blockSignals(False)
undo = False
if undo:
self.peaktable.blockSignals(True)
it.setText(self._prev_pos)
self.peaktable.blockSignals(False)
def set_graphs(self, graphs: list):
last_graph = self.graph_combobox.currentData()
self.graph_combobox.clear()
idx = 0
for i, g in enumerate(graphs):
self.graph_combobox.addItem(g[1], userData=g[0])
if g[0] == last_graph:
idx = i
self.graph_combobox.setCurrentIndex(idx)
@QtCore.pyqtSlot(int, name='on_graph_checkbox_stateChanged')
def changed_state(self, checked):
self.graph_combobox.setEnabled(checked!=QtCore.Qt.Checked)