Source code for pdsspect.pan_view

"""Display data in pan and make ROI selections"""
from functools import wraps

from qtpy import QtWidgets

from .roi import Polygon, Rectangle, Pencil
from .pds_image_view_canvas import PDSImageViewCanvas
from .pdsspect_image_set import PDSSpectImageSetViewBase, SubPDSSpectImageSet


[docs]class PanViewController(object): """Controller for the :class:`PanView` Parameters ---------- image_set : :class:`~.pdsspect_image_set.PDSSpectImageSet` pdsspect model view : :class:`PanView` View to control Attributes ---------- image_set : :class:`~.pdsspect_image_set.PDSSpectImageSet` pdsspect model view : :class:`PanView` View to control """ def __init__(self, image_set, view): self.image_set = image_set self.view = view def _get_parent_set(self): if isinstance(self.image_set, SubPDSSpectImageSet): image_set = self.image_set.parent_set else: image_set = self.image_set return image_set
[docs] def add_ROI(self, coordinates): """Add a region of interest Parameters ---------- coordinates : :class:`numpy.ndarray` or :obj:`tuple` Either a ``(m x 2)`` array or a tuple of two arrays If an array, the first column are the x coordinates and the second are the y coordinates. If a tuple of arrays, the first array are x coordinates and the second are the corresponding y coordinates. """ if self.image_set.simultaneous_roi: parent_set = self._get_parent_set() for image_set in [parent_set] + parent_set.subsets: image_set.add_coords_to_roi_data_with_color( coordinates=coordinates, color=image_set.color, ) else: self.image_set.add_coords_to_roi_data_with_color( coordinates=coordinates, color=self.image_set.color, )
[docs] def erase_ROI(self, coordinates): """Erase any region of interest inside coordinates Parameters ---------- coordinates : :class:`numpy.ndarray` or :obj:`tuple` Either a ``(m x 2)`` array or a tuple of two arrays If an array, the first column are the x coordinates and the second are the y coordinates. If a tuple of arrays, the first array are x coordinates and the second are the corresponding y coordinates. """ if self.image_set.simultaneous_roi: parent_set = self._get_parent_set() for image_set in [parent_set] + parent_set.subsets: image_set._erase_coords(coordinates) else: self.image_set._erase_coords(coordinates)
[docs]class PanView(QtWidgets.QWidget, PDSSpectImageSetViewBase): """View of the image inside the pan Parameters ---------- image_set : :class:`~.pdsspect_image_set.PDSSpectImageSet` pdsspect model parent : None The parent of the view Attributes ---------- image_set : :class:`~.pdsspect_image_set.PDSSpectImageSet` pdsspect model controller : :class:`PanViewController` The view's controller parent : None The view's parent main_layout : :class:`QtWidgets.QVBoxLayout <PySide.QtGui.QVBoxLayout>` The main layout of the view view_canvas : :class:`~pdsspect.pds_image_view_canvas.PDSImageViewCanvas` Canvas to view the image """ def __init__(self, image_set, parent=None): super(PanView, self).__init__(parent) self.image_set = image_set self.image_set.register(self) self.controller = PanViewController(self.image_set, self) self._making_roi = False self._current_roi = None self.main_layout = QtWidgets.QVBoxLayout() save_layout = QtWidgets.QHBoxLayout() save_frame_btn = QtWidgets.QPushButton('Save Frame') save_frame_btn.clicked.connect(self.save_frame) save_layout.addWidget(save_frame_btn) save_layout.addStretch() self.main_layout.addLayout(save_layout) self.view_canvas = PDSImageViewCanvas() self.view_canvas.set_callback('cursor-down', self.start_ROI) self.view_canvas.set_callback('draw-down', self.stop_ROI) self.view_canvas.set_callback('motion', self.extend_ROI) self.view_canvas.set_window_size(*self.image_set.pan_data.shape) self.main_layout.addWidget(self.view_canvas.get_widget()) self.view_canvas.get_widget().setMouseTracking(True) self.setLayout(self.main_layout) self.setMouseTracking(True) self.set_data() self.view_canvas.add(self.image_set._maskrgb_obj) @property def is_erasing(self): """:obj:`bool` : True if current color is ``eraser`` false otherwise""" return self.image_set.color == 'eraser'
[docs] def set_data(self): """Set pan data on the canvas""" self.view_canvas.set_data(self.image_set.pan_data) self.set_roi_data()
[docs] def set_roi_data(self): """Set the ROI data on the canvas""" self.image_set._maskrgb.set_data(self.image_set.pan_roi_data) self.view_canvas.redraw()
def change_roi_opacity(self): self.set_roi_data()
[docs] def set_image(self): """Set the data""" self.set_data() self.view_canvas.zoom_fit()
[docs] def move_pan(self): """Set the data when the pan is moved""" self.set_data() self.view_canvas.zoom_fit()
def _make_x_y_in_pan(self, x, y): bottom, top = -.5, self.image_set.pan_height * 2 - 1. left, right = -.5, self.image_set.pan_width * 2 - 1. x = left if x < left else x x = right if x > right else x y = bottom if y < bottom else y y = top if y > top else y return x, y
[docs] def check_ROI_in_pan(func): """Wrapper to make sure ROI stays inside the current view""" @wraps(func) def wrapper(self, view_canvas, button, data_x, data_y): data_x, data_y = self._make_x_y_in_pan(data_x, data_y) return func(self, view_canvas, button, data_x, data_y) return wrapper
[docs] @check_ROI_in_pan def start_ROI(self, view_canvas, button, data_x, data_y): """Start the ROI at the mouse location""" if self._making_roi: self.continue_ROI(view_canvas, button, data_x, data_y) else: if self.image_set.selection_type == 'filled polygon': ROI = Polygon elif self.image_set.selection_type == 'filled rectangle': ROI = Rectangle elif self.image_set.selection_type == 'pencil': ROI = Pencil fillalpha = self.image_set.alpha if not self.is_erasing else 0 self._current_roi = ROI( self.image_set, view_canvas, color=self.image_set.color, fill=True, alpha=self.image_set.alpha, fillcolor=self.image_set.color, fillalpha=fillalpha, ) self._current_roi.start_ROI(data_x, data_y) self._making_roi = True if ROI == Pencil: self.stop_ROI(view_canvas, button, data_x, data_y)
[docs] def check_roi_in_process(func): """Wrapper to make sure the roi making is in process""" @wraps(func) def wrapper(self, *args, **kwargs): if not self._making_roi or not self._current_roi: return else: return func(self, *args, **kwargs) return wrapper
[docs] @check_ROI_in_pan @check_roi_in_process def continue_ROI(self, view_canvas, button, data_x, data_y): """Continue the ROI making on click""" if self.image_set.selection_type == 'filled polygon': self._current_roi.continue_ROI(data_x, data_y) elif self.image_set.selection_type == 'filled rectangle': self.stop_ROI(view_canvas, button, data_x, data_y) elif self.image_set.selection_type == 'pencil': self._current_roi.continue_ROI(data_x, data_y)
[docs] @check_ROI_in_pan @check_roi_in_process def extend_ROI(self, view_canvas, button, data_x, data_y): """Extend the ROI on mouse motion""" self._current_roi.extend_ROI(data_x, data_y)
[docs] @check_ROI_in_pan @check_roi_in_process def stop_ROI(self, view_canvas, button, data_x, data_y): """Stop ROI on right click""" coords = self._current_roi.stop_ROI(data_x, data_y) if self.is_erasing: self.controller.erase_ROI(coords) else: self.controller.add_ROI(coords) self._making_roi = False self._current_roi = None
[docs] def redraw(self): """Redraw :attr:`view_canvas`""" self.view_canvas.redraw()
def resizeEvent(self, event): self.view_canvas.zoom_fit() self.redraw()
[docs] def save_frame(self): """Save current frame as image""" save_file, _ = QtWidgets.QFileDialog.getSaveFileName(parent=self) frame = self.view_canvas.get_widget().grab() frame.save(save_file)
class PanViewWidget(QtWidgets.QDialog): """Widget to hold the different pan windows Parameters ---------- pan : :class:`PanView` First :class:`PanView` to include in the widget parent : :class:`QWidget <PySide.QtWidgets.QWidget>` The parent widget Attributes ---------- pans : :obj:`list` of :class:`PanView` The :class:`PanView`s in the widget """ def __init__(self, pan, parent): super(PanViewWidget, self).__init__(parent) self.main_layout = QtWidgets.QHBoxLayout() self.pans = [] self.add_pan(pan) self.setWindowTitle('Pan View') self.setLayout(self.main_layout) def add_pan(self, pan): """Add a :class:`PanView` to the widget Parameters ---------- pan : :class:`PanView` First :class:`PanView` to include in the widget """ self.pans.append(pan) self.main_layout.addWidget(pan)