Python tkinter: how to properly propagate a bind to a function inside childs of a widget

44 views Asked by At

I wrote the following code to create a ScrollableFrame, it works almost perfectly, but my problem is that the bind to the mouse scroll event does not propagate to all widget inserrted inside the canvas. The mouse scroll works only on the Scrollbar and the parts of the canvas not covered by the internal widgets.

''' lib scrollable frame '''
from tkinter import Canvas
from tkinter.ttk import Frame, Scrollbar

# ----------------------------------------------------------------------------------
# This Class creates a Scrollable Frame to be used as a normal Frame
# It is only important to use the command scrollFrame.scrollable_frame for the
# Widgets contained inside it
# ----------------------------------------------------------------------------------
class ScrollableFrame(Frame):
    ''' class scrollable frame '''
    def __init__(self, container, *args, width=100, height=100, **kwargs):
        super().__init__(container, *args, **kwargs)
        # Create Frame, Canvas, Scrollbar
        self.canvas = Canvas(self, width=width, height=height)
        self.scrollbar = Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.scrollable_frame = Frame(self.canvas)

        # Canvas Configure event
        self.scrollable_frame.columnconfigure(0, weight=1)
        self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
        self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw", tag="frame")    # "create_window" method is a geometry manager like grid or pack, specific for canvas
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.bind("<Configure>", lambda e: self.canvas.itemconfig("frame", width=e.width))

        # Grid widgets
        self.canvas.grid(row=0, column=0, sticky='nsew')
        self.scrollbar.grid(row=0, column=1, sticky='nsew')

        # Connect the scroll to all sub widget
        self.attach_scroll_to_widget_and_children(self)

    def attach_scroll_to_widget_and_children(self, widget):
        ''' disable a widget and all it's children '''
        for child in widget.winfo_children():
            child.bind("<MouseWheel>", self.on_mouse_wheel)
            self.attach_scroll_to_widget_and_children(child)

    def on_mouse_wheel(self, event):
        ''' Scroll the canvas '''
        self.canvas.yview("scroll", int(-event.delta/100), "units")

if __name__ == '__main__':
    from tkinter import Tk
    from tkinter.ttk import Label

    root = Tk()
    # root.geometry('600x600+100+100')
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)

    scroll_frame = ScrollableFrame(root, width=200, height=300)
    scroll_frame.grid(row=0, column=0, sticky='nsew')
    scroll_frame.columnconfigure(0, weight=1)
    scroll_frame.rowconfigure(0, weight=1)

    labelList = []
    for i in range(100):
        label = (Label(scroll_frame.scrollable_frame, text='label'+str(i), background='green'))
        label.grid(row=i, column=0, padx=10, pady=10, sticky='nsew')
        labelList.append(label)

    root.mainloop()

I tryied to pass to the attach_scroll_to_widget_and_children(self, widget) self, canvas and scrollable_frame, but it didn't work.

0

There are 0 answers