【问题标题】:Do the "w" and "s" key bindings work differently in Tkinter?“w”和“s”键绑定在 Tkinter 中的工作方式不同吗?
【发布时间】:2019-06-20 19:40:58
【问题描述】:

我一直致力于将我使用 CodeSkulptor 为在线 Python 课程构建的游戏“Pong”翻译成使用 Tkinter 的“桌面”python 脚本,作为一种自学如何使用 Tkinter 的方法.除了左侧(玩家 1)桨外,我已经设法让整个游戏正常运行。我认为我的键绑定正确,因为右侧(玩家 2)桨按预期工作,因为当您按住“向上”或“向下”箭头键时,桨移动直到它达到上限或下限画布,或在任何一个键被释放时停止。我将按键传递给 keydown 和 keyup 处理程序,在那里我检查哪个键被按下/释放,并采取相应的行动。令我困惑的是,如果我将左桨移动映射到不同的键(例如“a”或“d”,或者“向上”或“向下”箭头),它会按预期工作,但它拒绝当我映射了“w”和“s”键时工作。有谁知道为什么会这样,或者我做错了什么?

我在下面提供的代码是我放在一起的一个基本示例,它演示了这个问题,以及我试图实现的桨运动(它几乎反映了我的 Pong 项目)。右侧桨叶正确移动,而左侧桨叶没有。提前感谢您的帮助!

from Tkinter import *
import random


WIDTH = 500
HEIGHT = 500
PAD_WIDTH = 10
PAD_HEIGHT = 80
HALF_PAD_WIDTH = PAD_WIDTH / 2
HALF_PAD_HEIGHT = PAD_HEIGHT / 2


class Example(Frame, object):
    def __init__(self, master):
        super(Example, self).__init__(master)

        self._paddle1_pos = 200
        self._paddle2_pos = 200
        self._paddle1_vel = 0
        self._paddle2_vel = 0

        self.initUI()

    def initUI(self):
        scn_cent_height = self.master.winfo_screenheight() // 2 - HEIGHT // 2
        scn_cent_width = self.master.winfo_screenwidth() // 2 - WIDTH // 2
        self.master.geometry("%sx%s+%s+%s" % (WIDTH, HEIGHT, scn_cent_width, scn_cent_height))
        self.master.minsize(WIDTH, HEIGHT)

        self.master.title("Example Pong Paddles")

        self._canvasFrame = Frame(self.master)
        self._canvasFrame.pack(expand=True, fill=BOTH)

        self._canvas = Canvas(self._canvasFrame, bg="black", highlightthickness=0, bd=0)
        self._canvas.pack(fill=BOTH, expand=True)
        self.update_idletasks()

        # Key handlers
        self.master.bind("<KeyPress>", self.keydown)
        self.master.bind("<KeyRelease>", self.keyup)

        while True:
            self._canvas.after(1)
            self._canvas.delete("all")
            self.draw()
            self._canvas.update()


    def draw(self):
        self._cheight = self._canvasFrame.winfo_height()
        self._cwidth = self._canvasFrame.winfo_width()

        # Draw mid line and gutters
        self._rline = self._canvas.create_line(self._cwidth / 2, 0, self._cwidth / 2, self._cheight, width=1, fill="White")
        self._mline = self._canvas.create_line(PAD_WIDTH, 0, PAD_WIDTH, self._cheight, width=1, fill="White")
        self._lline = self._canvas.create_line(self._cwidth - PAD_WIDTH, 0, self._cwidth - PAD_WIDTH, self._cheight, width=1, fill="White")

        # Update paddle's vertical position, keep paddle on the screen
        # Paddle 1 - Check height and update position
        if self._paddle1_pos + self._paddle1_vel >= HALF_PAD_HEIGHT and self._paddle1_pos + self._paddle1_vel <= HEIGHT - HALF_PAD_HEIGHT:
            self._paddle1_pos += self._paddle1_vel

        # Paddle 2 - Check height and update position
        if self._paddle2_pos + self._paddle2_vel >= HALF_PAD_HEIGHT and self._paddle2_pos + self._paddle2_vel <= HEIGHT - HALF_PAD_HEIGHT:
            self._paddle2_pos += self._paddle2_vel

        # Draw paddles
        self._p1paddle = self._canvas.create_line([HALF_PAD_WIDTH, self._paddle1_pos - HALF_PAD_HEIGHT],
                                                  [HALF_PAD_WIDTH, self._paddle1_pos + HALF_PAD_HEIGHT], width=PAD_WIDTH, fill="White")
        self._p2paddle = self._canvas.create_line([self._cwidth - HALF_PAD_WIDTH, self._paddle2_pos - HALF_PAD_HEIGHT],
                                                  [self._cwidth - HALF_PAD_WIDTH, self._paddle2_pos + HALF_PAD_HEIGHT], width=PAD_WIDTH, fill="White")


        # Draw paddles
        self._p1paddle = self._canvas.create_line([HALF_PAD_WIDTH, self._paddle1_pos - HALF_PAD_HEIGHT],
                                                  [HALF_PAD_WIDTH, self._paddle1_pos + HALF_PAD_HEIGHT], width=PAD_WIDTH, fill="White")
        self._p2paddle = self._canvas.create_line([self._cwidth - HALF_PAD_WIDTH, self._paddle2_pos - HALF_PAD_HEIGHT],
                                                  [self._cwidth - HALF_PAD_WIDTH, self._paddle2_pos + HALF_PAD_HEIGHT], width=PAD_WIDTH, fill="White")


    def keydown(self, key):
        key = key.keysym

        if key == "w":
            self._paddle1_vel = -10
        elif key == "s":
            self._paddle1_vel = 10
        elif key == "Up":
            self._paddle2_vel = -10
        elif key == "Down":
            self._paddle2_vel = 10


    def keyup(self, key):
        key = key.keysym

        if key == "w":
            self._paddle1_vel = 0
        elif key == "s":
            self._paddle1_vel = 0
        elif key == "Up":
            self._paddle2_vel = 0
        elif key == "Down":
            self._paddle2_vel = 0




def main():
    root = Tk()
    example = Example(root)
    root.mainloop()


if __name__ == '__main__':
    main()

【问题讨论】:

  • 你有大写锁定吗?如果是这样,'w''W' 就不一样了。
  • 你的代码对我来说工作得很好(Linux,python3.6)。
  • 我只是做了一个理智的检查以确保我之前没有使用大写锁定,并且得到了相同的结果。作为另一个健全性检查,我还尝试使用命令行(而不是 Spyder)运行它,并且在那里也得到了相同的结果。我在 MacOSX Mojave (10.14.3) 上运行 python 2.7,顺便说一句。也许 MacOS 有点奇怪? Fwiw,我的笔记本电脑上还连接了一个 Apple 无线魔术键盘,尽管我在该键盘和笔记本电脑的内置键盘上都对其进行了测试,没有任何区别。
  • 你确定是 2.7 吗?您的 tkinter 导入适用于 python3。如果您安装了诸如six 之类的兼容性包,则可以按照您拥有它们的方式使用它们。
  • @Novel,你说得对,我不小心打错了导入语句。谢谢你抓住那个。我在上面的代码中修复了它,并重新测试了它,但我仍然遇到了同样的问题。仅作为背景,我使用 Anaconda,我的根 python 安装是 2.7。

标签: python python-2.7 tkinter


【解决方案1】:

我只是想补充一下,为了其他遇到此问题的人的利益,我能够将它的来源追溯到我的 Anaconda 虚拟环境(Python 2 和 Python 3 各有一个)。在测试中,当专门使用这些环境时,我能够重现此问题,但在 Anaconda 之外使用 Python 2(mac 框架构建)或 Python 3(通过 Brew 安装)时无法重现它。我清除了两个 Anaconda 虚拟环境,以防万一那里出现问题,并重新构建了它们(即 Python 2 和 3 的全新安装),并且仍然能够重现此问题(无需安装任何其他模块)。我只能推测 Anaconda 安装的 python 与系统框架的一部分或单独安装的 python 之间存在一些不同。

另外,请注意,当我在@Novel 的上述评论中成功测试 Python 3 时,我无意中在系统 Python 3 上进行了测试,而不是在 Anaconda Python 3 虚拟环境中进行测试。

编辑: 事实证明,这个问题实际上是由于我的 python 安装使用的 Tkinter (Tcl/Tk) 版本造成的。我笔记本电脑上的 python 框架使用的是 Tk 8.5.9,而 Anaconda 安装使用的是 8.6.8。在测试中,我在带有 Tk 8.6.8 的 python 2.7.16 和 3.7.3 中运行我的 Pong 脚本时遇到了这个问题,但它在带有 Tk 8.5.9 的相同 python 版本上运行得非常好。我不确定这是 Tk 的问题,还是 Tk 8.6.8 和 MacOS 框架之间的一些不兼容(因为框架本身使用 8.5.9)。

【讨论】:

    【解决方案2】:

    您的代码非常混乱,需要重构,所以我这样做了。一些笔记:

    • 最大的注意事项是,与其清除屏幕并重新绘制所有内容,不如绘制一次对象然后简单地移动它们,它会更整洁,性能也会更好。
    • 使用 tkinter 的主循环;不要自己制作并手动更新。 update()update_idletasks() 方法是最后的手段;普通代码没有它们。改为通过after() 使用 tkinter 的主循环。这将使您的窗口更具反应性。
    • Python2 Tkinter 类是旧式的,不要通过添加对象继承来强制它们成为新式。
    • 画布框架没用,所以我把它去掉了。
    • 将键保存在字典中可以释放大量重复代码。
    • _ 为所有变量名添加前缀没有任何作用,并且难以阅读和输入;把它关掉。

    -

    try:
        import tkinter as tk # python3 detected
    except ImportError:
        import Tkinter as tk # python2 detected
    
    WIDTH = 500
    HEIGHT = 500
    PAD_WIDTH = 10
    PAD_HEIGHT = 80
    VELOCITY = 10
    HALF_PAD_WIDTH = PAD_WIDTH // 2
    HALF_PAD_HEIGHT = PAD_HEIGHT // 2
    
    P1_UP = 111 # Up arrow key
    P1_DOWN = 116 # Down arrrow key
    P2_UP = 25 # 'w' key
    P2_DOWN = 39 # 's' key
    
    class Example(tk.Frame):
        def __init__(self, master):
            tk.Frame.__init__(self, master)
    
            self.keys = {}
            self.initUI()
    
            # Key handlers
            self.master.bind("<KeyPress>", self.keydown)
            self.master.bind("<KeyRelease>", self.keyup)
    
            self.draw() # add the game loop to the mainloop
    
        def initUI(self):
            scn_cent_height = self.master.winfo_screenheight() // 2 - HEIGHT // 2
            scn_cent_width = self.master.winfo_screenwidth() // 2 - WIDTH // 2
            self.master.geometry("%sx%s+%s+%s" % (WIDTH, HEIGHT, scn_cent_width, scn_cent_height))
            self.master.minsize(WIDTH, HEIGHT)
    
            self.master.title("Example Pong Paddles")
    
            self.canvas = tk.Canvas(self, bg="black", highlightthickness=0, bd=0, width=WIDTH, height=HEIGHT)
            self.canvas.pack(fill=tk.BOTH, expand=True)
    
            # Draw mid line and gutters
            self.rline = self.canvas.create_line(WIDTH//2, 0, WIDTH//2, HEIGHT, width=1, fill="White")
            self.mline = self.canvas.create_line(PAD_WIDTH, 0, PAD_WIDTH, HEIGHT, width=1, fill="White")
            self.lline = self.canvas.create_line(WIDTH - PAD_WIDTH, 0, WIDTH - PAD_WIDTH, HEIGHT, width=1, fill="White")
    
            # Draw paddles
            self.p1paddle = self.canvas.create_line([HALF_PAD_WIDTH, HEIGHT//2 - HALF_PAD_HEIGHT],
                                                      [HALF_PAD_WIDTH, HEIGHT//2 + HALF_PAD_HEIGHT], width=PAD_WIDTH, fill="White")
            self.p2paddle = self.canvas.create_line([WIDTH - HALF_PAD_WIDTH, HEIGHT//2 - HALF_PAD_HEIGHT],
                                                        [WIDTH - HALF_PAD_WIDTH, HEIGHT//2 + HALF_PAD_HEIGHT], width=PAD_WIDTH, fill="White")
    
        def draw(self):
            if self.keys.get(P2_UP) and self.canvas.coords(self.p1paddle)[1] > 0:
                self.canvas.move(self.p1paddle, 0, -VELOCITY)
            if self.keys.get(P2_DOWN) and self.canvas.coords(self.p1paddle)[3] < HEIGHT:
                self.canvas.move(self.p1paddle, 0, VELOCITY)
            if self.keys.get(P1_UP) and self.canvas.coords(self.p2paddle)[1] > 0:
                self.canvas.move(self.p2paddle, 0, -VELOCITY)
            if self.keys.get(P1_DOWN) and self.canvas.coords(self.p2paddle)[3] < HEIGHT:
                self.canvas.move(self.p2paddle, 0, VELOCITY)
    
            self.after(10, self.draw)
    
        def keydown(self, key):
            self.keys[key.keycode] = True
    
        def keyup(self, key):
            self.keys[key.keycode] = False
    
    def main():
        root = tk.Tk()
        example = Example(root)
        example.pack()
        root.mainloop()
    
    if __name__ == '__main__':
        main()
    

    也许,偶然地,那里的一些改进也可以解决您最初的问题。

    【讨论】:

    • 不幸的是,这仍然对我不起作用。要么是 MacOSX 的东西,要么是 Tkinter 在 Python 2 中对按键命令的解释与在 Python 3 中不同。我对 tkinter 还是比较陌生,我猜我被教导与 SimpleGUI 一起使用的所有概念都没有t真的转移过来,就像教授说的那样。不过感谢您的提示,我会考虑使用它们来更新我的主要游戏代码。
    • 我不明白为什么它会有所帮助,但作为一个在黑暗中的镜头,你可以尝试安装 python3,它带有更新版本的 tcl(tkinter 背后的引擎)。
    • @Travis:黑暗中的另一个镜头:尝试使用key.keycode 而不是key.keysym。我将编辑我的答案以包含它。
    • FWIW 我在 python2.7 和 python3.6 中测试了这个,它对我来说很好。
    • 在切换到 key.keycode 之前,我在 python 3 中重新测试了你和我的代码,它运行良好!在 python 2.7 中使用 key.keycode 对我也不起作用。不过,我仍然对为什么它拒绝为我在 python 2 中工作感到困惑。我想这是一个永远无法回答的问题。感谢您的所有帮助!
    猜你喜欢
    • 2016-02-15
    • 2020-12-24
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-22
    相关资源
    最近更新 更多