Above Average Joe
Above Average Joe

Reputation: 53

Tkinter - How to stop scrolling above canvas window

I have a scrollable frame with the mousewheel bound to scrolling, but when the frame is smaller than the canvas I am able to scroll above it - how can I keep the frame at the top of the canvas?

Here is a minimal version of the code:

import tkinter as tk

class Gui:
    def __init__(self, master):
        self.root = master
        self.canvas = tk.Canvas(self.root)
        self.frame = tk.Frame(self.canvas,bg='white')
        self.scroll = tk.Scrollbar(self.canvas,orient='vertical', command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)

        self.scroll.pack(side='right', fill='y')
        self.canvas.place(relheight=1, relwidth=0.85, relx=0.15)
        self.canvas_frame = self.canvas.create_window((0,0), window=self.frame, anchor='nw')

        self.frame.bind('<Configure>', self.onFrameConfigure)
        self.canvas.bind_all('<MouseWheel>', lambda event: self.canvas.yview_scroll(int(-1*(event.delta/120)), 'units'))
        self.addToFrame()
    def onFrameConfigure(self, event):
        self.canvas.configure(scrollregion=self.canvas.bbox('all'))

    def addToFrame(self):
        self.label = tk.Label(self.frame,text='Label')
        self.label.pack()
        
if __name__ == "__main__":
    root = tk.Tk()
    gui = Gui(root)
    root.mainloop()

Upvotes: 4

Views: 1258

Answers (1)

j_4321
j_4321

Reputation: 16169

You can define your own yview() method that does the scrolling only when not the whole content of the canvas is visible. For that I used self.canvas.yview(), which returns (0.0, 1.0) if all the canvas content is visible. Then I used the custom yview() method both as the scrollbar command and in the mouse wheel binding.

import tkinter as tk

class Gui:
    def __init__(self, master):
        self.root = master
        self.canvas = tk.Canvas(self.root)
        self.frame = tk.Frame(self.canvas,bg='white')
        self.scroll = tk.Scrollbar(self.canvas,orient='vertical', command=self.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)

        self.scroll.pack(side='right', fill='y')
        self.canvas.place(relheight=1, relwidth=0.85, relx=0.15)
        self.canvas_frame = self.canvas.create_window((0,0), window=self.frame, anchor='nw')

        self.frame.bind('<Configure>', self.onFrameConfigure)
        self.canvas.bind_all('<MouseWheel>', lambda event: self.yview('scroll', int(-1*(event.delta/120)), 'units'))
        self.addToFrame()

    def onFrameConfigure(self, event):
        self.canvas.configure(scrollregion=self.canvas.bbox('all'))

    def addToFrame(self):
        self.label = tk.Label(self.frame,text='Label')
        self.label.pack()

    def yview(self, *args):
        if self.canvas.yview() == (0.0, 1.0):
            return
        self.canvas.yview(*args)

if __name__ == "__main__":
    root = tk.Tk()
    gui = Gui(root)
    root.mainloop()

Upvotes: 7

Related Questions