Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.5k views
in Technique[技术] by (71.8m points)

python - Scrollable frame class in tkinter

I have implemented my own scrollable frame class in tkinter:

class scrolledFrame(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.canvas = tk.Canvas(self)
        self.canvas.pack(fill = "both", expand = True, side = "left")
        self.scroll = tk.Scrollbar(self, command = self.canvas.yview)
        self.scroll.pack(side = "right", fill = "y")
        self.canvas.config(yscrollcommand = self.scroll.set)
        self.content = tk.Frame(self.canvas)
        self.content.bind("<Configure>", self.resizeCanvas)
        self.contentWindow = self.canvas.create_window((0,0), window = self.content, anchor = "nw")
        self.content.bind("<Enter>", self.enableScrollCanvas)
        self.content.bind("<Leave>", self.disableScrollCanvas)
    def scrollCanvas(self, event):
        self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
    def enableScrollCanvas(self, event):
        self.canvas.bind_all("<MouseWheel>", self.scrollCanvas)
    def disableScrollCanvas(self, event):
        self.canvas.unbind_all("<MouseWheel>")
    def resizeCanvas(self, event):
        self.update_idletasks()
        self.canvas.config(scrollregion = self.canvas.bbox("all"))
        self.canvas.itemconfig(self.contentWindow, width = self.canvas.winfo_width())

root = tk.Tk()
exampleFrame = scrolledFrame(root)
exampleFrame.pack()
exampleLabel = tk.Label(exampleFrame.content, text = "I'm in the scrolled frame!")
exampleLabel.pack()
root.mainloop()

This works fine, but the problem is to add widgets to the scrolled frame, the parent has to be exampleFrame.content. I have looked at several other examples which all have the same limitation. Is it possible to configure the class so exampleFrame can be the parent of the widgets instead of exampleFrame.content? Thanks in advance.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you don't mind a little trickery, you can simulate what you want. It's a bit of a hack though.

The trick is that when you call tk.Frame.__init__, you need to be giving it the canvas as the parent, making self the content frame. Of course, to do that you have to create the canvas first, and to create the canvas you have to create the outer frame.

It looks something like this:

class scrolledFrame(tk.Frame):
    def __init__(self, parent):
        self.outer = tk.Frame(parent)
        self.canvas = tk.Canvas(self.outer)
        self.scroll = tk.Scrollbar(self.outer, command = self.canvas.yview)
        tk.Frame.__init__(self, self.canvas)
        self.contentWindow = self.canvas.create_window((0,0), window = self, anchor = "nw")

However, when you do the above and you try to call pack, place, or grid on the instance of scrolledFrame it's going to do the wrong thing since the instance points to the inner frame rather than the outer frame.

Here's the trickery: the solution to that is to redirect calls to pack, place, and grid to the outer frame.

class scrolledFrame(tk.Frame):
    def __init__(self, parent):
        ...
        self.pack = self.outer.pack
        self.place = self.outer.place
        self.grid = self.outer.grid

With that, you can use scrolledFrame like you want, as long as you use pack, place, or grid when adding it to the layout.

exampleFrame = scrolledFrame(root)
exampleFrame.pack(fill="both", expand=True)
for i in range(100):
    exampleLabel = tk.Label(exampleFrame, text = f"I'm in the scrolled frame! ({i})")
    exampleLabel.pack()

Here's a complete working example, though I've removed the mousewheel code for brevity.

import tkinter as tk

class scrolledFrame(tk.Frame):
    def __init__(self, parent):
        self.outer = tk.Frame(parent)
        self.canvas = tk.Canvas(self.outer)
        self.scroll = tk.Scrollbar(self.outer, command = self.canvas.yview)
        tk.Frame.__init__(self, self.canvas)
        self.contentWindow = self.canvas.create_window((0,0), window = self, anchor = "nw")

        self.canvas.pack(fill = "both", expand = True, side = "left")
        self.scroll.pack(side = "right", fill = "y")
        self.canvas.config(yscrollcommand = self.scroll.set)
        self.bind("<Configure>", self.resizeCanvas)

        self.pack = self.outer.pack
        self.place = self.outer.place
        self.grid = self.outer.grid

    def resizeCanvas(self, event):
        self.canvas.config(scrollregion = self.canvas.bbox("all"))
        self.canvas.itemconfig(self.contentWindow, width = self.canvas.winfo_width())

root = tk.Tk()
exampleFrame = scrolledFrame(root)
exampleFrame.pack(fill="both", expand=True)
for i in range(100):
    exampleLabel = tk.Label(exampleFrame, text = f"I'm in the scrolled frame! ({i})")
    exampleLabel.pack(fill="x")
root.mainloop()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...