如何在Tkinter的 Python GUI中实时显示Vosk的语音识别文本?

编程语言 2026-07-11

我在Python中使用Vosk语音识别库,实时从麦克风识别语音。
识别结果工作正常,我可以在终端打印输出。
然而,我想在Tkinter GUI的标签中实时显示识别文本,但当音频识别循环运行时,GUI会卡死。

下面是我的代码的一个简化版本。

import tkinter as tk
from vosk import Model, KaldiRecognizer
import pyaudio
import json

model = Model("vosk-model-small-cn-0.22")
rec = KaldiRecognizer(model, 16000)

p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
                channels=1,
                rate=16000,
                input=True,
                frames_per_buffer=8000)

root = tk.Tk()
label = tk.Label(root, text="Speech result")
label.pack()

while True:
    data = stream.read(4000)
    if rec.AcceptWaveform(data):
        result = json.loads(rec.Result())
        text = result["text"]
        label.config(text=text)

root.mainloop()

运行这段代码时,Tkinter窗口会卡死且不会更新。
如何在不阻塞GUI线程的情况下,使用Vosk的识别结果来更新Tkinter标签?

解决方案

while True: 循环阻止tkinter的 mainloop 运行(也就是说,进程没有执行到 mainloop 调用),因为主代码是顺序执行的。

可以使用 threading Python模块将语音识别部分移到另一个线程,如下所示:

import tkinter as tk
from vosk import Model, KaldiRecognizer
import pyaudio
import json
import threading # Added import.

# Reads audio, recognizes text, and updates the label:
def listen(recognizer, stream, label):
    while True:
        data = stream.read(4000)
        if rec.AcceptWaveform(data):
            result = json.loads(rec.Result())
            text = result["text"]
            label.config(text=text)

model = Model("vosk-model-small-cn-0.22")
rec = KaldiRecognizer(model, 16000)

p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
                channels=1,
                rate=16000,
                input=True,
                frames_per_buffer=8000)

root = tk.Tk()
label = tk.Label(root, text="Speech result")
label.pack()

# Create and start the thread which performs voice recognition:
t = threading.Thread(
    target=listen,
    kwargs=dict(recognizer=rec, stream=stream, label=label)
)
t.daemon = True # When the user closes the tkinter's window then the thread will not prevent the end of the process.
t.start()

root.mainloop()

根据 tkinter的 Threading model

对tkinter的调用可以来自任意Python线程。内部来说,如果调用来自创建Tk对象的线程以外的线程,解释器的事件队列中会投放一个事件,当该事件被执行时,结果会返回给调用该Python线程。

...因此,可以安全地从另一个线程更新标签。


另一种选择是把 mainloop 本身也放到另一个线程中。若 mainloop 可以在创建 root 的线程之外运行,那么把 mainloop 放到另一个线程里就像把 root.mainloop() 替换为以下内容一样简单:

threading.Thread(target=root.mainloop).start()

但由于我目前找不到明确的说明,可以参考其他类似的帖子,例如 Multithreading: tkinter mainloop not in main thread,将 mainloop 移动到另一个线程。


注意,上面的代码没有正确处理资源释放。这是因为结束 mainloop 会结束进程,因此 stream 会被释放(因为进程终止)。不过,在更大型的应用中,当不再需要时,通常需要正确地释放 stream

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章