繁体   English   中英

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

[英]Tkinter Window/Widget Proportional Resize

我目前正在为编程课程开发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>'导致程序崩溃?

抱歉,代码混乱,我仍在努力。

谢谢。

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

  1. 小部件获取一个事件,该事件...
  2. 调用on_resize其中...
  3. 创建一个嵌套循环,该循环...
  4. 创建一个画布,...
  5. 导致小部件中的配置更改
  6. 您在嵌套循环中调用更新...
  7. 导致事件被处理,这...
  8. 调用on_resize,这...
  9. 创建一个嵌套循环...
  10. 创建一个画布...
  11. 导致小部件中的配置更改
  12. 您在嵌套循环中调用更新...
  13. 导致事件被处理,这...
  14. 调用on_resize,从而...
  15. ...

根据经验,除非绝对确定需要这样做,否则永远不要调用更新,即使那样,您也必须停下来认真考虑一下。 几乎永远不应打破的规则是在响应事件而调用的函数中调用update。 每次调用update时,就好像您再次调用mainloop一样-它会创建一个嵌套的事件循环,该循环尝试处理所有未决的事件。 如果未决事件的处理导致新事件的产生,您将最终遇到递归情况。

第一个解决方法是删除要更新的呼叫。 第二个解决方法可能是在configure事件的处理中创建新的小部件。 您实际上只需要一次创建所有这些小部件。 重绘事件只需要重绘椭圆形,因为画布对象会自动调整其大小。

我的建议是退后一步,放慢脚步。 重写程序以仅画图板,而不必担心椭圆或游戏逻辑。 以所需的方式获得电路板的调整尺寸行为, 然后担心重新绘制所有椭圆形。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM