Tkinter滚动条仅适用于create_window

时间:2019-12-10 06:31:06

标签: python tkinter

我有一个窗格,在右窗格中,我有一个要滚动的画布框架。仅当我调用canvas create_window函数时,它才能滚动。当我调用create_window时,一切正常,除了内容框架没有扩展并且将网格设置为sticky=nsew。在示例代码中,有一个名为x的变量,如果x设置为1,则框架会展开,并且滚动条会正确显示,但它们不起作用;如果将其设置为0,则滚动条会起作用,但框架不会扩展。如果将x设置为1或0,我需要滚动条才能工作。您需要调整窗口大小才能看到此问题,请注意,分隔器小部件会展开以填充框架,但是当x设置为时,滚动条不会滚动任何内容1。

我就在那儿,我有99%的工作量,只有一点点小故障。当窗口高度短于内容,窗口宽度宽于内容时,分隔窗口小部件不会展开以填充内容框架。我已经更新了代码并添加了背景色以突出显示此问题。

import tkinter as tk
import tkinter.ttk as ttk


lorem_ipsum = 'Lorem ipsum dolor sit amet, luctus non. Litora viverra ligula'


class Scrollbar(ttk.Scrollbar):
    def __init__(self, parent, canvas, **kwargs):
        ttk.Scrollbar.__init__(self, parent, **kwargs)

        command = canvas.xview if kwargs.get('orient', tk.VERTICAL) == tk.HORIZONTAL else canvas.yview
        self.configure(command=command)

    def set(self, low, high):
        if float(low) > 0 or float(high) < 1:
            self.grid()
        else:
            self.grid_remove()

        ttk.Scrollbar.set(self, low, high)


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.title('Paned Window Demo')
        self.geometry('420x200')

        style = ttk.Style()
        style.theme_use('clam')
        style.configure('TPanedwindow', background='black')

        pw = ttk.PanedWindow(self, orient=tk.HORIZONTAL)
        left_frame = ttk.Frame(pw)
        right_frame = ttk.Frame(pw)

        ttk.Label(left_frame, text='Left Pane').grid()
        left_frame.rowconfigure(0, weight=1)
        left_frame.columnconfigure(0, weight=1)
        left_frame.grid(sticky=tk.NSEW)

        right_frame.rowconfigure(0, weight=1)
        right_frame.columnconfigure(0, weight=1)
        right_frame.grid(sticky=tk.NSEW)

        pw.add(left_frame)
        pw.add(right_frame)
        pw.grid(sticky=tk.NSEW)

        canvas = tk.Canvas(right_frame, bg=style.lookup('TFrame', 'background'))
        canvas.frame = ttk.Frame(canvas)
        canvas.rowconfigure(0, weight=1)
        canvas.columnconfigure(0, weight=1)
        canvas.grid(sticky=tk.NSEW)
        canvas.frame.rowconfigure(990, weight=1)
        canvas.frame.columnconfigure(0, weight=1)
        canvas.frame.grid(sticky=tk.NSEW)

        content = tk.Frame(canvas.frame, bg='blue')
        content.rowconfigure(0, weight=1)
        content.columnconfigure(0, weight=1)
        content.grid(sticky=tk.NSEW)

        xscroll = Scrollbar(right_frame, canvas, orient=tk.HORIZONTAL)
        yscroll = Scrollbar(right_frame, canvas, orient=tk.VERTICAL)
        xscroll.grid(row=990, column=0, sticky=tk.EW)
        yscroll.grid(row=0, column=990, sticky=tk.NS)

        for idx in range(1, 11):
            tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)

        ttk.Separator(content, orient=tk.HORIZONTAL).grid(pady=10, sticky=tk.EW)

        for idx in range(11, 21):
            tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)

        self.window = canvas.create_window((0, 0), window=canvas.frame, anchor=tk.NW)

        self.update_idletasks()
        pw.sashpos(0, newpos=100)

        def update_canvas(event):
            content.update_idletasks()
            _, _, width, height = content.bbox(tk.ALL)

            if event.width < width or event.height < height:
                if not self.window:
                    self.window = canvas.create_window((0, 0), window=canvas.frame, anchor=tk.NW)
            else:
                self.window = None
                canvas.frame.grid(sticky=tk.NSEW)

        canvas.bind('<Configure>', update_canvas)

        canvas.configure(scrollregion=content.bbox(tk.ALL))
        canvas.configure(xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)


def main():
    app = App()
    app.mainloop()


if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:1)

滚动条仅与canvas.create_window()一起使用是完全正常的,因为这是在画布内显示窗口小部件的适当方法,以便画布知道窗口小部件的大小。否则,您只是将画布用作框架。但是,canvas.create_window()方法没有sticky选项,因此当画布更改大小时,您必须手动更改窗口小部件的宽度。

因此,在您的update_canvas回调中,如果画布的宽度大于内容的所需宽度,则每次画布更改宽度时都需要更改窗口的宽度:

    def update_canvas(event):
        # if the new canvas's width (event.width) is larger than the content's 
        # minimum width (content.winfo_reqwidth()) then make canvas.frame the 
        # same width as the canvas
        if event.width > content.winfo_reqwidth():
            canvas.itemconfigure(self.window, width=event.width)

此外,您的代码中还有几行无用的行,尤其是您不必使用canvas.frame来显示canvas.create_window()的网格,请参见下文:

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.title('Paned Window Demo')
        self.geometry('420x200')

        style = ttk.Style()
        style.theme_use('clam')
        style.configure('TPanedwindow', background='black')

        pw = ttk.PanedWindow(self, orient=tk.HORIZONTAL)
        left_frame = ttk.Frame(pw)
        right_frame = ttk.Frame(pw)

        ttk.Label(left_frame, text='Left Pane').grid()
        left_frame.rowconfigure(0, weight=1)
        left_frame.columnconfigure(0, weight=1)
        left_frame.grid(sticky=tk.NSEW)

        right_frame.rowconfigure(0, weight=1)
        right_frame.columnconfigure(0, weight=1)
        right_frame.grid(sticky=tk.NSEW)

        pw.add(left_frame)
        pw.add(right_frame)
        pw.grid(sticky=tk.NSEW)

        canvas = tk.Canvas(right_frame, bg=style.lookup('TFrame', 'background'))
        canvas.frame = ttk.Frame(canvas)
#        canvas.rowconfigure(0, weight=1) -> useless since no widget will be gridded in the canvas
#        canvas.columnconfigure(0, weight=1) -> useless since no widget will be gridded in the canvas
        canvas.grid(sticky=tk.NSEW)
        canvas.frame.rowconfigure(990, weight=1)
        canvas.frame.columnconfigure(0, weight=1)
#        canvas.frame.grid(sticky=tk.NSEW) -> no need to grid canvas.frame since it is displayed it with canvas.create_window()
        content = tk.Frame(canvas.frame, bg='blue')
        content.rowconfigure(0, weight=1)
        content.columnconfigure(0, weight=1)
        content.grid(sticky=tk.NSEW)

        xscroll = Scrollbar(right_frame, canvas, orient=tk.HORIZONTAL)
        yscroll = Scrollbar(right_frame, canvas, orient=tk.VERTICAL)
        xscroll.grid(row=990, column=0, sticky=tk.EW)
        yscroll.grid(row=0, column=990, sticky=tk.NS)

        for idx in range(1, 11):
            tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)

        ttk.Separator(content, orient=tk.HORIZONTAL).grid(pady=10, sticky=tk.EW)

        for idx in range(11, 21):
            tk.Label(content, bg='#aaaaaa', fg='#000000', text=f'{idx} {lorem_ipsum}').grid(sticky=tk.NW)

        self.window = canvas.create_window((0, 0), window=canvas.frame, anchor=tk.NW)

        self.update_idletasks()
        pw.sashpos(0, newpos=100)

        def update_canvas(event):
            # if the new canvas's width (event.width) is larger than the content's 
            # minimum width (content.winfo_reqwidth()) then make canvas.frame the 
            # same width as the canvas
            if event.width > content.winfo_reqwidth():
                canvas.itemconfigure(self.window, width=event.width)


        canvas.bind('<Configure>', update_canvas)

        canvas.configure(scrollregion=content.bbox(tk.ALL))
        canvas.configure(xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)


def main():
    app = App()
    app.mainloop()


if __name__ == '__main__':
    main()