2015-11-20 236 views
2

我想加载应该在其自己的线程中运行的Tkinter UI。创建UI实例后,当尝试访问该属性时出现此错误:line 18, in do_move: self.game_ui.setTile(4, 0, 0) AttributeError: 'Play' object has no attribute 'game_ui'无法访问类属性

我不确定是否弄乱了线程或其他内容,但我也注意到self.game_ui.drawBoard(..)并未实际更新UI。

import GUI 
import game 


class Play: 
    def __init__(self): 
     print ('Init') 
     self.game_core = game.Board() 
     print(self.game_core.tiles) 
     self.game_ui = GUI.getNewBoardWindow(self.keylistener) 
     self.game_ui.drawBoard(self.game_core.tiles) 

    def keylistener(self, event): 
     self.do_move((event.keycode-38) % 4) 

    def do_move(self, key): 
     print(key) 
     self.game_ui.setTile(4, 0, 0) 


def main(): 
    Play() 


main() 

凡GUI是这样的:

from tkinter import * 
from threading import Thread 


class GUI(Frame, Thread): 
    cellColors = {0: "#CCC0B3", 2: "#eee4da", 4: "#ede0c8", 8: "#f2b179", 16: "#f59563", 32: "#f67c5f", 64: "#f65e3b", 128: "#edcf72", 256: "#edcc61", 512: "#edc850", 1024: "#edc53f", 2048: "#edc22e", 4096: "#3c3a32"} 
    boardSize = 4 

    def __init__(self, parent): 
     Frame.__init__(self, parent, bg="#BBADA0") 
     Thread.__init__(self) 
     self.parent = parent 
     self.cells = [] 
     self.initUI(150) 
     self.pack() 
     self.start() 
     self.parent.mainloop() 

    def initUI(self, cellSize): 
     for row in range(self.boardSize-1, -1, -1): 
      self.cells.append([]) 
      for column in range(self.boardSize): 
       cell = Frame(self, width=cellSize, height=cellSize) 
       cell.grid(row=row, column=column, padx=4, pady=4) 
       cell.pack_propagate(0) 

       tile = Label(cell, bg="#CCC0B3", font=("Helvetica", 35, "bold")) 
       tile.pack(fill=BOTH, expand=1) 
       self.cells[-1].append(tile) 

    def setTile(self, value, x, y): 
     print(value) 
     self.cells[y][x].config({ 
      "bg": GUI.cellColors[min(4096, value)], 
      "fg": ("#776E65" if value < 8 else "#f9f6f2"), 
      "text": str(value) if value else ''}) 

    def drawBoard(self, board): 
     for col in range(len(board)): 
      for row in range(len(board)): 
       self.setTile(board[col][row], col, row) 


def getNewBoardWindow(listener=None): 
    root = Tk() 
    root.title("2048") 
    if listener: 
     root.bind("<Key>", listener) 

    app = GUI(root) 
    root.mainloop() 
    #mainThread = Thread(target=root.mainloop) 
    #mainThread.start() 
    return app 

回答

1

你开始在getNewBoardWindow()主循环root.mainloop(),这反过来将返回app将在Play被assinged到self.game_ui。由于getNewBoardWindow()在GUI运行时不能返回,因此self.game_ui不能存在。

您可以通过app一起返回root解决这个问题:

def getNewBoardWindow(listener=None): 
    root = Tk() 
    root.title("2048") 
    if listener: 
     root.bind("<Key>", listener) 
    app = GUI(root) 
    return app, root 

Play

class Play: 
    def __init__(self): 
     print ('Init') 
     self.game_core = game.Board() 
     print(self.game_core.tiles) 
     self.game_ui, self.root = GUI.getNewBoardWindow(self.keylistener) 
     self.game_ui.drawBoard(self.game_core.tiles) 
     self.root.mainloop() 

我会建议重新设计你的程序,以便root.mainloop()是在结束主程序,不在__init__()内。

+0

所以我要调用start'root.mainloop()''中Play',分配后? – tsorn

+0

是的,我添加了一个建议,你可以做到这一点。 –