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[+-]*\d+(?:\.\d*)*(?:[eE][+-]*\d+)*)' r'(?: ?- ?(?P[+-]*\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)