Exemple #1
0
class ImageWidget(QGraphicsView):

    def __init__(self, *args, **kwargs):
        super(ImageWidget, self).__init__(*args, **kwargs)
        self.setObjectName('graphicsView')
        self.setBackgroundBrush(QColor(25, 25, 25))
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
                            QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing)
        self.setCacheMode(self.CacheBackground)
        self.setViewportUpdateMode(self.SmartViewportUpdate)

        self._zoomDelta = 0.1
        self._pixmap = None
        self._item = QGraphicsPixmapItem()
        self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable |
                            QGraphicsPixmapItem.ItemIsMovable)

        self._scene = QGraphicsScene(self)
        self.setScene(self._scene)

        self._scene.addItem(self._item)

    def pixmap(self):
        return self._pixmap

    def setPixmap(self, pixmap, fitIn=True):
        self._pixmap = pixmap
        self._item.setPixmap(pixmap)
        self._item.update()
        self.setSceneDims()
        if fitIn:
            self.fitInView(QRectF(self._item.pos(), QSizeF(
                self._pixmap.size())), Qt.KeepAspectRatio)
        self.update()

    def setSceneDims(self):
        if not self._pixmap:
            return
        self.setSceneRect(
            QRectF(
                QPointF(0, 0),
                QPointF(self._pixmap.width(), self._pixmap.height()))
        )

    def fitInView(self, rect, flags=Qt.IgnoreAspectRatio):
        if not self.scene() or rect.isNull():
            return
        unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
        self.scale(1 / unity.width(), 1 / unity.height())
        viewRect = self.viewport().rect()
        sceneRect = self.transform().mapRect(rect)
        xratio = viewRect.width() / sceneRect.width()
        yratio = viewRect.height() / sceneRect.height()
        if flags == Qt.KeepAspectRatio:
            xratio = yratio = min(xratio, yratio)
        elif flags == Qt.KeepAspectRatioByExpanding:
            xratio = yratio = max(xratio, yratio)
        self.scale(xratio, yratio)
        self.centerOn(rect.center())

    def getPos(self, pos):
        spos = self.mapToScene(pos)
        pos = self._item.mapFromScene(spos)
        return pos.toPoint() if self._item.boundingRect().contains(pos) else QPoint()

    def wheelEvent(self, event):
        if event.angleDelta().y() > 0:
            self.zoomIn()
        else:
            self.zoomOut()

    def zoomIn(self):
        """放大"""
        self.zoom(1 + self._zoomDelta)

    def zoomOut(self):
        """缩小"""
        self.zoom(1 - self._zoomDelta)

    def zoom(self, scaleFactor):
        """
        # 缩放
        :param scaleFactor: 缩放的比例因子
        """
        factor = self.transform().scale(
            scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width()
        if factor < 0.07 or factor > 100:
            # 防止过大过小
            return
        self.scale(scaleFactor, scaleFactor)
class ImageView(QGraphicsView):
    def __init__(self, mode):
        # parameter
        self.mode = mode

        # create scene
        self.scene = QGraphicsScene()
        super().__init__(self.scene)
        self.setScene(self.scene)

        # use in draw sequence
        # - items : Scene 에 추가된 item list
        # - 임시 item 들은 obj에 관리된다.
        self.items = []
        self.obj = {}  # View 출력을 위한 임시 저장장소
        self._object_reset()

        self.rect_p0 = None
        self.rect_p1 = None
        self.select = []

        # use in data load
        self.image = None
        self.pixmap = None
        self.image_item = None
        self.metadata = None

        # use in key event
        self._zoom = 0
        self.KEY_SHIFT = False
        self.KEY_ALT = False
        self.KEY_CTRL = False
        self.KEY_MOUSE_LEFT = False

        # backup
        self.last_mask = None
        self.last_polygon = None

        # QGraphicsView setting
        self.setDragMode(self.mode.VIEW_DRAG_MODE)
        self.setMouseTracking(True)

        # remove scrollbar option
        if not self.mode.SCROLLBAR_VIEW:
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def load_image(self, image, metadata):
        self.reset()
        self.image = image

        # Qimage 를 가져온다.
        image = self.image.get_image()

        # pixmap data 생성
        pixmap = QPixmap(image)

        self.pixmap = pixmap
        self.image_item = QGraphicsPixmapItem(pixmap)
        self.scene.addItem(self.image_item)

        self._load_metadata(metadata)

    def refresh(self):
        """ 현재 작업중인 임시 정보를 모두 삭제하고, 현재 설정값을 재설정한다.

        """
        # Object 초기화
        self._object_reset()

        # Mode 설정
        self.setDragMode(self.mode.VIEW_DRAG_MODE)

        # Data Reset
        self.rect_p0 = None
        self.rect_p1 = None
        self.select.clear()

        # Key 초기화
        self.KEY_SHIFT = False
        self.KEY_ALT = False
        self.KEY_MOUSE_LEFT = False

        # Mask Draw
        self._mask_draw()

        # update
        self._update()

    def reset(self):
        """ 이미지 정보를 포함하여 모든 정보를 삭제한다.

        """
        # Scene 에 표시되는 물체들을 제거한다.
        self._delete_items(self.items)
        self.scene.removeItem(self.image_item)

        # backup 초기화
        self.last_mask = None
        self.last_polygon = None

        # image 정보 초기화
        self.image = None
        self.pixmap = None
        self.image_item = None
        self.metadata = None

        # refresh
        self.refresh()

    def restore(self):
        print('restore')
        if self.last_mask is not None and self.image is not None:
            self.image.restore_mask(self.last_mask)
            if self.last_polygon is not None:
                self._polygon_delete(self.last_polygon)
            print('1')

            self.last_mask = None
            self.last_polygon = None
            self._mask_draw()

    def callback_selected(self, e, item):
        """ Image Item 객체가 선택되었을 때, View 에 직접 호출 할 수 있는 함수

            Image Item 객체의 event 함수에서 호출된다.
        """
        pass

    def callback_polygon_double_click(self, polygon_item):
        if self.KEY_CTRL and self.mode.CURRENT == 0:
            # delete polygon
            # 1. remove text item
            if polygon_item.text_item is not None:
                self.items.remove(polygon_item.text_item)
                self.scene.removeItem(polygon_item.text_item)

            # 2. remove polygon
            self.items.remove(polygon_item)
            self.scene.removeItem(polygon_item)
        elif self.KEY_CTRL:
            print('2')
            self._polygon_delete(polygon_item)

    def callback_polygon_hover_enter(self, polygon_item):
        if self.mode.CURRENT == 0:
            self.setDragMode(QGraphicsView.NoDrag)

    def callback_polygon_hover_move(self, polygon_item):
        if self.mode.CURRENT == 0:
            self.setDragMode(QGraphicsView.NoDrag)

    def callback_polygon_hover_leave(self, polygon_item):
        if self.mode.CURRENT == 0:
            self.setDragMode(self.mode.VIEW_DRAG_MODE)

    def _load_metadata(self, metadata):
        """ Image Annotation 을 읽어와 View 에 출력한다.


     """
        self.metadata = metadata
        polygons = metadata.out_polygons_to_list()
        for polygon in polygons:
            [attribute, x, y, id] = polygon
            assert len(x) == len(y), "Invalid polygon information in metadata"
            qpol = QPolygonF()
            for idx in range(len(x)):
                qpol.append(QPointF(x[idx], y[idx]))

            self._polygon_draw(qpol, attribute, id)

    def _update(self):
        pass

    def _delete_items(self, items):
        for item in items:
            self.scene.removeItem(item)
        items.clear()

    def _object_reset(self):
        """ Object 를 초기화한다.

            obj Dictionary 에는 view 그리기에 필요한 임시값들이 저장되어있다.
        """
        # polygon : 현재 작성중인 polygon 좌표들을 QPointF list 형태로 가지고 있는다.
        if 'polygon' not in self.obj:
            self.obj['polygon'] = []
        else:
            self.obj['polygon'].clear()

        # lines : View 에 출력되는 직선들이다.
        if 'lines' not in self.obj:
            self.obj['lines'] = []
        else:
            self._delete_items(self.obj['lines'])
            self.obj['lines'].clear()

        # mask : magicwand mask
        if 'mask' in self.obj and self.obj['mask'] is not None:
            self.scene.removeItem(self.obj['mask'])
        self.obj['mask'] = None

        # paint sign
        if 'paint' in self.obj and self.obj['paint'] is not None:
            self.scene.removeItem(self.obj['paint'])
        self.obj['paint'] = None

    def _polygon_check(self, polygon_list):
        """ polygon 의 시작 위치와 종료 위치를 이용하여 생성 조건을 반환한다.

        """
        if polygon_list is None or len(polygon_list) < 3:
            return False

        # 거리 계산
        dx = polygon_list[-1].x() - polygon_list[0].x()
        dy = polygon_list[-1].y() - polygon_list[0].y()
        d = dx * dx + dy * dy

        if d < self.mode.POLYGON_END_THRESHOLD:
            # 마지막 위치 수정
            del (polygon_list[-1])
            polygon_list.append(polygon_list[0])
            return True
        else:
            return False

    def _polygon_draw(self, polygon, attribute, id=-1):
        """ polygon 을 그린다.

            polygon 객체를 인수로 받아서 scene 에 추가한다.
        """
        # Call by Value
        attribute = attribute.copy()

        pen = QPen(self.mode.DRAW_PEN_COLOR, 3)
        if attribute['name'] in self.mode.POLYGON_BRUSH_COLOR:
            brush = QBrush(self.mode.POLYGON_BRUSH_COLOR[attribute['name']])
        else:
            brush = QBrush(self.mode.DRAW_BRUSH_COLOR)

        # polygon_item 생성
        polygon_item = PolygonItem(self, polygon, attribute, id)
        polygon_item.setPen(pen)
        polygon_item.setBrush(brush)

        # scene 에 추가
        self.items.append(polygon_item)
        self.scene.addItem(polygon_item)

        # polygon location
        self._polygon_location(polygon_item)

        # QGraphicPolygonItem 반환
        return polygon_item

    def _polygon_location(self, polygon_item):
        polygon = polygon_item.polygon
        attribute = polygon_item.attribute

        # Set text location
        text_locate, compare_list_y = [], []

        for i in range(len(polygon) - 1):
            compare_list_y.append(polygon[i].toPoint().y())
        top_of_polygon_y = min(compare_list_y)
        index = compare_list_y.index(top_of_polygon_y)

        text_locate.append(polygon[index].toPoint().x())
        text_locate.append(polygon[index].toPoint().y())
        text_locate[0] = text_locate[0] - 5
        text_locate[1] = text_locate[1] - 5

        # add text item
        if 'name' not in attribute:
            attribute['name'] = 'none'
        item = self.scene.addText(attribute['name'], QFont('Arial', 10))
        self.items.append(item)
        polygon_item.text_item = item
        item.setPos(text_locate[0], text_locate[1])

    def _polygon_add_info(self, polygon_item):
        """ polygon 을 metadata 에 추가한다.

        """
        region_attribute = polygon_item.attribute
        polygon = polygon_item.polygon

        xs = []
        ys = []
        for point in polygon:
            xs.append(int(point.x()))
            ys.append(int(point.y()))
        polygon_item.id = self.metadata.add_polygon(region_attribute, xs, ys)

    def _polygon_delete(self, polygon_item):
        # delete polygon
        # 1. remove metadata
        print('0')
        print(polygon_item.id)
        self.metadata.delete_polygon(polygon_item.id)

        # 2. remove text item
        if polygon_item.text_item is not None:
            self.items.remove(polygon_item.text_item)
            self.scene.removeItem(polygon_item.text_item)

        # 3. remove polygon
        self.items.remove(polygon_item)
        self.scene.removeItem(polygon_item)

    def _mask_draw(self):
        if self.image is None:
            return

        # QBitmap 생성
        bitmap = self.image.get_mask()

        # QPixmap 생성
        pixmap = QPixmap(self.pixmap)
        pixmap.fill(self.mode.DRAW_MASK_COLOR)

        # pixmap 에 Mask를 씌운다.
        pixmap.setMask(bitmap)

        # Item 생성 및 추가
        if self.obj['mask'] is not None:
            self.scene.removeItem(self.obj['mask'])
        self.obj['mask'] = QGraphicsPixmapItem(pixmap)
        self.scene.addItem(self.obj['mask'])

    def _mask_reset(self):
        # Reset Mask
        if self.image is not None:
            self.image.reset_mask()

    def _paint_draw_sign(self, x, y):
        # draw paint sign
        brush = QBrush(self.mode.DRAW_MASK_COLOR
                       ) if self.mode.DRAW_PAINT_MODE else QBrush(
                           QColor(255, 255, 255, 50))
        pen = QPen(QColor(0, 0, 0), 3)

        # Item 생성 및 추가
        width = self.mode.DRAW_PAINT_WIDTH
        if self.obj['paint'] is not None:
            self.scene.removeItem(self.obj['paint'])
        self.obj['paint'] = QGraphicsEllipseItem(int(x - width / 2),
                                                 int(y - width / 2), width,
                                                 width)
        self.obj['paint'].setBrush(brush)
        self.obj['paint'].setPen(pen)
        self.scene.addItem(self.obj['paint'])

    def _draw_sequence_press(self, pos, out_of_range):
        """ view 에 item 들을 그린다.

            mode 로 view 에 그리는 방법을 다르게 설정 할 수 있다.
            press, move, release sequence 함수들은 서로 의존적이다.
            mousePressEvent 에서 호출된다.
        """
        if out_of_range:
            return

        last_pos = self.rect_p0
        self.rect_p0 = pos

        # Mode 1. polygon draw
        if self.mode.CURRENT == 1:
            # 새로운 위치를 지정 및 기억한다.
            polygon_list = self.obj['polygon']
            polygon_list.append(pos)

            # 조건을 확인하여 polygon 을 그린다.
            if self._polygon_check(polygon_list):
                # polygon list 를 이용하여 QPolygonF 객체 생성
                polygon = QPolygonF([QPointF(point) for point in polygon_list])

                # polygon 을 그린다.
                polygon_item = self._polygon_draw(
                    polygon, self.mode.POLYGON_CURRENT_ATTRIBUTE)

                # metadata 추가 / id  할당
                self._polygon_add_info(polygon_item)

                # polygon list 정보 삭제
                polygon_list.clear()

                # lines 삭제
                self._delete_items(self.obj['lines'])

                # 좌표 초기화
                self.rect_p0 = None
                self.rect_p1 = None

            elif self.rect_p1 != None:
                # 임시 표시 직선을 그린다.
                pen = QPen(self.mode.DRAW_PEN_COLOR, self.mode.DRAW_PEN_WIDTH) if not out_of_range \
                    else QPen(self.mode.DRAW_PEN_COLOR_WARNNING, self.mode.DRAW_PEN_WIDTH)

                line = QLineF(self.rect_p0.x(), self.rect_p0.y(), last_pos.x(),
                              last_pos.y())
                self.obj['lines'].insert(0, self.scene.addLine(line, pen))

        # Mode 2. Magic Wand
        elif self.mode.CURRENT == 2:
            # option 설정
            if self.KEY_SHIFT and self.KEY_ALT:
                option = 'and'
            elif self.KEY_SHIFT:
                option = 'or'
            elif self.KEY_SHIFT:
                option = 'sub'
            else:
                option = 'select'

            # 현재 point mask 생성
            self.image.set_tolerance(self.mode.DRAW_MASK_TOLERANCE)
            self.image.set_mask(int(pos.x()), int(pos.y()), option=option)

            # mask 를 그린다.
            self._mask_draw()

        # Mode 3. Paint
        elif self.mode.CURRENT == 3:
            # paint or erase
            if self.mode.DRAW_PAINT_MODE:
                self.image.paint_mask(int(pos.x()), int(pos.y()),
                                      self.mode.DRAW_PAINT_WIDTH)
            else:
                self.image.erase_mask(int(pos.x()), int(pos.y()),
                                      self.mode.DRAW_PAINT_WIDTH)
            self._mask_draw()
            self._paint_draw_sign(int(pos.x()), int(pos.y()))

    def _draw_sequence_move(self, pos, out_of_range):
        """ view 에 item 들을 그린다.

            mouseMoveEvent 에서 호출된다.
        """

        # polygon draw
        if self.mode.CURRENT == 1:
            if self.rect_p0 is None:
                return
            else:
                self.rect_p1 = pos

            # QPen 선택
            pen = QPen(self.mode.DRAW_PEN_COLOR, self.mode.DRAW_PEN_WIDTH) if not out_of_range \
                else QPen(self.mode.DRAW_PEN_COLOR_WARNNING, self.mode.DRAW_PEN_WIDTH)

            # 마지막에 그렸던 직선 삭제
            if len(self.obj['lines']) > 0:
                self.scene.removeItem(self.obj['lines'][-1])
                del (self.obj['lines'][-1])

            # 새로운 직선을 그린다
            line = QLineF(self.rect_p0.x(), self.rect_p0.y(), self.rect_p1.x(),
                          self.rect_p1.y())
            self.obj['lines'].append(self.scene.addLine(line, pen))

        elif self.mode.CURRENT == 3 and not out_of_range:
            # paint or erase
            if self.KEY_MOUSE_LEFT:
                if self.mode.DRAW_PAINT_MODE:
                    self.image.paint_mask(int(pos.x()), int(pos.y()),
                                          self.mode.DRAW_PAINT_WIDTH)
                else:
                    self.image.erase_mask(int(pos.x()), int(pos.y()),
                                          self.mode.DRAW_PAINT_WIDTH)

                self._mask_draw()
            self._paint_draw_sign(int(pos.x()), int(pos.y()))

    def _mouse_check(self, e):
        """ 현재 좌표와 좌표가 이미지 위에 위치하는지 여부를 반환한다.

            Image 가 할당된 이후에 호출될 수 있어야 한다.
            QMouseEvent 가 주어져야한다.
        """
        assert self.image_item is not None, "No image to check"

        # 마우스 위치를 Scene 좌표로 변환한다.
        pos = self.mapToScene(e.pos())

        # 변환된 Scene 좌표를 item 좌표로 변환한다.
        pos = self.image_item.mapFromScene(pos)

        # image item 좌표 범위를 QRectF 클래스로 받아온다.
        rect = self.image_item.boundingRect()

        # 좌표와 좌표 위치 정보 반환.
        return pos, not rect.contains(pos)

    def keyPressEvent(self, e):
        super().keyPressEvent(e)
        if self.image is None:
            return

        if e.key() == Qt.Key_Escape:
            self.last_mask = self.image.backup_mask()
            self._mask_reset()
            self.refresh()
        if e.key() == Qt.Key_Shift:
            self.KEY_SHIFT = True
            self.mode.DRAW_PAINT_MODE = not self.mode.DRAW_PAINT_MODE
        if e.key() == Qt.Key_Alt:
            self.KEY_ALT = True
        if e.key() == Qt.Key_Control:
            self.KEY_CTRL = True
        if e.key() == Qt.Key_Space and self.image is not None:
            # Mask To Polygon (with backup)
            self.last_mask = self.image.backup_mask()
            polygon = self.image.get_polygon()
            if polygon is not None:
                polygon_item = self._polygon_draw(
                    polygon, self.mode.POLYGON_CURRENT_ATTRIBUTE)
                self._polygon_add_info(polygon_item)
                self.last_polygon = polygon_item

                self._mask_reset()
                self.refresh()

    def keyReleaseEvent(self, e):
        if e.key() == Qt.Key_Shift:
            self.KEY_SHIFT = False
        if e.key() == Qt.Key_Alt:
            self.KEY_ALT = False
        if e.key() == Qt.Key_Control:
            self.KEY_CTRL = False

    def showEvent(self, e):
        self._update()
        super().showEvent(e)

    def wheelEvent(self, e):
        if self.KEY_CTRL:
            if self.mode.CURRENT == 3:
                pos, out_of_range = self._mouse_check(e)
                if not out_of_range:
                    # pain wheel
                    self._paint_draw_sign(int(pos.x()), int(pos.y()))
            return

        # zoom in / out 구현
        if e.angleDelta().y() > 0 and self._zoom < 10:
            self.scale(1.25, 1.25)
            self._zoom += 1
        elif e.angleDelta().y() < 0 and self._zoom > -10:
            self.scale(0.8, 0.8)
            self._zoom -= 1

    def mousePressEvent(self, e):
        # QGraphicsView.ScrollHandDrag middle mouse
        super().mousePressEvent(e)

        if self.KEY_CTRL:
            return

        # 좌표와 정보를 받아온다.
        if self.image_item is not None:
            pos, out_of_range = self._mouse_check(e)
        else:
            return

        # Reset Mask
        if self.mode.CURRENT not in [0, 2, 3]:
            self._mask_reset()
            self._mask_draw()

        # Left Button
        if e.button() == Qt.LeftButton:
            self.KEY_MOUSE_LEFT = True

            # 좌표를 이용하여 view 를 그린다.
            self._draw_sequence_press(pos, out_of_range)

    def mouseMoveEvent(self, e):
        super().mouseMoveEvent(e)

        # Left Button
        if self.image_item is not None:
            # 좌표와 정보를 받아온다.
            pos, out_of_range = self._mouse_check(e)

            # 좌표를 이용하여 view 를 그린다.
            self._draw_sequence_move(pos, out_of_range)

    def mouseReleaseEvent(self, e):
        super().mouseReleaseEvent(e)

        # 좌표와 정보를 받아온다.
        if self.image_item is not None:
            pos, out_of_range = self._mouse_check(e)
        else:
            return

        # Left Button
        if e.button() == Qt.LeftButton:
            self.KEY_MOUSE_LEFT = False
Exemple #3
0
class ASCGraphicsView(QGraphicsView):
    scale_signal = pyqtSignal('float', 'float')
    set_focus_point_signal = pyqtSignal('int', 'int', 'int')
    set_focus_point_percent_signal = pyqtSignal('float', 'float', 'float')
    move_focus_point_signal = pyqtSignal('int', 'int', 'int')
    # x, y, z, BRUSH_TYPE, BRUSH_SIZE, ERASE
    paint_anno_on_point_signal = pyqtSignal('int', 'int', 'int', 'int', 'int',
                                            'bool', 'bool')

    def __init__(self, parent=None):
        super(ASCGraphicsView, self).__init__(parent)
        self.scene = QGraphicsScene(self)

        self.raw_img_item = QGraphicsPixmapItem()
        self.raw_img_item.setZValue(0)
        self.anno_img_item = QGraphicsPixmapItem()
        self.anno_img_item.setZValue(1)

        self.cross_bar_v_line_item = QGraphicsLineItem()
        self.cross_bar_h_line_item = QGraphicsLineItem()
        self.cross_bar_v_line_item.setZValue(100)
        self.cross_bar_h_line_item.setZValue(100)
        self.cross_bar_v_line_item.setPen(
            QPen(Qt.blue, 0, Qt.DotLine, Qt.FlatCap, Qt.RoundJoin))
        self.cross_bar_h_line_item.setPen(
            QPen(Qt.blue, 0, Qt.DotLine, Qt.FlatCap, Qt.RoundJoin))

        self.paint_brush_circle_item = QGraphicsEllipseItem()
        self.paint_brush_rect_item = QGraphicsPolygonItem()
        self.paint_brush_circle_item.setZValue(10)
        self.paint_brush_rect_item.setZValue(11)
        self.paint_brush_circle_item.setVisible(False)
        self.paint_brush_rect_item.setVisible(False)
        self.paint_brush_circle_item.setPen(
            QPen(Qt.red, 0, Qt.DotLine, Qt.FlatCap, Qt.RoundJoin))
        self.paint_brush_rect_item.setPen(
            QPen(Qt.red, 0, Qt.DotLine, Qt.FlatCap, Qt.RoundJoin))

        self.scene.addItem(self.raw_img_item)
        self.scene.addItem(self.anno_img_item)
        self.scene.addItem(self.cross_bar_v_line_item)
        self.scene.addItem(self.cross_bar_h_line_item)
        self.scene.addItem(self.paint_brush_circle_item)
        self.scene.addItem(self.paint_brush_rect_item)

        self.setScene(self.scene)
        self.setViewport(QOpenGLWidget())

        self._last_button_press = Qt.NoButton
        self._last_pos_middle_button = None
        self._last_pos_right_button = None

        self._brush_stats = {'type': BRUSH_TYPE_NO_BRUSH, 'size': 5}

        self.setResizeAnchor(QGraphicsView.AnchorViewCenter)

        self.slice_scroll_bar = None
        self.image_size = None
        self.is_valid = False

    def clear(self):
        """before loading new image"""
        self._last_pos_middle_button = None
        self._last_pos_right_button = None
        self._last_button_press = Qt.NoButton
        self.raw_img_item.setPixmap(QPixmap())
        self.anno_img_item.setPixmap(QPixmap())
        self.paint_brush_circle_item.setVisible(False)
        self.paint_brush_rect_item.setVisible(False)
        self.image_size = None
        self.is_valid = False

    def init_view(self, image_size):
        """after loading new image"""
        self.is_valid = True
        self.image_size = image_size
        self.slice_scroll_bar = self.parent().findChild(
            QScrollBar,
            self.objectName()[0] + 'SliceScrollBar')

        trans_mat = item2scene_transform[self.objectName()[0]]
        self.raw_img_item.setTransform(trans_mat)
        self.anno_img_item.setTransform(trans_mat)
        self.cross_bar_v_line_item.setTransform(trans_mat)
        self.cross_bar_h_line_item.setTransform(trans_mat)
        self.paint_brush_rect_item.setTransform(trans_mat)
        self.paint_brush_circle_item.setTransform(trans_mat)

        self.fitInView(self.raw_img_item, Qt.KeepAspectRatio)
        self.paint_brush_circle_item.setVisible(False)
        self.paint_brush_rect_item.setVisible(False)

    @property
    def brush_stats(self):
        return self._brush_stats

    @brush_stats.setter
    def brush_stats(self, stats_tuple):
        b_type, size = stats_tuple
        if b_type in [
                BRUSH_TYPE_NO_BRUSH, BRUSH_TYPE_CIRCLE_BRUSH,
                BRUSH_TYPE_RECT_BRUSH
        ]:
            self._brush_stats['type'] = b_type
            self._brush_stats['size'] = size
        if b_type != BRUSH_TYPE_NO_BRUSH:
            self.setMouseTracking(True)
        else:
            self.setMouseTracking(False)

    def update_brush_preview(self, x, y, out_of_sight=False):
        if not self.is_valid or self.brush_stats[
                'type'] == BRUSH_TYPE_NO_BRUSH or out_of_sight:
            self.paint_brush_rect_item.setVisible(False)
            self.paint_brush_circle_item.setVisible(False)
            return

        center = self.anno_img_item.mapFromScene(self.mapToScene(x, y))

        start_x = self.raw_img_item.boundingRect().topLeft().x()
        start_y = self.raw_img_item.boundingRect().topLeft().y()
        end_x = self.raw_img_item.boundingRect().bottomRight().x()
        end_y = self.raw_img_item.boundingRect().bottomRight().y()
        center.setX(min(max(start_x, center.x()), end_x) + 0.5)
        center.setY(min(max(start_y, center.y()), end_y) + 0.5)
        top_left_x = int(center.x() - self.brush_stats['size'] / 2)
        top_left_y = int(center.y() - self.brush_stats['size'] / 2)
        rect = QRectF(top_left_x, top_left_y, self.brush_stats['size'],
                      self.brush_stats['size'])
        if self.brush_stats['type'] == BRUSH_TYPE_CIRCLE_BRUSH:
            self.paint_brush_rect_item.setVisible(False)
            self.paint_brush_circle_item.setVisible(True)
            self.paint_brush_circle_item.setRect(rect)
        if self.brush_stats['type'] == BRUSH_TYPE_RECT_BRUSH:
            self.paint_brush_rect_item.setVisible(True)
            self.paint_brush_circle_item.setVisible(False)
            self.paint_brush_rect_item.setPolygon(QPolygonF(rect))

    def anno_paint(self, x, y, erase=False, new_step=False):
        pos_on_item = self.raw_img_item.mapFromScene(self.mapToScene(x, y))
        if self.objectName() == 'aGraphicsView':
            paint_point = [pos_on_item.y(), pos_on_item.x(), 999999]
        if self.objectName() == 'sGraphicsView':
            paint_point = [999999, pos_on_item.y(), pos_on_item.x()]
        if self.objectName() == 'cGraphicsView':
            paint_point = [pos_on_item.y(), 999999, pos_on_item.x()]
        self.paint_anno_on_point_signal.emit(math.floor(paint_point[0]),
                                             math.floor(paint_point[1]),
                                             math.floor(paint_point[2]),
                                             self.brush_stats['type'],
                                             self.brush_stats['size'], erase,
                                             new_step)

    @pyqtSlot('int')
    def on_slice_scroll_bar_changed(self, value):
        if not self.is_valid:
            return
        ratios = [-1, -1, -1]
        ratio = (value - self.slice_scroll_bar.minimum()) / \
                (self.slice_scroll_bar.maximum() - self.slice_scroll_bar.minimum())
        if self.objectName() == 'aGraphicsView':
            ratios[2] = ratio
        if self.objectName() == 'sGraphicsView':
            ratios[0] = ratio
        if self.objectName() == 'cGraphicsView':
            ratios[1] = ratio
        self.set_focus_point_percent_signal.emit(ratios[0], ratios[1],
                                                 ratios[2])

    @pyqtSlot('int', 'int')
    def set_brush_stats(self, b_type, size):
        self.brush_stats = [b_type, size]

    @pyqtSlot('int', 'int', 'int')
    def set_cross_bar(self, x, y, z):
        if self.objectName() == 'aGraphicsView':
            cross_bar_x = y
            cross_bar_y = x
            slice_bar_ratio = z / self.image_size[2]
        if self.objectName() == 'sGraphicsView':
            cross_bar_x = z
            cross_bar_y = y
            slice_bar_ratio = x / self.image_size[0]
        if self.objectName() == 'cGraphicsView':
            cross_bar_x = z
            cross_bar_y = x
            slice_bar_ratio = y / self.image_size[1]
        # cross line in voxel center
        cross_bar_x = cross_bar_x + 0.5
        cross_bar_y = cross_bar_y + 0.5
        start_x = self.raw_img_item.boundingRect().topLeft().x()
        start_y = self.raw_img_item.boundingRect().topLeft().y()
        end_x = self.raw_img_item.boundingRect().bottomRight().x()
        end_y = self.raw_img_item.boundingRect().bottomRight().y()
        self.cross_bar_v_line_item.setLine(cross_bar_x, start_y, cross_bar_x,
                                           end_y)
        self.cross_bar_h_line_item.setLine(start_x, cross_bar_y, end_x,
                                           cross_bar_y)

        slice_bar_value = round(slice_bar_ratio * (self.slice_scroll_bar.maximum() - self.slice_scroll_bar.minimum())) \
                          + self.slice_scroll_bar.minimum()
        self.slice_scroll_bar.setValue(slice_bar_value)

    def mousePressEvent(self, event: QtGui.QMouseEvent):
        self._last_button_press = event.button()

        if self.brush_stats['type'] == BRUSH_TYPE_NO_BRUSH:
            if event.button() == Qt.LeftButton:
                item_coord_pos = self.raw_img_item.mapFromScene(
                    self.mapToScene(event.pos()))
                if self.objectName() == 'aGraphicsView':
                    new_focus_point = [
                        item_coord_pos.y(),
                        item_coord_pos.x(), 999999
                    ]
                if self.objectName() == 'sGraphicsView':
                    new_focus_point = [
                        999999, item_coord_pos.y(),
                        item_coord_pos.x()
                    ]
                if self.objectName() == 'cGraphicsView':
                    new_focus_point = [
                        item_coord_pos.y(), 999999,
                        item_coord_pos.x()
                    ]
                self.set_focus_point_signal.emit(
                    math.floor(new_focus_point[0]),
                    math.floor(new_focus_point[1]),
                    math.floor(new_focus_point[2]))
            elif event.button() == Qt.MiddleButton:
                self._last_pos_middle_button = event.pos()
                self.setCursor(Qt.ClosedHandCursor)
            elif event.button() == Qt.RightButton:
                self._last_pos_right_button = event.pos()
            else:
                super(ASCGraphicsView, self).mousePressEvent(event)
        if self.brush_stats['type'] in [
                BRUSH_TYPE_CIRCLE_BRUSH, BRUSH_TYPE_RECT_BRUSH
        ]:
            if event.button() == Qt.LeftButton:
                self.anno_paint(event.x(),
                                event.y(),
                                erase=False,
                                new_step=True)
            elif event.button() == Qt.MiddleButton:
                self._last_pos_middle_button = event.pos()
                self.setCursor(Qt.ClosedHandCursor)
            elif event.button() == Qt.RightButton:
                self.anno_paint(event.x(),
                                event.y(),
                                erase=True,
                                new_step=True)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent):
        if self.brush_stats['type'] == BRUSH_TYPE_NO_BRUSH:
            if self._last_button_press == Qt.LeftButton:
                item_coord_pos = self.raw_img_item.mapFromScene(
                    self.mapToScene(event.pos()))
                if self.objectName() == 'aGraphicsView':
                    new_focus_point = [
                        item_coord_pos.y(),
                        item_coord_pos.x(), 999999
                    ]
                if self.objectName() == 'sGraphicsView':
                    new_focus_point = [
                        999999, item_coord_pos.y(),
                        item_coord_pos.x()
                    ]
                if self.objectName() == 'cGraphicsView':
                    new_focus_point = [
                        item_coord_pos.y(), 999999,
                        item_coord_pos.x()
                    ]
                self.set_focus_point_signal.emit(
                    math.floor(new_focus_point[0]),
                    math.floor(new_focus_point[1]),
                    math.floor(new_focus_point[2]))
            elif self._last_button_press == Qt.MiddleButton:
                delta_x = event.x() - self._last_pos_middle_button.x()
                delta_y = event.y() - self._last_pos_middle_button.y()
                self.horizontalScrollBar().setValue(
                    self.horizontalScrollBar().value() - delta_x)
                self.verticalScrollBar().setValue(
                    self.verticalScrollBar().value() - delta_y)
                self._last_pos_middle_button = event.pos()
            elif self._last_button_press == Qt.RightButton:
                delta = event.pos().y() - self._last_pos_right_button.y()
                scale = 1 - float(delta) / float(self.size().height())
                self.scale_signal.emit(scale, scale)
                self._last_pos_right_button = event.pos()
            else:
                super(ASCGraphicsView, self).mouseMoveEvent(event)
        if self.brush_stats['type'] in [
                BRUSH_TYPE_CIRCLE_BRUSH, BRUSH_TYPE_RECT_BRUSH
        ]:
            self.update_brush_preview(event.x(), event.y())
            if self._last_button_press == Qt.LeftButton:
                self.anno_paint(event.x(),
                                event.y(),
                                erase=False,
                                new_step=False)
            elif self._last_button_press == Qt.MiddleButton:
                delta_x = event.x() - self._last_pos_middle_button.x()
                delta_y = event.y() - self._last_pos_middle_button.y()
                self.horizontalScrollBar().setValue(
                    self.horizontalScrollBar().value() - delta_x)
                self.verticalScrollBar().setValue(
                    self.verticalScrollBar().value() - delta_y)
                self._last_pos_middle_button = event.pos()
            elif self._last_button_press == Qt.RightButton:
                self.anno_paint(event.x(),
                                event.y(),
                                erase=True,
                                new_step=False)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent):
        self._last_button_press = Qt.NoButton

        if self.brush_stats['type'] == BRUSH_TYPE_NO_BRUSH:
            if event.button() == Qt.MiddleButton:
                self.setCursor(Qt.ArrowCursor)
        if self.brush_stats['type'] in [
                BRUSH_TYPE_CIRCLE_BRUSH, BRUSH_TYPE_RECT_BRUSH
        ]:
            if event.button() == Qt.LeftButton:
                pass
            elif event.button() == Qt.RightButton:
                pass
        else:
            super(ASCGraphicsView, self).mouseReleaseEvent(event)

    def wheelEvent(self, event: QtGui.QWheelEvent):
        # super(ASCGraphicsView, self).wheelEvent(event)
        if self.objectName() == 'aGraphicsView':
            if event.angleDelta().y() > 0:
                self.move_focus_point_signal.emit(0, 0, -1)
            elif event.angleDelta().y() < 0:
                self.move_focus_point_signal.emit(0, 0, 1)
        if self.objectName() == 'sGraphicsView':
            if event.angleDelta().y() > 0:
                self.move_focus_point_signal.emit(-1, 0, 0)
            elif event.angleDelta().y() < 0:
                self.move_focus_point_signal.emit(1, 0, 0)
        if self.objectName() == 'cGraphicsView':
            if event.angleDelta().y() > 0:
                self.move_focus_point_signal.emit(0, -1, 0)
            elif event.angleDelta().y() < 0:
                self.move_focus_point_signal.emit(0, 1, 0)

    def leaveEvent(self, event: QtCore.QEvent):
        self._last_button_press = Qt.NoButton
        if self.brush_stats['type'] == BRUSH_TYPE_NO_BRUSH:
            super(ASCGraphicsView, self).leaveEvent(event)
        else:
            self.update_brush_preview(0, 0, out_of_sight=True)
Exemple #4
0
class RemoteArea(QGraphicsView):
    def __init__(self):
        super(RemoteArea, self).__init__()
        self.setFixedSize(800, 600)
        self.setStyleSheet(
            "background-color: rgb(255, 255, 255); border:1px solid black")
        self.zoomscale = 1
        self.file_path = ""
        self.pic = None
        self.pixel = None
        self.di = {
            "苗木": [0, 229, 254],
            "果园,柑橘": [0, 254, 162],
            "休闲农业": [254, 212, 0],
            "粮田,水稻": [0, 5, 255],
            "其它": [255, 101, 0],
            "经作": [0, 255, 50],
            "Background": [0, 0, 0],
            "菜田,蔬菜": [0, 117, 255],
            "林地,廊道,生态林": [178, 254, 0],
            "水产": [67, 255, 0],
            "未确定用途": [255, 0, 16]
        }

    def load_image(self, image):
        self.pic = QtGui.QPixmap.fromImage(image)
        # self.pic2 = QtGui.QPixmap.fromImage(image)

        self.item = QGraphicsPixmapItem(self.pic)
        self.item.setFlag(QGraphicsItem.ItemIsMovable)
        # self.item2 = QGraphicsPixmapItem(self.pic2)

        self.scene = QGraphicsScene()  # 创建场景
        self.scene.addItem(self.item)
        # self.scene.addItem(self.item2)
        self.setScene(self.scene)
        self.item.setScale(self.zoomscale)

    # def mousePressEvent(self, event):
    #     if self.pic:
    #         if event.button() == Qt.LeftButton:
    #             self.mousePressPos = event.pos()
    #             self.mouseIsPress = True
    #             print(self.mousePressPos)
    #
    # def mouseReleaseEvent(self, event):
    #     if self.pic:
    #         if event.buttons() == Qt.LeftButton:
    #             self.mouseIsPress = False
    #
    # def mouseMoveEvent(self, event):
    #     if self.pic:
    #         if self.mouseIsPress:
    #             moveDistance = event.pos() - self.mousePressPos
    #             print(moveDistance.x())
    #             print(moveDistance.y())
    #             # self.scene.setSceneRect(self.item.x()+moveDistance.x(),self.item.y()+moveDistance.y(),self.item.)
    #             self.item.moveBy(self.item.x() + moveDistance.x(), self.item.y() + moveDistance.y())

    def wheelEvent(self, event):
        if self.pic:
            angle = event.angleDelta() / 8  # 返回QPoint对象,为滚轮转过的数值,单位为1/8度
            angleX = angle.x()  # 水平滚过的距离(此处用不上)
            angleY = angle.y()  # 竖直滚过的距离
            if angleY < 0:
                self.zoomscale = self.zoomscale - angleY / 360
                if self.zoomscale <= 0:
                    self.zoomscale = 0.2
                self.item.setScale(self.zoomscale)
                self.other_pic.item.setScale(self.zoomscale)
                # print("滚轮上滚")
            else:  # 滚轮下滚
                self.zoomscale = self.zoomscale - angleY / 360
                if self.zoomscale >= 1.8:
                    self.zoomscale = 1.8
                self.item.setScale(self.zoomscale)
                self.other_pic.item.setScale(self.zoomscale)

                # print("鼠标滚轮下滚")  # 响应测试语句

    def contextMenuEvent(self, event):
        if self.pic:
            self.pixel = self.item.mapFromScene(self.mapToScene(
                event.pos()))  #转换为相对像素位置,也就是点击图片像素点位置
            x = int(self.pixel.x())
            y = int(self.pixel.y())
            print("x:" + str(x), "y:" + str(y))
            mask_pic_pixel = list(self.mask_pic[y][x])
            type_name = list(self.di.keys())[list(
                self.di.values()).index(mask_pic_pixel)]
            print(type_name)
            self.menu = QMenu(self)
            Qaction = self.menu.addAction(type_name)
            action = self.menu.exec_(self.mapToGlobal(event.pos()))
            if action == Qaction:
                pass