def draw_pixmap(self, painter): p = self.current_scaled_pixmap width, height = p.width(), p.height() pwidth, pheight = self.last_canvas_size x = int(abs(pwidth - width)/2.) y = int(abs(pheight - height)/2.) self.target = QRectF(x, y, width, height) painter.drawPixmap(self.target, p, QRectF(p.rect()))
def draw_pixmap(self, painter): p = self.current_scaled_pixmap try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() width, height = int(p.width()/dpr), int(p.height()/dpr) pwidth, pheight = self.last_canvas_size x = int(abs(pwidth - width)/2.) y = int(abs(pheight - height)/2.) self.target = QRectF(x, y, width, height) painter.drawPixmap(self.target, p, QRectF(p.rect()))
def __init__(self, parent=None): QWidget.__init__(self, parent) self.setAcceptDrops(True) self.setMouseTracking(True) self.setFocusPolicy(Qt.ClickFocus) self.selection_state = SelectionState() self.undo_stack = u = QUndoStack() u.setUndoLimit(10) u.canUndoChanged.connect(self.emit_undo_redo_state) u.canRedoChanged.connect(self.emit_undo_redo_state) self.original_image_data = None self.is_valid = False self.original_image_format = None self.current_image = None self.current_scaled_pixmap = None self.last_canvas_size = None self.target = QRectF(0, 0, 0, 0) self.undo_action = a = self.undo_stack.createUndoAction(self, _('Undo') + ' ') a.setIcon(QIcon(I('edit-undo.png'))) self.redo_action = a = self.undo_stack.createRedoAction(self, _('Redo') + ' ') a.setIcon(QIcon(I('edit-redo.png')))
class Canvas(QWidget): BACKGROUND = QColor(60, 60, 60) SHADE_COLOR = QColor(0, 0, 0, 180) SELECT_PEN = QPen(QColor(Qt.white)) selection_state_changed = pyqtSignal(object) undo_redo_state_changed = pyqtSignal(object, object) image_changed = pyqtSignal(object) @property def has_selection(self): return self.selection_state.current_mode == 'selected' @property def is_modified(self): return self.current_image is not self.original_image # Drag 'n drop {{{ DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS def dragEnterEvent(self, event): md = event.mimeData() if dnd_has_extension(md, self.DROPABBLE_EXTENSIONS) or dnd_has_image(md): event.acceptProposedAction() def dropEvent(self, event): event.setDropAction(Qt.CopyAction) md = event.mimeData() x, y = dnd_get_image(md) if x is not None: # We have an image, set cover event.accept() if y is None: # Local image self.undo_stack.push(Replace(x.toImage(), _('Drop image'), self)) else: d = DownloadDialog(x, y, self.gui) d.start_download() if d.err is None: with open(d.fpath, 'rb') as f: img = QImage() img.loadFromData(f.read()) if not img.isNull(): self.undo_stack.push(Replace(img, _('Drop image'), self)) event.accept() def dragMoveEvent(self, event): event.acceptProposedAction() # }}} def __init__(self, parent=None): QWidget.__init__(self, parent) self.setAcceptDrops(True) self.setMouseTracking(True) self.setFocusPolicy(Qt.ClickFocus) self.selection_state = SelectionState() self.undo_stack = u = QUndoStack() u.setUndoLimit(10) u.canUndoChanged.connect(self.emit_undo_redo_state) u.canRedoChanged.connect(self.emit_undo_redo_state) self.original_image_data = None self.is_valid = False self.original_image_format = None self.current_image = None self.current_scaled_pixmap = None self.last_canvas_size = None self.target = QRectF(0, 0, 0, 0) self.undo_action = a = self.undo_stack.createUndoAction(self, _('Undo') + ' ') a.setIcon(QIcon(I('edit-undo.png'))) self.redo_action = a = self.undo_stack.createRedoAction(self, _('Redo') + ' ') a.setIcon(QIcon(I('edit-redo.png'))) def load_image(self, data): self.is_valid = False try: fmt = identify_data(data)[-1].encode('ascii') except Exception: fmt = b'' self.original_image_format = fmt.decode('ascii').lower() self.selection_state.reset() self.original_image_data = data self.current_image = i = self.original_image = ( QImage.fromData(data, format=fmt) if fmt else QImage.fromData(data)) self.is_valid = not i.isNull() self.update() self.image_changed.emit(self.current_image) def set_image(self, qimage): self.selection_state.reset() self.current_scaled_pixmap = None self.current_image = qimage self.is_valid = not qimage.isNull() self.update() self.image_changed.emit(self.current_image) def get_image_data(self, quality=90): if not self.is_modified: return self.original_image_data return pixmap_to_data(self.current_image, format=self.original_image_format or 'JPEG', quality=90) def copy(self): if not self.is_valid: return clipboard = QApplication.clipboard() if not self.has_selection or self.selection_state.rect is None: clipboard.setImage(self.current_image) else: trim = Trim(self) clipboard.setImage(trim.after_image) trim.before_image = trim.after_image = None def paste(self): clipboard = QApplication.clipboard() md = clipboard.mimeData() if md.hasImage(): img = QImage(md.imageData()) if not img.isNull(): self.undo_stack.push(Replace(img, _('Paste image'), self)) else: error_dialog(self, _('No image'), _( 'No image available in the clipboard'), show=True) def break_cycles(self): self.undo_stack.clear() self.original_image_data = self.current_image = self.current_scaled_pixmap = None def emit_undo_redo_state(self): self.undo_redo_state_changed.emit(self.undo_action.isEnabled(), self.redo_action.isEnabled()) @imageop def trim_image(self): if self.selection_state.rect is None: error_dialog(self, _('No selection'), _( 'No active selection, first select a region in the image, by dragging with your mouse'), show=True) return False self.undo_stack.push(Trim(self)) return True @imageop def autotrim_image(self): self.undo_stack.push(AutoTrim(self)) return True @imageop def rotate_image(self): self.undo_stack.push(Rotate(self)) return True @imageop def resize_image(self, width, height): self.undo_stack.push(Scale(width, height, self)) return True @imageop def sharpen_image(self, sigma=3.0): self.undo_stack.push(Sharpen(sigma, self)) return True @imageop def blur_image(self, sigma=3.0): self.undo_stack.push(Blur(sigma, self)) return True @imageop def despeckle_image(self): self.undo_stack.push(Despeckle(self)) return True # The selection rectangle {{{ @property def dc_size(self): sr = self.selection_state.rect dx = min(75, sr.width() / 4) dy = min(75, sr.height() / 4) return dx, dy def get_drag_corner(self, pos): dx, dy = self.dc_size sr = self.selection_state.rect x, y = pos.x(), pos.y() hedge = 'left' if x < sr.x() + dx else 'right' if x > sr.right() - dx else None vedge = 'top' if y < sr.y() + dy else 'bottom' if y > sr.bottom() - dy else None return (hedge, vedge) if hedge or vedge else None def get_drag_rect(self): sr = self.selection_state.rect dc = self.selection_state.drag_corner if None in (sr, dc): return dx, dy = self.dc_size if None in dc: # An edge if dc[0] is None: top = sr.top() if dc[1] == 'top' else sr.bottom() - dy ans = QRectF(sr.left() + dx, top, sr.width() - 2 * dx, dy) else: left = sr.left() if dc[0] == 'left' else sr.right() - dx ans = QRectF(left, sr.top() + dy, dx, sr.height() - 2 * dy) else: # A corner left = sr.left() if dc[0] == 'left' else sr.right() - dx top = sr.top() if dc[1] == 'top' else sr.bottom() - dy ans = QRectF(left, top, dx, dy) return ans def get_cursor(self): dc = self.selection_state.drag_corner if dc is None: ans = Qt.OpenHandCursor if self.selection_state.last_drag_pos is None else Qt.ClosedHandCursor elif None in dc: ans = Qt.SizeVerCursor if dc[0] is None else Qt.SizeHorCursor else: ans = Qt.SizeBDiagCursor if dc in {('left', 'bottom'), ('right', 'top')} else Qt.SizeFDiagCursor return ans def move_edge(self, edge, dp): sr = self.selection_state.rect horiz = edge in {'left', 'right'} func = getattr(sr, 'set' + capitalize(edge)) delta = getattr(dp, 'x' if horiz else 'y')() buf = 50 if horiz: minv = self.target.left() if edge == 'left' else sr.left() + buf maxv = sr.right() - buf if edge == 'left' else self.target.right() else: minv = self.target.top() if edge == 'top' else sr.top() + buf maxv = sr.bottom() - buf if edge == 'top' else self.target.bottom() func(max(minv, min(maxv, delta + getattr(sr, edge)()))) def move_selection_rect(self, x, y): sr = self.selection_state.rect half_width = sr.width() / 2.0 half_height = sr.height() / 2.0 c = sr.center() nx = c.x() + x ny = c.y() + y minx = self.target.left() + half_width maxx = self.target.right() - half_width miny, maxy = self.target.top() + half_height, self.target.bottom() - half_height nx = max(minx, min(maxx, nx)) ny = max(miny, min(maxy, ny)) sr.moveCenter(QPointF(nx, ny)) def move_selection(self, dp): dm = self.selection_state.dragging if dm is None: self.move_selection_rect(dp.x(), dp.y()) else: for edge in dm: if edge is not None: self.move_edge(edge, dp) def mousePressEvent(self, ev): if ev.button() == Qt.LeftButton and self.target.contains(ev.pos()): pos = ev.pos() self.selection_state.last_press_point = pos if self.selection_state.current_mode is None: self.selection_state.current_mode = 'select' elif self.selection_state.current_mode == 'selected': if self.selection_state.rect is not None and self.selection_state.rect.contains(pos): self.selection_state.drag_corner = self.selection_state.dragging = self.get_drag_corner(pos) self.selection_state.last_drag_pos = pos self.setCursor(self.get_cursor()) else: self.selection_state.current_mode = 'select' self.selection_state.rect = None self.selection_state_changed.emit(False) def mouseMoveEvent(self, ev): changed = False if self.selection_state.in_selection: changed = True self.selection_state.in_selection = False self.selection_state.drag_corner = None pos = ev.pos() cursor = Qt.ArrowCursor try: if not self.target.contains(pos): return if ev.buttons() & Qt.LeftButton: if self.selection_state.last_press_point is not None and self.selection_state.current_mode is not None: if self.selection_state.current_mode == 'select': self.selection_state.rect = QRectF(self.selection_state.last_press_point, pos).normalized() changed = True elif self.selection_state.last_drag_pos is not None: self.selection_state.in_selection = True self.selection_state.drag_corner = self.selection_state.dragging dp = pos - self.selection_state.last_drag_pos self.selection_state.last_drag_pos = pos self.move_selection(dp) cursor = self.get_cursor() changed = True else: if self.selection_state.rect is None or not self.selection_state.rect.contains(pos): return if self.selection_state.current_mode == 'selected': if self.selection_state.rect is not None and self.selection_state.rect.contains(pos): self.selection_state.drag_corner = self.get_drag_corner(pos) self.selection_state.in_selection = True cursor = self.get_cursor() changed = True finally: if changed: self.update() self.setCursor(cursor) def mouseReleaseEvent(self, ev): if ev.button() == Qt.LeftButton: self.selection_state.dragging = self.selection_state.last_drag_pos = None if self.selection_state.current_mode == 'select': r = self.selection_state.rect if r is None or max(r.width(), r.height()) < 3: self.selection_state.reset() else: self.selection_state.current_mode = 'selected' self.selection_state_changed.emit(self.has_selection) elif self.selection_state.current_mode == 'selected' and self.selection_state.rect is not None and self.selection_state.rect.contains(ev.pos()): self.setCursor(self.get_cursor()) self.update() def keyPressEvent(self, ev): k = ev.key() if k in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down) and self.selection_state.rect is not None and self.has_selection: ev.accept() delta = 10 if ev.modifiers() & Qt.ShiftModifier else 1 x = y = 0 if k in (Qt.Key_Left, Qt.Key_Right): x = delta * (-1 if k == Qt.Key_Left else 1) else: y = delta * (-1 if k == Qt.Key_Up else 1) self.move_selection_rect(x, y) self.update() else: return QWidget.keyPressEvent(self, ev) # }}} # Painting {{{ @painter def draw_background(self, painter): painter.fillRect(self.rect(), self.BACKGROUND) @painter def draw_image_error(self, painter): font = painter.font() font.setPointSize(3 * font.pointSize()) font.setBold(True) painter.setFont(font) painter.setPen(QColor(Qt.black)) painter.drawText(self.rect(), Qt.AlignCenter, _('Not a valid image')) def load_pixmap(self): canvas_size = self.rect().width(), self.rect().height() if self.last_canvas_size != canvas_size: if self.last_canvas_size is not None and self.selection_state.rect is not None: self.selection_state.reset() # TODO: Migrate the selection rect self.last_canvas_size = canvas_size self.current_scaled_pixmap = None if self.current_scaled_pixmap is None: pwidth, pheight = self.last_canvas_size i = self.current_image width, height = i.width(), i.height() scaled, width, height = fit_image(width, height, pwidth, pheight) if scaled: i = self.current_image.scaled(width, height, transformMode=Qt.SmoothTransformation) self.current_scaled_pixmap = QPixmap.fromImage(i) @painter def draw_pixmap(self, painter): p = self.current_scaled_pixmap width, height = p.width(), p.height() pwidth, pheight = self.last_canvas_size x = int(abs(pwidth - width)/2.) y = int(abs(pheight - height)/2.) self.target = QRectF(x, y, width, height) painter.drawPixmap(self.target, p, QRectF(p.rect())) @painter def draw_selection_rect(self, painter): cr, sr = self.target, self.selection_state.rect painter.setPen(self.SELECT_PEN) painter.setRenderHint(QPainter.Antialiasing, False) if self.selection_state.current_mode == 'selected': # Shade out areas outside the selection rect for r in ( QRectF(cr.topLeft(), QPointF(sr.left(), cr.bottom())), # left QRectF(QPointF(sr.left(), cr.top()), sr.topRight()), # top QRectF(QPointF(sr.right(), cr.top()), cr.bottomRight()), # right QRectF(sr.bottomLeft(), QPointF(sr.right(), cr.bottom())), # bottom ): painter.fillRect(r, self.SHADE_COLOR) dr = self.get_drag_rect() if self.selection_state.in_selection and dr is not None: # Draw the resize rectangle painter.save() painter.setCompositionMode(QPainter.RasterOp_SourceAndNotDestination) painter.setClipRect(sr.adjusted(1, 1, -1, -1)) painter.drawRect(dr) painter.restore() # Draw the selection rectangle painter.setCompositionMode(QPainter.RasterOp_SourceAndNotDestination) painter.drawRect(sr) def paintEvent(self, event): QWidget.paintEvent(self, event) p = QPainter(self) p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) try: self.draw_background(p) if self.original_image_data is None: return if not self.is_valid: return self.draw_image_error(p) self.load_pixmap() self.draw_pixmap(p) if self.selection_state.rect is not None: self.draw_selection_rect(p) finally: p.end()
def _setup_ui(self): self.setRenderHint(QPainter.Antialiasing) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setFrameShape(QFrame.NoFrame) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self._loading_map = self._generate_loading_map() self._scene = QGraphicsScene(self) self._scene.setBackgroundBrush(QBrush(self.palette().dark().color())) self._image = QGraphicsPixmapItem() self._scene.addItem(self._image) # hud hud_color = self.palette().highlight().color().lighter(150) self._overlay = self._scene.addEllipse(QRectF(), QPen(hud_color, 4), QBrush(Qt.NoBrush)) self._crosshair = self._scene.addEllipse(QRectF(), QPen(hud_color, 2), QBrush(Qt.NoBrush)) rect = QRectF(0, 0, self._w, self._h) cx = rect.center().x() cy = rect.center().y() r = min(rect.width(), rect.height()) * 0.7 self._overlay.setRect(QRectF(cx - r / 2, cy - r / 2, r, r)) rc = min(rect.width(), rect.height()) * 0.05 self._crosshair.setRect(QRectF(cx - rc / 2, cy - rc / 2, rc, rc)) self._overlay.setVisible(False) self._crosshair.setVisible(False) # scene self.setScene(self._scene) self._info = CameraInspectorInfo() self.setLayout(self._info)
class Canvas(QWidget): BACKGROUND = QColor(60, 60, 60) SHADE_COLOR = QColor(0, 0, 0, 180) SELECT_PEN = QPen(QColor(Qt.white)) selection_state_changed = pyqtSignal(object) undo_redo_state_changed = pyqtSignal(object, object) image_changed = pyqtSignal(object) @property def has_selection(self): return self.selection_state.current_mode == 'selected' @property def is_modified(self): return self.current_image is not self.original_image # Drag 'n drop {{{ def dragEnterEvent(self, event): md = event.mimeData() if dnd_has_extension(md, image_extensions()) or dnd_has_image(md): event.acceptProposedAction() def dropEvent(self, event): event.setDropAction(Qt.CopyAction) md = event.mimeData() x, y = dnd_get_image(md) if x is not None: # We have an image, set cover event.accept() if y is None: # Local image self.undo_stack.push( Replace(x.toImage(), _('Drop image'), self)) else: d = DownloadDialog(x, y, self.gui) d.start_download() if d.err is None: with open(d.fpath, 'rb') as f: img = QImage() img.loadFromData(f.read()) if not img.isNull(): self.undo_stack.push( Replace(img, _('Drop image'), self)) event.accept() def dragMoveEvent(self, event): event.acceptProposedAction() # }}} def __init__(self, parent=None): QWidget.__init__(self, parent) self.setAcceptDrops(True) self.setMouseTracking(True) self.setFocusPolicy(Qt.ClickFocus) self.selection_state = SelectionState() self.undo_stack = u = QUndoStack() u.setUndoLimit(10) u.canUndoChanged.connect(self.emit_undo_redo_state) u.canRedoChanged.connect(self.emit_undo_redo_state) self.original_image_data = None self.is_valid = False self.original_image_format = None self.current_image = None self.current_scaled_pixmap = None self.last_canvas_size = None self.target = QRectF(0, 0, 0, 0) self.undo_action = a = self.undo_stack.createUndoAction( self, _('Undo') + ' ') a.setIcon(QIcon(I('edit-undo.png'))) self.redo_action = a = self.undo_stack.createRedoAction( self, _('Redo') + ' ') a.setIcon(QIcon(I('edit-redo.png'))) def load_image(self, data): self.is_valid = False try: fmt = identify(data)[0].encode('ascii') except Exception: fmt = b'' self.original_image_format = fmt.decode('ascii').lower() self.selection_state.reset() self.original_image_data = data self.current_image = i = self.original_image = (QImage.fromData( data, format=fmt) if fmt else QImage.fromData(data)) self.is_valid = not i.isNull() self.current_scaled_pixmap = None self.update() self.image_changed.emit(self.current_image) def set_image(self, qimage): self.selection_state.reset() self.current_scaled_pixmap = None self.current_image = qimage self.is_valid = not qimage.isNull() self.update() self.image_changed.emit(self.current_image) def get_image_data(self, quality=90): if not self.is_modified: return self.original_image_data fmt = self.original_image_format or 'JPEG' if fmt.lower() not in { x.data().decode('utf-8') for x in QImageWriter.supportedImageFormats() }: if fmt.lower() == 'gif': data = image_to_data(self.current_image, fmt='PNG', png_compression_level=0) from PIL import Image i = Image.open(BytesIO(data)) buf = BytesIO() i.save(buf, 'gif') return buf.getvalue() else: raise ValueError('Cannot save %s format images' % fmt) return pixmap_to_data(self.current_image, format=fmt, quality=90) def copy(self): if not self.is_valid: return clipboard = QApplication.clipboard() if not self.has_selection or self.selection_state.rect is None: clipboard.setImage(self.current_image) else: trim = Trim(self) clipboard.setImage(trim.after_image) trim.before_image = trim.after_image = None def paste(self): clipboard = QApplication.clipboard() md = clipboard.mimeData() if md.hasImage(): img = QImage(md.imageData()) if not img.isNull(): self.undo_stack.push(Replace(img, _('Paste image'), self)) else: error_dialog(self, _('No image'), _('No image available in the clipboard'), show=True) def break_cycles(self): self.undo_stack.clear() self.original_image_data = self.current_image = self.current_scaled_pixmap = None def emit_undo_redo_state(self): self.undo_redo_state_changed.emit(self.undo_action.isEnabled(), self.redo_action.isEnabled()) @imageop def trim_image(self): if self.selection_state.rect is None: error_dialog( self, _('No selection'), _('No active selection, first select a region in the image, by dragging with your mouse' ), show=True) return False self.undo_stack.push(Trim(self)) return True @imageop def autotrim_image(self): self.undo_stack.push(AutoTrim(self)) return True @imageop def rotate_image(self): self.undo_stack.push(Rotate(self)) return True @imageop def resize_image(self, width, height): self.undo_stack.push(Scale(width, height, self)) return True @imageop def sharpen_image(self, sigma=3.0): self.undo_stack.push(Sharpen(sigma, self)) return True @imageop def blur_image(self, sigma=3.0): self.undo_stack.push(Blur(sigma, self)) return True @imageop def despeckle_image(self): self.undo_stack.push(Despeckle(self)) return True @imageop def normalize_image(self): self.undo_stack.push(Normalize(self)) return True @imageop def oilify_image(self, radius=4.0): self.undo_stack.push(Oilify(radius, self)) return True # The selection rectangle {{{ @property def dc_size(self): sr = self.selection_state.rect dx = min(75, sr.width() / 4) dy = min(75, sr.height() / 4) return dx, dy def get_drag_corner(self, pos): dx, dy = self.dc_size sr = self.selection_state.rect x, y = pos.x(), pos.y() hedge = 'left' if x < sr.x() + dx else 'right' if x > sr.right( ) - dx else None vedge = 'top' if y < sr.y() + dy else 'bottom' if y > sr.bottom( ) - dy else None return (hedge, vedge) if hedge or vedge else None def get_drag_rect(self): sr = self.selection_state.rect dc = self.selection_state.drag_corner if None in (sr, dc): return dx, dy = self.dc_size if None in dc: # An edge if dc[0] is None: top = sr.top() if dc[1] == 'top' else sr.bottom() - dy ans = QRectF(sr.left() + dx, top, sr.width() - 2 * dx, dy) else: left = sr.left() if dc[0] == 'left' else sr.right() - dx ans = QRectF(left, sr.top() + dy, dx, sr.height() - 2 * dy) else: # A corner left = sr.left() if dc[0] == 'left' else sr.right() - dx top = sr.top() if dc[1] == 'top' else sr.bottom() - dy ans = QRectF(left, top, dx, dy) return ans def get_cursor(self): dc = self.selection_state.drag_corner if dc is None: ans = Qt.OpenHandCursor if self.selection_state.last_drag_pos is None else Qt.ClosedHandCursor elif None in dc: ans = Qt.SizeVerCursor if dc[0] is None else Qt.SizeHorCursor else: ans = Qt.SizeBDiagCursor if dc in {('left', 'bottom'), ('right', 'top')} else Qt.SizeFDiagCursor return ans def move_edge(self, edge, dp): sr = self.selection_state.rect horiz = edge in {'left', 'right'} func = getattr(sr, 'set' + capitalize(edge)) delta = getattr(dp, 'x' if horiz else 'y')() buf = 50 if horiz: minv = self.target.left() if edge == 'left' else sr.left() + buf maxv = sr.right() - buf if edge == 'left' else self.target.right() else: minv = self.target.top() if edge == 'top' else sr.top() + buf maxv = sr.bottom() - buf if edge == 'top' else self.target.bottom() func(max(minv, min(maxv, delta + getattr(sr, edge)()))) def move_selection_rect(self, x, y): sr = self.selection_state.rect half_width = sr.width() / 2.0 half_height = sr.height() / 2.0 c = sr.center() nx = c.x() + x ny = c.y() + y minx = self.target.left() + half_width maxx = self.target.right() - half_width miny, maxy = self.target.top() + half_height, self.target.bottom( ) - half_height nx = max(minx, min(maxx, nx)) ny = max(miny, min(maxy, ny)) sr.moveCenter(QPointF(nx, ny)) def move_selection(self, dp): dm = self.selection_state.dragging if dm is None: self.move_selection_rect(dp.x(), dp.y()) else: for edge in dm: if edge is not None: self.move_edge(edge, dp) def mousePressEvent(self, ev): if ev.button() == Qt.LeftButton and self.target.contains(ev.pos()): pos = ev.pos() self.selection_state.last_press_point = pos if self.selection_state.current_mode is None: self.selection_state.current_mode = 'select' elif self.selection_state.current_mode == 'selected': if self.selection_state.rect is not None and self.selection_state.rect.contains( pos): self.selection_state.drag_corner = self.selection_state.dragging = self.get_drag_corner( pos) self.selection_state.last_drag_pos = pos self.setCursor(self.get_cursor()) else: self.selection_state.current_mode = 'select' self.selection_state.rect = None self.selection_state_changed.emit(False) def mouseMoveEvent(self, ev): changed = False if self.selection_state.in_selection: changed = True self.selection_state.in_selection = False self.selection_state.drag_corner = None pos = ev.pos() cursor = Qt.ArrowCursor try: if not self.target.contains(pos): return if ev.buttons() & Qt.LeftButton: if self.selection_state.last_press_point is not None and self.selection_state.current_mode is not None: if self.selection_state.current_mode == 'select': self.selection_state.rect = QRectF( self.selection_state.last_press_point, pos).normalized() changed = True elif self.selection_state.last_drag_pos is not None: self.selection_state.in_selection = True self.selection_state.drag_corner = self.selection_state.dragging dp = pos - self.selection_state.last_drag_pos self.selection_state.last_drag_pos = pos self.move_selection(dp) cursor = self.get_cursor() changed = True else: if self.selection_state.rect is None or not self.selection_state.rect.contains( pos): return if self.selection_state.current_mode == 'selected': if self.selection_state.rect is not None and self.selection_state.rect.contains( pos): self.selection_state.drag_corner = self.get_drag_corner( pos) self.selection_state.in_selection = True cursor = self.get_cursor() changed = True finally: if changed: self.update() self.setCursor(cursor) def mouseReleaseEvent(self, ev): if ev.button() == Qt.LeftButton: self.selection_state.dragging = self.selection_state.last_drag_pos = None if self.selection_state.current_mode == 'select': r = self.selection_state.rect if r is None or max(r.width(), r.height()) < 3: self.selection_state.reset() else: self.selection_state.current_mode = 'selected' self.selection_state_changed.emit(self.has_selection) elif self.selection_state.current_mode == 'selected' and self.selection_state.rect is not None and self.selection_state.rect.contains( ev.pos()): self.setCursor(self.get_cursor()) self.update() def keyPressEvent(self, ev): k = ev.key() if k in ( Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down ) and self.selection_state.rect is not None and self.has_selection: ev.accept() delta = 10 if ev.modifiers() & Qt.ShiftModifier else 1 x = y = 0 if k in (Qt.Key_Left, Qt.Key_Right): x = delta * (-1 if k == Qt.Key_Left else 1) else: y = delta * (-1 if k == Qt.Key_Up else 1) self.move_selection_rect(x, y) self.update() else: return QWidget.keyPressEvent(self, ev) # }}} # Painting {{{ @painter def draw_background(self, painter): painter.fillRect(self.rect(), self.BACKGROUND) @painter def draw_image_error(self, painter): font = painter.font() font.setPointSize(3 * font.pointSize()) font.setBold(True) painter.setFont(font) painter.setPen(QColor(Qt.black)) painter.drawText(self.rect(), Qt.AlignCenter, _('Not a valid image')) def load_pixmap(self): canvas_size = self.rect().width(), self.rect().height() if self.last_canvas_size != canvas_size: if self.last_canvas_size is not None and self.selection_state.rect is not None: self.selection_state.reset() # TODO: Migrate the selection rect self.last_canvas_size = canvas_size self.current_scaled_pixmap = None if self.current_scaled_pixmap is None: pwidth, pheight = self.last_canvas_size i = self.current_image width, height = i.width(), i.height() scaled, width, height = fit_image(width, height, pwidth, pheight) try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() if scaled: i = self.current_image.scaled( int(dpr * width), int(dpr * height), transformMode=Qt.SmoothTransformation) self.current_scaled_pixmap = QPixmap.fromImage(i) self.current_scaled_pixmap.setDevicePixelRatio(dpr) @painter def draw_pixmap(self, painter): p = self.current_scaled_pixmap try: dpr = self.devicePixelRatioF() except AttributeError: dpr = self.devicePixelRatio() width, height = int(p.width() / dpr), int(p.height() / dpr) pwidth, pheight = self.last_canvas_size x = int(abs(pwidth - width) / 2.) y = int(abs(pheight - height) / 2.) self.target = QRectF(x, y, width, height) painter.drawPixmap(self.target, p, QRectF(p.rect())) @painter def draw_selection_rect(self, painter): cr, sr = self.target, self.selection_state.rect painter.setPen(self.SELECT_PEN) painter.setRenderHint(QPainter.Antialiasing, False) if self.selection_state.current_mode == 'selected': # Shade out areas outside the selection rect for r in ( QRectF(cr.topLeft(), QPointF(sr.left(), cr.bottom())), # left QRectF(QPointF(sr.left(), cr.top()), sr.topRight()), # top QRectF(QPointF(sr.right(), cr.top()), cr.bottomRight()), # right QRectF(sr.bottomLeft(), QPointF(sr.right(), cr.bottom())), # bottom ): painter.fillRect(r, self.SHADE_COLOR) dr = self.get_drag_rect() if self.selection_state.in_selection and dr is not None: # Draw the resize rectangle painter.save() painter.setCompositionMode( QPainter.RasterOp_SourceAndNotDestination) painter.setClipRect(sr.adjusted(1, 1, -1, -1)) painter.drawRect(dr) painter.restore() # Draw the selection rectangle painter.setCompositionMode(QPainter.RasterOp_SourceAndNotDestination) painter.drawRect(sr) def paintEvent(self, event): QWidget.paintEvent(self, event) p = QPainter(self) p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) try: self.draw_background(p) if self.original_image_data is None: return if not self.is_valid: return self.draw_image_error(p) self.load_pixmap() self.draw_pixmap(p) if self.selection_state.rect is not None: self.draw_selection_rect(p) finally: p.end()
def paint(self, painter, option, index): QStyledItemDelegate.paint(self, painter, option, index) hovering = index.data(HOVER_ROLE) is True painter.save() rect = option.rect is_current = index.data(Qt.FontRole) is not None if not hovering and is_current: qpp = QPainterPath() qpp.addRoundedRect(QRectF(rect), 6, 6) painter.fillPath(qpp, self.current_background) icon_rect = QRect(rect.left() + self.MARGIN, rect.top() + self.MARGIN, ICON_SIZE, ICON_SIZE) left = icon_rect.right() + 2 * self.MARGIN text_rect = QRect(left, icon_rect.top(), rect.width() - left + rect.left(), icon_rect.height()) mark = index.data(MARK_ROLE) if hovering or mark: text_rect.adjust(0, 0, -text_rect.height(), 0) text = index.data(DISPLAY_ROLE) or '' font = index.data(Qt.FontRole) if font: painter.setFont(font) text_flags = Qt.AlignVCenter | Qt.AlignLeft | Qt.TextSingleLine text = elided_text(text, font, text_rect.width(), 'right') if option.state & QStyle.State_Selected: painter.setPen(QPen(self.highlighted_text)) painter.drawText(text_rect, text_flags, text) if mark: hrect = QRect(text_rect.right(), text_rect.top(), text_rect.height(), text_rect.height()) painter.fillRect(hrect, QColor('#ffffaa')) painter.drawText(hrect, Qt.AlignCenter, mark) elif hovering: hrect = QRect(text_rect.right(), text_rect.top(), text_rect.height(), text_rect.height()) close_hover = index.data(CLOSE_HOVER_ROLE) is True if close_hover: pen = painter.pen() pen.setColor(QColor('red')) painter.setPen(pen) painter.drawText(hrect, Qt.AlignCenter, '✖ ') if index.data(LOADING_ROLE): if not self.errored_out: angle = index.data(ANGLE_ROLE) try: draw_snake_spinner(painter, icon_rect, angle, self.light, self.dark) except Exception: import traceback traceback.print_exc() self.errored_out = True else: icurl = index.data(URL_ROLE) if icurl == WELCOME_URL: icon = welcome_icon() elif icurl == DOWNLOADS_URL: icon = downloads_icon() else: icon = index.data(DECORATION_ROLE) icon.paint(painter, icon_rect) painter.restore()
def boundingRect(self): return QRectF(0, 0, self._width, self._height)
def _activate_image_tab(self): """ Initialization method for the image tab """ camera_width = self.spectrumlogic().camera_constraints.width camera_height = self.spectrumlogic().camera_constraints.height for read_mode in self.spectrumlogic().camera_constraints.read_modes: if read_mode.name[:5] == "IMAGE": self._image_tab.read_modes.addItem(read_mode.name, read_mode.name) if read_mode == self._image_read_mode: self._image_tab.read_modes.setCurrentText(read_mode.name) for acquisition_mode in AcquisitionMode.__members__: if acquisition_mode != "MULTI_SCAN": self._image_tab.acquisition_modes.addItem( acquisition_mode, acquisition_mode) if acquisition_mode == self._image_acquisition_mode: self._image_tab.acquisition_modes.setCurrentText( acquisition_mode) self.image_exposure_time_widget = ScienDSpinBox() self.image_exposure_time_widget.setMinimum(0) self.image_exposure_time_widget.setValue(self._image_exposure_time) self.image_exposure_time_widget.setSuffix('s') self._image_tab.exposure_time_layout.addWidget( self.image_exposure_time_widget) for readout_speed in self.spectrumlogic( ).camera_constraints.readout_speeds: self._image_tab.readout_speed.addItem( "{:.2r}Hz".format(ScaledFloat(readout_speed)), readout_speed) if readout_speed == self._image_readout_speed: self._image_tab.readout_speed.setCurrentText("{:.2r}Hz".format( ScaledFloat(readout_speed))) self._image_tab.save.clicked.connect(partial(self.save_data, 0)) self._save_data_buttons.append(self._image_tab.save) self._image_tab.acquire_dark.clicked.connect( partial(self.start_dark_acquisition, 0)) self._acquire_dark_buttons.append(self._image_tab.acquire_dark) self._image_tab.start_acquisition.clicked.connect( partial(self.start_acquisition, 0)) self._start_acquisition_buttons.append( self._image_tab.start_acquisition) self._image_tab.stop_acquisition.clicked.connect(self.stop_acquisition) self._stop_acquisition_buttons.append(self._image_tab.stop_acquisition) self._image_tab.remove_dark.clicked.connect( partial(self.remove_dark, 0)) self.my_colors = ColorScaleInferno() self._image = pg.ImageItem(image=self._image_data, axisOrder='row-major') self._image.setLookupTable(self.my_colors.lut) self._image_tab.graph.addItem(self._image) self._colorbar = ColorbarWidget(self._image) self._image_tab.colorbar.addWidget(self._colorbar) self.track_colors = np.array( [palette.c5, palette.c2, palette.c6, palette.c4]) self.plot_colors = self.track_colors height = self.spectrumlogic().camera_constraints.height for i in range(4): self._track_buttons[i].setCheckable(True) self._track_buttons[i].clicked.connect( partial(self._manage_track_buttons, i)) tracks = self.spectrumlogic().active_tracks if 2 * i < len(tracks): top_pos = tracks[2 * i] bottom_pos = tracks[2 * i + 1] else: top_pos = 0 bottom_pos = 10 color = self.track_colors[i].getRgb() track_color = pg.mkBrush(color[0], color[1], color[2], 100) track = pg.LinearRegionItem( values=[top_pos, bottom_pos], orientation=pg.LinearRegionItem.Horizontal, brush=track_color) track.setBounds([0, height]) track.hide() self._track_selector.append(track) self._image_tab.graph.addItem(track) self._image_tab.image_advanced.setCheckable(True) self._image_tab.image_advanced.clicked.connect( self._manage_image_advanced_button) self._image_advanced_widget = pg.ROI( [0, 0], [camera_width, camera_height], maxBounds=QRectF(QPoint(0, 0), QPoint(camera_width, camera_height))) self._image_advanced_widget.addScaleHandle((1, 0), (0, 1)) self._image_advanced_widget.addScaleHandle((0, 1), (1, 0)) self._image_advanced_widget.hide() self._image_tab.graph.addItem(self._image_advanced_widget) self._image_tab.horizontal_binning.setRange(1, camera_width - 1) self._image_tab.vertical_binning.setRange(1, camera_height - 1) self._image_tab.horizontal_binning.editingFinished.connect( self.set_image_params) self._image_tab.vertical_binning.editingFinished.connect( self.set_image_params) self._image_tab.read_modes.currentTextChanged.connect( self.set_image_params) self._image_tab.acquisition_modes.currentTextChanged.connect( self.set_image_params) self.image_exposure_time_widget.editingFinished.connect( self.set_image_params) self._image_tab.readout_speed.currentTextChanged.connect( self.set_image_params)
def paintEvent(self, event): # Piirtää peliruudun if self.started == False: return qp = QtGui.QPainter() qp.begin(self) xcenter = self.xsize * 7 ycenter = self.ysize * 9 xsize = self.xsize ysize = self.ysize xoffset = self.game.player.offset.x yoffset = self.game.player.offset.y howmanybackgroundpixmaps = self.game.map.x / 2 source = QRectF(0, 0, 500, 1000) pixmap = QPixmap(os.getcwd() + "/assets/" + self.gamebackground) for i in range(int(-howmanybackgroundpixmaps / 2), int(howmanybackgroundpixmaps / 2)): target = QRectF(xcenter + i * 500 + (self.game.player.offset.x/3), -200 , 500, 1000) qp.drawPixmap(target, pixmap, source) for y in range(-10, 7): if y < 0: if (self.game.player.coordinates.y + y - int(yoffset/40)) < 0: continue if y >= 0: if (self.game.player.coordinates.y + y - int(yoffset/40) >= self.game.map.y): continue for x in range(-8, 9): if x < 0: if (self.game.player.coordinates.x + x - int(xoffset/40)< 0): continue if x >= 0: if (self.game.player.coordinates.x + x - int(yoffset/40) >= self.game.map.x): continue if self.game.map.map[self.game.player.coordinates.x + x - int(xoffset/40)][self.game.player.coordinates.y + y - int(yoffset/40)] == PixelType.BLACK: if xoffset % 40 == 0: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + (yoffset % 40), xsize, ysize) else: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + (yoffset % 40), xsize, ysize) source = QRectF(0, 0, 40, 40) pixmap = QPixmap(os.getcwd() + "/assets/" + self.blacktiletexture) qp.drawPixmap(target, pixmap, source) elif self.game.map.map[self.game.player.coordinates.x + x - int(xoffset/40)][self.game.player.coordinates.y + y - int(yoffset/40)] == PixelType.RED: if xoffset % 40 == 0: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + (yoffset % 40), xsize, ysize) else: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + (yoffset % 40), xsize, ysize) source = QRectF(0, 0, 40, 40) pixmap = QPixmap(os.getcwd() + "/assets/" + self.redtiletexture) qp.drawPixmap(target, pixmap, source) elif self.game.map.map[self.game.player.coordinates.x + x - int(xoffset/40)][self.game.player.coordinates.y + y - int(yoffset/40)] == PixelType.GREEN: if xoffset % 40 == 0: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + (yoffset % 40), xsize, ysize) else: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + (yoffset % 40), xsize, ysize) source = QRectF(0, 0, 40, 40) pixmap = QPixmap(os.getcwd() + "/assets/" + self.greentiletexture) qp.drawPixmap(target, pixmap, source) elif self.game.map.map[self.game.player.coordinates.x + x - int(xoffset/40)][self.game.player.coordinates.y + y - int(yoffset/40)] == PixelType.DANGER: if xoffset % 40 == 0: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + ((xoffset % 40)), ycenter + y*ysize + (yoffset % 40), xsize, ysize) else: if yoffset % 40 == 0: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + ((yoffset % 40)+40), xsize, ysize) else: target = QRectF(xcenter + (x)*xsize + (xoffset % 40)-40, ycenter + y*ysize + (yoffset % 40), xsize, ysize) source = QRectF(0, 0, 40, 40) pixmap = QPixmap(os.getcwd() + "/assets/" + self.yellowtiletexture) qp.drawPixmap(target, pixmap, source) else: continue target = QRectF(xcenter, ycenter, xsize, ysize) source = QRectF(0, 0, 40, 40) pixmap = QPixmap(os.getcwd() + "/assets/plr.png") qp.drawPixmap(target, pixmap, source) qp.end()
def set_rect(self, rect: QRectF): self.setPos(rect.topLeft()) self.rect = rect self.rect.moveTo(0.0, 0.0) self.update()
class StickLinkManager(QGraphicsObject): def __init__(self, parent: QGraphicsItem = None): QGraphicsObject.__init__(self, parent) self.source: Optional[StickWidget] = None self.target: Optional[StickWidget] = None self.target_point = QPointF() self.anchored = False self.unused_colors: List[QColor] = [] step = 60 for num_groups in range(24): offset = 0 if num_groups < 6 else step / ((num_groups + 6) // 6) hue = int((num_groups % 6) * step + offset) self.unused_colors.append(QColor.fromHsv(hue, 255, 255, 255)) self.current_link_item: StickLink = None self.stick_links_list: List[StickLink] = [] self.rect = QRectF() self.color_links: Dict[QColor, List[StickLink]] = {} def boundingRect(self): return self.rect def paint(self, painter: QPainter, options: QStyleOptionGraphicsItem, widget=None): pass def set_target(self, point: QPointF): if not self.anchored: if self.current_link_item is not None: self.current_link_item.set_temporary_target(point) self.update() def cancel(self): if self.current_link_item is not None: self.stick_links_list.pop() self.scene().removeItem(self.current_link_item) self.current_link_item.setEnabled(False) self.current_link_item.deleteLater() self.current_link_item = None self.toggle_highlight_target_sticks(False) self.toggle_highlight_source_sticks(True) self.anchored = False self.update() def confirm(self) -> Optional[StickLink]: if self.current_link_item is not None: if self.current_link_item.stick2 is not None: link = self.current_link_item link.confirm_link() self.current_link_item = None return link return None def set_target_stick(self, entered: bool, stick_widget: StickWidget) -> StickLink: if self.current_link_item is None or stick_widget.link_source: return None self.anchored = entered if self.anchored: self.current_link_item.set_target_stick(stick_widget) else: self.current_link_item.set_temporary_target( stick_widget.mapToScene(stick_widget.mid_handle.rect().center())) self.update() return self.current_link_item def set_source_stick(self, sw: StickWidget) -> StickLink: self.current_link_item = StickLink(sw, parent=None) self.stick_links_list.append(self.current_link_item) color = self.unused_colors.pop() self.current_link_item.set_color(color) a = self.color_links.setdefault(color.hue(), []) a.append(self.current_link_item) self.scene().addItem(self.current_link_item) return self.current_link_item def get_new_link_group_color(self) -> QColor: if len(self.unused_colors) > 0: return self.unused_colors.pop() num_groups = len(self.stick_links_list) step = 60 offset = 0 if num_groups < 6 else step / ((num_groups + 6) // 6) hue = int((num_groups % 6) * step + offset) return QColor.fromHsv(hue, 255, 255, 255) def hoverMoveEvent(self, event: QGraphicsSceneHoverEvent) -> None: if self.current_link_item is not None: return found: List[Tuple[float, StickLink, float]] = [] for stick_link in self.stick_links_list: if stick_link == self.current_link_item: continue stick_link.btn_break_link.setVisible(False) stick_link.fade_out(False) e1 = stick_link.line_item.line().unitVector() e1 = e1.p2() - e1.p1() e2 = stick_link.line_item.line().normalVector().unitVector() e2 = e2.p2() - e2.p1() sp = event.scenePos() - stick_link.line_item.line().p1() proj = QPointF.dotProduct(sp, e1) * e1 perp = sp - proj dist = math.sqrt(QPointF.dotProduct(perp, perp)) if dist < 50: t = math.sqrt(QPointF.dotProduct(proj, proj)) / stick_link.line_item.line().length() if 0.1 < t < 0.9 and QPointF.dotProduct(proj, e1) > 0.0: found.append((dist, stick_link, t)) if len(found) > 0: found.sort(key=lambda tup: tup[0]) dist_stick_link_t = found[0] stick_link = dist_stick_link_t[1] stick_link.fade_out(False) t = dist_stick_link_t[2] p = stick_link.line_item.line().pointAt(t) q = stick_link.line_item.mapFromScene(p) stick_link.btn_break_link.setPos(q - QPointF(stick_link.btn_break_link.boundingRect().width() * 0.5, stick_link.btn_break_link.boundingRect().height() * 0.5)) stick_link.btn_break_link.setVisible(True) for stick_link_ in self.stick_links_list: if stick_link_ == stick_link: continue stick_link_.fade_out(True) def remove_link(self, link: StickLink): if link in self.stick_links_list: if link == self.current_link_item: self.current_link_item = None j = self.color_links[link.color.hue()] j.remove(link) if len(j) == 0: self.unused_colors.append(link.color) self.stick_links_list.remove(link) link.remove_sticks() self.scene().removeItem(link) link.setEnabled(False) link.deleteLater() def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent) -> None: super().hoverLeaveEvent(event) def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent) -> None: super().hoverEnterEvent(event) def set_rect(self, rect: QRectF): self.setPos(rect.topLeft()) self.rect = rect self.rect.moveTo(0.0, 0.0) self.update() def change_link_color(self, link: StickLink, color: QColor): j = self.color_links.get(link.color.hue(), []) if len(j) > 0: j.remove(link) link.set_color(color) k = self.color_links.setdefault(color.hue(), []) k.append(link) def remove_all_links(self): lk = self.stick_links_list.copy() for link in lk: self.remove_link(link) lk.clear() def hide_links(self): for link in self.stick_links_list: link.setVisible(False) self.setAcceptHoverEvents(False) def show_links(self): for link in self.stick_links_list: link.setVisible(True) self.setAcceptHoverEvents(True)
def draw(self, painter, xmap, ymap, rect, use_cache=True): """Implements QwtPlotItem.draw(), to render the image on the given painter.""" xp1, xp2, xdp, xs1, xs2, xds = xinfo = xmap.p1(), xmap.p2(), xmap.pDist(), xmap.s1(), xmap.s2(), xmap.sDist() yp1, yp2, ydp, ys1, ys2, yds = yinfo = ymap.p1(), ymap.p2(), ymap.pDist(), ymap.s1(), ymap.s2(), ymap.sDist() dprint(5, "draw:", rect, xinfo, yinfo) self._current_rect = QRectF(QPointF(xs2, ys1), QSizeF(xds, yds)) self._current_rect_pix = QRect(QPoint(*self.lmToPix(xs1, ys1)), QPoint(*self.lmToPix(xs2, ys2))).intersected( self._bounding_rect_pix) dprint(5, "draw:", self._current_rect_pix) # put together tuple describing current mapping mapping = xinfo, yinfo # if mapping has changed w.r.t. cache (i.e. zoom has changed), discard all cached QImages if mapping != self._cache_mapping: dprint(2, "does not match cached mapping, cache is:", self._cache_mapping) dprint(2, "and we have:", mapping) self.clearDisplayCache() self._cache_mapping = mapping t0 = time.time() # check cached QImage for current image key. self.qimg = self._cache_qimage.get(self._image_key) if self.qimg: dprint(5, "QImage found in cache, reusing") # else regenerate image else: # check for cached intensity-mapped data if self._cache_imap is not None: dprint(5, "intensity-mapped data found in cache, reusing") else: if self._cache_interp is not None: dprint(5, "interpolated data found in cache, reusing") else: image = self._image.transpose() if self._data_fortran_order else self._image spline_order = 2 xsamp = abs(xmap.sDist() / xmap.pDist()) / abs(self._dl) ysamp = abs(ymap.sDist() / ymap.pDist()) / abs(self._dm) if max(xsamp, ysamp) < .33 or min(xsamp, ysamp) > 2: spline_order = 1 dprint(2, "regenerating drawing cache, sampling factors are", xsamp, ysamp, "spline order is", spline_order) self._cache_imap = None if self._prefilter is None and spline_order > 1: self._prefilter = interpolation.spline_filter(image, order=spline_order) dprint(2, "spline prefiltering took", time.time() - t0, "secs") t0 = time.time() # make arrays of plot coordinates # xp[0],yp[0] corresponds to pixel 0,0, where 0,0 is the upper-left corner of the plot # the maps are in a funny order (w.r.t. meaning of p1/p2/s1/s2), so the indices here are determined empirically # We also adjust by half-pixel, to get the world coordinate of the pixel _center_ xp = xmap.s1() - (xmap.sDist() / xmap.pDist()) * (0.5 + numpy.arange(int(xmap.pDist()))) yp = ymap.s2() - (ymap.sDist() / ymap.pDist()) * (0.5 + numpy.arange(int(ymap.pDist()))) # now convert plot coordinates into fractional image pixel coordinates xi = self._x0 + (xp - self._l0) / self._dl yi = self._y0 + (yp - self._m0) / self._dm # interpolate image data ### # old code for nearest-neighbour interpolation ### # superceded by interpolation below (we simply round pixel coordinates to go to NN when oversampling) ### xi = xi.round().astype(int) ### oob_x = (xi<0)|(xi>=self._nx) ### xi[oob_x] = 0 ### yi = yi.round().astype(int) ### oob_y = (yi<0)|(yi>=self._ny) ### yi[oob_y] = 0 ### idx = (xi[:,numpy.newaxis]*self._ny + yi[numpy.newaxis,:]).ravel() ### interp_image = self._image.ravel()[idx].reshape((len(xi),len(yi))) ### interp_image[oob_x,:] = 0 ### interp_image[:,oob_y] = 0 ### self._qimage_cache = self.colormap.colorize(interp_image,self._img_range) ### self._qimage_cache_attrs = (rect,xinfo,yinfo) # if either axis is oversampled by a factor of 3 or more, switch to nearest-neighbour interpolation by rounding pixel values if xsamp < .33: xi = xi.round() if ysamp < .33: yi = yi.round() # make [2,nx,ny] array of interpolation coordinates xy = numpy.zeros((2, len(xi), len(yi))) xy[0, :, :] = xi[:, numpy.newaxis] xy[1, :, :] = yi[numpy.newaxis, :] # interpolate. Use NAN for out of range pixels... # for fortran order, tranpose axes for extra speed (flip XY around then) if self._data_fortran_order: xy = xy[-1::-1, ...] if spline_order > 1: interp_image = interpolation.map_coordinates(self._prefilter, xy, order=spline_order, cval=numpy.nan, prefilter=False) else: interp_image = interpolation.map_coordinates(image, xy, order=spline_order, cval=numpy.nan) # ...and put a mask on them (Colormap.colorize() will make these transparent). mask = ~numpy.isfinite(interp_image) self._cache_interp = numpy.ma.masked_array(interp_image, mask) dprint(2, "interpolation took", time.time() - t0, "secs") t0 = time.time() # ok, we have interpolated data in _cache_interp self._cache_imap = self.imap.remap(self._cache_interp) dprint(2, "intensity mapping took", time.time() - t0, "secs") t0 = time.time() # ok, we have intensity-mapped data in _cache_imap self.qimg = self.colormap.colorize(self._cache_imap) dprint(2, "colorizing took", time.time() - t0, "secs") t0 = time.time() if use_cache: # cache the qimage self._cache_qimage[self._image_key] = self.qimg.copy() # now draw the image t0 = time.time() painter.drawImage(xp1, yp2, self.qimg) dprint(2, "drawing took", time.time() - t0, "secs") # when exporting images to PNG cache needs to be cleared if not use_cache: self.clearDisplayCache()