Files
detektor/src/detektor_region.py
2025-03-10 17:10:04 +01:00

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)