Проблема с отрисовкой хвоста змейки PyQt5
Пытаюсь создать игру змейка используя PyQt5.
Элементы змейки разделяются на виджеты, т.е. каждый квадрат - это отдельный виджет. Возникает проблема с отрисовкой виджетов хвоста.
Идея такова: генерация этих элементов должна происходить динамически (функция tail) в соответствии со значением счетчика (self.snake_len). По итогу не получается добавить созданный виджет хвоста на экран.
Может есть советы по поводу того, как реализовать нужный функционал?
main.py:
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.Qt import Qt
import sys
import random
import snake_ui
class main_window(QtWidgets.QMainWindow, snake_ui.Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.new_pos_x = 280
self.new_pos_y = 280
self.head_widget.move(self.new_pos_x, self.new_pos_y)
self.new_food_pos_x = 170
self.new_food_pos_y = 170
self.food_widget.move(self.new_food_pos_x, self.new_food_pos_y)
self.current_key = "right"
self.previous_key = "left"
self.snake_len = 0
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.change_pos)
self.timer.timeout.connect(self.food)
self.timer.timeout.connect(self.tail)
self.timer.start(300)
def keyPressEvent(self, e):
self.previous_key = self.current_key
if e.key() == Qt.Key_Up:
self.current_key = ("up" if self.previous_key != "down" else "down")
elif e.key() == Qt.Key_Down:
self.current_key = ("down" if self.previous_key != "up" else "up")
elif e.key() == Qt.Key_Left:
self.current_key = ("left" if self.previous_key != "right" else "right")
elif e.key() == Qt.Key_Right:
self.current_key = ("right" if self.previous_key != "left" else "left")
def food(self):
if self.new_pos_x == self.new_food_pos_x and self.new_pos_y == self.new_food_pos_y:
self.new_food_pos_x = random.randrange(5, 556, 55)
self.new_food_pos_y = random.randrange(5, 556, 55)
self.food_widget.move(self.new_food_pos_x, self.new_food_pos_y)
self.snake_len += 1
def change_pos(self):
if self.current_key == "up":
self.new_pos_y = (self.new_pos_y - 55 if self.new_pos_y != 5 else 555)
elif self.current_key == "down":
self.new_pos_y = (self.new_pos_y + 55 if self.new_pos_y != 555 else 5)
elif self.current_key == "left":
self.new_pos_x = (self.new_pos_x - 55 if self.new_pos_x != 5 else 555)
elif self.current_key == "right":
self.new_pos_x = (self.new_pos_x + 55 if self.new_pos_x != 555 else 5)
self.head_widget.move(self.new_pos_x, self.new_pos_y)
def tail(self):
if self.snake_len != 0:
self.element_list = [i for i in range(self.snake_len)]
for i in range(self.snake_len):
self.element_list[i] = QtWidgets.QWidget(self.centralwidget)
self.element_list[i].setMinimumSize(QtCore.QSize(50, 50))
self.element_list[i].setMaximumSize(QtCore.QSize(50, 50))
self.element_list[i].setStyleSheet("background-color: green")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = main_window()
window.show()
sys.exit(app.exec())
snake_ui.py:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(610, 610)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.head_widget = QtWidgets.QWidget(self.centralwidget)
self.head_widget.setMinimumSize(QtCore.QSize(50, 50))
self.head_widget.setMaximumSize(QtCore.QSize(50, 50))
self.head_widget.setStyleSheet("background-color: green")
self.head_widget.setObjectName("head_widget")
self.food_widget = QtWidgets.QWidget(self.centralwidget)
self.food_widget.setMinimumSize(QtCore.QSize(50, 50))
self.food_widget.setMaximumSize(QtCore.QSize(50, 50))
self.food_widget.setStyleSheet("background-color: red")
self.food_widget.setObjectName("food_widget")
self.head_widget.raise_()
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
Ответы (2 шт):
Вы создаете, но не добавляете виджет в окно. Сразу говорю что это не решит все ваши проблемы, но виджеты добавятся (хоть и не совсем корректно). Можете дальше думать как исправлять. Удачи!
def tail(self):
if self.snake_len != 0:
self.element_list = [i for i in range(self.snake_len)]
for i in range(self.snake_len):
self.element_list[i] = QtWidgets.QWidget(self.centralwidget)
self.element_list[i].resize(QtCore.QSize(50, 50))
self.element_list[i].setStyleSheet("background-color: green")
self.element_list[i].move(self.new_pos_x,self.new_pos_y)
self.layout().addWidget(self.element_list[i])
Snake game:
import sys
import random
from PyQt5.Qt import *
class Board(QFrame):
msg2statusbar = pyqtSignal(str)
# скорость змеи
SPEED = 100
# ширина и высота
WIDTHINBLOCKS = 50
HEIGHTINBLOCKS = 50
def __init__(self, parent=None):
super(Board, self).__init__(parent)
# creating a timer
self.timer = QBasicTimer()
# snake
self.snake = [[5, 10], [5, 11]]
# current_x_head
self.current_x_head = self.snake[0][0]
# current_y_head
self.current_y_head = self.snake[0][1]
# food list
self.food = []
# growing is false
self.grow_snake = False
# board list
self.board = []
# направление
self.direction = 1
self.drop_food()
self.setFocusPolicy(Qt.StrongFocus)
# ширина квадрата
def square_width(self):
return self.contentsRect().width() / self.WIDTHINBLOCKS
# высота квадрата
def square_height(self):
return self.contentsRect().height() / self.HEIGHTINBLOCKS
# start method
def start(self):
# msg for status bar
# score = current len - 2
self.msg2statusbar.emit(str(len(self.snake) - 2))
# starting timer
self.timer.start(self.SPEED, self)
def paintEvent(self, event):
painter = QPainter(self)
rect = self.contentsRect()
boardtop = rect.bottom() - self.HEIGHTINBLOCKS * self.square_height()
# drawing snake
for pos in self.snake:
self.draw_square(
painter,
rect.left() + pos[0] * self.square_width(),
boardtop + pos[1] * self.square_height(),
'#5FD068'
)
# drawing food
for pos in self.food:
self.draw_square(
painter,
rect.left() + pos[0] * self.square_width(),
boardtop + pos[1] * self.square_height(),
'#DA1212'
)
# drawing square
def draw_square(self, painter, x, y, color):
color = QColor(color)
painter.fillRect(
x + 1,
y + 1,
self.square_width() - 2,
self.square_height() - 2, color
)
def keyPressEvent(self, event):
key = event.key()
# if left key pressed
if key == Qt.Key_Left:
# if direction is not right
if self.direction != 2:
# set direction to left
self.direction = 1
# if right key is pressed
elif key == Qt.Key_Right:
# if direction is not left
if self.direction != 1:
# set direction to right
self.direction = 2
# if down key is pressed
elif key == Qt.Key_Down:
# if direction is not up
if self.direction != 4:
# set direction to down
self.direction = 3
# if up key is pressed
elif key == Qt.Key_Up:
# if direction is not down
if self.direction != 3:
# set direction to up
self.direction = 4
# перемещение змейки
def move_snake(self):
# if direction is left change its position
if self.direction == 1:
self.current_x_head = self.current_x_head - 1
self.current_y_head = self.current_y_head
# if it goes beyond left wall
if self.current_x_head < 0:
self.current_x_head = self.WIDTHINBLOCKS - 1
# if direction is right change its position
if self.direction == 2:
self.current_x_head, self.current_y_head = self.current_x_head + 1, self.current_y_head
if self.current_x_head == self.WIDTHINBLOCKS:
self.current_x_head = 0
# if direction is down change its position
if self.direction == 3:
self.current_x_head, self.current_y_head = self.current_x_head, self.current_y_head + 1
if self.current_y_head == self.HEIGHTINBLOCKS:
self.current_y_head = 0
# if direction is up change its position
if self.direction == 4:
self.current_x_head, self.current_y_head = self.current_x_head, self.current_y_head - 1
if self.current_y_head < 0:
self.current_y_head = self.HEIGHTINBLOCKS
# changing head position
head = [self.current_x_head, self.current_y_head]
# inset head in snake list
self.snake.insert(0, head)
# if snake grow is False
if not self.grow_snake:
# pop the last element
self.snake.pop()
else:
self.msg2statusbar.emit(str(len(self.snake)-2))
self.grow_snake = False
# time event method
def timerEvent(self, event):
if event.timerId() == self.timer.timerId():
self.move_snake()
self.is_food_collision()
self.is_suicide()
self.update()
# method to check if snake collides itself
def is_suicide(self):
for i in range(1, len(self.snake)):
if self.snake[i] == self.snake[0]:
self.msg2statusbar.emit(str("Game Ended"))
self.setStyleSheet("background-color : black;")
self.timer.stop()
self.update()
# method to check if the food cis collied
def is_food_collision(self):
for pos in self.food:
if pos == self.snake[0]:
self.food.remove(pos)
self.drop_food()
self.grow_snake = True
def drop_food(self):
# создание случайных координат
x = random.randint(3, 47)
y = random.randint(3, 47)
for pos in self.snake:
if pos == [x, y]:
self.drop_food()
self.food.append([x, y])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.statusbar = self.statusBar()
self.statusbar.setStyleSheet("border: 2px solid black;")
self.board = Board(self)
self.board.msg2statusbar[str].connect(self.statusbar.showMessage)
self.setCentralWidget(self.board)
self.board.start()
if __name__ == '__main__':
app = QApplication([])
w = MainWindow()
w.setWindowTitle('Snake game')
w.setMinimumSize(QSize(400, 422))
w.resize(500, 522)
w.show()
sys.exit(app.exec_())
