[英]How to run a function periodically in python
我有一個簡單的節拍器在運行,出於某種原因,當它在較低的 bpm 時很好,但在較高的 bpm 時它不一致且不穩定。 我不知道發生了什么。 我想嘗試使用一些東西來定期運行它。 有沒有辦法做到這一點?
這是我的代碼:
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
播放聲音以模擬普通節拍器不需要“實時”功能。
看起來您使用 Tkinter 框架來創建 GUI。 root.after()
允許您使用延遲調用函數。 您可以使用它來實現刻度:
def tick(interval, function, *args):
root.after(interval - timer() % interval, tick, interval, function, *args)
function(*args) # assume it doesn't block
tick()
每interval
毫秒使用給定的args
運行function
。 單個滴答的持續時間受root.after()
精度的影響, 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。
這是asyncio
模擬:
#!/usr/bin/env python3
"""Metronome in asyncio."""
import asyncio
import sys
async def async_main():
"""Entry point for the script."""
timer = asyncio.get_event_loop().time
last = timer()
def print_tempo(now):
nonlocal last
elapsed = now - last
print(f"{60/elapsed:03.1f} BPM", end="\r", file=sys.stderr)
last = now
interval = 0.250 # seconds
while True:
await asyncio.sleep(interval - timer() % interval)
print_tempo(timer())
if __name__ == "__main__":
asyncio.run(async_main())
由於處理器需要與其他程序共享自身,因此做任何需要時間精度的事情都非常困難。 不幸的是,對於定時關鍵程序,操作系統可以隨時選擇切換到另一個進程。 這可能意味着它可能會在明顯延遲之后才返回到您的程序。 在導入時間后使用time.sleep是嘗試平衡嗶聲之間時間的更一致的方法,因為處理器沒有更少的“理由”切換。 盡管 Windows 上的 sleep 的默認粒度為 15.6ms,但我假設您不需要播放超過 64Hz 的節拍。 此外,您似乎正在使用多線程來嘗試解決您的問題,但是,線程的 Python 實現有時會強制線程按順序運行。 這使得離開您的流程變得更糟。
我覺得最好的解決方案是生成包含所需頻率的節拍器蜂鳴聲的聲音數據。 然后您可以以操作系統能夠很好理解的方式播放聲音數據。 由於系統知道如何以可靠的方式處理聲音,因此您的節拍器就可以工作了。
很抱歉讓您失望,但計時關鍵應用程序非常困難,除非您想親自接觸正在使用的系統。
我想告訴你,由於競爭條件(即使你使用鎖和信號量!),你不能精確地處理線程的時間。 甚至我也遇到過這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.