Skip to content Skip to sidebar Skip to footer

How Can I Program Two Simultaneous Key Press Events In Tkinter To Move A Canvas Item Diagonally Using A Dictionary Of Keypress Events?

Below is the code to move a square around the canvas. It captures the arrow key press events and moves the square up, down, left and right. Pressing two arrows at once (e.g. up and

Solution 1:

How can I program ... events

Program part: Tkinter can, on it's own, generate UI-event, without an external stimulus actually happening "in front" of the UI. So, "How can I program an event" part is done with this method:

self.event_generate( <eventNameId>, **args ) # fire STIMULUS without User-interaction
#                                            # triggers <eventNameId>
#                                            # **args allow to set <keyword>=<value>
#                                            #        pairs for Event-fields,
#                                            #        that are passed to anEventHANDLER
#                                            #        via an-<Event>-object ...
#                                            #        ref below ( not the system-assigned ones, sure )

Problems with Simultaneity:

As a principle, Tkinter / Python code is being executed sequentially. There is no simple way how to instantiate two events at the very same moment. Simply put, your code has to somehow mimic/detect the near-simultaneous events, because it is by nature a sequential processor.

As Bryan Oakley has well explained in some other post, the UI-detection shall bear in mind, that holding an ArrowUp and/or ArrowLeft may lead in reality into auto-generated sequence of UI-*events, outside of one's control ( old days with BIOS keyboard typematic rate setting, responsible for auto-repeating the key-stroke once a keyboard detects a key is held pressed ... are not over ... )

How to read the input-stimuli

Tkinter has a powerful set of MVC-Controller-Part methods for handling ( both naturally UI-detected and artificially "injected" by .event_generate() ) events. That will be important for other aspects of the task:

# eventInstanceMethods() bear many details about click/key/time/.widget()
#       <event>.char        on-{ <KeyPress> | <KeyRelease> }
#              .keysym      on-{ <KeyPress> | <KeyRelease> }
#              .keysym_num  on-{ <KeyPress> | <KeyRelease> }
#              .num         on-{ <Mouse-1>  | <Mouse-2> | ... } ? 4,5 == <MouseWheel>
#              .height      on-{ <Configure> }
#              .width       on-{ <Configure> }
#              .serial      <-- system-assigned Integer
#              .time        <-- system-assigned Integer ( .inc each msec )
#              .widget      <-- system-assigned <widget>-instance
#              .x           <-- system-assigned <Event>-in-<widget>-mouse-location.x
#              .y           <-- system-assigned <Event>-in-<widget>-mouse-location.y
#              .x_root      <-- system-assigned <Event>-on-<Screen>-mouse-location.x
#              .y_root      <-- system-assigned <Event>-on-<Screen>-mouse-location.y

For detecting such events, Tkinter is equipped with these methods:

#                      |<<_aNamedEVENT_>>|<<______________________________aHANDLER>>|
#                      |or               |                                          |
#                      |<<_VirtualEVENT>>|                                          |
#                      |                 |                                          |
.bind(                  "<KeyPress-Left>", self.__doWidgetBoundTaskSpecificHANDLER  )
.bind_class( "Button",  "<KeyPress-Left>", self.__doClass_BoundTaskSpecificHANDLER  )
.bind_all(              "<KeyPress-Left>", self.__doApplicBoundTaskSpecificHANDLER  )

How to program to move with dictionary

This is a green-field issue and if restricted to use a dictionary in MVC-Model-Part, there you go. After issues above, your Finite-State-Automaton (FSA) for direction ( based on not only a { <KeyPress> | <KeyRelease> } pair of a blind state-transitions' triggers, but on sequence of keys, with a TimeDOMAIN proximity handling and with an extended, single-key and dual-key-pressed state grammar { nil, Left, Up, Right, Down, Left&&Up, Left&&Dn, Right&&Up, Right&&Dn } and handling ) grows a bit complex, but for a first prototype, you may just change the dictionary assignment rules and start with something like this:

def on_keypress( event ):                          # keeping the Globals-style,
    global direction                               #         however shall be rather
    global x_vel                                   #         implemented in a Class-based
    global y_vel                                   #         manner

    direction     = dir_vel[event.keysym][0]       # ref. remark on more complex FSA
    x_vel        += dir_vel[event.keysym][1]
    y_vel        += dir_vel[event.keysym][2]

def on_keyrelease( event ):
    global direction
    global x_vel
    global y_vel
    x_vel        -= dir_vel[event.keysym][1]
    y_vel        -= dir_vel[event.keysym][2]
    if abs( x_vel * y_vel ) < 0.1:
        direction = None                          # ref. remark on more complex FSA

Solution 2:

I was just playing around with simultaneous keypresses and it turns out they're up to 10 ms apart, so if you check that two buttons are pressed within 20 ms of each other, you can consider them simultaneous. I'm sure there's a more elegant solution though.


Post a Comment for "How Can I Program Two Simultaneous Key Press Events In Tkinter To Move A Canvas Item Diagonally Using A Dictionary Of Keypress Events?"