139 lines
4.0 KiB
Python
139 lines
4.0 KiB
Python
from enum import Enum
|
|
from math import trunc
|
|
|
|
import pyqtgraph as pg
|
|
|
|
from callbacks import CallbackDispatcher, CallbackType
|
|
from detektor_plot import DetektorPlot
|
|
from detektor_data import DetektorContainer
|
|
|
|
|
|
class DetektorRegionState(Enum):
|
|
UNSET = 1
|
|
SET = 4
|
|
COPIED = 5
|
|
|
|
|
|
class DetektorRegion(pg.LinearRegionItem):
|
|
"""Encapsulates a linear region that snaps to discrete X-axis values and shows a context menu."""
|
|
|
|
# in which mode the region is
|
|
_state: DetektorRegionState = DetektorRegionState.UNSET
|
|
_plot: DetektorPlot = None
|
|
|
|
# the start and end is integer, not a label (time)
|
|
_start_position: int = 0
|
|
_end_position: int = 0
|
|
|
|
def __init__(self, plot: DetektorPlot):
|
|
super().__init__()
|
|
|
|
# reference to the chart so we can add and remove the widget
|
|
self._plot = plot
|
|
|
|
# move the rectangle behind the plot lines
|
|
self.setZValue(-10)
|
|
|
|
# color is the same for selecting and hovering
|
|
grid_color = pg.mkBrush((100, 100, 250, 50))
|
|
self.setBrush(grid_color)
|
|
self.setHoverBrush(grid_color)
|
|
|
|
self.setAcceptHoverEvents(True)
|
|
|
|
# callback for changing the width
|
|
self.sigRegionChanged.connect(self.snap_to_x_labels)
|
|
|
|
@property
|
|
def state(self):
|
|
return self._state
|
|
|
|
@state.setter
|
|
def state(self, v: DetektorRegionState):
|
|
""" Sets state and calls hooks if it changes from the previous one """
|
|
if v != self._state:
|
|
self._state = v
|
|
|
|
CallbackDispatcher().call(CallbackType.REGION_STATE)
|
|
|
|
def set(self):
|
|
""" Displays region occupying roughly a third of the actual view range """
|
|
self.state = DetektorRegionState.SET
|
|
x_range, y_range = self._plot.view_box.viewRange()
|
|
x_min, x_max = x_range
|
|
third = (x_max - x_min) / 3
|
|
self.setRegion([x_min + third, x_max - third])
|
|
self.display()
|
|
|
|
def unset(self):
|
|
self.state = DetektorRegionState.UNSET
|
|
self.hide()
|
|
|
|
def get_safe_region(self):
|
|
start, end = self.getRegion()
|
|
if start < 0:
|
|
start = 0
|
|
if end > DetektorContainer().get().data_count()-1:
|
|
end = DetektorContainer().get().data_count()
|
|
|
|
return trunc(start), trunc(end)
|
|
|
|
def delete(self):
|
|
""" Deletes data by cutting the region without keeping it """
|
|
start, end = self.get_safe_region()
|
|
DetektorContainer().duplicate()
|
|
DetektorContainer().get().cut(start, end)
|
|
|
|
self.state = DetektorRegionState.UNSET
|
|
self.hide()
|
|
|
|
def copy(self):
|
|
""" Copies the data """
|
|
start, end = self.get_safe_region()
|
|
DetektorContainer().get().copy(start, end)
|
|
|
|
self.state = DetektorRegionState.COPIED
|
|
|
|
def cut(self):
|
|
""" Cuts the data and hiding the region """
|
|
start, end = self.get_safe_region()
|
|
DetektorContainer().duplicate()
|
|
DetektorContainer().get().cut(start, end)
|
|
|
|
self.state = DetektorRegionState.COPIED
|
|
self.hide()
|
|
|
|
def paste_end(self):
|
|
DetektorContainer().duplicate()
|
|
DetektorContainer().get().paste(
|
|
DetektorContainer().get().data_count()
|
|
)
|
|
|
|
self.unset()
|
|
|
|
def paste_after(self):
|
|
_, end = self.get_safe_region()
|
|
DetektorContainer().duplicate()
|
|
DetektorContainer().get().paste(end)
|
|
|
|
self.unset()
|
|
|
|
def paste_at(self):
|
|
cursor_x = self._plot.cursorLine.getXPos()
|
|
DetektorContainer().duplicate()
|
|
DetektorContainer().get().paste(cursor_x)
|
|
|
|
self.unset()
|
|
|
|
def snap_to_x_labels(self):
|
|
"""Snaps the region boundaries to the nearest discrete X-axis values."""
|
|
min_x, max_x = self.getRegion()
|
|
self.setRegion([int(round(min_x)), int(round(max_x))])
|
|
|
|
def display(self):
|
|
""" Adds the region to the plot """
|
|
self._plot.graphWidget.addItem(self)
|
|
|
|
def hide(self):
|
|
""" Removes the region from the plot """
|
|
self._plot.graphWidget.removeItem(self) |