Created
June 13, 2018 10:40
TKinter scrollable frame
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from tkinter import * # from x import * is bad practice | |
from tkinter import ttk | |
# http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame | |
class VerticalScrolledFrame(Frame): | |
"""A pure Tkinter scrollable frame that actually works! | |
* Use the 'interior' attribute to place widgets inside the scrollable frame | |
* Construct and pack/place/grid normally | |
* This frame only allows vertical scrolling | |
""" | |
def __init__(self, parent, *args, **kw): | |
Frame.__init__(self, parent, *args, **kw) | |
# create a canvas object and a vertical scrollbar for scrolling it | |
vscrollbar = Scrollbar(self, orient=VERTICAL) | |
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) | |
canvas = Canvas(self, bd=0, highlightthickness=0, | |
yscrollcommand=vscrollbar.set) | |
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) | |
vscrollbar.config(command=canvas.yview) | |
# reset the view | |
canvas.xview_moveto(0) | |
canvas.yview_moveto(0) | |
# create a frame inside the canvas which will be scrolled with it | |
self.interior = interior = Frame(canvas) | |
interior_id = canvas.create_window(0, 0, window=interior, | |
anchor=NW) | |
def _on_mousewheel(event): | |
canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") | |
self.interior.bind_all("<MouseWheel>", _on_mousewheel) | |
# track changes to the canvas and frame width and sync them, | |
# also updating the scrollbar | |
def _configure_interior(event): | |
# update the scrollbars to match the size of the inner frame | |
size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) | |
canvas.config(scrollregion="0 0 %s %s" % size) | |
if interior.winfo_reqwidth() != canvas.winfo_width(): | |
# update the canvas's width to fit the inner frame | |
canvas.config(width=interior.winfo_reqwidth()) | |
interior.bind('<Configure>', _configure_interior) | |
def _configure_canvas(event): | |
if interior.winfo_reqwidth() != canvas.winfo_width(): | |
# update the inner frame's width to fill the canvas | |
canvas.itemconfigure(interior_id, width=canvas.winfo_width()) | |
canvas.bind('<Configure>', _configure_canvas) | |
if __name__ == "__main__": | |
class SampleApp(Tk): | |
def __init__(self, *args, **kwargs): | |
root = Tk.__init__(self, *args, **kwargs) | |
self.frame = VerticalScrolledFrame(root) | |
self.frame.pack() | |
self.label = Label(text="Shrink the window to activate the scrollbar.") | |
self.label.pack() | |
buttons = [] | |
for i in range(100): | |
buttons.append(Button(self.frame.interior, text="Button " + str(i))) | |
buttons[-1].pack() | |
app = SampleApp() | |
app.mainloop() |
Here's some code that lets the box scroll only when the mouse hovers over it.
def _on_leave(event): self.focused = False interior.bind('<Leave>', _on_leave) def _on_enter(event): self.focused = True interior.bind("<Enter>", _on_enter) def _on_mousewheel(event): if(self.focused): canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") self.interior.bind_all("<MouseWheel>", _on_mousewheel)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very nice! Thanks, this saved me a shitton of time lol