2

I have multiple buttons in tkinter window, but on a computer we only have one mouse pointer at one location at a time, how can I multi-press buttons simultaneously just like on a touch screen?

Here is a sample code of what I am trying to do,

#!/usr/bin/python
import Tkinter as Tk

def button_pressed_cb(event):
    print("You pressed " + event.widget['text'])

def button_release_cb(event):
    print("You released " + event.widget['text'])

root = Tk.Tk()
button1 = Tk.Button(root, text="button1")
button1.pack()
button2 = Tk.Button(root, text="button2")
button2.pack()

button1.bind("<ButtonPress>", button_pressed_cb)
button1.bind("<ButtonRelease>", button_release_cb)
button2.bind("<ButtonPress>", button_pressed_cb)
button2.bind("<ButtonRelease>", button_release_cb)

root.mainloop()

After executing this, I get a sample output of something like this,

You pressed button1
You released button1
You pressed button2
You released button2

What I want to achieve is to be able to generate events which occur in this sequence,

You pressed button1
You pressed button2
You released button2
You released button1

Does anybody know what's the best way to achieve this? Thank you so much.

2
  • Are you looking for code to synthesize events? Or just to construct them, without dispatching them through the event loop? Or to just call the handlers directly? Or to handle multitouch events even though Tkinter doesn't support them? Or…? Commented Apr 18, 2018 at 3:44
  • Yes, essentially I want to handle multi touch events even though Tkinter doesn't support them. Since using mouse we are limited to only clicking one button at one time, so need a solution which can enable me to handle any two button presses together or 3 button presses together etc. Commented Apr 18, 2018 at 8:44

2 Answers 2

3

If you just want to call the callbacks, you can do that—they're ordinary Python functions, after all.

If you're asking how to create an event and dispatch it, you can use the event_generate method. Unfortunately, I don't know of any tkinter documentation for this, so you have to look at the Tcl/Tk docs instead. But the basic idea is:

def hit_the_buttons():
    button1.event_generate('<ButtonPress>', when='tail')
    button2.event_generate('<ButtonPress>', when='tail')
    button2.event_generate('<ButtonRelease>', when='tail')
    button1.event_generate('<ButtonRelease>', when='tail')

The when argument puts the events at the end of the event queue rather than processing them immediately, so they won't interfere with things like redraws or actual mouse events. It isn't really necessary here; I included it mainly to show how to map one of those Tcl arguments (-when tail) to tkinter.

If you want this to go through the normal dispatch instead of directly to the buttons, you can call root.event_generate and pass x and y arguments that will hit-test as inside the buttons.

Sign up to request clarification or add additional context in comments.

Comments

2

Ok, I found a solution. I put in extra checks that if user presses and holds ctrl before clicking a button, that button will latch on to that pressed state. Then user can go and click other button etc. User can click that button again to unlatch it.

I also added a hover message as well for user to tell the user they can use ctrl to latch a button and achieve multikey presses.

Here is the new code,

#!/usr/bin/python
import Tkinter as Tk

class HoverInfo(Tk.Menu):
    def __init__(self, parent, text):
        Tk.Menu.__init__(self, parent, tearoff=0, 
                         background='light goldenrod yellow')

        self.__displayed = False
        for line in text.split('\n'):
            self.add_command(label=line)

        parent.bind("<Enter>", self.__display)
        parent.bind("<Leave>", self.__remove)

    def __display(self, event):
        if event.widget['state'] == Tk.NORMAL:
            if not self.__displayed:
                self.__displayed = True
                self.post(event.x_root+5, event.y_root+5)

    def __remove(self, event):
        if self.__displayed:
            self.__displayed = False
            self.unpost()

CONTROL = 4

def button_pressed_cb(event):
    if event.widget['state'] != Tk.DISABLED:
        print("You pressed " + event.widget['text'])
        if (event.state & CONTROL) == CONTROL:
            event.widget.config(state=Tk.DISABLED)
            event.widget.config(relief=Tk.SUNKEN)

def button_release_cb(event):
    if (event.state & CONTROL) != CONTROL:
        print("You released " + event.widget['text'])
        event.widget.config(state=Tk.NORMAL)
        event.widget.config(relief=Tk.RAISED)


root = Tk.Tk()
button1 = Tk.Button(root, text="button1")
button1.pack()
button2 = Tk.Button(root, text="button2")
button2.pack()

button1.bind("<ButtonPress>", button_pressed_cb)
button1.bind("<ButtonRelease>", button_release_cb)
button2.bind("<ButtonPress>", button_pressed_cb)
button2.bind("<ButtonRelease>", button_release_cb)

HoverInfo(button1, "Hint:\nYou can hold Ctrl key before\nclicking a button to latch it.")

root.mainloop()

Now, this is how the hover message looks, https://ibb.co/dEb6Bn

And when user holds ctrl key and clicks button 1 and then button 2, then this is how it looks, https://ibb.co/eVzRBn

Now, I can easily generate this sequence of events,

You pressed button1
You pressed button2
You released button2
You released button1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.