How can I program two simultaneous key press events in tkinter to move a canvas item diagonally using a dictionary of keypress events?
下面是在画布上移动方块的代码。
它捕获箭头键按下事件并向上、向下、向左和向右移动方块。一次按两个箭头(例如向上和向左)不会诊断性地移动方块。相反,它仅沿所需的两个方向之一移动。
如何更改此代码以实现正方形的平滑对角线移动。感谢您的宝贵时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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 的"前面"实际发生外部刺激。因此,"我如何对事件进行编程"部分是用这种方法完成的:
1 2 3 4 5 6 7 | 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 代码是按顺序执行的。没有简单的方法可以同时实例化两个事件。简而言之,您的代码必须以某种方式模拟/检测近乎同时发生的事件,因为它本质上是一个顺序处理器。
正如 Bryan Oakley 在其他帖子中很好解释的那样,UI 检测应牢记,持有 ArrowUp 和/或 ArrowLeft 可能会在现实中导致自动生成的 UI-* 事件序列,而不是一个\\的控制(过去使用 BIOS 键盘打字速率设置,一旦键盘检测到按键被按下,负责自动重复击键......还没有结束......)
如何阅读输入刺激
Tkinter 有一组强大的 MVC-Controller-Part 方法来处理(自然的 UI 检测和人工"注入"由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 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 配备了以下方法:
1 2 3 4 5 6 7 | # |<<_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) 的方向(不仅基于 {
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
我只是在玩同时按键,结果发现它们之间的间隔最多为 10 毫秒,因此如果您检查两个按钮是否在 20 毫秒内被按下,您可以认为它们是同时按下的。不过,我确信有一个更优雅的解决方案。