0

I have one text widget where input can be entered by the user and I'm using a second text widget to display corresponding line numbers. I managed to scroll both simultaneously with one scrollbar using this solution https://stackoverflow.com/a/58190229/. But this only works when I either drag the scrollbar with the mouse or using the mousewheel while hovering on the scrollbar.

But when the mouse is hovering over the entered text and the the mousewheel is used only this text widget is scrolled while the other doesn't move. I tried to bind the mousewheel to the same function scrolling both text widgets but then I only receive the following error when hovering over the text and using the mousewheel: _tkinter.TclError: bad text index "<MouseWheel event send_event=True state=Mod1 delta=120 x=324 y=163>"

Here is the code:

import tkinter as tk

def multiple_yview(*args):
    text.yview(*args)
    line_numbers.yview(*args)

root = tk.Tk()
text_frame = tk.Frame(root)
scrollbar = tk.Scrollbar(text_frame)
line_numbers = tk.Text(text_frame, width=9, state="disabled", bd=0, bg="#F0F0F0")
text = tk.Text(text_frame, yscrollcommand=scrollbar.set, wrap="none")
text.bind('<MouseWheel>', multiple_yview)
scrollbar.config(command=multiple_yview)

for i in range(0, 100):  # For entering some text and line numbers
    text.insert("end", "A" + str(i) + "\n")
    line_numbers.configure(state="normal")
    line_numbers.insert("end", str(i) + ") \n")
    line_numbers.configure(state="disabled")

text_frame.grid(row=0, column=0)
line_numbers.grid(row=0, column=0)
text.grid(row=0, column=1)
scrollbar.grid(row=0, column=2, sticky= tk.N + tk.S)

root.mainloop()
Plantz
  • 97
  • 7

1 Answers1

3

If you print out args, you will find that the content of args is different for both command option and event binding.

You need to convert args of event binding to the format used by yview and return "break" to disable the default handler of the event:

def multiple_yview(*args):
    rv = None
    if isinstance(args[0], tk.Event):
        args = ('scroll', args[0].delta//-30, "units")
        rv = "break"  # disable default handler
    text.yview(*args)
    line_numbers.yview(*args)
    return rv

Note that you need to bind <MouseWheel> event on line_numbers as well.

acw1668
  • 40,144
  • 5
  • 22
  • 34
  • Thank you very much, it works perfectly fine! I never would have figured out this approach by myself. – Plantz Apr 24 '23 at 12:58