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


      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)

  • 相关阅读:
    C#把外部文件拖入PictureBox中
    DirectInfo.GetFiles返回数组的默认排序
    NULL在SQLServer数据库日志文件中的存储
    C#中用NamedPipe进程间通信
    punycode和中文相互转换
    C#事件和委托的基础知识模型
    反射APP_CODE下的类和方法
    [ListView.View=List]的垂直滚动条
    换个思路"SQL2005下字符串字段内的字符排序"
    mono for android 的ADB
  • 原文地址:https://www.cnblogs.com/dcb3688/p/4450454.html
Copyright © 2020-2023  润新知