如何在Tkinter文本小部件中配置默认​​的双鼠标单击行为?

时间:2017-06-19 08:07:15

标签: python python-3.x tkinter

在tkinter文本小部件上,双击的默认行为是选择鼠标下的文本。

该事件将选择“”(空格)字符之间的所有字符。

所以 - 假设文本小部件有: 1111111 222222 双击第一个单词(所有1)将仅选择它(双击2单词将选择它)

我希望有类似的行为,但添加额外的字符作为工作分隔符(例如.()) 目前,如果文字有111111.222222 - 双击文字的任何地方都会突出显示所有字符(不会按.分隔字词)

有办法吗?

2 个答案:

答案 0 :(得分:2)

Changing what is a 'word'

The double click is defined to select the 'word' under the cursor. If you are wanting to change the default behavior for all text widgets, tkinter has a way to tell it what is a "word" character. If you change what tkinter thinks is a "word", you change what gets selected with a double-click. This requires that we directly call the built-in tcl interpreter upon which tkinter is based.

Note: this will affect other aspects of the widget as well, such as key bindings for moving the cursor to the beginning or ending of a word.

Here's an example:

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

Custom key binding

If you do not want to affect any widget except one, or do not want to affect other aspects of tkinter that depend on the definition of a 'word', you can create your own binding to select whatever you want.

The important thing to remember is that your binding should return the string "break" in order prevent the default behavior for double-click:

def handle_double_click(event):
    <your code for selecting whatever you want>
    return "break"
...
text.bind("<Double-1>", handle_double_click)

To facilitate this, the text widget has a search method that makes it possible to search backwards and forwards through the text for a given string or regular expression.

答案 1 :(得分:0)

  

有办法吗?

当然,甚至没有一种方式。但无论如何 - 我们需要为@timestamp小部件设置一个自定义类,所以让我们开始吧:

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': '' } 会导致两个选项:

  1. 如果出现分隔符,我们可以依赖RegEx search作为分隔符。

  2. 如果省略分隔符,我们可以依赖内置表达式,尤其是那两个(类似RegEx的词边界):delimiterswordstart。根据{{​​3}}:

      

    wordendwordstart将索引移动到当前单词的开头(结尾)。单词是字母,数字和下划线的序列,或单个非空格字符。

  3. 逻辑很简单 - 当双击发生时 - 我们跟踪此事件并将索引存储在字典中。之后我们处理选择的更改,并相应地采取选择的选项(见上文)。

    这是一个完整的代码段:

    wordend

    总之,如果你真的不关心分隔符,而是关于单词 - 第二种选择是好的,否则 - 第一种选择。

    更新

    非常感谢@Bryan Oakley指出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() - 字符串阻止了默认行为,因此代码可以缩短为只有一个回调,不再需要'break'

    <<Selection>>