使用Tkinter文本索引表达式删除最后一行文本

时间:2017-04-22 16:18:58

标签: python tkinter widget

我有一个文本小部件,可以在程序运行时显示信息。我想添加一些功能,这将允许我用新行覆盖文本小部件的最后一行。我的代码如下所示:

class TextRedirector(object):
   def __init__(self, text_widget):
      self.text_widget = text_widget
   def write(self, the_string):
      self.text_widget.configure(state="normal")
      self.text_widget.insert("end", the_string)
      self.text_widget.see(END)
      self.text_widget.configure(state="disabled")
   def overwrite(self, the_string):
      self.text_widget.configure(state="normal")
      self.text_widget.delete("end-1l linestart+1c", "end")
      self.text_widget.insert("end", the_string)
      self.text_widget.see(END)
      self.text_widget.configure(state="disabled")

How do I get the line and column of the end position of text in Tkinter? - 我看过这篇文章,其中Bryan Oakley似乎用textwidget.delete("end-1c linestart", "end")回答了我的问题,但是当我使用它时,文本交替放在最后一行的末尾,实际上覆盖了最后一行。也就是说,它在一半的时间内工作,而新文本在旧文本末尾被打了一半的时间。我对Text小部件的索引表达式的理解(简要介绍http://effbot.org/tkinterbook/text.htm)是" end-1c linestart"意味着像"结束,支持一个字符,开始行"所以我不明白为什么它会走到一行文本的末尾,而且只是每隔一段时间。结果在每个步骤看起来都是这样的(每个部分都是整个文本小部件的更新版本,只修改了最后一行):

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42iteration: 2 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.
iteration: 3 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.iteration: 4 / 42
-----

我尝试使用self.text_widget.delete("end-1l linestart+1c", "end")。这几乎可行,但我最终得到了

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42
iteration: 2 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42
iiteration: 3 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42
iiteration: 4 / 42

我已经尝试了一些其他的东西,根据我的理解使用这些索引表达式,但我还没有解决问题。我尝试在覆盖函数中添加if语句来处理可能位于窗口小部件末尾的文本的不同场景,例如,如果它以空行结束,或者两个空行等,我也没有成功。

对于完全公开,我可能会提到我使用此Text小部件作为命令行打印输出的替代。因此,类的名称,TextRedirector。我不认为这会对手头的问题产生任何影响,但错误的假设可能就是让我首先出现在这里......课后的界限是:

sys.stdout = TextRedirector(self.textbox)

self.textbox是在定义类之前创建的Text小部件。

更新:我尝试在最后一次插入文本之前保存索引,并根据该表达式构建一个字符串表达式以删除最后一行。结果仍然不完美。

class TextRedirector(object):
   def __init__(self, text_widget):
      self.text_widget = text_widget
      self.index_before_last_print = ""
   def write(self, the_string):
      self.index_before_last_print = self.text_widget.index("end")
      self.text_widget.configure(state="normal")
      self.text_widget.insert("end", the_string)
      self.text_widget.see(END)
      self.text_widget.configure(state="disabled")
   def overwrite(self, the_string):
      self.text_widget.configure(state="normal")
      self.text_widget.delete(self.index_before_last_print + "-1c linestart+1c", "end")
      self.index_before_last_print = self.text_widget.index("end")
      self.text_widget.insert("end", the_string)
      self.text_widget.see(END)
      self.text_widget.configure(state="disabled")

结果是

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iteration: 1 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iiteration: 2 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iiteration: 3 / 42
-----

!!!!! Settings Have Been Backed Up !!!!!

Computing coordinates...

Coordinates have been computed.

iiteration: 4 / 42

2 个答案:

答案 0 :(得分:1)

  

我看过这篇文章[似乎]用textwidget.delete(“ end-1c linestart”,“ end”)回答了我的问题,但是当我使用它时,文本交替出现在最后一行,实际上覆盖了最后一行。

添加文本的原因很容易解释。如果插入以换行符结尾的行(例如:“ foo \ n”),则“ end-1c linestart”是指紧接在最后一个换行符之前的位置。因此,您的代码仅覆盖换行符,这会使新文本附加到旧文本上。

如果您的代码不一致,有时您会插入一行带有换行符的文本,有时却没有插入换行符,或者有时会插入多个换行符,这将解释为什么代码在不同时间表现出不同的行为。

如果 all 写入文本小部件都经过了重定向器类,那么一个非常简单的解决方案是在最后添加的数据中添加标签。然后,您的覆盖方法可以简单地删除带有此标记的所有文本。

这是一个工作示例,提供了两个按钮,一个按钮用于添加文本,一个按钮用于覆盖文本:

import tkinter as tk
import datetime

class TextRedirector(object):
    def __init__(self, text_widget):
        self.text_widget = text_widget
        self.text_widget.tag_configure("last_insert", background="bisque")

    def write(self, the_string):
        self.text_widget.configure(state="normal")
        self.text_widget.tag_remove("last_insert", "1.0", "end")
        self.text_widget.insert("end", the_string, "last_insert")
        self.text_widget.see("end")
        self.text_widget.configure(state="disabled")

    def overwrite(self, the_string):
        self.text_widget.configure(state="normal")
        last_insert = self.text_widget.tag_ranges("last_insert")
        self.text_widget.delete(last_insert[0], last_insert[1])
        self.write(the_string)

def overwrite():
    stdout.overwrite(str(datetime.datetime.now()) + "\n")

def append():
    stdout.write(str(datetime.datetime.now()) + "\n")

root = tk.Tk()
text = tk.Text(root)
stdout = TextRedirector(text)

append = tk.Button(root, text="append", command=append)
overwrite = tk.Button(root, text="overwrite", command=overwrite)

append.pack()
overwrite.pack()
text.pack()

root.mainloop()

答案 1 :(得分:0)

更新:下面描述的行为记录在Tcl / Tk文档中,这里为https://www.tcl.tk/man/tcl8.6/TkCmd/text.htm。具体而言,pathName insert文档中指出了指向小部件中最后换行之后的位置的插入实际上发生在该换行之前的事实。 pathName delete文档说不会使小部件带有最后换行符的删除将被静默修改以保留最终的换行符。

原始答案:

我遇到了同样的问题,并且找到了适合我的解决方案。 Tl; dr: Tk文本小部件在插入和 在末尾删除,但如果您总是添加新内容, 前导(而不是尾随)换行符,它应该表现出预期的效果。 具体来说,text.delete("end-1l","end")后跟text.insert("end",u"\nContent") 将替换最后一行。

通过在wish(Tk外壳)和Python 2.7中试验文本小部件, 我发现:

1)新创建的文本窗口小部件包含换行符。您无法轻易删除 它,如果您设法做到这一点,小部件的行为将非常 此后很奇怪。

>>> import Tkinter as tk
>>> root = tk.Frame()
>>> t = tk.Text(root)
>>> t.grid()
>>> root.grid()
>>> t.get('1.0','end')
u'\n'
>>> t.delete('1.0','end')
>>> t.get('1.0','end')
u'\n'

这真是个惊喜! “从一开始就删除所有内容”的哪一部分 到底”,Tk,你不明白吗?

2)在“末尾”的插入实际上是在最后的换行符之前插入。这个 似乎与Tk文档有冲突,这表明 “结束”位置是指紧随之后的字符位置 小部件中的最后一个换行符。

>>> t.insert('end',u"\nA line")
>>> t.get('1.0',"end')
u'\nA line\n'

尽管我们想在最后插入,但实际上是在插入 在最终换行符之前

>>> t.insert('end',u"\nLine 2") 
>>> t.get('1.0','end')
u'\nA line\nLine 2\n'

并且这种行为似乎是一致的。如果我们尝试删除一行怎么办?好 以“直观”的方式执行此操作:从“结束”备份一行,然后从中删除 在那里“结束”:

>>> t.delete('end-1l','end')
>>> t.get('1.0','end')
u'\nA line\n'

我们又回到了以前的状态,这是个好消息!另一个插入 将插入的行放在预期的位置:

>>> t.insert('end',u"\nA new line")
>>> t.get('1.0','end')
u'\nA line\nA new line\n'

但这仅在我们使用前导换行符添加行时才按预期工作。 如果我们使用尾随换行符添加它们,则文本将附加到 上一行,以及一条附加的尾随换行符被添加到 小部件:

>>> t.insert('end',u"Trailing newline\n")
>>> t.get('1.0','end')
u'\nA line\nA new lineTrailing newline\n\n'

如果您相信Tk文档,这不是您期望的-您 希望在“末尾”插入会在之后插入测试 最后的换行符。但是a,你会错的。

以下完整的测试程序将显示一个文本小部件以及一个 输入字段和两个按钮,一个按钮将输入字段添加到 小部件,另一个覆盖小部件的最后一行 输入字段文本。 addLine()和replaceLastLine()函数实现 这些行为很简单。开头的空白行 该小部件是一个小麻烦,您可以通过执行以下操作来补救 t.delete("1.0","1.0+1c"),但只能在之后将一些文本插入小部件。

import Tkinter as tk
root = tk.Frame()
t = tk.Text(root)
t.grid(row=0,column=0,columnspan=3)
root.grid()

def addLine():
  msg = lineField.get()
  t.insert("end",u"\n{}".format(msg))

def replaceLastLine():
  msg = lineField.get()
  t.delete("end-1l","end")
  t.insert("end",u"\n{}".format(msg))

lineField = tk.Entry(root)
lineField.grid(row=1,column=0)

addBtn = tk.Button(root,text="Add line",command=addLine)
addBtn.grid(row=1,column=1)

replBtn = tk.Button(root,text="Replace line",command=replaceLastLine)
replBtn.grid(row=1,column=2)

tk.mainloop()