用python实现英语学习系统
用python实现英语学习系统
英语学习工具,用HTML/JavaScript实现见 https://blog.csdn.net/cnds123/article/details/148353183
HTML/JS版本只需浏览器,无需安装,可部署到服务器或本地打开。
现在介绍用python实现
文本转语音:使用 pyttsx3 库实现,它不支持暂停和继续。
词典查询:双击任意英文单词,通过 Free Dictionary API 查询单词释义和读音。
语音参数调整:可以调整语速、音调和音量
播放控制:支持播放选中文本、当前段落和全部文本
播放次数设置:可以设置播放次数或循环播放
文本朗读(TTS):使用pyttsx3,将任意文本转换为语音,可以调节语速、音量、音调等参数,不支持暂停和继续。
单词发音:通过Free Dictionary API获取单词的真人发音音频(如果有的话),然后使用pygame播放。这种方式发音更准确,但只限于单个单词。
Python版本实现的桌面应用,需要安装Python环境和相关依赖,需要安装以下第三方依赖:
pyttsx3 、PyQt6、requests
使用 pyttsx3实现文本转语音提供文本朗读功能,使用PyQt6构建图形用户界面、播放音频,HTTP请求需要requests。
界面如下:

源码如下:
import sys
import re
import requests
import threading
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QLabel, QTextEdit, QPushButton, QComboBox, QSlider,QWidget, QFrame, QMessageBox, QScrollArea, QDialog)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QUrl
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtGui import QFont
import pyttsx3class DictionaryLookupThread(QThread):result_ready = pyqtSignal(str, dict)error_occurred = pyqtSignal(str)def __init__(self, word):super().__init__()self.word = worddef run(self):try:response = requests.get(f"https://api.dictionaryapi.dev/api/v2/entries/en/{self.word}")if response.status_code == 200:data = response.json()self.result_ready.emit(self.word, data[0])else:self.error_occurred.emit("未找到该单词的释义")except Exception as e:self.error_occurred.emit(f"查询失败: {str(e)}")class WordDefinitionDialog(QDialog):def __init__(self, word, data, parent=None):super().__init__(parent)self.word = wordself.data = dataself.audio_url = None# 创建媒体播放器self.media_player = QMediaPlayer()self.audio_output = QAudioOutput()self.media_player.setAudioOutput(self.audio_output)self.init_ui()def init_ui(self):self.setWindowTitle(f"单词释义 - {self.word}")self.setGeometry(100, 100, 500, 400)layout = QVBoxLayout()# 单词标题title_layout = QHBoxLayout()word_label = QLabel(self.word)word_label.setFont(QFont("Arial", 16, QFont.Weight.Bold))title_layout.addWidget(word_label)# 音标if self.data.get('phonetics') and self.data['phonetics'][0].get('text'):phonetic = self.data['phonetics'][0]['text']phonetic_label = QLabel(phonetic)phonetic_label.setFont(QFont("Courier", 12))title_layout.addWidget(phonetic_label)title_layout.addStretch()layout.addLayout(title_layout)# 发音按钮for phonetic in self.data.get('phonetics', []):if phonetic.get('audio'):self.audio_url = phonetic['audio']breakif self.audio_url:audio_btn = QPushButton("🔊 播放发音")audio_btn.clicked.connect(self.play_audio)layout.addWidget(audio_btn)# 释义内容scroll_area = QScrollArea()content_widget = QWidget()content_layout = QVBoxLayout(content_widget)if self.data.get('meanings'):for meaning in self.data['meanings']:part_of_speech = meaning.get('partOfSpeech', '')pos_label = QLabel(part_of_speech.capitalize())pos_label.setFont(QFont("Arial", 12, QFont.Weight.Bold))pos_label.setStyleSheet("color: blue;")content_layout.addWidget(pos_label)for i, definition in enumerate(meaning.get('definitions', [])):def_text = f"{i+1}. {definition.get('definition', '')}"def_label = QLabel(def_text)def_label.setWordWrap(True)content_layout.addWidget(def_label)if definition.get('example'):example_text = f" 示例: {definition['example']}"example_label = QLabel(example_text)example_label.setWordWrap(True)example_label.setStyleSheet("color: gray; font-style: italic;")content_layout.addWidget(example_label)content_layout.addSpacing(10)scroll_area.setWidget(content_widget)scroll_area.setWidgetResizable(True)layout.addWidget(scroll_area)# 关闭按钮close_btn = QPushButton("关闭")close_btn.clicked.connect(self.accept)layout.addWidget(close_btn)self.setLayout(layout)def play_audio(self):"""播放单词发音"""if self.audio_url:try:# 重置播放位置到开头self.media_player.setPosition(0)# 设置音频源(如果尚未设置)if self.media_player.source() != QUrl(self.audio_url):self.media_player.setSource(QUrl(self.audio_url))# 播放音频self.media_player.play()except Exception as e:QMessageBox.critical(self, "错误", f"播放音频失败: {str(e)}")class EnglishLearningSystem(QMainWindow):def __init__(self):super().__init__()self.init_ui()self.init_tts()# 播放控制self.is_playing = Falseself.play_count = 1self.current_play_count = 0self.current_word = ""def init_ui(self):self.setWindowTitle("英语学习系统 - 文本转语音与词典查询")self.setGeometry(100, 100, 900, 700)# 设置样式self.setStyleSheet("""QMainWindow {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #667eea, stop: 1 #764ba2);}QWidget#centralWidget {background: rgba(255, 255, 255, 0.95);border-radius: 15px;}QLabel#title {font-size: 24px;font-weight: bold;color: #2c3e50;}QLabel#subtitle {font-style: italic;color: #666;}QFrame#controls {background: white;border-radius: 12px;padding: 15px;}QTextEdit {border: 2px solid #e0e0e0;border-radius: 8px;padding: 10px;font-size: 14px;}QTextEdit:focus {border-color: #667eea;}QPushButton {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #4CAF50, stop: 1 #45a049);color: white;border: none;border-radius: 8px;padding: 10px 20px;font-weight: bold;}QPushButton:hover {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #45a049, stop: 1 #4CAF50);}QPushButton.special {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #2196F3, stop: 1 #1976D2);}QPushButton.special:hover {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #1976D2, stop: 1 #2196F3);}QLabel#status {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #667eea, stop: 1 #764ba2);color: white;border-radius: 8px;padding: 10px;text-align: center;}""")# 中央部件central_widget = QWidget()central_widget.setObjectName("centralWidget")self.setCentralWidget(central_widget)# 主布局layout = QVBoxLayout(central_widget)layout.setContentsMargins(20, 20, 20, 20)layout.setSpacing(15)# 标题title_label = QLabel("英语学习系统")title_label.setObjectName("title")title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)layout.addWidget(title_label)subtitle_label = QLabel("文本转语音 + 实时词典查询")subtitle_label.setObjectName("subtitle")subtitle_label.setAlignment(Qt.AlignmentFlag.AlignCenter)layout.addWidget(subtitle_label)# 学习提示tip_frame = QFrame()tip_frame.setStyleSheet("""QFrame {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1,stop: 0 #f093fb, stop: 1 #f5576c);border-radius: 8px;padding: 15px;}QLabel {color: white;font-weight: bold;}""")tip_layout = QVBoxLayout(tip_frame)tip_text = QLabel("💡 (1)如果朗读不发音,请考虑选择语音是否不当。\n""(2)可以设置播放次数,调整语速、音量和音调。\n""(3)双击英文单词可查看英文释义、音标(需要联网,使用Free Dictionary API)。")tip_text.setWordWrap(True)tip_layout.addWidget(tip_text)layout.addWidget(tip_frame)# 文本区域text_label = QLabel("你可以在下面文本区域输入文字,或者往其中粘贴复制的文本:")layout.addWidget(text_label)self.text_edit = QTextEdit()self.text_edit.setPlainText("Welcome to the English Learning System!\n""This is an interactive text-to-speech application with dictionary lookup.\n""You can practice pronunciation, adjust speech parameters, and learn new vocabulary.\n""Double-click on any word like \"pronunciation\" or \"vocabulary\" to see its definition and hear how it sounds.")layout.addWidget(self.text_edit)# 控制区域controls_frame = QFrame()controls_frame.setObjectName("controls")controls_layout = QVBoxLayout(controls_frame)# 语音和播放次数选择voice_layout = QHBoxLayout()voice_layout.addWidget(QLabel("语音:"))self.voice_combo = QComboBox()voice_layout.addWidget(self.voice_combo)voice_layout.addWidget(QLabel("播放次数:"))self.play_count_combo = QComboBox()self.play_count_combo.addItems(["播放1次", "播放2次", "播放3次", "循环播放"])voice_layout.addWidget(self.play_count_combo)voice_layout.addStretch()controls_layout.addLayout(voice_layout)# 语速、音调、音量滑块self.rate_slider = self.create_slider("语速:", 0.5, 2.0, 1.0, controls_layout)self.pitch_slider = self.create_slider("音调:", 0.5, 2.0, 1.0, controls_layout)self.volume_slider = self.create_slider("音量:", 0.0, 1.0, 1.0, controls_layout)# 状态显示self.status_label = QLabel("系统就绪 - 开始你的英语学习之旅!")self.status_label.setObjectName("status")self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)controls_layout.addWidget(self.status_label)# 按钮区域buttons_layout = QHBoxLayout()main_buttons = [("🔊 开始朗读", self.speak_text, False),("⏹️ 停止", self.stop_speech, False),("📖 读选中文本", self.speak_selected_text, True),("📄 读当前段落", self.speak_current_paragraph, True)]for text, slot, is_special in main_buttons:btn = QPushButton(text)if is_special:btn.setProperty("class", "special")btn.clicked.connect(slot)buttons_layout.addWidget(btn)controls_layout.addLayout(buttons_layout)layout.addWidget(controls_frame)# 绑定事件self.text_edit.mouseDoubleClickEvent = self.on_text_double_clickdef create_slider(self, label, min_val, max_val, default_val, parent_layout):"""创建滑块控件"""slider_layout = QHBoxLayout()slider_layout.addWidget(QLabel(label))slider = QSlider(Qt.Orientation.Horizontal)slider.setMinimum(int(min_val * 10))slider.setMaximum(int(max_val * 10))slider.setValue(int(default_val * 10))slider_layout.addWidget(slider)value_label = QLabel(f"{default_val:.1f}")slider_layout.addWidget(value_label)parent_layout.addLayout(slider_layout)# 连接值改变事件slider.valueChanged.connect(lambda value, lbl=value_label: lbl.setText(f"{value/10:.1f}"))return sliderdef init_tts(self):"""初始化TTS引擎"""self.tts_engine = pyttsx3.init()self.voices = self.tts_engine.getProperty('voices')# 填充语音选项for voice in self.voices:self.voice_combo.addItem(f"{voice.name} ({voice.id})")def on_text_double_click(self, event):"""文本区域双击事件"""cursor = self.text_edit.textCursor()cursor.select(cursor.SelectionType.WordUnderCursor)word = cursor.selectedText().strip()# 检查是否为英文单词if word and re.match(r'^[a-zA-Z]+$', word):self.current_word = wordself.lookup_word(word)super().mouseDoubleClickEvent(event)def lookup_word(self, word):"""查询单词"""self.update_status(f"正在查询单词 \"{word}\"...")self.lookup_thread = DictionaryLookupThread(word)self.lookup_thread.result_ready.connect(self.show_word_definition)self.lookup_thread.error_occurred.connect(self.show_error)self.lookup_thread.start()def show_word_definition(self, word, data):"""显示单词释义"""dialog = WordDefinitionDialog(word, data, self)dialog.exec()self.update_status(f"单词 \"{word}\" 查询成功!")def update_status(self, message):"""更新状态显示"""self.status_label.setText(message)QApplication.processEvents()def speak_text(self):"""朗读全部文本"""if self.is_playing:returntext = self.text_edit.toPlainText().strip()if not text:self.update_status("没有文本可以朗读")return# 获取播放次数play_count_text = self.play_count_combo.currentText()if play_count_text == "循环播放":self.play_count = -1else:self.play_count = int(play_count_text.replace("播放", "").replace("次", ""))self.current_play_count = 0# 在新线程中播放threading.Thread(target=self._speak_text_thread, args=(text,), daemon=True).start()def _speak_text_thread(self, text):"""在新线程中执行文本朗读"""self.is_playing = True# 设置TTS参数self.tts_engine.setProperty('rate', self.rate_slider.value() / 10 * 200)self.tts_engine.setProperty('volume', self.volume_slider.value() / 10)self.tts_engine.setProperty('pitch', self.pitch_slider.value() / 10)# 设置语音if self.voice_combo.currentIndex() >= 0:self.tts_engine.setProperty('voice', self.voices[self.voice_combo.currentIndex()].id)# 播放逻辑while self.is_playing and (self.play_count == -1 or self.current_play_count < self.play_count):self.current_play_count += 1self.update_status(f"正在播放第 {self.current_play_count} 次... 🔊")# 播放文本self.tts_engine.say(text)self.tts_engine.runAndWait()# 如果不是循环播放,且已达到播放次数,则退出if self.play_count != -1 and self.current_play_count >= self.play_count:breakself.is_playing = Falseself.update_status("播放完成 ✓")def stop_speech(self):"""停止朗读"""self.is_playing = Falseself.tts_engine.stop()self.update_status("已停止 ⏹️")def speak_selected_text(self):"""朗读选中的文本"""cursor = self.text_edit.textCursor()selected_text = cursor.selectedText().strip()if not selected_text:self.update_status("请先选中要朗读的文本")return# 获取播放次数play_count_text = self.play_count_combo.currentText()if play_count_text == "循环播放":self.play_count = -1else:self.play_count = int(play_count_text.replace("播放", "").replace("次", ""))self.current_play_count = 0# 在新线程中播放threading.Thread(target=self._speak_text_thread, args=(selected_text,), daemon=True).start()def speak_current_paragraph(self):"""朗读当前段落"""cursor = self.text_edit.textCursor()cursor.select(cursor.SelectionType.BlockUnderCursor)paragraph = cursor.selectedText().strip()if not paragraph:self.update_status("光标所在位置没有找到有效段落")return# 获取播放次数play_count_text = self.play_count_combo.currentText()if play_count_text == "循环播放":self.play_count = -1else:self.play_count = int(play_count_text.replace("播放", "").replace("次", ""))self.current_play_count = 0# 在新线程中播放threading.Thread(target=self._speak_text_thread, args=(paragraph,), daemon=True).start()def show_error(self, message):"""显示错误信息"""QMessageBox.critical(self, "错误", message)if __name__ == "__main__":app = QApplication(sys.argv)window = EnglishLearningSystem()window.show()sys.exit(app.exec())
