Exemplo n.º 1
0
def draw_arrow_p(start: Tuple[int, int],
                 end: Tuple[int, int],
                 arrow_head1: Tuple[int, int],
                 arrow_head2: Tuple[int, int],
                 painter: QPainter,
                 filled_arrow_head: bool = False):

    # painter.setPen(color)

    painter.drawLine(*start, *end)
    if not filled_arrow_head:
        painter.drawLine(*end, *arrow_head1)
        painter.drawLine(*end, *arrow_head2)
    else:
        # brush0 = painter.pen().brush()
        # painter.setBrush(painter.pen().color())
        path = QPainterPath()
        path.moveTo(*end)
        path.lineTo(*arrow_head1)
        path.lineTo(*arrow_head2)
        path.lineTo(*end)
        painter.drawPath(path)
Exemplo n.º 2
0
class MazeView(QWidget):
    __painter: QPainter
    __maze: MazeProtocol
    __keepAspectRatio: bool
    __solverState: Optional[MazeSolverState] = None

    onMazeSolverAgentUpdate = pyqtSignal(MazeSolverState)

    def __init__(
        self,
        minimumSize: QSize,
        maze: MazeProtocol,
        parent: Optional[QWidget] = None,
        keepAspectRatio: bool = True,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ):
        super().__init__(parent=parent, *args, **kwargs)

        self.__keepAspectRatio = keepAspectRatio
        self.__maze = maze

        self.setContentsMargins(0, 0, 0, 0)

        self.setMinimumSize(minimumSize)

        # connect the onMazeSolverAgentUpdate signal with our internal method
        self.onMazeSolverAgentUpdate.connect(self.__onMazeSolverAgentUpdate)

    def __onMazeSolverAgentUpdate(self, newState: MazeSolverState) -> None:
        self.__solverState = newState
        self.update()
        # self.__drawAgent(self.__solverState)

    def resizeEvent(self, a0: QResizeEvent) -> None:
        if self.__keepAspectRatio:
            # Force the widget to rezise with a 1:1 aspect ratio
            # first calculate the smallest size axis:
            smallestAxisSize = min(a0.size().width(), a0.size().height())
            # set a newSize var with this as both the axes sizes
            newSize = QSize(smallestAxisSize, smallestAxisSize)
            # resize with this new 1:1 size
            self.resize(newSize)
            # force resizeEvent of super with implicitly modified resize

        return super().resizeEvent(a0)

    def paintEvent(self, a0: QPaintEvent) -> None:
        # Initialise a painter to paint on this widget
        self.__painter = QPainter(self)

        # only draw the agent if the solverState is bound
        # if self.__solverState is not None:
        # it is a bound variable so draw it
        if self.__solverState is not None:
            agent = self.__drawAgent(self.__solverState)
            self.__painter.drawPath(agent)

        mazePath = self.__createMazePath()
        self.__painter.drawPath(mazePath)

        self.__painter.end()

    def __drawAgent(
        self,
        solverState: MazeSolverState,
    ) -> QPainterPath:
        # solverAgentPainter = QPainter(self)
        # Calculate size of each cell to be able to draw to proper scale
        cellSize = (
            self.width() / self.__maze.size.x,
            self.height() / self.__maze.size.y,
        )

        solverAgent = QPainterPath()

        # draw the agent shape
        currentPosition = (
            solverState.currentCell.x * cellSize[0],
            solverState.currentCell.y * cellSize[1],
        )

        solverAgent.addEllipse(
            currentPosition[0],
            currentPosition[1],
            cellSize[0],
            cellSize[1],
        )

        ###
        ### Draw the direction arrow of the solver
        ###
        # get the facing direction of the solver
        direction = solverState.facingDirection
        # calculte middle of agent solver sprite to draw from
        middleOfCircle = (
            currentPosition[0] + cellSize[0] * 0.5,
            currentPosition[1] + cellSize[1] * 0.5,
        )
        # calculate the XY of the edge of the circle that we want to move to (to the facing direction)
        #   start by getting the middle of the circle and then changing it accordingly
        edgeOfCircle = [
            middleOfCircle[0],
            middleOfCircle[1],
        ]
        if direction == AbsoluteDirection.north:
            # north, so take away half the circle size in the Y direction
            edgeOfCircle[1] -= cellSize[1] * 0.5
        elif direction == AbsoluteDirection.south:
            # south, so add half the circle size in the Y direction
            edgeOfCircle[1] += cellSize[1] * 0.5
        elif direction == AbsoluteDirection.west:
            # west, so take away half the circle size in the X direction
            edgeOfCircle[0] -= cellSize[0] * 0.5
        elif direction == AbsoluteDirection.east:
            # east, so add half the circle size in the X direction
            edgeOfCircle[0] += cellSize[0] * 0.5

        # move to the center of the circle
        solverAgent.moveTo(
            middleOfCircle[0],
            middleOfCircle[1],
        )
        # draw a line from the center of the circle to the facing direction edge of the circle
        solverAgent.lineTo(
            edgeOfCircle[0],
            edgeOfCircle[1],
        )

        return solverAgent

    def __createMazePath(self) -> QPainterPath:
        cellSize = (
            self.width() / self.__maze.size.x,
            self.height() / self.__maze.size.y,
        )

        path = QPainterPath(QPointF(0, 0))

        # draw outline of maze
        path.addRect(
            0,
            0,
            self.width() - 1,
            self.height() - 1,
        )

        for y in range(self.__maze.size.y):
            currentY = y * cellSize[1]

            for x in range(self.__maze.size.x):
                currentX = x * cellSize[0]

                # get the list of walls surrounding this cell
                thisCellsWalls: Set[
                    AbsoluteDirection] = self.__maze.getWallsOfCellAtCoordinate(
                        XY(x, y))

                # draw north and west walls only, because the next iterations will draw the south and east walls for us (don't wanna waste paint /s)
                if AbsoluteDirection.west in thisCellsWalls:
                    path.moveTo(currentX, currentY)
                    path.lineTo(currentX, currentY + cellSize[1])

                if AbsoluteDirection.north in thisCellsWalls:
                    path.moveTo(currentX, currentY)
                    path.lineTo(currentX + cellSize[0], currentY)

        return path