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
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)
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