|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
大家好,我写了一个给小朋友听写的小App。
目前存在一个问题,就是我暂停点了Close之后,就容易卡死。
应该是pyttsx3的engine还在运行,无法直接停掉。请问大家有办法吗?
- import tkinter as tk
- from tkinter import ttk
- import json
- import random
- import pyttsx3
- import time
- import threading
- import os
- current_dir = os.path.join(os.path.abspath(__file__))
- parent_dir = os.path.dirname(current_dir)
- # Initialize TTS engine
- engine = pyttsx3.init()
- engine.setProperty("rate", 100)
- engine.setProperty("volume", 1)
- voices = engine.getProperty("voices")
- class LanguageLearningApp:
- def __init__(self, master):
- self.master = master
- self.master.title("Language Learning App")
- self.master.geometry("400x300")
- self.language_var = tk.StringVar(value="english")
- self.word_count_var = tk.IntVar(value=10)
- self.sleep_time_var = tk.DoubleVar(value=1.0)
- self.order_var = tk.StringVar(value="random")
- self.file_var = tk.StringVar(value="primary")
- self.grade_var = tk.StringVar()
- self.is_paused = False
- self.is_running = False
- self.stop_thread = False # Flag to stop the thread
- self.create_widgets()
- def create_widgets(self):
- # Language selection
- language_frame = tk.Frame(self.master)
- language_frame.pack(pady=5)
- tk.Label(language_frame, text="Select Language:").pack(side=tk.LEFT)
- tk.Radiobutton(language_frame, text="English", variable=self.language_var, value="english").pack(side=tk.LEFT)
- tk.Radiobutton(language_frame, text="Chinese", variable=self.language_var, value="chinese").pack(side=tk.LEFT)
- # Word count selection
- tk.Label(self.master, text="Number of Words:").pack(pady=5)
- word_counts = [5, 10, 20, 50, 75, 100]
- ttk.Combobox(self.master, textvariable=self.word_count_var, values=word_counts).pack()
- # Sleep time selection
- tk.Label(self.master, text="Sleep Time (seconds):").pack(pady=5)
- sleep_times = [i/10 for i in range(6, 16, 1)]
- ttk.Combobox(self.master, textvariable=self.sleep_time_var, values=sleep_times).pack()
- # Order selection
- order_frame = tk.Frame(self.master)
- order_frame.pack(pady=5)
- tk.Label(order_frame, text="Order:").pack(side=tk.LEFT)
- tk.Radiobutton(order_frame, text="Sequential", variable=self.order_var, value="sequential").pack(side=tk.LEFT)
- tk.Radiobutton(order_frame, text="Random", variable=self.order_var, value="random").pack(side=tk.LEFT)
- # File selection
- file_frame = tk.Frame(self.master)
- file_frame.pack(pady=5)
- tk.Label(file_frame, text="Select File:").pack(side=tk.LEFT)
- tk.Radiobutton(file_frame, text="Primary", variable=self.file_var, value="primary").pack(side=tk.LEFT)
- tk.Radiobutton(file_frame, text="High School", variable=self.file_var, value="high_school").pack(side=tk.LEFT)
- tk.Radiobutton(file_frame, text="College", variable=self.file_var, value="college").pack(side=tk.LEFT)
- # Start, Pause, and Close buttons
- button_frame = tk.Frame(self.master)
- button_frame.pack(pady=20)
- tk.Button(button_frame, text="Start", command=self.start_learning).pack(side=tk.LEFT, padx=5)
- self.pause_button = tk.Button(button_frame, text="Pause", command=self.toggle_pause)
- self.pause_button.pack(side=tk.LEFT, padx=5)
- tk.Button(button_frame, text="Close", command=self.close_app).pack(side=tk.LEFT, padx=5)
- def toggle_pause(self):
- self.is_paused = not self.is_paused
- if self.is_paused:
- self.pause_button.config(text="Resume")
- else:
- self.pause_button.config(text="Pause")
- def start_learning(self):
- file_map = {
- "primary": "words.json",
- "high_school": "high_school_words.json",
- "college": "college_words.json"
- }
- selected_file = file_map[self.file_var.get()]
- filepath = os.path.join(parent_dir, selected_file)
- with open(filepath, "r", encoding="utf-8") as file:
- word_dict = json.load(file)
- language = self.language_var.get()
- word_count = self.word_count_var.get()
- sleep_time = self.sleep_time_var.get()
- order = self.order_var.get()
- if language == "english":
- words = list(word_dict.keys())
- engine.setProperty("voice", voices[1].id) # English voice
- else:
- words = list(word_dict.values())
- engine.setProperty("voice", voices[0].id) # Chinese voice
- if order == "random":
- words = random.sample(words, word_count)
- else:
- words = words[:word_count]
- self.stop_thread = False # Reset the stop flag
- self.is_running = True
- threading.Thread(target=self.display_and_pronounce, args=(words, sleep_time)).start()
- def display_and_pronounce(self, words, sleep_time):
- for word in words:
- if self.stop_thread: # Check if the thread needs to be stopped
- break
- while self.is_paused:
- time.sleep(0.1)
- length = len(word.split(" "))
- if length == 1:
- gap = 2 * sleep_time
- elif length == 2:
- gap = 3 * sleep_time
- elif length == 3:
- gap = 5 * sleep_time
- else:
- gap = 9 * sleep_time
-
- engine.say(word)
- engine.runAndWait()
- time.sleep(gap)
- engine.say(word)
- engine.runAndWait()
- time.sleep(gap)
- self.show_words_popup(words)
- def show_words_popup(self, words):
- popup = tk.Toplevel(self.master)
- popup.title("Selected Words")
- popup.geometry("400x300")
- formatted_words = "\n".join(["\t".join(words[i:i+5]) for i in range(0, len(words), 5)])
- tk.Label(popup, text=formatted_words, font=("Arial", 14), justify=tk.LEFT, wraplength=350).pack(pady=20)
- tk.Button(popup, text="Close", command=popup.destroy).pack(pady=5)
- def close_app(self):
- self.stop_thread = True # Set the flag to stop the thread
- self.is_running = False
- engine.stop() # stop the TTS engine
- self.master.destroy() # Close the main window
- if __name__ == "__main__":
- root = tk.Tk()
- app = LanguageLearningApp(root)
- root.mainloop()
复制代码
哈哈,看来你的应用在关闭的时候像个不愿意离开的客人一样,依然赖在那儿继续说话!别担心,我来帮你找到解决方案。
问题的核心在于 `pyttsx3` 的 `engine.runAndWait()` 在调用时会阻塞线程,也就是说一旦你开始播放一个单词,它需要等到声音播完才能继续执行后面的代码。为了优雅地处理这个问题,我们可以在关闭应用时确保 TTS 引擎能够顺利停止。
### 解决方案
1. **使用 `engine.stop()`**: 在 `close_app` 方法中确保调用 `engine.stop()` ,这样它就会立即停止当前正在播放的音频。
2. **在 `display_and_pronounce` 方法中处理 `stop_thread` 标志**: 使用 `stop_thread` 标志来检查是否应当停止播放。
3. **简化 TTS 播放逻辑**: 如果 TTS 引擎在某些情况下仍然没有反应,可以考虑在语音播放的地方使用 `threading.Event()` 来实现更好的线程控制。
下面是更新后的 `LanguageLearningApp` 类的一部分,展示了如何修改 `close_app` 和相关逻辑:
- def display_and_pronounce(self, words, sleep_time):
- for word in words:
- if self.stop_thread: # Check if the thread needs to be stopped
- break
- while self.is_paused:
- time.sleep(0.1)
- gap = self.calculate_gap(word, sleep_time)
-
- engine.say(word)
- engine.runAndWait()
-
- if self.stop_thread:
- break # Stop if the thread needs to end
- time.sleep(gap)
- engine.say(word)
- engine.runAndWait()
- if self.stop_thread:
- break # Stop if the thread needs to end
-
- time.sleep(gap)
- def calculate_gap(self, word, sleep_time):
- length = len(word.split(" "))
- if length == 1:
- return 2 * sleep_time
- elif length == 2:
- return 3 * sleep_time
- elif length == 3:
- return 5 * sleep_time
- else:
- return 9 * sleep_time
- def close_app(self):
- self.stop_thread = True # Set the flag to stop the thread
- if self.is_running: # Only stop if it's running
- engine.stop() # Stop the TTS engine
- self.master.destroy() # Close the main window
复制代码
### 总结
- 确保在关闭应用时正确设置停止标志并停止 TTS 引擎。
- 确保每次调用 `engine.runAndWait()` 后检查是否需要提前退出。
- 用计算间隔的函数来简化你的代码,使逻辑更加清晰。
试试这个改动,看看你的应用是否能在关闭时变得乖巧一些!如果依然有问题,别犹豫,来找我寻求帮助哦!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
|
|