-2

For loop parses file and adds info to a dictionary. Then inserts a link into the Text widget that a user can click and it gives them a popup with details from the dictionary. This works fine if only one file is in u but if multiple files are in u then the tag_bind uses the last fle in u for the popfunc function. I understand why it does this but I cant think of a better way go through the loop that avoids this problem. Recommendations?

def popfunc(event, path, dictionary):
    win = Toplevel()
    txt2 = Text()
    txt2.grid()
    for key, value in dictionary.items():
        if path == key:
            txt2.insert('end', value)

txt = Text()
txt.grid()
u = <list of files>
for i in u:
   txt.tag_bind('pop', '<Button-1>', lambda event: popfunc(event, i, dictionary))
   with open(i, 'r') as f:
       h = f.readlines()
       for line in h:
           <parse file and add info to dictionary>       
           txt.insert('end', 'User Info: ')
           txt.insert('end', 'Click here to see info', 'pop')
sidnical
  • 421
  • 1
  • 6
  • 23
  • There are many duplicates of this question. The one I used isn't a precise dupe since it asks about commands rather than binds, but the solution is the same and that question has an answer with many positive votes. – Bryan Oakley May 19 '17 at 18:53
  • @BryanOakley doing it this way sends a tkinter.event object at .... to the function for i but when that is compared to the dictionary in the function nothing matches it because the dictionary holds strings. Do I need to change something in the function? Also, I had to replace event with i=i if that changes anything. – sidnical May 19 '17 at 19:23
  • @BryanOakley please see the duplicate I used for this question. For cases that are specific to Tkinter button commands, we are now using http://stackoverflow.com/questions/10865116/, rather than the one you had (https://stackoverflow.com/questions/17677649). – Karl Knechtel Aug 18 '22 at 03:31

1 Answers1

1

You have two problems. The first is that you are using the same tag for every line, so any click on any of those lines will trigger the last binding that you made.

A simple solution would be to give a unique tag to each line. For example:

for i in u:
    tag = "pop-%s" % i
    ...
    txt.insert('end', 'Click here to see info', tag)

The second problem is that your binding will always use the last value of i. You need create a closure to attach the current value in the loop to the binding. The most common way to do that is via a default argument to the lambda:

txt.tag_bind('pop', '<Button-1>', lambda event, item=i: popfunc(event, item, dictionary))

You don't have to use a different variable name (item vs i). For example, this works just as well:

txt.tag_bind('pop', '<Button-1>', lambda event, i=i: popfunc(event, i, dictionary))

Personally I find that people unfamiliar with lambdas get confused by that so I like to use separate names.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • if I define tag as a variable and then use tag in the insert to do the bind it prints the "click here to see info" but the text color isn't changed and it doesn't do anything when clicked. It only works if tag_bind() is in qoates. – sidnical May 19 '17 at 20:22
  • I don't understand that comment. If you want the tag to have a color, you will have to configure the tag to have a color. I don't understand the part about quotes. You can do `tag_bind(tag, ...)`. – Bryan Oakley May 19 '17 at 20:40
  • I was saying that it didn't work if I used a variable for the tag name. I'm investigating now. – sidnical May 22 '17 at 14:25
  • Found the problem. For some reason the i(path) wouldn't work with the variable. I can do pop + 'test' and it works fine. If I do pop + i it wouldn't work. Tried str() and replacing the \ with \\ but couldn't get it. I just didn't use the path in the name and its all working fine. Thanks for assisting with how to fix my function. – sidnical May 22 '17 at 15:40