zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【转载】Pyqt 编写的俄罗斯方块

编写 转载 Pyqt 俄罗斯 方块
2023-09-11 14:17:02 时间

 

  1 #!/usr/bin/env python
  2 # -*- coding: utf-8 -*-
  3 from __future__ import print_function
  4 from __future__ import unicode_literals
  5 from __future__ import division
  6 from __future__ import absolute_import
  7 try:
  8     str = unicode
  9 except NameError:
 10     pass
 11 
 12 import random, sys, sip
 13 try:
 14     sip.setapi("QString" ,2)
 15 except ValueError:
 16     pass
 17 
 18 from PyQt4 import QtCore, QtGui
 19 
 20 
 21 NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape = range(8)
 22 
 23 random.seed(None)
 24 
 25 
 26 class TetrixWindow(QtGui.QWidget):
 27     def __init__(self, parent = None):
 28         QtGui.QWidget.__init__(self, parent, QtCore.Qt.Window)
 29 
 30         self.board = TetrixBoard()
 31         self.indictor = TetrixIndictor()
 32 
 33         nextPieceLabel = QtGui.QLabel(self)
 34         nextPieceLabel.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Raised)
 35         nextPieceLabel.setAlignment(QtCore.Qt.AlignCenter)
 36         self.board.setNextPieceLabel(nextPieceLabel)
 37 
 38         scoreLcd = QtGui.QLCDNumber(6)
 39         scoreLcd.setSegmentStyle(QtGui.QLCDNumber.Filled)
 40         levelLcd = QtGui.QLCDNumber(2)
 41         levelLcd.setSegmentStyle(QtGui.QLCDNumber.Filled)
 42         linesLcd = QtGui.QLCDNumber(6)
 43         linesLcd.setSegmentStyle(QtGui.QLCDNumber.Filled)
 44 
 45         startButton = QtGui.QPushButton(self.trUtf8("开始(&S)"))
 46         startButton.setFocusPolicy(QtCore.Qt.NoFocus)
 47         quitButton = QtGui.QPushButton(self.trUtf8("退出(&X)"))
 48         quitButton.setFocusPolicy(QtCore.Qt.NoFocus)
 49         pauseButton = QtGui.QPushButton(self.trUtf8("暂停(&P)"))
 50         pauseButton.setFocusPolicy(QtCore.Qt.NoFocus)
 51 
 52         startButton.clicked.connect(self.board.start)
 53         pauseButton.clicked.connect(self.board.pause)
 54         quitButton.clicked.connect(self.close)
 55         self.board.scoreChanged.connect(scoreLcd.display)
 56         self.board.levelChanged.connect(levelLcd.display)
 57         self.board.linesRemovedChanged.connect(linesLcd.display)
 58         self.board.act.connect(self.indictor.showIndictor)
 59 
 60         layout1 = QtGui.QHBoxLayout()
 61         layout3 = QtGui.QVBoxLayout()
 62         layout3.addWidget(self.board)
 63         layout3.addWidget(self.indictor)
 64         layout3.setSpacing(0)
 65         layout1.addLayout(layout3)
 66         layout2 = QtGui.QVBoxLayout()
 67         layout2.addWidget(self.createLabel(self.trUtf8("下一个方块")))
 68         layout2.addWidget(nextPieceLabel)
 69         layout2.addWidget(self.createLabel(self.trUtf8("级别")))
 70         layout2.addWidget(levelLcd)
 71         layout2.addWidget(self.createLabel(self.trUtf8("成绩")),)
 72         layout2.addWidget(scoreLcd)
 73         layout2.addWidget(self.createLabel(self.trUtf8("总共消去行数")))
 74         layout2.addWidget(linesLcd)
 75         layout2.addWidget(startButton)
 76         layout2.addWidget(quitButton)
 77         layout2.addWidget(pauseButton)
 78         layout1.addLayout(layout2)
 79         layout1.setStretch(0, 75)
 80         layout1.setStretch(1, 25)
 81         self.setLayout(layout1)
 82 
 83         self.setWindowTitle(self.trUtf8("俄罗斯方块(Tetrix)"))
 84         self.resize(self.logicalDpiX() / 96 * 275, self.logicalDpiY() / 96 * 380)
 85 
 86         r = self.geometry()
 87         r.moveCenter(QtGui.qApp.desktop().screenGeometry().center())
 88         self.setGeometry(r)
 89 
 90     def createLabel(self, text):
 91         lbl = QtGui.QLabel(text)
 92         lbl.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom)
 93         return lbl
 94 
 95 
 96 class TetrixIndictor(QtGui.QWidget):
 97     """位于主游戏区下方的一个扁小的控件,用于显示当前位置落下时的位置。
 98     现在主要的问题是游戏区的大小超出了人类的眼睛的焦点区。
 99     或许可以让整个游戏界面更小一些。"""
100 
101     def __init__(self, parent = None):
102         QtGui.QWidget.__init__(self, parent)
103         self.begin = self.end = None
104         self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
105 
106     def showIndictor(self, curX, piece):
107         self.begin = curX + piece.minX()
108         self.end = curX + piece.maxX()
109         self.update()
110 
111     def paintEvent(self, event):
112         QtGui.QWidget.paintEvent(self, event)
113         if self.begin is None:
114             return
115         board = self.parent().board
116         pieceWidth = board.contentsRect().width() // TetrixBoard.BoardWidth
117         brush = QtGui.QBrush(QtCore.Qt.yellow)
118         painter = QtGui.QPainter(self)
119         painter.setBrush(brush)
120         painter.drawRect(board.contentsRect().left() + self.begin * pieceWidth, 0, \
121                          (self.end - self.begin + 1) * pieceWidth, self.height() - 1 )
122 
123     def sizeHint(self):
124         return QtCore.QSize(self.parent().board.width(), 8)
125 
126 
127 class TetrixBoard(QtGui.QFrame):
128     BoardWidth = 11
129     BoardHeight = 22
130 
131     scoreChanged = QtCore.pyqtSignal(int)
132     levelChanged = QtCore.pyqtSignal(int)
133     linesRemovedChanged = QtCore.pyqtSignal(int)
134     act = QtCore.pyqtSignal(int, "PyQt_PyObject")
135 
136     def __init__(self, parent = None):
137         super(TetrixBoard, self).__init__(parent)
138         self.setStyleSheet("background-color:black;border:2px solid darkGreen;")
139 
140         self.timer = QtCore.QBasicTimer()
141         self.nextPieceLabel = None
142         self.isWaitingAfterLine = False
143         self.curPiece = TetrixPiece()
144         self.nextPiece = TetrixPiece()
145         self.curX = 0
146         self.curY = 0
147         self.numLinesRemoved = 0
148         self.numPiecesDropped = 0
149         self.score = 0
150         self.level = 0
151         self.board = None
152 
153         #self.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Sunken)
154         self.setFrameStyle(QtGui.QFrame.Box)
155         self.setFocusPolicy(QtCore.Qt.StrongFocus)
156         self.isStarted = False
157         self.isPaused = False
158         self.clearBoard()
159 
160         self.nextPiece.setRandomShape()
161 
162     def focusOutEvent(self, event):
163         if self.isStarted and not self.isPaused:
164             self.pause()
165         QtGui.QFrame.focusOutEvent(self, event)
166 
167     def shapeAt(self, x, y):
168         return self.board[(y * TetrixBoard.BoardWidth) + x]
169 
170     def setShapeAt(self, x, y, shape):
171         self.board[(y * TetrixBoard.BoardWidth) + x] = shape
172 
173     def timeoutTime(self):
174         return 1000 // (1 + self.level)
175 
176     def squareWidth(self):
177         return self.contentsRect().width() // TetrixBoard.BoardWidth
178 
179     def squareHeight(self):
180         return self.contentsRect().height() // TetrixBoard.BoardHeight
181 
182     def setNextPieceLabel(self, label):
183         self.nextPieceLabel = label
184         #label.setScaledContents(True)
185         label.setMinimumSize(label.width(), label.width())
186 
187     def sizeHint(self):
188         return QtCore.QSize(TetrixBoard.BoardWidth * 15 + self.frameWidth() * 2,
189                 TetrixBoard.BoardHeight * 15 + self.frameWidth() * 2)
190 
191     def minimumSizeHint(self):
192         return QtCore.QSize(TetrixBoard.BoardWidth * 15 + self.frameWidth() * 2,
193                 TetrixBoard.BoardHeight * 15 + self.frameWidth() * 2)
194 
195     def start(self):
196         if self.isPaused:
197             return
198 
199         self.isStarted = True
200         self.isWaitingAfterLine = False
201         self.numLinesRemoved = 0
202         self.numPiecesDropped = 0
203         self.score = 0
204         self.level = 1
205         self.clearBoard()
206 
207         self.linesRemovedChanged.emit(self.numLinesRemoved)
208         self.scoreChanged.emit(self.score)
209         self.levelChanged.emit(self.level)
210 
211         self.newPiece()
212         self.timer.start(self.timeoutTime(), self)
213 
214     def pause(self):
215         if not self.isStarted:
216             return
217 
218         self.isPaused = not self.isPaused
219         if self.isPaused:
220             self.timer.stop()
221         else:
222             self.timer.start(self.timeoutTime(), self)
223 
224         self.update()
225 
226     def paintEvent(self, event):
227         super(TetrixBoard, self).paintEvent(event)
228 
229         painter = QtGui.QPainter(self)
230         rect = self.contentsRect()
231 
232         if self.isPaused:
233             painter.drawText(rect, QtCore.Qt.AlignCenter, self.trUtf8("暂停"))
234             return
235 
236         boardTop = rect.bottom() - TetrixBoard.BoardHeight * self.squareHeight()
237 
238         for i in range(TetrixBoard.BoardHeight):
239             for j in range(TetrixBoard.BoardWidth):
240                 shape = self.shapeAt(j, TetrixBoard.BoardHeight - i - 1)
241                 if shape != NoShape:
242                     self.drawSquare(painter,
243                             rect.left() + j * self.squareWidth(),
244                             boardTop + i * self.squareHeight(), shape)
245 
246         if self.curPiece.shape() != NoShape:
247             for i in range(4):
248                 x = self.curX + self.curPiece.x(i)
249                 y = self.curY - self.curPiece.y(i)
250                 self.drawSquare(painter, rect.left() + x * self.squareWidth(),
251                         boardTop + (TetrixBoard.BoardHeight - y - 1) * self.squareHeight(),
252                         self.curPiece.shape())
253 
254     def keyPressEvent(self, event):
255         if not self.isStarted or self.isPaused or self.curPiece.shape() == NoShape:
256             super(TetrixBoard, self).keyPressEvent(event)
257             return
258 
259         key = event.key()
260         if key == QtCore.Qt.Key_Left:
261             self.tryMove(self.curPiece, self.curX - 1, self.curY)
262         elif key == QtCore.Qt.Key_Right:
263             self.tryMove(self.curPiece, self.curX + 1, self.curY)
264         elif key == QtCore.Qt.Key_Down:
265             self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
266         elif key == QtCore.Qt.Key_Up:
267             self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
268         elif key == QtCore.Qt.Key_Space:
269             self.dropDown()
270         elif key == QtCore.Qt.Key_D:
271             self.oneLineDown()
272         else:
273             super(TetrixBoard, self).keyPressEvent(event)
274 
275     def timerEvent(self, event):
276         if event.timerId() == self.timer.timerId():
277             if self.isWaitingAfterLine:
278                 self.isWaitingAfterLine = False
279                 self.newPiece()
280                 self.timer.start(self.timeoutTime(), self)
281             else:
282                 self.oneLineDown()
283         else:
284             super(TetrixBoard, self).timerEvent(event)
285 
286     def clearBoard(self):
287         self.board = [NoShape for i in range(TetrixBoard.BoardHeight * TetrixBoard.BoardWidth)]
288 
289     def dropDown(self):
290         dropHeight = 0
291         newY = self.curY
292         while newY > 0:
293             if not self.tryMove(self.curPiece, self.curX, newY - 1):
294                 break
295             newY -= 1
296             dropHeight += 1
297 
298         self.pieceDropped(dropHeight)
299 
300     def oneLineDown(self):
301         if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
302             self.pieceDropped(0)
303 
304     def pieceDropped(self, dropHeight):
305         for i in range(4):
306             x = self.curX + self.curPiece.x(i)
307             y = self.curY - self.curPiece.y(i)
308             self.setShapeAt(x, y, self.curPiece.shape())
309 
310         self.numPiecesDropped += 1
311         if self.numPiecesDropped % 25 == 0:
312             self.level += 1
313             self.timer.start(self.timeoutTime(), self)
314             self.levelChanged.emit(self.level)
315 
316         self.score += dropHeight + 7
317         self.scoreChanged.emit(self.score)
318         self.removeFullLines()
319 
320         if not self.isWaitingAfterLine:
321             self.newPiece()
322 
323     def removeFullLines(self):
324         numFullLines = 0
325 
326         for i in range(TetrixBoard.BoardHeight - 1, -1, -1):
327             lineIsFull = True
328 
329             for j in range(TetrixBoard.BoardWidth):
330                 if self.shapeAt(j, i) == NoShape:
331                     lineIsFull = False
332                     break
333 
334             if lineIsFull:
335                 numFullLines += 1
336                 for k in range(i, TetrixBoard.BoardHeight - 1):
337                     for j in range(TetrixBoard.BoardWidth):
338                         self.setShapeAt(j, k, self.shapeAt(j, k + 1))
339 
340                 for j in range(TetrixBoard.BoardWidth):
341                     self.setShapeAt(j, TetrixBoard.BoardHeight - 1, NoShape)
342 
343         if numFullLines > 0:
344             self.numLinesRemoved += numFullLines
345             self.score += 10 * numFullLines
346             self.linesRemovedChanged.emit(self.numLinesRemoved)
347             self.scoreChanged.emit(self.score)
348 
349             self.timer.start(200, self)
350             self.isWaitingAfterLine = True
351             self.curPiece.setShape(NoShape)
352             self.update()
353 
354     def newPiece(self):
355         self.curPiece = self.nextPiece
356         self.nextPiece = TetrixPiece()
357         self.nextPiece.setRandomShape()
358         self.showNextPiece()
359         self.curX = TetrixBoard.BoardWidth // 2
360         self.curY = TetrixBoard.BoardHeight - 1 + self.curPiece.minY()
361         self.act.emit(self.curX, self.curPiece)
362 
363         if not self.tryMove(self.curPiece, self.curX, self.curY):
364             self.curPiece.setShape(NoShape)
365             self.timer.stop()
366             self.isStarted = False
367 
368     def showNextPiece(self):
369         if self.nextPieceLabel is None:
370             return
371 
372         dx = self.nextPiece.maxX() - self.nextPiece.minX() + 1
373         dy = self.nextPiece.maxY() - self.nextPiece.minY() + 1
374 
375         self.pixmapNextPiece = QtGui.QPixmap(dx * self.squareWidth(), dy * self.squareHeight())
376         painter = QtGui.QPainter(self.pixmapNextPiece)
377         painter.fillRect(self.pixmapNextPiece.rect(), self.nextPieceLabel.palette().background())
378 
379         for i in range(4):
380             x = self.nextPiece.x(i) - self.nextPiece.minX()
381             y = self.nextPiece.y(i) - self.nextPiece.minY()
382             self.drawSquare(painter, x * self.squareWidth(),
383                     y * self.squareHeight(), self.nextPiece.shape())
384 
385         self.nextPieceLabel.setPixmap(self.pixmapNextPiece)
386 
387     def tryMove(self, newPiece, newX, newY):
388         for i in range(4):
389             x = newX + newPiece.x(i)
390             y = newY - newPiece.y(i)
391             if x < 0 or x >= TetrixBoard.BoardWidth or y < 0 or y >= TetrixBoard.BoardHeight:
392                 return False
393             if self.shapeAt(x, y) != NoShape:
394                 return False
395 
396         self.curPiece = newPiece
397         self.curX = newX
398         self.curY = newY
399         self.update()
400         self.act.emit(self.curX, self.curPiece)
401         return True
402 
403     def drawSquare(self, painter, x, y, shape):
404         colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
405                       0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
406 
407         color = QtGui.QColor(colorTable[shape])
408         painter.fillRect(x + 1, y + 1, self.squareWidth() - 2,
409                 self.squareHeight() - 2, color)
410 
411         painter.setPen(color.light())
412         painter.drawLine(x, y + self.squareHeight() - 1, x, y)
413         painter.drawLine(x, y, x + self.squareWidth() - 1, y)
414 
415         painter.setPen(color.dark())
416         painter.drawLine(x + 1, y + self.squareHeight() - 1,
417                 x + self.squareWidth() - 1, y + self.squareHeight() - 1)
418         painter.drawLine(x + self.squareWidth() - 1,
419                 y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
420 
421 
422 class TetrixPiece(object):
423     coordsTable = (
424         ((0, 0),     (0, 0),     (0, 0),     (0, 0)),
425         ((0, -1),    (0, 0),     ( - 1, 0),    ( - 1, 1)),
426         ((0, -1),    (0, 0),     (1, 0),     (1, 1)),
427         ((0, -1),    (0, 0),     (0, 1),     (0, 2)),
428         (( - 1, 0),    (0, 0),     (1, 0),     (0, 1)),
429         ((0, 0),     (1, 0),     (0, 1),     (1, 1)),
430         (( - 1, -1),   (0, -1),    (0, 0),     (0, 1)),
431         ((1, -1),    (0, -1),    (0, 0),     (0, 1))
432     )
433 
434     def __init__(self):
435         self.coords = [[0,0] for _ in range(4)]
436         self.pieceShape = NoShape
437 
438         self.setShape(NoShape)
439 
440     def shape(self):
441         return self.pieceShape
442 
443     def setShape(self, shape):
444         table = TetrixPiece.coordsTable[shape]
445         for i in range(4):
446             for j in range(2):
447                 self.coords[i][j] = table[i][j]
448 
449         self.pieceShape = shape
450 
451     def setRandomShape(self):
452         self.setShape(random.randint(1, 7))
453 
454     def x(self, index):
455         return self.coords[index][0]
456 
457     def y(self, index):
458         return self.coords[index][1]
459 
460     def setX(self, index, x):
461         self.coords[index][0] = x
462 
463     def setY(self, index, y):
464         self.coords[index][1] = y
465 
466     def minX(self):
467         m = self.coords[0][0]
468         for i in range(4):
469             m = min(m, self.coords[i][0])
470 
471         return m
472 
473     def maxX(self):
474         m = self.coords[0][0]
475         for i in range(4):
476             m = max(m, self.coords[i][0])
477 
478         return m
479 
480     def minY(self):
481         m = self.coords[0][1]
482         for i in range(4):
483             m = min(m, self.coords[i][1])
484 
485         return m
486 
487     def maxY(self):
488         m = self.coords[0][1]
489         for i in range(4):
490             m = max(m, self.coords[i][1])
491 
492         return m
493 
494     def rotatedLeft(self):
495         if self.pieceShape == SquareShape:
496             return self
497 
498         result = TetrixPiece()
499         result.pieceShape = self.pieceShape
500         for i in range(4):
501             result.setX(i, self.y(i))
502             result.setY(i, -self.x(i))
503 
504         return result
505 
506     def rotatedRight(self):
507         if self.pieceShape == SquareShape:
508             return self
509 
510         result = TetrixPiece()
511         result.pieceShape = self.pieceShape
512         for i in range(4):
513             result.setX(i, -self.y(i))
514             result.setY(i, self.x(i))
515 
516         return result
517 
518 if __name__ == '__main__':
519     app = QtGui.QApplication(sys.argv)
520     window = TetrixWindow()
521     window.show()
522     if hasattr(app, "exec"):
523         result = getattr(app, "exec")()
524     else:
525         result = getattr(app, "exec_")()
526     sys.exit(result)