def set_figure(self, figure): """Call this with the matplotlib Figure() object.""" self.figure = figure ax = self.figure.add_axes( (0, 0, 1, 1), frame_on=False, #viewer=self, #projection='ginga' ) #ax = fig.add_subplot(111) self.ax_img = ax # We don't want the axes cleared every time plot() is called ax.hold(False) # TODO: is this needed, since frame_on == False? ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) #ax.patch.set_alpha(0.0) ax.patch.set_visible(False) #ax.autoscale(enable=True, tight=True) ax.autoscale(enable=False) # Add an overlapped axis for drawing graphics newax = self.figure.add_axes( self.ax_img.get_position(), sharex=ax, sharey=ax, frameon=False, #viewer=self, #projection='ginga' ) newax.hold(True) newax.autoscale(enable=False) newax.get_xaxis().set_visible(False) newax.get_yaxis().set_visible(False) self.ax_util = newax # Create timers self._msg_timer = None self._defer_timer = None if hasattr(figure.canvas, 'new_timer'): self._msg_timer = Timer(mplcanvas=figure.canvas) self._msg_timer.add_callback( 'expired', lambda timer: self.onscreen_message(None)) self._defer_timer = Timer(mplcanvas=figure.canvas) self._defer_timer.add_callback('expired', lambda timer: self.delayed_redraw()) canvas = figure.canvas if hasattr(canvas, 'viewer'): canvas.set_viewer(self) else: canvas.mpl_connect("resize_event", self._resize_cb) # Because we don't know if resize callback works with all backends left, bottom, wd, ht = self.ax_img.bbox.bounds self.configure_window(wd, ht)
def set_figure(self, figure): """Call this with the matplotlib Figure() object.""" self.figure = figure ax = self.figure.add_axes((0, 0, 1, 1), frame_on=False) #ax = fig.add_subplot(111) self.ax_img = ax # We don't want the axes cleared every time plot() is called ax.hold(False) # TODO: is this needed, since frame_on == False? ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) #ax.patch.set_alpha(0.0) ax.patch.set_visible(False) #ax.autoscale(enable=True, tight=True) ax.autoscale(enable=False) # Add an overlapped axis for drawing graphics newax = self.figure.add_axes(self.ax_img.get_position(), sharex=ax, sharey=ax, frameon=False) newax.hold(True) newax.autoscale(enable=False) newax.get_xaxis().set_visible(False) newax.get_yaxis().set_visible(False) self.ax_util = newax # Create timers self._msg_timer = None self._defer_timer = None if hasattr(figure.canvas, 'new_timer'): self._msg_timer = Timer(0.0, lambda timer: self.onscreen_message(None), mplcanvas=figure.canvas) self._defer_timer = Timer(0.0, lambda timer: self.delayed_redraw(), mplcanvas=figure.canvas) canvas = figure.canvas if hasattr(canvas, 'viewer'): canvas.set_viewer(self) else: canvas.mpl_connect("resize_event", self._resize_cb) # Because we don't know if resize callback works with all backends left, bottom, wd, ht = self.ax_img.bbox.bounds self.configure_window(wd, ht)
class ImageViewMpl(ImageView.ImageViewBase): def __init__(self, logger=None, rgbmap=None, settings=None): ImageView.ImageViewBase.__init__(self, logger=logger, rgbmap=rgbmap, settings=settings) # Our Figure self.figure = None # Our Axes self.ax_img = None self.ax_util = None # Holds the image on ax_img self.mpimage = None # NOTE: matplotlib manages it's Y coordinates by default with # the origin at the bottom (see base class) self.origin_upper = False self.img_fg = (1.0, 1.0, 1.0) self.img_bg = (0.5, 0.5, 0.5) self.in_axes = False # Matplotlib expects RGBA data for color images self.rgb_order = 'RGBA' self.renderer = CanvasRenderer(self) # For timing events self._msg_timer = None self._defer_timer = None def set_figure(self, figure): """Call this with the matplotlib Figure() object.""" self.figure = figure ax = self.figure.add_axes((0, 0, 1, 1), frame_on=False) #ax = fig.add_subplot(111) self.ax_img = ax # We don't want the axes cleared every time plot() is called ax.hold(False) # TODO: is this needed, since frame_on == False? ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) #ax.patch.set_alpha(0.0) ax.patch.set_visible(False) #ax.autoscale(enable=True, tight=True) ax.autoscale(enable=False) # Add an overlapped axis for drawing graphics newax = self.figure.add_axes(self.ax_img.get_position(), sharex=ax, sharey=ax, frameon=False) newax.hold(True) newax.autoscale(enable=False) newax.get_xaxis().set_visible(False) newax.get_yaxis().set_visible(False) self.ax_util = newax # Create timers self._msg_timer = None self._defer_timer = None if hasattr(figure.canvas, 'new_timer'): self._msg_timer = Timer(mplcanvas=figure.canvas) self._msg_timer.add_callback( 'expired', lambda timer: self.onscreen_message(None)) self._defer_timer = Timer(mplcanvas=figure.canvas) self._defer_timer.add_callback('expired', lambda timer: self.delayed_redraw()) canvas = figure.canvas if hasattr(canvas, 'viewer'): canvas.set_viewer(self) else: canvas.mpl_connect("resize_event", self._resize_cb) # Because we don't know if resize callback works with all backends left, bottom, wd, ht = self.ax_img.bbox.bounds self.configure_window(wd, ht) def get_figure(self): return self.figure def set_widget(self, canvas): if hasattr(canvas, 'viewer'): canvas.set_viewer(self) def get_widget(self): return self.figure.canvas def calculate_aspect(self, shape, extent): dx = abs(extent[1] - extent[0]) / float(shape[1]) dy = abs(extent[3] - extent[2]) / float(shape[0]) return dx / dy def render_image1(self, rgbobj, dst_x, dst_y): """Render the image represented by (rgbobj) at dst_x, dst_y in the pixel space. NOTE: this version uses a Figure.FigImage to render the image. """ self.logger.debug("redraw surface") if self.figure is None: return ## left, bottom, width, height = self.ax_img.bbox.bounds ## self._imgwin_wd, self._imgwin_ht = width, height # Grab the RGB array for the current image and place it in the # matplotlib figure axis data = self.getwin_array(order=self.rgb_order) dst_x = dst_y = 0 # fill background color ## rect = self.figure.patch ## rect.set_facecolor(self.img_bg) # attempt 1: using a FigureImage (non-scaled) if self.mpimage is None: self.mpimage = self.figure.figimage(data, xo=dst_x, yo=dst_y, origin='upper') else: # note: this is not a typo--these offsets have a different # attribute name than in the constructor ('ox' vs. 'xo') self.mpimage.ox = dst_x self.mpimage.oy = dst_y self.mpimage.set_data(data) def render_image2(self, rgbobj, dst_x, dst_y): """Render the image represented by (rgbobj) at dst_x, dst_y in the pixel space. NOTE: this version renders the image in an Axes with imshow(). """ self.logger.debug("redraw surface") if self.figure is None: return ## left, bottom, width, height = self.ax_img.bbox.bounds ## self._imgwin_wd, self._imgwin_ht = width, height # Grab the RGB array for the current image and place it in the # matplotlib figure axis arr = self.getwin_array(order=self.rgb_order) # Get the data extents x0, y0 = 0, 0 y1, x1 = arr.shape[:2] flipx, flipy, swapxy = self.get_transforms() if swapxy: x0, x1, y0, y1 = y0, y1, x0, x1 extent = (x0, x1, y1, y0) self.logger.debug("extent=%s" % (str(extent))) # Calculate aspect ratio aspect = self.calculate_aspect(arr.shape, extent) if self.mpimage is None: img = self.ax_img.imshow(arr, interpolation='none', origin="upper", vmin=0, vmax=255, extent=extent, aspect=aspect) self.mpimage = img else: self.mpimage.set_data(arr) self.ax_img.set_aspect(aspect) self.mpimage.set_extent(extent) #self.ax_img.relim() def render_image(self, rgbobj, dst_x, dst_y): # Ugly, ugly hack copied from matplotlib.lines to cause line # objects to recompute their cached transformed_path # Other mpl artists don't seem to have this affliction for ax in self.figure.axes: if not (ax in (self.ax_img, self.ax_util)): if hasattr(ax, "lines"): for line in ax.lines: try: line._transformed_path.invalidate() except AttributeError: pass # render_image1() currently seems a little faster if self.in_axes: self.render_image2(rgbobj, dst_x, dst_y) else: self.render_image1(rgbobj, dst_x, dst_y) # clear utility axis self.ax_util.cla() # force an update of the figure self.figure.canvas.draw() # Set the axis limits # TODO: should we do this only for those who have autoaxis=True? ## wd, ht = self.get_window_size() ## x0, y0 = self.get_data_xy(0, 0) ## x1, tm = self.get_data_xy(wd-1, 0) ## tm, y1 = self.get_data_xy(0, ht-1) ## for ax in self.figure.axes: ## ax.set_xlim(x0, x1) ## ax.set_ylim(y0, y1) def configure_window(self, width, height): self.configure(width, height) def _resize_cb(self, event): wd, ht = event.width, event.height self.logger.debug("canvas resized %dx%d" % (wd, ht)) self.configure_window(wd, ht) def add_axes(self): ax = self.figure.add_axes( self.ax_img.get_position(), #sharex=self.ax_img, sharey=self.ax_img, frameon=False, viewer=self, projection='ginga') ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) return ax def get_png_image_as_buffer(self, output=None): ibuf = output if ibuf is None: ibuf = BytesIO() qimg = self.figure.write_to_png(ibuf) return ibuf def update_image(self): pass def set_cursor(self, cursor): pass def onscreen_message(self, text, delay=None, redraw=True): if self._msg_timer is not None: self._msg_timer.stop() self.set_onscreen_message(text, redraw=redraw) if self._msg_timer is not None and delay is not None: self._msg_timer.start(delay) def reschedule_redraw(self, time_sec): if self._defer_timer is None: self.delayed_redraw() return self._defer_timer.stop() self._defer_timer.start(time_sec)
class ImageViewMpl(ImageView.ImageViewBase): def __init__(self, logger=None, rgbmap=None, settings=None): ImageView.ImageViewBase.__init__(self, logger=logger, rgbmap=rgbmap, settings=settings) # Our Figure self.figure = None # Our Axes self.ax_img = None self.ax_util = None # Holds the image on ax_img self.mpimage = None # NOTE: matplotlib manages it's Y coordinates by default with # the origin at the bottom (see base class) self._originUpper = False self.img_fg = (1.0, 1.0, 1.0) self.img_bg = (0.5, 0.5, 0.5) self.in_axes = False # Matplotlib expects RGBA data for color images self._rgb_order = 'RGBA' self.renderer = CanvasRenderer(self) # cursors self.cursor = {} # For timing events self._msg_timer = None self._defer_timer = None def set_figure(self, figure): """Call this with the matplotlib Figure() object.""" self.figure = figure ax = self.figure.add_axes((0, 0, 1, 1), frame_on=False) #ax = fig.add_subplot(111) self.ax_img = ax # We don't want the axes cleared every time plot() is called ax.hold(False) # TODO: is this needed, since frame_on == False? ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) #ax.patch.set_alpha(0.0) ax.patch.set_visible(False) #ax.autoscale(enable=True, tight=True) ax.autoscale(enable=False) # Add an overlapped axis for drawing graphics newax = self.figure.add_axes(self.ax_img.get_position(), sharex=ax, sharey=ax, frameon=False) newax.hold(True) newax.autoscale(enable=False) newax.get_xaxis().set_visible(False) newax.get_yaxis().set_visible(False) self.ax_util = newax # Create timers self._msg_timer = None self._defer_timer = None if hasattr(figure.canvas, 'new_timer'): self._msg_timer = Timer(0.0, lambda timer: self.onscreen_message(None), mplcanvas=figure.canvas) self._defer_timer = Timer(0.0, lambda timer: self.delayed_redraw(), mplcanvas=figure.canvas) canvas = figure.canvas if hasattr(canvas, 'viewer'): canvas.set_viewer(self) else: canvas.mpl_connect("resize_event", self._resize_cb) # Because we don't know if resize callback works with all backends left, bottom, wd, ht = self.ax_img.bbox.bounds self.configure_window(wd, ht) def get_figure(self): return self.figure def set_widget(self, canvas): if hasattr(canvas, 'viewer'): canvas.set_viewer(self) def get_widget(self): return self.figure.canvas def get_rgb_order(self): return self._rgb_order def calculate_aspect(self, shape, extent): dx = abs(extent[1] - extent[0]) / float(shape[1]) dy = abs(extent[3] - extent[2]) / float(shape[0]) return dx / dy def render_image1(self, rgbobj, dst_x, dst_y): """Render the image represented by (rgbobj) at dst_x, dst_y in the pixel space. NOTE: this version uses a Figure.FigImage to render the image. """ self.logger.debug("redraw surface") if self.figure is None: return ## left, bottom, width, height = self.ax_img.bbox.bounds ## self._imgwin_wd, self._imgwin_ht = width, height # Grab the RGB array for the current image and place it in the # matplotlib figure axis data = self.getwin_array(order=self._rgb_order) dst_x = dst_y = 0 # fill background color ## rect = self.figure.patch ## rect.set_facecolor(self.img_bg) # attempt 1: using a FigureImage (non-scaled) if self.mpimage is None: self.mpimage = self.figure.figimage(data, xo=dst_x, yo=dst_y, origin='upper') else: # note: this is not a typo--these offsets have a different # attribute name than in the constructor ('ox' vs. 'xo') self.mpimage.ox = dst_x self.mpimage.oy = dst_y self.mpimage.set_data(data) def render_image2(self, rgbobj, dst_x, dst_y): """Render the image represented by (rgbobj) at dst_x, dst_y in the pixel space. NOTE: this version renders the image in an Axes with imshow(). """ self.logger.debug("redraw surface") if self.figure is None: return ## left, bottom, width, height = self.ax_img.bbox.bounds ## self._imgwin_wd, self._imgwin_ht = width, height # Grab the RGB array for the current image and place it in the # matplotlib figure axis arr = self.getwin_array(order=self._rgb_order) # Get the data extents x0, y0 = 0, 0 y1, x1 = arr.shape[:2] flipx, flipy, swapxy = self.get_transforms() if swapxy: x0, x1, y0, y1 = y0, y1, x0, x1 extent = (x0, x1, y1, y0) self.logger.debug("extent=%s" % (str(extent))) # Calculate aspect ratio aspect = self.calculate_aspect(arr.shape, extent) if self.mpimage is None: img = self.ax_img.imshow(arr, interpolation='none', origin="upper", vmin=0, vmax=255, extent=extent, aspect=aspect) self.mpimage = img else: self.mpimage.set_data(arr) self.ax_img.set_aspect(aspect) self.mpimage.set_extent(extent) #self.ax_img.relim() def render_image(self, rgbobj, dst_x, dst_y): # Ugly, ugly hack copied from matplotlib.lines to cause line # objects to recompute their cached transformed_path # Other mpl artists don't seem to have this affliction for ax in self.figure.axes: if not (ax in (self.ax_img, self.ax_util)): if hasattr(ax, "lines"): for line in ax.lines: try: line._transformed_path.invalidate() except AttributeError: pass # render_image1() currently seems a little faster if self.in_axes: self.render_image2(rgbobj, dst_x, dst_y) else: self.render_image1(rgbobj, dst_x, dst_y) # clear utility axis self.ax_util.cla() # force an update of the figure self.figure.canvas.draw() # Set the axis limits # TODO: should we do this only for those who have autoaxis=True? ## wd, ht = self.get_window_size() ## x0, y0 = self.get_data_xy(0, 0) ## x1, tm = self.get_data_xy(wd-1, 0) ## tm, y1 = self.get_data_xy(0, ht-1) ## for ax in self.figure.axes: ## ax.set_xlim(x0, x1) ## ax.set_ylim(y0, y1) def configure_window(self, width, height): self.configure(width, height) def _resize_cb(self, event): wd, ht = event.width, event.height self.logger.debug("canvas resized %dx%d" % (wd, ht)) self.configure_window(wd, ht) def add_axes(self): ax = self.figure.add_axes(self.ax_img.get_position(), #sharex=self.ax_img, sharey=self.ax_img, frameon=False, viewer=self, projection='ginga') ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) return ax def get_png_image_as_buffer(self, output=None): ibuf = output if ibuf is None: ibuf = BytesIO() qimg = self.figure.write_to_png(ibuf) return ibuf def update_image(self): pass def set_cursor(self, cursor): pass def define_cursor(self, ctype, cursor): self.cursor[ctype] = cursor def get_cursor(self, ctype): return self.cursor[ctype] def switch_cursor(self, ctype): self.set_cursor(self.cursor[ctype]) def onscreen_message(self, text, delay=None, redraw=True): if self._msg_timer is not None: self._msg_timer.cancel() self.set_onscreen_message(text, redraw=redraw) if self._msg_timer is not None and delay is not None: self._msg_timer.start(delay) def reschedule_redraw(self, time_sec): if self._defer_timer is None: self.delayed_redraw() return self._defer_timer.cancel() self._defer_timer.start(time_sec)