Tkinter窗口/小部件比例调整大小

时间:2013-12-03 18:20:27

标签: python user-interface tkinter

我目前正在为编程类开发一个用于Reversi的Python GUI版本。我已经编写了游戏逻辑,我正在尝试使用Tkinter实现GUI。我在比例调整游戏板(根窗口)及其上的所有内容(画布和形状)时遇到了一些问题。游戏目前有效,但我试图让主板正确调整大小的一切都没有用。相关代码如下。

class OthelloApplication:
    def __init__(self, game_state: othello.Othello):
        self._game_state = game_state
        self._game_state.new_game()
        self._game_board = game_state.show_board()
        self._root_window = tkinter.Tk()

        for row in range(self._game_state.show_rows()):
            for col in range(self._game_state.show_cols()):
                canvas = tkinter.Canvas(self._root_window, width = 100, height = 100, 
                    borderwidth = 0, highlightthickness = 1, 
                    background = _BACKGROUND_COLOR, highlightbackground = 'black')
                canvas.grid(row = row, column = col, padx = 0, pady = 0,
                    sticky = tkinter.N + tkinter.S + tkinter.E + tkinter.W)
                self._root_window.rowconfigure(row, weight = 1)
                self._root_window.columnconfigure(col, weight = 1)

        self._turn_window = tkinter.Canvas(self._root_window, width = 200,
            height = 100, highlightthickness = 0, background = 'white')

        self._root_window.bind('<Button-1>', self._on_canvas_clicked)
        self._root_window.bind('<Configure>', self.on_resize)



    def draw_game_pieces(self) -> None:
        for row in range(self._game_state.show_rows()):
            for col in range(self._game_state.show_cols()):
                if self._game_board[col][row] == ' ':
                    pass
                else:
                    canvas = tkinter.Canvas(master = self._root_window, width = 100, height = 100, 
                        borderwidth = 0, highlightthickness = 1,
                        background = _BACKGROUND_COLOR, highlightbackground = 'black')
                    canvas.grid(row = row, column = col, padx = 0, pady = 0,
                        sticky = tkinter.N + tkinter.S + tkinter.E + tkinter.W)
                    canvas.update()
                    canvas.create_oval(2, 2, canvas.winfo_width() - 2,
                        canvas.winfo_height() - 2, fill = self._which_color(col,
                        row), outline = self._which_color(col, row))
                    self._root_window.rowconfigure(row, weight = 1)
                    self._root_window.columnconfigure(col, weight = 1)



    def display_turn(self) -> None:
        if self._game_state.show_turn() == 'B':
            turn = 'black'
        else:
            turn = 'white'
        self._turn_window.grid(row = self._game_state.show_rows() // 2 - 1, 
            column = self._game_state.show_cols() + 1,
            sticky = tkinter.N + tkinter.S + tkinter.E + tkinter.W)
        self._turn_info = self._turn_window.create_text(10, 10, 
            font = 'Helvetica', anchor = 'nw')
        self._turn_window.itemconfig(self._turn_info, text = 'Turn = ' + turn)



    def on_resize(self, event: tkinter.Event) -> None:
        self.draw_game_pieces()



    def _which_color(self, col: int, row: int) -> str:
        if self._game_board[col][row] == 'B':
            return 'black'
        elif self._game_board[col][row] == 'W':
            return 'white'



    def _on_canvas_clicked(self, event: tkinter.Event) -> (int):
        print(event.widget.winfo_reqwidth(), event.widget.winfo_reqheight())
        print(event.widget.winfo_width(), event.widget.winfo_height())
        try:
            grid_info = event.widget.grid_info()
            move = (int(grid_info["row"]), int(grid_info["column"]))
            self._game_state.player_move(move[1], move[0])
            self.draw_game_pieces()
            self.display_turn()
        except AttributeError:
            pass
        except othello.InvalidMoveError:
            print('Error: that wasn\'t a valid move.')
        except othello.GameOverError:
            print('The game is over.')



    def start(self) -> None:
        self.draw_game_pieces()
        self.display_turn()
        self._root_window.mainloop()

draw_game_pieces()方法根据绘制它的画布大小在棋盘上的正确空间中绘制适当颜色的圆圈。

我的调整大小问题的解决方案是将on_resize()绑定到 init 方法中的'<Configure>'事件,但这会导致程序在递归循环中崩溃。我是tkinter和GUI的新手。为什么on_resize()方法绑定到'<Configure>'会导致程序崩溃?

对于凌乱的代码感到抱歉,我仍然在努力。

感谢。

1 个答案:

答案 0 :(得分:0)

由于递归导致的崩溃可能是因为您的on_resize方法创建了新的小部件。这是正在发生的事情:

  1. 小部件获得一个......
  2. 的事件
  3. 调用on_resize ......
  4. 创建一个嵌套循环......
  5. 创建一个画布,其中......
  6. 导致小部件中的配置更改
  7. 你在嵌套循环中调用update ...
  8. 导致事件被处理,...
  9. 调用on_resize,其中......
  10. 创建一个嵌套循环......
  11. 创建一个画布......
  12. 导致小部件中的配置更改
  13. 你在嵌套循环中调用update ...
  14. 导致事件被处理,...
  15. 调用on_resize,其中......
  16. ...
  17. 根据经验,除非你绝对肯定,否则你永远不应该打电话给更新,即便如此,你也需要停下来思考它。几乎永远不会被破坏的规则是在响应事件而调用的函数中调用update。每次调用update时,它几乎就像你再次调用mainloop一样 - 它创建了一个嵌套的事件循环,它试图处理所有挂起的事件。如果对待处理事件的处理导致生成新事件,您将最终得到您现在所处的递归情况。

    第一个修复是删除要更新的调用。第二个修复可能是在处理configure事件时创建新的小部件。你真的只需要创建一次所有这些小部件。重绘事件只需要重绘椭圆,因为画布对象会自动调整大小。

    我的建议是退后一步,慢慢来。重写你的程序只是绘制板而不用担心椭圆或游戏逻辑。以您想要的方式获得电路板的调整大小行为,然后担心重新绘制所有椭圆。