Example #1
0
 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()))
Example #2
0
 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()))
Example #3
0
    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')))
Example #4
0
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()
Example #5
0
    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)
Example #6
0
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()
Example #7
0
 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()
Example #8
0
 def boundingRect(self):
     return QRectF(0, 0, self._width, self._height)
Example #9
0
    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)
Example #10
0
    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()
Example #11
0
 def set_rect(self, rect: QRectF):
     self.setPos(rect.topLeft())
     self.rect = rect
     self.rect.moveTo(0.0, 0.0)
     self.update()
Example #12
0
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)
Example #13
0
    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()