简体   繁体   中英

Tkinter Frame Not Recognizing Keypresses

This question is NOT a duplicate of this question: Why doesn't the .bind() method work with a frame widget in Tkinter?

As you can see, I set the focus to the current frame in my game_frame() method.

I'm writing a Chip-8 emulator in Python and using Tkinter for my GUI. The emulator is running, but I can't get Tkinter to recognize keypresses. Here is my code:

def game_frame(self):
    self.screen = Frame(self.emulator_frame, width=640, height=320)
    self.screen.focus_set()
    self.canvas = Canvas(self.screen, width=640, height=320, bg="black")
    self._root.bind("<KeyPress-A>", self.hello)
    for key in self.CPU.KEY_MAP.keys():
        print(key)
        self.screen.bind(key, self.hello)
    self.screen.pack()
    self.canvas.pack()

def hello(self, event):
    if event.keysym in self.CPU.KEY_MAP.keys():
        self.CPU.keypad[self.CPU.KEY_MAP[event.keysym]] = 1
        self.CPU.key_pressed = True
        self.CPU.pc += 2
    sys.exit()

def run_game(self, event):
    self.game_frame()
    self.CPU.load_rom("TANK")
    while True:
        self._root.update()
        self.after(0, self.CPU.emulate_cycle)

Could you please help me figure out what's going wrong? I think it might have something to do with my game loop interfering with the key bindings, but I'm not sure. The hello method never gets called when I run the game because the program continues to run in an infinite loop and never exits, regardless of what key is pressed. Thank you!

The problem could be due to two things. Without seeing all your code it's impossible to say for sure.

For one, you are binding to a capital "A" rather than a lowercase "a" -- have you testing that the binding works or not when you press a capital A?

Also, you are using after and update incorrectly. You may be starving the event loop, preventing it from processing key presses. The right way to run a function periodically is to have a function that (re)schedules itself.

class CPU_Class():
    ...
    def run_cycle(self):
        self.emulate_cycle()
        self._root.after(1, self.run_cycle)

Two things to note:

  1. don't use after(0, ...) -- you need to give tkinter at least a ms or so to process other events.
  2. the run_cycle function is responsible for running one cycle, and then scheduling the next cycle to run in the future.

Once you do that, you no longer need your while loop. You can simply call run_cycle once, and that will start the CPU running.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM