|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
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 小助理,如未能正确解答您的问题,请继续追问。
|
|