Nézzük át a kódját, hogy is működik...
A játékot Qt felülettel készítettem el, amihez a Pyside nevű, a Nokiások által fejlesztett és LGPL licensz alatt közreadott nyílt forrású eszközkészletet használtam fel.
Első lépésként megterveztem a felületet a Qtdesigner nevű felülettervezővel. Noha a felület teljesen egyszerű, mivel túl sok tapasztalatom nincs sem Qt felületek kialakításában, sem a Qtdesigner használatában, elbabráltam vele egy darabig.
Az ablak alapja egy Horizontal Layout, abban van egy Vertical Layout, ami a színes gombokat, a feliratokat és az indítógombokat tartalmazza, mellette meg egy Horizontal Layout, ami a játéktáblát, azaz egy QGraphicsView objektumot tartalmaz.
A színes gombokhoz nem akartam saját widgetet készíteni, ezért azokat QPushButton objektumokkal oldottam meg, amiket egy 3x2-es GridLayout-ba pakoltam. Az ui tartalmaz még egy állapotsort, meg egy menüsort (alapból), de a menüt nem használtam, így nem jelenik meg.
A következő lépés az ui-ból python modult faragni. Ezt a pyside-uic segédprogrammal tudjuk megtenni:
pyside-iuc pyflood.ui ui_pyflood.py
Ez egy python osztályt generál nekünk nagyon okosan, amelyet egyszerűen be tudunk importálni a programunkba.
A játék működése a programozás szempontjából a következő:
- tábla feltöltése és kirajzolása
- várakozás a start gomb kattintására, addig a többi gomb (a színes négyzetek) le vannak tiltva
- bármely színes gomb lenyomása esetén a megnyomott színnek megfelelően a tábla frissítése
- a 3. lépés ismétlése amíg a lépések száma el nem éri a maximumot, vagy a tábla egyszínű nem lesz
- üzenet kiírása a nyerésről vagy vesztésről, start gomb frissítése 'Új játék'-ra és kezdődik előlről
A játék közben is lehetőség van az 'Új játék' megnyomására, ilyenkor a tábla resetelődik, és indul előlröl a dolog, illetve él még egy 'Segítség' link, amire kattintva egy kisablakba leírunk pár sort a játék működéséről.
A következő importokra van szükségünk:
import sys, locale import random from PySide.QtCore import * from PySide.QtGui import * from ui_pyflood import * import os basedir = os.path.dirname(os.path.realpath(__file__)) VERSION='1.1'
Ezenkívül már itt elmentjük egy változóba a futó program helyét, kelleni fog a nyelvi fájlok betöltéséhez, valamint egy verzió változót hozunk létre az esetleges változatok követéséhez.
A program törzse végtelenül egyszerű:
if __name__=="__main__":
app=QApplication(sys.argv)
translator = QTranslator()
translator.load(basedir + '/'+ locale.getdefaultlocale()[0] + ".qm")
app.installTranslator(translator)
g=Game()
g.show()
app.exec_()
Létrehozunk egy QApplication példányt (ez minden Qt programhoz kell), példányosítunk egy QTranslatort, betöltjük a locale szerinti nyelvi fájlt (ha van, de ezt a translator lekezeli), majd betoljuk az app-ba a translator példányunkat, így az majd szorgalmasan lefordít "mindent".
Példányosítjuk Game osztályunkat, ami egy QMainWindow tulajdonképpen, csak megspékeltük a feldolgozó metódusainkkal, megjelenítjük a főablakot, majd indítjuk az app feldolgozó hurkát.
Mielőtt rátérnénk a Game osztály "kibeszélésére" előbb meg kell ismerkednünk a Board osztállyal, ugyanis ez jeleníti meg a játéktáblánkat, és a Game is őt használja.
Íme:
class Board(QGraphicsScene):
def __init__(self,parent):
super(Board,self).__init__()
self.colors=[]
self.squares=[]
for i in range(14):
self.colors.append([0]*14)
self.squares.append([0]*14)
self.colorcodes=['#FFAAFF','#0055FF','#F4F400','#DE0000','#00D8D8','#007E00']
self.recolor()
for i in range(14):
for j in range(14):
pen=QPen(QColor(self.colorcodes[self.colors[i][j]]),0)
brush=QBrush(QColor(self.colorcodes[self.colors[i][j]]))
self.squares[i][j]=self.addRect(i*24,j*24,24,24,pen,brush)
def recolor(self):
for i in range(14):
for j in range(14):
self.colors[i][j]=random.randint(0,5)
def repaint(self):
for i in range(14):
for j in range(14):
self.squares[i][j].setBrush(QBrush(QColor(self.colorcodes[self.colors[i][j]])))
self.squares[i][j].setPen(QPen(QColor(self.colorcodes[self.colors[i][j]])))
Ez az osztály tulajdonképpen egy QtGraphicsScene néhány belső változóval és saját metódussal, vagy is egy grafikai jelenet tárolására képes osztály.
A konstruktor létrehozza a négyzetek színeit tároló kétdimenziós listát (self.colors), és egy másikat maguknak a grafikai elemeknek, vagyis a négyzeteknek (self.squares). Ezután feltölti a színlistát random színekkel, és megalkotja a négyzet elemeket.
Két metódust kapott a Board osztályunk, recolor és repaint, az előbbi arra szolgál hogy véletlenszerűen feltöltse a színek listáját, az utóbbi pedig, hogy az aktuális színekkel (self.colors tartalma) újraszínezze a négyzeteket.
Jöhet a Game osztály!
class Game(QMainWindow,Ui_MainWindow):
def __init__(self, parent=None):
super(Game, self).__init__(parent)
self.setupUi(self)
self.board=Board(self.horizontalLayout)
self.floodBoard.setScene(self.board)
self.gameInProgress=False
self.fillInProgress=False
self.steps=0
self.board_dirty=False
self.setWindowTitle("pyFlood" + ' (v ' + VERSION + ')')
self.pinkButton.clicked.connect(self.pinkclicked)
self.blueButton.clicked.connect(self.blueclicked)
self.yellowButton.clicked.connect(self.yellowclicked)
self.redButton.clicked.connect(self.redclicked)
self.cyanButton.clicked.connect(self.cyanclicked)
self.greenButton.clicked.connect(self.greenclicked)
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow",'Click \'Start\' to start the game.', None, QtGui.QApplication.UnicodeUTF8))
self.startButton.clicked.connect(self.restartgame)
self.ruleLabel.linkActivated.connect(self.showrules)
def pinkclicked(self):
self.colorclicked(0)
def blueclicked(self):
self.colorclicked(1)
def yellowclicked(self):
self.colorclicked(2)
def redclicked(self):
self.colorclicked(3)
def cyanclicked(self):
self.colorclicked(4)
def greenclicked(self):
self.colorclicked(5)
def colorclicked(self,color):
if self.gameInProgress and not self.fillInProgress:
self.fillInProgress=True
currentcolor=self.board.colors[0][0]
fillbuffer=[(0,0)]
self.steps=self.steps+1
self.stepLabel.setText("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Droid Sans\'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:12pt; font-weight:600;\">%s</span></p></body></html>" % (QtGui.QApplication.translate("MainWindow",'Step: %d/25', None, QtGui.QApplication.UnicodeUTF8) % self.steps))
while len(fillbuffer)>0:
cur=fillbuffer.pop()
if self.board.colors[cur[0]][cur[1]]==currentcolor:
self.board.colors[cur[0]][cur[1]]=color
for i,j in [(-1,0),(0,-1),(1,0),(0,1)]:
x,y=cur[0]+i,cur[1]+j
if x>-1 and x<14 and y>-1 and y<14:
if self.board.colors[x][y]==currentcolor and (x,y) not in fillbuffer:
fillbuffer.append((x,y))
self.board.repaint()
self.fillInProgress=False
self.checkifwin()
def checkifwin(self):
currentcolor=self.board.colors[0][0]
wincolors=[]
for i in range(14):
wincolors.append([currentcolor]*14)
if self.board.colors == wincolors:
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow","You win!", None, QtGui.QApplication.UnicodeUTF8))
self.win=True
self.gameInProgress=False
ret=QMessageBox.information(self, "pyFlood",
QtGui.QApplication.translate("MainWindow","Congratulations!\nYou have won the game in %d steps!", None, QtGui.QApplication.UnicodeUTF8) % self.steps,
QMessageBox.Ok)
if ret==QMessageBox.Ok:
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow",'Click \'New game\' to start another game.', None, QtGui.QApplication.UnicodeUTF8))
elif self.steps >= 25:
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow","You have lost the game!", None, QtGui.QApplication.UnicodeUTF8))
self.win=False
self.gameInProgress=False
ret=QMessageBox.information(self, "pyFlood",
QtGui.QApplication.translate("MainWindow","You have lost the game!\nCould have a better luck next time...", None, QtGui.QApplication.UnicodeUTF8),
QMessageBox.Ok)
if ret==QMessageBox.Ok:
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow",'Click \'New game\' to start another game.', None, QtGui.QApplication.UnicodeUTF8))
def showrules(self):
ret=QMessageBox.information(self, "pyFlood",
('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> \
<html><head><meta name="qrichtext" content="1" /><style type="text/css"> \
p, li { white-space: pre-wrap; } \
</style></head><body style=" font-family:\'Droid Sans\'; font-size:10pt; font-weight:400; font-style:normal;"> \
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%s</p> \
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> \
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%s</p> \
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> \
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%s</p> \
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> \
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%s</p> \
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%s</p></body></html>') % (QtGui.QApplication.translate("MainWindow",'Click the \'Start\' buton to start a game (It will change to \'New game\').', None, QtGui.QApplication.UnicodeUTF8),QtGui.QApplication.translate("MainWindow",'On the upper left you can see six colored squares, and on the right you can see the gameboard.', None, QtGui.QApplication.UnicodeUTF8),QtGui.QApplication.translate("MainWindow","Your task is to make the whole board colored in one color. To achieve this, you should use the six colored square-buttons. The coloring starts from the upper left corner of the board. When you click on a color, all the squares started from the upper left with the same color will turn to the new color you clicked.", None, QtGui.QApplication.UnicodeUTF8),QtGui.QApplication.translate("MainWindow","It is harder to explain than do it :D", None, QtGui.QApplication.UnicodeUTF8),QtGui.QApplication.translate("MainWindow","Try it out!", None, QtGui.QApplication.UnicodeUTF8)),
QMessageBox.Ok)
def restartgame(self):
if self.gameInProgress:
ret = QMessageBox.information(self, self.tr("pyFlood"),
QtGui.QApplication.translate("MainWindow","This will terminate the current running game.\nDo you really want to do that?", None, QtGui.QApplication.UnicodeUTF8),
QMessageBox.Ok | QMessageBox.Cancel)
print ret
if ret==QMessageBox.Ok:
self.steps=0
self.board.recolor()
self.board.repaint()
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow","New game started.", None, QtGui.QApplication.UnicodeUTF8))
else:
self.gameInProgress=True
self.steps=0
if self.board_dirty:
self.board.recolor()
self.board.repaint()
else:
self.board_dirty=True
self.startButton.setText(QtGui.QApplication.translate("MainWindow", "New game", None, QtGui.QApplication.UnicodeUTF8))
self.statusbar.showMessage(QtGui.QApplication.translate("MainWindow","Game started.", None, QtGui.QApplication.UnicodeUTF8))
Az osztály egy QtMainWindow ami ráadásként a Qtdesigner és a pyside-uic által generált Ui_MainWindow osztálynak is gyermeke, vagyis tartalmazza az összes widgetet, amit a designerrel megalkottunk.
Nagyon nem ragoznám a működését, nem olyan bonyolult osztály. A konstruktor létrehozza a táblát a fenti Board osztályból, beállít pár játékváltozót, és metódusokat rendel a színgombokhoz, amik tulajdonképpen ugyanazt a metódust hívják meg - colorclicked - , csak a színüknek megfelelő színkóddal.
A colorclicked metódusba kellett egy "lock" mechanizmust építeni, ami megakadályozza, hogy a túl sűrű kattintások ne keverjék meg a színkezelést. Ezt a fillInProgress objektumváltozóval csináljuk. Ha elindul egy kitöltés, akkor ezt igazra állítjuk, így az újabb kattintás esetén, ha ez a változó igaz, akkor az előző kitöltés még nem fejeződött be, így nem kell csinálni semmit. Minden kitöltés után meghívódik a chekifwin metódus, ami azt ellenőrzi, hogy a játékot megnyerte-e a játékos, vagy esetleg elvesztette-e. Ha valamelyik igen, akkor megteszi a kiírásokat, és alaphelyzetbe állítja a játékot.
Röviden ennyi, remélem követhető volt. Lehet kérdezni!
Innen le tudod tölteni a forrást egyben: katt

A következő lépés az ui-ból python modult faragni. Ezt a pyside-uic segédprogrammal tudjuk megtenni:
VálaszTörléspyside-iuc pyflood.ui ui_pyflood.py
-------------------
leírnád részletesen hogy ezt a lépést hogy kell emgcsinálni?
A C:\Python32\Scripts\pyside-uic.exe nekem csak így átfut és eltűnik, hol/hogy tudom megadni neki hogy meik fájlt mibe rakja át?
köszi!
Én linux alatt pythonozok, lényegesen egyszerűbb, de gondolom win alatt ezt egy parancssorból a legegyszerűbb végrehajtani.
VálaszTörlésStart menü -> Futtatás -> cmd -> Enter
itt a cd paranccsal elnavigálsz a mappába ahol a fájlok vannak, és ott adod ki a parancsot. Szerintem így sikerrel jársz.