I am building a Tkinter application in Python, and I want to add a Text widget for the user to add comments, while putting numbers next to the comments for each one. I use a second small Text widget to add the numbers and I followed the suggestions in this question to make both widgets scroll together so that the numbers keep their proper position.
The problem is that when the height of the Text exceeds the height of the widget and the Scrollbar should "follow" to show the INSERT index, instead of that I get an unexpected behavior with the Scrollbar going up and down alternately each time I type a new char.
I am monitoring the values given to the yscrollcommand each time I type a new char, and they follow the sequence below, while I am writing in the same line, the last one in the low end of the widget:
f/l: 0.0 0.9732142857142857
---
f/l: 0.026785714285714284 1.0
---
f/l: 0.0 0.9732142857142857
---
f/l: 0.026785714285714284 1.0
---
f/l: 0.0 0.9732142857142857
---
f/l: 0.026785714285714284 1.0
---
f/l: 0.0 0.9732142857142857
---
f/l: 0.026785714285714284 1.0
Here is the related code:
from tkinter import *
from tkinter.ttk import *
class MyApp(Frame):
def __init__(self, parent, *args, **kwargs):
Frame.__init__(self, parent)
self.pack()
self.parent = parent
self.action_frame = Frame(self, name="action")
self.vis_frame = Panedwindow(self.action_frame, orient=HORIZONTAL)
self.action_frame.pack(fill=BOTH, expand=YES)
self.vis_frame.pack(fill=BOTH, expand=YES)
fr = LabelFrame(self.vis_frame, text="Comments")
fr1 = LabelFrame(self.vis_frame, text="Draft")
self.text = Text(fr)
self.lbl_fr = Text(fr, width=4, state=DISABLED, background="SystemMenu")
self.scrl = Scrollbar(fr, orient=VERTICAL)
Button(fr, text="Submit", command=self.submit).pack(side=BOTTOM)
self.scrl.pack(side=RIGHT, fill=Y)
self.text.pack(side=RIGHT, fill=BOTH)
self.lbl_fr.pack(side=RIGHT, fill=Y)
self.scrl.configure(command=self.__scrollBoth)
self.text.configure(yscrollcommand=self.__updateScroll)
self.lbl_fr.configure(yscrollcommand=self.__updateScroll)
self.vis_frame.add(fr1, weight=1)
self.vis_frame.add(fr)
bindtags = list(self.text.bindtags())
bindtags.insert(2, "custom")
self.text.bindtags(tuple(bindtags))
self.text.bind_class("custom", "<Return>", self.lbls)
self.text.bind_class("custom", "<Key>", self.lbls)
self.text.bind_class("custom", "<BackSpace>", self.lbls)
self.text.bind_class("custom", "<Delete>", self.lbls)
self.cur = self.lbl_fr.index(INSERT)
def lbls(self, e=None):
self.text.see("{}+1c".format(INSERT))
self.lbl_fr["state"] = NORMAL
self.lbl_fr.delete("0.0", END)
i = "1.0"
while True:
if i == self.text.index(END):
break
linenum = str(i).split(".")[0]
if i[:-2] == self.text.index(INSERT).split(".")[0]:
self.cur = self.lbl_fr.index(INSERT)
what = "{}.\n".format(linenum)
self.lbl_fr.insert(INSERT, what)
for j in range(len(self.text.get(i, i+"+1l-1c"))//self.text.cget("width")):
if i[:-2] == self.text.index(INSERT).split(".")[0]:
if j+1 == int(self.text.index(INSERT).split(".")[1]) // self.text.cget("width"):
self.cur = self.lbl_fr.index(INSERT)
self.lbl_fr.insert(INSERT, " \n")
i = self.text.index("{}+1l".format(i))
self.lbl_fr.delete(INSERT+"-1c", INSERT+" lineend")
self.lbl_fr["state"] = DISABLED
print("---------------------------")
# print("--->", self.cur)
def __scrollBoth(self, act, pos, type=None):
self.text.yview_moveto(pos)
self.lbl_fr.yview_moveto(pos)
print("pos: ", pos)
def __updateScroll(self, first, last, type=None):
self.text.yview_moveto(first)
self.lbl_fr.yview_moveto(first)
self.scrl.set(first, last)
# self.text.see(INSERT)
# self.lbl_fr.see(self.cur)
print("f/l: ", first, last)
def submit(self):
pass
if __name__ == "__main__":
root = Tk()
MyApp(root)
root.mainloop()
Does anyone happen to have any similar problem or could have an idea about why this happens? I've tried a workaround by adding the two commented lines in the __updateScroll method to explicitly put both widgets wherever the INSERT is (the self.cur variable is always updated with the respective position of the label of INSERT in the lbl_fr Text widget), but of course this isn't a perfect solution, as it doesn't work properly while scrolling the widget without changing the INSERT.