Skip to content
Advertisement

How to fix most recent Label gets cut off Python Tkinter when using scrollbar

So I was making a chat gui for my tcp chat and I wanted to use a scrollbar so you would be able to see the messages no matter how long the messages got. So I was testing somethings and got it to work for the most part. The only problem is that the most recent message after filling the viewable frame get cut off. Then every most recent message is cut off no matter if you scroll all the way down.

This is after I typed 1 more msg than what fits in the frame (notice how the scrollbar is still disabled)

Here I stretched the window down so you could see that the label is there it is just that the scrollbar did not update for just that label

from tkinter import *
from tkinter.ttk import Combobox
from tkinter import messagebox
from time import sleep

w = Tk()
w.title('Title')
w.geometry('500x400')
w.resizable(width=False, height=False)

def send():
    if send_entry.get() != '':
        messages.append(send_entry.get())
        i = 0
        for msg in messages:
            Label(chat_frame, text=f'User: {msg}').grid(column=0, row=i, padx=10, sticky=W)
            i += 1
        canvas.configure(scrollregion=canvas.bbox("all"))
        send_entry.delete(0, 'end')

def sendEvent(event):
    if send_entry.get() != '':
        messages.append(send_entry.get())
        i = 0
        for msg in messages:
            Label(chat_frame, text=f'User: {msg}').grid(column=0, row=i, padx=10, sticky=W)
            i += 1
        canvas.configure(scrollregion=canvas.bbox("all"))
        send_entry.delete(0, 'end')

messages = []

main_frame = Frame(w)
main_frame.pack(fill=BOTH, expand=1, pady=10)

canvas = Canvas(main_frame)
canvas.pack(side=LEFT, fill=BOTH, expand=1, pady=10)

scrollbar = Scrollbar(main_frame, orient=VERTICAL, command=canvas.yview)
scrollbar.pack(side=RIGHT, fill=Y)

canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

chat_frame = Frame(canvas)
canvas.create_window((0, 0), window=chat_frame, anchor=NW)

bot_frame = Frame(w).pack(side=BOTTOM)

send_entry = Entry(bot_frame, width=55)
send_entry.pack(side=LEFT, padx=30, pady=10)
send_button = Button(bot_frame, text='Send', command=send).pack(side=LEFT)

w.bind('<Return>', sendEvent)

w.mainloop()

Advertisement

Answer

First you should bind <Configure> on chat_frame instead of canvas. Second you need to update the canvas before updating scrollregion option.

Also you can combine send() and sendEvent() as they do the same thing. And don’t create new set of labels every time a new message is added, just create a new label for the newly added message.

from tkinter import *
from tkinter.ttk import Combobox
#from tkinter import messagebox
#from time import sleep

w = Tk()
w.title('Title')
w.geometry('500x400')
w.resizable(width=False, height=False)

def send(event=None):
    msg = send_entry.get().strip()
    if msg != '':
        messages.append(msg)
        Label(chat_frame, text=f"User: {msg}").grid(padx=10, sticky=W)
        send_entry.delete(0, 'end')
        # make the last message visible
        canvas.update()
        canvas.yview_moveto(1)

messages = []

main_frame = Frame(w)
main_frame.pack(fill=BOTH, expand=1, pady=10)

canvas = Canvas(main_frame)
canvas.pack(side=LEFT, fill=BOTH, expand=1, pady=10)

scrollbar = Scrollbar(main_frame, orient=VERTICAL, command=canvas.yview)
scrollbar.pack(side=RIGHT, fill=Y)

canvas.configure(yscrollcommand=scrollbar.set)
#canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

chat_frame = Frame(canvas)
canvas.create_window((0, 0), window=chat_frame, anchor=NW)
chat_frame.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

bot_frame = Frame(w)
bot_frame.pack(side=BOTTOM)

send_entry = Entry(bot_frame, width=55)
send_entry.pack(side=LEFT, padx=30, pady=10)

Button(bot_frame, text='Send', command=send).pack(side=LEFT)

w.bind('<Return>', send)

w.mainloop()

However better use ScrolledText widget instead for the chat messages box.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement