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.