Python 3 PyQt5 Grid Size Snake Apples Game With Statistics GUI Desktop App Full Project For Beginners

Python 3 PyQt5 Grid Size Snake Apples Game With Statistics GUI Desktop App Full Project For Beginners

 

Welcome folks today in this blog post we will be building a snakes apples game in grid size using pyqt5 framework in python. All the full source code of the application is given below.

 

 

Get Started

 

 

In order to get started you need to install the following library using the pip command as shown below

 

pip install pyqt5

 

After this make an main.py file and copy paste the following code

 

main.py`

 

from PyQt5.QtWidgets import QApplication

import Window


if __name__ == "__main__":
    main_event = QApplication([])
    window = Window.MainWindow()
    main_event.exec()

 

 

Now create an window.py file and copy paste the following code

 

window.py

 

from PyQt5.QtCore import Qt, QTime, QTimer, QElapsedTimer, QRect
from PyQt5.QtWidgets import QMainWindow, QWidget, QLabel
from PyQt5.QtGui import QFont

import GameWidget


class Timer(QLabel):
    def __init__(self, parent):
        QLabel.__init__(self, parent)
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_timer)
        self.timer.start(1000)
        self.setGeometry(QRect(0, 60, 200, 40))
        self.elapsed = QElapsedTimer()
        self.elapsed.start()

    def update_timer(self):
        time = QTime.currentTime().toString("hh:mm:ss")
        seconds = str(round(self.elapsed.elapsed()/1000))
        minutes = str(round(self.elapsed.elapsed()/60000))
        hours = str(round(self.elapsed.elapsed()/3600000))
        self.setText(f"Time: {time}.\nElapsed: {hours}h {minutes}' {seconds}\"")


class UI(QMainWindow):
    def __init__(self, parent):
        QMainWindow.__init__(self)
        self.main_window = parent
        self.setWindowTitle("Statistics")
        self.setMinimumSize(200, 200)
        self.setWindowFlag(Qt.WindowStaysOnTopHint, True)
        self.setWindowFlag(Qt.Tool, True)
        font = QFont()
        font.setPointSize(12)
        self.setFont(font)

        central_widget = QWidget()
        self.label_food_count = QLabel("Food count: 0", central_widget, geometry=QRect(0, 0, 200, 20))
        self.label_snake_length = QLabel("Snake length: 3", central_widget, geometry=QRect(0, 20, 200, 20))
        self.label_death_counter = QLabel("Deaths counter: 0", central_widget, geometry=QRect(0, 40, 200, 20))
        self.label_time_elapsed = Timer(central_widget)

        self.setCentralWidget(central_widget)
        self.show()


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("QSnake")

        self.ui = UI(self)
        game_widget = GameWidget.GameWidget(self)
        game_widget.setFocusPolicy(Qt.ClickFocus)

        self.setCentralWidget(game_widget)

        self.setMinimumSize(400, 400)
        self.showMaximized()

 

 

Now create an GameWidget.py file and copy paste the following code

 

GameWidget.py

 

from random import choice

from PyQt5.QtCore import QRect, QTime
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QPaintEvent, QPen, QColor, QBrush, QKeyEvent, QResizeEvent
from PyQt5.QtWidgets import QWidget


class Snake:
    def __init__(self):
        self.body_positions = [[4, 2], [3, 2], [2, 2]]
        self.orientation = "Right"
        self.deaths_counter = 0
        self.color_head = QColor(0, 255, 0)
        self.color_body = QColor(0, 200, 0)

    @property
    def deaths(self):
        self.deaths_counter += 1
        return self.deaths_counter

    @property
    def snake_length(self):
        return len(self.body_positions)

    @property
    def head(self):
        return self.body_positions[0]

    def reset(self):
        self.body_positions = [[4, 2], [3, 2], [2, 2]]
        self.orientation = "Right"

    def grow(self):
        self.body_positions.append(self.body_positions[-1].copy())

    def move_forward(self):
        self.body_positions.insert(0, self.body_positions[0].copy())
        if self.orientation == "Right":
            self.body_positions[0][0] += 1
        elif self.orientation == "Left":
            self.body_positions[0][0] -= 1
        elif self.orientation == "Up":
            self.body_positions[0][1] -= 1
        else:
            self.body_positions[0][1] += 1
        self.body_positions.pop()

    def set_orientation(self, orientation: str):
        if self.orientation == "Right" and orientation == "Left":
            pass
        elif self.orientation == "Up" and orientation == "Down":
            pass
        elif self.orientation == "Left" and orientation == "Right":
            pass
        elif self.orientation == "Down" and orientation == "Up":
            pass
        else:
            self.orientation = orientation
            self.move_forward()

    def move_up(self):
        self.set_orientation("Up")

    def move_down(self):
        self.set_orientation("Down")

    def move_right(self):
        self.set_orientation("Right")

    def move_left(self):
        self.set_orientation("Left")


class GameWidget(QWidget):
    GRID_SIZE = 15

    def __init__(self, main_window):
        QWidget.__init__(self)
        self.snake = Snake()
        self.main_window = main_window

        self.pen_grid = QPen(QColor(0, 0, 0))
        self.pen_grid.setWidth(1)

        self.brush_snake = QBrush(self.snake.color_head)
        self.brush_food = QBrush(QColor(255, 0, 0))
        self.timer = QTime()
        self.frame_count = 1

        self.food_position = self.create_food()

    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == Qt.Key_W or event.key() == Qt.Key_Up:
            self.snake.set_orientation("Up")
        elif event.key() == Qt.Key_A or event.key() == Qt.Key_Left:
            self.snake.set_orientation("Left")
        elif event.key() == Qt.Key_S or event.key() == Qt.Key_Down:
            self.snake.set_orientation("Down")
        elif event.key() == Qt.Key_D or event.key() == Qt.Key_Right:
            self.snake.set_orientation("Right")
        elif event.key() == Qt.Key_G:
            self.snake.grow()
        elif event.key() == Qt.Key_P:
            pass
        self.update()

    def paintEvent(self, event: QPaintEvent):
        qp = QPainter(self)

        if self.frame_count == 1:
            self.timer.start()

        """
        Automaticly moves snake every second
        try:
            if self.frame_count % round(1000 / (self.timer.elapsed() / self.frame_count)) == 0:
                self.snake.move()
        except ZeroDivisionError:
            pass
        """
        self.check_snake_food_collision()
        self.check_snake_body_collision()
        self.check_snake_boundary_collision()

        self.draw_grid(qp)
        self.draw_snake(qp)
        self.draw_food(qp)

        self.frame_count += 1

    def draw_grid(self, qp):
        x, y = self.width(), self.height()
        qp.setPen(self.pen_grid)

        for i in range(x//GameWidget.GRID_SIZE):
            qp.drawLine(0 + i * GameWidget.GRID_SIZE, 0,
                        i * GameWidget.GRID_SIZE, y - y % GameWidget.GRID_SIZE - GameWidget.GRID_SIZE)

        for i in range(y//GameWidget.GRID_SIZE):
            qp.drawLine(0, i * GameWidget.GRID_SIZE,
                        x - x % GameWidget.GRID_SIZE - GameWidget.GRID_SIZE, i * GameWidget.GRID_SIZE)

    def draw_snake(self, qp):
        # Draws the head
        self.brush_snake.setColor(self.snake.color_head)
        qp.setBrush(self.brush_snake)
        qp.drawRect(QRect(self.snake.body_positions[0][0] * GameWidget.GRID_SIZE,
                          self.snake.body_positions[0][1] * GameWidget.GRID_SIZE,
                          GameWidget.GRID_SIZE, GameWidget.GRID_SIZE))
        # Draws the body
        temp_color = QColor(0, 200, 0)
        for i in range(1, self.snake.snake_length):
            temp_color.setGreen(255*(1-i/self.snake.snake_length))
            self.brush_snake.setColor(temp_color)
            qp.setBrush(self.brush_snake)
            qp.drawRect(QRect(self.snake.body_positions[i][0] * GameWidget.GRID_SIZE,
                              self.snake.body_positions[i][1] * GameWidget.GRID_SIZE,
                              GameWidget.GRID_SIZE, GameWidget.GRID_SIZE))

    def draw_food(self, qp):
        qp.setBrush(self.brush_food)
        qp.drawRect(QRect(self.food_position[0] * GameWidget.GRID_SIZE,
                          self.food_position[1] * GameWidget.GRID_SIZE,
                          GameWidget.GRID_SIZE, GameWidget.GRID_SIZE))

    def create_food(self):
        self.main_window.ui.label_food_count.setText(f"Food count: {self.snake.snake_length - 3}")
        self.main_window.ui.label_snake_length.setText(f"Snake length: {self.snake.snake_length}")

        x, y = self.width()//GameWidget.GRID_SIZE - 2, self.height()//GameWidget.GRID_SIZE - 2
        # Avoids spawning food inside the snake's body
        avaiable_cells = []
        for i in range(x):
            for j in range(y):
                if not [i, j] in self.snake.body_positions:
                    avaiable_cells.append((i, j))
        return choice(avaiable_cells)

    def check_snake_body_collision(self):
        if any(self.snake.head.copy() == i for i in self.snake.body_positions[1:].copy()):
            # Currently bugged
            # msg = QMessageBox()
            # msg.setWindowTitle("Game over!")
            # msg.setText("Snake has eaten itself!")
            # msg.exec()
            self.main_window.ui.label_death_counter.setText(f"Deaths counter: {self.snake.deaths}")
            self.snake.reset()
            self.food_position = self.create_food()

    def check_snake_food_collision(self):
        if tuple(self.snake.head) == self.food_position:
            self.snake.grow()
            self.food_position = self.create_food()

    def check_snake_boundary_collision(self):
        x, y = self.width() // GameWidget.GRID_SIZE - 1, self.height() // GameWidget.GRID_SIZE - 1
        if self.snake.head[0] < 0 or self.snake.head[1] < 0 or self.snake.head[0] == x or self.snake.head[1] == y:
            self.main_window.ui.label_death_counter.setText(f"Death count: {self.snake.deaths}")
            self.snake.reset()
            self.food_position = self.create_food()

    @property
    def cell_number(self):
        return self.width() // GameWidget.GRID_SIZE - 1, self.height() // GameWidget.GRID_SIZE - 1

    def resizeEvent(self, event: QResizeEvent):
        self.snake.reset()
        self.food_position = self.create_food()
        QWidget.resizeEvent(self, event)

 

See also  Python 3 Tkinter Random Color Changing Text Game GUI Script Full Project For Beginners

 

Now if you execute the main.py file by typing the below command as shown below

 

python main.py

 

Leave a Reply