如何检测 Tkinter 中是否按下了某个键?
How to detect if a key is being held down in Tkinter?
作为 Python 的新手,我曾尝试编写自己的游戏以开始,并参考了指南的建议。但是,对于这个游戏,我试图检测何时持续按住某个键而不是仅仅按下。我正在使用的当前代码不会让角色移动,并且如果没有实现 halt(self, evt)
代码,会导致飞船在按下按钮足够长的时间后不受控制地加速。
from tkinter import *
import random
import time
class Game:
def __init__(self):
self.tk = Tk()
self.tk.title("Shooter")
self.tk.resizable(0, 0)
self.tk.wm_attributes("-topmost", 1)
self.canvas = Canvas(self.tk, width=500, height=1000, highlightthickness=0)
self.canvas.pack()
self.tk.update()
self.canvas_height = 1000
self.canvas_width = 500
self.bg = PhotoImage(file="background.gif")
w = self.bg.width()
h = self.bg.height()
for x in range(0, 5):
for y in range(0, 10):
self.canvas.create_image(x * w, y * h, \
image=self.bg, anchor='nw')
self.sprites = []
self.running = True
def mainloop(self):
while 1:
if self.running == True:
for sprite in self.sprites:
sprite.move()
self.tk.update_idletasks()
self.tk.update()
time.sleep(0.01)
class Coords:
def __init__(self, x1=0, y1=0, x2=0, y2=0):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
class Sprite:
def __init__(self, game):
self.game = game
self.endgame = False
self.coordinates = None
def move(self):
pass
def coords(self):
return self.coordinates
class PlayerSprite(Sprite):
def __init__(self, game):
Sprite.__init__(self, game)
self.renderimage = [
PhotoImage(file="player_1.gif"),
PhotoImage(file="player_2.gif"),
PhotoImage(file="player_3.gif"),
PhotoImage(file="player_4.gif"),
]
self.image = game.canvas.create_image(225, 900, \
image=self.renderimage[0], anchor='nw')
self.x = 0
self.y = 0
self.velx = 0
self.current_image = 0
self.current_image_add = 1
self.shoot_timer = 0
self.last_time = time.time()
self.coordinates = Coords()
x_move = None
y_move = None
game.canvas.bind_all('<KeyPress-Left>', self.move_left)
game.canvas.bind_all('<KeyPress-Right>', self.move_right)
game.canvas.bind_all('<KeyPress-Up>', self.move_up)
game.canvas.bind_all('<KeyPress-Down>', self.move_down)
game.canvas.bind_all('<KeyPress-Left>', self.halt)
game.canvas.bind_all('<KeyPress-Right>', self.halt)
game.canvas.bind_all('<KeyPress-Up>', self.halt)
game.canvas.bind_all('<KeyPress-Down>', self.halt)
game.canvas.bind_all('<space>', self.shoot)
def move_left(self, evt):
x_move = self.x - 1.5
self.x = x_move
def move_right(self, evt):
x_move = self.x + 1.5
self.x = x_move
def move_up(self, evt):
y_move = self.y - 1.5
self.y = y_move
def move_down(self, evt):
y_move = self.y + 1.5
self.y = y_move
def halt(self, evt):
time.sleep(0.01)
if x_move < 0:
x_move = -1.5
elif x_move > 0:
x_move = 1.5
elif y_move < 0:
y_move = -1.5
elif y_move > 0:
y_move = 1.5
def shoot(self, evt):
print("Placeholder")
def move(self):
self.game.canvas.move(self.image, self.x, self.y)
def coords(self):
xy = self.game.canvas.coords(self.image)
self.coordinates.x1 = xy[0]
self.coordinates.y1 = xy[1]
self.coordinates.x2 = xy[0] + 24
self.coordinates.y2 = xy[1] + 32
return self.coordinates
g = Game()
sp = PlayerSprite(g)
g.sprites.append(sp)
g.mainloop()
我的目标是让我的角色在按下相应的键时以恒定的速度移动(而不是在一段时间后不受控制地快速移动)。
对于您的问题,最直接的解决方案是避免在每次按键时都添加一个值,而是设置一个常量值。
def move_left(self, evt):
x_move = -5
self.x = x_move
运动虽然会失去动力,但会保持不变。否则,您可以创建最大值。像这样:
def move_left(self, evt):
int max_val_left = -10
if( self.x < max_val_left):
x_move = self.x - 1.5
self.x = x_move
从而迫使 self.x
在达到 max_val
时保持上限并保持不变。
按住一个键与重复按下该键本质上是一样的。通过在你的 move_* 函数中添加来自 self.x/self.y 属性的 to/subtracting 你正在做的是增加 canvas 将在每个中移动你的玩家精灵的数量函数调用(例如,从 1.5 到 3 到 4.5 到 6,等等,当您按住方向键时)。
因为每次在 "PlayerSprite" 下调用 "move" 时 canvas 都会将玩家移动 (self.x, self.y) 个单位 class,我们希望 self.x 和 self.y 为 0 或您想要的任何速度(以下代码中的 1.5)。因此,我们应该将其分配给一个常量值,而不是添加到 self.x 和 self.y:
def move_left(self, evt):
self.x = -1.5
def move_right(self, evt):
self.x = 1.5
def move_up(self, evt):
self.y = -1.5
def move_down(self, evt):
self.y = -1.5
此外,您可以不使用 "halt",而是包含 'KeyRelease-*' 绑定,以便在您停止按住方向键后停止播放器:
game.canvas.bind_all('KeyRelease-Left'>, self.stop_horz_move)
game.canvas.bind_all('KeyRelease-Right'>, self.stop_horz_move)
game.canvas.bind_all('KeyRelease-Up'>, self.stop_vert_move)
game.canvas.bind_all('KeyRelease-Down'>, self.stop_vert_move)
(我已经将左右方向概括为水平,将上下方向概括为垂直,以节省函数定义的数量。)
然后您可以创建将 self.x 值或 self.y 值分配给 0 的函数,这样您的玩家在调用 "move" 后就不会移动。
def stop_move_horz(self, evt):
self.x = 0
def stop_move_vert(self, evt):
self.y = 0
作为 Python 的新手,我曾尝试编写自己的游戏以开始,并参考了指南的建议。但是,对于这个游戏,我试图检测何时持续按住某个键而不是仅仅按下。我正在使用的当前代码不会让角色移动,并且如果没有实现 halt(self, evt)
代码,会导致飞船在按下按钮足够长的时间后不受控制地加速。
from tkinter import *
import random
import time
class Game:
def __init__(self):
self.tk = Tk()
self.tk.title("Shooter")
self.tk.resizable(0, 0)
self.tk.wm_attributes("-topmost", 1)
self.canvas = Canvas(self.tk, width=500, height=1000, highlightthickness=0)
self.canvas.pack()
self.tk.update()
self.canvas_height = 1000
self.canvas_width = 500
self.bg = PhotoImage(file="background.gif")
w = self.bg.width()
h = self.bg.height()
for x in range(0, 5):
for y in range(0, 10):
self.canvas.create_image(x * w, y * h, \
image=self.bg, anchor='nw')
self.sprites = []
self.running = True
def mainloop(self):
while 1:
if self.running == True:
for sprite in self.sprites:
sprite.move()
self.tk.update_idletasks()
self.tk.update()
time.sleep(0.01)
class Coords:
def __init__(self, x1=0, y1=0, x2=0, y2=0):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
class Sprite:
def __init__(self, game):
self.game = game
self.endgame = False
self.coordinates = None
def move(self):
pass
def coords(self):
return self.coordinates
class PlayerSprite(Sprite):
def __init__(self, game):
Sprite.__init__(self, game)
self.renderimage = [
PhotoImage(file="player_1.gif"),
PhotoImage(file="player_2.gif"),
PhotoImage(file="player_3.gif"),
PhotoImage(file="player_4.gif"),
]
self.image = game.canvas.create_image(225, 900, \
image=self.renderimage[0], anchor='nw')
self.x = 0
self.y = 0
self.velx = 0
self.current_image = 0
self.current_image_add = 1
self.shoot_timer = 0
self.last_time = time.time()
self.coordinates = Coords()
x_move = None
y_move = None
game.canvas.bind_all('<KeyPress-Left>', self.move_left)
game.canvas.bind_all('<KeyPress-Right>', self.move_right)
game.canvas.bind_all('<KeyPress-Up>', self.move_up)
game.canvas.bind_all('<KeyPress-Down>', self.move_down)
game.canvas.bind_all('<KeyPress-Left>', self.halt)
game.canvas.bind_all('<KeyPress-Right>', self.halt)
game.canvas.bind_all('<KeyPress-Up>', self.halt)
game.canvas.bind_all('<KeyPress-Down>', self.halt)
game.canvas.bind_all('<space>', self.shoot)
def move_left(self, evt):
x_move = self.x - 1.5
self.x = x_move
def move_right(self, evt):
x_move = self.x + 1.5
self.x = x_move
def move_up(self, evt):
y_move = self.y - 1.5
self.y = y_move
def move_down(self, evt):
y_move = self.y + 1.5
self.y = y_move
def halt(self, evt):
time.sleep(0.01)
if x_move < 0:
x_move = -1.5
elif x_move > 0:
x_move = 1.5
elif y_move < 0:
y_move = -1.5
elif y_move > 0:
y_move = 1.5
def shoot(self, evt):
print("Placeholder")
def move(self):
self.game.canvas.move(self.image, self.x, self.y)
def coords(self):
xy = self.game.canvas.coords(self.image)
self.coordinates.x1 = xy[0]
self.coordinates.y1 = xy[1]
self.coordinates.x2 = xy[0] + 24
self.coordinates.y2 = xy[1] + 32
return self.coordinates
g = Game()
sp = PlayerSprite(g)
g.sprites.append(sp)
g.mainloop()
我的目标是让我的角色在按下相应的键时以恒定的速度移动(而不是在一段时间后不受控制地快速移动)。
对于您的问题,最直接的解决方案是避免在每次按键时都添加一个值,而是设置一个常量值。
def move_left(self, evt):
x_move = -5
self.x = x_move
运动虽然会失去动力,但会保持不变。否则,您可以创建最大值。像这样:
def move_left(self, evt):
int max_val_left = -10
if( self.x < max_val_left):
x_move = self.x - 1.5
self.x = x_move
从而迫使 self.x
在达到 max_val
时保持上限并保持不变。
按住一个键与重复按下该键本质上是一样的。通过在你的 move_* 函数中添加来自 self.x/self.y 属性的 to/subtracting 你正在做的是增加 canvas 将在每个中移动你的玩家精灵的数量函数调用(例如,从 1.5 到 3 到 4.5 到 6,等等,当您按住方向键时)。
因为每次在 "PlayerSprite" 下调用 "move" 时 canvas 都会将玩家移动 (self.x, self.y) 个单位 class,我们希望 self.x 和 self.y 为 0 或您想要的任何速度(以下代码中的 1.5)。因此,我们应该将其分配给一个常量值,而不是添加到 self.x 和 self.y:
def move_left(self, evt):
self.x = -1.5
def move_right(self, evt):
self.x = 1.5
def move_up(self, evt):
self.y = -1.5
def move_down(self, evt):
self.y = -1.5
此外,您可以不使用 "halt",而是包含 'KeyRelease-*' 绑定,以便在您停止按住方向键后停止播放器:
game.canvas.bind_all('KeyRelease-Left'>, self.stop_horz_move)
game.canvas.bind_all('KeyRelease-Right'>, self.stop_horz_move)
game.canvas.bind_all('KeyRelease-Up'>, self.stop_vert_move)
game.canvas.bind_all('KeyRelease-Down'>, self.stop_vert_move)
(我已经将左右方向概括为水平,将上下方向概括为垂直,以节省函数定义的数量。)
然后您可以创建将 self.x 值或 self.y 值分配给 0 的函数,这样您的玩家在调用 "move" 后就不会移动。
def stop_move_horz(self, evt):
self.x = 0
def stop_move_vert(self, evt):
self.y = 0