Search code examples
pythontkintertkinter-scrolledtext

Text widget scroll is not working within Frame


This is very strange and I wonder if there's any fix besides "add extra space at the bottom" Here's some sample code to explain what I'm doing:

from tkinter import *

root = Tk()
root.geometry("100x300")

scrollbar = Scrollbar(root)
scrollbar.pack( side = RIGHT, fill=Y )

box = Text(root, wrap="char", cursor="arrow", yscrollcommand=scrollbar.set, font=("Helvetica", 30))
box.pack(fill="both", expand=True)
scrollbar.config( command = box.yview )
scrollbar.lift()

for i in range(5):
    box.insert(INSERT, f"Button {i+1} ")

root.mainloop()

This works amazing and this is what the max scroll looks like:

Example of what should happen

However, once I put everything in a Frame there's a problem Code:

from tkinter import *

root = Tk()
root.geometry("100x400")

frame = Frame(root)
frame.place(x=0, y=100, relwidth=1, relheight=1)

scrollbar = Scrollbar(frame)
scrollbar.pack( side = RIGHT, fill=Y )

box = Text(frame, wrap="char", cursor="arrow", yscrollcommand=scrollbar.set, font=("Helvetica", 30))
box.pack(fill="both", expand=True)
scrollbar.config( command = box.yview )
scrollbar.lift()

for i in range(5):
    box.insert(INSERT, f"Button {i+1} ")

root.mainloop()

And the max scroll looks like this:

The problem

As you can see, at least 2 lines in the text widget were cutoff, and I have no idea why. Can someone help out?


Solution

  • This is because of the incorrect geometry settings for the root window and the frame widget.

    Example -1:

    from tkinter import *
    
    root = Tk()
    
    # Set a more appropriate geometry
    root.geometry("600x400")  
    
    # Use the entire root window
    frame = Frame(root)
    frame.place(x=0, y=0, relwidth=1, relheight=1)  
    
    scrollbar = Scrollbar(frame)
    scrollbar.pack(side=RIGHT, fill=Y)
    
    box = Text(frame, wrap="char", cursor="arrow", yscrollcommand=scrollbar.set, font=("Helvetica", 30))
    box.pack(fill="both", expand=True)
    scrollbar.config(command=box.yview)
    
    for i in range(5):
        box.insert(INSERT, f"Button {i+1} ")
    
    root.mainloop()
    

    You may think what to do if you want to display another widget above the frame. The solution is to use pack or grid to properly allocate space. The following example with grid but still make your Text widget scrollable.

    Example-2

    from tkinter import *
    
    root = Tk()
    root.geometry("600x400")
    
    # Adding another widget at the top
    top_label = Label(root, text="This is the top label", font=("Helvetica", 16))
    top_label.grid(row=0, column=0, columnspan=2, sticky="ew")
    
    frame = Frame(root)
    frame.grid(row=1, column=0, columnspan=2, sticky="nsew")  # Adjust to fill remaining space
    
    scrollbar = Scrollbar(frame)
    scrollbar.pack(side=RIGHT, fill=Y)
    
    box = Text(frame, wrap="char", cursor="arrow", yscrollcommand=scrollbar.set, font=("Helvetica", 30))
    box.pack(side=LEFT, fill=BOTH, expand=True)
    scrollbar.config(command=box.yview)
    
    # Configure the grid to expand properly
    root.grid_rowconfigure(1, weight=1)
    root.grid_columnconfigure(0, weight=1)
    
    for i in range(50):  # Add more lines to test scrolling
        box.insert(INSERT, f"Button {i+1} ")
    
    root.mainloop()
    

    I've given the above examples so that one can compare and understand the concept.

    Edit for more clarity:

    Whether you use grid or pack, Have an approximate idea of how much space each widget will occupy and what is the height and breadth of the root window. Let us assume the first 4 widgets occupy 380px vertically and the height of your root window is 400. So only 20px is remaining. If the height of your 5th widget is more than 20px, The excess height will be clipped even though if you had given scroll bar.