环境

python3.7,pyqt5,pyinstaller进行打包成exe文件。

效果

代码

import sys, random
from pyqt5.qtwidgets import qmainwindow, qframe, qdesktopwidget, qapplication
from pyqt5.qtcore import qt, qbasictimer, pyqtsignal
from pyqt5.qtgui import qpainter, qcolor
class tetris(qmainwindow):
def __init__(self):
super().__init__()
self.initui()
def initui(self):
self.tboard = board(self)
self.setcentralwidget(self.tboard)
self.statusbar = self.statusbar()
self.tboard.msg2statusbar[str].connect(self.statusbar.showmessage)
self.tboard.start()
self.resize(180, 380)
self.center()
self.setwindowtitle('tetris')
self.show()
def center(self):
screen = qdesktopwidget().screengeometry()
size = self.geometry()
self.move((screen.width() - size.width()) / 2,
(screen.height() - size.height()) / 2)
class board(qframe):
msg2statusbar = pyqtsignal(str)
boardwidth = 10
boardheight = 22
speed = 300
def __init__(self, parent):
super().__init__(parent)
self.initboard()
def initboard(self):
self.timer = qbasictimer()
self.iswaitingafterline = false
self.curx = 0
self.cury = 0
self.numlinesremoved = 0
self.board = []
self.setfocuspolicy(qt.strongfocus)
self.isstarted = false
self.ispaused = false
self.clearboard()
def shapeat(self, x, y):
return self.board[(y * board.boardwidth) + x]
def setshapeat(self, x, y, shape):
self.board[(y * board.boardwidth) + x] = shape
def squarewidth(self):
return self.contentsrect().width() // board.boardwidth
def squareheight(self):
return self.contentsrect().height() // board.boardheight
def start(self):
if self.ispaused:
return
self.isstarted = true
self.iswaitingafterline = false
self.numlinesremoved = 0
self.clearboard()
self.msg2statusbar.emit(str(self.numlinesremoved))
self.newpiece()
self.timer.start(board.speed, self)
def pause(self):
if not self.isstarted:
return
self.ispaused = not self.ispaused
if self.ispaused:
self.timer.stop()
self.msg2statusbar.emit("paused")
else:
self.timer.start(board.speed, self)
self.msg2statusbar.emit(str(self.numlinesremoved))
self.update()
def paintevent(self, event):
painter = qpainter(self)
rect = self.contentsrect()
boardtop = rect.bottom() - board.boardheight * self.squareheight()
for i in range(board.boardheight):
for j in range(board.boardwidth):
shape = self.shapeat(j, board.boardheight - i - 1)
if shape != tetrominoe.noshape:
self.drawsquare(painter,
rect.left() + j * self.squarewidth(),
boardtop + i * self.squareheight(), shape)
if self.curpiece.shape() != tetrominoe.noshape:
for i in range(4):
x = self.curx + self.curpiece.x(i)
y = self.cury - self.curpiece.y(i)
self.drawsquare(painter, rect.left() + x * self.squarewidth(),
boardtop + (board.boardheight - y - 1) * self.squareheight(),
self.curpiece.shape())
def keypressevent(self, event):
if not self.isstarted or self.curpiece.shape() == tetrominoe.noshape:
super(board, self).keypressevent(event)
return
key = event.key()
if key == qt.key_p:
self.pause()
return
if self.ispaused:
return
elif key == qt.key_left:
self.trymove(self.curpiece, self.curx - 1, self.cury)
elif key == qt.key_right:
self.trymove(self.curpiece, self.curx + 1, self.cury)
elif key == qt.key_down:
self.trymove(self.curpiece.rotateright(), self.curx, self.cury)
elif key == qt.key_up:
self.trymove(self.curpiece.rotateleft(), self.curx, self.cury)
elif key == qt.key_space:
self.dropdown()
elif key == qt.key_d:
self.onelinedown()
else:
super(board, self).keypressevent(event)
def timerevent(self, event):
if event.timerid() == self.timer.timerid():
if self.iswaitingafterline:
self.iswaitingafterline = false
self.newpiece()
else:
self.onelinedown()
else:
super(board, self).timerevent(event)
def clearboard(self):
for i in range(board.boardheight * board.boardwidth):
self.board.append(tetrominoe.noshape)
def dropdown(self):
newy = self.cury
while newy > 0:
if not self.trymove(self.curpiece, self.curx, newy - 1):
break
newy -= 1
self.piecedropped()
def onelinedown(self):
if not self.trymove(self.curpiece, self.curx, self.cury - 1):
self.piecedropped()
def piecedropped(self):
for i in range(4):
x = self.curx + self.curpiece.x(i)
y = self.cury - self.curpiece.y(i)
self.setshapeat(x, y, self.curpiece.shape())
self.removefulllines()
if not self.iswaitingafterline:
self.newpiece()
def removefulllines(self):
numfulllines = 0
rowstoremove = []
for i in range(board.boardheight):
n = 0
for j in range(board.boardwidth):
if not self.shapeat(j, i) == tetrominoe.noshape:
n = n + 1
if n == 10:
rowstoremove.append(i)
rowstoremove.reverse()
for m in rowstoremove:
for k in range(m, board.boardheight):
for l in range(board.boardwidth):
self.setshapeat(l, k, self.shapeat(l, k + 1))
numfulllines = numfulllines + len(rowstoremove)
if numfulllines > 0:
self.numlinesremoved = self.numlinesremoved + numfulllines
self.msg2statusbar.emit(str(self.numlinesremoved))
self.iswaitingafterline = true
self.curpiece.setshape(tetrominoe.noshape)
self.update()
def newpiece(self):
self.curpiece = shape()
self.curpiece.setrandomshape()
self.curx = board.boardwidth // 2 + 1
self.cury = board.boardheight - 1 + self.curpiece.miny()
if not self.trymove(self.curpiece, self.curx, self.cury):
self.curpiece.setshape(tetrominoe.noshape)
self.timer.stop()
self.isstarted = false
self.msg2statusbar.emit("game over")
def trymove(self, newpiece, newx, newy):
for i in range(4):
x = newx + newpiece.x(i)
y = newy - newpiece.y(i)
if x < 0 or x >= board.boardwidth or y < 0 or y >= board.boardheight:
return false
if self.shapeat(x, y) != tetrominoe.noshape:
return false
self.curpiece = newpiece
self.curx = newx
self.cury = newy
self.update()
return true
def drawsquare(self, painter, x, y, shape):
colortable = [0x000000, 0xcc6666, 0x66cc66, 0x6666cc,
0xcccc66, 0xcc66cc, 0x66cccc, 0xdaaa00]
color = qcolor(colortable[shape])
painter.fillrect(x + 1, y + 1, self.squarewidth() - 2,
self.squareheight() - 2, color)
painter.setpen(color.lighter())
painter.drawline(x, y + self.squareheight() - 1, x, y)
painter.drawline(x, y, x + self.squarewidth() - 1, y)
painter.setpen(color.darker())
painter.drawline(x + 1, y + self.squareheight() - 1,
x + self.squarewidth() - 1, y + self.squareheight() - 1)
painter.drawline(x + self.squarewidth() - 1,
y + self.squareheight() - 1, x + self.squarewidth() - 1, y + 1)
class tetrominoe(object):
noshape = 0
zshape = 1
sshape = 2
lineshape = 3
tshape = 4
squareshape = 5
lshape = 6
mirroredlshape = 7
class shape(object):
coordstable = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
((0, -1), (0, 0), (1, 0), (1, 1)),
((0, -1), (0, 0), (0, 1), (0, 2)),
((-1, 0), (0, 0), (1, 0), (0, 1)),
((0, 0), (1, 0), (0, 1), (1, 1)),
((-1, -1), (0, -1), (0, 0), (0, 1)),
((1, -1), (0, -1), (0, 0), (0, 1))
)
def __init__(self):
self.coords = [[0, 0] for i in range(4)]
self.pieceshape = tetrominoe.noshape
self.setshape(tetrominoe.noshape)
def shape(self):
return self.pieceshape
def setshape(self, shape):
table = shape.coordstable[shape]
for i in range(4):
for j in range(2):
self.coords[i][j] = table[i][j]
self.pieceshape = shape
def setrandomshape(self):
self.setshape(random.randint(1, 7))
def x(self, index):
return self.coords[index][0]
def y(self, index):
return self.coords[index][1]
def setx(self, index, x):
self.coords[index][0] = x
def sety(self, index, y):
self.coords[index][1] = y
def minx(self):
m = self.coords[0][0]
for i in range(4):
m = min(m, self.coords[i][0])
return m
def maxx(self):
m = self.coords[0][0]
for i in range(4):
m = max(m, self.coords[i][0])
return m
def miny(self):
m = self.coords[0][1]
for i in range(4):
m = min(m, self.coords[i][1])
return m
def maxy(self):
m = self.coords[0][1]
for i in range(4):
m = max(m, self.coords[i][1])
return m
def rotateleft(self):
if self.pieceshape == tetrominoe.squareshape:
return self
result = shape()
result.pieceshape = self.pieceshape
for i in range(4):
result.setx(i, self.y(i))
result.sety(i, -self.x(i))
return result
def rotateright(self):
if self.pieceshape == tetrominoe.squareshape:
return self
result = shape()
result.pieceshape = self.pieceshape
for i in range(4):
result.setx(i, -self.y(i))
result.sety(i, self.x(i))
return result
if __name__ == '__main__':
app = qapplication([])
tetris = tetris()
sys.exit(app.exec_())