Pygame mixer 会导致 tkinter 冻结



>语音模块说一个列表,我正在使用的音频模块是混音器形式的pygame。当我开始播放音频时,tkinter程序无法与之交互,如果我移动窗口,音频将停止。我认为某种线程可能会解决此问题,但我不确定它是如何工作的。

问题
Tkinter 窗口冻结。

目标
能够在播放音频时与程序进行交互。

法典

import tkinter as tk
from gtts import gTTS
from io import BytesIO
import pygame
window = tk.Tk()
window.geometry("800x600")
window.resizable(0, 0)
def speak(text,language="en",accent="com"):
mp3_fp = BytesIO()
phrase = gTTS(text=text,lang=language,tld=accent)
phrase.write_to_fp(mp3_fp)
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(mp3_fp,"mp3")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.delay(10)
pygame.event.poll()
def play():
data = list([boy,girl,pink,blue])
for i in data:
speak(i)
window.mainloop()

代码解释
play()将字典中的每个值(例如男孩,女孩)分别传递到speak(),然后连续逐个播放 在speak()中,来自文本转语音模块 (gTTS) 的音频被传递到pygame.mixer,在说出最后一个单词时播放音频。

首先,您需要发布演示问题所需的最少代码。 你还没有这样做。 幸运的是,这是一个众所周知的"问题",无需导入四个库并围绕您提供的内容构建应用程序即可轻松回答。

其次,在您提供的代码中,data = list[boy,girl,pink,blue]甚至不是正确的语法。 应该是data = list(["boy", "girl", "pink", "blue"]). 您必须发布正在运行的代码才能获得最佳答案。

讲完。

问题是传统的未经修改的Python在单个线程中运行。 如果你想知道为什么会这样,那么我邀请你研究GIL(全局解释器锁)以获得更多背景。

只有一个线程,当 PyGame 正在做某事时,线程很忙,TkInter 停止响应输入,反之亦然——当 TkInter 处于某事中间时,你会发现 PyGame 停止响应。

你可以用这个来演示这种现象:

import tkinter as tk
import time
def delay():
time.sleep(10)
def main():
root = tk.Tk()
tk.Button(root, text="Test Me", command=delay).pack(expand=True, fill=tk.BOTH)

root.mainloop()
if __name__ == "__main__":
main()

运行此操作时,您将看到按钮已按下,按钮在应用程序进入睡眠状态时保持按下状态,并且按钮在唤醒之前不会返回到其未单击状态。

我知道解决您的特定问题的唯一方法是在单独的线程上运行 TkInter 和/或 PyGame。

你将不得不阅读Python的Threading()模块。 你可以从这里开始。 我已经浏览过它,它似乎很完整。

只是为了证明差异:

import tkinter as tk
import time
import threading
def delay():
print("Delay started...")
time.sleep(10)
print("... and finished.")
def dispatchDelayToThread():
t = threading.Thread(target=delay)
t.start()
def main():
root = tk.Tk()
tk.Button(root, text="Test Me", command=dispatchDelayToThread).pack(expand=True, fill=tk.BOTH)

root.mainloop()
if __name__ == "__main__":
main()

我什至没有真正更改代码! 我添加了一个函数来调度我已经编写的代码,然后更改按钮以调用调度程序而不是代码。 非常容易实现。

运行此按钮,您将看到按钮立即返回到就绪状态。 如果从命令行运行此操作,您将看到它在输入线程时打印一行,在线程完成时打印另一行。 更酷的是,如果您连续单击该按钮三次,您将获得三条"开始"消息,然后不久后收到三条"完成"消息。

若要使用自己的代码演示此线程,请执行以下操作:

import pygame
import tkinter as tk
import io
import gtts
import threading
def speak(text,language="en",accent="com"):
mp3_fp = io.BytesIO()
phrase = gtts.gTTS(text=text,lang=language,tld=accent)
phrase.write_to_fp(mp3_fp)
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(mp3_fp,"mp3")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.delay(10)
pygame.event.poll()
def dispatchPlay():
t = threading.Thread(target=play)
t.start()
def play():
data = list(["boy", "girl", "pink", "blue"])
for i in data:
speak(i)
def main():
root = tk.Tk()
root.geometry('300x200')
tk.Button(root, text="This is a clicky button", command=dispatchPlay).pack(expand=True, fill=tk.BOTH)
root.mainloop()
if __name__ == "__main__":
main()

通常,您的用户界面将放在一个线程上,如果是游戏,则在另一个线程上更新帧,在另一个线程上播放声音内容,在线程上连接网络连接等,所有这些都通过某种消息传递系统联系在一起。

但是,请注意,在传统的未修改的Python中,任何时候都只有一个线程在运行! 您可以生成一百个线程,但一次只能运行其中一个线程。 Python在多线程计算密集型任务方面很糟糕,但在线程化I/O方面却大放异彩,而这些东西通常只是花时间等待。

最新更新