Tkinter 中的双滚动条 Python

Double Scrollbars in Tkinter Python

我无法使用 Python 在 Tkinter 中使用 x 和 y 滚动条,尽管我遵循了多个示例:

  1. Adding a scrollbar to a group of widgets in tkinter
  2. How to make a proper double scrollbar frame in tkinter
  3. Vertical and Horizontal Scrollbars on Tkinter Widget
  4. Scrolling a Canvas widget horizontally and vertically

滚动条出现,但当 window 小于框架时不激活。我怎样才能让它工作(见下图)?

下面是产生我的问题的最少代码 (Python 3.7)

import tkinter as tk
from tkinter import ttk

big_font = ("Arial", 50)

class DoubleScrollbarFrame(ttk.Frame):

    def __init__(self,
                 parent,
                 *args,
                 **kwargs):
        self.parent = parent

        super().__init__(self.parent,
                         *args,
                         **kwargs)

        #Set widgets to fill main window such that they are
        #all the same size
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self.create_widgets()
        self.position_widgets()

    def create_widgets(self):
        self.canvas = tk.Canvas(self)
        self.frame = ttk.Frame(self.canvas)
        self.scroll_x = ttk.Scrollbar(self,
                                      orient = tk.HORIZONTAL,
                                      command = self.canvas.xview)
        self.scroll_y = ttk.Scrollbar(self,
                                      orient = tk.VERTICAL,
                                      command = self.canvas.yview)
        self.canvas.config(xscrollcommand = self.scroll_x.set,
                           yscrollcommand = self.scroll_y.set)
        self.canvas.create_window((0,0),
                                  window = self.frame,
                                  anchor = 'nw')
        self.frame.bind('<Configure>',
                        self.set_scrollregion)
        self.sizegrip = ttk.Sizegrip(self)

        #Test
        self.test1 = tk.Label(self.frame,
                              text = 'Test 1',
                              font = big_font)
        self.test2 = tk.Label(self.frame,
                              text = 'Test 2',
                              font = big_font)
        self.test3 = tk.Label(self.frame,
                              text = 'Test 3',
                              font = big_font)

    def position_widgets(self,
                         **kwargs):
        self.test1.grid(row = 0,
                        column = 0,
                        sticky = 'w')
        self.test2.grid(row = 1,
                        column = 0,
                        sticky = 'w')
        self.test3.grid(row = 2,
                        column = 0,
                        sticky = 'w')
        
        self.scroll_x.grid(row = 1,
                           column = 0,
                           sticky = 'ew')

        self.scroll_y.grid(row = 0,
                           column = 1,
                           sticky = 'ns')

        self.canvas.grid(row = 0,
                         column = 0,
                         sticky = 'nsew')
        
        self.frame.grid(row = 0,
                        column = 0,
                        sticky = 'nsew')

        self.sizegrip.grid(row = 1,
                           column = 1,
                           sticky = 'se')

    def set_scrollregion(self,
                         event):
        print('Frame Dimensions: {} x {}'.format(self.frame.winfo_width(),
                                                 self.frame.winfo_height()))
        print('Canvas Dimensions: {} x {}'.format(self.canvas.winfo_width(),
                                                  self.canvas.winfo_height()))
        self.canvas.configure(scrollregion = self.canvas.bbox('all'))

class MainApp(tk.Tk):

    def __init__(self,
                 *args,
                 **kwargs):

        super().__init__(*args,
                         **kwargs)

        #Set widgets to fill main window such that they are
        #all the same size
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self.create_widgets()
        self.position_widgets()

    def create_widgets(self):
        self.frame = DoubleScrollbarFrame(self)

    def position_widgets(self):
        self.frame.grid(row = 0,
                        column = 0,
                        sticky = 'nsew')

    def exit(self):
        self.destroy()

if __name__ == '__main__':

    #Create GUI
    root = MainApp()

    #Run program
    root.mainloop()

问题出在DoubleScrollbarFrame.position_widgets:

这几行代码里面
self.frame.grid(row = 0,
                column = 0,
                sticky = 'nsew')

这会从 canvas 中移除小部件的控制权,并将控制权交给 grid。它不再是 canvas 对象,因此 self.canvas.bbox("all") 返回 (0, 0, 1, 1)。如果scrollregion设置不正确,滚动条不知道滚动多少

解决方法很简单:不要在 self.frame 上调用 grid