def getArrayRegion(self, arr, img=None, axes=(0, 1), **kwds): """ Return the result of ROI.getArrayRegion() masked by the elliptical shape of the ROI. Regions outside the ellipse are set to 0. """ # Note: we could use the same method as used by PolyLineROI, but this # implementation produces a nicer mask. if kwds["returnMappedCoords"]: arr, coords = pgROI.getArrayRegion(self, arr, img, axes, **kwds) else: arr = pgROI.getArrayRegion(self, arr, img, axes, **kwds) if arr is None or arr.shape[axes[0]] == 0 or arr.shape[axes[1]] == 0: return arr w = arr.shape[axes[0]] h = arr.shape[axes[1]] ## generate an ellipsoidal mask mask = np.fromfunction( lambda x, y: (((x + 0.5) / (w / 2.) - 1)**2 + ((y + 0.5) / (h / 2.) - 1)**2)**0.5 < 1, (w, h)) # reshape to match array axes if axes[0] > axes[1]: mask = mask.T shape = [(n if i in axes else 1) for i, n in enumerate(arr.shape)] mask = mask.reshape(shape) if kwds["returnMappedCoords"]: return arr * mask, coords else: return arr * mask
def __init__(self, pos=None, size=None, **kargs): if size == None: # size = [100e-6,100e-6] size = [20, 20] if pos == None: pos = [0, 0] self._shape = None ROI.__init__(self, pos, size, **kargs) self.sigRegionChanged.connect(self.invalidate) self.aspectLocked = True
def __init__(self, pos=None, size=None, **kargs): if size == None: #size = [100e-6,100e-6] size = [20, 20] if pos == None: pos = [0, 0] self._shape = None ROI.__init__(self, pos, size, **kargs) self.sigRegionChanged.connect(self.invalidate) self.aspectLocked = True
def test_add_lineout(self, mock_except, init_prog): mock_except.side_effect = [None, ExpectedError] prog = init_prog ROI_dict = {'ROI_00': ROI((0, 0)), 'ROI_01': ROI((1, 1)), 'ROI_02': ROI((2, 2)), 'ROI_03': ROI((3, 3))} prog.roi_manager.ROIs = ROI_dict item_param_0 = Parameter(name='ROI_00') item_param_1 = Parameter(name='ROI_01') item_param_2 = Parameter(name='ROI_02') item_param_3 = Parameter(name='ROI_03') item_params = [item_param_0, item_param_1, item_param_2, item_param_3] for item_param in item_params: channel_param = Parameter(name='use_channel', type=[]) color_param = Parameter(name='Color', type=[]) children = [channel_param, color_param] item_param.addChildren(children) rois_param = Parameter(name='ROIs', children=item_params) prog.roi_manager.settings = Parameter(name='settings', children=[rois_param]) prog.labels = ['label_0', 'label_1', 'label_2', 'label_3'] prog.add_lineout(1) item = prog.roi_manager.ROIs['ROI_01'] item_param = prog.roi_manager.settings.child('ROIs', 'ROI_01') assert item_param.child('use_channel').value() == prog.labels[0] for ind in prog.lo_items: assert np.array_equal(prog.lo_data[ind], np.zeros((1,))) item_param_4 = Parameter(name='ROI_04') channel_param = Parameter(name='use_channel') color_param = Parameter(name='Color') children = [channel_param, color_param] item_param_4.addChildren(children) prog.roi_manager.settings.child('ROIs').addChild(item_param_4) prog._labels = [] with pytest.raises(ExpectedError): prog.add_lineout(4)
def mouseClickEvent(self, ev): if ev.button() == QtCore.Qt.LeftButton: print "left click ui image" print ev.pos() print "is delete = ", self.state_flag['isDelete'] ev.accept() if not (self.state_flag['isPaint'] or self.state_flag['isDelete'] ): ## if is painting, don't send click signal self.sigAddVertexRequested.emit((self, ev.pos())) elif self.state_flag['isDelete']: self.delete_curve(ev.pos().x(), ev.pos().y()) ## delete curve: build a rect around pos and judge interlect ROI.mouseClickEvent(self, ev)
def __init__(self, value, parent=None): self._center = (0, 0) self._radius = value.radius self._width = value.width self._angle = value.angle self._angle_std = value.angle_std AbstractROI.__init__(self, value) ROI.__init__(self, self._center, (self._radius, self._radius), movable=False, parent=parent) self.aspectLocked = True self.init_roi() self.set_radius(self._radius)
def __init__(self, *args, **kwargs): kwargs['viewBox'] = ScanViewBox() # Use custom pg.ViewBox subclass super().__init__(*args, **kwargs) self.getViewBox().sigMouseAreaSelected.connect(self.sigMouseAreaSelected) self._min_crosshair_factor = 0.02 self._crosshair_size = (0, 0) self._crosshair_range = None self.getViewBox().sigRangeChanged.connect(self._constraint_crosshair_size) self.crosshair = ROI((0, 0), (0, 0), pen={'color': '#00ff00', 'width': 1}) self.hline = InfiniteLine(pos=0, angle=0, movable=True, pen={'color': '#00ff00', 'width': 1}, hoverPen={'color': '#ffff00', 'width': 1}) self.vline = InfiniteLine(pos=0, angle=90, movable=True, pen={'color': '#00ff00', 'width': 1}, hoverPen={'color': '#ffff00', 'width': 1}) self.vline.sigDragged.connect(self._update_pos_from_line) self.hline.sigDragged.connect(self._update_pos_from_line) self.crosshair.sigRegionChanged.connect(self._update_pos_from_roi) self.sigCrosshairDraggedPosChanged.connect(self.sigCrosshairPosChanged)
def getArrayRegion(self, data, img, axes=(0, 1), **kwds): """ Return the result of ROI.getArrayRegion(), masked by the shape of the ROI. Values outside the ROI shape are set to 0. """ br = self.boundingRect() if br.width() > 1000: raise Exception() sliced = ROI.getArrayRegion(self, data, img, axes=axes, fromBoundingRect=True, **kwds) if kwds.get("returnMappedCoords"): sliced, mapped = sliced if img.axisOrder == "col-major": mask = self.renderShapeMask(sliced.shape[axes[0]], sliced.shape[axes[1]]) else: mask = self.renderShapeMask(sliced.shape[axes[1]], sliced.shape[axes[0]]) mask = mask.T # reshape mask to ensure it is applied to the correct data axes shape = [1] * data.ndim shape[axes[0]] = sliced.shape[axes[0]] shape[axes[1]] = sliced.shape[axes[1]] mask = mask.reshape(shape) if kwds.get("returnMappedCoords"): return sliced * mask, mapped else: return sliced * mask
def __init__(self, *args, **kwargs): kwargs['viewBox'] = ScanViewBox() # Use custom pg.ViewBox subclass super().__init__(*args, **kwargs) self.getViewBox().sigMouseAreaSelected.connect( self.sigMouseAreaSelected) self._min_crosshair_factor = 0.02 self._crosshair_size = (0, 0) self._crosshair_range = None self.getViewBox().sigRangeChanged.connect( self._constraint_crosshair_size) self.crosshair = ROI((0, 0), (0, 0), pen={ 'color': '#00ff00', 'width': 1 }) self.hline = InfiniteLine(pos=0, angle=0, movable=True, pen={ 'color': '#00ff00', 'width': 1 }, hoverPen={ 'color': '#ffff00', 'width': 1 }) self.vline = InfiniteLine(pos=0, angle=90, movable=True, pen={ 'color': '#00ff00', 'width': 1 }, hoverPen={ 'color': '#ffff00', 'width': 1 }) self.vline.sigDragged.connect(self._update_pos_from_line) self.hline.sigDragged.connect(self._update_pos_from_line) self.crosshair.sigRegionChanged.connect(self._update_pos_from_roi) self.sigCrosshairDraggedPosChanged.connect(self.sigCrosshairPosChanged)
def add_rect_roi(self): x = int(self.data.shape[0]/2) y = int(self.data.shape[1]/2) w = int(self.data.shape[0]/10) h = int(self.data.shape[1]/10) pen, sign = self._get_pen_and_sign() new = ROI([x,y], [w,h], removable=True, pen=pen) new.addScaleHandle([1, 0.5], [0, 0.5]) new.addScaleHandle([0, 0.5], [1, 0.5]) new.addScaleHandle([0.5, 0], [0.5, 1]) new.addScaleHandle([0.5, 1], [0.5, 0]) new.addScaleHandle([0, 1], [1, 0]) new.addScaleHandle([1, 0], [0, 1]) new.addScaleHandle([1, 1], [0, 0]) new.addScaleHandle([0, 0], [1, 1]) new.sigRegionChangeFinished.connect(self.update_mask) new.sigRemoveRequested.connect(self.remove_roi) self._append_roi(new, sign)
def _toggle_roi(self, checked): if not checked: try: self.imageView.view.removeItem(self.data['roi']) self.data['roi'].sigRegionChangeFinished.disconnect( self._update_images) self.data['roi'] = None except KeyError: pass else: try: image = self.data['image'] except KeyError: return roi = ROI((0, 0), (image.shape[0] // 2, image.shape[1] // 2), movable=False, rotatable=False, resizable=False) roi.addScaleHandle([0, 1], [0.5, 0.5]) roi.addScaleHandle([1, 0], [0.5, 0.5]) roi.addTranslateHandle([0.5, 0.5]) roi.sigRegionChangeFinished.connect(self._update_images) self.data['roi'] = roi self.imageView.view.addItem(roi) self._update_images()
def movePoint(self, handle, pos, modifiers=QtCore.Qt.KeyboardModifier(), finish=True, coords='parent'): ## overload ROI.py's movePoint(), reload image after scaling ## call ROI.movePoint() at the end #print "imageROI movePoint" newState = self.stateCopy() index = self.indexOfHandle(handle) h = self.handles[index] p0 = self.mapToParent(h['pos'] * self.state['size']) p1 = pg.Point(pos) if coords == 'parent': pass elif coords == 'scene': p1 = self.mapSceneToParent(p1) else: raise Exception( "New point location must be given in either 'parent' or 'scene' coordinates." ) if 'center' in h: c = h['center'] cs = c * self.state['size'] lp0 = self.mapFromParent(p0) - cs lp1 = self.mapFromParent(p1) - cs if h['type'] == 's': ## If a handle and its center have the same x or y value, we can't scale across that axis. if h['center'][0] == h['pos'][0]: lp1[0] = 0 if h['center'][1] == h['pos'][1]: lp1[1] = 0 ## snap if self.scaleSnap or (modifiers & QtCore.Qt.ControlModifier): lp1[0] = round(lp1[0] / self.snapSize) * self.snapSize lp1[1] = round(lp1[1] / self.snapSize) * self.snapSize ## preserve aspect ratio (this can override snapping) if h['lockAspect'] or (modifiers & QtCore.Qt.AltModifier): #arv = Point(self.preMoveState['size']) - lp1 = lp1.proj(lp0) ## determine scale factors and new size of ROI hs = h['pos'] - c if hs[0] == 0: hs[0] = 1 if hs[1] == 0: hs[1] = 1 newSize = lp1 / hs ## Perform some corrections and limit checks if newSize[0] == 0: newSize[0] = newState['size'][0] if newSize[1] == 0: newSize[1] = newState['size'][1] if not self.invertible: if newSize[0] < 0: newSize[0] = newState['size'][0] if newSize[1] < 0: newSize[1] = newState['size'][1] if self.aspectLocked: newSize[0] = newSize[1] minx = 50 miny = 50 self.do_scale(int(max(miny, newSize[1])), int(max(minx, newSize[0]))) if newSize[0] > minx and newSize[1] > minx: ROI.movePoint(self, handle, pos, modifiers, finish, coords) else: ROI.movePoint(self, handle, pos, modifiers, finish, coords)
def __init__(self, size): ROI.__init__(self, pos=[0, 0], size=size) self.addScaleHandle([1, 1], [0, 0])
class ScanPlotWidget(PlotWidget): """ Extend the PlotWidget Class with more functionality used for qudi scan images. Supported features: - draggable/static crosshair with optional range and size constraints. - zoom feature by rubberband selection - rubberband area selection This class depends on the ScanViewBox class defined further below. This class can be promoted in the Qt designer. """ sigMouseAreaSelected = QtCore.Signal( QtCore.QRectF) # mapped rectangle mouse cursor selection sigCrosshairPosChanged = QtCore.Signal(QtCore.QPointF) sigCrosshairDraggedPosChanged = QtCore.Signal(QtCore.QPointF) def __init__(self, *args, **kwargs): kwargs['viewBox'] = ScanViewBox() # Use custom pg.ViewBox subclass super().__init__(*args, **kwargs) self.getViewBox().sigMouseAreaSelected.connect( self.sigMouseAreaSelected) self._min_crosshair_factor = 0.02 self._crosshair_size = (0, 0) self._crosshair_range = None self.getViewBox().sigRangeChanged.connect( self._constraint_crosshair_size) self.crosshair = ROI((0, 0), (0, 0), pen={ 'color': '#00ff00', 'width': 1 }) self.hline = InfiniteLine(pos=0, angle=0, movable=True, pen={ 'color': '#00ff00', 'width': 1 }, hoverPen={ 'color': '#ffff00', 'width': 1 }) self.vline = InfiniteLine(pos=0, angle=90, movable=True, pen={ 'color': '#00ff00', 'width': 1 }, hoverPen={ 'color': '#ffff00', 'width': 1 }) self.vline.sigDragged.connect(self._update_pos_from_line) self.hline.sigDragged.connect(self._update_pos_from_line) self.crosshair.sigRegionChanged.connect(self._update_pos_from_roi) self.sigCrosshairDraggedPosChanged.connect(self.sigCrosshairPosChanged) @property def crosshair_enabled(self): items = self.items() return (self.vline in items) and (self.hline in items) and (self.crosshair in items) @property def crosshair_movable(self): return bool(self.crosshair.translatable) @property def crosshair_position(self): pos = self.vline.pos() pos[1] = self.hline.pos()[1] return tuple(pos) @property def crosshair_size(self): return tuple(self._crosshair_size) @property def crosshair_min_size_factor(self): return float(self._min_crosshair_factor) @property def crosshair_range(self): if self._crosshair_range is None: return None return tuple(self._crosshair_range) @property def selection_enabled(self): return bool(self.getViewBox().rectangle_selection) @property def zoom_by_selection_enabled(self): return bool(self.getViewBox().zoom_by_selection) def toggle_selection(self, enable): """ De-/Activate the rectangular rubber band selection tool. If active you can select a rectangular region within the ViewBox by dragging the mouse with the left button. Each selection rectangle in real-world data coordinates will be emitted by sigMouseAreaSelected. By using activate_zoom_by_selection you can optionally de-/activate zooming in on the selection. @param bool enable: Toggle selection on (True) or off (False) """ return self.getViewBox().toggle_selection(enable) def toggle_zoom_by_selection(self, enable): """ De-/Activate automatic zooming into a selection. See also: toggle_selection @param bool enable: Toggle zoom upon selection on (True) or off (False) """ return self.getViewBox().toggle_zoom_by_selection(enable) def _update_pos_from_line(self, obj): """ Called each time the position of the InfiniteLines has been changed by a user drag. Causes the crosshair rectangle to follow the lines. """ if obj not in (self.hline, self.vline): return pos = self.vline.pos() pos[1] = self.hline.pos()[1] size = self.crosshair.size() self.crosshair.blockSignals(True) self.crosshair.setPos((pos[0] - size[0] / 2, pos[1] - size[1] / 2)) self.crosshair.blockSignals(False) self.sigCrosshairDraggedPosChanged.emit(QtCore.QPointF(pos[0], pos[1])) return def _update_pos_from_roi(self, obj): """ Called each time the position of the rectangular ROI has been changed by a user drag. Causes the InfiniteLines to follow the ROI. """ if obj is not self.crosshair: return pos = self.crosshair.pos() size = self.crosshair.size() pos[0] += size[0] / 2 pos[1] += size[1] / 2 self.vline.setPos(pos[0]) self.hline.setPos(pos[1]) self.sigCrosshairDraggedPosChanged.emit(QtCore.QPointF(pos[0], pos[1])) return def toggle_crosshair(self, enable, movable=True): """ Disable/Enable the crosshair within the PlotWidget. Optionally also toggle if it can be dragged by the user. @param bool enable: enable crosshair (True), disable crosshair (False) @param bool movable: enable user drag (True), disable user drag (False) """ if not isinstance(enable, bool): raise TypeError('Positional argument "enable" must be bool type.') if not isinstance(movable, bool): raise TypeError('Optional argument "movable" must be bool type.') self.toggle_crosshair_movable(movable) is_enabled = self.crosshair_enabled if enable and not is_enabled: self.addItem(self.vline) self.addItem(self.hline) self.addItem(self.crosshair) elif not enable and is_enabled: self.removeItem(self.vline) self.removeItem(self.hline) self.removeItem(self.crosshair) return def toggle_crosshair_movable(self, enable): """ Toggle if the crosshair can be dragged by the user. @param bool enable: enable (True), disable (False) """ self.crosshair.translatable = bool(enable) self.vline.setMovable(enable) self.hline.setMovable(enable) return def set_crosshair_pos(self, pos): """ Set the crosshair center to the given coordinates. @param QPointF|float[2] pos: (x,y) position of the crosshair """ try: pos = tuple(pos) except TypeError: pos = (pos.x(), pos.y()) size = self.crosshair.size() self.crosshair.blockSignals(True) self.vline.blockSignals(True) self.hline.blockSignals(True) self.crosshair.setPos(pos[0] - size[0] / 2, pos[1] - size[1] / 2) self.vline.setPos(pos[0]) self.hline.setPos(pos[1]) self.crosshair.blockSignals(False) self.vline.blockSignals(False) self.hline.blockSignals(False) self.sigCrosshairPosChanged.emit(QtCore.QPointF(*pos)) return def set_crosshair_size(self, size, force_default=True): """ Set the default size of the crosshair rectangle (x, y) and update the display. @param QSize|float[2] size: the (x,y) size of the crosshair rectangle @param bool force_default: Set default crosshair size and enforce minimal size (True). Enforce displayed crosshair size while keeping default size untouched (False). """ try: size = tuple(size) except TypeError: size = (size.width(), size.height()) if force_default: if size[0] <= 0 and size[1] <= 0: self._crosshair_size = (0, 0) else: self._crosshair_size = size # Check if actually displayed size needs to be adjusted due to minimal size size = self._get_corrected_crosshair_size(size) pos = self.vline.pos() pos[1] = self.hline.pos()[1] - size[1] / 2 pos[0] -= size[0] / 2 if self._crosshair_range: crange = self._crosshair_range self.crosshair.maxBounds = QtCore.QRectF( crange[0][0] - size[0] / 2, crange[1][0] - size[1] / 2, crange[0][1] - crange[0][0] + size[0], crange[1][1] - crange[1][0] + size[1]) self.crosshair.blockSignals(True) self.crosshair.setSize(size) self.crosshair.setPos(pos) self.crosshair.blockSignals(False) return def set_crosshair_min_size_factor(self, factor): """ Sets the minimum crosshair size factor. This will determine the minimum size of the smallest edge of the crosshair center rectangle. This minimum size is calculated by taking the smallest visible axis of the ViewBox and multiplying it with the scale factor set by this method. The crosshair rectangle will be then scaled accordingly if the set crosshair size is smaller than this minimal size. @param float factor: The scale factor to set. If <= 0 no minimal crosshair size enforced. """ if factor <= 0: self._min_crosshair_factor = 0 elif factor <= 1: self._min_crosshair_factor = float(factor) else: raise ValueError('Crosshair min size factor must be a value <= 1.') return def set_crosshair_range(self, new_range): """ Sets a range boundary for the crosshair position. @param float[2][2] new_range: two min-max range value tuples (for x and y axis). If None set unlimited ranges. """ if new_range is None: self.vline.setBounds([None, None]) self.hline.setBounds([None, None]) self.crosshair.maxBounds = None else: self.vline.setBounds(new_range[0]) self.hline.setBounds(new_range[1]) size = self.crosshair.size() pos = self.crosshair_position self.crosshair.maxBounds = QtCore.QRectF( new_range[0][0] - size[0] / 2, new_range[1][0] - size[1] / 2, new_range[0][1] - new_range[0][0] + size[0], new_range[1][1] - new_range[1][0] + size[1]) self.crosshair.setPos(pos[0] - size[0] / 2, pos[1] - size[1] / 2) self._crosshair_range = new_range return def set_crosshair_pen(self, pen): """ Sets the pyqtgraph compatible pen to be used for drawing the crosshair lines. @param pen: pyqtgraph compatible pen to use """ self.crosshair.setPen(pen) self.vline.setPen(pen) self.hline.setPen(pen) return def _constraint_crosshair_size(self): if self._min_crosshair_factor == 0: return if self._crosshair_size[0] == 0 or self._crosshair_size[1] == 0: return corr_size = self._get_corrected_crosshair_size(self._crosshair_size) if corr_size != tuple(self.crosshair.size()): self.set_crosshair_size(corr_size, force_default=False) return def _get_corrected_crosshair_size(self, size): try: size = tuple(size) except TypeError: size = (size.width(), size.height()) min_size = min(size) if min_size == 0: return size vb_size = self.getViewBox().viewRect().size() short_index = int(vb_size.width() > vb_size.height()) min_vb_size = vb_size.width() if short_index == 0 else vb_size.height() min_vb_size *= self._min_crosshair_factor if min_size < min_vb_size: scale_factor = min_vb_size / min_size size = (size[0] * scale_factor, size[1] * scale_factor) return size
class ScanPlotWidget(PlotWidget): """ Extend the PlotWidget Class with more functionality used for qudi scan images. Supported features: - draggable/static crosshair with optional range and size constraints. - zoom feature by rubberband selection - rubberband area selection This class depends on the ScanViewBox class defined further below. This class can be promoted in the Qt designer. """ sigMouseAreaSelected = QtCore.Signal(QtCore.QRectF) # mapped rectangle mouse cursor selection sigCrosshairPosChanged = QtCore.Signal(QtCore.QPointF) sigCrosshairDraggedPosChanged = QtCore.Signal(QtCore.QPointF) def __init__(self, *args, **kwargs): kwargs['viewBox'] = ScanViewBox() # Use custom pg.ViewBox subclass super().__init__(*args, **kwargs) self.getViewBox().sigMouseAreaSelected.connect(self.sigMouseAreaSelected) self._min_crosshair_factor = 0.02 self._crosshair_size = (0, 0) self._crosshair_range = None self.getViewBox().sigRangeChanged.connect(self._constraint_crosshair_size) self.crosshair = ROI((0, 0), (0, 0), pen={'color': '#00ff00', 'width': 1}) self.hline = InfiniteLine(pos=0, angle=0, movable=True, pen={'color': '#00ff00', 'width': 1}, hoverPen={'color': '#ffff00', 'width': 1}) self.vline = InfiniteLine(pos=0, angle=90, movable=True, pen={'color': '#00ff00', 'width': 1}, hoverPen={'color': '#ffff00', 'width': 1}) self.vline.sigDragged.connect(self._update_pos_from_line) self.hline.sigDragged.connect(self._update_pos_from_line) self.crosshair.sigRegionChanged.connect(self._update_pos_from_roi) self.sigCrosshairDraggedPosChanged.connect(self.sigCrosshairPosChanged) @property def crosshair_enabled(self): items = self.items() return (self.vline in items) and (self.hline in items) and (self.crosshair in items) @property def crosshair_movable(self): return bool(self.crosshair.translatable) @property def crosshair_position(self): pos = self.vline.pos() pos[1] = self.hline.pos()[1] return tuple(pos) @property def crosshair_size(self): return tuple(self._crosshair_size) @property def crosshair_min_size_factor(self): return float(self._min_crosshair_factor) @property def crosshair_range(self): if self._crosshair_range is None: return None return tuple(self._crosshair_range) @property def selection_enabled(self): return bool(self.getViewBox().rectangle_selection) @property def zoom_by_selection_enabled(self): return bool(self.getViewBox().zoom_by_selection) def toggle_selection(self, enable): """ De-/Activate the rectangular rubber band selection tool. If active you can select a rectangular region within the ViewBox by dragging the mouse with the left button. Each selection rectangle in real-world data coordinates will be emitted by sigMouseAreaSelected. By using activate_zoom_by_selection you can optionally de-/activate zooming in on the selection. @param bool enable: Toggle selection on (True) or off (False) """ return self.getViewBox().toggle_selection(enable) def toggle_zoom_by_selection(self, enable): """ De-/Activate automatic zooming into a selection. See also: toggle_selection @param bool enable: Toggle zoom upon selection on (True) or off (False) """ return self.getViewBox().toggle_zoom_by_selection(enable) def _update_pos_from_line(self, obj): """ Called each time the position of the InfiniteLines has been changed by a user drag. Causes the crosshair rectangle to follow the lines. """ if obj not in (self.hline, self.vline): return pos = self.vline.pos() pos[1] = self.hline.pos()[1] size = self.crosshair.size() self.crosshair.blockSignals(True) self.crosshair.setPos((pos[0] - size[0] / 2, pos[1] - size[1] / 2)) self.crosshair.blockSignals(False) self.sigCrosshairDraggedPosChanged.emit(QtCore.QPointF(pos[0], pos[1])) return def _update_pos_from_roi(self, obj): """ Called each time the position of the rectangular ROI has been changed by a user drag. Causes the InfiniteLines to follow the ROI. """ if obj is not self.crosshair: return pos = self.crosshair.pos() size = self.crosshair.size() pos[0] += size[0] / 2 pos[1] += size[1] / 2 self.vline.setPos(pos[0]) self.hline.setPos(pos[1]) self.sigCrosshairDraggedPosChanged.emit(QtCore.QPointF(pos[0], pos[1])) return def toggle_crosshair(self, enable, movable=True): """ Disable/Enable the crosshair within the PlotWidget. Optionally also toggle if it can be dragged by the user. @param bool enable: enable crosshair (True), disable crosshair (False) @param bool movable: enable user drag (True), disable user drag (False) """ if not isinstance(enable, bool): raise TypeError('Positional argument "enable" must be bool type.') if not isinstance(movable, bool): raise TypeError('Optional argument "movable" must be bool type.') self.toggle_crosshair_movable(movable) is_enabled = self.crosshair_enabled if enable and not is_enabled: self.addItem(self.vline) self.addItem(self.hline) self.addItem(self.crosshair) elif not enable and is_enabled: self.removeItem(self.vline) self.removeItem(self.hline) self.removeItem(self.crosshair) return def toggle_crosshair_movable(self, enable): """ Toggle if the crosshair can be dragged by the user. @param bool enable: enable (True), disable (False) """ self.crosshair.translatable = bool(enable) self.vline.setMovable(enable) self.hline.setMovable(enable) return def set_crosshair_pos(self, pos): """ Set the crosshair center to the given coordinates. @param QPointF|float[2] pos: (x,y) position of the crosshair """ try: pos = tuple(pos) except TypeError: pos = (pos.x(), pos.y()) size = self.crosshair.size() self.crosshair.blockSignals(True) self.vline.blockSignals(True) self.hline.blockSignals(True) self.crosshair.setPos(pos[0] - size[0] / 2, pos[1] - size[1] / 2) self.vline.setPos(pos[0]) self.hline.setPos(pos[1]) self.crosshair.blockSignals(False) self.vline.blockSignals(False) self.hline.blockSignals(False) self.sigCrosshairPosChanged.emit(QtCore.QPointF(*pos)) return def set_crosshair_size(self, size, force_default=True): """ Set the default size of the crosshair rectangle (x, y) and update the display. @param QSize|float[2] size: the (x,y) size of the crosshair rectangle @param bool force_default: Set default crosshair size and enforce minimal size (True). Enforce displayed crosshair size while keeping default size untouched (False). """ try: size = tuple(size) except TypeError: size = (size.width(), size.height()) if force_default: if size[0] <= 0 and size[1] <= 0: self._crosshair_size = (0, 0) else: self._crosshair_size = size # Check if actually displayed size needs to be adjusted due to minimal size size = self._get_corrected_crosshair_size(size) pos = self.vline.pos() pos[1] = self.hline.pos()[1] - size[1] / 2 pos[0] -= size[0] / 2 if self._crosshair_range: crange = self._crosshair_range self.crosshair.maxBounds = QtCore.QRectF(crange[0][0] - size[0] / 2, crange[1][0] - size[1] / 2, crange[0][1] - crange[0][0] + size[0], crange[1][1] - crange[1][0] + size[1]) self.crosshair.blockSignals(True) self.crosshair.setSize(size) self.crosshair.setPos(pos) self.crosshair.blockSignals(False) return def set_crosshair_min_size_factor(self, factor): """ Sets the minimum crosshair size factor. This will determine the minimum size of the smallest edge of the crosshair center rectangle. This minimum size is calculated by taking the smallest visible axis of the ViewBox and multiplying it with the scale factor set by this method. The crosshair rectangle will be then scaled accordingly if the set crosshair size is smaller than this minimal size. @param float factor: The scale factor to set. If <= 0 no minimal crosshair size enforced. """ if factor <= 0: self._min_crosshair_factor = 0 elif factor <= 1: self._min_crosshair_factor = float(factor) else: raise ValueError('Crosshair min size factor must be a value <= 1.') return def set_crosshair_range(self, new_range): """ Sets a range boundary for the crosshair position. @param float[2][2] new_range: two min-max range value tuples (for x and y axis). If None set unlimited ranges. """ if new_range is None: self.vline.setBounds([None, None]) self.hline.setBounds([None, None]) self.crosshair.maxBounds = None else: self.vline.setBounds(new_range[0]) self.hline.setBounds(new_range[1]) size = self.crosshair.size() pos = self.crosshair_position self.crosshair.maxBounds = QtCore.QRectF(new_range[0][0] - size[0] / 2, new_range[1][0] - size[1] / 2, new_range[0][1] - new_range[0][0] + size[0], new_range[1][1] - new_range[1][0] + size[1]) self.crosshair.setPos(pos[0] - size[0] / 2, pos[1] - size[1] / 2) self._crosshair_range = new_range return def set_crosshair_pen(self, pen): """ Sets the pyqtgraph compatible pen to be used for drawing the crosshair lines. @param pen: pyqtgraph compatible pen to use """ self.crosshair.setPen(pen) self.vline.setPen(pen) self.hline.setPen(pen) return def _constraint_crosshair_size(self): if self._min_crosshair_factor == 0: return if self._crosshair_size[0] == 0 or self._crosshair_size[1] == 0: return corr_size = self._get_corrected_crosshair_size(self._crosshair_size) if corr_size != tuple(self.crosshair.size()): self.set_crosshair_size(corr_size, force_default=False) return def _get_corrected_crosshair_size(self, size): try: size = tuple(size) except TypeError: size = (size.width(), size.height()) min_size = min(size) if min_size == 0: return size vb_size = self.getViewBox().viewRect().size() short_index = int(vb_size.width() > vb_size.height()) min_vb_size = vb_size.width() if short_index == 0 else vb_size.height() min_vb_size *= self._min_crosshair_factor if min_size < min_vb_size: scale_factor = min_vb_size / min_size size = (size[0] * scale_factor, size[1] * scale_factor) return size
def weightClick(self, event): '''Clicked on weight matrix to select neurons ''' event.accept() mousePoint = event.pos() print('mousepoint: ', int(mousePoint.x()), int(mousePoint.y())) if self.last_x is None: self.last_x = int(mousePoint.x()) self.last_y = int(mousePoint.y()) pen = pyqtgraph.mkPen(width=2, color='r') pen2 = pyqtgraph.mkPen(width=2, color='r') loc, lines, strengths = self.visual.selectWeights( int(mousePoint.x()), int(mousePoint.y())) if self.flagW: # need to draw, currently off self.rect = ROI(pos=(int(mousePoint.x()), 0), size=(1, 10), pen=pen, movable=False) self.rect2 = ROI(pos=(0, int(mousePoint.y())), size=(10, 1), pen=pen2, movable=False) self.rawplot_3.getView().addItem(self.rect) self.rawplot_3.getView().addItem(self.rect2) pen = pyqtgraph.mkPen(width=1, color='g') self.green_circ = CircleROI(pos=np.array([loc[0][0], loc[0][1]]) - 5, size=10, movable=False, pen=pen) self.rawplot_2.getView().addItem(self.green_circ) self.lines = [] self.pens = [] colors = ['g'] * 9 + ['r'] * 9 for i in range(18): n = lines[i] if strengths[i] > 1e-6: if strengths[i] > 1e-4: self.pens.append( pyqtgraph.mkPen(width=2, color=colors[i])) else: self.pens.append( pyqtgraph.mkPen(width=1, color=colors[i])) self.lines.append( LineSegmentROI(positions=([n[0], n[2]], [n[1], n[3]]), handles=(None, None), pen=self.pens[i], movable=False)) self.rawplot_2.getView().addItem(self.lines[i]) else: self.pens.append(pyqtgraph.mkPen(width=1, color=colors[i])) self.lines.append( LineSegmentROI(positions=([n[0], n[0]], [n[0], n[0]]), handles=(None, None), pen=self.pens[i], movable=False)) self.rawplot_2.getView().addItem(self.lines[i]) self.last_x = int(mousePoint.x()) self.last_y = int(mousePoint.y()) self.flagW = False else: self.rawplot_3.getView().removeItem(self.rect) self.rawplot_3.getView().removeItem(self.rect2) self.rawplot_2.getView().removeItem(self.green_circ) for i in range(18): self.rawplot_2.getView().removeItem(self.lines[i]) if self.last_x != int(mousePoint.x()) or self.last_y != int( mousePoint.y()): self.rect = ROI(pos=(int(mousePoint.x()), 0), size=(1, 10), pen=pen, movable=False) self.rect2 = ROI(pos=(0, int(mousePoint.y())), size=(10, 1), pen=pen2, movable=False) self.rawplot_3.getView().addItem(self.rect) self.rawplot_3.getView().addItem(self.rect2) pen = pyqtgraph.mkPen(width=1, color='g') self.green_circ = CircleROI( pos=np.array([loc[0][0], loc[0][1]]) - 5, size=10, movable=False, pen=pen) self.rawplot_2.getView().addItem(self.green_circ) colors = ['g'] * 9 + ['r'] * 9 for i in range(18): n = lines[i] if strengths[i] > 1e-6: if strengths[i] > 1e-4: self.pens[i] = (pyqtgraph.mkPen(width=2, color=colors[i])) else: self.pens[i] = (pyqtgraph.mkPen(width=1, color=colors[i])) self.lines[i] = (LineSegmentROI( positions=([n[0], n[2]], [n[1], n[3]]), handles=(None, None), pen=self.pens[i], movable=False)) else: self.pens[i] = (pyqtgraph.mkPen(width=1, color=colors[i])) self.lines[i] = (LineSegmentROI( positions=([n[0], n[0]], [n[0], n[0]]), handles=(None, None), pen=self.pens[i], movable=False)) self.rawplot_2.getView().addItem(self.lines[i]) self.last_x = int(mousePoint.x()) self.last_y = int(mousePoint.y()) else: self.flagW = True
def __new__(cls, *args, **kwargs): BetterROI.roi_count += 1 instance = ROI.__new__(cls, *args, **kwargs) instance.index = cls.roi_count return instance
def __init__(self, pos, size, centered=False, simple=False, sideScalers=False, movable=True, rotatable=True, sendBack=True, alpha=False, **args): self.sendBack = sendBack self.image_array = None self.image_item = None ## created for histo self.original_array = None self.current_array = None self.current_curve_array = None ## created for sample photo self.heatmap_original_arrays = None ## dictionary self.heatmap_current_array = None self.curve_color = QtGui.QColor(79, 106, 25) self.curve_tense = 0.4 self.curve_size = 5 self.prev_alpha = 255 self.state_flag = {} self.state_flag['isDrag'] = False self.state_flag['isPaint'] = False self.state_flag['isMoving'] = False self.state_flag['isHeatmap'] = False self.state_flag['isDelete'] = False self.widgets = {} self.curves = [] ## storing curveObject self.track = [] if simple: print "simple image roi" ROI.__init__(self, pos, size, removable=False, movable=movable, **args) return ROI.__init__(self, pos, size, movable=movable, **args) if centered: center = [0.5, 0.5] else: center = [0, 0] if rotatable: self.addRotateHandle([1, 0], [0.5, 0.5]) self.addRotateHandle([0, 1], [0.5, 0.5]) self.addScaleHandle([1, 1], [0.5, 0.5]) #self.addScaleRotateHandle([1,0], [1,0.5]) #self.addScaleRotateHandle([0,1], [0,0.5]) self.get_menu() if alpha: self.add_alpha_slider()
def mouseDragEvent(self, ev): if ev.button() != QtCore.Qt.LeftButton: ev.ignore() return else: self.state_flag['isDrag'] = True print "start drag", self.state_flag['isDrag'] if ev.isStart(): self.track = [] if self.state_flag['isPaint']: if ev.isStart(): print "start" self.state_flag['isMoving'] = True ev.accept() print ev.pos() try: self.image_item.draw_at(ev.pos()) self.track.append((ev.pos().x(), ev.pos().y())) #self.sigDragTriggered.emit(pg.Point(ev.pos().y(),-ev.pos().x())) #self.sigDragTriggered.emit(ev.pos()) except ValueError as e: pass else: ROI.mouseDragEvent(self, ev) if ev.isFinish(): print "finish drag" self.state_flag['isDrag'] = False if self.state_flag['isMoving'] == True: print "end" self.track.append((ev.pos().x(), ev.pos().y())) try: self.image_item.draw_at(ev.pos()) except ValueError as e: pass self.image_item.setImage(np.array(self.original_array, copy=True), levels=[0, 255]) #self.image_item.setImage(self.image_array) #self.sigDragTriggered.emit(pg.Point(ev.pos().y(),-ev.pos().x())) #self.sigDragTriggered.emit(ev.pos()) #self.sigTrackCompleted.emit(self) slack = True self.track = [x[0] for x in groupby(self.track)] segments = [] for i in range(len(self.track) - 1): segments.append((self.track[i], self.track[i + 1])) try: isect = poly_point_isect.isect_segments( tuple(segments), True) except AssertionError as e: ## there are some problems with the method isect = [] isect.sort(key=lambda k: k[1][1]) print "track has", len(self.track), "points" ## allow drawing unclosed curve, so the instersection judgement is not needed ''' if len(isect) > 0: first_isect = isect[0] print "first intersect: ", first_isect self.track = self.track[first_isect[1][0]+1 : first_isect[1][1]-1] + [first_isect[0]] print len(self.track) #self.curves.append(self.make_curve([[30,30],[50,5],[300,30],[400,100],[300,300],[200,400],[30,300]], 0.5, 1)) #print self.track #self.track = [(124.79421229324313, 300.80197629011207), (134.39463578398144, 267.200494072528), (148.7952710200889, 233.59901185494397), (161.59583567440663, 203.1976708009393), (172.79632974693467, 187.1969649830421), (179.19661207409354, 179.19661207409354), (193.597247310201, 164.79597683798607), (217.59830603704674, 139.19484752935062), (241.5993647638925, 123.19414171145343), (264.00035290894857, 113.59371822071512), (288.00141163579434, 103.99329472997684), (308.8023291990607, 97.59301240281798), (328.0031761805373, 94.39287123923853), (350.40416432559334, 94.39287123923853), (372.8051524706494, 103.99329472997684), (385.6057171249671, 108.79350647534599), (393.60607003391567, 115.19378880250486), (406.40663468823345, 123.19414171145343), (414.406987597182, 131.19449462040205), (420.8072699243409, 143.99505927471978), (427.20755225149975, 155.19555334724777), (433.60783457865864, 171.19625916514497), (440.0081169058175, 191.99717672841126), (444.8083286511866, 214.39816487346735), (446.40839923297636, 223.99858836420566), (444.8083286511866, 243.19943534568222), (438.4080463240278, 265.6004234907383), (432.0077639968689, 283.2011998904252), (427.20755225149975, 291.20155279937376), (424.00741108792033, 292.8016233811635), (420.8072699243409, 296.0017645447429), (419.2071993425512, 297.60183512653265), (409.60677585181287, 304.0021174536915), (401.6064229428643, 308.80232919906064), (395.2061406157054, 312.0024703626401), (380.80550537959795, 320.0028232715887), (363.20472897991107, 328.00317618053725), (345.6039525802242, 336.0035290894859), (329.603246762327, 345.60395258022413), (323.20296443516816, 348.8040937438036), (315.2026115262195, 353.60430548917276), (312.0024703626401, 353.60430548917276), (304.00211745369154, 358.4045172345419), (296.0017645447429, 361.60465839812133), (284.8012704722149, 368.0049407252802), (278.40098814505603, 371.2050818888596), (268.8005646543177, 372.8051524706493), (262.4002823271589, 372.8051524706493), (254.39992941821023, 372.8051524706493), (239.99929418210277, 372.8051524706493), (220.7984472006262, 366.4048701434905), (209.59795312809817, 363.20472897991107), (206.39781196451875, 361.60465839812133), (201.5976002191496, 356.8044466527522), (193.597247310201, 352.004234907383), (185.5968944012524, 344.00388199843445), (180.79668265588325, 337.60359967127556), (172.79632974693467, 326.4031055987476), (164.79597683798607, 318.40275268979894), (163.19590625619637, 316.80268210800926), (153.59548276545806, 305.6021880354812), (147.1952004382992, 294.4016939629532), (140.7949181111403, 281.60112930863545), (132.358182316249, 274.328081209592)] self.curves.append(CurveObject(path=self.make_curve(self.track, self.curve_tense, 8), pen=QtGui.QPen(self.curve_color, self.curve_size, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap, QtCore.Qt.MiterJoin))) ''' #self.curves.append(CurveObject(path=self.make_curve(self.track, self.curve_tense, step='auto'), pen=QtGui.QPen(self.curve_color, self.curve_size, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))) self.curves.append( CurveObject( self.make_curve(self.track, self.curve_tense, step='auto'), QtGui.QPen(self.curve_color, self.curve_size, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin), self.track, self.curve_tense, self.curve_color, self.curve_size, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) self.update_curve() self.state_flag['isMoving'] = False