Ejemplo n.º 1
0
    def __init__(self, parent=None):
        QGraphicsView.__init__(self, parent)
        self.setFixedWidth(56)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # self.setFrameStyle(QFrame.NoFrame)
        # self.setStyleSheet("""
        #     QGraphicsView { border: 1px solid black }
        # """)

        scene = QGraphicsScene()
        scene.addSimpleText('nice!!')
        self.text = scene.addText('hello')

        scene.addText('world')
        self.setScene(scene)
Ejemplo n.º 2
0
class Overview(QGraphicsView):
    """Show an overview of data, such as hypnogram and data in memory.

    Attributes
    ----------
    parent : instance of QMainWindow
        the main window.
    config : ConfigChannels
        preferences for this widget

    minimum : int or float
        start time of the recording, from the absolute time of start_time in s
    maximum : int or float
        length of the recordings in s
    start_time : datetime
        absolute start time of the recording

    scene : instance of QGraphicsScene
        to keep track of the objects
    idx_current : QGraphicsRectItem
        instance of the current time window
    idx_markers : list of QGraphicsRectItem
        list of markers in the dataset
    idx_annot : list of QGraphicsRectItem
        list of user-made annotations
    """
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.config = ConfigOverview(self.update_settings)

        self.minimum = None
        self.maximum = None
        self.start_time = None  # datetime, absolute start time

        self.scene = None
        self.idx_current = None
        self.idx_markers = []
        self.idx_annot = []

        self.setMinimumHeight(TOTAL_HEIGHT + 30)

    def update(self):
        """Read full duration and update maximum."""
        if self.parent.info.dataset is not None:
            # read from the dataset, if available
            header = self.parent.info.dataset.header
            maximum = header['n_samples'] / header['s_freq']  # in s
            self.minimum = 0
            self.maximum = maximum
            self.start_time = self.parent.info.dataset.header['start_time']

        elif self.parent.notes.annot is not None:
            # read from annotations
            annot = self.parent.notes.annot
            self.minimum = annot.first_second
            self.maximum = annot.last_second
            self.start_time = annot.start_time

        # make it time-zone unaware
        self.start_time = self.start_time.replace(tzinfo=None)

        self.parent.value('window_start', 0)  # the only value that is reset

        self.display()

    def display(self):
        """Updates the widgets, especially based on length of recordings."""
        lg.debug('GraphicsScene is between {}s and {}s'.format(self.minimum,
                                                               self.maximum))

        x_scale = 1 / self.parent.value('overview_scale')
        lg.debug('Set scene x-scaling to {}'.format(x_scale))

        self.scale(1 / self.transform().m11(), 1)  # reset to 1
        self.scale(x_scale, 1)

        self.scene = QGraphicsScene(self.minimum, 0,
                                    self.maximum,
                                    TOTAL_HEIGHT)
        self.setScene(self.scene)

        # reset annotations
        self.idx_markers = []
        self.idx_annot = []

        self.display_current()

        for name, pos in BARS.items():
            item = QGraphicsRectItem(self.minimum, pos['pos0'],
                                     self.maximum, pos['pos1'])
            item.setToolTip(pos['tip'])
            self.scene.addItem(item)

        self.add_timestamps()

    def add_timestamps(self):
        """Add timestamps at the bottom of the overview."""
        transform, _ = self.transform().inverted()

        stamps = _make_timestamps(self.start_time, self.minimum, self.maximum,
                                  self.parent.value('timestamp_steps'))

        for stamp, xpos in zip(*stamps):
            text = self.scene.addSimpleText(stamp)
            text.setFlag(QGraphicsItem.ItemIgnoresTransformations)

            # set xpos and adjust for text width
            text_width = text.boundingRect().width() * transform.m11()
            text.setPos(xpos - text_width / 2, TIME_HEIGHT)

    def update_settings(self):
        """After changing the settings, we need to recreate the whole image."""
        self.display()
        self.display_markers()
        if self.parent.notes.annot is not None:
            self.parent.notes.display_notes()

    def update_position(self, new_position=None):
        """Update the cursor position and much more.

        Parameters
        ----------
        new_position : int or float
            new position in s, for plotting etc.

        Notes
        -----
        This is a central function. It updates the cursor, then updates
        the traces, the scores, and the power spectrum. In other words, this
        function is responsible for keep track of the changes every time
        the start time of the window changes.
        """
        if new_position is not None:
            lg.debug('Updating position to {}'.format(new_position))
            self.parent.value('window_start', new_position)
            self.idx_current.setPos(new_position, 0)

            current_time = (self.start_time +
                            timedelta(seconds=new_position))
            msg = 'Current time: ' + current_time.strftime('%H:%M:%S')
            self.parent.statusBar().showMessage(msg)
        else:
            lg.debug('Updating position at {}'
                     ''.format(self.parent.value('window_start')))

        if self.parent.info.dataset is not None:
            self.parent.traces.read_data()
            if self.parent.traces.data is not None:
                self.parent.traces.display()
                self.parent.spectrum.display_window()

        if self.parent.notes.annot is not None:
            self.parent.notes.set_stage_index()

        self.display_current()

    def display_current(self):
        """Create a rectangle showing the current window."""
        if self.idx_current in self.scene.items():
            self.scene.removeItem(self.idx_current)

        item = QGraphicsRectItem(0,
                                 CURR['pos0'],
                                 self.parent.value('window_length'),
                                 CURR['pos1'])
        # it's necessary to create rect first, and then move it
        item.setPos(self.parent.value('window_start'), 0)
        item.setPen(QPen(Qt.lightGray))
        item.setBrush(QBrush(Qt.lightGray))
        item.setZValue(-10)
        self.scene.addItem(item)
        self.idx_current = item

    def display_markers(self):
        """Mark all the markers, from the dataset.

        This function should be called only when we load the dataset or when
        we change the settings.
        """
        for rect in self.idx_markers:
            self.scene.removeItem(rect)
        self.idx_markers = []

        markers = []
        if self.parent.info.markers is not None:
            if self.parent.value('marker_show'):
                markers = self.parent.info.markers

        for mrk in markers:
            rect = QGraphicsRectItem(mrk['start'],
                                     BARS['markers']['pos0'],
                                     mrk['end'] - mrk['start'],
                                     BARS['markers']['pos1'])
            self.scene.addItem(rect)

            color = self.parent.value('marker_color')
            rect.setPen(QPen(QColor(color)))
            rect.setBrush(QBrush(QColor(color)))
            rect.setZValue(-5)
            self.idx_markers.append(rect)

    def display_annotations(self):
        """Mark all the bookmarks/events, from annotations.

        This function is similar to display_markers, but they are called at
        different stages (f.e. when loading annotations file), so we keep them
        separate
        """
        for rect in self.idx_annot:
            self.scene.removeItem(rect)
        self.idx_annot = []

        if self.parent.notes.annot is None:
            return

        bookmarks = []
        events = []
        if self.parent.value('annot_show'):
            bookmarks = self.parent.notes.annot.get_bookmarks()
            events = self.parent.notes.get_selected_events()

        annotations = bookmarks + events

        for annot in annotations:
            rect = QGraphicsRectItem(annot['start'],
                                     BARS['annot']['pos0'],
                                     annot['end'] - annot['start'],
                                     BARS['annot']['pos1'])
            self.scene.addItem(rect)

            if annot in bookmarks:
                color = self.parent.value('annot_bookmark_color')
            if annot in events:
                color = convert_name_to_color(annot['name'])

            rect.setPen(QPen(QColor(color), LINE_WIDTH))
            rect.setBrush(QBrush(QColor(color)))
            rect.setZValue(-5)
            self.idx_annot.append(rect)

        for epoch in self.parent.notes.annot.epochs:
            self.mark_stages(epoch['start'],
                             epoch['end'] - epoch['start'],
                             epoch['stage'])

    def mark_stages(self, start_time, length, stage_name):
        """Mark stages, only add the new ones.

        Parameters
        ----------
        start_time : int
            start time in s of the epoch being scored.
        length : int
           duration in s of the epoch being scored.
        stage_name : str
            one of the stages defined in global stages.
        """
        y_pos = BARS['stage']['pos0']

        # the -1 is really important, otherwise we stay on the edge of the rect
        old_score = self.scene.itemAt(start_time + length / 2,
                                      y_pos +
                                      STAGES[stage_name]['pos0'] +
                                      STAGES[stage_name]['pos1'] - 1,
                                      self.transform())

        # check we are not removing the black border
        if old_score is not None and old_score.pen() == NoPen:
            lg.debug('Removing old score at {}'.format(start_time))
            self.scene.removeItem(old_score)
            self.idx_annot.remove(old_score)

        rect = QGraphicsRectItem(start_time,
                                 y_pos + STAGES[stage_name]['pos0'],
                                 length,
                                 STAGES[stage_name]['pos1'])
        rect.setPen(NoPen)
        rect.setBrush(STAGES[stage_name]['color'])
        self.scene.addItem(rect)
        self.idx_annot.append(rect)

    def mousePressEvent(self, event):
        """Jump to window when user clicks on overview.

        Parameters
        ----------
        event : instance of QtCore.QEvent
            it contains the position that was clicked.
        """
        if self.scene is not None:
            x_in_scene = self.mapToScene(event.pos()).x()
            window_length = self.parent.value('window_length')
            window_start = int(floor(x_in_scene / window_length) *
                               window_length)
            self.update_position(window_start)

    def reset(self):
        """Reset the widget, and clear the scene."""
        self.minimum = None
        self.maximum = None
        self.start_time = None  # datetime, absolute start time

        self.idx_current = None
        self.idx_markers = []
        self.idx_annot = []

        if self.scene is not None:
            self.scene.clear()
        self.scene = None
Ejemplo n.º 3
0
class QMyGraphicsview(QGraphicsView):
    sigMouseMovePoint = pyqtSignal(QPoint)
    sigNetDeviceItemPress = pyqtSignal(list)
    paths = [[[0], [1, 4, 3, 2], [1, 4, 3], [1, 4], [0], [1, 6]],
             [[2, 1], [0], [2, 4, 3], [2, 4], [0], [2, 6]], [], [], [], []]
    devices = []
    line_items = []

    def __init__(self, parent=None):
        super(QMyGraphicsview, self).__init__(parent)
        self.rect = QRectF(-200, -200, 400, 400)
        self.myScene = QGraphicsScene(self.rect)
        self.setScene(self.myScene)
        self.initPoints()
        self.initGraphicSystem()

    def initPoints(self):
        # 准备6个节点圆形
        for i in range(1, 7):
            item = NetDeviceItem(self, NetNodeInfo(i, "green"))
            x = 150 * math.sin(math.radians(60 * i))
            y = 150 * math.cos(math.radians(60 * i))
            item.setPos(x, y)
            item.setBrush(Qt.green)
            item.setRect(-15, -15, 30, 30)
            item.setFlags(QGraphicsItem.ItemIsMovable
                          | QGraphicsItem.ItemIsSelectable
                          | QGraphicsItem.ItemIsFocusable)
            self.devices.append(item)

    def initGraphicSystem(self):
        # 清除所有item
        self.myScene.clear()
        # 显示scene边框
        item1 = WholeDeviceItem(self.rect, self, ["whole", "white"])
        item1.setFlags(QGraphicsItem.ItemIsSelectable
                       | QGraphicsItem.ItemIsFocusable)
        self.myScene.addItem(item1)
        # 显示节点
        for item in self.devices:
            self.myScene.addItem(item)
            text_item = self.myScene.addSimpleText(str(item.infos.id))
            text_item.setPos(item.pos().x() + 20, item.pos().y())
        self.myScene.clearSelection()

    def mouseMoveEvent(self, evt):
        self.sigMouseMovePoint.emit(evt.pos())

    def emitItemPressEvent(self, infos):
        self.sigNetDeviceItemPress.emit(infos)

    def removeAllLines(self):
        for item in self.line_items:
            self.myScene.removeItem(item)
        self.line_items.clear()

    def drawAllLines(self):
        self.removeAllLines()
        for path in self.paths:
            if len(path) == 6:
                for line in path:
                    if len(line) > 1:
                        for i in range(len(line) - 1):
                            p1 = self.devices[line[i] - 1]
                            p2 = self.devices[line[i + 1] - 1]
                            line_item = self.myScene.addLine(
                                p1.pos().x(),
                                p1.pos().y(),
                                p2.pos().x(),
                                p2.pos().y())
                            self.line_items.append(line_item)

    def drawLines(self, id):
        self.removeAllLines()
        """画出id节点的连接线"""
        for line in self.paths[id - 1]:
            if len(line) > 1:
                for i in range(len(line) - 1):
                    p1 = self.devices[line[i] - 1]
                    p2 = self.devices[line[i + 1] - 1]
                    line_item = self.myScene.addLine(p1.pos().x(),
                                                     p1.pos().y(),
                                                     p2.pos().x(),
                                                     p2.pos().y())
                    self.line_items.append(line_item)
Ejemplo n.º 4
0
class Screenshot(QGraphicsView):
    """ Main Class """

    screen_shot_grabed = pyqtSignal(QImage)
    widget_closed = pyqtSignal()

    def __init__(self, flags=constant.DEFAULT, parent=None):
        """
        flags: binary flags. see the flags in the constant.py
        """
        super().__init__(parent)

        # Init
        self.penColorNow = QColor(PENCOLOR)
        self.penSizeNow = PENSIZE
        self.fontNow = QFont('Sans')
        self.clipboard = QApplication.clipboard()

        self.drawListResult = [
        ]  # draw list that sure to be drew, [action, coord]
        self.drawListProcess = None  # the process to the result
        self.selectedArea = QRect(
        )  # a QRect instance which stands for the selected area
        self.selectedAreaRaw = QRect()
        self.mousePosition = MousePosition.OUTSIDE_AREA  # mouse position
        self.screenPixel = None
        self.textRect = None

        self.mousePressed = False
        self.action = ACTION_SELECT
        self.mousePoint = self.cursor().pos()

        self.startX, self.startY = 0, 0  # the point where you start
        self.endX, self.endY = 0, 0  # the point where you end
        self.pointPath = QPainterPath(
        )  # the point mouse passes, used by draw free line
        self.itemsToRemove = [
        ]  # the items that should not draw on screenshot picture
        self.textPosition = None

        # result
        self.target_img = None

        # Init window
        self.getscreenshot()
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)

        self.setMouseTracking(True)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setContentsMargins(0, 0, 0, 0)
        self.setStyleSheet("QGraphicsView { border-style: none; }")

        self.tooBar = MyToolBar(flags, self)
        self.tooBar.trigger.connect(self.changeAction)

        self.penSetBar = None
        if flags & constant.RECT or flags & constant.ELLIPSE or flags & constant.LINE or flags & constant.FREEPEN \
                or flags & constant.ARROW or flags & constant.TEXT:
            self.penSetBar = PenSetWidget(self)
            self.penSetBar.penSizeTrigger.connect(self.changePenSize)
            self.penSetBar.penColorTrigger.connect(self.changePenColor)
            self.penSetBar.fontChangeTrigger.connect(self.changeFont)

        self.textInput = TextInput(self)
        self.textInput.inputChanged.connect(self.textChange)
        self.textInput.cancelPressed.connect(self.cancelInput)
        self.textInput.okPressed.connect(self.okInput)

        self.graphicsScene = QGraphicsScene(0, 0, self.screenPixel.width(),
                                            self.screenPixel.height())

        self.show()
        self.setScene(self.graphicsScene)
        self.windowHandle().setScreen(QGuiApplication.screenAt(QCursor.pos()))
        self.scale = self.get_scale()
        # self.setFixedSize(self.screenPixel.width(), self.screenPixel.height())
        self.setGeometry(QGuiApplication.screenAt(QCursor.pos()).geometry())
        self.showFullScreen()
        self.redraw()

        QShortcut(QKeySequence('ctrl+s'),
                  self).activated.connect(self.saveScreenshot)
        QShortcut(QKeySequence('esc'), self).activated.connect(self.close)

    @staticmethod
    def take_screenshot(flags):
        loop = QEventLoop()
        screen_shot = Screenshot(flags)
        screen_shot.show()
        screen_shot.widget_closed.connect(loop.quit)

        loop.exec()
        img = screen_shot.target_img
        return img

    def getscreenshot(self):
        screen = QGuiApplication.screenAt(QCursor.pos())
        self.screenPixel = screen.grabWindow(0)

    def mousePressEvent(self, event):
        """
        :type event: QMouseEvent
        :param event:
        :return:
        """
        if event.button() != Qt.LeftButton:
            return

        if self.action is None:
            self.action = ACTION_SELECT

        self.startX, self.startY = event.x(), event.y()

        if self.action == ACTION_SELECT:
            if self.mousePosition == MousePosition.OUTSIDE_AREA:
                self.mousePressed = True
                self.selectedArea = QRect()
                self.selectedArea.setTopLeft(QPoint(event.x(), event.y()))
                self.selectedArea.setBottomRight(QPoint(event.x(), event.y()))
                self.redraw()
            elif self.mousePosition == MousePosition.INSIDE_AREA:
                self.mousePressed = True
            else:
                pass
        elif self.action == ACTION_MOVE_SELECTED:
            if self.mousePosition == MousePosition.OUTSIDE_AREA:
                self.action = ACTION_SELECT
                self.selectedArea = QRect()
                self.selectedArea.setTopLeft(QPoint(event.x(), event.y()))
                self.selectedArea.setBottomRight(QPoint(event.x(), event.y()))
                self.redraw()
            self.mousePressed = True
        elif self.action in DRAW_ACTION:
            self.mousePressed = True
            if self.action == ACTION_FREEPEN:
                self.pointPath = QPainterPath()
                self.pointPath.moveTo(QPoint(event.x(), event.y()))
            elif self.action == ACTION_TEXT:
                if self.textPosition is None:
                    self.textPosition = QPoint(event.x(), event.y())
                    self.textRect = None
                    self.redraw()

    def mouseMoveEvent(self, event: QMouseEvent):
        """
        :type event: QMouseEvent
        :param event:
        :return:
        """
        self.mousePoint = QPoint(event.globalPos().x(), event.globalPos().y())

        if self.action is None:
            self.action = ACTION_SELECT

        if not self.mousePressed:
            point = QPoint(event.x(), event.y())
            self.detectMousePosition(point)
            self.setCursorStyle()
            self.redraw()
        else:
            self.endX, self.endY = event.x(), event.y()

            # if self.mousePosition != OUTSIDE_AREA:
            #    self.action = ACTION_MOVE_SELECTED

            if self.action == ACTION_SELECT:
                self.selectedArea.setBottomRight(QPoint(event.x(), event.y()))
                self.redraw()
            elif self.action == ACTION_MOVE_SELECTED:
                self.selectedArea = QRect(self.selectedAreaRaw)

                if self.mousePosition == MousePosition.INSIDE_AREA:
                    moveToX = event.x() - self.startX + self.selectedArea.left(
                    )
                    moveToY = event.y() - self.startY + self.selectedArea.top()
                    if 0 <= moveToX <= self.screenPixel.width(
                    ) - 1 - self.selectedArea.width():
                        self.selectedArea.moveLeft(moveToX)
                    if 0 <= moveToY <= self.screenPixel.height(
                    ) - 1 - self.selectedArea.height():
                        self.selectedArea.moveTop(moveToY)
                    self.selectedArea = self.selectedArea.normalized()
                    self.selectedAreaRaw = QRect(self.selectedArea)
                    self.startX, self.startY = event.x(), event.y()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_LEFT_SIDE:
                    moveToX = event.x() - self.startX + self.selectedArea.left(
                    )
                    if moveToX <= self.selectedArea.right():
                        self.selectedArea.setLeft(moveToX)
                        self.selectedArea = self.selectedArea.normalized()
                        self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_RIGHT_SIDE:
                    moveToX = event.x(
                    ) - self.startX + self.selectedArea.right()
                    self.selectedArea.setRight(moveToX)
                    self.selectedArea = self.selectedArea.normalized()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_UP_SIDE:
                    moveToY = event.y() - self.startY + self.selectedArea.top()
                    self.selectedArea.setTop(moveToY)
                    self.selectedArea = self.selectedArea.normalized()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_DOWN_SIDE:
                    moveToY = event.y(
                    ) - self.startY + self.selectedArea.bottom()
                    self.selectedArea.setBottom(moveToY)
                    self.selectedArea = self.selectedArea.normalized()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_TOP_LEFT_CORNER:
                    moveToX = event.x() - self.startX + self.selectedArea.left(
                    )
                    moveToY = event.y() - self.startY + self.selectedArea.top()
                    self.selectedArea.setTopLeft(QPoint(moveToX, moveToY))
                    self.selectedArea = self.selectedArea.normalized()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_BOTTOM_RIGHT_CORNER:
                    moveToX = event.x(
                    ) - self.startX + self.selectedArea.right()
                    moveToY = event.y(
                    ) - self.startY + self.selectedArea.bottom()
                    self.selectedArea.setBottomRight(QPoint(moveToX, moveToY))
                    self.selectedArea = self.selectedArea.normalized()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_TOP_RIGHT_CORNER:
                    moveToX = event.x(
                    ) - self.startX + self.selectedArea.right()
                    moveToY = event.y() - self.startY + self.selectedArea.top()
                    self.selectedArea.setTopRight(QPoint(moveToX, moveToY))
                    self.selectedArea = self.selectedArea.normalized()
                    self.redraw()
                elif self.mousePosition == MousePosition.ON_THE_BOTTOM_LEFT_CORNER:
                    moveToX = event.x() - self.startX + self.selectedArea.left(
                    )
                    moveToY = event.y(
                    ) - self.startY + self.selectedArea.bottom()
                    self.selectedArea.setBottomLeft(QPoint(moveToX, moveToY))
                    self.redraw()
                else:
                    pass
            elif self.action == ACTION_RECT:
                self.drawRect(self.startX, self.startY, event.x(), event.y(),
                              False)
                self.redraw()
                pass
            elif self.action == ACTION_ELLIPSE:
                self.drawEllipse(self.startX, self.startY, event.x(),
                                 event.y(), False)
                self.redraw()
            elif self.action == ACTION_ARROW:
                self.drawArrow(self.startX, self.startY, event.x(), event.y(),
                               False)
                self.redraw()
            elif self.action == ACTION_LINE:
                self.drawLine(self.startX, self.startY, event.x(), event.y(),
                              False)
                self.redraw()
            elif self.action == ACTION_FREEPEN:
                y1, y2 = event.x(), event.y()
                rect = self.selectedArea.normalized()
                if y1 <= rect.left():
                    y1 = rect.left()
                elif y1 >= rect.right():
                    y1 = rect.right()

                if y2 <= rect.top():
                    y2 = rect.top()
                elif y2 >= rect.bottom():
                    y2 = rect.bottom()

                self.pointPath.lineTo(y1, y2)
                self.drawFreeLine(self.pointPath, False)
                self.redraw()

    def mouseReleaseEvent(self, event):
        """
        :type event: QMouseEvent
        :param event:
        :return:
        """
        if event.button() != Qt.LeftButton:
            return

        if self.mousePressed:
            self.mousePressed = False
            self.endX, self.endY = event.x(), event.y()

            if self.action == ACTION_SELECT:
                self.selectedArea.setBottomRight(QPoint(event.x(), event.y()))
                self.selectedAreaRaw = QRect(self.selectedArea)
                self.action = ACTION_MOVE_SELECTED
                self.redraw()
            elif self.action == ACTION_MOVE_SELECTED:
                self.selectedAreaRaw = QRect(self.selectedArea)
                self.redraw()
                # self.action = None
            elif self.action == ACTION_RECT:
                self.drawRect(self.startX, self.startY, event.x(), event.y(),
                              True)
                self.redraw()
            elif self.action == ACTION_ELLIPSE:
                self.drawEllipse(self.startX, self.startY, event.x(),
                                 event.y(), True)
                self.redraw()
            elif self.action == ACTION_ARROW:
                self.drawArrow(self.startX, self.startY, event.x(), event.y(),
                               True)
                self.redraw()
            elif self.action == ACTION_LINE:
                self.drawLine(self.startX, self.startY, event.x(), event.y(),
                              True)
                self.redraw()
            elif self.action == ACTION_FREEPEN:
                self.drawFreeLine(self.pointPath, True)
                self.redraw()

    def detectMousePosition(self, point):
        """
        :type point: QPoint
        :param point: the mouse position you want to check
        :return:
        """
        if self.selectedArea == QRect():
            self.mousePosition = MousePosition.OUTSIDE_AREA
            return

        if self.selectedArea.left() - ERRORRANGE <= point.x(
        ) <= self.selectedArea.left() and (self.selectedArea.top() - ERRORRANGE
                                           <= point.y() <=
                                           self.selectedArea.top()):
            self.mousePosition = MousePosition.ON_THE_TOP_LEFT_CORNER
        elif self.selectedArea.right() <= point.x() <= self.selectedArea.right(
        ) + ERRORRANGE and (self.selectedArea.top() - ERRORRANGE <= point.y()
                            <= self.selectedArea.top()):
            self.mousePosition = MousePosition.ON_THE_TOP_RIGHT_CORNER
        elif self.selectedArea.left() - ERRORRANGE <= point.x(
        ) <= self.selectedArea.left() and (
                self.selectedArea.bottom() <= point.y() <=
                self.selectedArea.bottom() + ERRORRANGE):
            self.mousePosition = MousePosition.ON_THE_BOTTOM_LEFT_CORNER
        elif self.selectedArea.right() <= point.x() <= self.selectedArea.right(
        ) + ERRORRANGE and (self.selectedArea.bottom() <= point.y() <=
                            self.selectedArea.bottom() + ERRORRANGE):
            self.mousePosition = MousePosition.ON_THE_BOTTOM_RIGHT_CORNER
        elif -ERRORRANGE <= point.x() - self.selectedArea.left() <= 0 and (
                self.selectedArea.topLeft().y() < point.y() <
                self.selectedArea.bottomLeft().y()):
            self.mousePosition = MousePosition.ON_THE_LEFT_SIDE
        elif 0 <= point.x() - self.selectedArea.right() <= ERRORRANGE and (
                self.selectedArea.topRight().y() < point.y() <
                self.selectedArea.bottomRight().y()):
            self.mousePosition = MousePosition.ON_THE_RIGHT_SIDE
        elif -ERRORRANGE <= point.y() - self.selectedArea.top() <= 0 and (
                self.selectedArea.topLeft().x() < point.x() <
                self.selectedArea.topRight().x()):
            self.mousePosition = MousePosition.ON_THE_UP_SIDE
        elif 0 <= point.y() - self.selectedArea.bottom() <= ERRORRANGE and (
                self.selectedArea.bottomLeft().x() < point.x() <
                self.selectedArea.bottomRight().x()):
            self.mousePosition = MousePosition.ON_THE_DOWN_SIDE
        elif not self.selectedArea.contains(point):
            self.mousePosition = MousePosition.OUTSIDE_AREA
        else:
            self.mousePosition = MousePosition.INSIDE_AREA

    def setCursorStyle(self):
        if self.action in DRAW_ACTION:
            self.setCursor(Qt.CrossCursor)
            return

        if self.mousePosition == MousePosition.ON_THE_LEFT_SIDE or \
                        self.mousePosition == MousePosition.ON_THE_RIGHT_SIDE:

            self.setCursor(Qt.SizeHorCursor)
        elif self.mousePosition == MousePosition.ON_THE_UP_SIDE or \
                        self.mousePosition == MousePosition.ON_THE_DOWN_SIDE:

            self.setCursor(Qt.SizeVerCursor)
        elif self.mousePosition == MousePosition.ON_THE_TOP_LEFT_CORNER or \
                        self.mousePosition == MousePosition.ON_THE_BOTTOM_RIGHT_CORNER:

            self.setCursor(Qt.SizeFDiagCursor)
        elif self.mousePosition == MousePosition.ON_THE_TOP_RIGHT_CORNER or \
                        self.mousePosition == MousePosition.ON_THE_BOTTOM_LEFT_CORNER:

            self.setCursor(Qt.SizeBDiagCursor)
        elif self.mousePosition == MousePosition.OUTSIDE_AREA:
            self.setCursor(Qt.ArrowCursor)
        elif self.mousePosition == MousePosition.INSIDE_AREA:
            self.setCursor(Qt.OpenHandCursor)
        else:
            self.setCursor(Qt.ArrowCursor)
            pass

    def drawMagnifier(self):
        # First, calculate the magnifier position due to the mouse position
        watchAreaWidth = 16
        watchAreaHeight = 16
        watchAreaPixmap = QPixmap()

        cursor_pos = self.mousePoint

        watchArea = QRect(
            QPoint(cursor_pos.x() - watchAreaWidth / 2,
                   cursor_pos.y() - watchAreaHeight / 2),
            QPoint(cursor_pos.x() + watchAreaWidth / 2,
                   cursor_pos.y() + watchAreaHeight / 2))
        if watchArea.left() < 0:
            watchArea.moveLeft(0)
            watchArea.moveRight(watchAreaWidth)
        if self.mousePoint.x() + watchAreaWidth / 2 >= self.screenPixel.width(
        ):
            watchArea.moveRight(self.screenPixel.width() - 1)
            watchArea.moveLeft(watchArea.right() - watchAreaWidth)
        if self.mousePoint.y() - watchAreaHeight / 2 < 0:
            watchArea.moveTop(0)
            watchArea.moveBottom(watchAreaHeight)
        if self.mousePoint.y(
        ) + watchAreaHeight / 2 >= self.screenPixel.height():
            watchArea.moveBottom(self.screenPixel.height() - 1)
            watchArea.moveTop(watchArea.bottom() - watchAreaHeight)

        # tricks to solve the hidpi impact on QCursor.pos()
        watchArea.setTopLeft(
            QPoint(watchArea.topLeft().x() * self.scale,
                   watchArea.topLeft().y() * self.scale))
        watchArea.setBottomRight(
            QPoint(watchArea.bottomRight().x() * self.scale,
                   watchArea.bottomRight().y() * self.scale))
        watchAreaPixmap = self.screenPixel.copy(watchArea)

        # second, calculate the magnifier area
        magnifierAreaWidth = watchAreaWidth * 10
        magnifierAreaHeight = watchAreaHeight * 10
        fontAreaHeight = 40

        cursorSize = 24
        magnifierArea = QRectF(
            QPoint(QCursor.pos().x() + cursorSize,
                   QCursor.pos().y() + cursorSize),
            QPoint(QCursor.pos().x() + cursorSize + magnifierAreaWidth,
                   QCursor.pos().y() + cursorSize + magnifierAreaHeight))
        if magnifierArea.right() >= self.screenPixel.width():
            magnifierArea.moveLeft(QCursor.pos().x() - magnifierAreaWidth -
                                   cursorSize / 2)
        if magnifierArea.bottom() + fontAreaHeight >= self.screenPixel.height(
        ):
            magnifierArea.moveTop(QCursor.pos().y() - magnifierAreaHeight -
                                  cursorSize / 2 - fontAreaHeight)

        # third, draw the watch area to magnifier area
        watchAreaScaled = watchAreaPixmap.scaled(
            QSize(magnifierAreaWidth * self.scale,
                  magnifierAreaHeight * self.scale))
        magnifierPixmap = self.graphicsScene.addPixmap(watchAreaScaled)
        magnifierPixmap.setOffset(magnifierArea.topLeft())

        # then draw lines and text
        self.graphicsScene.addRect(QRectF(magnifierArea),
                                   QPen(QColor(255, 255, 255), 2))
        self.graphicsScene.addLine(
            QLineF(QPointF(magnifierArea.center().x(), magnifierArea.top()),
                   QPointF(magnifierArea.center().x(),
                           magnifierArea.bottom())),
            QPen(QColor(0, 255, 255), 2))
        self.graphicsScene.addLine(
            QLineF(QPointF(magnifierArea.left(),
                           magnifierArea.center().y()),
                   QPointF(magnifierArea.right(),
                           magnifierArea.center().y())),
            QPen(QColor(0, 255, 255), 2))

        # get the rgb of mouse point
        pointRgb = QColor(self.screenPixel.toImage().pixel(self.mousePoint))

        # draw information
        self.graphicsScene.addRect(
            QRectF(
                magnifierArea.bottomLeft(),
                magnifierArea.bottomRight() + QPoint(0, fontAreaHeight + 30)),
            Qt.black, QBrush(Qt.black))
        rgbInfo = self.graphicsScene.addSimpleText(
            ' Rgb: ({0}, {1}, {2})'.format(pointRgb.red(), pointRgb.green(),
                                           pointRgb.blue()))
        rgbInfo.setPos(magnifierArea.bottomLeft() + QPoint(0, 5))
        rgbInfo.setPen(QPen(QColor(255, 255, 255), 2))

        rect = self.selectedArea.normalized()
        sizeInfo = self.graphicsScene.addSimpleText(' Size: {0} x {1}'.format(
            rect.width() * self.scale,
            rect.height() * self.scale))
        sizeInfo.setPos(magnifierArea.bottomLeft() + QPoint(0, 15) +
                        QPoint(0, fontAreaHeight / 2))
        sizeInfo.setPen(QPen(QColor(255, 255, 255), 2))

    def get_scale(self):
        return self.devicePixelRatio()

    def saveScreenshot(self,
                       clipboard=False,
                       fileName='screenshot.png',
                       picType='png'):
        fullWindow = QRect(0, 0, self.width() - 1, self.height() - 1)
        selected = QRect(self.selectedArea)
        if selected.left() < 0:
            selected.setLeft(0)
        if selected.right() >= self.width():
            selected.setRight(self.width() - 1)
        if selected.top() < 0:
            selected.setTop(0)
        if selected.bottom() >= self.height():
            selected.setBottom(self.height() - 1)

        source = (fullWindow & selected)
        source.setTopLeft(
            QPoint(source.topLeft().x() * self.scale,
                   source.topLeft().y() * self.scale))
        source.setBottomRight(
            QPoint(source.bottomRight().x() * self.scale,
                   source.bottomRight().y() * self.scale))
        image = self.screenPixel.copy(source)
        image.setDevicePixelRatio(1)

        if clipboard:
            QGuiApplication.clipboard().setImage(QImage(image),
                                                 QClipboard.Clipboard)
        else:
            image.save(fileName, picType, 10)
        self.target_img = image
        self.screen_shot_grabed.emit(QImage(image))

    def redraw(self):
        self.graphicsScene.clear()

        # draw screenshot
        self.graphicsScene.addPixmap(self.screenPixel)

        # prepare for drawing selected area
        rect = QRectF(self.selectedArea)
        rect = rect.normalized()

        topLeftPoint = rect.topLeft()
        topRightPoint = rect.topRight()
        bottomLeftPoint = rect.bottomLeft()
        bottomRightPoint = rect.bottomRight()
        topMiddlePoint = (topLeftPoint + topRightPoint) / 2
        leftMiddlePoint = (topLeftPoint + bottomLeftPoint) / 2
        bottomMiddlePoint = (bottomLeftPoint + bottomRightPoint) / 2
        rightMiddlePoint = (topRightPoint + bottomRightPoint) / 2

        # draw the picture mask
        mask = QColor(0, 0, 0, 155)

        if self.selectedArea == QRect():
            self.graphicsScene.addRect(0, 0, self.screenPixel.width(),
                                       self.screenPixel.height(),
                                       QPen(Qt.NoPen), mask)
        else:
            self.graphicsScene.addRect(0, 0, self.screenPixel.width(),
                                       topRightPoint.y(), QPen(Qt.NoPen), mask)
            self.graphicsScene.addRect(0, topLeftPoint.y(), topLeftPoint.x(),
                                       rect.height(), QPen(Qt.NoPen), mask)
            self.graphicsScene.addRect(
                topRightPoint.x(), topRightPoint.y(),
                self.screenPixel.width() - topRightPoint.x(), rect.height(),
                QPen(Qt.NoPen), mask)
            self.graphicsScene.addRect(
                0, bottomLeftPoint.y(), self.screenPixel.width(),
                self.screenPixel.height() - bottomLeftPoint.y(),
                QPen(Qt.NoPen), mask)

        # draw the toolBar
        if self.action != ACTION_SELECT:
            spacing = 5
            # show the toolbar first, then move it to the correct position
            # because the width of it may be wrong if this is the first time it shows
            self.tooBar.show()

            dest = QPointF(rect.bottomRight() -
                           QPointF(self.tooBar.width(), 0) -
                           QPointF(spacing, -spacing))
            if dest.x() < spacing:
                dest.setX(spacing)
            pen_set_bar_height = self.penSetBar.height(
            ) if self.penSetBar is not None else 0
            if dest.y() + self.tooBar.height(
            ) + pen_set_bar_height >= self.height():
                if rect.top() - self.tooBar.height(
                ) - pen_set_bar_height < spacing:
                    dest.setY(rect.top() + spacing)
                else:
                    dest.setY(rect.top() - self.tooBar.height() -
                              pen_set_bar_height - spacing)

            self.tooBar.move(dest.toPoint())

            if self.penSetBar is not None:
                self.penSetBar.show()
                self.penSetBar.move(dest.toPoint() +
                                    QPoint(0,
                                           self.tooBar.height() + spacing))

                if self.action == ACTION_TEXT:
                    self.penSetBar.showFontWidget()
                else:
                    self.penSetBar.showPenWidget()
        else:
            self.tooBar.hide()

            if self.penSetBar is not None:
                self.penSetBar.hide()

        # draw the list
        for step in self.drawListResult:
            self.drawOneStep(step)

        if self.drawListProcess is not None:
            self.drawOneStep(self.drawListProcess)
            if self.action != ACTION_TEXT:
                self.drawListProcess = None

        if self.selectedArea != QRect():
            self.itemsToRemove = []

            # draw the selected rectangle
            pen = QPen(QColor(0, 255, 255), 2)
            self.itemsToRemove.append(self.graphicsScene.addRect(rect, pen))

            # draw the drag point
            radius = QPoint(3, 3)
            brush = QBrush(QColor(0, 255, 255))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(topLeftPoint - radius, topLeftPoint + radius), pen,
                    brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(topMiddlePoint - radius, topMiddlePoint + radius),
                    pen, brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(topRightPoint - radius, topRightPoint + radius),
                    pen, brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(leftMiddlePoint - radius, leftMiddlePoint + radius),
                    pen, brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(rightMiddlePoint - radius,
                           rightMiddlePoint + radius), pen, brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(bottomLeftPoint - radius, bottomLeftPoint + radius),
                    pen, brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(bottomMiddlePoint - radius,
                           bottomMiddlePoint + radius), pen, brush))
            self.itemsToRemove.append(
                self.graphicsScene.addEllipse(
                    QRectF(bottomRightPoint - radius,
                           bottomRightPoint + radius), pen, brush))

        # draw the textedit
        if self.textPosition is not None:
            textSpacing = 50
            position = QPoint()
            if self.textPosition.x() + self.textInput.width(
            ) >= self.screenPixel.width():
                position.setX(self.textPosition.x() - self.textInput.width())
            else:
                position.setX(self.textPosition.x())

            if self.textRect is not None:
                if self.textPosition.y() + self.textInput.height(
                ) + self.textRect.height() >= self.screenPixel.height():
                    position.setY(self.textPosition.y() -
                                  self.textInput.height() -
                                  self.textRect.height())
                else:
                    position.setY(self.textPosition.y() +
                                  self.textRect.height())
            else:
                if self.textPosition.y() + self.textInput.height(
                ) >= self.screenPixel.height():
                    position.setY(self.textPosition.y() -
                                  self.textInput.height())
                else:
                    position.setY(self.textPosition.y())

            self.textInput.move(position)
            self.textInput.show()
            # self.textInput.getFocus()

        # draw the magnifier
        if self.action == ACTION_SELECT:
            self.drawMagnifier()
            if self.mousePressed:
                self.drawSizeInfo()

        if self.action == ACTION_MOVE_SELECTED:
            self.drawSizeInfo()

    # deal with every step in drawList
    def drawOneStep(self, step):
        """
        :type step: tuple
        """
        if step[0] == ACTION_RECT:
            self.graphicsScene.addRect(
                QRectF(QPointF(step[1], step[2]), QPointF(step[3], step[4])),
                step[5])
        elif step[0] == ACTION_ELLIPSE:
            self.graphicsScene.addEllipse(
                QRectF(QPointF(step[1], step[2]), QPointF(step[3], step[4])),
                step[5])
        elif step[0] == ACTION_ARROW:
            arrow = QPolygonF()

            linex = float(step[1] - step[3])
            liney = float(step[2] - step[4])
            line = sqrt(pow(linex, 2) + pow(liney, 2))

            # in case to divided by 0
            if line == 0:
                return

            sinAngel = liney / line
            cosAngel = linex / line

            # sideLength is the length of bottom side of the body of an arrow
            # arrowSize is the size of the head of an arrow, left and right
            # sides' size is arrowSize, and the bottom side's size is arrowSize / 2
            sideLength = step[5].width()
            arrowSize = 8
            bottomSize = arrowSize / 2

            tmpPoint = QPointF(step[3] + arrowSize * sideLength * cosAngel,
                               step[4] + arrowSize * sideLength * sinAngel)

            point1 = QPointF(step[1] + sideLength * sinAngel,
                             step[2] - sideLength * cosAngel)
            point2 = QPointF(step[1] - sideLength * sinAngel,
                             step[2] + sideLength * cosAngel)
            point3 = QPointF(tmpPoint.x() - sideLength * sinAngel,
                             tmpPoint.y() + sideLength * cosAngel)
            point4 = QPointF(tmpPoint.x() - bottomSize * sideLength * sinAngel,
                             tmpPoint.y() + bottomSize * sideLength * cosAngel)
            point5 = QPointF(step[3], step[4])
            point6 = QPointF(tmpPoint.x() + bottomSize * sideLength * sinAngel,
                             tmpPoint.y() - bottomSize * sideLength * cosAngel)
            point7 = QPointF(tmpPoint.x() + sideLength * sinAngel,
                             tmpPoint.y() - sideLength * cosAngel)

            arrow.append(point1)
            arrow.append(point2)
            arrow.append(point3)
            arrow.append(point4)
            arrow.append(point5)
            arrow.append(point6)
            arrow.append(point7)
            arrow.append(point1)

            self.graphicsScene.addPolygon(arrow, step[5], step[6])
        elif step[0] == ACTION_LINE:
            self.graphicsScene.addLine(
                QLineF(QPointF(step[1], step[2]), QPointF(step[3], step[4])),
                step[5])
        elif step[0] == ACTION_FREEPEN:
            self.graphicsScene.addPath(step[1], step[2])
        elif step[0] == ACTION_TEXT:
            textAdd = self.graphicsScene.addSimpleText(step[1], step[2])
            textAdd.setPos(step[3])
            textAdd.setBrush(QBrush(step[4]))
            self.textRect = textAdd.boundingRect()

    # draw the size information on the top left corner
    def drawSizeInfo(self):
        sizeInfoAreaWidth = 200
        sizeInfoAreaHeight = 30
        spacing = 5
        rect = self.selectedArea.normalized()
        sizeInfoArea = QRect(rect.left(),
                             rect.top() - spacing - sizeInfoAreaHeight,
                             sizeInfoAreaWidth, sizeInfoAreaHeight)

        if sizeInfoArea.top() < 0:
            sizeInfoArea.moveTopLeft(rect.topLeft() + QPoint(spacing, spacing))
        if sizeInfoArea.right() >= self.screenPixel.width():
            sizeInfoArea.moveTopLeft(rect.topLeft() -
                                     QPoint(spacing, spacing) -
                                     QPoint(sizeInfoAreaWidth, 0))
        if sizeInfoArea.left() < spacing:
            sizeInfoArea.moveLeft(spacing)
        if sizeInfoArea.top() < spacing:
            sizeInfoArea.moveTop(spacing)

        self.itemsToRemove.append(
            self.graphicsScene.addRect(QRectF(sizeInfoArea), Qt.white,
                                       QBrush(Qt.black)))

        sizeInfo = self.graphicsScene.addSimpleText('  {0} x {1}'.format(
            rect.width() * self.scale,
            rect.height() * self.scale))
        sizeInfo.setPos(sizeInfoArea.topLeft() + QPoint(0, 2))
        sizeInfo.setPen(QPen(QColor(255, 255, 255), 2))
        self.itemsToRemove.append(sizeInfo)

    def drawRect(self, x1, x2, y1, y2, result):
        rect = self.selectedArea.normalized()
        tmpRect = QRect(QPoint(x1, x2), QPoint(y1, y2)).normalized()
        resultRect = rect & tmpRect
        tmp = [
            ACTION_RECT,
            resultRect.topLeft().x(),
            resultRect.topLeft().y(),
            resultRect.bottomRight().x(),
            resultRect.bottomRight().y(),
            QPen(QColor(self.penColorNow), int(self.penSizeNow))
        ]
        if result:
            self.drawListResult.append(tmp)
        else:
            self.drawListProcess = tmp

    def drawEllipse(self, x1, x2, y1, y2, result):
        rect = self.selectedArea.normalized()
        tmpRect = QRect(QPoint(x1, x2), QPoint(y1, y2)).normalized()
        resultRect = rect & tmpRect
        tmp = [
            ACTION_ELLIPSE,
            resultRect.topLeft().x(),
            resultRect.topLeft().y(),
            resultRect.bottomRight().x(),
            resultRect.bottomRight().y(),
            QPen(QColor(self.penColorNow), int(self.penSizeNow))
        ]
        if result:
            self.drawListResult.append(tmp)
        else:
            self.drawListProcess = tmp

    def drawArrow(self, x1, x2, y1, y2, result):
        rect = self.selectedArea.normalized()
        if y1 <= rect.left():
            y1 = rect.left()
        elif y1 >= rect.right():
            y1 = rect.right()

        if y2 <= rect.top():
            y2 = rect.top()
        elif y2 >= rect.bottom():
            y2 = rect.bottom()

        tmp = [
            ACTION_ARROW, x1, x2, y1, y2,
            QPen(QColor(self.penColorNow), int(self.penSizeNow)),
            QBrush(QColor(self.penColorNow))
        ]
        if result:
            self.drawListResult.append(tmp)
        else:
            self.drawListProcess = tmp

    def drawLine(self, x1, x2, y1, y2, result):
        rect = self.selectedArea.normalized()
        if y1 <= rect.left():
            y1 = rect.left()
        elif y1 >= rect.right():
            y1 = rect.right()

        if y2 <= rect.top():
            y2 = rect.top()
        elif y2 >= rect.bottom():
            y2 = rect.bottom()

        tmp = [
            ACTION_LINE, x1, x2, y1, y2,
            QPen(QColor(self.penColorNow), int(self.penSizeNow))
        ]
        if result:
            self.drawListResult.append(tmp)
        else:
            self.drawListProcess = tmp

    def drawFreeLine(self, pointPath, result):
        tmp = [
            ACTION_FREEPEN,
            QPainterPath(pointPath),
            QPen(QColor(self.penColorNow), int(self.penSizeNow))
        ]
        if result:
            self.drawListResult.append(tmp)
        else:
            self.drawListProcess = tmp

    def textChange(self):
        if self.textPosition is None:
            return
        self.text = self.textInput.getText()
        self.drawListProcess = [
            ACTION_TEXT,
            str(self.text),
            QFont(self.fontNow),
            QPoint(self.textPosition),
            QColor(self.penColorNow)
        ]
        self.redraw()

    def undoOperation(self):
        if len(self.drawListResult) == 0:
            self.action = ACTION_SELECT
            self.selectedArea = QRect()
            self.selectedAreaRaw = QRect()
            self.tooBar.hide()
            if self.penSetBar is not None:
                self.penSetBar.hide()
        else:
            self.drawListResult.pop()
        self.redraw()

    def saveOperation(self):
        filename = QFileDialog.getSaveFileName(self, 'Save file',
                                               './screenshot.png',
                                               '*.png;;*.jpg')
        if len(filename[0]) == 0:
            return
        else:
            self.saveScreenshot(False, filename[0], filename[1][2:])
            self.close()

    def close(self):
        self.widget_closed.emit()
        super().close()
        self.tooBar.close()
        if self.penSetBar is not None:
            self.penSetBar.close()

    def saveToClipboard(self):
        QApplication.clipboard().setText('Test in save function')

        self.saveScreenshot(True)
        self.close()

    # slots
    def changeAction(self, nextAction):
        QApplication.clipboard().setText('Test in changeAction function')

        if nextAction == ACTION_UNDO:
            self.undoOperation()
        elif nextAction == ACTION_SAVE:
            self.saveOperation()
        elif nextAction == ACTION_CANCEL:
            self.close()
        elif nextAction == ACTION_SURE:
            self.saveToClipboard()

        else:
            self.action = nextAction

        self.setFocus()

    def changePenSize(self, nextPenSize):
        self.penSizeNow = nextPenSize

    def changePenColor(self, nextPenColor):
        self.penColorNow = nextPenColor

    def cancelInput(self):
        self.drawListProcess = None
        self.textPosition = None
        self.textRect = None
        self.textInput.hide()
        self.textInput.clearText()
        self.redraw()

    def okInput(self):
        self.text = self.textInput.getText()
        self.drawListResult.append([
            ACTION_TEXT,
            str(self.text),
            QFont(self.fontNow),
            QPoint(self.textPosition),
            QColor(self.penColorNow)
        ])
        self.textPosition = None
        self.textRect = None
        self.textInput.hide()
        self.textInput.clearText()
        self.redraw()

    def changeFont(self, font):
        self.fontNow = font
Ejemplo n.º 5
0
class Overview(QGraphicsView):
    """Show an overview of data, such as hypnogram and data in memory.

    Attributes
    ----------
    parent : instance of QMainWindow
        the main window.
    config : ConfigChannels
        preferences for this widget

    minimum : int or float
        start time of the recording, from the absolute time of start_time in s
    maximum : int or float
        length of the recordings in s
    start_time : datetime
        absolute start time of the recording

    scene : instance of QGraphicsScene
        to keep track of the objects
    idx_current : QGraphicsRectItem
        instance of the current time window
    idx_markers : list of QGraphicsRectItem
        list of markers in the dataset
    idx_annot : list of QGraphicsRectItem
        list of user-made annotations
    """
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.config = ConfigOverview(self.update_settings)

        self.minimum = None
        self.maximum = None
        self.start_time = None  # datetime, absolute start time

        self.scene = None
        self.idx_current = None
        self.idx_markers = []
        self.idx_annot = []

        self.setMinimumHeight(TOTAL_HEIGHT + 30)

    def update(self, reset=True):
        """Read full duration and update maximum.

        Parameters
        ----------
        reset: bool
            If True, current window start time is reset to 0.
        """
        if self.parent.info.dataset is not None:
            # read from the dataset, if available
            header = self.parent.info.dataset.header
            maximum = header['n_samples'] / header['s_freq']  # in s
            self.minimum = 0
            self.maximum = maximum
            self.start_time = self.parent.info.dataset.header['start_time']

        elif self.parent.notes.annot is not None:
            # read from annotations
            annot = self.parent.notes.annot
            self.minimum = annot.first_second
            self.maximum = annot.last_second
            self.start_time = annot.start_time

        # make it time-zone unaware
        self.start_time = self.start_time.replace(tzinfo=None)

        if reset:
            self.parent.value('window_start',
                              0)  # the only value that is reset

        self.display()

    def display(self):
        """Updates the widgets, especially based on length of recordings."""
        lg.debug('GraphicsScene is between {}s and {}s'.format(
            self.minimum, self.maximum))

        x_scale = 1 / self.parent.value('overview_scale')
        lg.debug('Set scene x-scaling to {}'.format(x_scale))

        self.scale(1 / self.transform().m11(), 1)  # reset to 1
        self.scale(x_scale, 1)

        self.scene = QGraphicsScene(self.minimum, 0, self.maximum,
                                    TOTAL_HEIGHT)
        self.setScene(self.scene)

        # reset annotations
        self.idx_markers = []
        self.idx_annot = []

        self.display_current()

        for name, pos in BARS.items():
            item = QGraphicsRectItem(self.minimum, pos['pos0'], self.maximum,
                                     pos['pos1'])
            item.setToolTip(pos['tip'])
            self.scene.addItem(item)

        self.add_timestamps()

    def add_timestamps(self):
        """Add timestamps at the bottom of the overview."""
        transform, _ = self.transform().inverted()

        stamps = _make_timestamps(self.start_time, self.minimum, self.maximum,
                                  self.parent.value('timestamp_steps'))

        for stamp, xpos in zip(*stamps):
            text = self.scene.addSimpleText(stamp)
            text.setFlag(QGraphicsItem.ItemIgnoresTransformations)

            # set xpos and adjust for text width
            text_width = text.boundingRect().width() * transform.m11()
            text.setPos(xpos - text_width / 2, TIME_HEIGHT)

    def update_settings(self):
        """After changing the settings, we need to recreate the whole image."""
        self.display()
        self.display_markers()
        if self.parent.notes.annot is not None:
            self.parent.notes.display_notes()

    def update_position(self, new_position=None):
        """Update the cursor position and much more.

        Parameters
        ----------
        new_position : int or float
            new position in s, for plotting etc.

        Notes
        -----
        This is a central function. It updates the cursor, then updates
        the traces, the scores, and the power spectrum. In other words, this
        function is responsible for keep track of the changes every time
        the start time of the window changes.
        """
        if new_position is not None:
            lg.debug('Updating position to {}'.format(new_position))
            self.parent.value('window_start', new_position)
            self.idx_current.setPos(new_position, 0)

            current_time = (self.start_time + timedelta(seconds=new_position))
            msg = 'Current time: ' + current_time.strftime('%H:%M:%S')
            self.parent.statusBar().showMessage(msg)
            lg.debug(msg)
        else:
            lg.debug('Updating position at {}'
                     ''.format(self.parent.value('window_start')))

        if self.parent.info.dataset is not None:
            self.parent.traces.read_data()
            if self.parent.traces.data is not None:
                self.parent.traces.display()
                self.parent.spectrum.display_window()

        if self.parent.notes.annot is not None:
            self.parent.notes.set_stage_index()
            self.parent.notes.set_quality_index()

        self.display_current()

    def display_current(self):
        """Create a rectangle showing the current window."""
        if self.idx_current in self.scene.items():
            self.scene.removeItem(self.idx_current)

        item = QGraphicsRectItem(0, CURR['pos0'],
                                 self.parent.value('window_length'),
                                 CURR['pos1'])
        # it's necessary to create rect first, and then move it
        item.setPos(self.parent.value('window_start'), 0)
        item.setPen(QPen(Qt.lightGray))
        item.setBrush(QBrush(Qt.lightGray))
        item.setZValue(-10)
        self.scene.addItem(item)
        self.idx_current = item

    def display_markers(self):
        """Mark all the markers, from the dataset.

        This function should be called only when we load the dataset or when
        we change the settings.
        """
        for rect in self.idx_markers:
            self.scene.removeItem(rect)
        self.idx_markers = []

        markers = []
        if self.parent.info.markers is not None:
            if self.parent.value('marker_show'):
                markers = self.parent.info.markers

        for mrk in markers:
            rect = QGraphicsRectItem(mrk['start'], BARS['markers']['pos0'],
                                     mrk['end'] - mrk['start'],
                                     BARS['markers']['pos1'])
            self.scene.addItem(rect)

            color = self.parent.value('marker_color')
            rect.setPen(QPen(QColor(color)))
            rect.setBrush(QBrush(QColor(color)))
            rect.setZValue(-5)
            self.idx_markers.append(rect)

    def display_annotations(self):
        """Mark all the bookmarks/events, from annotations.

        This function is similar to display_markers, but they are called at
        different stages (f.e. when loading annotations file), so we keep them
        separate
        """
        for rect in self.idx_annot:
            self.scene.removeItem(rect)
        self.idx_annot = []

        if self.parent.notes.annot is None:
            return

        bookmarks = []
        events = []
        if self.parent.value('annot_show'):
            bookmarks = self.parent.notes.annot.get_bookmarks()
            events = self.parent.notes.get_selected_events()

        annotations = bookmarks + events

        for annot in annotations:
            rect = QGraphicsRectItem(annot['start'], BARS['annot']['pos0'],
                                     annot['end'] - annot['start'],
                                     BARS['annot']['pos1'])
            self.scene.addItem(rect)

            if annot in bookmarks:
                color = self.parent.value('annot_bookmark_color')
            if annot in events:
                color = convert_name_to_color(annot['name'])

            rect.setPen(QPen(QColor(color), LINE_WIDTH))
            rect.setBrush(QBrush(QColor(color)))
            rect.setZValue(-5)
            self.idx_annot.append(rect)

        for epoch in self.parent.notes.annot.epochs:
            self.mark_stages(epoch['start'], epoch['end'] - epoch['start'],
                             epoch['stage'])
            self.mark_quality(epoch['start'], epoch['end'] - epoch['start'],
                              epoch['quality'])

        cycles = self.parent.notes.annot.rater.find('cycles')
        cyc_starts = [float(mrkr.text) for mrkr in cycles.findall('cyc_start')]
        cyc_ends = [float(mrkr.text) for mrkr in cycles.findall('cyc_end')]

        for mrkr in cyc_starts:
            self.mark_cycles(mrkr, 30)  # TODO: better width solution
        for mrkr in cyc_ends:
            self.mark_cycles(mrkr, 30, end=True)

    def mark_stages(self, start_time, length, stage_name):
        """Mark stages, only add the new ones.

        Parameters
        ----------
        start_time : int
            start time in s of the epoch being scored.
        length : int
           duration in s of the epoch being scored.
        stage_name : str
            one of the stages defined in global stages.
        """
        y_pos = BARS['stage']['pos0']

        # the -1 is really important, otherwise we stay on the edge of the rect
        old_score = self.scene.itemAt(
            start_time + length / 2, y_pos + STAGES[stage_name]['pos0'] +
            STAGES[stage_name]['pos1'] - 1, self.transform())

        # check we are not removing the black border
        if old_score is not None and old_score.pen() == NoPen:
            lg.debug('Removing old score at {}'.format(start_time))
            self.scene.removeItem(old_score)
            self.idx_annot.remove(old_score)

        rect = QGraphicsRectItem(start_time,
                                 y_pos + STAGES[stage_name]['pos0'], length,
                                 STAGES[stage_name]['pos1'])
        rect.setPen(NoPen)
        rect.setBrush(STAGES[stage_name]['color'])
        self.scene.addItem(rect)
        self.idx_annot.append(rect)

    def mark_quality(self, start_time, length, qual_name):
        """Mark signal quality, only add the new ones.

        Parameters
        ----------
        start_time : int
            start time in s of the epoch being scored.
        length : int
           duration in s of the epoch being scored.
        qual_name : str
            one of the stages defined in global stages.
        """
        y_pos = BARS['quality']['pos0']
        height = 10

        # the -1 is really important, otherwise we stay on the edge of the rect
        old_score = self.scene.itemAt(start_time + length / 2,
                                      y_pos + height - 1, self.transform())

        # check we are not removing the black border
        if old_score is not None and old_score.pen() == NoPen:
            lg.debug('Removing old score at {}'.format(start_time))
            self.scene.removeItem(old_score)
            self.idx_annot.remove(old_score)

        if qual_name == 'Poor':
            rect = QGraphicsRectItem(start_time, y_pos, length, height)
            rect.setPen(NoPen)
            rect.setBrush(Qt.black)
            self.scene.addItem(rect)
            self.idx_annot.append(rect)

    def mark_cycles(self, start_time, length, end=False):
        """Mark cycle bound, only add the new one.

        Parameters
        ----------
        start_time: int
            start time in s of the bounding epoch
        length : int
           duration in s of the epoch being scored.
        end: bool
            If True, marker will be a cycle end marker; otherwise, it's start.
        """
        y_pos = STAGES['cycle']['pos0']
        height = STAGES['cycle']['pos1']
        color = STAGES['cycle']['color']

        # the -1 is really important, otherwise we stay on the edge of the rect
        old_rect = self.scene.itemAt(start_time + length / 2,
                                     y_pos + height - 1, self.transform())

        # check we are not removing the black border
        if old_rect is not None and old_rect.pen() == NoPen:
            lg.debug('Removing old score at {}'.format(start_time))
            self.scene.removeItem(old_rect)
            self.idx_annot.remove(old_rect)

        rect = QGraphicsRectItem(start_time, y_pos, length, height)
        rect.setPen(NoPen)
        rect.setBrush(color)
        self.scene.addItem(rect)
        self.idx_annot.append(rect)

        if end:
            start_time += length
            length = -length

        kink_hi = QGraphicsRectItem(start_time, y_pos, length * 5, 1)
        kink_hi.setPen(NoPen)
        kink_hi.setBrush(color)
        self.scene.addItem(kink_hi)
        self.idx_annot.append(kink_hi)

        kink_lo = QGraphicsRectItem(start_time, y_pos + height, length * 5, 1)
        kink_lo.setPen(NoPen)
        kink_lo.setBrush(color)
        self.scene.addItem(kink_lo)
        self.idx_annot.append(kink_lo)

    def mousePressEvent(self, event):
        """Jump to window when user clicks on overview.

        Parameters
        ----------
        event : instance of QtCore.QEvent
            it contains the position that was clicked.
        """
        if self.scene is not None:
            x_in_scene = self.mapToScene(event.pos()).x()
            window_length = self.parent.value('window_length')
            window_start = int(
                floor(x_in_scene / window_length) * window_length)
            if self.parent.notes.annot is not None:
                window_start = self.parent.notes.annot.get_epoch_start(
                    window_start)
            self.update_position(window_start)

    def reset(self):
        """Reset the widget, and clear the scene."""
        self.minimum = None
        self.maximum = None
        self.start_time = None  # datetime, absolute start time

        self.idx_current = None
        self.idx_markers = []
        self.idx_annot = []

        if self.scene is not None:
            self.scene.clear()
        self.scene = None
Ejemplo n.º 6
0
class PhotoViewer(QGraphicsView):

    leftMouseButtonPressed = pyqtSignal(float, float)
    leftMouseButtonReleased = pyqtSignal(float, float)

    def __init__(self):
        QGraphicsView.__init__(self)
        self.screen = QDesktopWidget().screenGeometry()
        self.scene = QGraphicsScene()
        self.setScene(self.scene)

        self.photo_base_dir = ''
        self.pixmap_photo = QPixmap()
        self.current_photo = None
        self.zoom = BASE_ZOOM
        self.max_zoom = MAX_PHOTO_ZOOM
        self.min_zoom = MIN_ZOOM
        self.zoom_ratio = ZOOM_RATIO
        self.photo_rotate = 0

        self.ratio_mode = Qt.KeepAspectRatio
        self.setMouseTracking(True)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setFixedSize(self.get_sizes())
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setCursor(QCursor(Qt.OpenHandCursor))

    def get_sizes(self):
        """
        Returns max photo viewer size for typing module window
        :return: QSize
        """
        width = int(self.screen.width() * 0.5)
        height = int(self.screen.height() * 0.875)
        return QSize(width, height)

    def open_photo(self, filename):
        self.current_photo = filename
        path = os.path.join(self.photo_base_dir, filename)
        project_path = os.path.join(PROJECT_PATH, filename)
        self.scene.clear()
        if os.path.isfile(path):
            self._prepare_photo(path)
        elif os.path.isfile(project_path):
            self._prepare_photo(project_path)
        else:
            self.scene.addSimpleText(
                "Отсутствует фотография по указаному пути:\n\n{path}".format(
                    path=path))

    def _prepare_photo(self, path):
        self.pixmap_photo.load(path)
        self.scene.addItem(QGraphicsPixmapItem(self.pixmap_photo))
        self.auto_rotate_photo()
        self.setTransform(QTransform().rotate(self.photo_rotate))
        self.scene.update()
        self.fitInView(self.scene.sceneRect(), mode=self.ratio_mode)

    def auto_rotate_photo(self):
        width = self.pixmap_photo.width()
        height = self.pixmap_photo.height()
        if width > height:
            self.photo_rotate = 90
        else:
            self.photo_rotate = 0

    def zoom_in(self):
        self.zoom *= self.zoom_ratio
        if self.zoom > self.max_zoom:
            self.zoom = self.max_zoom
        self.setTransform(QTransform().scale(self.zoom, self.zoom).rotate(
            self.photo_rotate))

    def zoom_out(self):
        self.zoom /= self.zoom_ratio
        self.scene.setMinimumRenderSize(1)
        if self.zoom < self.min_zoom:
            self.zoom = self.min_zoom
        self.setTransform(QTransform().scale(self.zoom, self.zoom).rotate(
            (self.photo_rotate)))

    def wheelEvent(self, wheel_event):
        moose = wheel_event.angleDelta().y() / 15 / 8
        if moose > 0:
            self.zoom_in()
        elif moose < 0:
            self.zoom_out()
        # TODO: исправить зум

    def mousePressEvent(self, event):
        pos = self.mapToScene(event.pos())
        if event.button() == Qt.LeftButton:
            self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.leftMouseButtonPressed.emit(pos.x(), pos.y())
        QGraphicsView.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        QGraphicsView.mouseReleaseEvent(self, event)
        pos = self.mapToScene(event.pos())
        if event.button() == Qt.LeftButton:
            self.setDragMode(QGraphicsView.NoDrag)
        self.leftMouseButtonPressed.emit(pos.x(), pos.y())
Ejemplo n.º 7
0
class Board(QGraphicsView):
    """ Class that creates the game UI and manages player actions """

    # Default variables
    BOARD_HEIGHT = 621
    BOARD_WIDTH = 1000

    """ Initialize the GameBoard class """
    def __init__(self):
        super(Board, self).__init__()
        print("Initiated board")
        self.setHorizontalScrollBarPolicy(1)  # Qt::ScrollBarAlwaysOff = 1
        self.setVerticalScrollBarPolicy(1)

        self.scene = QGraphicsScene(self)
        self.scene.setSceneRect(0,0, self.BOARD_WIDTH, self.BOARD_HEIGHT)

        bgPixMap = QPixmap('C:\\Users\\michaelh\\Desktop\\CSC Project\\Server\\src\\resources\\grey_tiles.png')
        self.scene.setBackgroundBrush(QBrush(bgPixMap))

        # Create the walls on the map
        wallAngPixMap = QPixmap('C:\\Users\\michaelh\\Desktop\\CSC Project\\Server\\src\\resources\\angular_wall.png')
        self.angWall = Wall(wallAngPixMap, 100, 100)
        self.scene.addItem(self.angWall)


        wallCirPixMap = QPixmap('C:\\Users\\michaelh\\Desktop\\CSC Project\\Server\\src\\resources\\circular_wall.png')
        self.cirWall = Wall(wallCirPixMap, 595, 125)
        self.scene.addItem(self.cirWall)

        self.setScene(self.scene)
        self.show()

    def start(self, adrs):
        self.setupUI(adrs)

    """ Create initial state for the initial board UI """
    def setupUI(self, adrs):

        self.resize(self.BOARD_WIDTH, self.BOARD_HEIGHT)
        self.setWindowTitle('Blobbageddon!')

        # sort clients into correct teams for each blob, client 1 = driver, client 2 = gunner
        clients = []
        self.listBlobs = []
        pIndices = []
        i = 0
        # for all sockets add their key (player index) to a single list of keys and sort
        for s in adrs.keys():
            pIndices.append(list(adrs[s].keys())[0])
        pIndices = sorted(pIndices)  # sort the keys by numerical value (0,1,2,3)
        print("sorted KEYS = ", pIndices)
        # go through each index in order and then add the address of the socket that has that player index
        for index in pIndices:
            for s in adrs.keys():
                if index in list(adrs[s].keys()):
                    print("KEY: ", index)
                    print(adrs[s][index])
                    clients.append(adrs[s][index])  # since keys are sorted add in order to ads list
            i += 1
        j = 0
        while j < len(clients):
            print(clients[j])
            self.listBlobs.append((Blob(self.scene, clients[j], clients[j+1], self.BOARD_WIDTH, self.BOARD_HEIGHT)))
            j += 2
        for blob in self.listBlobs:
            self.scene.addItem(blob)

        self.timeTxt = self.scene.addSimpleText("Time: ")
        self.timeTxt.setPos(self.BOARD_WIDTH/2-100, 50)
        self.timeTxt.setScale(2)
        # brush = QBrush()
        # brush.setColor(QColor(200, 0, 0))
        # self.timeTxt.setBrush(brush)

        self.setScene(self.scene)

        self.show()

    def getXY(self, strPos):
        """ Gets x and y from the movement string passed from clients

        :param strPos: Movement string received from clients - e.g. '5.432, -145.623532;'
        :return:
        """
        # regex to distinguish if c is a correct character
        pattern = "-|\d|\."

        x = ''
        y = ''
        isXFinished = False

        # Receive the position from the client and change to x and y
        for c in strPos:
            if c == ';':
                break
            else:
                match = re.match(pattern, c)
            if not isXFinished:
                if c == ',':
                    isXFinished = True
                elif match:
                    x += c
            elif match:
                y += c

        return (x, y)

    def updatePos(self, address, strPos):
        """ If address is a driver for a blob then that blob's position will be recalculated using the string passed in

        :param address: The client that has sent the string, must appear in a blob's driver to change pos
        :param strPos:  The movement string passed by the client
        :return: True if driver, meaning address is not a gunner, allows updateGun call to be skipped
        """
        for blob in self.listBlobs:
            if blob.driver == address:

                x, y = self.getXY(strPos)

                # Divide x and y by 100 and remove decimal points to get speed factor
                x = Decimal(x)
                y = Decimal(y)
                x = int(x/100)
                y = int(y/100)


                # Speed becomes equal to x * defSpeed, y * defSpeed
                blob.xSpeed = blob.defSpeed * x
                blob.ySpeed = blob.defSpeed * y
                return True  # return true, know not to carry out gunner
            else:
                return False  # return fals, so carry out gunner

    def getFireTally(self, strGun):
        """ Get the amount of fires sent by a client, and add to the tally of fires to be made by that gunner

        :param strGun:
        :return: the amount of "fires" to add to the Gun's fire tally, followed by the remainder of strGun
        """

        # regex to distinguish if c is a correct character
        pattern = "\d+\,"
        search = re.findall(pattern, strGun)

        tally = 0

        if not search:
            return -1  # return -1, no valid angle so keep orientation the same
        else:
            for e in search:
                tally += int(e[:len(e)-1])
        return tally

    def getAngle(self, strGun):
        """ Takes a string and retries the angle passed in

        :param strGun: eg. ".532523523;3.151515125l;4.323123;"
        :return: the angle passed in eg. 3
        """
        # regex to distinguish if c is a correct character
        pattern = "-?\d+(\.\d+)\;"
        search = re.search(pattern, strGun)

        if not search:
            print("NOT VALID")
            return -1  # return -1, no valid angle so keep orientation the same
        else:
            angle = search.group(0)
            return math.trunc(float(angle[:len(angle)-1]))

    def updateGun(self, address, strGun):
        """ Take inputs from user and if the fire button has been pressed fire a projectile

        :param address: The client that sent the update gun request
        :param strGun: Contains information about what direction the gun is facing and if fire button has been pressed
        :return: True if address was a gunner
        """
        for blob in self.listBlobs:
            if blob.gunner == address: # if address passed in is a gunner carry out gunner stuff
                blob.gun.fireTally = self.getFireTally(strGun)
                blob.gun.desiredAngle = self.getAngle(strGun)

    """ Update game graphics with respect to the current game state """
    def updateGame(self, addressDict, input, currTime):
        try:
            print(currTime)
            diff = 120 - currTime
            print("diff: ", diff)
            timeString = "Time: {}".format(diff)
            print(timeString)
            self.timeTxt.setText(timeString)
            for key in addressDict.keys():
                address = addressDict[key]
            isDriver = self.updatePos(address, input)
            if not isDriver:
                isGunner = self.updateGun(address, input)
            for blob in self.listBlobs:
                self.manageProjectiles(blob)

            self.drawGame()
        except ValueError:
            print("address was not assigned a value before being used")

    def manageBlob(self, blob):
        """ Check if a blob has collided with an item, if a blob has collided with an item carry out the correct
        response.
            # Wall - Blob is not allowed to pass into a wall

        :param blob:
        :return:
        """
        isCollision = False
        collidedItems = blob.collidingItems()

        for item in collidedItems:
            if item is self.angWall or item is self.cirWall:
                isCollision = True
        print(isCollision)
        return isCollision

    def manageProjectiles(self, blob):
        """ Manage projectiles - send the call to update position on screen, remove from active projectiles if
        necessary, check for collisions with other Blobs
        :return:
        """
        # add particles to seen if they are not in the scene already
        for p in blob.gun.projectiles:
            if not p in self.scene.items():
                self.scene.addItem(p)

        # check for collision with other blobs
        # for b in self.listBlobs:
        #     if b != blob:
        collidedItems = []
        for p in blob.gun.projectiles:
            # for each projectile check if p collides with blob b
            collidedItems = p.collidingItems()
            for item in collidedItems:
                if item is self.angWall or item is self.cirWall:
                    # item is a wall remove particle
                    blob.gun.pToRemove.add(p)
                elif item in self.listBlobs and item != blob:
                    # item is a blob and is not the current blob the particles belong to
                    blob.hits += 1
                    blob.gun.pToRemove.add(p)


        tempList = []
        """ Put items that are still in the group and are marked to be removed into a temporary list to allow for their
        removal without causing an error within the for loop
        """
        for item in self.scene.items():
            if item in blob.gun.pToRemove:
                tempList.append(item)  # items in tempList are Projectiles that are marked to be removed

        for p in tempList:
            blob.gun.projectiles.remove(p)  # remove from active projectile set
            self.scene.removeItem(p)  # Remove the item from the group
            blob.gun.pToRemove.remove(p)  # Remove the projectile from the set signifying it should be removed

        print("SCORE: ", blob.hits)

    """ Draw all graphics in the game """
    def drawGame(self):
        for blob in self.listBlobs:
            blob.onDraw([self.width(), self.height()], self.angWall, self.cirWall)
            # self.manageProjectiles(blob)

    def gameLoop(self):
        """
Ejemplo n.º 8
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.ui = ui_main_window.Ui_MainWindow()
        self.ui.setupUi(self)

        self.cities = cities

        self.autoplay = False
        self._zoom = 1
        self.genetic = None
        self.paths = []
        self.last = None

        self.scene = QGraphicsScene()
        self.ui.graphicsView.setScene(self.scene)

        self.ui.popSize.setValue(20)
        self.ui.algorithm.addItem('ant', AntColony)
        self.ui.algorithm.addItem('genetic', Genetic)
        self.ui.algorithm.addItem('solver', Test)

        self.ui.playBtn.clicked.connect(self.on_play_click)
        self.ui.nextBtn.clicked.connect(self.next)
        self.ui.restartBtn.clicked.connect(self.restart)
        self.ui.showCur.stateChanged.connect(self.redraw)

        self.timer = QTimer()
        self.timer.timeout.connect(self.gen_next)
        self.timer.start(1)

        for city in self.cities:
            self.scene.addEllipse(city.x, city.y, 5, 5, QPen(),
                                  QBrush(QColor(255, 0, 0)))
            self.scene.addSimpleText(city.name).setPos(city.x + 5, city.y + 5)

        self.start()

    def restart(self):
        self.start()
        self.autoplay = True

    def start(self):
        try:
            seed = int(self.ui.seed.text())
        except ValueError as e:
            seed = -1

        if seed <= 0:
            seed = random.randint(0, 1000)

        #seed = 100
        print(seed)
        random.seed(seed)
        np.random.seed(seed)

        self.clear_paths()
        self.ui.distance.setText("")
        clazz = self.ui.algorithm.currentData()
        self.genetic = clazz(self.cities, int(self.ui.popSize.text())).run({})

    def on_play_click(self, play):
        self.autoplay = play

    def gen_next(self):
        if self.autoplay:
            self.next()

    @QtCore.pyqtSlot()
    def next(self):
        try:
            trajectories = next(self.genetic)
        except StopIteration:
            self.ui.playBtn.setChecked(False)
            self.autoplay = False
            return

        self.ui.distance.setText(f"{trajectories[0].distance:.2f} units")

        self.last = trajectories
        self.draw_trajectories(trajectories)

    def redraw(self):
        self.draw_trajectories(self.last)

    def draw_trajectories(self, trajectories):
        self.clear_paths()

        pens = [QPen(QColor(255, 0, 0)), QPen(QColor(0, 0, 0))]

        for i, trajectory in enumerate(trajectories):
            if i != 0 and not self.ui.showCur.isChecked():
                break

            path = QPainterPath()
            path.moveTo(self.cities[trajectory.path[0]].x,
                        self.cities[trajectory.path[0]].y)
            for hop in trajectory.path:
                city = self.cities[hop]
                path.lineTo(city.x, city.y)
            path.lineTo(self.cities[trajectory.path[0]].x,
                        self.cities[trajectory.path[0]].y)

            self.paths.append(self.scene.addPath(path, pen=pens[i]))

    def clear_paths(self):
        for path in self.paths:
            self.scene.removeItem(path)
        self.paths = []