Search code examples
pythontkinterscrollbarmousewheelttk

Need to install a mousewheel in a ttk notebook with a scrollbar


I need the mousewheel to function on all tabs. Right now it only works on the friday tab. The controller for the mouse wheel is around line 18. I've got two possible approaches for controlling the mouse wheel, one of which is commented out.The second works if I replace "bind_all" with "bind" but then only the areas that are not covered by widgets are scrollable via the mouse wheel. Anyhow, the fact that the mouse wheel works on the last tab tells me it is possible for the mouse wheel to work on all the tabs, I just can't figure it out.

Edit: I prematurely posted a solution for this problem. I had to take that back as I found the areas of the canvas covered by widgets could not scrolled with the mouse wheel. When the whole canvas is covered, the mouse wheel was useless.

from tkinter import *
from tkinter import ttk
out_root = Tk()
out_root.title("KLUSTERBOX - Output")
# list the names of the tabs
tab = ["saturday", "sunday", "monday", "tuesday", "wednesday", "thursday", "friday"]
C = ["C0","C1","C2","C3","C4","C5","C6"]
#   create a notebook
tabControl = ttk.Notebook(out_root)  # Create Tab Control
tabControl.pack()
for i in range(7): #   loop to fill notebook pages.
    tabs = Frame(tabControl)  #     put frame in notebook
    tabs.pack()
    tabControl.add(tabs, text="{}".format(tab[i]))  # Add the tab
    C[i] = Canvas(tabs)    #   put canvas inside notebook frame
    S = Scrollbar(tabs, command=C[i].yview)    #   define and bind the scrollbar with the canvas
    C[i].config(yscrollcommand=S.set, scrollregion=(0, 0, 10000, 10000))  #   bind the canvas with the scrollbar
    #   Enable mousewheel option 1
    C[i].bind_all('<MouseWheel>', lambda event: C[i].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    #   Enable mousewheel option 2
    # if i==0: C[0].bind_all('<MouseWheel>', lambda event: C[0].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    # if i==1: C[1].bind_all('<MouseWheel>', lambda event: C[1].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    # if i==2: C[2].bind_all('<MouseWheel>', lambda event: C[2].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    # if i==3: C[3].bind_all('<MouseWheel>', lambda event: C[3].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    # if i==4: C[4].bind_all('<MouseWheel>', lambda event: C[4].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    # if i==5: C[5].bind_all('<MouseWheel>', lambda event: C[5].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    # if i==6: C[6].bind_all('<MouseWheel>', lambda event: C[6].yview_scroll(int(-1 * (event.delta / 120)), "units"))
    S.pack(side=RIGHT, fill=BOTH)
    C[i].pack(side=LEFT, fill=BOTH, expand=True)
    F = Frame(C[i]) #      put a frame in the canvas
    F.pack()
    C[i].create_window((0, 0), window=F, anchor=NW)    #   create window with frame
    ttk.Label(F, text="hello tab {}".format(tab[i])).grid(row=0) #    label for first line
    for ii in range(100):
        #   fill in text for body of document.
        Label(F, text="This label spans the entire lenght of the window. Line number: {}".format(ii)).grid(row=ii+1)
out_root.mainloop()

Solution

  • The solution is to have your mousewheel function figure out which canvas is the visible canvas, and then scroll it. One simple way is to create a binding that updates a global variable with the currently selected canvas (or the number of the canvas).

    Start by defining a function to call when a tab is selected:

    def tab_selected(i):
        global current_tab
        current_tab = i
    

    Next, call this function when the visibility of a canvas changes:

    for i in range(7):
        ...
        C[i].bind("<Map>", lambda event, i=i: tab_selected(i))
        ...
    

    That should give you a global variable named current_tab which will always be set to whichever tab is visible.

    Next, create a function to scroll the current tab:

    def scroll_tab(event):
        global current_tab
        C[current_tab].yview_scroll(int(-1 * (event.delta / 120)), "units")
    

    Finally, create a global binding outside of your loop to call this function whenever the mousewheel is used:

    out_root.bind_all("<MouseWheel>", scroll_tab)