tkinter copy-pasting to Entry不会删除所选文本

时间:2017-10-08 18:53:05

标签: linux python-3.x tkinter

当我复制一些文本并粘贴(crtl + v)它在tkinter条目中时,如果有选定的文本,它将不会从条目中删除它。我在Linux(Mint)64位上。

我在这里复制" d"用(ctrl + c): enter image description here

然后我选择" b": enter image description here

现在我粘贴" d" (ctrl + v)到它上面但结果如下: enter image description here

首先:我想知道这是否是特定于Linux的错误或者它应该是怎么回事?

第二:我正在考虑使用validatecommand解决此问题,但我遇到了另一个问题:

如果我要删除命令中的选定文本,我必须知道条目中的选择索引。否则,如果在光标之前和之前有所选文本的多个实例,我将不知道要删除哪一个并用新文本替换。因为光标可能位于选区的任一侧(取决于用户是否在文本上从右向左或从左向右拖动鼠标)。

现在有没有办法在条目中获取 选择 索引 ?或解决此问题的其他方法?

以下是一些代码示例:

import tkinter as tk

root = tk.Tk()

def validation(after_text, before_text, validation_text, cursor_index):
    cursor_index = int(cursor_index)
    print('cursor index:', cursor_index)
    print('text after change:', after_text)
    print('text before change:', before_text)
    print('text in need of validation:', validation_text)

    try:
        selection = root.selection_get()
    except:
        selection = ''
    print('selection:', selection)

    # EXAMPLE:

    # validation_text = 'd'
    # before text = "bb"

    # now if someone dragged from right to left on the 2nd b:
    # cursor position will be 1 (imagine | as the cursor): 'b|b' 
    # cursor_index = 1
    # after_text = 'bdb' --> but should be 'bd'

    # now if someone dragged from left to right on the 2nd b:
    # cursor position will be 2 (imagine | as the cursor): 'bb|' 
    # cursor_index = 2
    # after_text = 'bbd' --> but should be 'bd'

    # so there is no way for me to know which of these b's I have
    # to replace with d based on cursor position alone. I have to
    # know the index of selection itself in before_text to be
    # able to replace the text properly.

    # I don't know how to get that.

    return True

txtvar = tk.StringVar(value = 'a-b-c-d-e')
entry = tk.Entry(root, textvariable = txtvar)
font = tk.font.Font(family = entry.cget('font'), size = -50)

entry.config(validate = 'all',
    vcmd = (root.register(validation),'%P', '%s', '%S', '%i'),
    font = font)
entry.pack()

root.mainloop()

1 个答案:

答案 0 :(得分:5)

这不是一个错误。如果它是一个bug,有人会注意到它并在十年前修复它。 Tkinter已经存在很长时间了,像这样的基本事情不会被忽视。

在粘贴X11的系统上执行粘贴不会在粘贴之前删除所选文本。以下是我写这篇文章时的实际底层Tcl代码:

bind Entry <<Paste>> {
    global tcl_platform
    catch {
        if {[tk windowingsystem] ne "x11"} {
            catch {
                %W delete sel.first sel.last
            }
        }
        %W insert insert [::tk::GetSelection %W CLIPBOARD]
        tk::EntrySeeInsert %W
    }
}

使用验证功能绝对是解决此问题的错误方法。验证特别针对名称所暗示的内容:验证。正确的解决方案是创建自己的<<Paste>>事件绑定。

  

现在有办法在条目中获取选择索引吗?或解决此问题的其他方法?

是的,条目小部件具有特殊索引sel.first,它表示选择中的第一个字符,sel.last表示选择后的字符。

将上述代码转换为python(减去x11的检查)的相当字面翻译将如下所示:

def custom_paste(event):
    try:
        event.widget.delete("sel.first", "sel.last")
    except:
        pass
    event.widget.insert("insert", event.widget.clipboard_get())
    return "break"

要将此应用于特定窗口小部件,请绑定到该窗口小部件的<<Paste>>事件:

entry = tk.Entry(...)
entry.bind("<<Paste>>", custom_paste)

如果您想要执行适用于每个Entry窗口小部件的单个绑定,请使用bind_class

root = tk.Tk()
...
root.bind_class("Entry", "<<Paste>>", custom_paste)