如何在 tkinter 中对两个同时的按键事件进行编程,以使用按键事件字典对角移动画布项目? [英] How can I program two simultaneous key press events in tkinter to move a canvas item diagonally using a dictionary of keypress events?

查看:18
本文介绍了如何在 tkinter 中对两个同时的按键事件进行编程,以使用按键事件字典对角移动画布项目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是在画布周围移动正方形的代码.它捕获箭头键按下事件并向上、向下、向左和向右移动方块.一次按下两个箭头(例如向上和向左)不会对角移动方块.相反,它仅在所需的两个方向之一移动.

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 left) doesn't move the square diagnonally. Instead it is moved in only one of the two directions required.

如何更改此代码以实现正方形的平滑对角移动.感谢您抽出宝贵时间.

How can I alter this code to enable a smooth diagonal movement of the square. Thank you for your time.

from tkinter import *

x = 10
y = 10
a = 100
b = 100
direction = None

def move():
    global x_vel
    global y_vel
    global direction
    if direction is not None:
        canvas1.move(rect, x_vel,y_vel)
    window.after(33,move)

def on_keypress(event):
    global direction
    global x_vel
    global y_vel
    direction, x_vel, y_vel = dir_vel[event.keysym]

def on_keyrelease(event):
    global direction
    direction = None

dir_vel = {
    "Left": ("left", -5, 0),
    "Right": ('right', 5, 0),
    "Down": ('down', 0, 5),
    "Up": ('up', 0, -5),}


window = Tk()
window.geometry("400x200")

move()

#canvas and drawing
canvas1=Canvas(window, height = 200, width = 400)
canvas1.grid(row=0, column=0, sticky=W)
coord = [x, y, a, b]
rect = canvas1.create_rectangle(*coord, outline="#fb0", fill="#fb0")

#capturing keyboard inputs and assigning to function
window.bind_all('<KeyPress>', on_keypress)
window.bind_all('<KeyRelease>', on_keyrelease)

推荐答案

我如何编程...事件

程序部分:Tkinter 可以自行生成 UI 事件,而无需在 UI 的前面"实际发生外部刺激.所以,如何编写事件"部分是用这个方法完成的:

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 )

同时性问题:

原则上,Tkinter/Python 代码是按顺序执行的.没有简单的方法可以在同一时刻实例化两个事件.简而言之,您的代码必须以某种方式模拟/检测几乎同时发生的事件,因为它本质上是一个顺序处理器.

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.

正如 Bryan Oakley 在其他帖子中很好地解释的那样,UI 检测应牢记,持有 ArrowUp 和/或 ArrowLeft 可能会导致现实自动生成的 UI-* 事件序列,不受控制(过去使用 BIOS 键盘输入速率设置,负责在键盘检测到某个键被按下时自动重复击键......还没有结束...)

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 ... )

Tkinter 拥有一组强大的 MVC-Controller-Part 方法来处理(自然检测到 UI 和由 .event_generate() 人工注入")事件.这对于任务的其他方面很重要:

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

为了检测此类事件,Tkinter 配备了以下方法:

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  )

如何编程让字典移动

这是一个未解决的问题,如果限制在 MVC-Model-Part 中使用字典,那么就可以了.在上述问题之后,您的有限状态自动机 (FSA) 用于方向(不仅基于 { | } 一对盲状态转换的触发器,但在按键序列上,具有 TimeDOMAIN 邻近处理和扩展的单键和双键按下状态语法 { nil, Left, Up&&&&Dn&&Up, Right&&Dn } 和处理 ) 变得有点复杂,但是对于第一个原型,您可能只需更改字典分配规则并从以下内容开始:

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

这篇关于如何在 tkinter 中对两个同时的按键事件进行编程,以使用按键事件字典对角移动画布项目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆