簡體   English   中英

從另一個類調用Tkinter類(Raspberry Pi上的Python)

[英]Calling Tkinter class from another class (Python on Raspberry Pi)

我為為什么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()

而且,兩個類都可以完美地單獨運行。 我在一起使用時遇到問題。

我認為這是因為存在一個無限循環( 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代碼示例所使用的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()

變化:

  • 將讀取聲音循環移到一個單獨的函數中,該函數接受線程事件以發出信號以指示停止循環
  • Display.printMesssage()定義中刪除了parent參數,並改用了self.parent 因此,在setup_autdio函數中,調用printMessage() ,不再需要將root作為參數傳遞。

解決方案2使用Tkinter mainloop ,並在主線程中全部運行(注意,我還更改了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()

變化:

  • 移動從PCM設備讀取數據的代碼,並將UI更新到名為update_ui_from_audio的單獨函數中
  • Display.printMessage使用StringVar更新消息,因此僅使用1個消息小部件。
  • 用另一個調用update_ui_from_audio函數的函數替換while True循環,並要求Tkinter在毫秒之內再次調用它(因此,就像一個循環一樣)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM