[Python] Selection not working after changing Text widget focus

Discussão em 'Python' iniciado por Stack, Fevereiro 17, 2025.

  1. Stack

    Stack Membro Participativo

    I have an application with two Text widgets in two Frames. Clicking a link (formatted text with a tag bind) in one Text widget (Results.text) should select text in another (Editor.text).

    When I click once on the link, the see part works but mark_set and applying the selection do not.

    If I double click the link it works as intended, but I'd like it to work with a single click. I have tried returning "break" from the event handler and using focus_set, focus_force, update_idletasks and update in the goto method with no success.

    I'm using Python 3.13, Tcl/Tk 8.6.15 and Windows 10.

    import math
    import tkinter as tk
    from tkinter.font import Font
    from typing import Any, Callable

    LIPSUM = """\
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque et lectus lacinia enim dictum posuere. Quisque nunc tellus, luctus quis eleifend a, placerat maximus ipsum.
    Nam egestas, nisi in varius tempus, enim tortor consectetur eros, ac pretium urna ipsum at orci. Praesent ut dui eu lectus efficitur ultrices. In vehicula leo faucibus tempor posuere.
    Nam et est in nisi iaculis rhoncus eu et nibh. Aliquam eget risus lacus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."""


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

    self.grid_rowconfigure(1, weight=1)
    self.grid_columnconfigure(1, weight=1)

    self.line_num = tk.Text(
    self,
    width=3,
    yscrollcommand=self.scroll_update,
    cursor="arrow",
    )
    self.line_num.tag_configure("rjust", justify="right")
    self.line_num.insert("1.0", "1", "rjust")
    # disable selection
    self.line_num.bindtags((str(self.line_num), str(self), "all"))
    self.line_num.grid(row=1, column=0, sticky="ns")
    self.text = tk.Text(
    self,
    yscrollcommand=self.scroll_update,
    wrap="none",
    )
    self.text.bind("<KeyRelease>", lambda _: self.update_lines())
    self.text.grid(row=1, column=1, sticky="nesw")
    self.editor_v_sbr = tk.Scrollbar(self, command=self.editor_scroll)
    self.editor_v_sbr.grid(row=1, column=2, sticky="ns")
    self.editor_h_sbr = tk.Scrollbar(
    self, command=self.text.xview, orient="horizontal" # type:ignore
    )
    self.editor_h_sbr.grid(row=2, column=0, columnspan=2, sticky="ew")
    self.text.config(xscrollcommand=self.editor_h_sbr.set)

    def editor_scroll(self, _: Any, position: float):
    self.line_num.yview_moveto(position)
    self.text.yview_moveto(position)

    def scroll_update(self, first: float, last: float):
    self.text.yview_moveto(first)
    self.line_num.yview_moveto(first)
    self.editor_v_sbr.set(first, last)

    def update_lines(self, new_text: str | None = None):
    if not new_text:
    new_text = self.text.get("1.0", "end")
    self.line_num.config(state="normal")
    self.line_num.delete("1.0", "end")
    num_lines = len(new_text.splitlines(True))
    if num_lines >= 1000:
    new_width = int(math.log10(num_lines)) + 1
    self.line_num.config(width=new_width)
    self.line_num.insert(
    "1.0",
    "\n".join([str(x + 1) for x in range(num_lines)]),
    "rjust",
    )
    self.line_num.config(state="disabled")

    def view_file(self):
    self.text_content = LIPSUM
    self.update_lines(self.text_content)
    self.text.delete("1.0", "end")
    self.text.insert("1.0", self.text_content)

    def goto(self, start: tuple[int, int], end: tuple[int, int]):
    pos: Callable[[tuple[int, int]], str] = lambda x: f"{x[0]}.{x[1]-1}"
    self.text.focus_set()
    self.text.see(pos(start))
    self.text.mark_set("insert", pos(start))
    self.text.tag_remove("sel", "1.0", "end")
    self.text.tag_add("sel", pos(start), pos(end))


    class Results(tk.LabelFrame):
    def __init__(
    self,
    master: tk.Tk,
    goto: Callable[[tuple[int, int], tuple[int, int]], None],
    *args: Any,
    **kwargs: Any,
    ):
    tk.LabelFrame.__init__(self, master, *args, text="Results", **kwargs)
    self.grid_columnconfigure(0, weight=1)
    self.grid_rowconfigure(0, weight=1)
    self.text = tk.Text(
    self,
    wrap="none",
    )
    self.text.insert("end", "Lorem ipsum dolor sit amet\n")
    self.text.insert("end", "View in editor", "link")

    def callback():
    self.goto((1, 1), (1, 27))
    return "break"

    self.text.tag_bind("link", "<Button-1>", lambda _: callback())
    self.link_font = Font(self, "TkDefaultFont")
    self.link_font.configure(underline=True)
    self.text.tag_configure("link", font=self.link_font, foreground="SlateBlue3")

    self.text.configure(state="disabled")
    self.text.tag_bind("link", "<Enter>", self.link_enter)
    self.text.tag_bind("link", "<Leave>", self.link_leave)
    self.text.grid(row=0, column=0, sticky="nesw", padx=(10, 0), pady=(10, 0))

    self.y_sbr = tk.Scrollbar(self, command=self.text.yview) # type:ignore
    self.y_sbr.grid(row=0, column=1, sticky="ns", padx=(0, 10), pady=(10, 0))
    self.x_sbr = tk.Scrollbar(
    self, command=self.text.xview, orient="horizontal" # type:ignore
    )
    self.x_sbr.grid(row=1, column=0, sticky="ew", pady=(0, 10), padx=(10, 0))
    self.text.configure(yscrollcommand=self.y_sbr.set)
    self.text.configure(xscrollcommand=self.x_sbr.set)
    self.goto = goto

    def link_enter(self, _: Any):
    self.text.config(cursor="hand2")

    def link_leave(self, _: Any):
    self.text.config(cursor="")


    class App(tk.Tk):
    def __init__(self):
    tk.Tk.__init__(self)
    self.editor = Editor(self)
    self.editor.grid(row=0, column=1, sticky="nesw", padx=10, pady=10)
    self.results = Results(self, self.editor.goto)
    self.results.grid(row=0, column=2, sticky="nesw", padx=10, pady=10)

    self.editor.text.focus()

    self.editor.view_file()


    if __name__ == "__main__":
    app = App()
    app.mainloop()

    Continue reading...

Compartilhe esta Página