コード例 #1
0
 def __init__(self, route, parent):
     QGraphicsScene.__init__(self, parent)
     self.route_points = route.routePoints()  # len == legCount + 1
     background_map_item = QGraphicsPixmapItem(QPixmap(world_map_pixmap))
     rect = background_map_item.boundingRect()
     self._lon_factor = rect.width() / 360
     self._lat_factor = -rect.height() / 180
     background_map_item.setOffset(-rect.width() / 2, -rect.height() / 2)
     self.map_bounding_rect = background_map_item.boundingRect()
     self.addItem(background_map_item)
     self.point_items = []
     for p in self.route_points:
         item = RoutePointItem()
         item.setPos(self.scenePoint(p.coordinates))
         self.point_items.append(item)
         self.addItem(item)
     item = RoutePointCircleItem(3, route_colour_departure)
     item.setPos(self.scenePoint(self.route_points[0].coordinates))
     self.addItem(item)
     item = RoutePointCircleItem(5, route_colour_arrival)
     item.setPos(self.scenePoint(self.route_points[-1].coordinates))
     self.addItem(item)
     self.leg_items = []
     for i in range(route.legCount()):
         item = RouteLegItem(self.route_points[i], self.route_points[i + 1])
         self.leg_items.append(item)
         self.addItem(item)
コード例 #2
0
    def drawImage(self, image):
        height, width, dim = image.shape
        image = QImage(image, width, height, dim * width, QImage.Format_RGB888)
        image_item = QGraphicsPixmapItem(QPixmap.fromImage(image))
        self.scene = QGraphicsScene()

        __width = image_item.boundingRect().width()
        __height = image_item.boundingRect().height()
        self.graphicsResize(__width, __height)

        self.scene.addItem(image_item)
        self.imageView.setScene(self.scene)
コード例 #3
0
class Fan(QObject):
    """ Define a class to show a picture of a fan, for animation.
    
    To define a pyqtProperty for animation, the base class should be a QObject
    or any other inherited classes, like QWidget.
    Then, add a QGraphicsPixmapItem to host the picture.
    """
    def __init__(self, parent=None):
        super().__init__(parent)
        self.pixmap_item = QGraphicsPixmapItem(
            QPixmap(cwd + '/guiunits/imags/pon56gdemo/fan.png'))
        self.pixmap_item.setTransformOriginPoint(
            self.pixmap_item.boundingRect().center())
        #self.clickedSgnlWrapper = itemClickedSgnlWrapper()
        #self.clicked = self.clickedSgnlWrapper.sgnl
        #self.pixmap_item.mousePressEvent = self.clickEventHandler

    def clickEventHandler(self, event):
        print('emitting signal')
        self.clicked.emit()

    def _set_rotation_dgr(self, dgr):
        self.pixmap_item.setRotation(dgr)

    def fanAnimation(self):
        anim = QPropertyAnimation(self, b'rotation')
        anim.setDuration(1000)
        anim.setStartValue(0)
        anim.setEndValue(360)
        anim.setLoopCount(-1)
        return anim

    # define a property named as 'rotation', and designate a setter function.
    rotation = pyqtProperty(float, fset=_set_rotation_dgr)
コード例 #4
0
	def cloneBody(self, bodyspecName, dropPos, itemId=None, width=0):
		bodyDef = self.bodies[bodyspecName];
		if not itemId:
			if bodyspecName not in self.nameIndex:
				self.nameIndex[bodyspecName] = 0;
			self.nameIndex[bodyspecName] += 1;
			itemId = "{}{}".format(bodyspecName, self.nameIndex[bodyspecName]);
		body = BodyItem(itemId, bodyspecName, 2);
		self.bodyInstances.append(body);
		body.setPos(dropPos);
		group = QGraphicsItemGroup(body);
		self.renderScene.addItem(body);
		width = width*self.UNITS_PER_METER or self.DEFAULT_BODY_SIZE;

		for shape in bodyDef["shapes"]:
			vertices = shape["vertices"];
			if shape["type"] == "POLYGON":
				newItem = QGraphicsPolygonItem(QPolygonF(vertices));
			if shape["type"] == "CIRCLE":
				p1, p2 = vertices
				radius = math.hypot(p2.x()-p1.x(), p2.y()-p1.y());
				newItem = QGraphicsEllipseItem(p1.x()-radius, p1.y()-radius, radius*2, radius*2);
			pen = QPen();
			pen.setWidth(0);			
			newItem.setPen(pen);
			newItem.setParentItem(group);
		bounding = group.childrenBoundingRect();
		imagePath = None;
		height = 0;
		if (bodyDef["image"]):
			imagePath = bodyDef["image"];
			pixmap = QPixmap(imagePath);
			body.setPixmap(pixmap);
			pm = QGraphicsPixmapItem(pixmap.scaledToWidth(width), body);
			body.setImg(pm);
			pm.setFlags(QGraphicsItem.ItemStacksBehindParent);
			pm.setOffset(0, -pm.boundingRect().height());
			group.setScale(width/self.TRANSCOORD_X);
			height = pm.boundingRect().height();
		else:
			group.setScale(width/bounding.width());
			height = bounding.height();
		for item in body.childItems():
			item.setPos(item.pos().x(), item.pos().y() + height)
		body.updateBorder();

		return body;
コード例 #5
0
    def setImage(self, filePath: str) -> None:
        """
        Convert `filaPath` to an instance of QPixmap and add
        it to the scene.
        """
        pixmap = self.convertToPixmap(filePath)

        item = QGraphicsPixmapItem()
        item.setPixmap(pixmap)
        item.setTransformationMode(Qt.SmoothTransformation)
        self._update_scene(item, item.boundingRect())
コード例 #6
0
ファイル: kkitQGraphics.py プロジェクト: dilawar/moose-gui
class FuncItem(KineticsDisplayItem):
    name = constants.ITEM
    """Class for displaying Functions"""
    #fontMetrics = None
    font = QtGui.QFont(KineticsDisplayItem.defaultFontName)
    font.setPointSize(KineticsDisplayItem.defaultFontSize)
    fontMetrics = QtGui.QFontMetrics(font)

    def __init__(self, mobj, parent):
        super(FuncItem, self).__init__(mobj, parent)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        iconmap_file = (os.path.join(config.settings[config.KEY_ICON_DIR],
                                     'classIcon/Function.png'))
        self.funcImage = QtGui.QImage(iconmap_file).scaled(15, 33)
        self.bg = QGraphicsRectItem(self)
        self.bg.setAcceptHoverEvents(True)
        self.gobj = QGraphicsPixmapItem(
            QtGui.QPixmap.fromImage(self.funcImage), self.bg)
        self.gobj.setAcceptHoverEvents(True)
        self.gobj.mobj = self.mobj
        funcdoc = (moose.element(self.mobj.path)).expr
        self.gobj.setToolTip(funcdoc)

    def setDisplayProperties(self, x, y, textcolor, bgcolor):
        """Set the display properties of this item."""
        poolt = ["ZombieBufPool", "BufPool", "ZombiePool", "Pool"]
        if self.gobj.mobj.parent.className in poolt:
            self.setGeometry(0, 30,
                             self.gobj.boundingRect().width(),
                             self.gobj.boundingRect().height())
        else:
            self.setGeometry(x, y,
                             self.gobj.boundingRect().width(),
                             self.gobj.boundingRect().height())
        self.bg.setBrush(QtGui.QBrush(bgcolor))
        self.setFlag(QGraphicsItem.ItemIsMovable, False)

    def refresh(self, scale):
        pass

    def boundingRect(self):
        ''' reimplimenting boundingRect for redrawning '''
        return QtCore.QRectF(0, 0,
                             self.gobj.boundingRect().width(),
                             self.gobj.boundingRect().height())

    def updateSlot(self):
        return None

    def updateColor(self, bgcolor):
        return None

    def updateRect(self, ratio):
        return None

    def returnColor(self):
        return (self.bg.brush().color())

    def updateValue(self, gobj, funcdoc='Not Available'):
        self.gobj.setToolTip(funcdoc)
コード例 #7
0
class AnnotationScene(QGraphicsScene):
    def __init__(self, parent=None):
        super(AnnotationScene, self).__init__(parent)
        self.image_item = QGraphicsPixmapItem()
        self.image_item.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
        self.image_item.setZValue(9)
        self.addItem(self.image_item)
        self.polygon_item = None

    def load_image(self, pixmap):
        self.image_item.setPixmap(pixmap)
        self.setSceneRect(self.image_item.boundingRect())

    def setCurrentInstruction(self):
        self.polygon_item = PolygonAnnotation()
        self.addItem(self.polygon_item)
コード例 #8
0
    def __loadStaticImages(self, static_images):

        for image_info in static_images:
            pixmap = QPixmap(FileInfo().getPath(FileInfo.FileDataType.IMAGE,
                                                image_info.name))
            height = image_info.height
            width = image_info.width

            if height is None:
                if width is not None:
                    pixmap = pixmap.scaledToWidth(width)
            elif width is None:
                pixmap = pixmap.scaledToHeight(height)
            else:
                pixmap = pixmap.scaled(width, height)

            image_item = QGraphicsPixmapItem(pixmap)

            image_item.setPos(image_info.x, image_info.y)

            brect = image_item.boundingRect()
            image_item.setOffset(-brect.width() / 2, -brect.height() / 2)

            self.__ui.view.scene().addItem(image_item)
コード例 #9
0
class LooseStripBayScene(QGraphicsScene):
    def __init__(self, parent):
        QGraphicsScene.__init__(self, parent)
        self.gui = parent
        self.bg_item = None
        self.compact_strips = False
        self.fillBackground()
        wref = 5 * loose_strip_width
        href = .75 * wref
        self.addRect(QRectF(-wref / 2, -href / 2, wref, href),
                     pen=QPen(Qt.NoPen))  # avoid empty scene
        self.strip_items = EmptyGraphicsItem()
        self.addItem(self.strip_items)
        self.strip_items.setZValue(1)  # gets strips on top of bg_item
        # External signal connections below. CAUTION: these must all be disconnected on widget deletion
        signals.selectionChanged.connect(self.updateSelection)
        signals.colourConfigReloaded.connect(self.fillBackground)
        env.strips.stripMoved.connect(self.removeInvisibleStripItems)

    def disconnectAllSignals(self):
        signals.selectionChanged.disconnect(self.updateSelection)
        signals.colourConfigReloaded.disconnect(self.fillBackground)
        env.strips.stripMoved.disconnect(self.removeInvisibleStripItems)

    def getStrips(self):
        return [item.strip for item in self.strip_items.childItems()]

    def fillBackground(self):
        self.setBackgroundBrush(settings.colour('loose_strip_bay_background'))

    def clearBgImg(self):
        if self.bg_item != None:
            self.removeItem(self.bg_item)
            self.bg_item = None

    def setBgImg(self, pixmap, scale):  # pixmap None to clear background
        self.clearBgImg()
        self.bg_item = QGraphicsPixmapItem(pixmap, None)
        rect = self.bg_item.boundingRect()
        self.bg_item.setScale(scale * loose_strip_width / rect.width())
        self.bg_item.setOffset(-rect.center())
        self.addItem(self.bg_item)

    def setCompactStrips(self, b):
        self.compact_strips = b
        for item in self.strip_items.childItems():
            item.setCompact(b)

    def updateSelection(self):
        for item in self.strip_items.childItems():
            item.setZValue(1 if item.strip is selection.strip else 0)
            item.update()

    def placeNewStripItem(self, strip, pos):
        item = LooseStripItem(strip, self.compact_strips)
        item.setPos(pos)
        self.addStripItem(item)

    def deleteStripItem(self, strip):
        self.removeItem(
            next(item for item in self.strip_items.childItems()
                 if item.strip is strip))

    def removeInvisibleStripItems(self, strip):
        for item in self.strip_items.childItems():
            if not item.isVisible():
                self.removeItem(item)

    def deleteAllStripItems(self):
        for item in self.strip_items.childItems():
            self.removeItem(item)

    def addStripItem(self, item):
        item.setParentItem(self.strip_items)

    def dropEvent(self, event):
        if event.mimeData().hasFormat(strip_mime_type):
            strip = env.strips.fromMimeDez(event.mimeData())
            try:  # maybe it was already inside this bay
                item = next(item for item in self.strip_items.childItems()
                            if item.strip is strip)
                item.setPos(event.scenePos())
                item.setVisible(True)
            except StopIteration:
                env.strips.repositionStrip(strip, None)
                self.placeNewStripItem(strip, event.scenePos())
            signals.selectionChanged.emit()
            event.acceptProposedAction()

    def mousePressEvent(self, event):
        if self.mouseGrabberItem() == None:
            selection.deselect()
            event.accept()
        QGraphicsScene.mousePressEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        QGraphicsScene.mouseDoubleClickEvent(self, event)
        if not event.isAccepted(
        ):  # avoid creating when double clicking on a strip item
            event.accept()
            strip = new_strip_dialog(self.gui, None)
            if strip != None:
                self.placeNewStripItem(strip, event.scenePos())
                selection.selectStrip(strip)

    def dragMoveEvent(self, event):
        pass  # Scene's default impl. ignores the event when no item is under mouse (this enables mouse drop on scene)
コード例 #10
0
 def _load_pixmap(self, pixmap: QPixmap, reload_only: bool) -> None:
     """Load new pixmap into the graphics scene."""
     item = QGraphicsPixmapItem()
     item.setPixmap(pixmap)
     item.setTransformationMode(Qt.SmoothTransformation)
     self._update_scene(item, item.boundingRect(), reload_only)
コード例 #11
0
class GraphicsView(QGraphicsView):
    def __init__(self, parent=None):
        super(GraphicsView, self).__init__(parent)
        self.graphics_scene = QGraphicsScene()
        self.pixmap_item = QGraphicsPixmapItem()
        self.graphics_scene.addItem(self.pixmap_item)
        self.setScene(self.graphics_scene)
        self.image = None
        self.image_max = 255
        self.image_range = 255
        self.window_min, self.window_max = 0, 255
        self.shift = 0
        self.scroll = None
        self.current_language = 'en_GB'
        self.context_menu = QMenu()
        self.make_windowing_optional = False
        if self.make_windowing_optional:
            self.use_windowing_action = QAction(
                QCoreApplication.translate('MainWindow', 'Use Windowing'))
            self.use_windowing_action.setCheckable(True)
            self.use_windowing_action.setChecked(True)
            self.use_windowing_action.toggled.connect(
                self.on_use_windowing_action)
            self.context_menu.addAction(self.use_windowing_action)
        self.reset_window_action = QAction(
            QCoreApplication.translate('MainWindow', 'Reset Window'))
        self.reset_window_action.setCheckable(False)
        self.reset_window_action.triggered.connect(self.on_reset_window_action)
        self.context_menu.addAction(self.reset_window_action)

    def set_image(self, image, update_values=False):
        self.image = np.copy(image)
        if update_values:
            self.update_values_from_image(self.image)
        self.update()
        self.resizeEvent()

    def update_values_from_image(self, image):
        if image.min() < 0:
            self.shift = abs(image.min())
        self.image_max = image.max() + self.shift
        self.image_range = image.max() - image.min() + self.shift
        self.window_min, self.window_max = image.min() + self.shift, image.max(
        ) + self.shift

    def update(self):
        if self.image is None: return
        image = np.interp(np.copy(self.image + self.shift),
                          (self.window_min, self.window_max),
                          (0, 255)).astype(np.uint8)
        self.graphics_scene.removeItem(self.graphics_scene.items()[0])
        self.pixmap_item = QGraphicsPixmapItem(QPixmap(array2qimage(image)))
        self.graphics_scene.addItem(self.pixmap_item)

    def mouseMoveEvent(self, event):
        if self.make_windowing_optional and not self.use_windowing_action.isChecked(
        ):
            return
        window_center = self.image_max * event.pos().y() / self.height()
        half_window_width = 0.5 * self.image_range * event.pos().x(
        ) / self.width()
        self.window_min, self.window_max = window_center + half_window_width * np.array(
            [-1, 1])
        self.update()

    def wheelEvent(self, event):
        if self.scroll is not None:
            delta = np.sign(
                event.angleDelta().y())  # * 0.01 * self.scroll.maximum()
            self.scroll.setValue(self.scroll.value() + delta)

    def resizeEvent(self, event=None):
        self.fitInView(self.pixmap_item.boundingRect(), Qt.KeepAspectRatio)

    def contextMenuEvent(self, event):
        self.context_menu.exec(event.globalPos())

    def on_use_windowing_action(self):
        if not self.use_windowing_action.isChecked():
            self.on_reset_window_action()

    def on_reset_window_action(self):
        self.update_values_from_image(self.image)
        self.update()

    def change_language(self, lang):
        self.reset_window_action.setText(
            QCoreApplication.translate('MainWindow', 'Reset Window'))
        if self.make_windowing_optional:
            self.use_windowing_action.setText(
                QCoreApplication.translate('MainWindow', 'Use Windowing'))
コード例 #12
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)
コード例 #13
0
ファイル: GameArea.py プロジェクト: thehighestmath/sea_battle
class GameArea(QWidget):
    #constants of widget
    RATIO_WITH_SHIPLIST = 11 / 14
    RATIO_WITHOUT_SHIPLIST = 1
    EPS = 0.3

    #signals
    shipPlaced = pyqtSignal(Ship)

    def __init__(self, parent=None):
        super(GameArea, self).__init__(parent)
        self.__ui = Ui_GameArea()
        self.__ui.setupUi(self)

        self.__ratio = self.RATIO_WITH_SHIPLIST
        self.__scaleFactor = 1

        self.controller = Controller()
        self.controller._accept = self.__accept
        self.controller._decline = self.__decline
        self.__gameModel = None

        self.__shipList = {
            "boat": ShipListItem(length=1, name="boat", count=4),
            "destroyer": ShipListItem(length=2, name="destroyer", count=3),
            "cruiser": ShipListItem(length=3, name="cruiser", count=2),
            "battleship": ShipListItem(length=4, name="battleship", count=1),
        }

        # resources
        self.__cellImages = {"intact": None, "hit": None, "miss": None}

        self.__sprites = {"explosion": None, "splash": None}

        self.__originalTileSize = 0
        self.tileSize = 0

        self.__shipListImage = QImage()
        self.__counterImage = QImage()

        # drawing items
        self.__placedShips = []
        self.__field = [QGraphicsPixmapItem() for _ in range(100)]
        self.__letters = [QGraphicsTextItem() for _ in range(10)]
        self.__numbers = [QGraphicsTextItem() for _ in range(10)]
        for i in range(10):
            self.__letters[i].setDefaultTextColor(QColor.fromRgb(0, 0, 0))
            self.__numbers[i].setDefaultTextColor(QColor.fromRgb(0, 0, 0))

        self.__spriteAnimations = []

        self.__shipListItem = QGraphicsPixmapItem()
        self.__ghostShip = QGraphicsPixmapItem(
        )  # data: 0 - rotation; 1 - ShipListItem
        self.__placer = QGraphicsRectItem()
        self.__dragShip = False

        self.__targetPixmap = None
        self.__targets = []
        self.__scanEffect = None

        # prepare Qt objects
        self.__scene = QGraphicsScene()
        self.__loadResources()
        self.__initGraphicsView()
        self.__adjustedToSize = 0

    def serviceModel(self, game_model):
        self.removeModel()
        self.__gameModel = game_model
        self.__gameModel.shipKilled.connect(self.__shootAround)

    def removeModel(self):
        if self.__gameModel:
            self.shipKilled.disconnect()
            self.__gameModel = None

    def __loadResources(self):
        if DEBUG_RESOURCE:
            resourcesPath = os.path.join(os.path.dirname(__file__),
                                         DEBUG_RESOURCE)
        else:
            resourcesPath = Environment.Resources.path()

        first = True
        for imageName in self.__cellImages:
            image = QImage(
                os.path.join(resourcesPath, "img", "cells",
                             f"{imageName}.png"))
            if first:
                first = False
                self.__originalTileSize = min(image.width(), image.height())
            else:
                self.__originalTileSize = min(self.__originalTileSize,
                                              image.width(), image.height())
            self.__cellImages[imageName] = image

        for spriteName in self.__sprites:
            image = QImage(
                os.path.join(resourcesPath, "img", "cells",
                             f"{spriteName}.png"))
            self.__sprites[spriteName] = image

        for shipName, ship in self.__shipList.items():
            ship.image = QImage(
                os.path.join(resourcesPath, "img", "ships", f"{shipName}.png"))

        self.__shipListImage = QImage(
            os.path.join(resourcesPath, "img", "backgrounds", "shiplist.png"))
        self.__counterImage = QImage(
            os.path.join(resourcesPath, "img", "miscellaneous",
                         "shipcounter.png"))

        self.__targetPixmap = QPixmap(
            os.path.join(resourcesPath, "img", "cells", "target.png"))

    def __initGraphicsView(self):
        self.__ui.graphicsView.setScene(self.__scene)
        self.__ui.graphicsView.viewport().installEventFilter(self)
        self.__ui.graphicsView.setRenderHints(
            QPainter.RenderHint.HighQualityAntialiasing
            | QPainter.RenderHint.TextAntialiasing
            | QPainter.RenderHint.SmoothPixmapTransform)
        self.__ui.graphicsView.horizontalScrollBar().blockSignals(True)
        self.__ui.graphicsView.verticalScrollBar().blockSignals(True)

        for cell in self.__field:
            cell.setData(0, "intact")
            pixmap = QPixmap.fromImage(self.__cellImages["intact"])
            cell.setPixmap(pixmap)
            cell.setTransformationMode(
                Qt.TransformationMode.SmoothTransformation)
            self.__scene.addItem(cell)

        ca_letters = ["А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "К"]
        # ca_letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
        ca_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

        font = QFont("Roboto")
        font.setPixelSize(int(self.__originalTileSize * 0.8))
        for i in range(10):
            self.__letters[i].setPlainText(ca_letters[i])
            self.__letters[i].setFont(font)
            self.__numbers[i].setPlainText(ca_numbers[i])
            self.__numbers[i].setFont(font)
            self.__scene.addItem(self.__letters[i])
            self.__scene.addItem(self.__numbers[i])

        self.__shipListItem.setPixmap(QPixmap.fromImage(self.__shipListImage))
        self.__shipListItem.setTransformationMode(
            Qt.TransformationMode.SmoothTransformation)
        self.__scene.addItem(self.__shipListItem)

        font.setPixelSize(int(self.__originalTileSize * 0.3))
        for _, ship in self.__shipList.items():
            t = QTransform().rotate(90)
            ship.shipItem.setPixmap(
                QPixmap.fromImage(ship.image).transformed(t))
            ship.shipItem.setTransformationMode(
                Qt.TransformationMode.SmoothTransformation)
            ship.counterItem.setPixmap(QPixmap.fromImage(self.__counterImage))
            ship.counterItem.setTransformationMode(
                Qt.TransformationMode.SmoothTransformation)
            ship.counterText.setPlainText(str(ship.count))
            ship.counterText.setFont(font)
            self.__scene.addItem(ship.shipItem)
            self.__scene.addItem(ship.counterItem)
            self.__scene.addItem(ship.counterText)

        self.__ghostShip.setTransformationMode(
            Qt.TransformationMode.SmoothTransformation)
        self.__ghostShip.setOpacity(0.7)

        pen = QPen()
        pen.setWidth(2)
        pen.setStyle(Qt.PenStyle.DashLine)
        pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
        self.__placer.setPen(pen)

    def __setCell(self, x, y, cell_type):
        if cell_type not in self.__cellImages.keys():
            raise ValueError(
                f"Type is {cell_type}. Allowed \"intact\", \"miss\", \"hit\"")

        cellItem = self.__field[y * 10 + x]
        cellItem.setData(0, cell_type)
        pixmap = QPixmap.fromImage(self.__cellImages[cell_type])
        cellItem.setPixmap(pixmap)

    def __runAnimation(self, x, y, animation, looped=False):
        sprite = SpriteItem()
        sprite.setData(0, QPoint(x, y))
        spritePixmap = QPixmap.fromImage(self.__sprites[animation])

        sprite.setSpriteMap(spritePixmap, 60, 60, 5)
        sprite.setScale(self.__scaleFactor)
        sprite.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)
        sprite.setZValue(10)

        self.__spriteAnimations.append(sprite)
        self.__scene.addItem(sprite)

        def removeAnimation():
            self.__scene.removeItem(sprite)
            self.__spriteAnimations.remove(sprite)
            sprite.stopAnimation()

        sprite.startAnimation(100, looped, removeAnimation)

    def __shootAround(self, ship: Ship):
        shipCells = []
        for i in range(ship.length):
            shipCells.append(ship.pos +
                             (QPoint(0, i) if ship.vertical else QPoint(i, 0)))
        cells = []

        for cell in shipCells:
            arounds = [
                cell + QPoint(i, j) for i in range(-1, 2)
                for j in range(-1, 2)
            ]
            for candidate in arounds:
                if 0 <= candidate.x() < 10 and 0 <= candidate.y() < 10:
                    if candidate not in cells:
                        cells.append(candidate)

        for cell in cells:
            if cell not in shipCells:
                self.__setCell(cell.x(), cell.y(), "miss")
                self.__runAnimation(cell.x(), cell.y(), "splash")
        log.debug(
            f"name: {ship.name} | length: {ship.length} | pos: {ship.pos} | vertical: {ship.vertical}"
        )

    def hasHeightForWidth(self):
        return True

    def heightForWidth(self, width):
        return width / self.__ratio

    def hideShipList(self):
        self.__ratio = self.RATIO_WITHOUT_SHIPLIST
        self.__scene.removeItem(self.__shipListItem)
        for _, ship in self.__shipList.items():
            self.__scene.removeItem(ship.shipItem)

        self.__adjustedToSize = None
        resize = QResizeEvent(self.size(), self.size())
        QApplication.postEvent(self, resize)

    def getPlacedShips(self):
        return self.__placedShips

    def placedShipsCount(self):
        return len(self.__placedShips)

    def hideShips(self):
        for ship in self.__placedShips:
            self.__scene.removeItem(ship)

    def removePlacedShips(self):
        if self.__shipListItem.scene() is None:
            return

        for ship in self.__placedShips:
            shipListItem = ship.data(1)
            shipListItem.count += 1
            shipListItem.counterText.setPlainText(str(shipListItem.count))
            self.__scene.removeItem(ship)

        self.__placedShips.clear()

    def shuffleShips(self):
        if self.__shipListItem.scene() is None:
            return

        self.removePlacedShips()

        def findPossibleCells(length, rotation):
            vertical = rotation.isVertical()
            if vertical == rotation.isHorizontal():
                raise Exception(
                    "Unknown state! Rotation is not horizontal and not vertical."
                )  # wtf

            width = 1 if vertical else length
            height = length if vertical else 1

            cells = []

            for x in range(10):
                for y in range(10):
                    if QRect(0, 0, 10, 10) != QRect(0, 0, 10, 10).united(
                            QRect(x, y, width, height)):
                        break
                    if self.__validatePosition(x, y, width, height):
                        cells.append(QPoint(x, y))

            return cells

        shipList = list(self.__shipList.values())
        shipList.sort(key=lambda ship: ship.length, reverse=True)
        for shipItem in shipList:
            for i in range(shipItem.count):
                rot = random.choice(list(Rotation))
                cells = findPossibleCells(shipItem.length, rot)

                if not cells:
                    rot = rot.next()
                    cells = findPossibleCells(shipItem.length, rot)

                if not cells:
                    return

                cell = random.choice(cells)
                self.__placeShip(shipItem, cell.x(), cell.y(), rot)

    def resizeEvent(self, event):
        size = event.size()
        if size == self.__adjustedToSize:
            return

        self.__adjustedToSize = size

        nowWidth = size.width()
        nowHeight = size.height()

        width = min(nowWidth, nowHeight * self.__ratio)
        height = min(nowHeight, nowWidth / self.__ratio)

        h_margin = round((nowWidth - width) / 2) - 2
        v_margin = round((nowHeight - height) / 2) - 2

        self.setContentsMargins(
            QMargins(h_margin, v_margin, h_margin, v_margin))
        self.__resizeScene()

    def scanEffect(self, x, y):
        """
        :return: True on success, False otherwise
        """
        if self.__scanEffect:
            return False

        def scanFinisedCallback():
            self.__scene.removeItem(self.__scanEffect)
            del self.__scanEffect
            self.__scanEffect = None

            target = FadingPixmapItem(self.__targetPixmap)
            target.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)
            target.setScale(self.__scaleFactor)
            target.setData(0, (x, y))
            self.__targets.append(target)
            self.__scene.addItem(target)

        self.__scanEffect = FloatingGradientItem(
            QRectF(self.tileSize, self.tileSize, self.__originalTileSize * 10,
                   self.__originalTileSize * 10), scanFinisedCallback)

        self.__scanEffect.setScale(self.__scaleFactor)

        self.__scanEffect.setBackwards(True)
        self.__scene.addItem(self.__scanEffect)
        return True

    def __resizeScene(self):
        width = self.__ui.graphicsView.width()
        height = self.__ui.graphicsView.height()
        self.__scene.setSceneRect(0, 0, width, height)

        self.tileSize = min(width / 11, height / (11 / self.__ratio))
        self.__scaleFactor = self.tileSize / self.__originalTileSize

        for i, cell in enumerate(self.__field):
            x = i % 10
            y = i // 10
            cell.setScale(self.__scaleFactor)
            cell.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)

        for sprite in self.__spriteAnimations:
            pos = sprite.data(0)
            sprite.setPos((pos.x() + 1) * self.tileSize,
                          (pos.y() + 1) * self.tileSize)
            sprite.setScale(self.__scaleFactor)

        for i in range(10):
            letter = self.__letters[i]
            letter.setScale(self.__scaleFactor)
            offsetX = (self.tileSize -
                       letter.boundingRect().width() * self.__scaleFactor) / 2
            offsetY = (self.tileSize -
                       letter.boundingRect().height() * self.__scaleFactor) / 2
            letter.setPos((i + 1) * self.tileSize + offsetX, offsetY)

            number = self.__numbers[i]
            number.setScale(self.__scaleFactor)
            offsetX = (self.tileSize -
                       number.boundingRect().width() * self.__scaleFactor) / 2
            offsetY = (self.tileSize -
                       number.boundingRect().height() * self.__scaleFactor) / 2
            number.setPos(offsetX, (i + 1) * self.tileSize + offsetY)

        xPos = 0
        for _, ship in self.__shipList.items():
            xPos += (ship.length - 1)
            xOffset = xPos * self.tileSize

            ship.shipItem.setScale(self.__scaleFactor)
            ship.shipItem.setPos(self.tileSize + xOffset,
                                 self.tileSize * (12 + self.EPS))

            ship.counterItem.setScale(self.__scaleFactor)
            ship.counterItem.setPos(self.tileSize + xOffset,
                                    self.tileSize * (12.65 + self.EPS))

            counterSize = ship.counterItem.boundingRect()
            textSize = ship.counterText.boundingRect()
            textXOffset = (counterSize.width() -
                           textSize.width()) * self.__scaleFactor / 2
            textYOffset = (counterSize.height() -
                           textSize.height()) * self.__scaleFactor / 2
            textX = ship.counterItem.pos().x() + textXOffset
            textY = ship.counterItem.pos().y() + textYOffset
            ship.counterText.setScale(self.__scaleFactor)
            ship.counterText.setPos(textX, textY)

        for ship in self.__placedShips:
            mapPos = ship.data(2)
            ship.setPos((mapPos.x() + 1) * self.tileSize,
                        (mapPos.y() + 1) * self.tileSize)
            ship.setScale(self.__scaleFactor)

        for target in self.__targets:
            x, y = target.data(0)
            target.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)
            target.setScale(self.__scaleFactor)

        if self.__scanEffect:
            self.__scanEffect.setPos(self.tileSize, self.tileSize)
            self.__scanEffect.setScale(self.__scaleFactor)

        shipListX = self.tileSize
        shipListY = self.tileSize * (11 + self.EPS)
        self.__shipListItem.setScale(self.__scaleFactor)
        self.__shipListItem.setPos(shipListX, shipListY)
        self.__ghostShip.setScale(self.__scaleFactor)

    def eventFilter(self, obj, event):
        if obj is self.__ui.graphicsView.viewport():
            if event.type() == QEvent.MouseButtonPress:
                self.__viewportMousePressEvent(event)
            elif event.type() == QEvent.MouseButtonRelease:
                self.__viewportMouseReleaseEvent(event)
            elif event.type() == QEvent.MouseMove:
                self.__viewportMouseMoveEvent(event)

        return super().eventFilter(obj, event)

    def sceneToMap(self, x, y):
        x -= self.tileSize
        y -= self.tileSize
        x //= self.tileSize
        y //= self.tileSize
        return int(x), int(y)

    def __initGhostShip(self, ship, pos):
        self.__ghostShip.setPixmap(QPixmap.fromImage(ship.image))
        self.__ghostShip.setPos(pos)
        self.__ghostShip.setRotation(0)

        width = self.__ghostShip.boundingRect().width()
        height = self.__ghostShip.boundingRect().height()
        self.__ghostShip.setOffset(-width / 2, -height / 2)
        self.__ghostShip.setData(0, Rotation.UP)
        self.__ghostShip.setData(1, ship)

        self.__placer.setRect(0, 0, self.tileSize, self.tileSize * ship.length)
        self.__placer.setZValue(50)

        self.__scene.addItem(self.__ghostShip)
        self.__ghostShip.setZValue(100)

    def __rotateGhostShip(self, rotation=None):
        rotation = rotation if rotation else self.__ghostShip.data(0).next()
        self.__ghostShip.setData(0, rotation)
        self.__ghostShip.setRotation(rotation.value)

        placerRect = self.__placer.rect()
        maxSide = max(placerRect.width(), placerRect.height())
        minSide = min(placerRect.width(), placerRect.height())

        if rotation.isHorizontal():
            self.__placer.setRect(0, 0, maxSide, minSide)
        elif rotation.isVertical():
            self.__placer.setRect(0, 0, minSide, maxSide)
        else:
            raise Exception(
                "Unknown state! Rotation is not horizontal and not vertical."
            )  # wtf
        self.__validatePlacer()

    def __ghostShipLongSurface(self):
        pos = self.__ghostShip.pos()
        x = pos.x()
        y = pos.y()
        rot: Rotation = self.__ghostShip.data(0)
        length = self.__ghostShip.data(1).length
        if rot.isHorizontal():
            x -= self.tileSize * length / 2
            return x, y

        if rot.isVertical():
            y -= self.tileSize * length / 2
            return x, y

    def __validatePosition(self, x, y, width=1, height=1):
        positionRect = QRectF((x + 1) * self.tileSize, (y + 1) * self.tileSize,
                              self.tileSize * width, self.tileSize * height)

        isPlacerValid = True
        for ship in self.__placedShips:
            shipRect = ship.mapRectToScene(ship.boundingRect()).adjusted(
                -self.tileSize / 2, -self.tileSize / 2, self.tileSize / 2,
                self.tileSize / 2)
            isPlacerValid = not positionRect.intersects(shipRect)
            if not isPlacerValid:
                break
        return isPlacerValid

    def __validatePlacer(self):
        sceneX, sceneY = self.__ghostShipLongSurface()
        x, y = self.sceneToMap(sceneX, sceneY)
        self.__placer.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)

        permittedArea = QRectF(self.__ui.graphicsView.viewport().geometry())
        permittedArea.setTopLeft(QPointF(self.tileSize, self.tileSize))
        permittedArea.setBottomRight(
            QPointF(self.tileSize * 12, self.tileSize * 12))
        placerSize = self.__placer.boundingRect()
        placerRect = QRectF(sceneX, sceneY, placerSize.width(),
                            placerSize.height())

        isPlacerValid = False
        # first validation - ship can be placed inside game field
        if permittedArea.contains(self.__ghostShip.pos(
        )) and permittedArea == permittedArea.united(placerRect):
            if self.__placer.scene() == None:
                self.__scene.addItem(self.__placer)
            x, y = self.sceneToMap(sceneX, sceneY)
            self.__placer.setPos((x + 1) * self.tileSize,
                                 (y + 1) * self.tileSize)
            isPlacerValid = True

        else:
            if self.__placer.scene() == self.__scene:
                self.__scene.removeItem(self.__placer)

        # second validation - placer does not intersect with other ships
        if isPlacerValid:
            isPlacerValid = self.__validatePosition(
                x, y,
                placerSize.width() / self.tileSize,
                placerSize.height() / self.tileSize)

        # set color of placer
        pen = self.__placer.pen()
        if isPlacerValid:
            pen.setColor(Qt.GlobalColor.darkGreen)
        else:
            pen.setColor(Qt.GlobalColor.red)
        self.__placer.setPen(pen)
        self.__placer.setData(0, isPlacerValid)

    def __placeShip(self, shipListItem, x, y, rotation):
        sceneX, sceneY = (x + 1) * self.tileSize, (y + 1) * self.tileSize

        shipListItem.count -= 1
        shipListItem.counterText.setPlainText(str(shipListItem.count))

        pixmap = QPixmap(shipListItem.image).transformed(QTransform().rotate(
            (rotation.value)))
        placedShip = QGraphicsPixmapItem(pixmap)
        placedShip.setData(0, rotation)
        placedShip.setData(1, shipListItem)
        placedShip.setData(2, QPoint(x, y))  # position in map coordinates

        placedShip.setPos(sceneX, sceneY)
        placedShip.setTransformationMode(
            Qt.TransformationMode.SmoothTransformation)
        placedShip.setScale(self.__scaleFactor)

        self.__placedShips.append(placedShip)
        self.__scene.addItem(placedShip)

    def __placeGhostShip(self):
        isPlacingPermitted = self.__placer.data(0)
        if isPlacingPermitted:
            sceneX = self.__placer.pos().x() + self.tileSize / 2
            sceneY = self.__placer.pos().y() + self.tileSize / 2
            mapX, mapY = self.sceneToMap(sceneX, sceneY)
            rotation = self.__ghostShip.data(0)
            if rotation.isVertical():
                vertical = True
            elif rotation.isHorizontal():
                vertical = False
            else:
                raise Exception(
                    "Unknown state! Rotation is not horizontal and not vertical."
                )  # wtf

            shipListItem = self.__ghostShip.data(1)
            self.__placeShip(shipListItem, mapX, mapY, rotation)

            log.debug(
                f"ship \"{shipListItem.name}\"; "
                f"position ({mapX}, {mapY}); "
                f"oriented {'vertically' if vertical else 'horizontally'}")

            self.shipPlaced.emit(
                Ship(name=shipListItem.name,
                     length=shipListItem.length,
                     pos=QPoint(mapX, mapY),
                     vertical=vertical))

    def __viewportMousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            if self.__shipListItem.scene():
                # check press on shiplist
                shipUnderMouse = None
                for _, ship in self.__shipList.items():
                    if ship.shipItem.isUnderMouse():
                        shipUnderMouse = ship
                        break

                # check press on field
                rotation = Rotation.RIGHT
                if shipUnderMouse == None:
                    for ship in self.__placedShips:
                        if ship.isUnderMouse():
                            rotation = ship.data(0)
                            shipListItem = ship.data(1)

                            if shipListItem.count == 0:
                                shipListItem.shipItem.setGraphicsEffect(None)

                            shipListItem.count += 1
                            shipListItem.counterText.setPlainText(
                                str(shipListItem.count))
                            shipUnderMouse = shipListItem
                            self.__placedShips.remove(ship)
                            self.__scene.removeItem(ship)
                            break

                # if ship grabbed
                if shipUnderMouse and shipUnderMouse.count > 0:
                    self.__initGhostShip(shipUnderMouse, event.pos())
                    self.__rotateGhostShip(rotation)
                    self.__dragShip = True

            x, y = self.sceneToMap(event.pos().x(), event.pos().y())
            if x >= 0 and x < 10 and y >= 0 and y < 10:
                if self.controller.isBot:
                    return
                self.controller.emitHit(x, y)

        if event.button() == Qt.MouseButton.RightButton:
            if self.__dragShip:
                self.__rotateGhostShip()

    def __viewportMouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            if self.__dragShip:
                self.__scene.removeItem(self.__ghostShip)
                self.__dragShip = False
            if self.__placer.scene() != None:
                self.__scene.removeItem(self.__placer)

            # self.__placeShip()
            self.__placeGhostShip()
            self.__placer.setData(0, False)

    def __viewportMouseMoveEvent(self, event):
        if self.__dragShip:
            self.__ghostShip.setPos(event.pos())

            permittedArea = QRectF(
                self.__ui.graphicsView.viewport().geometry())
            permittedArea.setTopLeft(QPointF(self.tileSize, self.tileSize))

            # stop dragging ship if mouse out of field
            # if not permittedArea.contains(event.pos()):
            #     self.__scene.removeItem(self.__ghostShip)
            #     self.__dragShip = False

            self.__validatePlacer()

    def __accept(self, x, y, hit_type: CellState):
        log.debug(
            f" -- ACCEPTED -- hit on point ({x}, {y}) hit type: {hit_type}")
        cell = self.__field[y * 10 + x]

        if cell.data(0) != "intact":
            return
        if hit_type in [CellState.HIT, CellState.KILLED]:
            self.__setCell(x, y, 'hit')
            self.__runAnimation(x, y, "explosion", looped=True)
            for target in self.__targets:
                if (x, y) == target.data(0):
                    self.__scene.removeItem(target)
                    self.__targets.remove(target)
                    break
        elif hit_type in [CellState.MISS]:
            self.__setCell(x, y, 'miss')
            self.__runAnimation(x, y, "splash", looped=False)

        if hit_type == CellState.KILLED:
            for ship in self.__placedShips:
                rotation = ship.data(0)
                length = ship.data(1).length
                position = ship.data(2)
                width = 1 if rotation.isVertical() else length
                height = length if rotation.isVertical() else 1
                rect = QRect(position.x(), position.y(), width, height)
                if rect.contains(x, y):
                    self.__scene.addItem(ship)

    def __decline(self, x, y):
        log.debug(f"declined hit on point ({x}, {y})")
コード例 #14
0
ファイル: robot.py プロジェクト: ZxxWs/RoboCode
class Robot(QGraphicsItemGroup):
    def __init__(self, mapSize, parent, repr):
        QGraphicsItemGroup.__init__(self)

        # 公有属性
        self.gamePlace = 1

        # 私有属性
        self.__mapSize = mapSize
        self.__parent = parent
        self.__health = 100
        self.__repr = repr

        self.__gunLock = "free"
        self.__radarLock = "Free"  # 字面意思是雷达锁

        # 两个动画对象、一个physics对象
        self.__runAnimation = animation("run")
        self.__targetAnimation = animation("target")
        self.__physics = physics(self.__runAnimation)  # 一个physics对象

        # 设置一些颜色
        self.maskColor = QColor(0, 255, 255)
        self.gunMaskColor = QColor(0, 255, 255)  # 炮膛颜色
        self.radarMaskColor = QColor(0, 255, 255)  # 雷达颜色

        # 加载车身图片
        self.__base = QGraphicsPixmapItem()
        self.__base.pixmap = QPixmap(os.getcwd() + "/robotImages/baseGrey.png")
        self.__base.setPixmap(self.__base.pixmap)
        self.addToGroup(self.__base)
        self.__baseWidth = self.__base.boundingRect().width()
        self.__baseHeight = self.__base.boundingRect().height()

        # 加载炮膛图片
        self.__gun = QGraphicsPixmapItem()
        self.__gun.pixmap = QPixmap(os.getcwd() + "/robotImages/gunGrey.png")
        self.__gun.setPixmap(self.__gun.pixmap)
        self.addToGroup(self.__gun)
        self.__gunWidth = self.__gun.boundingRect().width()
        self.__gunHeight = self.__gun.boundingRect().height()

        # 炮膛位置
        x = self.__base.boundingRect().center().x()
        y = self.__base.boundingRect().center().y()
        self.__gun.setPos(x - self.__gunWidth / 2.0,
                          y - self.__gunHeight / 2.0)

        # --------------------------雷达初始化-----------------------------

        # 加载雷达图片
        self.__radar = QGraphicsPixmapItem()
        self.__radar.pixmap = QPixmap(os.getcwd() + "/robotImages/radar.png")
        self.__radar.setPixmap(self.__radar.pixmap)
        self.addToGroup(self.__radar)
        self.__radarWidth = self.__radar.boundingRect().width()
        self.__radarHeight = self.__radar.boundingRect().height()
        # radar position
        self.__radar.setPos(x - self.__radarWidth / 2.0,
                            y - self.__radarHeight / 2.0)

        # 加载雷达范围
        firstPoint = QPointF(x - self.__radarWidth / 2, y)
        secondPoint = QPointF(x + self.__radarWidth / 2, y)
        thirdPoint = QPointF(x + 4 * self.__radarWidth, y + 700)
        fourthPoint = QPointF(x - 4 * self.__radarWidth, y + 700)
        qPointListe = []
        qPointListe.append(firstPoint)
        qPointListe.append(secondPoint)
        qPointListe.append(thirdPoint)
        qPointListe.append(fourthPoint)
        self.__radarField = radarField(qPointListe, self, "poly")

        # 大雷达范围
        qPointListe.remove(fourthPoint)
        qPointListe.remove(thirdPoint)
        thirdPoint = QPointF(x + 10 * self.__radarWidth, y + 400)
        fourthPoint = QPointF(x - 10 * self.__radarWidth, y + 400)
        qPointListe.append(thirdPoint)
        qPointListe.append(fourthPoint)
        self.__largeRadarField = radarField(qPointListe, self, "poly")

        # 细雷达范围
        qPointListe.remove(fourthPoint)
        qPointListe.remove(thirdPoint)
        thirdPoint = QPointF(x + 0.4 * self.__radarWidth, y + 900)
        fourthPoint = QPointF(x - 0.4 * self.__radarWidth, y + 900)
        qPointListe.append(thirdPoint)
        qPointListe.append(fourthPoint)
        self.__thinRadarField = radarField(qPointListe, self, "poly")

        # 弧形雷达范围
        self.__roundRadarField = radarField([0, 0, 300, 300], self, "round")
        self.addToGroup(self.__roundRadarField)
        self.__roundRadarField.setPos(
            x - self.__roundRadarField.boundingRect().width() / 2.0,
            y - self.__roundRadarField.boundingRect().height() / 2.0)

        # add to group
        self.addToGroup(self.__radarField)
        self.addToGroup(self.__largeRadarField)
        self.addToGroup(self.__thinRadarField)

        # 一个坦克上有三个雷达范围,但初始化的时候都是隐藏的
        self.__largeRadarField.hide()
        self.__thinRadarField.hide()
        self.__roundRadarField.hide()
        # --------------------------雷达初始化(上-----------------------------

        # 设置坦克颜色:RGB
        self.setColor(0, 200, 100)
        self.setGunColor(0, 200, 100)
        self.setRadarColor(0, 200, 100)
        self.setBulletsColor(0, 200, 100)

        # 设置原点:
        # 雷达范围
        self.__radarField.setTransformOriginPoint(x, y)
        self.__largeRadarField.setTransformOriginPoint(x, y)
        self.__thinRadarField.setTransformOriginPoint(x, y)
        # 车身
        x = self.__baseWidth / 2
        y = self.__baseHeight / 2
        self.__base.setTransformOriginPoint(x, y)
        # 炮膛
        x = self.__gunWidth / 2
        y = self.__gunHeight / 2
        self.__gun.setTransformOriginPoint(x, y)
        # 雷达(雷达与雷达范围不是一个东西
        x = self.__radarWidth / 2
        y = self.__radarHeight / 2
        self.__radar.setTransformOriginPoint(x, y)

        # 添加self.item到items中以防冲突
        self.__items = set([
            self, self.__base, self.__gun, self.__radar, self.__radarField,
            self.__largeRadarField, self.__thinRadarField,
            self.__roundRadarField
        ])

        # 初始化基类(初始化子类中的初始化函数
        self.init()

        # 当前动画
        self.__currentAnimation = []
        # self.a = time.time()

    '''
        重写QGraphicsItem类的advance函数
    '''

    def advance(self, i):
        """
        if i ==1:
            print(time.time() - self.a)
            self.a = time.time()
        """
        # 如果机器人的血量小于等于0
        if self.__health <= 0:
            self.gamePlace = len(self.__parent.aliveBots)
            # print("Robot.advance:"+str(self.gamePlace))
            self.__parent.placeList.append(self.__repr__())
            self.__death()

        # 如果当前的动画列表为空
        if self.__currentAnimation == []:
            try:
                self.__currentAnimation = self.__physics.animation.list.pop()

            except IndexError:
                if self.__physics.animation.name == "target":
                    try:
                        self.__physics.animation = self.__runAnimation
                        self.__currentAnimation = self.__physics.animation.list.pop(
                        )
                    except IndexError:
                        pass
                else:
                    self.stop()
                    try:
                        self.run()
                    except:
                        traceback.print_exc()
                        exit(-1)
                    self.__physics.reverse()
                    try:
                        self.__currentAnimation = self.__physics.animation.list.pop(
                        )
                    except:
                        pass

        if i == 1:
            try:
                command = self.__currentAnimation.pop()  # load animation

                # 读取并执行移动命令
                dx, dy = self.__getTranslation(command["move"])
                self.setPos(dx, dy)

                # 读取并执行转向命令
                angle = self.__getRotation(command["turn"])
                self.__base.setRotation(angle)
                if self.__gunLock.lower() == 'base':
                    self.__gun.setRotation(angle)
                if self.__radarLock.lower() == 'base':
                    self.__setRadarRotation(angle)

                # gun Rotation
                angle = self.__getGunRotation(command["gunTurn"])
                self.__gun.setRotation(angle)
                if self.__radarLock.lower() == 'gun':
                    self.__setRadarRotation(angle)

                # radar Rotation
                angle = self.__getRadarRotation(command["radarTurn"])
                self.__setRadarRotation(angle)

                # asynchronous fire
                #读取并执行开火命令
                if command["fire"] != 0:
                    self.makeBullet(command["fire"])
            except:
                pass

        else:
            # 执行子类的函数——————————————————————!!!!!!!!!
            self.sensors()

            # collisions
            for item in set(self.__base.collidingItems(1)) - self.__items:
                if isinstance(item, QGraphicsRectItem):
                    # wall Collision
                    self.__wallRebound(item)
                elif isinstance(item, Robot):
                    if item.__base.collidesWithItem(self.__base):
                        # robot Collision
                        self.__robotRebound(item)
                elif isinstance(item, Bullet):
                    # bullet colision
                    self.__bulletRebound(item)
                elif isinstance(item, radarField):
                    if item.robot.__physics.animation.name != "target":
                        # targetSpotted
                        self.__targetSeen(item)

    # -------------------------------------------炮膛-------------------------------------------
    # 炮膛转向
    def gunTurn(self, angle):
        s = 1
        if angle < 0:
            s = -1
        steps = int(s * angle / self.__physics.step)
        a = angle % self.__physics.step
        if a != 0:
            self.__physics.gunTurn.append(s * a)
        for i in range(steps):
            self.__physics.gunTurn.append(s * self.__physics.step)

    def lockGun(self, part):
        self.__gunLock = part

    # 设置炮膛颜色
    def setGunColor(self, r, g, b):
        color = QColor(r, g, b)
        mask = self.__gun.pixmap.createMaskFromColor(self.gunMaskColor, 1)
        p = QPainter(self.__gun.pixmap)
        p.setPen(QColor(r, g, b))
        p.drawPixmap(self.__gun.pixmap.rect(), mask, mask.rect())
        p.end()
        self.__gun.setPixmap(self.__gun.pixmap)
        self.gunMaskColor = QColor(r, g, b)

    # -------------------------------------------车身(下-------------------------------------
    # 车身的移动方式
    def move(self, distance):
        s = 1
        if distance < 0:
            s = -1
        steps = int(s * distance / self.__physics.step)
        d = distance % self.__physics.step
        if d != 0:
            self.__physics.move.append(s * d)
        for i in range(steps):
            self.__physics.move.append(s * self.__physics.step)

    # 转向方法,传入的参数为一个角度(类型未知
    def turn(self, angle):
        s = 1
        if angle < 0:
            s = -1
        steps = int(s * angle / self.__physics.step)
        a = angle % self.__physics.step
        if a != 0:
            self.__physics.turn.append(s * a)
        for i in range(steps):
            self.__physics.turn.append(s * self.__physics.step)

    # 设置车身颜色
    def setColor(self, r, g, b):
        color = QColor(r, g, b)
        mask = self.__base.pixmap.createMaskFromColor(self.maskColor, 1)
        p = QPainter(self.__base.pixmap)
        p.setPen(QColor(r, g, b))
        p.drawPixmap(self.__base.pixmap.rect(), mask, mask.rect())
        p.end()
        self.__base.setPixmap(self.__base.pixmap)
        self.maskColor = QColor(r, g, b)

    # ---------------------------------------------雷达(下-----------------------------

    def setRadarField(self, form):
        '''
            设置《雷达范围》类型
            类型:normal、large、thin、round
            根据传入的类型来设置显示的类型(其他类型的就hide
        '''
        if form.lower() == "normal":
            self.__radarField.show()
            self.__largeRadarField.hide()
            self.__thinRadarField.hide()
            self.__roundRadarField.hide()
        if form.lower() == "large":
            self.__radarField.hide()
            self.__largeRadarField.show()
            self.__thinRadarField.hide()
            self.__roundRadarField.hide()
        if form.lower() == "thin":
            self.__radarField.hide()
            self.__largeRadarField.hide()
            self.__thinRadarField.show()
            self.__roundRadarField.hide()
        if form.lower() == "round":
            self.__radarField.hide()
            self.__largeRadarField.hide()
            self.__thinRadarField.hide()
            self.__roundRadarField.show()

    def lockRadar(self, part):
        self.__radarLock = part

    # 雷达转向
    def radarTurn(self, angle):
        s = 1
        if angle < 0:
            s = -1
        steps = int(s * angle / self.__physics.step)
        a = angle % self.__physics.step
        if a != 0:
            self.__physics.radarTurn.append(s * a)
        for i in range(steps):
            self.__physics.radarTurn.append(s * self.__physics.step)

    # 设置雷达颜色(雷达罩的颜色,在坦克车身上的
    def setRadarColor(self, r, g, b):
        color = QColor(r, g, b)
        mask = self.__radar.pixmap.createMaskFromColor(self.radarMaskColor, 1)
        p = QPainter(self.__radar.pixmap)
        p.setPen(QColor(r, g, b))
        p.drawPixmap(self.__radar.pixmap.rect(), mask, mask.rect())
        p.end()
        self.__radar.setPixmap(self.__radar.pixmap)
        self.radarMaskColor = QColor(r, g, b)

    # 设置雷达是否可见,将几个雷达类型都设置为统一的
    def radarVisible(self, bol):
        self.__radarField.setVisible(bol)
        self.__roundRadarField.setVisible(bol)
        self.__thinRadarField.setVisible(bol)
        self.__largeRadarField.setVisible(bol)

    # --------------------------------------------子弹(下------------------------------------
    def fire(self, power):
        # asynchronous fire
        self.stop()
        bullet = Bullet(power, self.bulletColor, self)
        self.__physics.fire.append(bullet)
        self.__items.add(bullet)
        self.__parent.addItem(bullet)
        bullet.hide()
        return id(bullet)

    # 制造子弹,传入的参数是个子弹对象
    def makeBullet(self, bullet):
        bullet.show()
        pos = self.pos()
        angle = self.__gun.rotation()
        # to find the initial position
        x = pos.x() + self.__baseWidth / 2.0
        y = pos.y() + self.__baseHeight / 2.0
        dx = -math.sin(math.radians(angle)) * self.__gunWidth / 2.0
        dy = math.cos(math.radians(angle)) * self.__gunHeight / 2.0
        pos.setX(x + dx)
        pos.setY(y + dy)
        bot = self

        #设置一些子弹的属性
        bullet.init(pos, angle, self.__parent)

        #生成子弹后,会扣除自己的血量,(如果击中敌人会回血
        self.__changeHealth(self, -bullet.power)
        return id(bullet)

    # 设置子弹颜色
    def setBulletsColor(self, r, g, b):
        self.bulletColor = QColor(r, g, b)

    # ---------------------------------------整体的方法(下--------------------------------------

    #停止方法,调用后会使__physics产生一个新的动画
    def stop(self):
        self.__physics.newAnimation()

    # 获取地图尺寸
    def getMapSize(self):
        return self.__mapSize

    # 获取位置
    def getPosition(self):
        p = self.pos()
        r = self.__base.boundingRect()
        return QPointF(p.x() + r.width() / 2, p.y() + r.height() / 2)

    # 获取炮膛朝向
    def getGunHeading(self):
        angle = self.__gun.rotation()
        # if angle > 360:
        #     a = int(angle) / 360
        #     angle = angle - (360*a)
        return angle

    # 获取朝向
    def getHeading(self):
        return self.__base.rotation()

    # 获取雷达朝向
    def getRadarHeading(self):
        return self.__radar.rotation()

    def reset(self):
        self.__physics.reset()
        self.__currentAnimation = []

    # 获取敌人
    def getEnemiesLeft(self):
        l = []
        for bot in self.__parent.aliveBots:
            dic = {"id": id(bot), "name": bot.__repr__()}
            l.append(dic)
        return l

    # 向细节面板加内容的方法
    def rPrint(self, msg):
        self.info.out.add(str(msg))

    def pause(self, duration):
        self.stop()
        for i in range(int(duration)):
            self.__physics.move.append(0)
        self.stop()

    # ---------------------------------下面为私有方法------------------------------------------

    def __getTranslation(self, step):
        angle = self.__base.rotation()
        pos = self.pos()
        x = pos.x()
        y = pos.y()
        dx = -math.sin(math.radians(angle)) * step
        dy = math.cos(math.radians(angle)) * step
        # print(dx, dy)
        return x + dx, y + dy

    def __setRadarRotation(self, angle):
        self.__radar.setRotation(angle)
        self.__radarField.setRotation(angle)
        self.__largeRadarField.setRotation(angle)
        self.__thinRadarField.setRotation(angle)

    def __getRotation(self, alpha):
        return self.__base.rotation() + alpha

    def __getGunRotation(self, alpha):
        return self.__gun.rotation() + alpha

    def __getRadarRotation(self, alpha):
        return self.__radar.rotation() + alpha

    def __wallRebound(self, item):
        self.reset()
        if item.name == 'left':
            x = self.__physics.step * 1.1
            y = 0
        elif item.name == 'right':
            x = -self.__physics.step * 1.1
            y = 0
        elif item.name == 'top':
            x = 0
            y = self.__physics.step * 1.1
        elif item.name == 'bottom':
            x = 0
            y = -self.__physics.step * 1.1
        self.setPos(self.pos().x() + x, self.pos().y() + y)
        self.__changeHealth(self, -1)
        self.stop()
        try:

            # 执行子类的方法-----------!!!!!!!!!!!!!!!
            self.onHitWall()
        except:
            traceback.print_exc()
            exit(-1)
        animation = self.__physics.makeAnimation()
        if animation != []:
            self.__currentAnimation = animation

    def __robotRebound(self, robot):
        try:
            self.reset()
            robot.reset()
            angle = self.__base.rotation()
            pos = self.pos()
            x = pos.x()
            y = pos.y()
            dx = -math.sin(math.radians(angle)) * self.__physics.step * 1.1
            dy = math.cos(math.radians(angle)) * self.__physics.step * 1.1
            self.setPos(x - dx, y - dy)
            pos = robot.pos()
            x = pos.x()
            y = pos.y()
            robot.setPos(x + dx, y + dy)
            self.__changeHealth(robot, -1)
            self.__changeHealth(self, -1)
            self.stop()
            # 执行子类的函数
            self.onRobotHit(id(robot), robot.__repr__())
            animation = self.__physics.makeAnimation()
            if animation != []:
                self.__currentAnimation = animation
            robot.stop()
            robot.onHitByRobot(id(self), self.__repr__())
            animation = robot.__physics.makeAnimation()
            if animation != []:
                robot.__currentAnimation = animation
        except:
            traceback.print_exc()
            exit(-1)

    def __bulletRebound(self, bullet):
        self.__changeHealth(self, -3 * bullet.power)
        try:
            if bullet.robot in self.__parent.aliveBots:
                self.__changeHealth(bullet.robot, 2 * bullet.power)
            self.stop()
            self.onHitByBullet(id(bullet.robot), bullet.robot.__repr__(),
                               bullet.power)
            animation = self.__physics.makeAnimation()
            if animation != []:
                self.__currentAnimation = animation
            bullet.robot.stop()
            bullet.robot.onBulletHit(id(self), id(bullet))
            animation = bullet.robot.__physics.makeAnimation()
            if animation != []:
                bullet.robot.__currentAnimation = animation
            self.__parent.removeItem(bullet)
        except:
            pass

    def __targetSeen(self, target):
        self.stop()
        anim = target.robot.__currentAnimation
        target.robot.__physics.animation = target.robot.__targetAnimation
        target.robot.__physics.reset()
        try:
            target.robot.onTargetSpotted(id(self), self.__repr__(),
                                         self.getPosition())
        except:
            traceback.print_exc()
            exit(-1)
        target.robot.__physics.newAnimation()
        target.robot.__physics.reverse()
        try:
            target.robot.__currentAnimation = target.robot.__physics.animation.list.pop(
            )
        except:
            target.robot.__physics.animation = target.robot.__runAnimation
            target.robot.__currentAnimation = anim

    def __changeHealth(self, bot, value):

        if bot.__health + value >= 100:
            bot.__health = 100
        else:
            bot.__health = bot.__health + value
        try:
            bot.progressBar.setValue(bot.__health)
        except:
            pass

    def removeMyProtectedItem(self, item):
        self.__items.remove(item)

    def __death(self):

        try:
            self.icon.setIcon(QIcon(os.getcwd() + "/robotImages/dead.png"))
            self.icon2.setIcon(QIcon(os.getcwd() + "/robotImages/dead.png"))
            self.progressBar.setValue(0)
        except:
            pass
        self.__parent.deadBots.append(self)
        self.__parent.aliveBots.remove(self)
        try:
            self.onRobotDeath()
        except:
            traceback.print_exc()
            exit(-1)
        self.__parent.removeItem(self)

        if len(self.__parent.aliveBots) <= 1:  # 如果生存的机器人小于等于1,则结束战斗
            self.__parent.battleFinished()

    def __repr__(self):
        repr = self.__repr.split(".")
        return repr[1].replace("'>", "")
コード例 #15
0
ファイル: specify_target.py プロジェクト: wpeaton/cvrobot2
class SpecifyTarget(Plugin):
    def __init__(self, parent=None):

        super(SpecifyTarget, self).__init__()

        self.setObjectName('specify_target')  # should be plugin name

        # ~ self.needsGraphicsView = True
        self.needsEventFilter = True
        self.fname = ''
        self.image = None
        self.targetImg = None

        # declare all parameter widgets below
        vbox1 = QVBoxLayout()
        hbox1 = QHBoxLayout()
        self.dblNumTargets = QDoubleSpinBox(objectName='dblNumTargets')
        self.dblNumTargets.setMinimum(1)
        self.dblNumTargets.setMaximum(5)
        self.dblNumTargets.setValue(1)
        self.dblNumTargets.setSingleStep(1)
        self.chkSaveTarget = QCheckBox('save target.png',
                                       objectName='chkSaveTarget')
        hbox1.addWidget(self.dblNumTargets)
        hbox1.addWidget(QLabel('num targets'))
        hbox1.addWidget(self.chkSaveTarget)

        labels = ['Rectangle', 'Square', 'Ellipse', 'Circle']
        groupBox1, self.bgrpShape = radio_filler('target shape', labels, 2)

        labels = ['opposite\ncorners', 'center\n-edge']
        groupBox2, self.bgrpDrawMethod = radio_filler('draw method', labels, 2)

        self.lblStatus = QLabel(wordWrap=True)
        groupBox2.layout().addWidget(self.lblStatus, 0, 2)

        self.bgrpShape.button(0).setChecked(True)
        self.bgrpDrawMethod.button(0).setChecked(True)

        self.bgrpShape.buttonClicked.connect(self.radio_handler)

        self.targetView = QGraphicsView()
        scene = QGraphicsScene()
        self.targetView.setScene(scene)
        # ~ vbox1.addLayout(hbox1)
        # ~ vbox1.addWidget(groupBox1)
        vbox1.addWidget(self.targetView)
        vbox1.addWidget(groupBox2)
        vbox1.setStretch(0, 3)
        vbox1.setStretch(1, 1)

        self.setLayout(vbox1)

        self.chkSaveTarget.setChecked(True)

        # class variables
        self.targetShape = None
        self.target = None
        self.begin = None
        self.end = None
        self.training = False

    def getParams(self):
        params = super(SpecifyTarget, self).getParams()
        params['fname'] = self.fname
        self.Params = params
        return params

    def setParams(self, params):
        params = super(SpecifyTarget, self).setParams(params)

        self.fname = self.Params['fname']

    def showImg(self, img):
        if len(img.shape) == 3:
            # ~ #img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
            height, width, bpc = img.shape
            bpl = bpc * width
            # ~ print(subimage.data, ', ', width, ', ', height, ', ', bpl)
            qimg = QImage(img, width, height, bpl, QImage.Format_RGB888)
        else:
            height, width = img.shape
            bpc = 1
            bpl = bpc * width
            qimg = QImage(img.data, width, height, bpl,
                          QImage.Format_Grayscale8)

        self.image = QGraphicsPixmapItem(QPixmap.fromImage(qimg))
        self.targetImg = img
        self.targetView.scene().clear()
        self.targetView.scene().addItem(self.image)

        self.targetView.fitInView(self.image.boundingRect(),
                                  Qt.KeepAspectRatio)

    def eventFilter(self, source, event):

        if event.type() == QtCore.QEvent.MouseButtonPress:
            print(time.time(), ' mousebuttonpress')
            if isinstance(source, QGraphicsView):

                self.mouseButtonPress(source)

                return True
        elif isinstance(
                source,
                QGraphicsView) or source.objectName() == 'MainWindowWindow':
            if self.training:
                if (event.type() == QtCore.QEvent.MouseMove
                        and source.objectName() == 'MainWindowWindow'):
                    if event.buttons() != QtCore.Qt.NoButton:
                        # ~ global end
                        self.mouseMove()

                    return True
                elif (event.type() == QtCore.QEvent.MouseButtonRelease
                      and source.objectName() == 'MainWindowWindow'):

                    self.mouseButtonRelease()

                    return True  # this is for eventFilter. when True, stop processing events

        return False  # this is for eventFilter. when False, keep processing events

    def mouseButtonPress(self, source):
        pos = QtGui.QCursor.pos()

        viewPoint = source.mapFromGlobal(pos)
        scenePoint = source.mapToScene(viewPoint)
        # ~ global graphicsView
        self.graphicsView = source
        self.begin = scenePoint
        self.training = True

        # ~ print(pos)
        # ~ print(self.begin)

        rect = QRectF(self.begin, self.begin)
        # ~ print(rect)
        # ~ self.targetShape = QGraphicsRectItem(rect)
        # ~ global targetShapes
        if self.targetShape is not None:
            try:
                print('targetShape ', self.targetShape)
                source.scene().removeItem(self.targetShape)
            except RuntimeError:
                pass

        # ~ bshape = self.bgrpShape.checkedId()
        bshape = 0
        # ~ print('bshape ', bshape)
        if bshape == 0:  # Rectangle
            self.targetShape = QGraphicsRectItem(rect)
        elif bshape == 1:  # Square
            self.targetShape = QGraphicsRectItem(rect)
        elif bshape == 2:  # Ellipse
            self.targetShape = QGraphicsEllipseItem(rect)
        elif bshape == 3:  # Circle
            self.targetShape = QGraphicsEllipseItem(rect)

        self.targetShape.setZValue(1)

        pen = QPen(QColor(0, 255, 0), 5)
        self.targetShape.setPen(pen)
        try:
            source.scene().addItem(self.targetShape)

        except RuntimeError:
            pass
        print('mousie pressie')

    def mouseMove(self):
        pos = QtGui.QCursor.pos()

        viewPoint = self.graphicsView.mapFromGlobal(pos)
        scenePoint = self.graphicsView.mapToScene(viewPoint)

        self.end = scenePoint
        # ~ print('end :', self.end)
        try:
            # ~ bshape = self.bgrpShape.checkedId()
            bshape = 0
            bmethod = self.bgrpDrawMethod.checkedId()
            if bshape == 3:  # circle
                if bmethod == 1:
                    # center radius circle
                    r = QLineF(self.begin, self.end).length()
                    cx = self.begin.x()
                    cy = self.begin.y()
                    # ~ x1 = self.begin.x() - r
                    # ~ y1 = self.begin.y() - r
                    # ~ x2 = self.begin.x() + r
                    # ~ y2 = self.begin.y() + r
                elif bmethod == 0:
                    r = QLineF(self.begin, self.end).length() / 2
                    # ~ center = line.center()  # not available until qt > 5.8
                    cx = float(self.begin.x() + self.end.x()) / 2.0
                    cy = float(self.begin.y() + self.end.y()) / 2.0
                x1 = cx - r
                y1 = cy - r
                x2 = cx + r
                y2 = cy + r
                # ~ print('begin ', self.begin, ' end ', self.end)
                # ~ print('r ', r, ' cx  ', cx, ' cy ', cy)
            elif bshape == 1:  # square
                if bmethod == 1:
                    # center radius circle
                    r = QLineF(self.begin, self.end).length() / np.sqrt(2)
                    cx = self.begin.x()
                    cy = self.begin.y()
                    x1 = cx - r
                    y1 = cy - r
                    x2 = cx + r
                    y2 = cy + r
                elif bmethod == 0:
                    line = QLineF(self.begin, self.end)
                    r = line.length() / np.sqrt(2)
                    sx = np.sign(line.dx())
                    sy = np.sign(line.dy())
                    x1 = self.begin.x()
                    y1 = self.begin.y()
                    x2 = self.begin.x() + sx * r
                    y2 = self.begin.y() + sy * r

            #rectangles and ellipses
            else:
                if bmethod == 0:
                    # 2 corners rectangle
                    x1 = self.begin.x()
                    y1 = self.begin.y()
                    x2 = self.end.x()
                    y2 = self.end.y()
                elif bmethod == 1:
                    # center/corner rectangle
                    x1 = self.begin.x() - abs(self.begin.x() - self.end.x())
                    y1 = self.begin.y() - abs(self.begin.y() - self.end.y())
                    x2 = self.begin.x() + abs(self.begin.x() - self.end.x())
                    y2 = self.begin.y() + abs(self.begin.y() - self.end.y())

            # ~ self.targetShape.setRect(QRectF(begin, end))
            # ~ idx = len(self.targetShapes)-1
            self.targetShape.setRect(QRectF(QPointF(x1, y1), QPointF(x2, y2)))
            self.lblStatus.setText('drawing target')

            # ~ print('x1, y1, x2, y2: ', x1, y1, x2, y2)

        except RuntimeError as e:
            # ~ pass
            print('RuntimeError in mouseMove. ', e)

    def mouseButtonRelease(self):
        self.training = False

        # time to save target image. but first we need to
        # check the sizes

        self.graphicsItems.append(self.targetShape)

        # only save to target.png if checkbox is checked
        # ~ if self.chkSaveTarget.isChecked():
        try:
            rect = self.targetShape.rect()
            print('target rect ', rect)

            x1 = int(round(min([rect.left(), rect.right()])))
            y1 = int(round(min([rect.top(), rect.bottom()])))
            x2 = int(round(max([rect.left(), rect.right()])))
            y2 = int(round(max([rect.top(), rect.bottom()])))

            # ~ print( 'x1, x2, y1, y2: %d, %d, %d, %d' % (x1, x2, y1, y2))
            # ~ print('width: ', rect.width(), '. height: ', rect.height())

            # for circle and ellipse, we want width and height to be divisible
            # by two. so we'll add an extra pixel if necessary
            x2 += (x2 - x1) % 2  # remainder will be 1 for odd widths
            y2 += (y2 - y1) % 2

            # ~ print( 'x2, y2: %d, %d' % (x2, y2))

            if abs(y2 - y1) > 20:

                # ~ dx = x2-x1
                # ~ dy = y2-y1

                # use +1 because slice notation is screwy
                subimage = self.inputImg[y1:y2, x1:x2].copy()
                self.showImg(subimage)

                if subimage.ndim == 3:
                    subimage = cv.cvtColor(subimage, cv.COLOR_RGB2BGR)

                fname = 'target_' + str(uuid4()) + '.png'
                # ~ print(fname)
                self.fname = fname
                cv.imwrite(fname, subimage)
                self.lblStatus.setText('target set')
            else:
                self.lblStatus.setText('')
            self.graphicsView.scene().removeItem(self.targetShape)
        except RuntimeError as e:
            print('RuntimeError in mouseButtonRelease. ', e)

    def mainFunc(self, playing, scriptList, row):
        # ~ print(self.inputImg)
        if self.inputImg is not None:
            self.outputImg = self.inputImg  # here we don't even need to
            # make a copy. it's a straight
            # pass through.
            # ~ self.outputImg = self.inputImg.copy()
            # ~ print('copy')
        if self.image is None:
            if self.fname:
                img = cv.imread(self.fname)
                if img is None:
                    self.lblStatus.setText('target file not found')
                else:
                    if img.ndim == 3:
                        img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
                    self.showImg(img)

    def resizeEvent(self, event=0):
        try:
            rect = self.targetView.scene().items()[0].boundingRect()
            self.targetView.fitInView(rect, Qt.KeepAspectRatio)
        except IndexError:
            pass

    def radio_handler(self):
        self.bgrpDrawMethod.button(1).setChecked(True)
コード例 #16
0
class NetScene(QGraphicsScene):
    SPACE_WIDTH = 200  # 留白宽度

    def __init__(self, parent, announces, api):
        super().__init__(parent)
        self.announces = announces
        self.api = api

        api['Scene.setBackgroundColor'] = self.setBackgroundBrush
        api['Scene.setBackgroundPixmap'] = self.setBackgroundPixmap
        api['Scene.setLayout'] = self.setLayout
        api['Scene.renderNode'] = self.renderNode
        api['Scene.renderEdge'] = self.renderEdge
        api['Scene.selectedNode'] = lambda: self.selected_node

        announces['addICNNode'].append(self.addICNNode)
        announces['addICNEdge'].append(self.addICNEdge)

        self.node_table = {}  # { node_id:node_item, ... }
        self.edge_table = {}  # { edge_id:edge_item, ... }

        self.cache_pixmap = None
        self.background_item = QGraphicsPixmapItem()
        self.addItem(self.background_item)

        self.selected_node = None

    def setLayout(self, layout):
        """
        设置场景中节点的位置
        :param layout: {node_id:(x,y), ...}
        :return: None
        """
        for node_id, (x, y) in layout.items():
            self.node_table[node_id].checkPos(x, y)
        self.adjustBounding()

    def addICNNode(self, node_id):
        node_item = NodeItem(node_id)
        node_item.press_callback = self.pressNode
        node_item.release_callback = self.releaseNode
        node_item.double_click_callback = self.announces['doubleClickNode']
        node_item.move_callback = self.moveNode

        self.node_table[node_id] = node_item
        self.addItem(node_item)
        self.update()

    def addICNEdge(self, edge_id):
        edge_item = EdgeItem(edge_id)
        edge_item.double_click_callback = self.announces['doubleClickEdge']

        src_id, dst_id = edge_id
        src_item = self.node_table[src_id]
        dst_item = self.node_table[dst_id]
        edge_item.adjust(src_item.pos(), dst_item.pos())

        self.edge_table[edge_id] = edge_item
        self.addItem(edge_item)
        self.update()

    def renderNode(self, node_id, style: dict):
        self.node_table[node_id].setStyle(style)

    def renderEdge(self, edge_id, style: dict):
        self.edge_table[edge_id].setStyle(style)

    # ------------------------------------------------------------------------------------------------------------------
    def setBackgroundPixmap(self, pixmap):
        if pixmap is None:
            self.background_item.hide()
        else:
            if self.cache_pixmap is not pixmap:
                self.cache_pixmap = pixmap
                self.background_item.setPixmap(pixmap)
                # 设置位置居中
                rect = self.background_item.boundingRect()
                self.background_item.setPos(-rect.center())
            self.background_item.show()

    def adjustBounding(self):
        self.setSceneRect(
            self.itemsBoundingRect().adjusted(
                -self.SPACE_WIDTH,
                -self.SPACE_WIDTH,
                self.SPACE_WIDTH,
                self.SPACE_WIDTH
            )
        )
        self.update()

    # ------------------------------------------------------------------------------------------------------------------
    def pressNode(self, node_id):
        self.selected_node= node_id
        self.announces['selectedNode'](self.selected_node)

    def moveNode(self, src_id):
        """
        当一个节点位置变化时,调整与该节点链接的边
        :param src_id: 节点id
        :return: None
        """
        graph = self.api['Sim.graph']()  # 邻接表{node_id:{neighbor_id, ...}, }
        src_pos = self.node_table[src_id].pos()
        for dst_id in graph[src_id]:
            dst_pos = self.node_table[dst_id].pos()
            self.edge_table[(src_id, dst_id)].adjust(src_pos, dst_pos)
            self.edge_table[(dst_id, src_id)].adjust(dst_pos, src_pos)

    def releaseNode(self, node_id):
        node_item = self.node_table[node_id]
        pos = node_item.pos()
        pos = pos.x(), pos.y()
        self.announces['sceneNodeMoved'](node_id, pos)
コード例 #17
0
ファイル: camera_view.py プロジェクト: mrazr/antarstick
class CameraView(QGraphicsObject):

    font: QFont = QFont("monospace", 16)
    stick_link_requested = pyqtSignal(StickWidget)
    stick_context_menu = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject')
    stick_widgets_out_of_sync = pyqtSignal('PyQt_PyObject')
    visibility_toggled = pyqtSignal()
    synchronize_clicked = pyqtSignal('PyQt_PyObject')
    previous_photo_clicked = pyqtSignal('PyQt_PyObject')
    next_photo_clicked = pyqtSignal('PyQt_PyObject')
    sync_confirm_clicked = pyqtSignal('PyQt_PyObject')
    sync_cancel_clicked = pyqtSignal('PyQt_PyObject')
    first_photo_clicked = pyqtSignal('PyQt_PyObject')
    enter_pressed = pyqtSignal()

    def __init__(self, scale: float, parent: Optional[QGraphicsItem] = None):
        QGraphicsObject.__init__(self, parent)
        self.current_highlight_color = QColor(0, 0, 0, 0)
        self.current_timer = -1
        self.scaling = scale
        self.pixmap = QGraphicsPixmapItem(self)
        self.stick_widgets: List[StickWidget] = []
        self.link_cam_text = QGraphicsSimpleTextItem("Link camera...", self)
        self.link_cam_text.setZValue(40)
        self.link_cam_text.setVisible(False)
        self.link_cam_text.setFont(CameraView.font)
        self.link_cam_text.setPos(0, 0)
        self.link_cam_text.setPen(QPen(QColor(255, 255, 255, 255)))
        self.link_cam_text.setBrush(QBrush(QColor(255, 255, 255, 255)))

        self.show_add_buttons = False
        self.camera = None

        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.show_stick_widgets = False
        self.setAcceptHoverEvents(True)
        self.stick_edit_mode = False

        self.original_pixmap = self.pixmap.pixmap()

        self.hovered = False

        self.mode = 0  # TODO make enum Mode
        self.click_handler = None
        self.double_click_handler: Callable[[int, int], None] = None

        self.stick_widget_mode = StickMode.Display

        self.highlight_animation = QPropertyAnimation(self, b"highlight_color")
        self.highlight_animation.setEasingCurve(QEasingCurve.Linear)
        self.highlight_animation.valueChanged.connect(
            self.handle_highlight_color_changed)
        self.highlight_rect = QGraphicsRectItem(self)
        self.highlight_rect.setZValue(4)
        self.highlight_rect.setPen(QPen(QColor(0, 0, 0, 0)))
        self.title_btn = Button('btn_title', '', parent=self)
        self.title_btn.setFlag(QGraphicsItem.ItemIgnoresTransformations, False)
        self.title_btn.setZValue(5)
        self.title_btn.setVisible(False)

        self.sticks_without_width: List[Stick] = []
        self.current_image_name: str = ''
        self.control_widget = ControlWidget(parent=self)
        self.control_widget.setFlag(QGraphicsItem.ItemIgnoresTransformations,
                                    False)
        self.control_widget.setVisible(True)
        self._connect_control_buttons()
        self.image_available = True
        self.blur_eff = QGraphicsBlurEffect()
        self.blur_eff.setBlurRadius(5.0)
        self.blur_eff.setEnabled(False)
        self.pixmap.setGraphicsEffect(self.blur_eff)
        self.overlay_message = QGraphicsSimpleTextItem('not available',
                                                       parent=self)
        font = self.title_btn.font
        font.setPointSize(48)
        self.overlay_message.setFont(font)
        self.overlay_message.setBrush(QBrush(QColor(200, 200, 200, 200)))
        self.overlay_message.setPen(QPen(QColor(0, 0, 0, 200), 2.0))
        self.overlay_message.setVisible(False)
        self.overlay_message.setZValue(6)

        self.stick_box = QGraphicsRectItem(parent=self)
        self.stick_box.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.stick_box.setVisible(False)
        self.stick_box_start_pos = QPoint()

    def _connect_control_buttons(self):
        self.control_widget.synchronize_btn.clicked.connect(
            lambda: self.synchronize_clicked.emit(self))
        self.control_widget.prev_photo_btn.clicked.connect(
            lambda: self.previous_photo_clicked.emit(self))
        self.control_widget.next_photo_btn.clicked.connect(
            lambda: self.next_photo_clicked.emit(self))
        self.control_widget.accept_btn.clicked.connect(
            lambda: self.sync_confirm_clicked.emit(self))
        self.control_widget.cancel_btn.clicked.connect(
            lambda: self.sync_cancel_clicked.emit(self))
        self.control_widget.first_photo_btn.clicked.connect(
            lambda: self.first_photo_clicked.emit(self))

    def paint(self, painter: QPainter,
              option: PyQt5.QtWidgets.QStyleOptionGraphicsItem,
              widget: QWidget):
        if self.pixmap.pixmap().isNull():
            return
        painter.setRenderHint(QPainter.Antialiasing, True)

        if self.show_stick_widgets:
            brush = QBrush(QColor(255, 255, 255, 100))
            painter.fillRect(self.boundingRect(), brush)

        if self.mode and self.hovered:
            pen = QPen(QColor(0, 125, 200, 255))
            pen.setWidth(4)
            painter.setPen(pen)

    def boundingRect(self) -> PyQt5.QtCore.QRectF:
        return self.pixmap.boundingRect().united(
            self.title_btn.boundingRect().translated(self.title_btn.pos()))

    def initialise_with(self, camera: Camera):
        if self.camera is not None:
            self.camera.stick_added.disconnect(self.handle_stick_created)
            self.camera.sticks_added.disconnect(self.handle_sticks_added)
            self.camera.stick_removed.disconnect(self.handle_stick_removed)
            self.camera.sticks_removed.disconnect(self.handle_sticks_removed)
            self.camera.stick_changed.disconnect(self.handle_stick_changed)
        self.camera = camera
        self.prepareGeometryChange()
        self.set_image(camera.rep_image, Path(camera.rep_image_path).name)
        self.title_btn.set_label(self.camera.folder.name)
        self.title_btn.set_height(46)
        self.title_btn.fit_to_contents()
        self.title_btn.set_width(int(self.boundingRect().width()))
        self.title_btn.setPos(0, self.boundingRect().height())
        self.control_widget.title_btn.set_label(self.camera.folder.name)
        self.camera.stick_added.connect(self.handle_stick_created)
        self.camera.sticks_added.connect(self.handle_sticks_added)
        self.camera.stick_removed.connect(self.handle_stick_removed)
        self.camera.sticks_removed.connect(self.handle_sticks_removed)
        self.camera.stick_changed.connect(self.handle_stick_changed)

        self.control_widget.set_font_height(32)
        self.control_widget.set_widget_height(
            self.title_btn.boundingRect().height())
        self.control_widget.set_widget_width(int(self.boundingRect().width()))
        self.control_widget.setPos(0,
                                   self.pixmap.boundingRect().height()
                                   )  #self.boundingRect().height())
        self.control_widget.set_mode('view')
        self.update_stick_widgets()

    def set_image(self,
                  img: Optional[np.ndarray] = None,
                  image_name: Optional[str] = None):
        if img is None:
            self.show_overlay_message('not available')
            return
        self.show_overlay_message(None)
        self.prepareGeometryChange()
        barray = QByteArray(img.tobytes())
        image = QImage(barray, img.shape[1], img.shape[0],
                       QImage.Format_BGR888)
        self.original_pixmap = QPixmap.fromImage(image)
        self.pixmap.setPixmap(self.original_pixmap)
        self.highlight_rect.setRect(self.boundingRect())
        self.current_image_name = image_name

    def update_stick_widgets(self):
        stick_length = 60
        for stick in self.camera.sticks:
            sw = StickWidget(stick, self.camera, self)
            sw.set_mode(self.stick_widget_mode)
            self.connect_stick_widget_signals(sw)
            self.stick_widgets.append(sw)
            stick_length = stick.length_cm
        self.update_stick_box()
        self.scene().update()

    def scale_item(self, factor: float):
        self.prepareGeometryChange()
        pixmap = self.original_pixmap.scaledToHeight(
            int(self.original_pixmap.height() * factor))
        self.pixmap.setPixmap(pixmap)
        self.__update_title()

    def set_show_stick_widgets(self, value: bool):
        for sw in self.stick_widgets:
            sw.setVisible(value)
        self.scene().update()

    def hoverEnterEvent(self, e: QGraphicsSceneHoverEvent):
        self.hovered = True
        self.scene().update(self.sceneBoundingRect())

    def hoverLeaveEvent(self, e: QGraphicsSceneHoverEvent):
        self.hovered = False
        self.scene().update(self.sceneBoundingRect())

    def mousePressEvent(self, e: QGraphicsSceneMouseEvent):
        super().mousePressEvent(e)

    def mouseReleaseEvent(self, e: QGraphicsSceneMouseEvent):
        if self.mode == 1:
            self.click_handler(self.camera)

    def mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent):
        if self.stick_widget_mode == StickMode.EditDelete:
            x = event.pos().toPoint().x()
            y = event.pos().toPoint().y()
            stick = self.camera.create_new_sticks(
                [(np.array([[x, y - 50], [x, y + 50]]), 3)],
                self.current_image_name)[
                    0]  #self.dataset.create_new_stick(self.camera)
            self.sticks_without_width.append(stick)

    def set_button_mode(self, click_handler: Callable[[Camera], None],
                        data: str):
        self.mode = 1  # TODO make a proper ENUM
        self.click_handler = lambda c: click_handler(c, data)

    def set_display_mode(self):
        self.mode = 0  # TODO make a proper ENUM
        self.click_handler = None

    def _remove_stick_widgets(self):
        for sw in self.stick_widgets:
            sw.setParentItem(None)
            self.scene().removeItem(sw)
            sw.deleteLater()
        self.stick_widgets.clear()

    def handle_stick_created(self, stick: Stick):
        if stick.camera_id != self.camera.id:
            return
        sw = StickWidget(stick, self.camera, self)
        sw.set_mode(self.stick_widget_mode)
        self.connect_stick_widget_signals(sw)
        self.stick_widgets.append(sw)
        self.stick_widgets_out_of_sync.emit(self)
        self.update()

    def handle_stick_removed(self, stick: Stick):
        if stick.camera_id != self.camera.id:
            return
        stick_widget = next(
            filter(lambda sw: sw.stick.id == stick.id, self.stick_widgets))
        self.disconnect_stick_widget_signals(stick_widget)
        self.stick_widgets.remove(stick_widget)
        stick_widget.setParentItem(None)
        self.scene().removeItem(stick_widget)
        stick_widget.deleteLater()
        self.update()

    def handle_sticks_removed(self, sticks: List[Stick]):
        if sticks[0].camera_id != self.camera.id:
            return
        for stick in sticks:
            to_remove: StickWidget = None
            for sw in self.stick_widgets:
                if sw.stick.id == stick.id:
                    to_remove = sw
                    break
            self.stick_widgets.remove(to_remove)
            to_remove.setParentItem(None)
            if self.scene() is not None:
                self.scene().removeItem(to_remove)
            to_remove.deleteLater()
        self.update()

    def handle_sticks_added(self, sticks: List[Stick]):
        if len(sticks) == 0:
            return
        if sticks[0].camera_id != self.camera.id:
            return
        for stick in sticks:
            sw = StickWidget(stick, self.camera, self)
            sw.set_mode(self.stick_widget_mode)
            self.connect_stick_widget_signals(sw)
            self.stick_widgets.append(sw)
        self.update_stick_box()
        self.stick_widgets_out_of_sync.emit(self)
        self.update()

    def connect_stick_widget_signals(self, stick_widget: StickWidget):
        stick_widget.delete_clicked.connect(
            self.handle_stick_widget_delete_clicked)
        stick_widget.stick_changed.connect(self.handle_stick_widget_changed)
        stick_widget.link_initiated.connect(self.handle_stick_link_initiated)
        stick_widget.right_clicked.connect(
            self.handle_stick_widget_context_menu)

    def disconnect_stick_widget_signals(self, stick_widget: StickWidget):
        stick_widget.delete_clicked.disconnect(
            self.handle_stick_widget_delete_clicked)
        stick_widget.stick_changed.disconnect(self.handle_stick_widget_changed)
        stick_widget.link_initiated.disconnect(
            self.handle_stick_link_initiated)
        stick_widget.right_clicked.disconnect(
            self.handle_stick_widget_context_menu)

    def handle_stick_widget_delete_clicked(self, stick: Stick):
        self.camera.remove_stick(stick)

    def set_stick_widgets_mode(self, mode: StickMode):
        self.stick_widget_mode = mode
        for sw in self.stick_widgets:
            sw.set_mode(mode)
        self.set_stick_edit_mode(mode == StickMode.Edit)

    def handle_stick_widget_changed(self, stick_widget: StickWidget):
        self.camera.stick_changed.emit(stick_widget.stick)

    def handle_stick_changed(self, stick: Stick):
        if stick.camera_id != self.camera.id:
            return
        sw = next(
            filter(lambda _sw: _sw.stick.id == stick.id, self.stick_widgets))
        sw.adjust_line()
        sw.update_tooltip()

    def handle_stick_link_initiated(self, stick_widget: StickWidget):
        self.stick_link_requested.emit(stick_widget)

    def get_top_left(self) -> QPointF:
        return self.sceneBoundingRect().topLeft()

    def get_top_right(self) -> QPointF:
        return self.sceneBoundingRect().topRight()

    def highlight(self, color: Optional[QColor]):
        if color is None:
            self.highlight_animation.stop()
            self.highlight_rect.setVisible(False)
            return
        alpha = color.alpha()
        color.setAlpha(0)
        self.highlight_animation.setStartValue(color)
        self.highlight_animation.setEndValue(color)
        color.setAlpha(alpha)
        self.highlight_animation.setKeyValueAt(0.5, color)
        self.highlight_animation.setDuration(2000)
        self.highlight_animation.setLoopCount(-1)
        self.highlight_rect.setPen(QPen(color))
        self.highlight_rect.setVisible(True)
        self.highlight_animation.start()

    @pyqtProperty(QColor)
    def highlight_color(self) -> QColor:
        return self.current_highlight_color

    @highlight_color.setter
    def highlight_color(self, color: QColor):
        self.current_highlight_color = color

    def handle_highlight_color_changed(self, color: QColor):
        self.highlight_rect.setBrush(QBrush(color))
        self.update()

    def handle_stick_widget_context_menu(self, sender: Dict[str, StickWidget]):
        self.stick_context_menu.emit(sender['stick_widget'], self)

    def show_overlay_message(self, msg: Optional[str]):
        if msg is None:
            self.overlay_message.setVisible(False)
            self.blur_eff.setEnabled(False)
            return
        self.overlay_message.setText(msg)
        self.overlay_message.setPos(
            self.pixmap.boundingRect().center() -
            QPointF(0.5 * self.overlay_message.boundingRect().width(), 0.5 *
                    self.overlay_message.boundingRect().height()))
        self.overlay_message.setVisible(True)
        self.blur_eff.setEnabled(True)

    def show_status_message(self, msg: Optional[str]):
        if msg is None:
            self.control_widget.set_title_text(self.camera.folder.name)
        else:
            self.control_widget.set_title_text(msg)

    def update_stick_box(self):
        left = 9000
        right = 0
        top = 9000
        bottom = -1

        for stick in self.camera.sticks:
            left = min(left, min(stick.top[0], stick.bottom[0]))
            right = max(right, max(stick.top[0], stick.bottom[0]))
            top = min(top, min(stick.top[1], stick.bottom[1]))
            bottom = max(bottom, max(stick.top[1], stick.bottom[1]))
        left -= 100
        right += 100
        top -= 100
        bottom += 100
        self.stick_box.setRect(left, top, right - left, bottom - top)
        pen = QPen(QColor(0, 100, 200, 200))
        pen.setWidth(2)
        pen.setStyle(Qt.DashLine)
        self.stick_box.setPen(pen)

    def set_stick_edit_mode(self, is_edit: bool):
        if is_edit:
            self.update_stick_box()
            self.stick_box_start_pos = self.stick_box.pos()
            for sw in self.stick_widgets:
                sw.setParentItem(self.stick_box)
        else:
            offset = self.stick_box.pos() - self.stick_box_start_pos
            for sw in self.stick_widgets:
                stick = sw.stick
                stick.translate(np.array([int(offset.x()), int(offset.y())]))
                sw.setParentItem(self)
                sw.set_stick(stick)
            self.stick_box.setParentItem(None)
            self.stick_box = QGraphicsRectItem(self)
            self.stick_box.setFlag(QGraphicsItem.ItemIsMovable, True)
            self.stick_box.setVisible(False)
        self.stick_box.setVisible(is_edit)

    def keyPressEvent(self, event: QKeyEvent) -> None:
        pass

    def keyReleaseEvent(self, event: QKeyEvent) -> None:
        if event.key() in [Qt.Key_Right, Qt.Key_Tab, Qt.Key_Space]:
            self.control_widget.next_photo_btn.click_button(True)
        elif event.key() in [Qt.Key_Left]:
            self.control_widget.prev_photo_btn.click_button(True)
        elif event.key() == Qt.Key_S:
            self.enter_pressed.emit()
コード例 #18
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)
コード例 #19
0
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