【问题标题】:How to detect if a key is being held down in Tkinter?如何检测 Tkinter 中是否按下了某个键?
【发布时间】:2018-01-07 22:09:03
【问题描述】:

作为一名 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()

我的目标是在按下相应的键时让我的角色以恒定的速度移动(而不是一段时间后无法控制的快速移动)。

【问题讨论】:

标签: python python-3.x tkinter keypress


【解决方案1】:

对您的问题最直接的解决方案是避免在每次按键时添加一个值,而是设置一个常量值。

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 时保持上限和不变。

【讨论】:

  • 如果这对你有帮助,请接受这个答案。否则让我知道出了什么问题
【解决方案2】:

按住一个键本质上与重复按下该键相同。通过在 move_* 函数中添加/减去 self.x/self.y 属性,您正在做的是增加画布在每个函数调用中移动玩家精灵的数量(例如,从 1.5 到 3到 4.5 到 6 等,同时按住方向键)。

由于每次在“PlayerSprite”类下调用“move”时,画布都会将玩家移动 (self.x, self.y) 个单位,因此我们希望 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

此外,除了使用“停止”之外,您还可以添加“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)

(我将左右方向概括为 horz 以及将上下方向概括为 vert 以节省函数定义的数量。)

然后您可以创建将 self.x 值或 self.y 值分配为 0 的函数,这样一旦调用“move”,您的玩家就不会移动。

def stop_move_horz(self, evt):
    self.x = 0

def stop_move_vert(self, evt):
    self.y = 0

【讨论】:

    猜你喜欢
    • 2017-09-03
    • 2014-07-13
    • 1970-01-01
    • 2016-03-27
    • 1970-01-01
    • 2010-11-13
    • 2018-05-02
    • 2014-04-05
    • 2011-06-22
    相关资源
    最近更新 更多