簡體   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