class ImageViewQt(ImageView.ImageViewBase): def __init__(self, logger=None, rgbmap=None, settings=None, render=None): ImageView.ImageViewBase.__init__(self, logger=logger, rgbmap=rgbmap, settings=settings) if render is None: render = 'widget' self.wtype = render if self.wtype == 'widget': self.imgwin = RenderWidget() elif self.wtype == 'scene': self.scene = QtGui.QGraphicsScene() self.imgwin = RenderGraphicsView(self.scene) else: raise ImageViewQtError("Undefined render type: '%s'" % (render)) self.imgwin.viewer = self self.pixmap = None # Qt expects 32bit BGRA data for color images self._rgb_order = 'BGRA' self.renderer = CanvasRenderer(self) self.msgtimer = Timer(0.0, lambda timer: self.onscreen_message_off()) # cursors self.cursor = {} # For optomized redrawing self._defer_task = Timer(0.0, lambda timer: self.delayed_redraw()) def get_widget(self): return self.imgwin def _render_offscreen(self, drawable, data, dst_x, dst_y, width, height): # NOTE [A] daht, dawd, depth = data.shape self.logger.debug("data shape is %dx%dx%d" % (dawd, daht, depth)) # Get qimage for copying pixel data qimage = self._get_qimage(data) painter = QPainter(drawable) painter.setWorldMatrixEnabled(True) # fill pixmap with background color imgwin_wd, imgwin_ht = self.get_window_size() bgclr = self._get_color(*self.img_bg) painter.fillRect(QtCore.QRect(0, 0, imgwin_wd, imgwin_ht), bgclr) # draw image data from buffer to offscreen pixmap painter.drawImage(QtCore.QRect(dst_x, dst_y, width, height), qimage, QtCore.QRect(0, 0, width, height)) def render_image(self, rgbobj, dst_x, dst_y): """Render the image represented by (rgbobj) at dst_x, dst_y in the pixel space. """ self.logger.debug("redraw pixmap=%s" % (self.pixmap)) if self.pixmap is None: return self.logger.debug("drawing to pixmap") # Prepare array for rendering arr = rgbobj.get_array(self._rgb_order) (height, width) = arr.shape[:2] return self._render_offscreen(self.pixmap, arr, dst_x, dst_y, width, height) def configure_window(self, width, height): self.logger.debug("window size reconfigured to %dx%d" % (width, height)) if hasattr(self, 'scene'): self.scene.setSceneRect(0, 0, width, height) # If we need to build a new pixmap do it here. We allocate one # twice as big as necessary to prevent having to reinstantiate it # all the time. On Qt this causes unpleasant flashing in the display. if (self.pixmap is None) or (self.pixmap.width() < width) or \ (self.pixmap.height() < height): pixmap = QPixmap(width * 2, height * 2) #pixmap.fill(QColor("black")) self.pixmap = pixmap self.imgwin.set_pixmap(pixmap) self.configure(width, height) def get_rgb_image_as_buffer(self, output=None, format='png', quality=90): ibuf = output if ibuf is None: ibuf = BytesIO() imgwin_wd, imgwin_ht = self.get_window_size() qpix = self.pixmap.copy(0, 0, imgwin_wd, imgwin_ht) qbuf = QtCore.QBuffer() qbuf.open(QtCore.QIODevice.ReadWrite) qpix.save(qbuf, format=format, quality=quality) ibuf.write(bytes(qbuf.data())) qbuf.close() return ibuf def get_rgb_image_as_bytes(self, format='png', quality=90): ibuf = self.get_rgb_image_as_buffer(format=format, quality=quality) return bytes(ibuf.getvalue()) def get_rgb_image_as_widget(self, output=None, format='png', quality=90): imgwin_wd, imgwin_ht = self.get_window_size() qpix = self.pixmap.copy(0, 0, imgwin_wd, imgwin_ht) return qpix.toImage() def save_rgb_image_as_file(self, filepath, format='png', quality=90): qimg = self.get_rgb_image_as_widget() res = qimg.save(filepath, format=format, quality=quality) def get_image_as_widget(self): """Used for generating thumbnails. Does not include overlaid graphics. """ arr = self.getwin_array(order=self._rgb_order) image = self._get_qimage(arr) return image def save_image_as_file(self, filepath, format='png', quality=90): """Used for generating thumbnails. Does not include overlaid graphics. """ qimg = self.get_image_as_widget() res = qimg.save(filepath, format=format, quality=quality) def reschedule_redraw(self, time_sec): self._defer_task.cancel() self._defer_task.start(time_sec) def update_image(self): if (not self.pixmap) or (not self.imgwin): return self.logger.debug("updating window from pixmap") if hasattr(self, 'scene'): imgwin_wd, imgwin_ht = self.get_window_size() self.scene.invalidate(0, 0, imgwin_wd, imgwin_ht, QtGui.QGraphicsScene.BackgroundLayer) else: self.imgwin.update() #self.imgwin.show() def set_cursor(self, cursor): if self.imgwin is not None: self.imgwin.setCursor(cursor) def define_cursor(self, ctype, cursor): self.cursor[ctype] = cursor def get_cursor(self, ctype): return self.cursor[ctype] def center_cursor(self): if self.imgwin is None: return win_x, win_y = self.get_center() w_pt = QtCore.QPoint(win_x, win_y) s_pt = self.imgwin.mapToGlobal(w_pt) # set the cursor position cursor = self.imgwin.cursor() cursor.setPos(s_pt) def position_cursor(self, data_x, data_y): if self.imgwin is None: return win_x, win_y = self.get_canvas_xy(data_x, data_y) w_pt = QtCore.QPoint(win_x, win_y) s_pt = self.imgwin.mapToGlobal(w_pt) # set the cursor position cursor = self.imgwin.cursor() cursor.setPos(s_pt) def get_rgb_order(self): return self._rgb_order def switch_cursor(self, ctype): self.set_cursor(self.cursor[ctype]) def _get_qimage(self, bgra): h, w, channels = bgra.shape fmt = QImage.Format_ARGB32 result = QImage(bgra.data, w, h, fmt) # Need to hang on to a reference to the array result.ndarray = bgra return result def _get_color(self, r, g, b): n = 255.0 clr = QColor(int(r * n), int(g * n), int(b * n)) return clr def onscreen_message(self, text, delay=None, redraw=True): self.msgtimer.cancel() self.set_onscreen_message(text, redraw=redraw) if delay is not None: self.msgtimer.start(delay) def onscreen_message_off(self): return self.onscreen_message(None)
class ImageViewQt(ImageView.ImageViewBase): def __init__(self, logger=None, rgbmap=None, settings=None, render=None): ImageView.ImageViewBase.__init__(self, logger=logger, rgbmap=rgbmap, settings=settings) if render is None: render = 'widget' self.wtype = render if self.wtype == 'widget': self.imgwin = RenderWidget() elif self.wtype == 'scene': self.scene = QtGui.QGraphicsScene() self.imgwin = RenderGraphicsView(self.scene) else: raise ImageViewQtError("Undefined render type: '%s'" % (render)) self.imgwin.viewer = self self.pixmap = None # Qt expects 32bit BGRA data for color images self._rgb_order = 'BGRA' self.renderer = CanvasRenderer(self) self.msgtimer = Timer(0.0, lambda timer: self.onscreen_message_off()) # For optomized redrawing self._defer_task = Timer(0.0, lambda timer: self.delayed_redraw()) def get_widget(self): return self.imgwin def _render_offscreen(self, drawable, data, dst_x, dst_y, width, height): # NOTE [A] daht, dawd, depth = data.shape self.logger.debug("data shape is %dx%dx%d" % (dawd, daht, depth)) # Get qimage for copying pixel data qimage = self._get_qimage(data) painter = QPainter(drawable) painter.setWorldMatrixEnabled(True) # fill pixmap with background color imgwin_wd, imgwin_ht = self.get_window_size() bgclr = self._get_color(*self.img_bg) painter.fillRect(QtCore.QRect(0, 0, imgwin_wd, imgwin_ht), bgclr) # draw image data from buffer to offscreen pixmap painter.drawImage(QtCore.QRect(dst_x, dst_y, width, height), qimage, QtCore.QRect(0, 0, width, height)) def render_image(self, rgbobj, dst_x, dst_y): """Render the image represented by (rgbobj) at dst_x, dst_y in the pixel space. """ self.logger.debug("redraw pixmap=%s" % (self.pixmap)) if self.pixmap is None: return self.logger.debug("drawing to pixmap") # Prepare array for rendering arr = rgbobj.get_array(self._rgb_order) (height, width) = arr.shape[:2] return self._render_offscreen(self.pixmap, arr, dst_x, dst_y, width, height) def configure_window(self, width, height): self.logger.debug("window size reconfigured to %dx%d" % ( width, height)) if hasattr(self, 'scene'): # By default, a QGraphicsView comes with a 1-pixel margin # You will get scrollbars unless you account for this # See http://stackoverflow.com/questions/3513788/qt-qgraphicsview-without-scrollbar width, height = width - 2, height - 2 self.scene.setSceneRect(1, 1, width-2, height-2) # If we need to build a new pixmap do it here. We allocate one # twice as big as necessary to prevent having to reinstantiate it # all the time. On Qt this causes unpleasant flashing in the display. if (self.pixmap is None) or (self.pixmap.width() < width) or \ (self.pixmap.height() < height): pixmap = QPixmap(width*2, height*2) #pixmap.fill(QColor("black")) self.pixmap = pixmap self.imgwin.set_pixmap(pixmap) self.configure(width, height) def get_rgb_image_as_buffer(self, output=None, format='png', quality=90): ibuf = output if ibuf is None: ibuf = BytesIO() imgwin_wd, imgwin_ht = self.get_window_size() qpix = self.pixmap.copy(0, 0, imgwin_wd, imgwin_ht) qbuf = QtCore.QBuffer() qbuf.open(QtCore.QIODevice.ReadWrite) qpix.save(qbuf, format=format, quality=quality) ibuf.write(bytes(qbuf.data())) qbuf.close() return ibuf def get_rgb_image_as_widget(self, output=None, format='png', quality=90): imgwin_wd, imgwin_ht = self.get_window_size() qpix = self.pixmap.copy(0, 0, imgwin_wd, imgwin_ht) return qpix.toImage() def save_rgb_image_as_file(self, filepath, format='png', quality=90): qimg = self.get_rgb_image_as_widget() res = qimg.save(filepath, format=format, quality=quality) def get_plain_image_as_widget(self): """Used for generating thumbnails. Does not include overlaid graphics. """ arr = self.getwin_array(order=self._rgb_order) image = self._get_qimage(arr) return image def save_plain_image_as_file(self, filepath, format='png', quality=90): """Used for generating thumbnails. Does not include overlaid graphics. """ qimg = self.get_plain_image_as_widget() res = qimg.save(filepath, format=format, quality=quality) def reschedule_redraw(self, time_sec): self._defer_task.cancel() self._defer_task.start(time_sec) def update_image(self): if (not self.pixmap) or (not self.imgwin): return self.logger.debug("updating window from pixmap") if hasattr(self, 'scene'): imgwin_wd, imgwin_ht = self.get_window_size() self.scene.invalidate(0, 0, imgwin_wd, imgwin_ht, QtGui.QGraphicsScene.BackgroundLayer) else: self.imgwin.update() #self.imgwin.show() def set_cursor(self, cursor): if self.imgwin is not None: self.imgwin.setCursor(cursor) def make_cursor(self, iconpath, x, y): image = QImage() image.load(iconpath) pm = QPixmap(image) return QCursor(pm, x, y) def center_cursor(self): if self.imgwin is None: return win_x, win_y = self.get_center() w_pt = QtCore.QPoint(win_x, win_y) s_pt = self.imgwin.mapToGlobal(w_pt) # set the cursor position cursor = self.imgwin.cursor() cursor.setPos(s_pt) def position_cursor(self, data_x, data_y): if self.imgwin is None: return win_x, win_y = self.get_canvas_xy(data_x, data_y) w_pt = QtCore.QPoint(win_x, win_y) s_pt = self.imgwin.mapToGlobal(w_pt) # set the cursor position cursor = self.imgwin.cursor() cursor.setPos(s_pt) def get_rgb_order(self): return self._rgb_order def _get_qimage(self, bgra): h, w, channels = bgra.shape fmt = QImage.Format_ARGB32 result = QImage(bgra.data, w, h, fmt) # Need to hang on to a reference to the array result.ndarray = bgra return result def _get_color(self, r, g, b): n = 255.0 clr = QColor(int(r*n), int(g*n), int(b*n)) return clr def onscreen_message(self, text, delay=None, redraw=True): self.msgtimer.cancel() self.set_onscreen_message(text, redraw=redraw) if delay is not None: self.msgtimer.start(delay)