2011-02-10 17 views
1

私はTkinterを初めて使い、基礎を学ぶための簡単なタイルフリッピングゲームを書いています。アイデアは、私がアプリを起動すると、私は "新しいゲームを開始"ボタン、終了ボタンとグリッドサイズのスライダーを取得することです。次に、グリッドサイズを選択してヒットスタートを選択すると、赤色または緑色のボタン(タイル)のn * nグリッドが表示されます。 1つをクリックすると、タイルの色と4方向に接続されたタイルの色が変わります。すべてのタイルが緑色になると、タイルは消えます。新しいゲームを始めることを許可します。TkInter:いくつかのボタンを破棄して(おそらく異なる)いくつかのボタンを作り直す

ゲームに勝って新しいゲームを開始したり、新しいゲームを開始したりすると、新しいタイルが表示されますが、そのうちの1つをクリックすると、次のような問題が発生します。

Exception in Tkinter callback 
Traceback (most recent call last): 
    File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1413, in __call__ 
    return self.func(*args) 
    File "/home/adejong/workspace/test2/view.py", line 116, in on_click 
    self.view.update() 
    File "/home/adejong/workspace/test2/view.py", line 84, in update 
    button.set_colour(View.UP) 
    File "/home/adejong/workspace/test2/view.py", line 112, in set_colour 
    self.gridButton.config(bg=colour) 
    File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1205, in configure 
    return self._configure('configure', cnf, kw) 
    File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1196, in _configure 
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf)) 
TclError: invalid command name ".33325424.33325640.33327872" 

私はグリッドをどのようにクリアしているかとはかなり関係していますが、問題の原因がわかりません。私はもともと、新しいゲームを始めたときに表示するボタンを手に入れることができなかったので、それを使って何かをするかもしれません。ここで

はそれが起こっているモジュールです:

# @todo make wrappers for all Tkinter widgets 
from Tkinter import * 
from observerPattern import Observer 
# USE Grid to organise widgets into a grid 
class View(Observer): 
    UP = "red" 
    DOWN = "green" 

    ## @brief initialises the GUI 
    # @param master ie the root 
    #@ model, a pointer to the model 
    def __init__(self, master, model): 

     View.instance = self 
     self.model = model 
     self.master = master 

     self.frame = Frame(self.master) 
     self.frame.grid() #size frame to fit the given text, and make itself visibl 
     self.optionsGrid = Frame(self.frame) 
     self.gameGrid = Frame(self.frame) 
     self.optionsGrid.grid(row = 0, column = 0) 
     self.gameGrid.grid(row = 1, column = 0) 

     self.gridSizeScale = Scale(self.optionsGrid, label = "grid size", from_ = 2, to = 20, bigincrement = 1, 
            showvalue = True, orient = HORIZONTAL) 

     self.quit = Button(self.optionsGrid, text="QUIT", fg="red", command=self.frame.quit) 


     self.newGame = Button(self.optionsGrid, text="Start New Game", command=self.init_game) 
     self.objective = Label(self.optionsGrid, text="Make all the tiles green") 

     self.newGame.grid(row=0, column=0) 
     self.quit.grid(row=0, column=3) 
     self.gridSizeScale.grid(row=0, column=1, columnspan=2) 
     self.objective.grid(row=1, column=1, columnspan=2) 
     self.gameButtons = [] 
     self.update() 

    # start a new game by re building the grid 
    def init_game(self): 
     size = self.gridSizeScale.get() 
     self.model.init_model(size) 
     self.objective.config(text = "Make all the tiles green") 
     print "MODEL INITIALISED" 

     #for i in range(len(self.gameButtons)): 
     # self.gameButtons[i].grid_forget() 
     for button in self.gameButtons: 
       #button.destroy() 
       #self.gameGrid.grid_forget(button) 
       button.__del__() 

     for button in range(size * size): 
      buttonRow = int(button/size) 
      buttonCol = button % size 

      state = self.model.getTileState(buttonRow, buttonCol) 
      if state == self.model.UP: 
       initialColour = View.UP 
      else: 
       initialColour = View.DOWN 

      newButton = GridButton(self.gameGrid, butRow = buttonRow, butCol = buttonCol, initColour = initialColour, model = self.model, view = self) 

      self.gameButtons.append(newButton) 
      print self.gameButtons 
     self.gameGrid.grid() 




    ## @brief gets the only View instance. A static method. Dont really need this 
    # @param None 
    # @returns the singleton View object  
    @staticmethod 
    def getInstance(): 
     if hasattr(View, 'instance') and View.instance != None: 
      return View.instance 
     else: 
      return View() 

    # make sure the tiles are the right colour etc  
    def update(self): 
     for button in self.gameButtons: 
      state = self.model.getTileState(button.row, button.col) 
      if state == self.model.UP: 
       button.set_colour(View.UP) 
      else: 
       button.set_colour(View.DOWN) 
     if self.model.check_win() == True and self.model.tilesInitialised: 
      for button in self.gameButtons: 
       #button.destroy() 
       #self.gameGrid.grid_forget(button) 
       button.__del__() 

      #self.gameGrid.grid_forget() 
      print "slaves", self.gameGrid.grid_slaves() 
      self.objective.config(text = "You Win!") 
      self.master.update() 
      print "YOU WIN!" 

# a wrapper i made so i could pass parameters into the button commands 
class GridButton(Button): 

    def __init__(self, master, butRow, butCol, initColour, model, view, *args, **kw): 
     Button.__init__(self, master) 
     self.gridButton = Button(master, command=self.on_click, *args, **kw) 
     self.gridButton.grid(row = butRow, column = butCol) 
     self.set_colour(initColour) 
     self.row = butRow 
     self.col = butCol 
     self.model = model 
     self.view = view 

    def set_colour(self, colour): 
     self.gridButton.config(bg=colour) 

    def on_click(self): 
     self.model.flip_tiles(Row = self.row, Col = self.col) 
     self.view.update() 

    def __del__(self): 
     self.gridButton.destroy() 
     del self 

ここで完了

from Tkinter import * 
from model import Model 
from view import View 
from controller import Controller 
import sys 

root = Tk() #enter the Tkinter event loop 


model = Model() 
view = View(root, model) 
#controller = Controller(root, model, view) 
root.mainloop() 

from Tkinter import * 
import random 

class Model: 
    UP = 1 
    DOWN = 0 
    def __init__(self, gridSize=None): 
     Model.instance = self 
     self.init_model(gridSize) 

    def init_model(self, gridSize): 
     self.size = gridSize 
     self.tiles = [] 
     self.won = False 
     self.tilesInitialised = False 
     if gridSize != None: 
      self.init_tiles() 


    ## @brief gets the only Model instance. A static method 
    # @param None 
    # @returns the singleton Model object  
    @staticmethod 
    def getInstance(gridSize = None): 
     if hasattr(Model, 'instance') and Model.instance != None: 
      return Model.instance 
     else: 
      return Model(gridSize) 

    #initially init tile vals randomly but since not all problems can be solved 
     # might want to start with a soln and work backwards 

    def init_tiles(self): 
     # i should also make sure they're not all 0 to start with 
     self.tiles = [] 
     for row in range(self.size): 
      rowList = [] 
      for col in range(self.size): 
       rowList.append(random.randint(Model.DOWN, Model.UP)) 
      self.tiles.append(rowList) 
     self.check_win() 
     if self.won == True: 
      self.init_tiles() 
     self.tilesInitialised = True 

    # tile indexing starts at 0    
    def flip_tiles(self, selected = None, Row = None, Col = None): 
     if selected == None and (Row == None and Col == None): 
      raise IOError("Need a tile to flip") 
     elif selected != None: 
      neighbours = self.get_neighbours(selected) 

      for r in neighbours: 
       for c in r: 
        if self.tiles[r][c] == Model.DOWN: 
         self.tiles[r][c] = Model.UP 
        elif self.tiles[r][c] == Model.UP: 
         self.tiles[r][c] = Model.DOWN 

     else: 
      selectedTile = Row, Col 
      neighbours = self.get_neighbours(selectedTile) 
      for tile in neighbours: 
        r = tile[0] 
        c = tile[1] 
        if self.tiles[r][c] == Model.DOWN: 
         self.tiles[r][c] = Model.UP 
        elif self.tiles[r][c] == Model.UP: 
         self.tiles[r][c] = Model.DOWN 

    # selected is a tuple (row, column) 
    # returns a list of tuples of tiles to flip 
    def get_neighbours(self, selected): 
     row = selected[0] 
     col = selected[1] 
     tilesToFlip = [] 
     for modifier in range(-1,2): 
      rowIndex = row + modifier 

      if rowIndex < 0: 
       pass 
      elif rowIndex > self.size - 1 : 
       pass 
      else: 
       final = rowIndex, col 
       tilesToFlip.append(final) 


     for modifier in range(-1,2): 
      colIndex = col + modifier 

      if colIndex < 0: 
       pass 
      elif colIndex > self.size - 1: 
       pass 
      else: 
       final = row, colIndex 
       tilesToFlip.append(final) 


     neighbours = set(tilesToFlip) 
     return neighbours 


    def check_win(self): 
     self.won = True 
     #everytime a tile is selected 
     for row in range(len(self.tiles)): 
      for col in range(len(self.tiles)): 

       if self.tiles[row][col] == Model.UP: 
        self.won = False 
        break 

     return self.won 

    def getTileState(self, buttonRow, buttonCol): 
     return self.tiles[buttonRow][buttonCol] 

のリセットですすべてのヘルプは非常にあなたがself.game_buttonsをリセットしていないように見えます

答えて

1

をいただければ幸いです。おそらくinit_gameでそれを行う必要がある場合は、__init__の空のリストに設定します。リセットされていないため、ゲームを2回実行すると、self.view.update()は、両方のゲームのボタンを含むリストを反復処理します。これらのボタンの半分が欠けているので、今削除されたボタンの色を最初に変更しようとするとエラーになります。

ところで、これを管理する本当の簡単な方法は、すべてのボタンを内側フレームの子として配置することです。そうすることで、フレームを単に削除して、すべての子を削除することができます。副次的な利点として、ボタンのリストは必要ありません。なぜなら、すべての子に対してフレーム(winfo_children)を要求できるからです。

+0

ありがとう。それが問題でした。 – Andre

関連する問題