You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Tkinter开发节拍器调用after_cancel()触发TypeError报错求助

Fixing Your Tkinter Metronome: TypeError & Other Key Issues

Hey there, let's break down what's causing that error and get your metronome working smoothly.

First, the TypeError Explained

The after_cancel() method only needs one argument: the unique ID returned by the after() call you want to cancel. Right now you're passing two arguments (1, run_), which is why you're seeing TypeError: after_cancel() takes 2 positional arguments but 3 were given (the first argument is always screen itself, so your two extra args make 3 total).

But there are a few other issues in your code that are going to cause problems beyond this error—like using sleep() which freezes your Tkinter GUI, and a loop logic that won't work as intended. Let's fix all of this step by step.

Corrected Code

Here's a revised version of your metronome with explanations of the key changes:

from tkinter import *
import os
import sys

# Global variable to track the after() task ID
after_id = None
times1 = 0

def quit_():
    screen.destroy()
    sys.exit()

def stop_metronome():
    global after_id
    if after_id is not None:
        screen.after_cancel(after_id)
        after_id = None

def run():
    global times1, after_id
    # Stop any existing metronome loop before starting a new one
    stop_metronome()
    times1 = 0
    
    try:
        bpm = int(bpm_.get())
        if bpm <= 0:
            raise ValueError("BPM must be a positive number")
    except ValueError as e:
        entry.delete(0, END)
        entry.insert(0, str(e))
        return
    
    # Calculate interval in milliseconds (60 seconds = 60000 ms)
    interval = int(60000 / bpm)
    
    def run_():
        global times1, after_id
        # Play the sound (using afplay, adjust if you're on Windows/Linux)
        os.system("afplay metronome.wav&")
        times1 += 1
        
        # Schedule the next tick
        after_id = screen.after(interval, run_)
    
    # Start the first tick
    run_()

def main():
    global bpm_, entry
    bpm_ = StringVar()
    Label(screen, text="").pack()
    Label(screen, text="Enter a BPM").pack()
    entry = Entry(screen, textvariable=bpm_)
    entry.pack()
    Label(screen, text="").pack()
    Button(screen, text="Start/Update BPM", command=run).pack()
    Label(screen, text="").pack()
    Button(screen, text="Quit", command=quit_).pack()

screen = Tk()
screen.title("Metronome")
main()
screen.mainloop()

Key Changes Made:

  • Fixed after_cancel() usage: We now store the ID returned by screen.after() in after_id, and pass only that ID to after_cancel() when stopping the loop.
  • Removed sleep(): sleep() blocks Tkinter's event loop, which makes your GUI unresponsive. Instead, we calculate the interval in milliseconds and use after() to schedule each tick—this keeps the GUI alive.
  • Added stop logic: The stop_metronome() function safely cancels any running metronome loop, so clicking "Start/Update BPM" multiple times won't create overlapping loops.
  • Input validation: We added a try/except block to handle invalid BPM inputs (like non-numbers or zero/negative values) so your app doesn't crash unexpectedly.
  • Simplified loop logic: The run_() function now schedules itself repeatedly using after(), with the correct interval based on the BPM.

How to Adjust BPM Now

Just enter a new BPM and click "Start/Update BPM"—the existing loop will stop automatically, and a new one with the updated BPM will start right away.

内容的提问来源于stack exchange,提问作者Leonardo Cremona

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 10:04:07