0

I want to highlight a given token inside a input text field (We can have several token highlighted) and when the user has the mouse over this token we get up dialogox. I tried the following:

import tkinter as tk
from tkinter import *
class Example(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        
        self.l1 = tk.Label(self, text="Hover over me")
        self.l2 = tk.Label(self, text="", width=40)
        self.l1.pack(side="top")
        self.l2.pack(side="top", fill="x")
        self.inputText = tk.Text(root, height = 10, width = 70, bg = "light yellow")
        self.inputText.insert('1.0', "token1 token2 token3 etc.")
        self.inputText.pack()
        self.display_annotate = tk.Button(self, height = 2, width = 20, text ="Annotate text", command = lambda: self.add_highlighter())
        self.display_annotate.place(x = 750, y = 20)
        
        print(self.__dict__.keys())


        self.l1.bind("<Enter>", lambda event, text="text": self.on_enter(text=text))
        self.l1.bind("<Leave>", self.on_leave)

    def take_input(self,):
        text_to_annotate = self.inputText.get("1.0", "end-1c")
        print(text_to_annotate)
        return text_to_annotate

    def on_enter(self, text):
        self.l2.configure(text=text)

    def on_leave(self, event):
        self.l2.configure(text="")

    def add_highlighter(self):
        self.inputText.tag_add("start", "1.0", "1.5")
        self.inputText.bind("<Enter>", lambda event, text="ali": self.on_enter(text=text))
        self.inputText.tag_config("start", background= "black", foreground= "white")

if __name__ == "__main__":
    root = tk.Tk()
    scrollb = tk.Scrollbar(root)
    scrollb.pack(side = tk.RIGHT, fill=tk.Y)
    var1 = tk.IntVar()
    var2 = tk.IntVar()
    root.geometry("900x500+10+10")
    root.title('Annotation page')
    Example(root).pack(side="top", fill="both", expand="true")     
    root.mainloop()

It works to highlight the concerned token 1 between the caracters 0 and 5. But it is not working when I haveover the mouse over token 1. noting that it is working for the label.

Any suggestion?

bib
  • 944
  • 3
  • 15
  • 32
  • You could try using a `tooltip` if your dialog is just for information. https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter – Derek Aug 10 '22 at 01:25
  • I deleted my answer. It is actually quite unclear to me what you are trying to do. I'm sorry I couldn't help. I would suggest tearing out the part that you understand to be broken and make it work by itself, then inject it back into your app after it works. You were actually supposed to do the extraction end post that here instead of your whole app. This is why it's hard to help you. Your code is dense for a question and written unfavorably. It's called a Minimal Reproducible Example. Yours is not minimal. If you minimized your problem you probably wouldn't even need us to solve it. – OneMadGypsy Aug 10 '22 at 15:43
  • @OneMadGypsy I am trying to annotate text. I have the result for the annotation in json. so now i wanna highlit each word existing in the result and when the user has its mouve over this word we display the result from the json. – bib Aug 10 '22 at 15:52
  • @bib I get that part, having all this random logic printing useless words in random places due to uncommented logic is the problem. To put it bluntly, the code is a mess and doesn't have any rhyme or teason. I mean no disrespect. A Minimal Reproducible Example of just the problem, with maybe some comments and better phrasing of your question would go a long way in getting others to successfully help you. If you just got rid of the fluff and tried to reproduce your problem you will probably solve it yourself. – OneMadGypsy Aug 10 '22 at 17:31
  • I can however tell you what your problem really is. You're trying to just build an app and treat everything like it is one thing. You should make each part individually. Make a working widget and then use it. Otherwise, your project is going to get harder and harder to debug as it gets bigger. Strip out your `Text` widget, make it work, then put it in your app. In the long run it won't be a Text widget. You want to add a bunch of new functionality so, that widget should be it's own class. – OneMadGypsy Aug 10 '22 at 17:40

1 Answers1

0

If I got you right, I've added a label when you hover over a token (together with rotating tokens)

import tkinter as tk
from tkinter import *


def on_enter(text, obj, e=None):
    obj.float_lbl = Label(root, text=text)
    obj.float_lbl.place(x=e.x + obj.winfo_x(), y=e.y + obj.winfo_y())


def on_leave(obj, event=None):
    obj.float_lbl.destroy()


class Example(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)

        self.l1 = tk.Label(self, text="Hover over me")
        self.l1.pack(side="top")
        # creating new Text object with new methods and properties
        self.inputText = InputText()
        # creating 15 new tokens
        for i in range(15):
            self.inputText.add_token(f"token{i + 1}")
        self.inputText.pack()
        # binding rotate_tag method to button
        self.display_annotate = tk.Button(self, height=2, width=20, text="Annotate text",
                                          command=self.inputText.rotate_tag)
        self.display_annotate.place(x=750, y=20)

        self.l1.bind("<Enter>", lambda event, text="text": on_enter(text=text, e=event, obj=self.l1))
        self.l1.bind("<Leave>", lambda event: on_leave(event=event, obj=self.l1))


class Token:
    """
    Token object for InputText
    """
    def __init__(self, name: str, pos: int, tag_parent: Text):
        self.token_name = name
        self.begin = "1." + str(pos)
        self.end = "1." + str(pos + len(name))
        self.parent = tag_parent
        self.add_tag()
        self.bind_tag()

    def add_tag(self):
        self.parent.tag_add(self.token_name, self.begin, self.end)

    def bind_tag(self):
        self.parent.tag_bind(self.token_name, "<Enter>",
                             lambda event, text="text": on_enter(obj=self.parent, e=event, text=self.token_name))
        self.parent.tag_bind(self.token_name, "<Leave>",
                             lambda event, text="text": on_leave(obj=self.parent, event=event))

    def highlight_tag(self):
        self.parent.tag_config(self.token_name, background='red', foreground='white')

    def disable_tag(self):
        self.parent.tag_config(self.token_name, background='gray', foreground='black')


class InputText(Text):
    """
    Text object with methods to add tokens and rotate tags
    """
    def __init__(self):
        super(InputText, self).__init__(root, height=10, width=70, bg="light yellow")

        self.tokens = []
        self.last = 0
        self.current_tag = None

    def add_token(self, name):
        self.insert(END, name + " ")
        self.tokens.append(Token(name, self.last, self))
        self.last += (1 + len(name))

    def rotate_tag(self):
        if self.current_tag is None:
            self.current_tag = 0
        else:
            self.tokens[self.current_tag - 1].disable_tag()
        self.tokens[self.current_tag].highlight_tag()
        self.current_tag = min(self.current_tag + 1, len(self.tokens) - 1)


if __name__ == "__main__":
    root = tk.Tk()
    scrollb = tk.Scrollbar(root)
    scrollb.pack(side=tk.RIGHT, fill=tk.Y)
    var1 = tk.IntVar()
    var2 = tk.IntVar()
    root.geometry("900x500+10+10")
    root.title('Annotation page')
    Example().pack(side="top", fill="both", expand="true")
    root.mainloop()
Ze'ev Ben-Tsvi
  • 1,174
  • 1
  • 3
  • 7