uTools-Manuals/docs/PyQt5/俄罗斯方块游戏.html
2019-05-14 02:17:51 +08:00

762 lines
77 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<h1 id="俄罗斯方块游戏">俄罗斯方块游戏</h1>
<p>本章我们要制作一个俄罗斯方块游戏。</p>
<h2 id="tetris">Tetris</h2>
<blockquote>
<p>译注:称呼:方块是由四个小方格组成的</p>
</blockquote>
<p>俄罗斯方块游戏是世界上最流行的游戏之一。是由一名叫Alexey Pajitnov的俄罗斯程序员在1985年制作的从那时起这个游戏就风靡了各个游戏平台。</p>
<p>俄罗斯方块归类为下落块迷宫游戏。游戏有7个基本形状S、Z、T、L、反向L、直线、方块每个形状都由4个方块组成方块最终都会落到屏幕底部。所以玩家通过控制形状的左右位置和旋转让每个形状都以合适的位置落下如果有一行全部被方块填充这行就会消失并且得分。游戏结束的条件是有形状接触到了屏幕顶部。</p>
<p>方块展示:</p>
<figure>
<img class="whitelist" src="docs/PyQt5/images/11-tetrominoes.png" alt="tetrominoes" />
</figure>
<p>PyQt5是专门为创建图形界面产生的里面一些专门为制作游戏而开发的组件所以PyQt5是能制作小游戏的。</p>
<p>制作电脑游戏也是提高自己编程能力的一种很好的方式。</p>
<h2 id="开发">开发</h2>
<p>没有图片,所以就自己用绘画画出来几个图形。每个游戏里都有数学模型的,这个也是。</p>
<p>开工之前:</p>
<ul>
<li><code>QtCore.QBasicTimer()</code>创建一个游戏循环</li>
<li>模型是一直下落的</li>
<li>模型的运动是以小块为基础单位的,不是按像素</li>
<li>从数学意义上来说,模型就是就是一串数字而已</li>
</ul>
<p>代码由四个类组成Tetris, Board, Tetrominoe和Shape。Tetris类创建游戏Board是游戏主要逻辑。Tetrominoe包含了所有的砖块Shape是所有砖块的代码。</p>
<div class="sourceCode" id="cb1"><pre><code class="language-python"><a class="sourceLine" id="cb1-1" data-line-number="1"><span class="co">#!/usr/bin/python3</span></a>
<a class="sourceLine" id="cb1-2" data-line-number="2"><span class="co"># -*- coding: utf-8 -*-</span></a>
<a class="sourceLine" id="cb1-3" data-line-number="3"></a>
<a class="sourceLine" id="cb1-4" data-line-number="4"><span class="co">&quot;&quot;&quot;</span></a>
<a class="sourceLine" id="cb1-5" data-line-number="5"><span class="co">ZetCode PyQt5 tutorial </span></a>
<a class="sourceLine" id="cb1-6" data-line-number="6"></a>
<a class="sourceLine" id="cb1-7" data-line-number="7"><span class="co">This is a Tetris game clone.</span></a>
<a class="sourceLine" id="cb1-8" data-line-number="8"></a>
<a class="sourceLine" id="cb1-9" data-line-number="9"><span class="co">Author: Jan Bodnar</span></a>
<a class="sourceLine" id="cb1-10" data-line-number="10"><span class="co">Website: zetcode.com </span></a>
<a class="sourceLine" id="cb1-11" data-line-number="11"><span class="co">Last edited: August 2017</span></a>
<a class="sourceLine" id="cb1-12" data-line-number="12"><span class="co">&quot;&quot;&quot;</span></a>
<a class="sourceLine" id="cb1-13" data-line-number="13"></a>
<a class="sourceLine" id="cb1-14" data-line-number="14"><span class="im">from</span> PyQt5.QtWidgets <span class="im">import</span> QMainWindow, QFrame, QDesktopWidget, QApplication</a>
<a class="sourceLine" id="cb1-15" data-line-number="15"><span class="im">from</span> PyQt5.QtCore <span class="im">import</span> Qt, QBasicTimer, pyqtSignal</a>
<a class="sourceLine" id="cb1-16" data-line-number="16"><span class="im">from</span> PyQt5.QtGui <span class="im">import</span> QPainter, QColor </a>
<a class="sourceLine" id="cb1-17" data-line-number="17"><span class="im">import</span> sys, random</a>
<a class="sourceLine" id="cb1-18" data-line-number="18"></a>
<a class="sourceLine" id="cb1-19" data-line-number="19"><span class="kw">class</span> Tetris(QMainWindow):</a>
<a class="sourceLine" id="cb1-20" data-line-number="20"> </a>
<a class="sourceLine" id="cb1-21" data-line-number="21"> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-22" data-line-number="22"> <span class="bu">super</span>().<span class="fu">__init__</span>()</a>
<a class="sourceLine" id="cb1-23" data-line-number="23"> </a>
<a class="sourceLine" id="cb1-24" data-line-number="24"> <span class="va">self</span>.initUI()</a>
<a class="sourceLine" id="cb1-25" data-line-number="25"> </a>
<a class="sourceLine" id="cb1-26" data-line-number="26"> </a>
<a class="sourceLine" id="cb1-27" data-line-number="27"> <span class="kw">def</span> initUI(<span class="va">self</span>): </a>
<a class="sourceLine" id="cb1-28" data-line-number="28"> <span class="co">&#39;&#39;&#39;initiates application UI&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-29" data-line-number="29"></a>
<a class="sourceLine" id="cb1-30" data-line-number="30"> <span class="va">self</span>.tboard <span class="op">=</span> Board(<span class="va">self</span>)</a>
<a class="sourceLine" id="cb1-31" data-line-number="31"> <span class="va">self</span>.setCentralWidget(<span class="va">self</span>.tboard)</a>
<a class="sourceLine" id="cb1-32" data-line-number="32"></a>
<a class="sourceLine" id="cb1-33" data-line-number="33"> <span class="va">self</span>.statusbar <span class="op">=</span> <span class="va">self</span>.statusBar() </a>
<a class="sourceLine" id="cb1-34" data-line-number="34"> <span class="va">self</span>.tboard.msg2Statusbar[<span class="bu">str</span>].<span class="ex">connect</span>(<span class="va">self</span>.statusbar.showMessage)</a>
<a class="sourceLine" id="cb1-35" data-line-number="35"> </a>
<a class="sourceLine" id="cb1-36" data-line-number="36"> <span class="va">self</span>.tboard.start()</a>
<a class="sourceLine" id="cb1-37" data-line-number="37"> </a>
<a class="sourceLine" id="cb1-38" data-line-number="38"> <span class="va">self</span>.resize(<span class="dv">180</span>, <span class="dv">380</span>)</a>
<a class="sourceLine" id="cb1-39" data-line-number="39"> <span class="va">self</span>.center()</a>
<a class="sourceLine" id="cb1-40" data-line-number="40"> <span class="va">self</span>.setWindowTitle(<span class="st">&#39;Tetris&#39;</span>) </a>
<a class="sourceLine" id="cb1-41" data-line-number="41"> <span class="va">self</span>.show()</a>
<a class="sourceLine" id="cb1-42" data-line-number="42"> </a>
<a class="sourceLine" id="cb1-43" data-line-number="43"></a>
<a class="sourceLine" id="cb1-44" data-line-number="44"> <span class="kw">def</span> center(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-45" data-line-number="45"> <span class="co">&#39;&#39;&#39;centers the window on the screen&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-46" data-line-number="46"> </a>
<a class="sourceLine" id="cb1-47" data-line-number="47"> screen <span class="op">=</span> QDesktopWidget().screenGeometry()</a>
<a class="sourceLine" id="cb1-48" data-line-number="48"> size <span class="op">=</span> <span class="va">self</span>.geometry()</a>
<a class="sourceLine" id="cb1-49" data-line-number="49"> <span class="va">self</span>.move((screen.width()<span class="op">-</span>size.width())<span class="op">/</span><span class="dv">2</span>, </a>
<a class="sourceLine" id="cb1-50" data-line-number="50"> (screen.height()<span class="op">-</span>size.height())<span class="op">/</span><span class="dv">2</span>)</a>
<a class="sourceLine" id="cb1-51" data-line-number="51"> </a>
<a class="sourceLine" id="cb1-52" data-line-number="52"></a>
<a class="sourceLine" id="cb1-53" data-line-number="53"><span class="kw">class</span> Board(QFrame):</a>
<a class="sourceLine" id="cb1-54" data-line-number="54"> </a>
<a class="sourceLine" id="cb1-55" data-line-number="55"> msg2Statusbar <span class="op">=</span> pyqtSignal(<span class="bu">str</span>)</a>
<a class="sourceLine" id="cb1-56" data-line-number="56"> </a>
<a class="sourceLine" id="cb1-57" data-line-number="57"> BoardWidth <span class="op">=</span> <span class="dv">10</span></a>
<a class="sourceLine" id="cb1-58" data-line-number="58"> BoardHeight <span class="op">=</span> <span class="dv">22</span></a>
<a class="sourceLine" id="cb1-59" data-line-number="59"> Speed <span class="op">=</span> <span class="dv">300</span></a>
<a class="sourceLine" id="cb1-60" data-line-number="60"></a>
<a class="sourceLine" id="cb1-61" data-line-number="61"> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, parent):</a>
<a class="sourceLine" id="cb1-62" data-line-number="62"> <span class="bu">super</span>().<span class="fu">__init__</span>(parent)</a>
<a class="sourceLine" id="cb1-63" data-line-number="63"> </a>
<a class="sourceLine" id="cb1-64" data-line-number="64"> <span class="va">self</span>.initBoard()</a>
<a class="sourceLine" id="cb1-65" data-line-number="65"> </a>
<a class="sourceLine" id="cb1-66" data-line-number="66"> </a>
<a class="sourceLine" id="cb1-67" data-line-number="67"> <span class="kw">def</span> initBoard(<span class="va">self</span>): </a>
<a class="sourceLine" id="cb1-68" data-line-number="68"> <span class="co">&#39;&#39;&#39;initiates board&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-69" data-line-number="69"></a>
<a class="sourceLine" id="cb1-70" data-line-number="70"> <span class="va">self</span>.timer <span class="op">=</span> QBasicTimer()</a>
<a class="sourceLine" id="cb1-71" data-line-number="71"> <span class="va">self</span>.isWaitingAfterLine <span class="op">=</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-72" data-line-number="72"> </a>
<a class="sourceLine" id="cb1-73" data-line-number="73"> <span class="va">self</span>.curX <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-74" data-line-number="74"> <span class="va">self</span>.curY <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-75" data-line-number="75"> <span class="va">self</span>.numLinesRemoved <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-76" data-line-number="76"> <span class="va">self</span>.board <span class="op">=</span> []</a>
<a class="sourceLine" id="cb1-77" data-line-number="77"></a>
<a class="sourceLine" id="cb1-78" data-line-number="78"> <span class="va">self</span>.setFocusPolicy(Qt.StrongFocus)</a>
<a class="sourceLine" id="cb1-79" data-line-number="79"> <span class="va">self</span>.isStarted <span class="op">=</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-80" data-line-number="80"> <span class="va">self</span>.isPaused <span class="op">=</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-81" data-line-number="81"> <span class="va">self</span>.clearBoard()</a>
<a class="sourceLine" id="cb1-82" data-line-number="82"> </a>
<a class="sourceLine" id="cb1-83" data-line-number="83"> </a>
<a class="sourceLine" id="cb1-84" data-line-number="84"> <span class="kw">def</span> shapeAt(<span class="va">self</span>, x, y):</a>
<a class="sourceLine" id="cb1-85" data-line-number="85"> <span class="co">&#39;&#39;&#39;determines shape at the board position&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-86" data-line-number="86"> </a>
<a class="sourceLine" id="cb1-87" data-line-number="87"> <span class="cf">return</span> <span class="va">self</span>.board[(y <span class="op">*</span> Board.BoardWidth) <span class="op">+</span> x]</a>
<a class="sourceLine" id="cb1-88" data-line-number="88"></a>
<a class="sourceLine" id="cb1-89" data-line-number="89"> </a>
<a class="sourceLine" id="cb1-90" data-line-number="90"> <span class="kw">def</span> setShapeAt(<span class="va">self</span>, x, y, shape):</a>
<a class="sourceLine" id="cb1-91" data-line-number="91"> <span class="co">&#39;&#39;&#39;sets a shape at the board&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-92" data-line-number="92"> </a>
<a class="sourceLine" id="cb1-93" data-line-number="93"> <span class="va">self</span>.board[(y <span class="op">*</span> Board.BoardWidth) <span class="op">+</span> x] <span class="op">=</span> shape</a>
<a class="sourceLine" id="cb1-94" data-line-number="94"> </a>
<a class="sourceLine" id="cb1-95" data-line-number="95"></a>
<a class="sourceLine" id="cb1-96" data-line-number="96"> <span class="kw">def</span> squareWidth(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-97" data-line-number="97"> <span class="co">&#39;&#39;&#39;returns the width of one square&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-98" data-line-number="98"> </a>
<a class="sourceLine" id="cb1-99" data-line-number="99"> <span class="cf">return</span> <span class="va">self</span>.contentsRect().width() <span class="op">//</span> Board.BoardWidth</a>
<a class="sourceLine" id="cb1-100" data-line-number="100"> </a>
<a class="sourceLine" id="cb1-101" data-line-number="101"></a>
<a class="sourceLine" id="cb1-102" data-line-number="102"> <span class="kw">def</span> squareHeight(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-103" data-line-number="103"> <span class="co">&#39;&#39;&#39;returns the height of one square&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-104" data-line-number="104"> </a>
<a class="sourceLine" id="cb1-105" data-line-number="105"> <span class="cf">return</span> <span class="va">self</span>.contentsRect().height() <span class="op">//</span> Board.BoardHeight</a>
<a class="sourceLine" id="cb1-106" data-line-number="106"> </a>
<a class="sourceLine" id="cb1-107" data-line-number="107"></a>
<a class="sourceLine" id="cb1-108" data-line-number="108"> <span class="kw">def</span> start(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-109" data-line-number="109"> <span class="co">&#39;&#39;&#39;starts game&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-110" data-line-number="110"> </a>
<a class="sourceLine" id="cb1-111" data-line-number="111"> <span class="cf">if</span> <span class="va">self</span>.isPaused:</a>
<a class="sourceLine" id="cb1-112" data-line-number="112"> <span class="cf">return</span></a>
<a class="sourceLine" id="cb1-113" data-line-number="113"></a>
<a class="sourceLine" id="cb1-114" data-line-number="114"> <span class="va">self</span>.isStarted <span class="op">=</span> <span class="va">True</span></a>
<a class="sourceLine" id="cb1-115" data-line-number="115"> <span class="va">self</span>.isWaitingAfterLine <span class="op">=</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-116" data-line-number="116"> <span class="va">self</span>.numLinesRemoved <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-117" data-line-number="117"> <span class="va">self</span>.clearBoard()</a>
<a class="sourceLine" id="cb1-118" data-line-number="118"></a>
<a class="sourceLine" id="cb1-119" data-line-number="119"> <span class="va">self</span>.msg2Statusbar.emit(<span class="bu">str</span>(<span class="va">self</span>.numLinesRemoved))</a>
<a class="sourceLine" id="cb1-120" data-line-number="120"></a>
<a class="sourceLine" id="cb1-121" data-line-number="121"> <span class="va">self</span>.newPiece()</a>
<a class="sourceLine" id="cb1-122" data-line-number="122"> <span class="va">self</span>.timer.start(Board.Speed, <span class="va">self</span>)</a>
<a class="sourceLine" id="cb1-123" data-line-number="123"></a>
<a class="sourceLine" id="cb1-124" data-line-number="124"> </a>
<a class="sourceLine" id="cb1-125" data-line-number="125"> <span class="kw">def</span> pause(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-126" data-line-number="126"> <span class="co">&#39;&#39;&#39;pauses game&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-127" data-line-number="127"> </a>
<a class="sourceLine" id="cb1-128" data-line-number="128"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.isStarted:</a>
<a class="sourceLine" id="cb1-129" data-line-number="129"> <span class="cf">return</span></a>
<a class="sourceLine" id="cb1-130" data-line-number="130"></a>
<a class="sourceLine" id="cb1-131" data-line-number="131"> <span class="va">self</span>.isPaused <span class="op">=</span> <span class="kw">not</span> <span class="va">self</span>.isPaused</a>
<a class="sourceLine" id="cb1-132" data-line-number="132"> </a>
<a class="sourceLine" id="cb1-133" data-line-number="133"> <span class="cf">if</span> <span class="va">self</span>.isPaused:</a>
<a class="sourceLine" id="cb1-134" data-line-number="134"> <span class="va">self</span>.timer.stop()</a>
<a class="sourceLine" id="cb1-135" data-line-number="135"> <span class="va">self</span>.msg2Statusbar.emit(<span class="st">&quot;paused&quot;</span>)</a>
<a class="sourceLine" id="cb1-136" data-line-number="136"> </a>
<a class="sourceLine" id="cb1-137" data-line-number="137"> <span class="cf">else</span>:</a>
<a class="sourceLine" id="cb1-138" data-line-number="138"> <span class="va">self</span>.timer.start(Board.Speed, <span class="va">self</span>)</a>
<a class="sourceLine" id="cb1-139" data-line-number="139"> <span class="va">self</span>.msg2Statusbar.emit(<span class="bu">str</span>(<span class="va">self</span>.numLinesRemoved))</a>
<a class="sourceLine" id="cb1-140" data-line-number="140"></a>
<a class="sourceLine" id="cb1-141" data-line-number="141"> <span class="va">self</span>.update()</a>
<a class="sourceLine" id="cb1-142" data-line-number="142"></a>
<a class="sourceLine" id="cb1-143" data-line-number="143"> </a>
<a class="sourceLine" id="cb1-144" data-line-number="144"> <span class="kw">def</span> paintEvent(<span class="va">self</span>, event):</a>
<a class="sourceLine" id="cb1-145" data-line-number="145"> <span class="co">&#39;&#39;&#39;paints all shapes of the game&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-146" data-line-number="146"> </a>
<a class="sourceLine" id="cb1-147" data-line-number="147"> painter <span class="op">=</span> QPainter(<span class="va">self</span>)</a>
<a class="sourceLine" id="cb1-148" data-line-number="148"> rect <span class="op">=</span> <span class="va">self</span>.contentsRect()</a>
<a class="sourceLine" id="cb1-149" data-line-number="149"></a>
<a class="sourceLine" id="cb1-150" data-line-number="150"> boardTop <span class="op">=</span> rect.bottom() <span class="op">-</span> Board.BoardHeight <span class="op">*</span> <span class="va">self</span>.squareHeight()</a>
<a class="sourceLine" id="cb1-151" data-line-number="151"></a>
<a class="sourceLine" id="cb1-152" data-line-number="152"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(Board.BoardHeight):</a>
<a class="sourceLine" id="cb1-153" data-line-number="153"> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(Board.BoardWidth):</a>
<a class="sourceLine" id="cb1-154" data-line-number="154"> shape <span class="op">=</span> <span class="va">self</span>.shapeAt(j, Board.BoardHeight <span class="op">-</span> i <span class="op">-</span> <span class="dv">1</span>)</a>
<a class="sourceLine" id="cb1-155" data-line-number="155"> </a>
<a class="sourceLine" id="cb1-156" data-line-number="156"> <span class="cf">if</span> shape <span class="op">!=</span> Tetrominoe.NoShape:</a>
<a class="sourceLine" id="cb1-157" data-line-number="157"> <span class="va">self</span>.drawSquare(painter,</a>
<a class="sourceLine" id="cb1-158" data-line-number="158"> rect.left() <span class="op">+</span> j <span class="op">*</span> <span class="va">self</span>.squareWidth(),</a>
<a class="sourceLine" id="cb1-159" data-line-number="159"> boardTop <span class="op">+</span> i <span class="op">*</span> <span class="va">self</span>.squareHeight(), shape)</a>
<a class="sourceLine" id="cb1-160" data-line-number="160"></a>
<a class="sourceLine" id="cb1-161" data-line-number="161"> <span class="cf">if</span> <span class="va">self</span>.curPiece.shape() <span class="op">!=</span> Tetrominoe.NoShape:</a>
<a class="sourceLine" id="cb1-162" data-line-number="162"> </a>
<a class="sourceLine" id="cb1-163" data-line-number="163"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-164" data-line-number="164"> </a>
<a class="sourceLine" id="cb1-165" data-line-number="165"> x <span class="op">=</span> <span class="va">self</span>.curX <span class="op">+</span> <span class="va">self</span>.curPiece.x(i)</a>
<a class="sourceLine" id="cb1-166" data-line-number="166"> y <span class="op">=</span> <span class="va">self</span>.curY <span class="op">-</span> <span class="va">self</span>.curPiece.y(i)</a>
<a class="sourceLine" id="cb1-167" data-line-number="167"> <span class="va">self</span>.drawSquare(painter, rect.left() <span class="op">+</span> x <span class="op">*</span> <span class="va">self</span>.squareWidth(),</a>
<a class="sourceLine" id="cb1-168" data-line-number="168"> boardTop <span class="op">+</span> (Board.BoardHeight <span class="op">-</span> y <span class="op">-</span> <span class="dv">1</span>) <span class="op">*</span> <span class="va">self</span>.squareHeight(),</a>
<a class="sourceLine" id="cb1-169" data-line-number="169"> <span class="va">self</span>.curPiece.shape())</a>
<a class="sourceLine" id="cb1-170" data-line-number="170"></a>
<a class="sourceLine" id="cb1-171" data-line-number="171"> </a>
<a class="sourceLine" id="cb1-172" data-line-number="172"> <span class="kw">def</span> keyPressEvent(<span class="va">self</span>, event):</a>
<a class="sourceLine" id="cb1-173" data-line-number="173"> <span class="co">&#39;&#39;&#39;processes key press events&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-174" data-line-number="174"> </a>
<a class="sourceLine" id="cb1-175" data-line-number="175"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.isStarted <span class="kw">or</span> <span class="va">self</span>.curPiece.shape() <span class="op">==</span> Tetrominoe.NoShape:</a>
<a class="sourceLine" id="cb1-176" data-line-number="176"> <span class="bu">super</span>(Board, <span class="va">self</span>).keyPressEvent(event)</a>
<a class="sourceLine" id="cb1-177" data-line-number="177"> <span class="cf">return</span></a>
<a class="sourceLine" id="cb1-178" data-line-number="178"></a>
<a class="sourceLine" id="cb1-179" data-line-number="179"> key <span class="op">=</span> event.key()</a>
<a class="sourceLine" id="cb1-180" data-line-number="180"> </a>
<a class="sourceLine" id="cb1-181" data-line-number="181"> <span class="cf">if</span> key <span class="op">==</span> Qt.Key_P:</a>
<a class="sourceLine" id="cb1-182" data-line-number="182"> <span class="va">self</span>.pause()</a>
<a class="sourceLine" id="cb1-183" data-line-number="183"> <span class="cf">return</span></a>
<a class="sourceLine" id="cb1-184" data-line-number="184"> </a>
<a class="sourceLine" id="cb1-185" data-line-number="185"> <span class="cf">if</span> <span class="va">self</span>.isPaused:</a>
<a class="sourceLine" id="cb1-186" data-line-number="186"> <span class="cf">return</span></a>
<a class="sourceLine" id="cb1-187" data-line-number="187"> </a>
<a class="sourceLine" id="cb1-188" data-line-number="188"> <span class="cf">elif</span> key <span class="op">==</span> Qt.Key_Left:</a>
<a class="sourceLine" id="cb1-189" data-line-number="189"> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece, <span class="va">self</span>.curX <span class="op">-</span> <span class="dv">1</span>, <span class="va">self</span>.curY)</a>
<a class="sourceLine" id="cb1-190" data-line-number="190"> </a>
<a class="sourceLine" id="cb1-191" data-line-number="191"> <span class="cf">elif</span> key <span class="op">==</span> Qt.Key_Right:</a>
<a class="sourceLine" id="cb1-192" data-line-number="192"> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece, <span class="va">self</span>.curX <span class="op">+</span> <span class="dv">1</span>, <span class="va">self</span>.curY)</a>
<a class="sourceLine" id="cb1-193" data-line-number="193"> </a>
<a class="sourceLine" id="cb1-194" data-line-number="194"> <span class="cf">elif</span> key <span class="op">==</span> Qt.Key_Down:</a>
<a class="sourceLine" id="cb1-195" data-line-number="195"> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece.rotateRight(), <span class="va">self</span>.curX, <span class="va">self</span>.curY)</a>
<a class="sourceLine" id="cb1-196" data-line-number="196"> </a>
<a class="sourceLine" id="cb1-197" data-line-number="197"> <span class="cf">elif</span> key <span class="op">==</span> Qt.Key_Up:</a>
<a class="sourceLine" id="cb1-198" data-line-number="198"> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece.rotateLeft(), <span class="va">self</span>.curX, <span class="va">self</span>.curY)</a>
<a class="sourceLine" id="cb1-199" data-line-number="199"> </a>
<a class="sourceLine" id="cb1-200" data-line-number="200"> <span class="cf">elif</span> key <span class="op">==</span> Qt.Key_Space:</a>
<a class="sourceLine" id="cb1-201" data-line-number="201"> <span class="va">self</span>.dropDown()</a>
<a class="sourceLine" id="cb1-202" data-line-number="202"> </a>
<a class="sourceLine" id="cb1-203" data-line-number="203"> <span class="cf">elif</span> key <span class="op">==</span> Qt.Key_D:</a>
<a class="sourceLine" id="cb1-204" data-line-number="204"> <span class="va">self</span>.oneLineDown()</a>
<a class="sourceLine" id="cb1-205" data-line-number="205"> </a>
<a class="sourceLine" id="cb1-206" data-line-number="206"> <span class="cf">else</span>:</a>
<a class="sourceLine" id="cb1-207" data-line-number="207"> <span class="bu">super</span>(Board, <span class="va">self</span>).keyPressEvent(event)</a>
<a class="sourceLine" id="cb1-208" data-line-number="208"> </a>
<a class="sourceLine" id="cb1-209" data-line-number="209"></a>
<a class="sourceLine" id="cb1-210" data-line-number="210"> <span class="kw">def</span> timerEvent(<span class="va">self</span>, event):</a>
<a class="sourceLine" id="cb1-211" data-line-number="211"> <span class="co">&#39;&#39;&#39;handles timer event&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-212" data-line-number="212"> </a>
<a class="sourceLine" id="cb1-213" data-line-number="213"> <span class="cf">if</span> event.timerId() <span class="op">==</span> <span class="va">self</span>.timer.timerId():</a>
<a class="sourceLine" id="cb1-214" data-line-number="214"> </a>
<a class="sourceLine" id="cb1-215" data-line-number="215"> <span class="cf">if</span> <span class="va">self</span>.isWaitingAfterLine:</a>
<a class="sourceLine" id="cb1-216" data-line-number="216"> <span class="va">self</span>.isWaitingAfterLine <span class="op">=</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-217" data-line-number="217"> <span class="va">self</span>.newPiece()</a>
<a class="sourceLine" id="cb1-218" data-line-number="218"> <span class="cf">else</span>:</a>
<a class="sourceLine" id="cb1-219" data-line-number="219"> <span class="va">self</span>.oneLineDown()</a>
<a class="sourceLine" id="cb1-220" data-line-number="220"> </a>
<a class="sourceLine" id="cb1-221" data-line-number="221"> <span class="cf">else</span>:</a>
<a class="sourceLine" id="cb1-222" data-line-number="222"> <span class="bu">super</span>(Board, <span class="va">self</span>).timerEvent(event)</a>
<a class="sourceLine" id="cb1-223" data-line-number="223"></a>
<a class="sourceLine" id="cb1-224" data-line-number="224"> </a>
<a class="sourceLine" id="cb1-225" data-line-number="225"> <span class="kw">def</span> clearBoard(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-226" data-line-number="226"> <span class="co">&#39;&#39;&#39;clears shapes from the board&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-227" data-line-number="227"> </a>
<a class="sourceLine" id="cb1-228" data-line-number="228"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(Board.BoardHeight <span class="op">*</span> Board.BoardWidth):</a>
<a class="sourceLine" id="cb1-229" data-line-number="229"> <span class="va">self</span>.board.append(Tetrominoe.NoShape)</a>
<a class="sourceLine" id="cb1-230" data-line-number="230"></a>
<a class="sourceLine" id="cb1-231" data-line-number="231"> </a>
<a class="sourceLine" id="cb1-232" data-line-number="232"> <span class="kw">def</span> dropDown(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-233" data-line-number="233"> <span class="co">&#39;&#39;&#39;drops down a shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-234" data-line-number="234"> </a>
<a class="sourceLine" id="cb1-235" data-line-number="235"> newY <span class="op">=</span> <span class="va">self</span>.curY</a>
<a class="sourceLine" id="cb1-236" data-line-number="236"> </a>
<a class="sourceLine" id="cb1-237" data-line-number="237"> <span class="cf">while</span> newY <span class="op">&gt;</span> <span class="dv">0</span>:</a>
<a class="sourceLine" id="cb1-238" data-line-number="238"> </a>
<a class="sourceLine" id="cb1-239" data-line-number="239"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece, <span class="va">self</span>.curX, newY <span class="op">-</span> <span class="dv">1</span>):</a>
<a class="sourceLine" id="cb1-240" data-line-number="240"> <span class="cf">break</span></a>
<a class="sourceLine" id="cb1-241" data-line-number="241"> </a>
<a class="sourceLine" id="cb1-242" data-line-number="242"> newY <span class="op">-=</span> <span class="dv">1</span></a>
<a class="sourceLine" id="cb1-243" data-line-number="243"></a>
<a class="sourceLine" id="cb1-244" data-line-number="244"> <span class="va">self</span>.pieceDropped()</a>
<a class="sourceLine" id="cb1-245" data-line-number="245"> </a>
<a class="sourceLine" id="cb1-246" data-line-number="246"></a>
<a class="sourceLine" id="cb1-247" data-line-number="247"> <span class="kw">def</span> oneLineDown(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-248" data-line-number="248"> <span class="co">&#39;&#39;&#39;goes one line down with a shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-249" data-line-number="249"> </a>
<a class="sourceLine" id="cb1-250" data-line-number="250"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece, <span class="va">self</span>.curX, <span class="va">self</span>.curY <span class="op">-</span> <span class="dv">1</span>):</a>
<a class="sourceLine" id="cb1-251" data-line-number="251"> <span class="va">self</span>.pieceDropped()</a>
<a class="sourceLine" id="cb1-252" data-line-number="252"> </a>
<a class="sourceLine" id="cb1-253" data-line-number="253"></a>
<a class="sourceLine" id="cb1-254" data-line-number="254"> <span class="kw">def</span> pieceDropped(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-255" data-line-number="255"> <span class="co">&#39;&#39;&#39;after dropping shape, remove full lines and create new shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-256" data-line-number="256"> </a>
<a class="sourceLine" id="cb1-257" data-line-number="257"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-258" data-line-number="258"> </a>
<a class="sourceLine" id="cb1-259" data-line-number="259"> x <span class="op">=</span> <span class="va">self</span>.curX <span class="op">+</span> <span class="va">self</span>.curPiece.x(i)</a>
<a class="sourceLine" id="cb1-260" data-line-number="260"> y <span class="op">=</span> <span class="va">self</span>.curY <span class="op">-</span> <span class="va">self</span>.curPiece.y(i)</a>
<a class="sourceLine" id="cb1-261" data-line-number="261"> <span class="va">self</span>.setShapeAt(x, y, <span class="va">self</span>.curPiece.shape())</a>
<a class="sourceLine" id="cb1-262" data-line-number="262"></a>
<a class="sourceLine" id="cb1-263" data-line-number="263"> <span class="va">self</span>.removeFullLines()</a>
<a class="sourceLine" id="cb1-264" data-line-number="264"></a>
<a class="sourceLine" id="cb1-265" data-line-number="265"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.isWaitingAfterLine:</a>
<a class="sourceLine" id="cb1-266" data-line-number="266"> <span class="va">self</span>.newPiece()</a>
<a class="sourceLine" id="cb1-267" data-line-number="267"> </a>
<a class="sourceLine" id="cb1-268" data-line-number="268"></a>
<a class="sourceLine" id="cb1-269" data-line-number="269"> <span class="kw">def</span> removeFullLines(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-270" data-line-number="270"> <span class="co">&#39;&#39;&#39;removes all full lines from the board&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-271" data-line-number="271"> </a>
<a class="sourceLine" id="cb1-272" data-line-number="272"> numFullLines <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-273" data-line-number="273"> rowsToRemove <span class="op">=</span> []</a>
<a class="sourceLine" id="cb1-274" data-line-number="274"></a>
<a class="sourceLine" id="cb1-275" data-line-number="275"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(Board.BoardHeight):</a>
<a class="sourceLine" id="cb1-276" data-line-number="276"> </a>
<a class="sourceLine" id="cb1-277" data-line-number="277"> n <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-278" data-line-number="278"> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(Board.BoardWidth):</a>
<a class="sourceLine" id="cb1-279" data-line-number="279"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.shapeAt(j, i) <span class="op">==</span> Tetrominoe.NoShape:</a>
<a class="sourceLine" id="cb1-280" data-line-number="280"> n <span class="op">=</span> n <span class="op">+</span> <span class="dv">1</span></a>
<a class="sourceLine" id="cb1-281" data-line-number="281"></a>
<a class="sourceLine" id="cb1-282" data-line-number="282"> <span class="cf">if</span> n <span class="op">==</span> <span class="dv">10</span>:</a>
<a class="sourceLine" id="cb1-283" data-line-number="283"> rowsToRemove.append(i)</a>
<a class="sourceLine" id="cb1-284" data-line-number="284"></a>
<a class="sourceLine" id="cb1-285" data-line-number="285"> rowsToRemove.reverse()</a>
<a class="sourceLine" id="cb1-286" data-line-number="286"> </a>
<a class="sourceLine" id="cb1-287" data-line-number="287"></a>
<a class="sourceLine" id="cb1-288" data-line-number="288"> <span class="cf">for</span> m <span class="kw">in</span> rowsToRemove:</a>
<a class="sourceLine" id="cb1-289" data-line-number="289"> </a>
<a class="sourceLine" id="cb1-290" data-line-number="290"> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(m, Board.BoardHeight):</a>
<a class="sourceLine" id="cb1-291" data-line-number="291"> <span class="cf">for</span> l <span class="kw">in</span> <span class="bu">range</span>(Board.BoardWidth):</a>
<a class="sourceLine" id="cb1-292" data-line-number="292"> <span class="va">self</span>.setShapeAt(l, k, <span class="va">self</span>.shapeAt(l, k <span class="op">+</span> <span class="dv">1</span>))</a>
<a class="sourceLine" id="cb1-293" data-line-number="293"></a>
<a class="sourceLine" id="cb1-294" data-line-number="294"> numFullLines <span class="op">=</span> numFullLines <span class="op">+</span> <span class="bu">len</span>(rowsToRemove)</a>
<a class="sourceLine" id="cb1-295" data-line-number="295"></a>
<a class="sourceLine" id="cb1-296" data-line-number="296"> <span class="cf">if</span> numFullLines <span class="op">&gt;</span> <span class="dv">0</span>:</a>
<a class="sourceLine" id="cb1-297" data-line-number="297"> </a>
<a class="sourceLine" id="cb1-298" data-line-number="298"> <span class="va">self</span>.numLinesRemoved <span class="op">=</span> <span class="va">self</span>.numLinesRemoved <span class="op">+</span> numFullLines</a>
<a class="sourceLine" id="cb1-299" data-line-number="299"> <span class="va">self</span>.msg2Statusbar.emit(<span class="bu">str</span>(<span class="va">self</span>.numLinesRemoved))</a>
<a class="sourceLine" id="cb1-300" data-line-number="300"> </a>
<a class="sourceLine" id="cb1-301" data-line-number="301"> <span class="va">self</span>.isWaitingAfterLine <span class="op">=</span> <span class="va">True</span></a>
<a class="sourceLine" id="cb1-302" data-line-number="302"> <span class="va">self</span>.curPiece.setShape(Tetrominoe.NoShape)</a>
<a class="sourceLine" id="cb1-303" data-line-number="303"> <span class="va">self</span>.update()</a>
<a class="sourceLine" id="cb1-304" data-line-number="304"> </a>
<a class="sourceLine" id="cb1-305" data-line-number="305"></a>
<a class="sourceLine" id="cb1-306" data-line-number="306"> <span class="kw">def</span> newPiece(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-307" data-line-number="307"> <span class="co">&#39;&#39;&#39;creates a new shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-308" data-line-number="308"> </a>
<a class="sourceLine" id="cb1-309" data-line-number="309"> <span class="va">self</span>.curPiece <span class="op">=</span> Shape()</a>
<a class="sourceLine" id="cb1-310" data-line-number="310"> <span class="va">self</span>.curPiece.setRandomShape()</a>
<a class="sourceLine" id="cb1-311" data-line-number="311"> <span class="va">self</span>.curX <span class="op">=</span> Board.BoardWidth <span class="op">//</span> <span class="dv">2</span> <span class="op">+</span> <span class="dv">1</span></a>
<a class="sourceLine" id="cb1-312" data-line-number="312"> <span class="va">self</span>.curY <span class="op">=</span> Board.BoardHeight <span class="op">-</span> <span class="dv">1</span> <span class="op">+</span> <span class="va">self</span>.curPiece.minY()</a>
<a class="sourceLine" id="cb1-313" data-line-number="313"> </a>
<a class="sourceLine" id="cb1-314" data-line-number="314"> <span class="cf">if</span> <span class="kw">not</span> <span class="va">self</span>.tryMove(<span class="va">self</span>.curPiece, <span class="va">self</span>.curX, <span class="va">self</span>.curY):</a>
<a class="sourceLine" id="cb1-315" data-line-number="315"> </a>
<a class="sourceLine" id="cb1-316" data-line-number="316"> <span class="va">self</span>.curPiece.setShape(Tetrominoe.NoShape)</a>
<a class="sourceLine" id="cb1-317" data-line-number="317"> <span class="va">self</span>.timer.stop()</a>
<a class="sourceLine" id="cb1-318" data-line-number="318"> <span class="va">self</span>.isStarted <span class="op">=</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-319" data-line-number="319"> <span class="va">self</span>.msg2Statusbar.emit(<span class="st">&quot;Game over&quot;</span>)</a>
<a class="sourceLine" id="cb1-320" data-line-number="320"></a>
<a class="sourceLine" id="cb1-321" data-line-number="321"></a>
<a class="sourceLine" id="cb1-322" data-line-number="322"></a>
<a class="sourceLine" id="cb1-323" data-line-number="323"> <span class="kw">def</span> tryMove(<span class="va">self</span>, newPiece, newX, newY):</a>
<a class="sourceLine" id="cb1-324" data-line-number="324"> <span class="co">&#39;&#39;&#39;tries to move a shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-325" data-line-number="325"> </a>
<a class="sourceLine" id="cb1-326" data-line-number="326"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-327" data-line-number="327"> </a>
<a class="sourceLine" id="cb1-328" data-line-number="328"> x <span class="op">=</span> newX <span class="op">+</span> newPiece.x(i)</a>
<a class="sourceLine" id="cb1-329" data-line-number="329"> y <span class="op">=</span> newY <span class="op">-</span> newPiece.y(i)</a>
<a class="sourceLine" id="cb1-330" data-line-number="330"> </a>
<a class="sourceLine" id="cb1-331" data-line-number="331"> <span class="cf">if</span> x <span class="op">&lt;</span> <span class="dv">0</span> <span class="kw">or</span> x <span class="op">&gt;=</span> Board.BoardWidth <span class="kw">or</span> y <span class="op">&lt;</span> <span class="dv">0</span> <span class="kw">or</span> y <span class="op">&gt;=</span> Board.BoardHeight:</a>
<a class="sourceLine" id="cb1-332" data-line-number="332"> <span class="cf">return</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-333" data-line-number="333"> </a>
<a class="sourceLine" id="cb1-334" data-line-number="334"> <span class="cf">if</span> <span class="va">self</span>.shapeAt(x, y) <span class="op">!=</span> Tetrominoe.NoShape:</a>
<a class="sourceLine" id="cb1-335" data-line-number="335"> <span class="cf">return</span> <span class="va">False</span></a>
<a class="sourceLine" id="cb1-336" data-line-number="336"></a>
<a class="sourceLine" id="cb1-337" data-line-number="337"> <span class="va">self</span>.curPiece <span class="op">=</span> newPiece</a>
<a class="sourceLine" id="cb1-338" data-line-number="338"> <span class="va">self</span>.curX <span class="op">=</span> newX</a>
<a class="sourceLine" id="cb1-339" data-line-number="339"> <span class="va">self</span>.curY <span class="op">=</span> newY</a>
<a class="sourceLine" id="cb1-340" data-line-number="340"> <span class="va">self</span>.update()</a>
<a class="sourceLine" id="cb1-341" data-line-number="341"> </a>
<a class="sourceLine" id="cb1-342" data-line-number="342"> <span class="cf">return</span> <span class="va">True</span></a>
<a class="sourceLine" id="cb1-343" data-line-number="343"> </a>
<a class="sourceLine" id="cb1-344" data-line-number="344"></a>
<a class="sourceLine" id="cb1-345" data-line-number="345"> <span class="kw">def</span> drawSquare(<span class="va">self</span>, painter, x, y, shape):</a>
<a class="sourceLine" id="cb1-346" data-line-number="346"> <span class="co">&#39;&#39;&#39;draws a square of a shape&#39;&#39;&#39;</span> </a>
<a class="sourceLine" id="cb1-347" data-line-number="347"> </a>
<a class="sourceLine" id="cb1-348" data-line-number="348"> colorTable <span class="op">=</span> [<span class="bn">0x000000</span>, <span class="bn">0xCC6666</span>, <span class="bn">0x66CC66</span>, <span class="bn">0x6666CC</span>,</a>
<a class="sourceLine" id="cb1-349" data-line-number="349"> <span class="bn">0xCCCC66</span>, <span class="bn">0xCC66CC</span>, <span class="bn">0x66CCCC</span>, <span class="bn">0xDAAA00</span>]</a>
<a class="sourceLine" id="cb1-350" data-line-number="350"></a>
<a class="sourceLine" id="cb1-351" data-line-number="351"> color <span class="op">=</span> QColor(colorTable[shape])</a>
<a class="sourceLine" id="cb1-352" data-line-number="352"> painter.fillRect(x <span class="op">+</span> <span class="dv">1</span>, y <span class="op">+</span> <span class="dv">1</span>, <span class="va">self</span>.squareWidth() <span class="op">-</span> <span class="dv">2</span>, </a>
<a class="sourceLine" id="cb1-353" data-line-number="353"> <span class="va">self</span>.squareHeight() <span class="op">-</span> <span class="dv">2</span>, color)</a>
<a class="sourceLine" id="cb1-354" data-line-number="354"></a>
<a class="sourceLine" id="cb1-355" data-line-number="355"> painter.setPen(color.lighter())</a>
<a class="sourceLine" id="cb1-356" data-line-number="356"> painter.drawLine(x, y <span class="op">+</span> <span class="va">self</span>.squareHeight() <span class="op">-</span> <span class="dv">1</span>, x, y)</a>
<a class="sourceLine" id="cb1-357" data-line-number="357"> painter.drawLine(x, y, x <span class="op">+</span> <span class="va">self</span>.squareWidth() <span class="op">-</span> <span class="dv">1</span>, y)</a>
<a class="sourceLine" id="cb1-358" data-line-number="358"></a>
<a class="sourceLine" id="cb1-359" data-line-number="359"> painter.setPen(color.darker())</a>
<a class="sourceLine" id="cb1-360" data-line-number="360"> painter.drawLine(x <span class="op">+</span> <span class="dv">1</span>, y <span class="op">+</span> <span class="va">self</span>.squareHeight() <span class="op">-</span> <span class="dv">1</span>,</a>
<a class="sourceLine" id="cb1-361" data-line-number="361"> x <span class="op">+</span> <span class="va">self</span>.squareWidth() <span class="op">-</span> <span class="dv">1</span>, y <span class="op">+</span> <span class="va">self</span>.squareHeight() <span class="op">-</span> <span class="dv">1</span>)</a>
<a class="sourceLine" id="cb1-362" data-line-number="362"> painter.drawLine(x <span class="op">+</span> <span class="va">self</span>.squareWidth() <span class="op">-</span> <span class="dv">1</span>, </a>
<a class="sourceLine" id="cb1-363" data-line-number="363"> y <span class="op">+</span> <span class="va">self</span>.squareHeight() <span class="op">-</span> <span class="dv">1</span>, x <span class="op">+</span> <span class="va">self</span>.squareWidth() <span class="op">-</span> <span class="dv">1</span>, y <span class="op">+</span> <span class="dv">1</span>)</a>
<a class="sourceLine" id="cb1-364" data-line-number="364"></a>
<a class="sourceLine" id="cb1-365" data-line-number="365"></a>
<a class="sourceLine" id="cb1-366" data-line-number="366"><span class="kw">class</span> Tetrominoe(<span class="bu">object</span>):</a>
<a class="sourceLine" id="cb1-367" data-line-number="367"> </a>
<a class="sourceLine" id="cb1-368" data-line-number="368"> NoShape <span class="op">=</span> <span class="dv">0</span></a>
<a class="sourceLine" id="cb1-369" data-line-number="369"> ZShape <span class="op">=</span> <span class="dv">1</span></a>
<a class="sourceLine" id="cb1-370" data-line-number="370"> SShape <span class="op">=</span> <span class="dv">2</span></a>
<a class="sourceLine" id="cb1-371" data-line-number="371"> LineShape <span class="op">=</span> <span class="dv">3</span></a>
<a class="sourceLine" id="cb1-372" data-line-number="372"> TShape <span class="op">=</span> <span class="dv">4</span></a>
<a class="sourceLine" id="cb1-373" data-line-number="373"> SquareShape <span class="op">=</span> <span class="dv">5</span></a>
<a class="sourceLine" id="cb1-374" data-line-number="374"> LShape <span class="op">=</span> <span class="dv">6</span></a>
<a class="sourceLine" id="cb1-375" data-line-number="375"> MirroredLShape <span class="op">=</span> <span class="dv">7</span></a>
<a class="sourceLine" id="cb1-376" data-line-number="376"></a>
<a class="sourceLine" id="cb1-377" data-line-number="377"></a>
<a class="sourceLine" id="cb1-378" data-line-number="378"><span class="kw">class</span> Shape(<span class="bu">object</span>):</a>
<a class="sourceLine" id="cb1-379" data-line-number="379"> </a>
<a class="sourceLine" id="cb1-380" data-line-number="380"> coordsTable <span class="op">=</span> (</a>
<a class="sourceLine" id="cb1-381" data-line-number="381"> ((<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">0</span>)),</a>
<a class="sourceLine" id="cb1-382" data-line-number="382"> ((<span class="dv">0</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">0</span>), (<span class="op">-</span><span class="dv">1</span>, <span class="dv">1</span>)),</a>
<a class="sourceLine" id="cb1-383" data-line-number="383"> ((<span class="dv">0</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">1</span>, <span class="dv">0</span>), (<span class="dv">1</span>, <span class="dv">1</span>)),</a>
<a class="sourceLine" id="cb1-384" data-line-number="384"> ((<span class="dv">0</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">1</span>), (<span class="dv">0</span>, <span class="dv">2</span>)),</a>
<a class="sourceLine" id="cb1-385" data-line-number="385"> ((<span class="op">-</span><span class="dv">1</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">1</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">1</span>)),</a>
<a class="sourceLine" id="cb1-386" data-line-number="386"> ((<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">1</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">1</span>), (<span class="dv">1</span>, <span class="dv">1</span>)),</a>
<a class="sourceLine" id="cb1-387" data-line-number="387"> ((<span class="op">-</span><span class="dv">1</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">1</span>)),</a>
<a class="sourceLine" id="cb1-388" data-line-number="388"> ((<span class="dv">1</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">-1</span>), (<span class="dv">0</span>, <span class="dv">0</span>), (<span class="dv">0</span>, <span class="dv">1</span>))</a>
<a class="sourceLine" id="cb1-389" data-line-number="389"> )</a>
<a class="sourceLine" id="cb1-390" data-line-number="390"></a>
<a class="sourceLine" id="cb1-391" data-line-number="391"> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-392" data-line-number="392"> </a>
<a class="sourceLine" id="cb1-393" data-line-number="393"> <span class="va">self</span>.coords <span class="op">=</span> [[<span class="dv">0</span>,<span class="dv">0</span>] <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>)]</a>
<a class="sourceLine" id="cb1-394" data-line-number="394"> <span class="va">self</span>.pieceShape <span class="op">=</span> Tetrominoe.NoShape</a>
<a class="sourceLine" id="cb1-395" data-line-number="395"></a>
<a class="sourceLine" id="cb1-396" data-line-number="396"> <span class="va">self</span>.setShape(Tetrominoe.NoShape)</a>
<a class="sourceLine" id="cb1-397" data-line-number="397"> </a>
<a class="sourceLine" id="cb1-398" data-line-number="398"></a>
<a class="sourceLine" id="cb1-399" data-line-number="399"> <span class="kw">def</span> shape(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-400" data-line-number="400"> <span class="co">&#39;&#39;&#39;returns shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-401" data-line-number="401"> </a>
<a class="sourceLine" id="cb1-402" data-line-number="402"> <span class="cf">return</span> <span class="va">self</span>.pieceShape</a>
<a class="sourceLine" id="cb1-403" data-line-number="403"> </a>
<a class="sourceLine" id="cb1-404" data-line-number="404"></a>
<a class="sourceLine" id="cb1-405" data-line-number="405"> <span class="kw">def</span> setShape(<span class="va">self</span>, shape):</a>
<a class="sourceLine" id="cb1-406" data-line-number="406"> <span class="co">&#39;&#39;&#39;sets a shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-407" data-line-number="407"> </a>
<a class="sourceLine" id="cb1-408" data-line-number="408"> table <span class="op">=</span> Shape.coordsTable[shape]</a>
<a class="sourceLine" id="cb1-409" data-line-number="409"> </a>
<a class="sourceLine" id="cb1-410" data-line-number="410"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-411" data-line-number="411"> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">2</span>):</a>
<a class="sourceLine" id="cb1-412" data-line-number="412"> <span class="va">self</span>.coords[i][j] <span class="op">=</span> table[i][j]</a>
<a class="sourceLine" id="cb1-413" data-line-number="413"></a>
<a class="sourceLine" id="cb1-414" data-line-number="414"> <span class="va">self</span>.pieceShape <span class="op">=</span> shape</a>
<a class="sourceLine" id="cb1-415" data-line-number="415"> </a>
<a class="sourceLine" id="cb1-416" data-line-number="416"></a>
<a class="sourceLine" id="cb1-417" data-line-number="417"> <span class="kw">def</span> setRandomShape(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-418" data-line-number="418"> <span class="co">&#39;&#39;&#39;chooses a random shape&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-419" data-line-number="419"> </a>
<a class="sourceLine" id="cb1-420" data-line-number="420"> <span class="va">self</span>.setShape(random.randint(<span class="dv">1</span>, <span class="dv">7</span>))</a>
<a class="sourceLine" id="cb1-421" data-line-number="421"></a>
<a class="sourceLine" id="cb1-422" data-line-number="422"> </a>
<a class="sourceLine" id="cb1-423" data-line-number="423"> <span class="kw">def</span> x(<span class="va">self</span>, index):</a>
<a class="sourceLine" id="cb1-424" data-line-number="424"> <span class="co">&#39;&#39;&#39;returns x coordinate&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-425" data-line-number="425"> </a>
<a class="sourceLine" id="cb1-426" data-line-number="426"> <span class="cf">return</span> <span class="va">self</span>.coords[index][<span class="dv">0</span>]</a>
<a class="sourceLine" id="cb1-427" data-line-number="427"></a>
<a class="sourceLine" id="cb1-428" data-line-number="428"> </a>
<a class="sourceLine" id="cb1-429" data-line-number="429"> <span class="kw">def</span> y(<span class="va">self</span>, index):</a>
<a class="sourceLine" id="cb1-430" data-line-number="430"> <span class="co">&#39;&#39;&#39;returns y coordinate&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-431" data-line-number="431"> </a>
<a class="sourceLine" id="cb1-432" data-line-number="432"> <span class="cf">return</span> <span class="va">self</span>.coords[index][<span class="dv">1</span>]</a>
<a class="sourceLine" id="cb1-433" data-line-number="433"></a>
<a class="sourceLine" id="cb1-434" data-line-number="434"> </a>
<a class="sourceLine" id="cb1-435" data-line-number="435"> <span class="kw">def</span> setX(<span class="va">self</span>, index, x):</a>
<a class="sourceLine" id="cb1-436" data-line-number="436"> <span class="co">&#39;&#39;&#39;sets x coordinate&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-437" data-line-number="437"> </a>
<a class="sourceLine" id="cb1-438" data-line-number="438"> <span class="va">self</span>.coords[index][<span class="dv">0</span>] <span class="op">=</span> x</a>
<a class="sourceLine" id="cb1-439" data-line-number="439"></a>
<a class="sourceLine" id="cb1-440" data-line-number="440"> </a>
<a class="sourceLine" id="cb1-441" data-line-number="441"> <span class="kw">def</span> setY(<span class="va">self</span>, index, y):</a>
<a class="sourceLine" id="cb1-442" data-line-number="442"> <span class="co">&#39;&#39;&#39;sets y coordinate&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-443" data-line-number="443"> </a>
<a class="sourceLine" id="cb1-444" data-line-number="444"> <span class="va">self</span>.coords[index][<span class="dv">1</span>] <span class="op">=</span> y</a>
<a class="sourceLine" id="cb1-445" data-line-number="445"></a>
<a class="sourceLine" id="cb1-446" data-line-number="446"> </a>
<a class="sourceLine" id="cb1-447" data-line-number="447"> <span class="kw">def</span> minX(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-448" data-line-number="448"> <span class="co">&#39;&#39;&#39;returns min x value&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-449" data-line-number="449"> </a>
<a class="sourceLine" id="cb1-450" data-line-number="450"> m <span class="op">=</span> <span class="va">self</span>.coords[<span class="dv">0</span>][<span class="dv">0</span>]</a>
<a class="sourceLine" id="cb1-451" data-line-number="451"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-452" data-line-number="452"> m <span class="op">=</span> <span class="bu">min</span>(m, <span class="va">self</span>.coords[i][<span class="dv">0</span>])</a>
<a class="sourceLine" id="cb1-453" data-line-number="453"></a>
<a class="sourceLine" id="cb1-454" data-line-number="454"> <span class="cf">return</span> m</a>
<a class="sourceLine" id="cb1-455" data-line-number="455"></a>
<a class="sourceLine" id="cb1-456" data-line-number="456"> </a>
<a class="sourceLine" id="cb1-457" data-line-number="457"> <span class="kw">def</span> maxX(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-458" data-line-number="458"> <span class="co">&#39;&#39;&#39;returns max x value&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-459" data-line-number="459"> </a>
<a class="sourceLine" id="cb1-460" data-line-number="460"> m <span class="op">=</span> <span class="va">self</span>.coords[<span class="dv">0</span>][<span class="dv">0</span>]</a>
<a class="sourceLine" id="cb1-461" data-line-number="461"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-462" data-line-number="462"> m <span class="op">=</span> <span class="bu">max</span>(m, <span class="va">self</span>.coords[i][<span class="dv">0</span>])</a>
<a class="sourceLine" id="cb1-463" data-line-number="463"></a>
<a class="sourceLine" id="cb1-464" data-line-number="464"> <span class="cf">return</span> m</a>
<a class="sourceLine" id="cb1-465" data-line-number="465"></a>
<a class="sourceLine" id="cb1-466" data-line-number="466"> </a>
<a class="sourceLine" id="cb1-467" data-line-number="467"> <span class="kw">def</span> minY(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-468" data-line-number="468"> <span class="co">&#39;&#39;&#39;returns min y value&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-469" data-line-number="469"> </a>
<a class="sourceLine" id="cb1-470" data-line-number="470"> m <span class="op">=</span> <span class="va">self</span>.coords[<span class="dv">0</span>][<span class="dv">1</span>]</a>
<a class="sourceLine" id="cb1-471" data-line-number="471"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-472" data-line-number="472"> m <span class="op">=</span> <span class="bu">min</span>(m, <span class="va">self</span>.coords[i][<span class="dv">1</span>])</a>
<a class="sourceLine" id="cb1-473" data-line-number="473"></a>
<a class="sourceLine" id="cb1-474" data-line-number="474"> <span class="cf">return</span> m</a>
<a class="sourceLine" id="cb1-475" data-line-number="475"></a>
<a class="sourceLine" id="cb1-476" data-line-number="476"> </a>
<a class="sourceLine" id="cb1-477" data-line-number="477"> <span class="kw">def</span> maxY(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-478" data-line-number="478"> <span class="co">&#39;&#39;&#39;returns max y value&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-479" data-line-number="479"> </a>
<a class="sourceLine" id="cb1-480" data-line-number="480"> m <span class="op">=</span> <span class="va">self</span>.coords[<span class="dv">0</span>][<span class="dv">1</span>]</a>
<a class="sourceLine" id="cb1-481" data-line-number="481"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-482" data-line-number="482"> m <span class="op">=</span> <span class="bu">max</span>(m, <span class="va">self</span>.coords[i][<span class="dv">1</span>])</a>
<a class="sourceLine" id="cb1-483" data-line-number="483"></a>
<a class="sourceLine" id="cb1-484" data-line-number="484"> <span class="cf">return</span> m</a>
<a class="sourceLine" id="cb1-485" data-line-number="485"></a>
<a class="sourceLine" id="cb1-486" data-line-number="486"> </a>
<a class="sourceLine" id="cb1-487" data-line-number="487"> <span class="kw">def</span> rotateLeft(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-488" data-line-number="488"> <span class="co">&#39;&#39;&#39;rotates shape to the left&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-489" data-line-number="489"> </a>
<a class="sourceLine" id="cb1-490" data-line-number="490"> <span class="cf">if</span> <span class="va">self</span>.pieceShape <span class="op">==</span> Tetrominoe.SquareShape:</a>
<a class="sourceLine" id="cb1-491" data-line-number="491"> <span class="cf">return</span> <span class="va">self</span></a>
<a class="sourceLine" id="cb1-492" data-line-number="492"></a>
<a class="sourceLine" id="cb1-493" data-line-number="493"> result <span class="op">=</span> Shape()</a>
<a class="sourceLine" id="cb1-494" data-line-number="494"> result.pieceShape <span class="op">=</span> <span class="va">self</span>.pieceShape</a>
<a class="sourceLine" id="cb1-495" data-line-number="495"> </a>
<a class="sourceLine" id="cb1-496" data-line-number="496"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-497" data-line-number="497"> </a>
<a class="sourceLine" id="cb1-498" data-line-number="498"> result.setX(i, <span class="va">self</span>.y(i))</a>
<a class="sourceLine" id="cb1-499" data-line-number="499"> result.setY(i, <span class="op">-</span><span class="va">self</span>.x(i))</a>
<a class="sourceLine" id="cb1-500" data-line-number="500"></a>
<a class="sourceLine" id="cb1-501" data-line-number="501"> <span class="cf">return</span> result</a>
<a class="sourceLine" id="cb1-502" data-line-number="502"></a>
<a class="sourceLine" id="cb1-503" data-line-number="503"> </a>
<a class="sourceLine" id="cb1-504" data-line-number="504"> <span class="kw">def</span> rotateRight(<span class="va">self</span>):</a>
<a class="sourceLine" id="cb1-505" data-line-number="505"> <span class="co">&#39;&#39;&#39;rotates shape to the right&#39;&#39;&#39;</span></a>
<a class="sourceLine" id="cb1-506" data-line-number="506"> </a>
<a class="sourceLine" id="cb1-507" data-line-number="507"> <span class="cf">if</span> <span class="va">self</span>.pieceShape <span class="op">==</span> Tetrominoe.SquareShape:</a>
<a class="sourceLine" id="cb1-508" data-line-number="508"> <span class="cf">return</span> <span class="va">self</span></a>
<a class="sourceLine" id="cb1-509" data-line-number="509"></a>
<a class="sourceLine" id="cb1-510" data-line-number="510"> result <span class="op">=</span> Shape()</a>
<a class="sourceLine" id="cb1-511" data-line-number="511"> result.pieceShape <span class="op">=</span> <span class="va">self</span>.pieceShape</a>
<a class="sourceLine" id="cb1-512" data-line-number="512"> </a>
<a class="sourceLine" id="cb1-513" data-line-number="513"> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">4</span>):</a>
<a class="sourceLine" id="cb1-514" data-line-number="514"> </a>
<a class="sourceLine" id="cb1-515" data-line-number="515"> result.setX(i, <span class="op">-</span><span class="va">self</span>.y(i))</a>
<a class="sourceLine" id="cb1-516" data-line-number="516"> result.setY(i, <span class="va">self</span>.x(i))</a>
<a class="sourceLine" id="cb1-517" data-line-number="517"></a>
<a class="sourceLine" id="cb1-518" data-line-number="518"> <span class="cf">return</span> result</a>
<a class="sourceLine" id="cb1-519" data-line-number="519"></a>
<a class="sourceLine" id="cb1-520" data-line-number="520"></a>
<a class="sourceLine" id="cb1-521" data-line-number="521"><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&#39;__main__&#39;</span>:</a>
<a class="sourceLine" id="cb1-522" data-line-number="522"> </a>
<a class="sourceLine" id="cb1-523" data-line-number="523"> app <span class="op">=</span> QApplication([])</a>
<a class="sourceLine" id="cb1-524" data-line-number="524"> tetris <span class="op">=</span> Tetris() </a>
<a class="sourceLine" id="cb1-525" data-line-number="525"> sys.exit(app.exec_())</a></code></pre></div>
<p>游戏很简单所以也就很好理解。程序加载之后游戏也就直接开始了可以用P键暂停游戏空格键让方块直接落到最下面。游戏的速度是固定的并没有实现加速的功能。分数就是游戏中消除的行数。</p>
<pre><code class="language-python">self.tboard = Board(self)
self.setCentralWidget(self.tboard)</code></pre>
<p>创建了一个Board类的实例并设置为应用的中心组件。</p>
<pre><code class="language-python">self.statusbar = self.statusBar()
self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)</code></pre>
<p>创建一个<code>statusbar</code>来显示三种信息:消除的行数,游戏暂停状态或者游戏结束状态。<code>msg2Statusbar</code>是一个自定义的信号用在Board类交互<code>showMessage()</code>方法是一个内建的用来在statusbar上显示信息的方法。</p>
<pre><code class="language-python">self.tboard.start()</code></pre>
<p>初始化游戏:</p>
<pre><code class="language-python">class Board(QFrame):
msg2Statusbar = pyqtSignal(str)
... </code></pre>
<p>创建了一个自定义信号<code>msg2Statusbar</code>,当我们想往<code>statusbar</code>里显示信息的时候,发出这个信号就行了。</p>
<pre><code class="language-python">BoardWidth = 10
BoardHeight = 22
Speed = 300</code></pre>
<p>这些是<code>Board</code>类的变量。<code>BoardWidth</code><code>BoardHeight</code>分别是board的宽度和高度。<code>Speed</code>是游戏的速度每300ms出现一个新的方块。</p>
<pre><code class="language-python">...
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
...</code></pre>
<p><code>initBoard()</code>里初始化了一些重要的变量。<code>self.board</code>定义了方块的形状和位置取值范围是0-7。</p>
<pre><code class="language-python">def shapeAt(self, x, y):
return self.board[(y * Board.BoardWidth) + x]</code></pre>
<p><code>shapeAt()</code>决定了board里方块的的种类。</p>
<pre><code class="language-python">def squareWidth(self):
return self.contentsRect().width() // Board.BoardWidth</code></pre>
<p>board的大小可以动态的改变。所以方格的大小也应该随之变化。<code>squareWidth()</code>计算并返回每个块应该占用多少像素–也即<code>Board.BoardWidth</code></p>
<pre><code class="language-python">def pause(self):
&#39;&#39;&#39;pauses game&#39;&#39;&#39;
if not self.isStarted:
return
self.isPaused = not self.isPaused
if self.isPaused:
self.timer.stop()
self.msg2Statusbar.emit(&quot;paused&quot;)
else:
self.timer.start(Board.Speed, self)
self.msg2Statusbar.emit(str(self.numLinesRemoved))
self.update()</code></pre>
<p><code>pause()</code>方法用来暂停游戏,停止计时并在<code>statusbar</code>上显示一条信息。</p>
<pre><code class="language-python">def paintEvent(self, event):
&#39;&#39;&#39;paints all shapes of the game&#39;&#39;&#39;
painter = QPainter(self)
rect = self.contentsRect()
...</code></pre>
<p>渲染是在paintEvent()方法里发生的<code>QPainter</code>负责PyQt5里所有低级绘画操作。</p>
<pre><code class="language-python">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)</code></pre>
<p>渲染游戏分为两步。第一步是先画出所有已经落在最下面的的图,这些保存在<code>self.board</code>里。可以使用<code>shapeAt()</code>查看这个这个变量。</p>
<pre><code class="language-python">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())</code></pre>
<p>第二步是画出更在下落的方块。</p>
<pre><code class="language-python">elif key == Qt.Key_Right:
self.tryMove(self.curPiece, self.curX + 1, self.curY)</code></pre>
<p><code>keyPressEvent()</code>方法获得用户按下的按键。如果按下的是右方向键,就尝试把方块向右移动,说尝试是因为有可能到边界不能移动了。</p>
<pre><code class="language-python">elif key == Qt.Key_Up:
self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY)</code></pre>
<p>上方向键是把方块向左旋转一下</p>
<pre><code class="language-python">elif key == Qt.Key_Space:
self.dropDown()</code></pre>
<p>空格键会直接把方块放到底部</p>
<pre><code class="language-python">elif key == Qt.Key_D:
self.oneLineDown()</code></pre>
<p>D键是加速一次下落速度。</p>
<pre><code class="language-python">def tryMove(self, newPiece, newX, newY):
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x &lt; 0 or x &gt;= Board.BoardWidth or y &lt; 0 or y &gt;= 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</code></pre>
<p><code>tryMove()</code>是尝试移动方块的方法。如果方块已经到达board的边缘或者遇到了其他方块就返回False。否则就把方块下落到想要</p>
<pre><code class="language-python">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)</code></pre>
<p>在计时器事件里要么是等一个方块下落完之后创建一个新的方块要么是让一个方块直接落到底move a falling piece one line down</p>
<pre><code class="language-python">def clearBoard(self):
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoe.NoShape)</code></pre>
<p><code>clearBoard(</code>)方法通过<code>Tetrominoe.NoShape</code>清空<code>broad</code></p>
<pre><code class="language-python">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)
...</code></pre>
<p>如果方块碰到了底部,就调用<code>removeFullLines()</code>方法,找到所有能消除的行消除它们。消除的具体动作就是把符合条件的行消除掉之后,再把它上面的行下降一行。注意移除满行的动作是倒着来的,因为我们是按照重力来表现游戏的,如果不这样就有可能出现有些方块浮在空中的现象。</p>
<pre><code class="language-python">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(&quot;Game over&quot;)</code></pre>
<p><code>newPiece()</code>方法是用来创建形状随机的方块。如果随机的方块不能正确的出现在预设的位置,游戏结束。</p>
<pre><code class="language-python">class Tetrominoe(object):
NoShape = 0
ZShape = 1
SShape = 2
LineShape = 3
TShape = 4
SquareShape = 5
LShape = 6
MirroredLShape = 7</code></pre>
<p><code>Tetrominoe</code>类保存了所有方块的形状。我们还定义了一个<code>NoShape</code>的空形状。</p>
<p>Shape类保存类方块内部的信息。</p>
<pre><code class="language-python">class Shape(object):
coordsTable = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
...
)
... </code></pre>
<p>coordsTable元组保存了所有的方块形状的组成。是一个构成方块的坐标模版。</p>
<pre><code class="language-python">self.coords = [[0,0] for i in range(4)]</code></pre>
<p>上面创建了一个新的空坐标数组,这个数组将用来保存方块的坐标。</p>
<p>坐标系示意图:</p>
<figure>
<img class="whitelist" src="docs/PyQt5/images/11-coordinates.png" alt="coordinates" />
</figure>
<p>上面的图片可以帮助我们更好的理解坐标值的意义。比如元组<code>(0, -1), (0, 0), (-1, 0), (-1, -1)</code>代表了一个Z形状的方块。这个图表就描绘了这个形状。</p>
<pre><code class="language-python">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</code></pre>
<p><code>rotateLeft()</code>方法向右旋转一个方块。正方形的方块就没必要旋转,就直接返回了。其他的是返回一个新的,能表示这个形状旋转了的坐标。</p>
<p>程序展示:</p>
<figure>
<img class="whitelist" src="docs/PyQt5/images/11-tetris.png" alt="Tetris" />
</figure>