def __init__(self, ax, maxdist=10, on_move=None, on_release=None, on_enter=None, useblit=True, line_props=None): super(RectangleSelection, self).__init__(ax, on_move=on_move, on_release=on_release, on_enter=on_enter, useblit=useblit, line_props=line_props) self.anchor = None self.origin = None self.origin_pix = None self.shape = 'rectangle' self.maxdist = maxdist self._extents = [0, 0, 0, 0] self.active_handle = None self.modifiers = set() x = (0, 0, 0, 0) y = (0, 0, 0, 0) props = dict() self._center_handle = ToolHandles(ax, x, y, marker='s', marker_props=props) self._corner_handles = ToolHandles(ax, x, y, marker_props=props) self._edge_handles = ToolHandles(ax, x, y, marker='s', marker_props=props) self._corner_order = ['NW', 'NE', 'SE', 'SW'] self._edge_order = ['W', 'N', 'E', 'S'] self._artists.extend([self._center_handle.artist, self._corner_handles.artist, self._edge_handles.artist]) self.connect_event('key_press_event', self.on_key) self.connect_event('key_release_event', self.off_key)
def __init__(self, ax, on_move=None, on_release=None, on_enter=None, maxdist=10, line_props=None): super(LineTool, self).__init__(ax, on_move=on_move, on_enter=on_enter, on_release=on_release) props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt') props.update(line_props if line_props is not None else {}) self.linewidth = props['linewidth'] self.maxdist = maxdist self._active_pt = None x = (0, 0) y = (0, 0) self._end_pts = np.transpose([x, y]) self._line = lines.Line2D(x, y, visible=False, animated=True, **props) ax.add_line(self._line) self._handles = ToolHandles(ax, x, y) self._handles.set_visible(False) self._artists = [self._line, self._handles.artist] if on_enter is None: def on_enter(pts): x, y = np.transpose(pts) print("length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2)) self.callback_on_enter = on_enter self.connect_event('button_press_event', self.on_mouse_press) self.connect_event('button_release_event', self.on_mouse_release) self.connect_event('motion_notify_event', self.on_move)
def __init__(self, ax, on_move=None, on_release=None, on_enter=None, maxdist=10, line_props=None): super(LineTool, self).__init__(ax, on_move=on_move, on_enter=on_enter, on_release=on_release) props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt') props.update(line_props if line_props is not None else {}) self.linewidth = props['linewidth'] self.maxdist = maxdist self._active_pt = None x = (0, 0) y = (0, 0) self._end_pts = np.transpose([x, y]) self._line = lines.Line2D(x, y, visible=False, animated=True, **props) ax.add_line(self._line) self._handles = ToolHandles(ax, x, y) self._handles.set_visible(False) self._artists = [self._line, self._handles.artist] if on_enter is None: def on_enter(pts): x, y = np.transpose(pts) print "length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2) self.callback_on_enter = on_enter self.connect_event('button_press_event', self.on_mouse_press) self.connect_event('button_release_event', self.on_mouse_release) self.connect_event('motion_notify_event', self.on_move)
def __init__(self, ax, on_move=None, on_enter=None, on_release=None, useblit=True, maxdist=10, line_props=None): super(LineTool, self).__init__(ax, on_move=on_move, on_enter=on_enter, on_release=on_release, useblit=useblit) if not line_props: line_props = dict() self.linewidth = line_props.get('linewidth', 1) self.shape = 'line' self.maxdist = maxdist self._active_pt = None x = (0, 0) y = (0, 0) self._end_pts = np.transpose([x, y]) self._handles = ToolHandles(ax, x, y) self._handles.set_visible(False) self._artists = [self._line, self._handles.artist] if on_enter is None: def on_enter(pts): x, y = np.transpose(pts) print("length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2)) self.callback_on_enter = on_enter
class LineTool(CanvasToolBase): """Widget for line selection in a plot. Parameters ---------- ax : :class:`matplotlib.axes.Axes` Matplotlib axes where tool is displayed. on_move : function Function called whenever a control handle is moved. This function must accept the end points of line as the only argument. on_release : function Function called whenever the control handle is released. on_enter : function Function called whenever the "enter" key is pressed. maxdist : float Maximum pixel distance allowed when selecting control handle. line_props : dict Properties for :class:`matplotlib.lines.Line2D`. Attributes ---------- end_points : 2D array End points of line ((x1, y1), (x2, y2)). """ def __init__(self, ax, on_move=None, on_release=None, on_enter=None, maxdist=10, line_props=None): super(LineTool, self).__init__(ax, on_move=on_move, on_enter=on_enter, on_release=on_release) props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt') props.update(line_props if line_props is not None else {}) self.linewidth = props['linewidth'] self.maxdist = maxdist self._active_pt = None x = (0, 0) y = (0, 0) self._end_pts = np.transpose([x, y]) self._line = lines.Line2D(x, y, visible=False, animated=True, **props) ax.add_line(self._line) self._handles = ToolHandles(ax, x, y) self._handles.set_visible(False) self._artists = [self._line, self._handles.artist] if on_enter is None: def on_enter(pts): x, y = np.transpose(pts) print("length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2)) self.callback_on_enter = on_enter self.connect_event('button_press_event', self.on_mouse_press) self.connect_event('button_release_event', self.on_mouse_release) self.connect_event('motion_notify_event', self.on_move) @property def end_points(self): return self._end_pts @end_points.setter def end_points(self, pts): self._end_pts = np.asarray(pts) self._line.set_data(np.transpose(pts)) self._handles.set_data(np.transpose(pts)) self._line.set_linewidth(self.linewidth) self.set_visible(True) self.redraw() def on_mouse_press(self, event): if event.button != 1 or not self.ax.in_axes(event): return self.set_visible(True) idx, px_dist = self._handles.closest(event.x, event.y) if px_dist < self.maxdist: self._active_pt = idx else: self._active_pt = 0 x, y = event.xdata, event.ydata self._end_pts = np.array([[x, y], [x, y]]) def on_mouse_release(self, event): if event.button != 1: return self._active_pt = None self.callback_on_release(self.geometry) def on_move(self, event): if event.button != 1 or self._active_pt is None: return if not self.ax.in_axes(event): return self.update(event.xdata, event.ydata) self.callback_on_move(self.geometry) def update(self, x=None, y=None): if x is not None: self._end_pts[self._active_pt, :] = x, y self.end_points = self._end_pts @property def geometry(self): return self.end_points
class LineTool(ROIToolBase): """Widget for line selection in a plot. Parameters ---------- ax : :class:`matplotlib.axes.Axes` Matplotlib axes where tool is displayed. on_move : function Function called whenever a control handle is moved. This function must accept the end points of line as the only argument. on_release : function Function called whenever the control handle is released. on_enter : function Function called whenever the "enter" key is pressed. maxdist : float Maximum pixel distance allowed when selecting control handle. line_props : dict Properties for :class:`matplotlib.lines.Line2D`. Attributes ---------- end_points : 2D array End points of line ((x1, y1), (x2, y2)). """ def __init__(self, ax, on_move=None, on_enter=None, on_release=None, useblit=True, maxdist=10, line_props=None): super(LineTool, self).__init__(ax, on_move=on_move, on_enter=on_enter, on_release=on_release, useblit=useblit) if not line_props: line_props = dict() self.linewidth = line_props.get('linewidth', 1) self.shape = 'line' self.maxdist = maxdist self._active_pt = None x = (0, 0) y = (0, 0) self._end_pts = np.transpose([x, y]) self._handles = ToolHandles(ax, x, y) self._handles.set_visible(False) self._artists = [self._line, self._handles.artist] if on_enter is None: def on_enter(pts): x, y = np.transpose(pts) print("length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2)) self.callback_on_enter = on_enter @property def end_points(self): return self._end_pts @end_points.setter def end_points(self, pts): self._end_pts = pts = np.asarray(pts) self._center = (pts[1] + pts[0]) / 2. handle_pts = np.vstack((pts[0], self._center, pts[1])).T self._line.set_data(np.transpose(pts)) self._handles.set_data(handle_pts) self._line.set_linewidth(self.linewidth) self.set_visible(True) self.publish_roi() self.redraw() def on_mouse_press(self, event): if event.button != 1 or not self.ax.in_axes(event): return self.start(event) self.set_visible(True) idx, px_dist = self._handles.closest(event.x, event.y) if px_dist < self.maxdist: self._active_pt = idx else: self._active_pt = 0 x, y = event.xdata, event.ydata self._end_pts = np.array([[x, y], [x, y]]) def on_mouse_release(self, event): if event.button != 1 or not self.active: return self._active_pt = None self.finalize() def on_move(self, event): if event.button != 1 or self._active_pt is None: return if not self.ax.in_axes(event): return self.update(event.xdata, event.ydata) def update(self, x=None, y=None): if x is not None: # check for center if self._active_pt == 1: xc, yc = self._center xo, yo = x - xc, y - yc self._end_pts += [xo, yo] elif self._active_pt == 0: self._end_pts[0, :] = x, y else: self._end_pts[1, :] = x, y self.end_points = self._end_pts @property def geometry(self): pt1, pt2 = self.end_points if pt1[0] > pt2[0]: pt1, pt2 = pt2, pt1 return np.array((pt1, pt2)) @geometry.setter def geometry(self, points, linewidth=None): self.end_points = points if linewidth: self.linewidth = linewidth def activate(self): self.active = True if hasattr(self, '_prev_line'): self._prev_line.set_data([], []) self.redraw() @property def data(self): if self.ax.images: return profile_line(self.source_data, self.end_points, self.linewidth)
class RectangleSelection(PolygonToolBase): def __init__(self, ax, maxdist=10, on_move=None, on_release=None, on_enter=None, useblit=True, line_props=None): super(RectangleSelection, self).__init__(ax, on_move=on_move, on_release=on_release, on_enter=on_enter, useblit=useblit, line_props=line_props) self.anchor = None self.origin = None self.origin_pix = None self.shape = 'rectangle' self.maxdist = maxdist self._extents = [0, 0, 0, 0] self.active_handle = None self.modifiers = set() x = (0, 0, 0, 0) y = (0, 0, 0, 0) props = dict() self._center_handle = ToolHandles(ax, x, y, marker='s', marker_props=props) self._corner_handles = ToolHandles(ax, x, y, marker_props=props) self._edge_handles = ToolHandles(ax, x, y, marker='s', marker_props=props) self._corner_order = ['NW', 'NE', 'SE', 'SW'] self._edge_order = ['W', 'N', 'E', 'S'] self._artists.extend([self._center_handle.artist, self._corner_handles.artist, self._edge_handles.artist]) self.connect_event('key_press_event', self.on_key) self.connect_event('key_release_event', self.off_key) def start(self, event): super(RectangleSelection, self).start(event) self.anchor = None self.origin = self.verts[0] self.origin_pix = (event.x, event.y) if not self.active_handle == 'C': self.extents = [0, 0, 0, 0] self._line.set_visible(True) def on_key(self, event): if self._busy: self.modifiers.add(event.key) def off_key(self, event): if self._busy and event.key in self.modifiers: self.modifiers.remove(event.key) def on_move(self, event): if not self._busy or not event.xdata: return if self.active_handle and not self.active_handle == 'C': x1, x2, y1, y2 = self._extents_on_press if self.active_handle in ['E', 'W'] + self._corner_order: x2 = event.xdata if self.active_handle in ['N', 'S'] + self._corner_order: y2 = event.ydata self.modifiers = set() self.extents = np.array([x1, x2, y1, y2]) elif ' ' in self.modifiers or self.active_handle == 'C': # move command if not self.anchor: self.anchor = event.xdata, event.ydata else: dx = event.xdata - self.anchor[0] dy = event.ydata - self.anchor[1] x1, x2, y1, y2 = self.extents x1 += dx x2 += dx y1 += dy y2 += dy self.extents = np.array([x1, x2, y1, y2]) self.origin = [self.origin[0] + dx, self.origin[1] + dy] self.anchor = event.xdata, event.ydata else: if not self.origin: self.start() dx = (event.xdata - self.origin[0]) / 2. dy = (event.ydata - self.origin[1]) / 2. center = np.array(self.origin) if 'shift' in self.modifiers or 'ctrl+shift' in self.modifiers: # square command dx_pix = abs(event.x - self.origin_pix[0]) dy_pix = abs(event.y - self.origin_pix[1]) if not dx_pix: return maxd = max(abs(dx_pix), abs(dy_pix)) if abs(dx_pix) < maxd: dx *= maxd / abs(dx_pix) if abs(dy_pix) < maxd: dy *= maxd / abs(dy_pix) if 'control' in self.modifiers or 'ctrl+shift' in self.modifiers: # from center dx *= 2 dy *= 2 else: # from corner center += np.array([dx, dy]) self.extents = np.array([center[0] - dx, center[0] + dx, center[1] - dy, center[1] + dy]) def set_verts(self): x1, x2, y1, y2 = self.extents self.verts = [[x1, y1], [x1, y2], [x2, y2], [x2, y1], [x1, y1]] self.update() def on_mouse_release(self, event): self._extents_on_press = None self.modifiers = set() self.finalize() def on_mouse_press(self, event): self._set_active_handle(event) self.start(event) def _set_active_handle(self, event): """Set active handle based on the location of the mouse event""" # Note: event.xdata/ydata in data coordinates, event.x/y in pixels c_idx, c_dist = self._corner_handles.closest(event.x, event.y) e_idx, e_dist = self._edge_handles.closest(event.x, event.y) m_idx, m_dist = self._center_handle.closest(event.x, event.y) # Set active handle as closest handle, if mouse click is close enough. if c_dist > self.maxdist and e_dist > self.maxdist: self.active_handle = None elif c_dist < e_dist: self.active_handle = self._corner_order[c_idx] else: self.active_handle = self._edge_order[e_idx] if not self.active_handle and m_dist < self.maxdist: self.active_handle = 'C' # Save coordinates of rectangle at the start of handle movement. x1, x2, y1, y2 = self.extents # Switch variables so that only x2 and/or y2 are updated on move. if self.active_handle in ['W', 'SW', 'NW']: x1, x2 = x2, event.xdata if self.active_handle in ['N', 'NW', 'NE']: y1, y2 = y2, event.ydata self._extents_on_press = x1, x2, y1, y2 @property def _rect_bbox(self): if not len(self.extents): return 0, 0, 0, 0 x1, x2, y1, y2 = np.array(self.extents).tolist() x0, x1 = np.sort((x1, x2)) y0, y1 = np.sort((y1, y2)) width = x1 - x0 height = y1 - y0 return x0, y0, width, height @property def corners(self): """Corners of rectangle from lower left, moving clockwise.""" x0, y0, width, height = self._rect_bbox xc = x0, x0 + width, x0 + width, x0 yc = y0, y0, y0 + height, y0 + height return xc, yc @property def edge_centers(self): """Midpoint of rectangle edges from left, moving clockwise.""" x0, y0, width, height = self._rect_bbox w = width / 2. h = height / 2. xe = x0, x0 + w, x0 + width, x0 + w ye = y0 + h, y0, y0 + h, y0 + height return xe, ye @property def extents(self): return self._extents @extents.setter def extents(self, extents): x1, x2, y1, y2 = extents xmin, xmax = np.sort([x1, x2]) ymin, ymax = np.sort([y1, y2]) self._extents = np.array([xmin, xmax, ymin, ymax]) # Update displayed handles self._center_handle.set_data((x2 + x1) / 2, (y2 + y1) / 2) self._corner_handles.set_data(*self.corners) self._edge_handles.set_data(*self.edge_centers) self._center_handle.set_visible(True) self._corner_handles.set_visible(True) self._edge_handles.set_visible(True) self.set_verts() @property def geometry(self): return self._rect_bbox @geometry.setter def geometry(self, x0, y0, width, height): self.extents = [x0, x0 + width, y0, y0 + height]
class LineTool(CanvasToolBase): """Widget for line selection in a plot. Parameters ---------- ax : :class:`matplotlib.axes.Axes` Matplotlib axes where tool is displayed. on_move : function Function called whenever a control handle is moved. This function must accept the end points of line as the only argument. on_release : function Function called whenever the control handle is released. on_enter : function Function called whenever the "enter" key is pressed. maxdist : float Maximum pixel distance allowed when selecting control handle. line_props : dict Properties for :class:`matplotlib.lines.Line2D`. Attributes ---------- end_points : 2D array End points of line ((x1, y1), (x2, y2)). """ def __init__(self, ax, on_move=None, on_release=None, on_enter=None, maxdist=10, line_props=None): super(LineTool, self).__init__(ax, on_move=on_move, on_enter=on_enter, on_release=on_release) props = dict(color='r', linewidth=1, alpha=0.4, solid_capstyle='butt') props.update(line_props if line_props is not None else {}) self.linewidth = props['linewidth'] self.maxdist = maxdist self._active_pt = None x = (0, 0) y = (0, 0) self._end_pts = np.transpose([x, y]) self._line = lines.Line2D(x, y, visible=False, animated=True, **props) ax.add_line(self._line) self._handles = ToolHandles(ax, x, y) self._handles.set_visible(False) self._artists = [self._line, self._handles.artist] if on_enter is None: def on_enter(pts): x, y = np.transpose(pts) print "length = %0.2f" % np.sqrt(np.diff(x)**2 + np.diff(y)**2) self.callback_on_enter = on_enter self.connect_event('button_press_event', self.on_mouse_press) self.connect_event('button_release_event', self.on_mouse_release) self.connect_event('motion_notify_event', self.on_move) @property def end_points(self): return self._end_pts @end_points.setter def end_points(self, pts): self._end_pts = np.asarray(pts) self._line.set_data(np.transpose(pts)) self._handles.set_data(np.transpose(pts)) self._line.set_linewidth(self.linewidth) self.set_visible(True) self.redraw() def on_mouse_press(self, event): if event.button != 1 or not self.ax.in_axes(event): return self.set_visible(True) idx, px_dist = self._handles.closest(event.x, event.y) if px_dist < self.maxdist: self._active_pt = idx else: self._active_pt = 0 x, y = event.xdata, event.ydata self._end_pts = np.array([[x, y], [x, y]]) def on_mouse_release(self, event): if event.button != 1: return self._active_pt = None self.callback_on_release(self.geometry) def on_move(self, event): if event.button != 1 or self._active_pt is None: return if not self.ax.in_axes(event): return self.update(event.xdata, event.ydata) self.callback_on_move(self.geometry) def update(self, x=None, y=None): if x is not None: self._end_pts[self._active_pt, :] = x, y self.end_points = self._end_pts @property def geometry(self): return self.end_points