我很难过为什么Display类没有在以下代码中运行。我在main()中使用Display(root)来调用它。主班运行得很好。我终端没有收到任何错误。
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
class Display():
def __init__(self, parent):
self.parent = parent
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title('Listen')
parent.configure(background='#000000')
parent.displayFont = tkFont.Font(family="Unit-Bold", size=150)
def printMessage(self, parent, messageString):
self.message = tk.Message(self.parent, text=messageString, bg="#000000", font=parent.displayFont, fg="#777777", justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def main():
root = tk.Tk()
window = Display(root)
# Set up audio
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while True:
# Read data from device
l,data = data_in.read()
if l:
# catch frame error
try:
max_vol=audioop.rms(data,2)
scaled_vol = max_vol//4680
print scaled_vol
window.printMessage(root, scaled_vol)
except audioop.error, e:
if e.message !="not a whole number of frames":
raise e
root.mainloop()
if __name__ == '__main__':
main()
此外,这两个课程分别完美运行。我在使用它们时遇到了问题。
答案 0 :(得分:0)
我认为这是因为有一个无限循环(while True
)不允许执行到达Tk事件循环root.mainloop()
。
使用线程可能有助于解决此问题。您可以在单独的线程中运行数据输入循环,并按原样在主线程中运行Tk事件循环。
更新:我运行了代码(Linux 3.13.0-24上的Python 2.7.6-通用#47-Ubuntu SMP,pyalsaaudio == 0.8.2)并显示了数据。但是,在不同平台上使用线程的行为可能会有所不同。我现在对这种情况下的线程有疑问,因为在你提到的评论中,甚至没有调用print
语句。另一种方法是不使用线程和while True
循环,而是使用Tkinter本身的mainloop
。
另请注意,Display.printMessage
正在为每次通话创建新的消息窗口小部件。这将最终显示许多消息小部件(并占用内存)。为了避免这种情况,我建议使用Tkinter变量,如第二个代码示例中使用的Tkinter mainloop
。
解决方案1 使用线程(问题中代码的最小更改):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
from threading import Thread, Event
class Display():
def __init__(self, parent):
self.parent = parent
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title('Listen')
parent.configure(background='#000000')
parent.displayFont = tkFont.Font(family="Unit-Bold", size=150)
def printMessage(self, messageString):
self.message = tk.Message(self.parent, text=messageString, bg="#000000", font=self.parent.displayFont, fg="#777777", justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def setup_audio(window, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l,data = data_in.read()
if l:
# catch frame error
try:
max_vol=audioop.rms(data,2)
scaled_vol = max_vol//4680
print scaled_vol
window.printMessage(scaled_vol)
except audioop.error, e:
if e.message !="not a whole number of frames":
raise e
def main():
root = tk.Tk()
window = Display(root)
stop_event = Event()
audio_thread = Thread(target=setup_audio, args=[window, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
if __name__ == '__main__':
main()
的变化:
parent
定义中删除Display.printMesssage()
arg,然后使用self.parent
。因此,在调用setup_autdio
时printMessage()
函数中,不再需要传递root
作为参数。 解决方案2 使用Tkinter mainloop
,并在主线程中运行all(请注意,我还更改了Display.printMessage()
类,主要是为了提高性能):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
class Display():
def __init__(self, parent):
self.parent = parent
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title('Listen')
parent.configure(background='#000000')
parent.displayFont = tkFont.Font(family="Unit-Bold", size=150)
# using a Tkinter variable to display different messages, on the same widget
self.messageVar = tk.StringVar()
self.message = tk.Message(self.parent, textvar=self.messageVar, bg="#000000", font=self.parent.displayFont, fg="#777777", justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def printMessage(self, messageString):
self.messageVar.set(messageString)
def update_ui_from_audio(data_in, window):
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol=audioop.rms(data,2)
scaled_vol = max_vol//4680
print scaled_vol
window.printMessage(scaled_vol)
except audioop.error, e:
if e.message !="not a whole number of frames":
raise e
def main():
root = tk.Tk()
window = Display(root)
# Set up audio
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
def audio_read_loop():
update_ui_from_audio(data_in, window)
root.after(1, audio_read_loop)
root.after(1, audio_read_loop)
root.mainloop()
if __name__ == '__main__':
main()
<强>的变化:强>
update_ui_from_audio
的单独函数Display.printMessage
使用StringVar
更新邮件,因此只使用了1个邮件小部件。while True
函数的函数替换update_ui_from_audio
循环,并要求Tkinter在一个毫秒内再次调用它(因此,就像循环一样)。