当前位置: 首页 > 面试题库 >

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

甄越
2023-03-14
问题内容

尝试学习tkinter和python。我想在相邻框架中显示“文本”小部件的行号

from Tkinter import *
root = Tk()
txt = Text(root)
txt.pack(expand=YES, fill=BOTH)
frame= Frame(root, width=25)
#

frame.pack(expand=NO, fill=Y, side=LEFT)
root.mainloop()

我在一个名为unpythonic的网站上看到了一个示例,但该示例假定txt的行高为6像素。

我正在尝试这样的事情:

1)将Any-KeyPress事件绑定到一个返回发生按键的行的函数

textPad.bind("<Any-KeyPress>", linenumber)


def linenumber(event=None):
    line, column = textPad.index('end').split('.')
    #creating line number toolbar
    try:
       linelabel.pack_forget()
       linelabel.destroy()
       lnbar.pack_forget()
       lnbar.destroy()
    except:
      pass
   lnbar = Frame(root,  width=25)
   for i in range(0, len(line)):
      linelabel= Label(lnbar, text=i)
      linelabel.pack(side=LEFT)
      lnbar.pack(expand=NO, fill=X, side=LEFT)

不幸的是,这在框架上给出了一些奇怪的数字。有没有更简单的解决方案?如何处理呢?


问题答案:

我有一个相对简单的解决方案,但是它很复杂并且可能很难理解,因为它需要一些有关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", "one\ntwo\nthree\n")
        self.text.insert("end", "four\n",("bigfont",))
        self.text.insert("end", "five\n")

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


 类似资料:
  • 问题内容: 我正在使用一个简单的消息传递系统,并且需要将以下内容添加到Tkinter文本小部件中: 拼写检查 更改字体的选项(在选定的文本上) 更改字体颜色的选项(在选定的文本上) 更改字体大小的选项(在选定的文本上) 我了解tkinter文本小部件可以通过标记机制使用多种字体和颜色,但是我不知道如何利用这些功能。 如何使用“文本”小部件的功能实现这些功能?具体来说,如何更改字体系列,单词的颜色和

  • 问题内容: 我正在尝试将函数的标准输出重定向到tkinter文本小部件。我遇到的问题是,它会将每一行写到一个新窗口中,而不是将所有内容都列出在一个窗口中。该函数扫描目录并列出任何0k文件。如果没有文件为0k,则将其打印出来。因此,问题在于,如果目录中有30个0k文件,它将打开30个窗口,每个窗口只有一行。现在,我知道问题出在哪里。如果您查看我的功能代码,我会告诉您: 我知道,每次os.stat看到

  • 问题内容: 无论如何在qtablewidget中添加像按钮一样的内容?但是必须在单元格中显示日期,例如,如果用户双击某个单元格,我是否可以像按钮一样发送信号?谢谢! edititem(): 表触发器: 问题答案: 您有几个问题可以归结为一个…简短答案,是的,您可以向QTableWidget添加按钮- 您可以通过调用setCellWidget将任何窗口小部件添加到表格窗口小部件: 但这听起来并不像您

  • 问题内容: 在我的在线计算机科学课上,我必须编写一个程序来确定太阳系中每个行星的表面重力。除了一个方面,我几乎已经掌握了它的所有方面。我需要使用单独的方法将表面重力写入文件。这是我目前的方法: 我的问题是,当我将其写入文件时,它将覆盖先前的值。我如何获得它包括所有的价值。如果有帮助,这是我的全部代码: 问题答案: 这样做是为了创建带有追加模式的作品:

  • 问题内容: 我需要能够向ELF文件添加任意部分。我无法在该程序中使用GPL代码,因此BFD成为不可能。我可以使用libelf / gelf来阅读节,但是这些文档很少,因此我无法弄清楚如何添加节。有人知道怎么做这个吗?我宁愿不编写自己的ELF代码。 问题答案: 关于ELF文件标头的问题有几个(可能)相关答案。提到的接受的答案用于将部分添加到ELF文件,并且BSD bintools 声称具有BSD许可

  • 问题内容: 我有一个Python程序,它执行一组操作并在STDOUT上打印响应。现在,我正在编写一个GUI,它将调用已经存在的代码,并且我想在GUI中打印相同的内容,而不是STDOUT。我将为此使用文本小部件。我不想修改执行该任务的现有代码(其他一些程序也使用此代码)。 有人可以指出我如何使用此现有任务定义并使用其STDOUT结果并将其插入文本小部件中吗?在主GUI程序中,我想调用此任务定义并将其