You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy界面调用语音识别函数时冻结,求多任务并行处理方案

解决Kivy语音识别时界面冻结的问题

问题根源

你的voice2text()方法在主线程中执行,里面的while True循环和r.listen()都是阻塞操作,会占用主线程导致Kivy界面无法响应其他交互(比如点击out按钮)。Kivy的UI更新和事件处理完全依赖主线程,因此必须把耗时/阻塞的任务放到独立子线程中运行。

解决方案:用多线程分离语音识别任务

使用Python的threading模块将语音识别逻辑放到子线程,让主线程专注处理UI交互。同时修正代码中的其他错误(文件操作、屏幕名称引用等),确保功能正常运行。

修改后的完整代码

from kivy.app import App
from kivymd.uix.screen import Screen
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.textfield import MDTextField
from kivy.lang import Builder
from kivy.uix.label import Label
from kivymd.uix.button import MDRectangleFlatButton
from kivy.core.window import Window
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
import speech_recognition as sr
from kivy.clock import Clock
from kivy.uix.scrollview import ScrollView
import threading  # 导入线程模块

Window.size=(300,500)

class Menuscreen(Screen):
    pass

class ButtonONEScreen(Screen):
    pass
class MopScreen(Screen):
    pass


# kivy code 
screen_helper = """
ScreenManager:
    id: screen_manager

    Menuscreen:
    ButtonONEScreen:
    MopScreen:

<Menuscreen>:
    name:"menu" 
    MDRectangleFlatButton:
        text: 'ButtonONE'
        pos_hint: {'center_x':0.5,'center_y':0.6}
        on_press: root.manager.current = 'ONE'
<ButtonONEScreen>:
    name: 'ONE'
    MDRectangleFlatButton:
        text: 'START'
        id: START_button
        pos_hint: {'center_x':0.2,'center_y':0.1}
        on_release:
            app.start_voice_recognition()  # 改为启动线程的方法
    MDRectangleFlatButton:
        text: 'back'
        id: back
        pos_hint: {'center_x':0.5,'center_y':0.1}
        on_release:
            root.manager.current="menu"

    MDRectangleFlatButton:
        text: 'out'
        id: out_button
        pos_hint: {'center_x':0.8,'center_y':0.1}
        on_release:
            app.text()
            root.manager.current= "mainout"
 <MopScreen>:
       name: "mainout"
       ScrollView:  # 添加滚动视图,避免文本过长显示不全
           MDLabel:
               id: mylabel
               size:self.texture_size
               size_hint_y: None
               text: ""

       MDRectangleFlatButton:
           text: 'back'
           id: back
           pos_hint: {'center_x':0.7,'center_y':0.1}
           on_release:
               root.manager.current="menu"
"""

# python code
class TestApp(MDApp):
    def __init__(self, **kwargs):
        self.title = "v2t App"
        self.voice_thread = None  # 保存线程对象
        self.is_recognizing = False  # 标记是否正在识别
        super().__init__(**kwargs)

    def build(self):
        sm = Builder.load_string(screen_helper)
        return sm

    def start_voice_recognition(self):
        # 避免重复启动线程
        if not self.is_recognizing:
            self.is_recognizing = True
            self.voice_thread = threading.Thread(target=self.voice2text)
            self.voice_thread.daemon = True  # 守护线程,退出APP时自动结束
            self.voice_thread.start()

    def voice2text(self):
        r = sr.Recognizer()
        while self.is_recognizing:  # 用标记控制循环,方便后续扩展停止功能
            try:
                with sr.Microphone() as source:
                    audio = r.listen(source, 10, 4)
                text = r.recognize_google(audio)
                print(text)
                # 用with语句自动关闭文件,避免资源泄漏
                with open("words.txt", "a", encoding="utf-8") as file:
                    file.write(text + "\n")
            except sr.UnknownValueError:
                print("无法识别语音")
            except sr.RequestError as e:
                print(f"请求识别服务失败: {e}")
            except Exception as e:
                print(f"发生错误: {e}")
                self.is_recognizing = False

    def text(self):
        # 先取消之前的调度,避免重复执行
        Clock.unschedule(self.callback)
        Clock.schedule_interval(self.callback, 0.5)

    def callback(self, dt):
        try:
            with open("words.txt", "r", encoding="utf-8") as file:
                load_file = file.read()
            # 修正屏幕名称:MopScreen的name是"mainout",不是"out"
            self.root.get_screen("mainout").ids.mylabel.text = load_file
        except FileNotFoundError:
            self.root.get_screen("mainout").ids.mylabel.text = "暂无识别内容"

TestApp().run()

关键修改说明

  • 多线程实现:

    • 新增start_voice_recognition()方法启动语音识别子线程,避免阻塞主线程
    • 将voice2text()放到子线程执行,设置为守护线程确保APP退出时自动终止
    • 用is_recognizing标记控制循环,方便后续扩展停止识别功能
  • 代码错误修复:

    • 修正Kivy语言中out按钮的缩进错误
    • 修正callback()方法中屏幕名称引用错误(将"out"改为"mainout")
    • 优化文件操作,使用with语句自动关闭文件,避免资源泄漏
  • 界面体验优化:

    • 给MopScreen添加ScrollView,避免识别文本过长无法完整显示
    • 调用text()时先取消之前的调度,避免重复执行更新逻辑

内容的提问来源于stack exchange,提问作者Romieo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 10:48:22