从另一个类调用Tkinter类(Raspberry Pi上的Python)

从另一个类调用Tkinter类(Raspberry Pi上的Python),python,tkinter,Python,Tkinter,我不明白为什么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

我不明白为什么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()

而且,这两个类完全分开运行。我只是在一起使用它们时遇到了问题。

我认为这是因为存在一个无限循环(
,而True
),它不允许执行到达Tk事件循环
root.mainloop()

使用线程可能有助于解决此问题。您可以在单独的线程中运行数据输入循环,并在主线程中按原样运行Tk事件循环

更新:我运行了代码(Linux 3.13.0-24-generic#47 Ubuntu SMP上的Python 2.7.6,pyalsaaudio==0.8.2),它显示了数据。但是,在不同平台上使用线程可能会有不同的行为。我现在对本例中的线程有疑问,因为在您提到的注释中,甚至没有调用
print
语句。另一种方法不是使用线程和
while True
循环,而是使用Tkinter本身的
mainloop

另外,请注意,
Display.printMessage
在每次通话中都会创建一个新的消息小部件。这将导致许多消息小部件显示(并占用内存)。为了避免这种情况,我建议使用Tkinter变量,正如在第二个代码示例中使用Tkinter
mainloop
所使用的那样

解决方案1使用线程(对问题代码的最小更改):

变化:

  • 将reading sound loop移动到一个单独的函数中,该函数接受线程事件以发出停止循环的信号
  • Display.printmessage()
    定义中删除了
    parent
    参数,并改用
    self.parent
    。因此,在调用
    printMessage()
    时,在
    setup\u autdio
    函数中,不再需要将
    root
    作为参数传入
解决方案2使用Tkinter
mainloop
,并在主线程中运行所有程序(请注意,我还更改了
Display.printMessage()
类,主要是为了提高性能):

更改:

  • 移动从PCM设备读取数据的代码,并将UI更新到名为
    update\u UI\u from\u audio的单独函数中
  • Display.printMessage
    使用
    StringVar
    更新消息,因此只使用一个消息小部件
  • while True
    循环替换为另一个函数,该函数调用
    update\u ui\u from\u audio
    函数,并要求Tkinter在毫秒内再次调用它(因此,就像循环一样)

使用Tkinter's after代替while True。您可以使用一个调用自身的函数。当我运行这个程序时,tKinter在监视器上给了我一个界面,但是它显示了单词“Listen”(parent.title)而不是scaled_vol。在提供的代码中,有一个
print
语句(我想是为了调试)。是否将正确的值打印到标准输出?顺便说一下,从代码示例来看,我猜您在Linux上使用的是Python 2。你能提供更多关于平台和Python/Tk版本的详细信息吗?嗨,Farzad,谢谢你的帮助。我正在Raspberry Pi上运行python 2.7。很抱歉没提那件事。否,print语句没有使用此代码向终端打印任何内容。基本上,我可以让代码的两部分(接口和声音信号代码)独立工作,但我不能把它们放在一起。当我不尝试集成GUI时,声音信号代码工作正常。我已经用另一个解决方案更新了答案。(正如Curly Joe在评论中建议的那样),使用UI主循环而不是使用单独的循环是一种常见的技术。此外,我认为对于这类问题,提供更多关于软件平台的详细信息可能会更有帮助(例如操作系统名称和版本(Raspbian、Raspbmc等)、内核版本(影响ALSA)、
pyalsaaudio
version等)。啊哈!在我将值打印到显示器的意义上,这确实有效。非常感谢。但是文本闪烁,当control-c停止程序时,我无法退出。
#!/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()
#!/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()