te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>python - Selection not working after changing Text widget focus - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Selection not working after changing Text widget focus - Stack Overflow

programmeradmin3浏览0评论

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()

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()
Share Improve this question asked Feb 17 at 19:44 HenryHenry 3,9442 gold badges15 silver badges41 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

I think the simplest solution is to force the focus back to the text widget, but schedule that to happen after all other event processing.

Add this anywhere in goto:

self.text.after_idle(self.text.focus_set)
发布评论

评论列表(0)

  1. 暂无评论