0

I am trying to invoke default widget behavior on demand. Based on this answer I know a bit about bindtags, and taglists.

Let's say for example I have two buttons, and I want to have the bindtag behavior of a specific item in its taglist, of first button, whenever I left click on the right button:

import tkinter as tk

root = tk.Tk()

first_btn = tk.Button(root, text="1st")
second_btn = tk.Button(root, text="2nd")

second_btn.bind("<Button-1>", "expect the bindtag operation here")

first_btn.pack()
second_btn.pack()

root.mainloop()

I haven't specified command=... for first_btn as I need to have the very effect of button push for example with my second_btn binding.


or as another example, I want to have second_btn to invoke all(ideally of my choosing) bindtag callbacks of first_btn:

import tkinter as tk

root = tk.Tk()

first_btn = tk.Button(root, text="1st")
second_btn = tk.Button(root, text="2nd", command="first_btn bindtag callbacks")

first_btn.pack()
second_btn.pack()

root.mainloop()

Or, what are reference names(if any) to the operations attached to bindtags?

Nae
  • 14,209
  • 7
  • 52
  • 79
  • I want to do exactly whatever's a widget by default doing on demand. As in I want to see `first_btn` pushed in and back out when I left click on `second_btn`. – Nae Dec 20 '17 at 09:44
  • So you just curious about the [`relief`](http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/relief.html) of the button? You able to control it in your callback. Also, there's an [`invoke`](http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/button.html) method. – CommonSense Dec 20 '17 at 10:09
  • @CommonSense These are only workarounds to what I exactly want. I want to learn this not only for buttons, but for all default widget behavior. For example there's a bindtag that adds a new line for Text widget when Ctrl - O is pressed for example. Or you can resize treeview columns with mouse drag operations. TL;DR I don't want to mimic what bindtags do, I want to call them on demand to do whatever they're supposed to do. – Nae Dec 20 '17 at 10:18
  • 1
    There is `first_btn.event_generate('<...>')` to trigger a given event. – j_4321 Dec 20 '17 at 10:20
  • @j_4321 I tried it but it seems to only invoke the callback of `bind`. If you can make it invoke bindtag callbacks please let me know. – Nae Dec 20 '17 at 10:49
  • What do you mean by invoke 'bindtags'? – j_4321 Dec 20 '17 at 10:50
  • @j_4321 I mean it seem to only run the callback function defined for an event using `bind` only. – Nae Dec 20 '17 at 11:10
  • @j_4321 Hm actually no, it does works pretty well. For buttons I need to add button press and then to mimic delay and release. And it doesn't seem to work with menu though? – Nae Dec 20 '17 at 11:21
  • Do you mean it does not work with a `Menu` widget? – j_4321 Dec 20 '17 at 11:52
  • @j_4321 Yes. I'm trying to make items in menu to open the menu when clicked similar to behavior in [this question](https://stackoverflow.com/q/47895168/7032856) but I couldn't. Alt-V and Alt-v opens view menu but I when I generate it it doesn't open. – Nae Dec 20 '17 at 11:56
  • 1
    "Animated behavior" depends on the events, regardless of with which tags these events are tied. You can easily `event_generate` a sequence of `` and `` events with a time delay between to "animate" a click. A crude explanation is that under the hood of `tk` to animate these events, each event calls a related function (`tkButtonDown` and `tkButtonUp` respectively). Hence, the only way to animate click without any event and without any "working around" with `relief` is a direct call to the `tkButtonInvoke` function (`root.tk.call('::tk::ButtonInvoke', second_btn)`). – CommonSense Dec 20 '17 at 12:46
  • @CommonSense This seems it. For buttons this seems to do exactly what I want. – Nae Dec 20 '17 at 13:32
  • 1
    Same applies to all widgets! For example, you could see how interaction of `Menu` and of "Alt-keys" is binded [here](http://core.tcl.tk/tk/artifact/a626a377b36c07d9d6fd99ef8b41bef8fefef25f) (look up for `tk::TraverseToMenu` function). – CommonSense Dec 20 '17 at 15:30
  • @CommonSense Thanks a lot. This is very valuable. – Nae Dec 20 '17 at 15:37

1 Answers1

1

To invoke the callback bound to a given sequence (e.g. <Button-1>), you can do widget.event_generate(<sequence>, **kwargs). The optional keyword arguments can be x=0, y=0 for a <Button-1> event for instance. This will trigger all callbacks associated with the sequence (it does not matter whether they were bound with bind, bind_class or bind_all).

In the below example, when the second button is clicked, it lowers the first one as if it were clicked too:

import tkinter as tk

root = tk.Tk()

first_btn = tk.Button(root, text="1st")
second_btn = tk.Button(root, text="2nd", command=lambda: first_btn.event_generate('<Button-1>'))

first_btn.pack()
second_btn.pack()

root.mainloop()

However, when doing tests interactively from the command line, generating keyboard events often does not work because the widget does not have keyboard focus. So, with the following code, when clicking on the button, the View menu will open as if we had done Alt+v:

import tkinter as tk

root = tk.Tk()
menubar = tk.Menu(root)
viewMenu = tk.Menu(menubar, tearoff=0)
viewMenu.add_command(label="Item 1")
viewMenu.add_command(label="Item 2")
menubar.add_cascade(menu=viewMenu, label="View", underline=0)
root.config(menu=menubar)
tk.Button(root, text='Test', command=lambda: root.event_generate('<Alt-v>')).pack()
root.mainloop()

But when I do root.event_generate('<Alt-v>') from the IPython QtConsole, nothing happens. The workaround is to force keyboard focus before generating the event:

root.focus_force()
root.event_generate('<Alt-v>')
j_4321
  • 15,431
  • 3
  • 34
  • 61
  • Still can't make it work(nothing happens when the button is clicked) with or without `focus_force`. – Nae Dec 20 '17 at 13:16
  • @Nae The piece of code with the menu works for me when I run it, the menu opens both with `Alt-v` and when I click on the button. So I don't know where your problem comes from. I don't know if it is relevant, but I am using linux and tk 8.6.7. – j_4321 Dec 20 '17 at 13:21
  • I'm on Windows 7, python 3.6, and tk 8.6. `Alt-v` opens the menu but the button doesn't seem to do anything. I also tried with defined function that calls those and with lambda [,] way. – Nae Dec 20 '17 at 13:27