多行插入选择 tkinter 文本小部件
Multiline insert selection tkinter Text widget
我想向我的文本小部件添加一项功能,以便我可以 select 多行并将其注释掉。我目前使用它来处理单行,但无法检测到跨越多个 characters/lines 的插入。我如何获得此类信息。我知道可以检测到它,因为您可以使用删除按钮一次删除多个 characters/lines。我想通过在每行的开头添加一个主题标签来注释掉这些块,它还应该能够删除多行注释。我如何获得插入信息来做这样的事情?
当前评论码:
#In __init__:
self.text.bind("<Command-slash>", self.make_comment)
#Later...
def make_comment(self, event):
self_anchor = (str(self.text.index("insert")).split("."))[0]
if self.text.get(self_anchor + ".0", self_anchor + ".1") != "#":
self.text.insert(float("{}.0".format(self_anchor)), "#")
else:
self.text.delete(self_anchor + ".0", self_anchor + ".1")
在的帮助下,我已经解决了这个问题。我将解释代码及其工作原理,但如果您只想要工作代码,它将位于此答案的底部。
为了comment/uncomment用户选择的所有行,我们需要知道每一行的编号。这可以通过使用 Python 的 range()
函数和用户选择的开始和结束行号来完成。
要获取用户选择的起始行,我们使用此代码:
first_line = self.text.index("sel.first").split(".")[0]
sel.first
只是表示“用户选择的起始索引”。同样,如果我们想获得用户选择的 end 行,我们做同样的事情但使用 sel.last
:
last_line = self.text.index("sel.last").split(".")[0]
然后,我们使用 range()
函数遍历这两行以及它们之间的每一行:
for line in range(first_line, last_line + 1):
# Comment or uncomment each line
...
请注意,我们使用 last_line + 1
来确保包含最后一行,因为 Python 的 range()
函数在 之前停止 它到达第二个数字。
现在,这段代码的唯一问题是,如果用户没有选择某些东西,你会得到一个_tkinter.TclError
说text doesn't contain any characters tagged with "sel"
。所以为了能够comment/uncomment一行,需要插入一个try
/except
块:
try:
first_line = self.text.index("sel.first").split(".")[0]
last_line = self.text.index("sel.last").split(".")[0]
for line in range(first_line, last_line + 1):
# Comment or uncomment each line
...
except tkinter.TclError:
# Comment or uncomment a single line
...
其余代码看起来与您已有的非常相似;您检查该行的第一个字符是否为 #
,如果是,则取消注释该行,如果不是,则注释该行。
还有两件事:
- 在
self.make_comment()
的末尾,您可能需要添加return "break"
;至少在我的系统上,Ctrl+/ 命令也会选择所有文本。在函数末尾返回 "break"
会阻止它这样做。
- 当循环注释多行时,您需要确保当前行号不大于文本中的换行数。例如:
for line in range(first_line, last_line+1):
if line <= int(self.text.get("1.0", "end").count("\n")):
# Comment or uncomment the lines
...
综上所述,这是一个完整的可重现示例。您可以一次 comment/uncomment 多行,或仅一行,具体取决于选择的方式:
import tkinter
class Window(tkinter.Tk):
"""The main window."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Our text widget
self.text = tkinter.Text(self)
with open(__file__) as f:
self.text.insert("1.0", f.read())
self.text.pack(expand=True, fill="both")
# Bind the text widget
self.text.bind("<Control-slash>", self.make_comment)
self.text.bind("<Control-a>", self.select_all)
def select_all(self, *args):
self.text.tag_add("sel", "1.0", "end")
return "break"
def make_comment(self, event):
# If this fails with an error, we know that the user only selected one line
try:
# Get the line number of the start and the end of the selection
first_line = int(self.text.index("sel.first").split(".")[0])
last_line = int(self.text.index("sel.last").split(".")[0])
# Loop through all the selected lines and comment or uncomment them
for line in range(first_line, last_line+1):
# This is to make sure that a # isn't added to something that isn't a line
if line <= int(self.text.get("1.0", "end").count("\n")):
if self.text.get("{}.0".format(line), "{}.1".format(line)) != "#":
self.text.insert("{}.0".format(line), "#")
else:
self.text.delete("{}.0".format(line), "{}.1".format(line))
except tkinter.TclError:
# Get the line number of the current cursor position
insert = self.text.index("insert").split(".")[0]
# Comment or uncomment the current line
if self.text.get("{}.0".format(insert), "{}.1".format(insert)) != "#":
self.text.insert("{}.0".format(insert), "#")
else:
self.text.delete("{}.0".format(insert), "{}.1".format(insert))
return "break"
if __name__ == "__main__":
Window().mainloop()
我想向我的文本小部件添加一项功能,以便我可以 select 多行并将其注释掉。我目前使用它来处理单行,但无法检测到跨越多个 characters/lines 的插入。我如何获得此类信息。我知道可以检测到它,因为您可以使用删除按钮一次删除多个 characters/lines。我想通过在每行的开头添加一个主题标签来注释掉这些块,它还应该能够删除多行注释。我如何获得插入信息来做这样的事情?
当前评论码:
#In __init__:
self.text.bind("<Command-slash>", self.make_comment)
#Later...
def make_comment(self, event):
self_anchor = (str(self.text.index("insert")).split("."))[0]
if self.text.get(self_anchor + ".0", self_anchor + ".1") != "#":
self.text.insert(float("{}.0".format(self_anchor)), "#")
else:
self.text.delete(self_anchor + ".0", self_anchor + ".1")
在
为了comment/uncomment用户选择的所有行,我们需要知道每一行的编号。这可以通过使用 Python 的 range()
函数和用户选择的开始和结束行号来完成。
要获取用户选择的起始行,我们使用此代码:
first_line = self.text.index("sel.first").split(".")[0]
sel.first
只是表示“用户选择的起始索引”。同样,如果我们想获得用户选择的 end 行,我们做同样的事情但使用 sel.last
:
last_line = self.text.index("sel.last").split(".")[0]
然后,我们使用 range()
函数遍历这两行以及它们之间的每一行:
for line in range(first_line, last_line + 1):
# Comment or uncomment each line
...
请注意,我们使用 last_line + 1
来确保包含最后一行,因为 Python 的 range()
函数在 之前停止 它到达第二个数字。
现在,这段代码的唯一问题是,如果用户没有选择某些东西,你会得到一个_tkinter.TclError
说text doesn't contain any characters tagged with "sel"
。所以为了能够comment/uncomment一行,需要插入一个try
/except
块:
try:
first_line = self.text.index("sel.first").split(".")[0]
last_line = self.text.index("sel.last").split(".")[0]
for line in range(first_line, last_line + 1):
# Comment or uncomment each line
...
except tkinter.TclError:
# Comment or uncomment a single line
...
其余代码看起来与您已有的非常相似;您检查该行的第一个字符是否为 #
,如果是,则取消注释该行,如果不是,则注释该行。
还有两件事:
- 在
self.make_comment()
的末尾,您可能需要添加return "break"
;至少在我的系统上,Ctrl+/ 命令也会选择所有文本。在函数末尾返回"break"
会阻止它这样做。 - 当循环注释多行时,您需要确保当前行号不大于文本中的换行数。例如:
for line in range(first_line, last_line+1):
if line <= int(self.text.get("1.0", "end").count("\n")):
# Comment or uncomment the lines
...
综上所述,这是一个完整的可重现示例。您可以一次 comment/uncomment 多行,或仅一行,具体取决于选择的方式:
import tkinter
class Window(tkinter.Tk):
"""The main window."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Our text widget
self.text = tkinter.Text(self)
with open(__file__) as f:
self.text.insert("1.0", f.read())
self.text.pack(expand=True, fill="both")
# Bind the text widget
self.text.bind("<Control-slash>", self.make_comment)
self.text.bind("<Control-a>", self.select_all)
def select_all(self, *args):
self.text.tag_add("sel", "1.0", "end")
return "break"
def make_comment(self, event):
# If this fails with an error, we know that the user only selected one line
try:
# Get the line number of the start and the end of the selection
first_line = int(self.text.index("sel.first").split(".")[0])
last_line = int(self.text.index("sel.last").split(".")[0])
# Loop through all the selected lines and comment or uncomment them
for line in range(first_line, last_line+1):
# This is to make sure that a # isn't added to something that isn't a line
if line <= int(self.text.get("1.0", "end").count("\n")):
if self.text.get("{}.0".format(line), "{}.1".format(line)) != "#":
self.text.insert("{}.0".format(line), "#")
else:
self.text.delete("{}.0".format(line), "{}.1".format(line))
except tkinter.TclError:
# Get the line number of the current cursor position
insert = self.text.index("insert").split(".")[0]
# Comment or uncomment the current line
if self.text.get("{}.0".format(insert), "{}.1".format(insert)) != "#":
self.text.insert("{}.0".format(insert), "#")
else:
self.text.delete("{}.0".format(insert), "{}.1".format(insert))
return "break"
if __name__ == "__main__":
Window().mainloop()