0

I'd like to assign a key combination to do the same as an existing standard binding.

Specifically, for a tkinter.Listbox, make Ctrl-A select all (same as Ctrl-/) and also do the same for Ctrl+<the keys corresponding to those two in my national keyboard layout>.

Since the subroutines doing that exist at Tk level and not on Python level, I cannot just .bind() to them as I would do for a Python function.

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • Your question includes some statements but doesn't ask a question. Also, is it really important to use the existing bindings, or is it sufficient to create your own "select all" function? – Bryan Oakley Feb 25 '18 at 15:29
  • @BryanOakley Reimplementing would be an inferior solution. You seem to have understood what the question is despite the conspitious lack of question marks. – ivan_pozdeev Feb 25 '18 at 16:03
  • Reimplementing is trivial. It's a couple lines of code. – Bryan Oakley Feb 25 '18 at 16:08
  • 1
    The fact that it seems like I understood the question is irrelevant. Apparently I got lucky with my guess. The title asks how to reuse an existing binding (in general terms), but the body seems to be asking how to specifically implement the "select all" feature. It's unclear exactly what problem you're needing help with. Do you really need to learn how to reuse any existing standard binding, only reuse the "select all" binding for the listbox, or simply make "" do the same thing as "select all". – Bryan Oakley Feb 25 '18 at 16:23
  • 1
    @BryanOakley _"I'd like to assign a key combination to do the same as an existing standard binding."_ The specific problem at hand is given as an example so that answers could have code more relevant to my case and may include additional details for that specific case if there happen to be any. A solution that would only work in the specific case of "`Ctrl-A` for "select all"" is too useless to be worth asking for specifically. – ivan_pozdeev Feb 25 '18 at 16:34

3 Answers3

1

If you want to duplicate any existing binding, the first thing you need to do is understand what the original sequence is bound to. In this case, the binding <Control-/> is bound to the binding tag "Listbox" (the name of the internal widget class)

The first step is to get the existing binding by making a raw call to the Tcl interpreter:

func = root.call("bind", "Listbox", "<Control-/>")

The second step is to associate that original function to the new key combination:

root.call("bind", "Listbox", "<Control-A>", func)

Note: the above will associate the binding with all Listbox widgets rather than just a specific listbox.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

You could find out from the source code what command is used for the specific binding.


For Listbox's specific case, Control-/ event first activates a virtual event, <<SelectAll>>. Which then calls the Tcl command for listbox'es Tcl procedure tk::ListboxSelectAll.

Let's assign Control-A to mimic Control-/.

Generate <<SelectAll>> event, so that it calls whatever it's supposed to call:

lb.bind('<Control-Key-a>', lambda event: lb.event_generate('<<SelectAll>>'))

Or you could go directly call what <<SelectAll>> eventually calls:

lb.bind('<Control-Key-a>', lambda event: lb.tk.call('tk::ListboxSelectAll', lb))

You may want to bind for all Listbox objects:

lb.bind_class(lb.winfo_class(), '<Control-Key-a>',
                    lambda event: lb.tk.call('tk::ListboxSelectAll', lb))

A complete example:

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
except ImportError:
    import Tkinter as tk


if __name__ == '__main__':
    root = tk.Tk()
    lb = tk.Listbox(root)
    for i in range(4):
        lb.insert('end', i)
    lb.bind_class(lb.winfo_class(), '<Control-Key-a>',
                        lambda event: lb.tk.call('tk::ListboxSelectAll', lb))
    # assign anything but "single" or "browse"
    # so that the effect is obvious
    lb['selectmode'] = 'asd'
    lb.pack()
    tk.mainloop()

Finally, note that you may want to bind to <Control-Key-A>, too, so the binding works even with Caps Lock on.

This will effectively bind Ctrl-Shift-A, too, which you may or may not want. Conversely, with Caps Lock on, Tk would interpret Ctrl-Shift-A as Ctrl-A.

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
Nae
  • 14,209
  • 7
  • 52
  • 79
  • I'm going to choose `lb.event_generate('<>')` because it makes the fewest assumptions. Thank you. – ivan_pozdeev Feb 25 '18 at 16:21
  • Hmm, `lb.event_generate('<>')` has no effect. Had to go with Bryan's suggestion due to this, even though it makes an unnecessary assumption (that the default binding was not redefined). – ivan_pozdeev Feb 28 '18 at 12:28
  • @ivan_pozdeev I left it deliberately as a capital letter to point out that one needs to bind both lower and uppercase letter, if one wants such functionality. – Nae Feb 28 '18 at 12:47
  • What's the point of binding with capital letter? I don't need to bind `Ctrl-Shift-A`. If there is some other, hidden, reason, you should explain it explicitly. – ivan_pozdeev Feb 28 '18 at 12:49
  • 1
    @ivan_pozdeev The reason is whatever the reason ctrl-a selects all string with or without capslock is on. – Nae Feb 28 '18 at 12:50
-2

I've asked a very similar question myself.

You can simply generate the event that you want to mimic.


Generating Control-/ event so that it handles everything from that point onward:

lb.bind('<Control-Key-a>',
                    lambda event:lb.event_generate('<Control-Key-slash>'))

A complete example:

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
except ImportError:
    import Tkinter as tk


if __name__ == '__main__':
    root = tk.Tk()
    lb = tk.Listbox(root)
    for i in range(4):
        lb.insert('end', i)

    lb.bind('<Control-Key-a>',
                    lambda event:lb.event_generate('<Control-Key-slash>'))
    # assign anything but "single" or "browse"
    # so that the effect is obvious
    lb['selectmode'] = 'asd'
    lb.pack()
    tk.mainloop()
Nae
  • 14,209
  • 7
  • 52
  • 79