2015-11-20 2 views
2

Я пытаюсь загрузить пользовательский интерфейс Tkinter, который должен запускаться в своем потоке. После создания экземпляра пользовательского интерфейса я получаю эту ошибку при попытке получить доступ к атрибуту: 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

Вы начинаете MainLoop root.mainloop() в getNewBoardWindow(), который, в свою очередь, будет возвращать app, которые будут assinged к self.game_ui в Play. Поскольку getNewBoardWindow() не может вернуться во время работы графического интерфейса, self.game_ui не может существовать.

Вы можете это исправить, вернув root вместе с app:

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

Да, я добавил предложение, как вы могли это сделать. –

Смежные вопросы