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)

Pyqt 编写的俄罗斯方块

你可能感兴趣的:(qt)