import
sys
from
PyQt6.QtWidgets
import
(QApplication, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit,
QPushButton, QLabel, QGridLayout, QScrollArea, QFileDialog)
from
PyQt6.QtCore
import
Qt, QThread, pyqtSignal
from
gtts
import
gTTS, gTTSError
import
os
import
pygame
from
googletrans
import
Translator
class
TranslateThread(QThread):
translation_done
=
pyqtSignal(
str
)
def
__init__(
self
, text, dest
=
'zh-cn'
):
QThread.__init__(
self
)
self
.text
=
text
self
.dest
=
dest
def
run(
self
):
translator
=
Translator()
try
:
translation
=
translator.translate(
self
.text, dest
=
self
.dest).text
self
.translation_done.emit(translation)
except
Exception as e:
self
.translation_done.emit(f
"翻译出错: {str(e)}"
)
class
AudioThread(QThread):
audio_done
=
pyqtSignal()
error_occurred
=
pyqtSignal(
str
)
def
__init__(
self
, text):
QThread.__init__(
self
)
self
.text
=
text
self
.audio_file
=
"temp_audio.mp3"
self
.is_paused
=
False
def
run(
self
):
try
:
tts
=
gTTS(text
=
self
.text, lang
=
'en'
, tld
=
'com'
)
tts.save(
self
.audio_file)
pygame.mixer.music.load(
self
.audio_file)
pygame.mixer.music.play()
while
pygame.mixer.music.get_busy()
or
self
.is_paused:
pygame.time.Clock().tick(
10
)
pygame.mixer.music.unload()
self
.audio_done.emit()
except
gTTSError as e:
self
.error_occurred.emit(f
"gTTS错误: {str(e)}"
)
except
Exception as e:
self
.error_occurred.emit(f
"音频播放出错: {str(e)}"
)
def
pause_audio(
self
):
pygame.mixer.music.pause()
self
.is_paused
=
True
def
resume_audio(
self
):
pygame.mixer.music.unpause()
self
.is_paused
=
False
class
EnglishLearningApp(QWidget):
def
__init__(
self
):
super
().__init__()
self
.initUI()
pygame.mixer.init()
self
.audio_thread
=
None
self
.is_playing
=
False
def
initUI(
self
):
main_layout
=
QVBoxLayout()
self
.input_field
=
QTextEdit()
self
.input_field.setFixedSize(
400
,
100
)
main_layout.addWidget(
self
.input_field)
button_layout
=
QHBoxLayout()
self
.play_button
=
QPushButton(
'朗读'
)
self
.play_button.clicked.connect(
self
.play_text)
button_layout.addWidget(
self
.play_button)
self
.stop_button
=
QPushButton(
'停止阅读'
)
self
.stop_button.clicked.connect(
self
.stop_audio)
button_layout.addWidget(
self
.stop_button)
self
.pause_button
=
QPushButton(
'暂停阅读'
)
self
.pause_button.clicked.connect(
self
.toggle_pause_resume)
self
.pause_button.setEnabled(
False
)
button_layout.addWidget(
self
.pause_button)
self
.split_button
=
QPushButton(
'拆分句子'
)
self
.split_button.clicked.connect(
self
.split_sentence)
button_layout.addWidget(
self
.split_button)
self
.save_button
=
QPushButton(
'保存音频'
)
self
.save_button.clicked.connect(
self
.save_audio)
button_layout.addWidget(
self
.save_button)
main_layout.addLayout(button_layout)
self
.translation_label
=
QLabel()
self
.translation_label.setWordWrap(
True
)
main_layout.addWidget(
self
.translation_label)
scroll_area
=
QScrollArea()
scroll_area.setWidgetResizable(
True
)
scroll_content
=
QWidget()
self
.word_layout
=
QGridLayout(scroll_content)
scroll_area.setWidget(scroll_content)
main_layout.addWidget(scroll_area)
self
.setLayout(main_layout)
self
.setWindowTitle(
'英语学习助手'
)
self
.setGeometry(
300
,
300
,
500
,
500
)
def
play_text(
self
):
text
=
self
.input_field.toPlainText()
if
text:
self
.play_button.setEnabled(
False
)
self
.pause_button.setEnabled(
True
)
self
.stop_button.setEnabled(
True
)
self
.translate_text(text)
self
.audio_thread
=
AudioThread(text)
self
.audio_thread.audio_done.connect(
self
.on_audio_done)
self
.audio_thread.error_occurred.connect(
self
.on_audio_error)
self
.audio_thread.start()
self
.is_playing
=
True
def
stop_audio(
self
):
pygame.mixer.music.stop()
self
.play_button.setEnabled(
True
)
self
.pause_button.setEnabled(
False
)
self
.stop_button.setEnabled(
False
)
self
.is_playing
=
False
def
toggle_pause_resume(
self
):
if
self
.audio_thread
and
self
.audio_thread.is_paused:
self
.audio_thread.resume_audio()
self
.pause_button.setText(
'暂停阅读'
)
else
:
if
self
.audio_thread:
self
.audio_thread.pause_audio()
self
.pause_button.setText(
'继续阅读'
)
def
on_audio_done(
self
):
self
.play_button.setEnabled(
True
)
self
.pause_button.setEnabled(
False
)
self
.stop_button.setEnabled(
False
)
self
.is_playing
=
False
def
on_audio_error(
self
, error_msg):
self
.play_button.setEnabled(
True
)
self
.pause_button.setEnabled(
False
)
self
.stop_button.setEnabled(
False
)
self
.translation_label.setText(error_msg)
def
translate_text(
self
, text):
self
.translation_thread
=
TranslateThread(text)
self
.translation_thread.translation_done.connect(
self
.update_translation)
self
.translation_thread.start()
def
update_translation(
self
, translation):
self
.translation_label.setText(f
"中文翻译: {translation}"
)
def
split_sentence(
self
):
text
=
self
.input_field.toPlainText()
words
=
text.split()
for
i
in
reversed
(
range
(
self
.word_layout.count())):
self
.word_layout.itemAt(i).widget().setParent(
None
)
for
i, word
in
enumerate
(words):
button
=
QPushButton(word)
button.clicked.connect(
lambda
checked, w
=
word:
self
.on_word_click(w))
row
=
i
/
/
3
col
=
i
%
3
self
.word_layout.addWidget(button, row, col)
def
on_word_click(
self
, word):
self
.audio_thread
=
AudioThread(word)
self
.audio_thread.audio_done.connect(
self
.on_audio_done)
self
.audio_thread.error_occurred.connect(
self
.on_audio_error)
self
.audio_thread.start()
self
.translate_text(word)
def
save_audio(
self
):
if
os.path.exists(
"temp_audio.mp3"
):
save_path, _
=
QFileDialog.getSaveFileName(
self
,
"保存音频文件"
, "
", "
MP3 Files (
*
.mp3)")
if
save_path:
try
:
with
open
(
"temp_audio.mp3"
,
'rb'
) as src_file,
open
(save_path,
'wb'
) as dst_file:
dst_file.write(src_file.read())
print
(f
"音频文件已保存到: {save_path}"
)
except
Exception as e:
print
(f
"保存音频文件时出错: {str(e)}"
)
else
:
print
(
"没有可保存的音频文件"
)
def
closeEvent(
self
, event):
if
os.path.exists(
"temp_audio.mp3"
):
os.remove(
"temp_audio.mp3"
)
event.accept()
if
__name__
=
=
'__main__'
:
app
=
QApplication(sys.argv)
ex
=
EnglishLearningApp()
ex.show()
sys.exit(app.
exec
())