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.