Exemple #1
0
class UndoDock(DockWidget):

    def __init__(self, parent=None):
        DockWidget.__init__(self, "历史", parent)
        self.setObjectName("undoViewDock")
        self._undo_view = QUndoView(self)
        self._clear_icon = QIcon(":/drive-harddisk.png")

        self._undo_view.setCleanIcon(self._clear_icon)
        self._undo_view.setUniformItemSizes(True)
        self._widget = QWidget(self)
        self._layout = QVBoxLayout(self._widget)
        self._layout.setStretch(0, 0)
        self._layout.setContentsMargins(0, 6, 6, 6)
        self._layout.addWidget(self._undo_view)
        self.setWidget(self._widget)
        self.retranslateUi()

    def set_stack(self, stack: QUndoStack):
        self._undo_view.setStack(stack)

    def set_group(self, group: QUndoGroup):
        self._undo_view.setGroup(group)

    def changeEvent(self, event: QEvent) -> None:
        pass
        # if e.type():
        # self.LanguageChange
        # self.retranslateUi()

    def retranslateUi(self):
        self.setWindowTitle("历史")
        self._undo_view.setEmptyLabel("<空>")
Exemple #2
0
class UndoDock(QDockWidget):

    def __init__(self, undoGroup, parent = None):
        super().__init__(parent)

        self.setObjectName("undoViewDock")
        self.mUndoView = QUndoView(undoGroup, self)
        cleanIcon = QIcon(":images/16x16/drive-harddisk.png")
        self.mUndoView.setCleanIcon(cleanIcon)
        self.mUndoView.setUniformItemSizes(True)
        widget = QWidget(self)
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.addWidget(self.mUndoView)
        self.setWidget(widget)
        self.retranslateUi()

    def changeEvent(self, e):
        super().changeEvent(e)
        x = e.type()
        if x==QEvent.LanguageChange:
            self.retranslateUi()
        else:
            pass

    def retranslateUi(self):
        self.setWindowTitle(self.tr("History"))
        self.mUndoView.setEmptyLabel(self.tr("<empty>"))
Exemple #3
0
class UndoDock(QDockWidget):
    def __init__(self, undoGroup, parent=None):
        super().__init__(parent)

        self.setObjectName("undoViewDock")
        self.mUndoView = QUndoView(undoGroup, self)
        cleanIcon = QIcon(":images/16x16/drive-harddisk.png")
        self.mUndoView.setCleanIcon(cleanIcon)
        self.mUndoView.setUniformItemSizes(True)
        widget = QWidget(self)
        layout = QVBoxLayout(widget)
        layout.setContentsMargins(5, 5, 5, 5)
        layout.addWidget(self.mUndoView)
        self.setWidget(widget)
        self.retranslateUi()

    def changeEvent(self, e):
        super().changeEvent(e)
        x = e.type()
        if x == QEvent.LanguageChange:
            self.retranslateUi()
        else:
            pass

    def retranslateUi(self):
        self.setWindowTitle(self.tr("History"))
        self.mUndoView.setEmptyLabel(self.tr("<empty>"))
Exemple #4
0
class AnnotationsNetworkView(NetworkView):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._undo_stack = QUndoStack(self)
        self._undo_view = QUndoView(self._undo_stack)
        self._undo_view.setCleanIcon(QIcon(":/icons/images/document-save.svg"))
        self._undo_menu = QMenu(self)
        action = QWidgetAction(self._undo_menu)
        action.setDefaultWidget(self._undo_view)
        self._undo_menu.addAction(action)

        self._orig_point = None
        self._item_to_draw = None
        self._item_old_pos = None
        self._item_old_line_or_rect = None
        self._mode = None
        self._dialog = None
        self.setDragMode(QGraphicsView.RubberBandDrag)

    def setScene(self, scene: AnnotationsNetworkScene):
        scene.editAnnotationItemRequested.connect(
            self.on_edit_annotation_item_requested)
        self._undo_stack.clear()
        super().setScene(scene)

    def addAnnotationItem(self, item: QGraphicsItem, pos: QPointF):
        scene = self.scene()
        if scene is None:
            return

        if isinstance(item, (ArrowItem, RectItem, EllipseItem)):
            item.setPen(
                QPen(Qt.black, scene.getDefaultPenSizeFromRect(),
                     Qt.SolidLine))

        self._undo_stack.push(AddCommand(item, scene))
        item.setPos(pos)
        scene.annotationAdded.emit(item)

        return item

    def addAnnotationLine(self, line: QLineF,
                          pos: QPointF) -> Union[ArrowItem, None]:
        return self.addAnnotationArrow(line, pos, False, False)

    def addAnnotationArrow(self,
                           line: QLineF,
                           pos: QPointF,
                           has_head=True,
                           has_tail=False) -> Union[ArrowItem, None]:
        item = ArrowItem(line, has_head=has_head, has_tail=has_tail)
        return self.addAnnotationItem(item, pos)

    def addAnnotationRect(self, rect: QRectF,
                          pos: QPointF) -> Union[RectItem, None]:
        return self.addAnnotationItem(RectItem(rect), pos)

    def addAnnotationEllipse(
            self, rect: QRectF,
            pos: QPointF) -> Union[QGraphicsEllipseItem, None]:
        return self.addAnnotationItem(EllipseItem(rect), pos)

    def addAnnotationText(self, text: str, font: QFont,
                          pos: QPointF) -> Union[TextItem, None]:
        item = TextItem(text)
        item.setFont(font)
        return self.addAnnotationItem(item, pos)

    def setDrawMode(self, mode):
        self._mode = mode
        if mode is None:
            self.setDragMode(QGraphicsView.RubberBandDrag)
            self._orig_point = None
        else:
            self.setDragMode(QGraphicsView.NoDrag)

    def mode(self):
        return self._mode

    def undoView(self):
        return self._undo_view

    def undoStack(self):
        return self._undo_stack

    def undoMenu(self):
        return self._undo_menu

    def loadAnnotations(self, buffer: bytes) -> None:
        if not buffer:
            return

        scene = self.scene()
        if scene is None:
            return

        def read(len):
            nonlocal buffer, pos
            data = buffer[pos:pos + len]
            pos += len
            return data

        pos = 0
        data = read(1)

        while data:
            if data in (b'L', b'A'):
                x1, y1, x2, y2 = struct.unpack("ffff", read(16))
                line = QLineF(0., 0., x2, y2)
                if data == b'L':
                    self.addAnnotationLine(line, QPointF(x1, y1))
                elif data == b'A':
                    data, = struct.unpack("B", read(1))
                    has_head = (data >> 0) & 1
                    has_tail = (data >> 1) & 1
                    self.addAnnotationArrow(line, QPointF(x1, y1), has_head,
                                            has_tail)
            elif data in (b'R', b'C'):
                x, y, w, h = struct.unpack("ffff", read(16))
                rect = QRectF(0., 0., w, h)
                if data == b'R':
                    self.addAnnotationRect(rect, QPointF(x, y))
                elif data == b'C':
                    self.addAnnotationEllipse(rect, QPointF(x, y))
            elif data == b'T':
                x, y, len = struct.unpack("ffH", read(10))
                text = read(len).decode()
                font_size, = struct.unpack("H", read(2))
                font = QFont()
                font.setPointSize(font_size)
                self.addAnnotationText(text, font, QPointF(x, y))

            data = read(1)

        self._undo_stack.setClean()

    def saveAnnotations(self) -> bytes:
        scene = self.scene()
        if scene is None:
            return b''

        buffer = b''
        for item in scene.items():
            if isinstance(item, ArrowItem):
                char = b'A' if item.hasHead() or item.hasTail() else b'L'
                line = item.line()
                pos = item.pos()
                buffer += char
                buffer += struct.pack("ffff", pos.x(), pos.y(), line.x2(),
                                      line.y2())
                if char == b'A':
                    buffer += struct.pack("B",
                                          item.hasHead() + item.hasTail() * 2)
            elif isinstance(item, (RectItem, EllipseItem)):
                char = b'R' if isinstance(item, RectItem) else b'C'
                rect = item.rect()
                pos = item.pos()
                buffer += char
                buffer += struct.pack("ffff", pos.x(), pos.y(), rect.width(),
                                      rect.height())
            elif isinstance(item, TextItem):
                pos = item.pos()
                text = item.text()
                font = item.font()
                buffer += b'T'
                buffer += struct.pack("ff", pos.x(), pos.y())
                buffer += struct.pack("H", len(text))
                buffer += str.encode(text)
                buffer += struct.pack("H", font.pointSize())

        return buffer

    def deleteSelectedAnnotations(self):
        scene = self.scene()
        if scene is None:
            return

        for item in scene.annotationsLayer.childItems():
            if item.isSelected():
                self._undo_stack.push(DeleteCommand(item, self))

    def on_edit_annotation_item_requested(self, item: QGraphicsItem):
        # Edit text item
        if isinstance(item, TextItem):

            def edit_text_item(result):
                if result == QDialog.Accepted:
                    old_text = item.text()
                    old_font = item.font()
                    text, font_size = self._dialog.getValues()
                    font = QFont()
                    font.setPointSize(font_size)
                    item.setText(text)
                    item.setFont(font)
                    self._undo_stack.push(
                        EditTextCommand(item, old_text, old_font.pointSize(),
                                        self.scene()))

            self._dialog = TextItemInputDialog(self)
            self._dialog.setValues(item.text(), item.font().pointSize())
            self._dialog.finished.connect(edit_text_item)
            self._dialog.open()

    def mouseDoubleClickEvent(self, event: QMouseEvent) -> None:
        scene = self.scene()
        if scene:
            if self._mode is None or self._mode == MODE_TEXT:
                item = self.itemAt(event.pos())

                if self._mode is None:
                    self.on_edit_annotation_item_requested(item)

                # Add a text item at mouse position
                elif self._mode == MODE_TEXT:

                    def add_text_item(result):
                        if result == QDialog.Accepted:
                            text, font_size = self._dialog.getValues()
                            font = QFont()
                            font.setPointSize(font_size)
                            self.addAnnotationText(text, font, pos)

                    pos = self.mapToScene(event.pos())
                    self._dialog = TextItemInputDialog(self)
                    self._dialog.setValues("",
                                           scene.getDefaultFontSizeFromRect())
                    self._dialog.finished.connect(add_text_item)
                    self._dialog.open()
        else:
            super().mouseDoubleClickEvent(event)

    def mousePressEvent(self, event: QMouseEvent) -> None:
        scene = self.scene()
        if scene:
            # Edit item (line, rect, ellipse, etc)
            if self._mode is None:
                self._item_to_draw = item = self.itemAt(event.pos())
                if item:
                    self._item_old_pos = item.pos()
                    event_pos = self.mapToScene(event.pos()) - item.pos()

                    # Edit Arrows head and tails
                    if isinstance(item, ArrowItem) and event.modifiers(
                    ) & Qt.ShiftModifier == Qt.ShiftModifier:
                        tol = item.pen().width() * 2.
                        if (event_pos -
                                item.line().p1()).manhattanLength() < tol:
                            has_tail = item.hasTail()
                            item.setTail(not has_tail)
                            self._undo_stack.push(
                                EditArrowCommand(item, has_tail,
                                                 item.hasHead(), self.scene()))
                            scene.arrowEdited.emit(item)
                        elif (event_pos -
                              item.line().p2()).manhattanLength() < tol:
                            has_head = item.hasHead()
                            item.setHead(not has_head)
                            self._undo_stack.push(
                                EditArrowCommand(item, item.hasTail(),
                                                 has_head, self.scene()))
                            scene.arrowEdited.emit(item)

                    # Resize Line and Arrow
                    if isinstance(item, ArrowItem):
                        tol = item.pen().width() * 2.
                        self._item_old_line_or_rect = item.line()
                        if (event_pos -
                                item.line().p1()).manhattanLength() < tol:
                            self._orig_point = item.line().p2() + item.pos()
                        elif (event_pos -
                              item.line().p2()).manhattanLength() < tol:
                            self._orig_point = item.line().p1() + item.pos()

                    # Resize Rect and Ellipse
                    elif isinstance(item, (EllipseItem, RectItem)):
                        self._item_old_line_or_rect = item.rect()
                        tol = item.pen().width()
                        if (event_pos -
                                item.rect().topLeft()).manhattanLength() < tol:
                            self._orig_point = item.rect().bottomRight(
                            ) + item.pos()
                        elif (event_pos - item.rect().bottomRight()
                              ).manhattanLength() < tol:
                            self._orig_point = item.rect().topLeft(
                            ) + item.pos()
                        elif (event_pos -
                              item.rect().topRight()).manhattanLength() < tol:
                            self._orig_point = item.rect().bottomLeft(
                            ) + item.pos()
                        elif (event_pos - item.rect().bottomLeft()
                              ).manhattanLength() < tol:
                            self._orig_point = item.rect().topRight(
                            ) + item.pos()

            # Define starting point of item (line, rect, ellipse, etc)
            elif self._mode != MODE_TEXT:
                self._orig_point = self.mapToScene(event.pos())

        super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
        scene = self.scene()
        if scene and self._orig_point is not None:
            # Edit line or arrow
            if self._mode in (MODE_LINE, MODE_ARROW) \
                    or (self._mode is None and isinstance(self._item_to_draw, QGraphicsLineItem)):
                pos = self.mapToScene(event.pos())
                x = pos.x() - self._orig_point.x()
                y = pos.y() - self._orig_point.y()
                if self._item_to_draw and self._orig_point == self._item_to_draw.line(
                ).p2() + self._item_to_draw.pos():
                    # Moving line around head
                    line = QLineF(0, 0, -x, -y)
                    point = pos
                else:
                    line = QLineF(0, 0, x, y)
                    point = None

                if self._item_to_draw is None:
                    if self._mode == MODE_LINE:
                        self._item_to_draw = self.addAnnotationLine(
                            line, self._orig_point)
                    elif self._mode == MODE_ARROW:
                        self._item_to_draw = self.addAnnotationArrow(
                            line, self._orig_point)
                else:
                    self._item_to_draw.setLine(line)
                    if point is not None:
                        self._item_to_draw.setPos(point)
                return

            # Edit rect or circle
            elif self._mode in (MODE_RECT, MODE_ELLIPSE) \
                    or (self._mode is None and isinstance(self._item_to_draw, (RectItem, EllipseItem))):
                pos = self.mapToScene(event.pos())
                width = pos.x() - self._orig_point.x()
                height = pos.y() - self._orig_point.y()
                dx = 0 if width >= 0 else width
                dy = 0 if height >= 0 else height
                rect = QRectF(0, 0, abs(width), abs(height))
                point = self._orig_point + QPointF(dx, dy)
                if self._item_to_draw is None:
                    if self._mode == MODE_RECT:
                        self._item_to_draw = self.addAnnotationRect(
                            rect, point)
                    elif self._mode == MODE_ELLIPSE:
                        self._item_to_draw = self.addAnnotationEllipse(
                            rect, point)
                else:
                    self._item_to_draw.setRect(rect)
                    self._item_to_draw.setPos(point)
                return

        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
        if self._mode is None and self._item_to_draw is not None and event.button(
        ) == Qt.LeftButton:
            if isinstance(self._item_old_line_or_rect, QRectF):
                line_or_rect = self._item_to_draw.rect()
            elif isinstance(self._item_old_line_or_rect, QLineF):
                line_or_rect = self._item_to_draw.line()
            else:
                line_or_rect = None

            if line_or_rect is None or self._item_old_line_or_rect == line_or_rect:
                if self._item_old_pos != self._item_to_draw.pos():
                    self._undo_stack.push(
                        MoveCommand(self._item_to_draw, self._item_old_pos,
                                    self.scene()))
            else:
                self._undo_stack.push(
                    ResizeCommand(self._item_to_draw, self._item_old_pos,
                                  self._item_old_line_or_rect, self.scene()))
        self._item_old_pos = None
        self._item_old_line_or_rect = None
        self._item_to_draw = None
        self._orig_point = None
        super().mouseReleaseEvent(event)