[英]How to configure default Double Mouse Click behavior in Tkinter text widget?
在tkinter文本小部件上,双击的默认行为是选择鼠标下的文本。
该事件将选择“”(空格)字符之间的所有字符。
所以 - 假设文本小部件有: 1111111 222222
双击第一个单词(全部1
)将只选择它(并双击2
单词将选择它)
我想有一个类似的行为,但添加额外的字符作为工作分隔符(例如, .
, (
, )
)当前,如果文本有111111.222222
- 双击文本上的任何地方将突出显示所有字符(不会分隔单词通过.
)
有办法吗?
双击定义为选择光标下的“单词”。 如果您想要更改所有文本小部件的默认行为,tkinter可以告诉它什么是“单词”字符。 如果您更改tkinter认为是“单词”的内容,则可以通过双击更改所选内容。 这要求我们直接调用tkinter所基于的内置tcl解释器。
注意:这也会影响窗口小部件的其他方面,例如用于将光标移动到单词的开头或结尾的键绑定。
这是一个例子:
import tkinter as tk
def set_word_boundaries(root):
# this first statement triggers tcl to autoload the library
# that defines the variables we want to override.
root.tk.call('tcl_wordBreakAfter', '', 0)
# this defines what tcl considers to be a "word". For more
# information see http://www.tcl.tk/man/tcl8.5/TclCmd/library.htm#M19
root.tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_.,]')
root.tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_.,]')
root = tk.Tk()
set_word_boundaries(root)
text = tk.Text(root)
text.pack(fill="both", expand=True)
text.insert("end", "foo 123.45,678 bar")
root.mainloop()
如果您不想影响除一个小部件之外的任何小部件,或者不想影响依赖于“单词”定义的tkinter的其他方面,您可以创建自己的绑定以选择您想要的任何内容。
要记住的重要一点是,绑定应该返回字符串"break"
,以防止双击的默认行为:
def handle_double_click(event):
<your code for selecting whatever you want>
return "break"
...
text.bind("<Double-1>", handle_double_click)
为了实现这一点,文本小部件有一个search
方法,可以在文本中向后和向前搜索给定的字符串或正则表达式。
有办法吗?
当然,甚至没有一种方式。 但无论如何 - 我们需要为Text
小部件设置一个自定义类,所以让我们开始吧:
class CustomText(tk.Text):
def __init__(self, parent, delimiters=[]):
tk.Text.__init__(self, parent)
# test text
self.insert('1.0', '1111111 222222'
'\n'
'1111111.222222'
'\n'
'1111111.222222,333333'
'\n'
'444444444444444444')
# binds
self.bind('<Double-1>', self.on_dbl_click)
self.bind('<<Selection>>', self.handle_selection)
# our delimiters
self.delimiters = ''.join(delimiters)
# stat dictionary for double-click event
self.dbl_click_stat = {'clicked': False,
'current': '',
'start': '',
'end': ''
}
可选的delimiters
有两个选项:
如果出现分隔符,我们可以依赖RegEx search
分隔符。
如果分隔符中省略我们可以依靠内置的表情,尤其是那两个(正则表达式样字边界): wordstart
和wordend
。 根据文件 :
wordstart
和wordend
移动当前字的索引的开始(结束)。 单词是字母,数字和下划线的序列,或单个非空格字符。
逻辑很简单 - 当双击发生时 - 我们跟踪此事件并将索引存储在字典中。 之后我们处理选择的更改,并相应地采取选择的选项(见上文)。
这是一个完整的片段:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class CustomText(tk.Text):
def __init__(self, parent, delimiters=[]):
tk.Text.__init__(self, parent)
# test text
self.insert('1.0', '1111111 222222'
'\n'
'1111111.222222'
'\n'
'1111111.222222,333333'
'\n'
'444444444444444444')
# binds
self.bind('<Double-1>', self.on_dbl_click)
self.bind('<<Selection>>', self.handle_selection)
# our delimiters
self.delimiters = ''.join(delimiters)
# stat dictionary for double-click event
self.dbl_click_stat = {'clicked': False,
'current': '',
'start': '',
'end': ''
}
def on_dbl_click(self, event):
# store stats on dbl-click
self.dbl_click_stat['clicked'] = True
# clicked position
self.dbl_click_stat['current'] = self.index('@%s,%s' % (event.x, event.y))
# start boundary
self.dbl_click_stat['start'] = self.index('@%s,%s wordstart' % (event.x, event.y))
# end boundary
self.dbl_click_stat['end'] = self.index('@%s,%s wordend' % (event.x, event.y))
def handle_selection(self, event):
if self.dbl_click_stat['clicked']:
# False to prevent a loop
self.dbl_click_stat['clicked'] = False
if self.delimiters:
# Preserve "default" selection
start = self.index('sel.first')
end = self.index('sel.last')
# Remove "default" selection
self.tag_remove('sel', '1.0', 'end')
# search for occurrences
occurrence_forward = self.search(r'[%s]' % self.delimiters, index=self.dbl_click_stat['current'],
stopindex=end, regexp=True)
occurrence_backward = self.search(r'[%s]' % self.delimiters, index=self.dbl_click_stat['current'],
stopindex=start, backwards=True, regexp=True)
boundary_one = occurrence_backward + '+1c' if occurrence_backward else start
boundary_two = occurrence_forward if occurrence_forward else end
# Add selection by boundaries
self.tag_add('sel', boundary_one, boundary_two)
else:
# Remove "default" selection
self.tag_remove('sel', '1.0', 'end')
# Add selection by boundaries
self.tag_add('sel', self.dbl_click_stat['start'], self.dbl_click_stat['end'])
root = tk.Tk()
text = CustomText(root)
text.pack()
root.mainloop()
总之,如果你真的不关心分隔符,而是关于单词 - 第二种选择是好的,否则 - 第一种选择。
更新:
非常感谢@Bryan Oakley指出'break'
break'-string阻止了默认行为,所以代码可以简化为一个回调,不再需要<<Selection>>
:
...
def on_dbl_click(self, event):
if self.delimiters:
# click position
current_idx = self.index('@%s,%s' % (event.x, event.y))
# start boundary
start_idx = self.search(r'[%s\s]' % self.delimiters, index=current_idx,
stopindex='1.0', backwards=True, regexp=True)
# quick fix for first word
start_idx = start_idx + '+1c' if start_idx else '1.0'
# end boundary
end_idx = self.search(r'[%s\s]' % self.delimiters, index=current_idx,
stopindex='end', regexp=True)
else:
# start boundary
start_idx = self.index('@%s,%s wordstart' % (event.x, event.y))
# end boundary
end_idx = self.index('@%s,%s wordend' % (event.x, event.y))
self.tag_add('sel', start_idx, end_idx)
return 'break'
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.