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`
1 2 3 4 5 6 7 8 9 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
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) |
Now if you execute the main.py
file by typing the below command as shown below
python main.py