I'm creating a custom text widget using tk.Canvas in Tkinter to display multiple lines of text, with support for mousewheel and scrollbar scrolling. What works: Mouse wheel scrolling scrolls through the full content correctly Clicking the scrollbar arrows or scrollbar trough jumps correctly to top/bottom What’s broken: When I drag the scrollbar thumb, it stops before reaching the bottom. The last few lines of content are unreachable until I manually click the scrollbar’s bottom area. Code: import tkinter as tk from tkinter import font class CustomText(tk.Canvas): def __init__(self, master, **kwargs): super().__init__(master, **kwargs) # Example content self.text_lines = [f"Line {i}" for i in range(100)] self.line_height = 20 self.font = font.Font(family="Courier", size=12) self.scroll_offset = 0 self.max_visible_lines = 1 self.yscrollcommand = None self.bind("<Configure>", self.on_resize) self.bind("<MouseWheel>", self.on_mousewheel) self.bind("<Button-4>", self.on_mousewheel) # Linux scroll up self.bind("<Button-5>", self.on_mousewheel) # Linux scroll down def attach_scrollbar(self, scrollbar): self.yscrollcommand = scrollbar.set scrollbar.config(command=self.yview) self.update_scrollbars() def on_resize(self, event): self.width = event.width self.height = event.height self.max_visible_lines = self.height // self.line_height self.draw_text() def on_mousewheel(self, event): if hasattr(event, 'delta'): direction = -1 if event.delta > 0 else 1 elif event.num == 4: direction = -1 else: direction = 1 scroll_amount = 1 self.scroll_offset = max(0, min( self.scroll_offset + direction * scroll_amount, max(0, len(self.text_lines) - self.max_visible_lines) )) self.draw_text() def yview(self, *args): if args[0] == 'moveto': fraction = float(args[1]) max_offset = max(0, len(self.text_lines) - self.max_visible_lines) self.scroll_offset = int(fraction * max_offset) elif args[0] == 'scroll': units = int(args[1]) self.scroll_offset = max(0, min(self.scroll_offset + units, len(self.text_lines) - self.max_visible_lines)) self.draw_text() def update_scrollbars(self): # Vertical total_lines = len(self.text_lines) if total_lines == 0: self.yscrollcommand(0, 1) else: top = self.scroll_offset / total_lines bottom = min(1.0, (self.scroll_offset + self.max_visible_lines) / total_lines) if self.yscrollcommand: self.yscrollcommand(top, bottom) def draw_text(self): self.delete("all") self.scroll_offset = max(0, min(self.scroll_offset, len(self.text_lines) - self.max_visible_lines)) start = self.scroll_offset end = min(len(self.text_lines), start + self.max_visible_lines) for i in range(start, end): y = (i - start) * self.line_height self.create_text(5, y, anchor='nw', text=self.text_lines, font=self.font) self.update_scrollbars() # Create root window root = tk.Tk() root.geometry("400x300") # Frame to hold canvas and scrollbar frame = tk.Frame(root) frame.pack(fill="both", expand=True) # Create scrollbar scrollbar = tk.Scrollbar(frame) scrollbar.pack(side="right", fill="y") # Create CustomText widget text = CustomText(frame, bg="white") text.pack(side="left", fill="both", expand=True) # Attach scrollbar text.attach_scrollbar(scrollbar) root.mainloop() Continue reading...