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 self.qimg_fmt = QImage.Format_RGB32 # find out optimum format for backing store #self.qimg_fmt = QPixmap(1, 1).toImage().format() # Qt needs this to be in BGR(A) self.rgb_order = 'BGRA' # default renderer is Qt one self.renderer = CanvasRenderer(self, surface_type='qpixmap') self.msgtimer = Timer() self.msgtimer.add_callback('expired', lambda timer: self.onscreen_message_off()) # For optomized redrawing self._defer_task = Timer() self._defer_task.add_callback('expired', lambda timer: self.delayed_redraw()) def get_widget(self): return self.imgwin 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) # tell renderer about our new size self.renderer.resize((width, height)) if isinstance(self.renderer.surface, QPixmap): # optimization when Qt is used as the renderer: # renderer surface is already a QPixmap self.pixmap = self.renderer.surface self.imgwin.set_pixmap(self.pixmap) else: # 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) 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): 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() 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, self.qimg_fmt) 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() qimg.save(filepath, format=format, quality=quality) def reschedule_redraw(self, time_sec): self._defer_task.stop() self._defer_task.start(time_sec) def update_image(self): if (not self.pixmap) or (not self.imgwin): return if isinstance(self.renderer.surface, QPixmap): # optimization when Qt is used as the renderer: # if renderer surface is already an offscreen QPixmap # then we can update the window directly from it #self.pixmap = self.renderer.surface pass else: if isinstance(self.renderer.surface, QImage): # optimization when Qt is used as the renderer: # renderer surface is already a QImage qimage = self.renderer.surface else: # otherwise, get the render surface as an array and # convert to a QImage try: arr = self.renderer.get_surface_as_array(order='BGRA') qimage = self._get_qimage(arr, QImage.Format_RGB32) except Exception as e: self.logger.error("Error from renderer: %s" % (str(e))) return # copy image from renderer to offscreen pixmap painter = QPainter(self.pixmap) #painter.setWorldMatrixEnabled(True) # fill surface with background color size = self.pixmap.size() width, height = size.width(), size.height() # draw image data from buffer to offscreen pixmap painter.drawImage(QtCore.QRect(0, 0, width, height), qimage, QtCore.QRect(0, 0, width, height)) 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() def _get_qimage(self, rgb_data, format): rgb_data = np.ascontiguousarray(rgb_data) ht, wd, channels = rgb_data.shape result = QImage(rgb_data.data, wd, ht, format) # Need to hang on to a reference to the array result.ndarray = rgb_data return result 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 make_timer(self): return Timer() def onscreen_message(self, text, delay=None, redraw=True): self.msgtimer.stop() self.set_onscreen_message(text, redraw=redraw) if delay is not None: self.msgtimer.start(delay) def take_focus(self): self.imgwin.setFocus()
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() self.msgtimer.add_callback('expired', lambda timer: self.onscreen_message_off()) # For optomized redrawing self._defer_task = Timer() self._defer_task.add_callback('expired', 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): 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.stop() 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() 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 make_timer(self): return Timer() 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.stop() self.set_onscreen_message(text, redraw=redraw) if delay is not None: self.msgtimer.start(delay)
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 self.qimg_fmt = QImage.Format_RGB32 # find out optimum format for backing store #self.qimg_fmt = QPixmap(1, 1).toImage().format() # Qt needs this to be in BGR(A) self.rgb_order = 'BGRA' # default renderer is Qt one self.renderer = CanvasRenderer(self, surface_type='qpixmap') self.msgtimer = Timer() self.msgtimer.add_callback('expired', lambda timer: self.onscreen_message_off()) # For optomized redrawing self._defer_task = Timer() self._defer_task.add_callback('expired', lambda timer: self.delayed_redraw()) def get_widget(self): return self.imgwin 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) # tell renderer about our new size self.renderer.resize((width, height)) if isinstance(self.renderer.surface, QPixmap): # optimization when Qt is used as the renderer: # renderer surface is already a QPixmap self.pixmap = self.renderer.surface self.imgwin.set_pixmap(self.pixmap) else: # 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) 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): 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() 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, self.qimg_fmt) 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() qimg.save(filepath, format=format, quality=quality) def reschedule_redraw(self, time_sec): self._defer_task.stop() self._defer_task.start(time_sec) def update_image(self): if (not self.pixmap) or (not self.imgwin): return if isinstance(self.renderer.surface, QPixmap): # optimization when Qt is used as the renderer: # if renderer surface is already an offscreen QPixmap # then we can update the window directly from it #self.pixmap = self.renderer.surface pass else: if isinstance(self.renderer.surface, QImage): # optimization when Qt is used as the renderer: # renderer surface is already a QImage qimage = self.renderer.surface else: # otherwise, get the render surface as an array and # convert to a QImage try: arr = self.renderer.get_surface_as_array(order='BGRA') qimage = self._get_qimage(arr, QImage.Format_RGB32) except Exception as e: self.logger.error("Error from renderer: %s" % (str(e))) return # copy image from renderer to offscreen pixmap painter = QPainter(self.pixmap) #painter.setWorldMatrixEnabled(True) # fill surface with background color size = self.pixmap.size() width, height = size.width(), size.height() # draw image data from buffer to offscreen pixmap painter.drawImage(QtCore.QRect(0, 0, width, height), qimage, QtCore.QRect(0, 0, width, height)) 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() def _get_qimage(self, rgb_data, format): rgb_data = np.ascontiguousarray(rgb_data) ht, wd, channels = rgb_data.shape result = QImage(rgb_data.data, wd, ht, format) # Need to hang on to a reference to the array result.ndarray = rgb_data return result 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 make_timer(self): return Timer() def onscreen_message(self, text, delay=None, redraw=True): self.msgtimer.stop() self.set_onscreen_message(text, redraw=redraw) if delay is not None: self.msgtimer.start(delay) def take_focus(self): self.imgwin.setFocus()