Jet Blue
Jet Blue

Reputation: 5281

Tkinter - autoscroll to bottom

I have a canvas. Inside the canvas is a frame. And inside that frame is a label. As the text in the label grows, the canvas grows in size to accommodate. I've setup a vertical scrollbar, that allows me to see the text that overflows the window size. The scrollbar works as expected, but I want it to automatically scroll to the bottom (i.e. stay locked to the bottom) as the text grows. How can I do this?

Using canvas.yview_moveto( 1 ) does nothing...

Below is my code:

def tk_onFrameConfigure( self, event ):

    # resize canvas
    self.tkCanvas.configure( scrollregion = self.tkCanvas.bbox( 'all' ) )

def tk_onCanvasConfigure( self, event ):

    # resize frame
    self.tkCanvas.itemconfigure( self.tkCanvasFrame, width = event.width )

def setupTkinter( self ):

    self.tkRoot = tkinter.Tk()

    self.tkCanvas = tkinter.Canvas( self.tkRoot )
    self.tkCanvas.pack( side = tkinter.LEFT, expand = True, fill = 'both' )
    self.tkCanvas.configure(

        width = self.width,
        height = self.height,
        highlightthickness = 0,
        bg = self.bgColor
    )

    scrollbar = tkinter.Scrollbar( self.tkRoot )
    scrollbar.pack( side = tkinter.RIGHT, fill = 'y' )
    scrollbar.configure(

        orient = 'vertical',
        command = self.tkCanvas.yview
    )
    self.tkCanvas.configure( yscrollcommand = scrollbar.set )
    # self.tkCanvas.yview_moveto( 1 )  # does nothing

    frame = tkinter.Frame( self.tkCanvas )
    self.tkCanvasFrame = self.tkCanvas.create_window(

        ( 0, 0 ),
        window = frame,
        anchor = 'nw'
    )

    self.tkTextBox = tkinter.Label( frame )
    self.tkTextBox.pack( expand = True, fill = 'both' )
    self.tkTextBox[ 'text' ] = self.displayBuffer
    self.tkTextBox.config(

        fg = self.textColor,
        bg = self.bgColor,
        anchor = 'nw',
        justify = tkinter.LEFT,
        wraplength = self.width - 5
    )

    frame.bind( '<Configure>', self.tk_onFrameConfigure )
    self.tkCanvas.bind( '<Configure>', self.tk_onCanvasConfigure )

    self.tkRoot.mainloop()

Upvotes: 3

Views: 6862

Answers (2)

Jonathan L
Jonathan L

Reputation: 10688

In case of a ScrolledText control, you can use <tk_widget>.yview(tk.END) to do auto-scroll to the bottom. This applies to most other scrollable ui widgets too. Here is a sample script:

import tkinter as tk
import tkinter.scrolledtext as st

class DemoApp:
    def __init__(self, parent):
        self.parent = parent

        # status frame
        self.lf_status = tk.LabelFrame(parent, text='Status')
        self.lf_status.grid(row=0, column=0, padx=5, pady=5, columnspan=3, sticky=tk.NSEW)

        # scrolled text
        self.status_text_area = st.ScrolledText(self.lf_status, wrap=tk.WORD, width=62, height=5)
        self.status_text_area.grid(row=0, column=0, pady=5, padx=5, sticky='WNSE')

        self.status_text_area.insert(tk.END, 'Demo started\n')
        self.status_text_area.insert(tk.END, 'Click Add Text button to add more lines\n')

        btn_add_text = tk.Button(parent, text='Add Text', command=self.add_text)
        btn_add_text.grid(row=1, column=0, pady=5)

    def add_text(self):
        for i in range(1, 100):
            self.add_status_message(f'line {i}\n')

    def add_status_message(self, msg: str) -> None:
        self.status_text_area.insert(tk.END, msg)
        self.status_text_area.update()
        self.status_text_area.yview(tk.END)


if __name__ == '__main__':
    root = tk.Tk()
    root.title("Auto-Scrolling Demo")
    frame = tk.Frame(root, bd=3, relief=tk.SUNKEN)
    frame.grid(row=0, column=0, pady=5, padx=3, sticky='WENS')
    root.geometry("500x170")

    DemoApp(frame)
    root.mainloop()

Tested on Python version 3.11.4, tkinter.TkVersion 8.6, tkinter.TclVersion 8.6

Upvotes: 0

Jet Blue
Jet Blue

Reputation: 5281

I found the solution here. canvas.yview_moveto( 1 ) appears to do nothing because I only call it once during setup. For the 'auto' scrolling to work, I need to call it each time the text grows.

Upvotes: 4

Related Questions