ARTICLE AD BOX
Since scrolling via mouse wheel already works while hovering the scrollbars, we can listen for the respective events (<MouseWheel> or <Button-4> and <Button-5> for the vertical scrollbar, depending on your platform, and similarly, <Shift-MouseWheel> or <Shift-Button-4> and <Shift-Button-5> for the horizontal scrollbar) on the frame canvas and propagate them to the scrollbar.
for event_name in ( "<MouseWheel>", "<Button-4>", "<Button-5>", "<Shift-MouseWheel>", "<Shift-Button-4>", "<Shift-Button-5>", ): def _callback(event, event_name=event_name): widget = event.widget while widget: if widget == canvas: target = horizontal_scrollbar if "Shift" in event_name else vertical_scrollbar kwargs = {"delta": event.delta} if "MouseWheel" in event_name else {} return target.event_generate(event_name, **kwargs) widget = widget.master canvas.bind_all(event_name, _callback, add="+")Integrating this into the code from above to get a fully working example:
import tkinter as tk root = tk.Tk() root.geometry("400x300") outer_frame = tk.Frame(root) outer_frame.pack(fill="both", expand=True) vertical_scrollbar = tk.Scrollbar(outer_frame, orient="vertical") vertical_scrollbar.pack(side="right", fill="y") horizontal_scrollbar = tk.Scrollbar(outer_frame, orient="horizontal") horizontal_scrollbar.pack(side="bottom", fill="x") canvas = tk.Canvas(outer_frame) canvas.pack(side="left", fill="both", expand=True) canvas.configure(yscrollcommand=vertical_scrollbar.set) canvas.configure(xscrollcommand=horizontal_scrollbar.set) inner_frame = tk.Frame(canvas) inner_frame.bind("<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all"))) canvas.create_window((0, 0), window=inner_frame, anchor="nw") vertical_scrollbar.configure( command=lambda *args, **kwargs: None if canvas.bbox("all")[3] <= canvas.winfo_height() else canvas.yview(*args, **kwargs), ) horizontal_scrollbar.configure( command=lambda *args, **kwargs: None if canvas.bbox("all")[2] <= canvas.winfo_width() else canvas.xview(*args, **kwargs), ) horizontal_scrollbar.update() vertical_scrollbar.pack_configure(pady=(0, horizontal_scrollbar.winfo_height())) # count_x, count_y = 1, 1 # count_x, count_y = 100, 1 # count_x, count_y = 1, 100 count_x, count_y = 100, 100 for i in range(count_y): text = " ".join(f"Label{i + 1}" for _ in range(count_x)) tk.Label(inner_frame, text=text).pack(side="top", anchor="nw") for event_name in ( "<MouseWheel>", "<Button-4>", "<Button-5>", "<Shift-MouseWheel>", "<Shift-Button-4>", "<Shift-Button-5>", ): def _callback(event, event_name=event_name): widget = event.widget while widget: if widget == canvas: target = horizontal_scrollbar if "Shift" in event_name else vertical_scrollbar kwargs = {"delta": event.delta} if "MouseWheel" in event_name else {} return target.event_generate(event_name, **kwargs) widget = widget.master canvas.bind_all(event_name, _callback, add="+") root.mainloop()