栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Tkinter将行号添加到文本小部件

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Tkinter将行号添加到文本小部件

我有一个相对简单的解决方案,但是它很复杂并且可能很难理解,因为它需要一些有关Tkinter和底层tcl /
tk文本小部件工作方式的知识。在这里,我将其作为可以按原样使用的完整解决方案进行介绍,因为我认为它说明了一种效果很好的独特方法。

请注意,无论您使用哪种字体,是否在不同的行上使用不同的字体,是否具有嵌入式小部件,等等,此解决方案均有效。

导入Tkinter

在开始之前,如果您使用的是python 3.0或更高版本,以下代码假定tkinter是这样导入的:

import tkinter as tk

…或者这个,对于python 2.x:

import Tkinter as tk

行号小部件

让我们来处理行号的显示。我们要做的是使用画布,以便我们可以精确地定位数字。我们将创建一个自定义类,并为其提供一个名为的新方法

redraw
,该方法将重绘关联文本小部件的行号。我们还为它提供了一种方法
attach
,用于将文本小部件与此小部件相关联。

这种方法利用了以下事实:文本小部件本身可以通过该

dlineinfo
方法准确地告诉我们一行文本的开始和结束位置。这可以准确地告诉我们在画布上绘制行号的位置。它还利用了以下事实:如果某行不可见,则
dlineinfo
返回该值
None
,我们可以用来知道何时停止显示行号。

class TextLineNumbers(tk.Canvas):    def __init__(self, *args, **kwargs):        tk.Canvas.__init__(self, *args, **kwargs)        self.textwidget = None    def attach(self, text_widget):        self.textwidget = text_widget    def redraw(self, *args):        '''redraw line numbers'''        self.delete("all")        i = self.textwidget.index("@0,0")        while True : dline= self.textwidget.dlineinfo(i) if dline is None: break y = dline[1] linenum = str(i).split(".")[0] self.create_text(2,y,anchor="nw", text=linenum) i = self.textwidget.index("%s+1line" % i)

如果将其与文本小部件关联,然后调用该

redraw
方法,则它应显示行号。

自动更新行号

这行得通,但有一个致命缺陷:您必须知道何时致电

redraw
。您可以创建一个在每次按键时都会触发的绑定,但是还必须在鼠标按键上触发,并且必须处理用户按下某个键并使用自动重复功能等的情况。行号也需要如果窗口变大或缩小或用户滚动则要重绘,因此我们陷入了一个困境,试图找出可能导致数字更改的所有可能事件。

还有另一种解决方案,那就是让文本小部件在发生任何更改时触发一个事件。不幸的是,文本小部件不直接支持通知程序更改。为了解决这个问题,我们可以使用代理来拦截对文本小部件的更改并为我们生成一个事件。

在回答“与光标移动绑定不会更改INSERT标记”这个问题的答案中,我提供了一种类似的解决方案,该解决方案显示了每当发生更改时如何使文本小部件调用回调。这次,我们将生成一个事件,而不是回调,因为我们的需求有所不同。

自定义文本类

这是一个创建自定义文本窗口小部件的类,

<<Change>>
每当插入或删除文本或滚动视图时,该窗口小部件都会生成一个事件。

class CustomText(tk.Text):    def __init__(self, *args, **kwargs):        tk.Text.__init__(self, *args, **kwargs)        # create a proxy for the underlying widget        self._orig = self._w + "_orig"        self.tk.call("rename", self._w, self._orig)        self.tk.createcommand(self._w, self._proxy)    def _proxy(self, *args):        # let the actual widget perform the requested action        cmd = (self._orig,) + args        result = self.tk.call(cmd)        # generate an event if something was added or deleted,        # or the cursor position changed        if (args[0] in ("insert", "replace", "delete") or  args[0:3] == ("mark", "set", "insert") or args[0:2] == ("xview", "moveto") or args[0:2] == ("xview", "scroll") or args[0:2] == ("yview", "moveto") or args[0:2] == ("yview", "scroll")        ): self.event_generate("<<Change>>", when="tail")        # return what the actual widget returned        return result

放在一起

最后,这是一个使用这两个类的示例程序:

class Example(tk.frame):    def __init__(self, *args, **kwargs):        tk.frame.__init__(self, *args, **kwargs)        self.text = CustomText(self)        self.vsb = tk.Scrollbar(orient="vertical", command=self.text.yview)        self.text.configure(yscrollcommand=self.vsb.set)        self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))        self.linenumbers = TextLineNumbers(self, width=30)        self.linenumbers.attach(self.text)        self.vsb.pack(side="right", fill="y")        self.linenumbers.pack(side="left", fill="y")        self.text.pack(side="right", fill="both", expand=True)        self.text.bind("<<Change>>", self._on_change)        self.text.bind("<Configure>", self._on_change)        self.text.insert("end", "onentwonthreen")        self.text.insert("end", "fourn",("bigfont",))        self.text.insert("end", "fiven")    def _on_change(self, event):        self.linenumbers.redraw()

…,当然,请在文件末尾添加以下内容以进行引导:

if __name__ == "__main__":    root = tk.Tk()    Example(root).pack(side="top", fill="both", expand=True)    root.mainloop()


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/647336.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号