0

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.

  • That's a lot of code, all of which may or may not be related to your problem. Also the code you posted now needs a lot of work to be made runnable. Please try to change it to the smallest piece of code that we can copy-paste and shows your problem, we call this a [mcve]. – fhdrsdg Jan 24 '19 at 11:34
  • I've edited the code to a small (enough) functional example. Even though the lbls method doesn't seem to be that related to the problem, I think it's better include it as well, as it is called after every pressed key and it may affect somehow the behavior. – Manolis Iliakis Jan 24 '19 at 12:13

0 Answers0