如何在python中定期运行函数

时间:2014-06-12 00:53:42

标签: python time tkinter

我有一个简单的节拍器运行,出于某种原因,当它在较低的bpm时它很好,但是在较高的bpms它是不一致的并且不稳定。 我不知道发生了什么事。 我想尝试使用某些东西定期运行它。有没有办法做到这一点?

这是我的代码:

class thalam():
    def __init__(self,root,e):
        self.lag=0.2
        self.root=root
        self.count=0
        self.thread=threading.Thread(target=self.play)
        self.thread.daemon=True
        self.tempo=60.0/120
        self.e=e
        self.pause=False
        self.tick=open("tick.wav","rb").read()
        self.count=0
        self.next_call = time.time()
    def play(self):
        if self.pause:
            return
        winsound.PlaySound(self.tick,winsound.SND_MEMORY)
        self.count+=1
        if self.count==990:
            self.thread=threading.Thread(target=self.play)
            self.thread.daemon=True
            self.thread.start()
            return

        self.next_call+=self.tempo
        new=threading.Timer(self.next_call-time.time(),self.play)
        new.daemon=True
        new.start()
    def stop(self):
        self.pause=True
        winsound.PlaySound(None,winsound.SND_ASYNC)
    def start(self):
        self.pause=False
    def settempo(self,a):
        self.tempo=a
class Metronome(Frame):
    def __init__(self,root):
        Frame.__init__(self,root)
        self.first=True
        self.root=root
        self.e=Entry(self)
        self.e.grid(row=0,column=1)
        self.e.insert(0,"120")
        self.play=Button(self,text="Play",command=self.tick)
        self.play.grid(row=1,column=1)
        self.l=Button(self,text="<",command=lambda:self.inc("l"))
        self.l.grid(row=0,column=0)
        self.r=Button(self,text=">",command=lambda:self.inc("r"))
        self.r.grid(row=0,column=2)
    def tick(self):
        self.beat=thalam(root,self.e)
        self.beat.thread.start()
        self.play.configure(text="Stop",command=self.notick)
    def notick(self):
        self.play.configure(text="Start",command=self.tick)
        self.beat.stop()
    def inc(self,a):
        if a=="l":
            try:
                new=str(int(self.e.get())-5)
                self.e.delete(0, END)
                self.e.insert(0,new)
                self.beat.settempo(60.0/(int(self.e.get())))
            except:
                print "Invalid BPM"
                return
        elif a=="r":
            try:
                new=str(int(self.e.get())+5)
                self.e.delete(0, END)
                self.e.insert(0,new)
                self.beat.settempo((60.0/(int(self.e.get()))))
            except:
                print "Invalid BPM"
                return

3 个答案:

答案 0 :(得分:1)

由于需要处理器与其他程序共享,所以做任何需要时间精度的事情都非常困难。不幸的是,对于计时关键程序,操作系统可以随时切换到另一个进程。这可能意味着在明显延迟之后它可能不会返回您的程序。在导入时间之后使用 time.sleep 是尝试平衡哔声之间的时间的更一致方式,因为处理器具有较少的原因&#34;切掉。虽然Windows上的睡眠默认粒度为15.6ms,但我认为你不需要超过64Hz的节拍。此外,您似乎正在使用多线程来尝试解决您的问题,但是,线程的python实现有时会强制线程按顺序运行。这使得切换到您的流程更加重要。

我认为最佳解决方案将是生成声音数据,其中包含所需频率的节拍器蜂鸣声。然后你可以用操作系统理解的方式播放声音数据。由于系统知道如何以可靠的方式处理声音,因此您的节拍器将起作用。

很抱歉让您失望,但时间紧迫的应用程序非常困难,除非您想弄清楚您正在使用的系统。

答案 1 :(得分:1)

播放声音来模仿普通的节拍器并不需要“实时”功能。

看起来您使用Tkinter框架来创建GUI。 root.after()允许您call a function with a delay。您可以使用它来实现滴答:

def tick(interval, function, *args):
    root.after(interval - timer() % interval, tick, interval, function, *args)
    function(*args) # assume it doesn't block

tick()function毫秒args运行interval。单个刻度的持续时间受root.after()精度的影响,但从长远来看,稳定性仅取决于timer()函数。

这是一个打印一些统计数据的脚本,每分钟240次节拍:

#!/usr/bin/env python
from __future__ import division, print_function
import sys
from timeit import default_timer
try:
    from Tkinter import Tk
except ImportError: # Python 3
    from tkinter import Tk

def timer():
    return int(default_timer() * 1000 + .5)

def tick(interval, function, *args):
    root.after(interval - timer() % interval, tick, interval, function, *args)
    function(*args) # assume it doesn't block

def bpm(milliseconds):
    """Beats per minute."""
    return 60000 / milliseconds

def print_tempo(last=[timer()], total=[0], count=[0]):
    now = timer()
    elapsed = now - last[0]
    total[0] += elapsed
    count[0] += 1
    average = total[0] / count[0]
    print("{:.1f} BPM, average: {:.0f} BPM, now {}"
          .format(bpm(elapsed), bpm(average), now),
          end='\r', file=sys.stderr)
    last[0] = now

interval = 250 # milliseconds
root = Tk()
root.withdraw() # don't show GUI
root.after(interval - timer() % interval, tick, interval, print_tempo)
root.mainloop()

速度只有一拍:我的机器上只有240±1。

答案 2 :(得分:0)

我想告诉您,由于竞争条件(即使您正在使用锁和信号量!),在计时的情况下也无法精确使用线程。甚至我都遇到了问题。