基于Python實(shí)現(xiàn)簡(jiǎn)易的單詞記憶軟件
前言
該代碼實(shí)現(xiàn)了一個(gè)基于PyQt5的單詞記憶軟件,支持高考、四級(jí)、六級(jí)和考研四個(gè)級(jí)別的詞庫(kù)。程序提供四種學(xué)習(xí)模式:拆分模式(將單詞拆分為片段重組)、填空模式(選擇正確字母填空)、輸入模式(手動(dòng)輸入單詞)和測(cè)試模式(評(píng)估單詞記憶效果)。軟件界面包含單詞釋義顯示、交互練習(xí)區(qū)域和反饋功能,支持單詞級(jí)別切換、結(jié)果統(tǒng)計(jì)和保存功能。采用隨機(jī)算法打亂單詞順序,確保學(xué)習(xí)效果,具有錯(cuò)誤提示和自動(dòng)跳轉(zhuǎn)下一題功能,適合不同英語(yǔ)水平的學(xué)習(xí)者使用。
目前存在的問(wèn)題和優(yōu)化思路,目前還沒(méi)實(shí)現(xiàn):
- 拆分模式再次點(diǎn)擊已經(jīng)被選擇的單詞片段可以取消鎖定,但是要按單詞拼寫的倒序取消(原本是沒(méi)鎖定的,但是那樣會(huì)導(dǎo)致一個(gè)字符可以重復(fù)點(diǎn)擊,導(dǎo)致單詞拼接不正確,而且還沒(méi)法判斷正誤)。
- 模式之間切換會(huì)導(dǎo)致窗口卡死,這個(gè)還沒(méi)找到是什么原因。
- 測(cè)試模式可以結(jié)合其他三種模式來(lái)進(jìn)行選擇。
- 當(dāng)保存的時(shí)候點(diǎn)擊那個(gè)詞書就會(huì)保存什么,比如我是測(cè)試的“高考”,但是在保存的時(shí)候點(diǎn)擊了“考研”,保存名字默認(rèn)是“考研”。
- 單詞太少了,完全是deepseek生成的,自己目前還沒(méi)看到錯(cuò)誤的,也可以是因?yàn)楸救擞⒄Z(yǔ)不好所以還沒(méi)發(fā)現(xiàn)。
拆分模式
- 顯示單詞的釋義
- 將單詞拆分成若干片段并打亂順序顯示
- 用戶按正確順序點(diǎn)擊片段拼出完整單詞
- 系統(tǒng)檢查是否正確,提供反饋
def setup_mode1(self, word, meaning):
"""設(shè)置拆分模式"""
self.mode1_meaning.setText(meaning)
self.mode1_answer.clear()
self.mode1_feedback.clear()
# 清理舊按鈕
while self.mode1_chunks_layout.count():
item = self.mode1_chunks_layout.takeAt(0)
if item.widget():
item.widget().deleteLater()
# 安全拆分單詞
if len(word) < 2:
chunks = [word]
else:
n = min(3, max(2, len(word) // 2))
split_points = sorted(random.sample(range(1, len(word)), n - 1))
chunks = []
start = 0
for point in split_points:
chunks.append(word[start:point])
start = point
chunks.append(word[start:])
# 打亂順序
random.shuffle(chunks)
# 創(chuàng)建新按鈕
self.chunk_buttons = []
self.selected_chunks = [] # 存儲(chǔ)字典:{"chunk": 片段, "button": 按鈕, "position": 位置}
for chunk in chunks:
btn = QPushButton(chunk)
btn.setStyleSheet("...")
btn.clicked.connect(lambda _, c=chunk, b=btn: self.toggle_chunk(c, b))
self.mode1_chunks_layout.addWidget(btn)
self.chunk_buttons.append(btn)
self.correct_word = word
self.user_answer = ""
填空模式
- 顯示單詞的釋義
- 顯示帶有空缺的單詞(部分字母被下劃線替代)
- 提供4個(gè)選項(xiàng)(1個(gè)正確,3個(gè)干擾項(xiàng))
- 用戶選擇正確選項(xiàng)填補(bǔ)空缺
- 系統(tǒng)檢查是否正確,提供反饋
def setup_mode2(self, word, meaning):
"""設(shè)置填空模式"""
self.mode2_meaning.setText(meaning)
self.mode2_feedback.clear()
# 清理舊選項(xiàng)
while self.mode2_options_layout.count():
item = self.mode2_options_layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
del item
# 創(chuàng)建填空
if len(word) <= 3:
blank_pos = len(word) // 2
blank_len = len(word) - blank_pos
correct_part = word[blank_pos:]
else:
blank_len = min(3, max(2, len(word) // 3))
blank_pos = random.randint(1, len(word) - blank_len - 1)
correct_part = word[blank_pos:blank_pos + blank_len]
blank_word = word[:blank_pos] + "_" * blank_len + word[blank_pos + blank_len:]
self.mode2_blank.setText(blank_word)
# 生成選項(xiàng)
options = [correct_part]
chars = list(set(word) - set(correct_part))
while len(options) < 4:
if chars:
option = ''.join(random.sample(chars, min(len(chars), blank_len)))
else:
option = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=blank_len))
if option != correct_part and option not in options:
options.append(option)
random.shuffle(options)
# 創(chuàng)建選項(xiàng)按鈕
self.option_buttons = [] # 保存按鈕引用防止被垃圾回收
for option in options:
btn = QPushButton(option)
btn.setStyleSheet("...")
btn.clicked.connect(lambda _, o=option, cp=correct_part, w=word: self.check_fill_answer(o, cp, w))
self.mode2_options_layout.addWidget(btn)
self.option_buttons.append(btn) # 保存按鈕引用
輸入模式
這個(gè)當(dāng)時(shí)最大的問(wèn)題就是那個(gè)錯(cuò)誤信息和文本信息沒(méi)有清空,導(dǎo)致再次輸入還可以看到提示信息,那豈不是可以照著輸入。
- 顯示單詞的釋義
- 用戶輸入框中輸入對(duì)應(yīng)單詞
- 系統(tǒng)檢查是否正確,提供反饋
- 可回車提交答案
def setup_mode3(self, meaning):
"""設(shè)置輸入模式"""
self.mode3_meaning.setText(meaning)
self.mode3_input.clear()
self.mode3_feedback.clear()
self.mode3_input.setFocus()
self.input_error_shown = False
def check_input_mode(self):
"""檢查輸入模式答案"""
user_input = self.mode3_input.text().strip().lower()
correct = self.current_words[self.current_index]["word"].lower()
if user_input == correct:
self.mode3_feedback.setText("? 正確!")
self.mode3_feedback.setStyleSheet("color: #28a745;")
QTimer.singleShot(1000, self.next_word)
else:
self.mode3_feedback.setText(f"? 錯(cuò)誤!正確答案是: {correct}")
self.mode3_feedback.setStyleSheet("color: #dc3545;")
self.input_error_shown = True
測(cè)試模式
- 點(diǎn)擊"開始測(cè)試"按鈕
- 系統(tǒng)隨機(jī)選擇詞庫(kù)中的單詞
- 顯示單詞釋義,用戶輸入對(duì)應(yīng)單詞
- 記錄錯(cuò)誤次數(shù)
- 測(cè)試完成后顯示統(tǒng)計(jì)結(jié)果(正確率、錯(cuò)誤最多的單詞等)
- 可保存測(cè)試結(jié)果到文件
def start_test(self):
"""開始測(cè)試"""
self.test_mode = True
self.test_results = {}
self.current_test_words = self.current_words.copy()
random.shuffle(self.current_test_words)
self.test_index = 0
for word_data in self.current_test_words:
word = word_data["word"]
self.test_results[word] = {"errors": 0, "meaning": word_data["meaning"]}
self.start_test_btn.setEnabled(False)
self.end_test_btn.setEnabled(True)
self.test_input.setEnabled(True)
self.test_input.clear()
self.test_feedback.clear()
self.show_next_test_word()
def check_test_answer(self):
"""檢查測(cè)試答案"""
if not self.test_mode:
return
user_input = self.test_input.text().strip().lower()
current_word_data = self.current_test_words[self.test_index]
correct_word = current_word_data["word"].lower()
if user_input == correct_word:
self.test_feedback.setText("? 正確!")
self.test_feedback.setStyleSheet("color: #28a745;")
self.test_index += 1
QTimer.singleShot(1000, self.show_next_test_word)
else:
self.test_feedback.setText(f"? 錯(cuò)誤!正確答案是: {correct_word}")
self.test_feedback.setStyleSheet("color: #dc3545;")
self.test_results[correct_word]["errors"] += 1
self.test_input_error_shown = True
QTimer.singleShot(1000, self.clear_test_input)
def end_test(self):
"""結(jié)束測(cè)試并顯示結(jié)果"""
self.test_mode = False
self.start_test_btn.setEnabled(True)
self.end_test_btn.setEnabled(False)
self.test_input.setEnabled(False)
self.test_input.clear()
self.test_feedback.clear()
# 按錯(cuò)誤次數(shù)排序,錯(cuò)誤多的排在前面
sorted_results = sorted(self.test_results.items(),
key=lambda x: x[1]["errors"],
reverse=True)
result_text = f"測(cè)試完成 - {self.current_level}詞庫(kù)\n\n"
result_text += "單詞\t\t錯(cuò)誤次數(shù)\t\t釋義\n"
result_text += "-" * 60 + "\n"
for word, data in sorted_results:
result_text += f"{word.ljust(15)}\t{data['errors']}\t\t{data['meaning']}\n"
# 添加統(tǒng)計(jì)信息
total_words = len(self.test_results)
correct_words = sum(1 for word in self.test_results.values() if word["errors"] == 0)
accuracy = correct_words / total_words * 100 if total_words > 0 else 0
most_mistakes = sorted_results[0] if sorted_results else ("", {"errors": 0, "meaning": ""})
result_text += "\n統(tǒng)計(jì)信息:\n"
result_text += f"總單詞數(shù): {total_words}\n"
result_text += f"正確掌握的單詞: {correct_words}\n"
result_text += f"準(zhǔn)確率: {accuracy:.1f}%\n"
result_text += f"錯(cuò)誤最多的單詞: {most_mistakes[0]} (錯(cuò)誤 {most_mistakes[1]['errors']} 次)\n"
self.test_results_text.setText(result_text)
self.results_stack.setCurrentIndex(1) # 顯示結(jié)果界面
完整代碼
import sys
import random
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QPushButton, QStackedWidget,
QRadioButton, QButtonGroup, QLineEdit, QMessageBox,
QFileDialog, QTextEdit)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont
class WordPracticeApp(QMainWindow):
def __init__(self):
super().__init__()
try:
self.setup_ui()
self.load_word_database()
self.setup_connections()
self.shuffle_words()
self.show_word()
# 初始化測(cè)試模式相關(guān)變量
self.test_mode = False
self.test_results = {}
self.current_test_words = []
self.input_error_shown = False # 標(biāo)記是否顯示了輸入錯(cuò)誤
except Exception as e:
self.show_error_message(f"初始化失敗: {str(e)}")
raise
def center_window(self):
"""將窗口居中顯示"""
frame = self.frameGeometry()
screen = QApplication.primaryScreen().availableGeometry()
frame.moveCenter(screen.center())
self.move(frame.topLeft())
def setup_ui(self):
"""初始化用戶界面"""
try:
# 設(shè)置主窗口
self.setWindowTitle("單詞記憶軟件")
self.setGeometry(100, 100, 900, 650)
self.center_window()
# 設(shè)置全局字體
font = QFont()
font.setPointSize(12)
QApplication.setFont(font)
# 主窗口布局
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.main_layout = QVBoxLayout(self.central_widget)
self.main_layout.setContentsMargins(20, 20, 20, 20)
self.main_layout.setSpacing(15)
# 初始化UI組件
self.setup_top_bar()
self.setup_mode_selector()
self.setup_stacked_widget()
self.setup_bottom_controls()
self.setup_test_results_widget()
# 初始化狀態(tài)
self.current_level = "高考"
self.current_mode = 0
self.current_index = 0
except Exception as e:
self.show_error_message(f"UI初始化失敗: {str(e)}")
raise
def load_word_database(self):
"""加載單詞數(shù)據(jù)庫(kù)"""
self.word_db = {
"考研": [
{"word": "abandon", "meaning": "放棄,遺棄"},
{"word": "abstract", "meaning": "抽象的,摘要"},
{"word": "academic", "meaning": "學(xué)術(shù)的,學(xué)院的"},
{"word": "access", "meaning": "進(jìn)入,使用權(quán)"},
{"word": "accompany", "meaning": "陪伴,伴隨"},
{"word": "accomplish", "meaning": "完成,實(shí)現(xiàn)"},
{"word": "accurate", "meaning": "精確的,準(zhǔn)確的"},
{"word": "accuse", "meaning": "指責(zé),控告"},
{"word": "achieve", "meaning": "達(dá)到,完成"},
{"word": "acknowledge", "meaning": "承認(rèn),答謝"},
{"word": "acquire", "meaning": "獲得,學(xué)到"},
{"word": "adapt", "meaning": "適應(yīng),改編"},
{"word": "adequate", "meaning": "足夠的,適當(dāng)?shù)?},
{"word": "adjust", "meaning": "調(diào)整,使適應(yīng)"},
{"word": "administration", "meaning": "管理,行政部門"},
{"word": "admit", "meaning": "承認(rèn),準(zhǔn)許進(jìn)入"},
{"word": "adopt", "meaning": "采用,收養(yǎng)"},
{"word": "advance", "meaning": "前進(jìn),進(jìn)展"},
{"word": "advantage", "meaning": "優(yōu)勢(shì),有利條件"},
{"word": "advertise", "meaning": "做廣告,宣傳"},
{"word": "affect", "meaning": "影響,感動(dòng)"},
{"word": "afford", "meaning": "負(fù)擔(dān)得起,提供"},
{"word": "agency", "meaning": "代理,機(jī)構(gòu)"},
{"word": "aggressive", "meaning": "侵略的,有進(jìn)取心的"},
{"word": "alarm", "meaning": "警報(bào),驚慌"},
{"word": "alert", "meaning": "警覺(jué)的,警惕的"},
{"word": "alliance", "meaning": "聯(lián)盟,聯(lián)合"},
{"word": "allow", "meaning": "允許,承認(rèn)"},
{"word": "alter", "meaning": "改變,修改"},
{"word": "amaze", "meaning": "使吃驚"},
{"word": "ambition", "meaning": "雄心,抱負(fù)"},
{"word": "amount", "meaning": "數(shù)量,總額"},
{"word": "amuse", "meaning": "使發(fā)笑,使愉快"},
{"word": "analyze", "meaning": "分析,分解"},
{"word": "announce", "meaning": "宣布,通告"},
{"word": "annoy", "meaning": "使惱怒,打擾"},
{"word": "anticipate", "meaning": "預(yù)期,期望"},
{"word": "anxiety", "meaning": "焦慮,渴望"},
{"word": "apologize", "meaning": "道歉,辯白"},
{"word": "apparent", "meaning": "顯然的,表面上的"},
{"word": "appeal", "meaning": "呼吁,上訴"},
{"word": "appear", "meaning": "出現(xiàn),似乎"},
{"word": "appliance", "meaning": "用具,器具"},
{"word": "apply", "meaning": "申請(qǐng),應(yīng)用"},
{"word": "appoint", "meaning": "任命,指定"},
{"word": "appreciate", "meaning": "欣賞,感激"},
{"word": "approach", "meaning": "接近,方法"},
{"word": "approve", "meaning": "批準(zhǔn),贊成"},
{"word": "argue", "meaning": "爭(zhēng)論,辯論"}
],
"高考": [
{"word": "book", "meaning": "書,預(yù)訂"},
{"word": "apple", "meaning": "蘋果"},
{"word": "computer", "meaning": "計(jì)算機(jī)"},
{"word": "school", "meaning": "學(xué)校"},
{"word": "teacher", "meaning": "老師"},
{"word": "student", "meaning": "學(xué)生"},
{"word": "classroom", "meaning": "教室"},
{"word": "homework", "meaning": "家庭作業(yè)"},
{"word": "examination", "meaning": "考試"},
{"word": "dictionary", "meaning": "字典"},
{"word": "library", "meaning": "圖書館"},
{"word": "knowledge", "meaning": "知識(shí)"},
{"word": "education", "meaning": "教育"},
{"word": "university", "meaning": "大學(xué)"},
{"word": "college", "meaning": "學(xué)院"},
{"word": "subject", "meaning": "科目,主題"},
{"word": "mathematics", "meaning": "數(shù)學(xué)"},
{"word": "physics", "meaning": "物理"},
{"word": "chemistry", "meaning": "化學(xué)"},
{"word": "biology", "meaning": "生物"},
{"word": "geography", "meaning": "地理"},
{"word": "history", "meaning": "歷史"},
{"word": "language", "meaning": "語(yǔ)言"},
{"word": "literature", "meaning": "文學(xué)"},
{"word": "philosophy", "meaning": "哲學(xué)"},
{"word": "economy", "meaning": "經(jīng)濟(jì)"},
{"word": "politics", "meaning": "政治"},
{"word": "culture", "meaning": "文化"},
{"word": "society", "meaning": "社會(huì)"},
{"word": "development", "meaning": "發(fā)展"},
{"word": "technology", "meaning": "技術(shù)"},
{"word": "science", "meaning": "科學(xué)"},
{"word": "research", "meaning": "研究"},
{"word": "experiment", "meaning": "實(shí)驗(yàn)"},
{"word": "discovery", "meaning": "發(fā)現(xiàn)"},
{"word": "invention", "meaning": "發(fā)明"},
{"word": "information", "meaning": "信息"},
{"word": "communication", "meaning": "交流,通訊"},
{"word": "internet", "meaning": "互聯(lián)網(wǎng)"},
{"word": "software", "meaning": "軟件"},
{"word": "hardware", "meaning": "硬件"},
{"word": "program", "meaning": "程序,節(jié)目"},
{"word": "system", "meaning": "系統(tǒng)"},
{"word": "database", "meaning": "數(shù)據(jù)庫(kù)"},
{"word": "network", "meaning": "網(wǎng)絡(luò)"},
{"word": "security", "meaning": "安全"},
{"word": "password", "meaning": "密碼"},
{"word": "account", "meaning": "賬戶"},
{"word": "application", "meaning": "應(yīng)用,申請(qǐng)"}
],
"四級(jí)": [
{"word": "examination", "meaning": "考試,檢查"},
{"word": "frequent", "meaning": "頻繁的"},
{"word": "generation", "meaning": "一代,產(chǎn)生"},
{"word": "global", "meaning": "全球的"},
{"word": "graduate", "meaning": "畢業(yè),畢業(yè)生"},
{"word": "habit", "meaning": "習(xí)慣"},
{"word": "handle", "meaning": "處理,把手"},
{"word": "harm", "meaning": "傷害,損害"},
{"word": "huge", "meaning": "巨大的"},
{"word": "human", "meaning": "人類,人的"},
{"word": "hurry", "meaning": "匆忙"},
{"word": "ideal", "meaning": "理想的,理想"},
{"word": "identify", "meaning": "識(shí)別,鑒定"},
{"word": "ignore", "meaning": "忽視,不理睬"},
{"word": "illustrate", "meaning": "說(shuō)明,闡明"},
{"word": "image", "meaning": "形象,圖像"},
{"word": "imagine", "meaning": "想象"},
{"word": "immediate", "meaning": "立即的,直接的"},
{"word": "impact", "meaning": "影響,沖擊"},
{"word": "implement", "meaning": "實(shí)施,工具"},
{"word": "imply", "meaning": "暗示,意味"},
{"word": "import", "meaning": "進(jìn)口,輸入"},
{"word": "impress", "meaning": "給...留下印象"},
{"word": "improve", "meaning": "改進(jìn),提高"},
{"word": "include", "meaning": "包括,包含"},
{"word": "income", "meaning": "收入,收益"},
{"word": "increase", "meaning": "增加,增長(zhǎng)"},
{"word": "indeed", "meaning": "確實(shí),的確"},
{"word": "indicate", "meaning": "表明,指出"},
{"word": "individual", "meaning": "個(gè)人的,個(gè)體"},
{"word": "industry", "meaning": "工業(yè),產(chǎn)業(yè)"},
{"word": "influence", "meaning": "影響"},
{"word": "inform", "meaning": "通知,告知"},
{"word": "initial", "meaning": "最初的,開始的"},
{"word": "injure", "meaning": "傷害,損害"},
{"word": "inner", "meaning": "內(nèi)部的,內(nèi)心的"},
{"word": "innocent", "meaning": "無(wú)辜的,天真的"},
{"word": "inquire", "meaning": "詢問(wèn),調(diào)查"},
{"word": "insert", "meaning": "插入,嵌入"},
{"word": "insist", "meaning": "堅(jiān)持,堅(jiān)決要求"},
{"word": "inspect", "meaning": "檢查,視察"},
{"word": "inspire", "meaning": "鼓舞,激勵(lì)"},
{"word": "install", "meaning": "安裝,安置"},
{"word": "instance", "meaning": "例子,實(shí)例"},
{"word": "instant", "meaning": "立即的,即時(shí)的"},
{"word": "instead", "meaning": "代替,反而"},
{"word": "institute", "meaning": "學(xué)會(huì),研究所"},
{"word": "instruct", "meaning": "指導(dǎo),命令"},
{"word": "instrument", "meaning": "儀器,工具"}
],
"六級(jí)": [
{"word": "phenomenon", "meaning": "現(xiàn)象"},
{"word": "quintessential", "meaning": "精髓的"},
{"word": "resilience", "meaning": "恢復(fù)力"},
{"word": "scrutinize", "meaning": "仔細(xì)檢查"},
{"word": "spontaneous", "meaning": "自發(fā)的"},
{"word": "subsequent", "meaning": "隨后的"},
{"word": "substantial", "meaning": "大量的,實(shí)質(zhì)的"},
{"word": "subtle", "meaning": "微妙的,精細(xì)的"},
{"word": "sufficient", "meaning": "足夠的"},
{"word": "superficial", "meaning": "表面的,膚淺的"},
{"word": "supplement", "meaning": "補(bǔ)充,增刊"},
{"word": "suppress", "meaning": "壓制,抑制"},
{"word": "surpass", "meaning": "超越,勝過(guò)"},
{"word": "suspend", "meaning": "暫停,懸掛"},
{"word": "sustain", "meaning": "維持,支撐"},
{"word": "symposium", "meaning": "研討會(huì)"},
{"word": "synthesis", "meaning": "合成,綜合"},
{"word": "tangible", "meaning": "有形的,實(shí)際的"},
{"word": "tedious", "meaning": "冗長(zhǎng)乏味的"},
{"word": "temperament", "meaning": "氣質(zhì),性情"},
{"word": "temporary", "meaning": "暫時(shí)的"},
{"word": "tentative", "meaning": "試探性的"},
{"word": "terminate", "meaning": "終止,結(jié)束"},
{"word": "terrific", "meaning": "極好的,可怕的"},
{"word": "testify", "meaning": "證明,作證"},
{"word": "therapy", "meaning": "治療,療法"},
{"word": "threshold", "meaning": "門檻,開端"},
{"word": "thrive", "meaning": "繁榮,興旺"},
{"word": "tolerant", "meaning": "寬容的"},
{"word": "tragedy", "meaning": "悲劇,慘案"},
{"word": "transaction", "meaning": "交易"},
{"word": "transcend", "meaning": "超越"},
{"word": "transient", "meaning": "短暫的"},
{"word": "transition", "meaning": "過(guò)渡,轉(zhuǎn)變"},
{"word": "transparent", "meaning": "透明的"},
{"word": "trivial", "meaning": "瑣碎的"},
{"word": "turbulent", "meaning": "動(dòng)蕩的"},
{"word": "ultimate", "meaning": "最終的"},
{"word": "unanimous", "meaning": "全體一致的"},
{"word": "underestimate", "meaning": "低估"},
{"word": "underlying", "meaning": "潛在的"},
{"word": "undertake", "meaning": "承擔(dān),從事"},
{"word": "unfold", "meaning": "展開,顯露"},
{"word": "unify", "meaning": "統(tǒng)一"},
{"word": "unique", "meaning": "獨(dú)特的"},
{"word": "universal", "meaning": "普遍的"},
{"word": "update", "meaning": "更新"},
{"word": "upgrade", "meaning": "升級(jí)"},
{"word": "uphold", "meaning": "支持,維護(hù)"},
{"word": "utilize", "meaning": "利用"}
]
}
self.current_words = self.word_db[self.current_level]
def shuffle_words(self):
"""打亂當(dāng)前詞庫(kù)的單詞順序"""
random.shuffle(self.current_words)
self.current_index = 0
def setup_connections(self):
"""設(shè)置信號(hào)槽連接"""
for level, btn in self.level_buttons.items():
btn.clicked.connect(lambda checked, l=level: self.change_level(l))
for i in range(self.mode_group.buttons().__len__()):
self.mode_group.button(i).toggled.connect(
lambda checked, idx=i: self.change_mode(idx) if checked else None)
self.prev_btn.clicked.connect(self.prev_word)
self.next_btn.clicked.connect(self.next_word)
self.check_btn.clicked.connect(self.check_answer)
self.mode3_input.returnPressed.connect(self.check_input_mode)
self.mode3_input.textChanged.connect(self.on_input_text_changed)
# 測(cè)試模式按鈕連接
self.start_test_btn.clicked.connect(self.start_test)
self.end_test_btn.clicked.connect(self.end_test)
self.save_results_btn.clicked.connect(self.save_test_results)
def on_input_text_changed(self):
"""處理輸入框文本變化事件"""
if self.input_error_shown:
self.mode3_input.clear()
self.mode3_feedback.clear()
self.input_error_shown = False
else:
# 正常處理文本變化
pass
def setup_top_bar(self):
"""設(shè)置頂部選擇欄"""
self.top_bar = QHBoxLayout()
self.level_buttons = {}
for level in ["高考", "四級(jí)", "六級(jí)", "考研"]:
btn = QPushButton(level)
btn.setStyleSheet("""
QPushButton {
padding: 8px 15px;
font-weight: bold;
border-radius: 5px;
background-color: #f0f0f0;
}
QPushButton:hover {
background-color: #e0e0e0;
}
QPushButton:pressed {
background-color: #d0d0d0;
}
""")
self.level_buttons[level] = btn
self.top_bar.addWidget(btn)
self.main_layout.addLayout(self.top_bar)
def setup_mode_selector(self):
"""設(shè)置模式選擇器"""
self.mode_group = QButtonGroup()
self.mode_layout = QHBoxLayout()
for i, mode in enumerate(["拆分模式", "填空模式", "輸入模式", "測(cè)試模式"]):
rb = QRadioButton(mode)
rb.setStyleSheet("""
QRadioButton {
spacing: 5px;
font-weight: bold;
}
QRadioButton::indicator {
width: 16px;
height: 16px;
}
""")
self.mode_group.addButton(rb, i)
self.mode_layout.addWidget(rb)
if i == 0:
rb.setChecked(True)
self.main_layout.addLayout(self.mode_layout)
def setup_stacked_widget(self):
"""設(shè)置堆疊窗口"""
self.stacked_widget = QStackedWidget()
self.mode1_widget = self.create_mode1_widget()
self.stacked_widget.addWidget(self.mode1_widget)
self.mode2_widget = self.create_mode2_widget()
self.stacked_widget.addWidget(self.mode2_widget)
self.mode3_widget = self.create_mode3_widget()
self.stacked_widget.addWidget(self.mode3_widget)
self.mode4_widget = self.create_test_mode_widget()
self.stacked_widget.addWidget(self.mode4_widget)
self.main_layout.addWidget(self.stacked_widget)
def create_mode1_widget(self):
"""創(chuàng)建拆分模式組件"""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(20)
self.mode1_meaning = QLabel()
self.mode1_meaning.setAlignment(Qt.AlignCenter)
self.mode1_meaning.setStyleSheet("""
QLabel {
font-size: 20px;
font-weight: bold;
color: #333;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
border: 1px solid #ddd;
}
""")
layout.addWidget(self.mode1_meaning)
self.mode1_chunks_container = QWidget()
self.mode1_chunks_layout = QHBoxLayout(self.mode1_chunks_container)
self.mode1_chunks_layout.setSpacing(10)
layout.addWidget(self.mode1_chunks_container)
self.mode1_answer = QLabel()
self.mode1_answer.setAlignment(Qt.AlignCenter)
self.mode1_answer.setStyleSheet("""
QLabel {
font-size: 28px;
font-weight: bold;
min-height: 50px;
border: 2px dashed #aaa;
border-radius: 8px;
padding: 10px;
}
""")
layout.addWidget(self.mode1_answer)
self.mode1_feedback = QLabel()
self.mode1_feedback.setAlignment(Qt.AlignCenter)
self.mode1_feedback.setStyleSheet("""
QLabel {
font-size: 16px;
min-height: 30px;
}
""")
layout.addWidget(self.mode1_feedback)
return widget
def create_mode2_widget(self):
"""創(chuàng)建填空模式組件"""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(20)
self.mode2_meaning = QLabel()
self.mode2_meaning.setAlignment(Qt.AlignCenter)
self.mode2_meaning.setStyleSheet("""
QLabel {
font-size: 20px;
font-weight: bold;
color: #333;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
border: 1px solid #ddd;
}
""")
layout.addWidget(self.mode2_meaning)
self.mode2_blank = QLabel()
self.mode2_blank.setAlignment(Qt.AlignCenter)
self.mode2_blank.setStyleSheet("""
QLabel {
font-size: 28px;
font-weight: bold;
min-height: 50px;
padding: 10px;
}
""")
layout.addWidget(self.mode2_blank)
self.mode2_options_container = QWidget()
self.mode2_options_layout = QHBoxLayout(self.mode2_options_container)
self.mode2_options_layout.setSpacing(15)
layout.addWidget(self.mode2_options_container)
self.mode2_feedback = QLabel()
self.mode2_feedback.setAlignment(Qt.AlignCenter)
self.mode2_feedback.setStyleSheet("""
QLabel {
font-size: 16px;
min-height: 30px;
}
""")
layout.addWidget(self.mode2_feedback)
return widget
def create_mode3_widget(self):
"""創(chuàng)建輸入模式組件"""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(20)
self.mode3_meaning = QLabel()
self.mode3_meaning.setAlignment(Qt.AlignCenter)
self.mode3_meaning.setStyleSheet("""
QLabel {
font-size: 20px;
font-weight: bold;
color: #333;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
border: 1px solid #ddd;
}
""")
layout.addWidget(self.mode3_meaning)
self.mode3_input = QLineEdit()
self.mode3_input.setStyleSheet("""
QLineEdit {
font-size: 24px;
padding: 10px;
border: 2px solid #ccc;
border-radius: 8px;
}
QLineEdit:focus {
border: 2px solid #6a9eda;
}
""")
self.mode3_input.setAlignment(Qt.AlignCenter)
layout.addWidget(self.mode3_input)
self.mode3_feedback = QLabel()
self.mode3_feedback.setAlignment(Qt.AlignCenter)
self.mode3_feedback.setStyleSheet("""
QLabel {
font-size: 16px;
min-height: 30px;
}
""")
layout.addWidget(self.mode3_feedback)
return widget
def create_test_mode_widget(self):
"""創(chuàng)建測(cè)試模式組件"""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(20)
# 測(cè)試模式控制按鈕
self.test_controls = QHBoxLayout()
self.start_test_btn = QPushButton("開始測(cè)試")
self.start_test_btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #4CAF50;
color: white;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3d8b40;
}
""")
self.end_test_btn = QPushButton("結(jié)束測(cè)試")
self.end_test_btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #f44336;
color: white;
}
QPushButton:hover {
background-color: #d32f2f;
}
QPushButton:pressed {
background-color: #b71c1c;
}
""")
self.end_test_btn.setEnabled(False)
self.test_controls.addWidget(self.start_test_btn)
self.test_controls.addWidget(self.end_test_btn)
layout.addLayout(self.test_controls)
# 測(cè)試模式顯示區(qū)域
self.test_info_label = QLabel("準(zhǔn)備開始測(cè)試...")
self.test_info_label.setAlignment(Qt.AlignCenter)
self.test_info_label.setStyleSheet("""
QLabel {
font-size: 18px;
font-weight: bold;
color: #333;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
border: 1px solid #ddd;
}
""")
layout.addWidget(self.test_info_label)
# 測(cè)試模式輸入?yún)^(qū)域
self.test_input = QLineEdit()
self.test_input.setStyleSheet("""
QLineEdit {
font-size: 24px;
padding: 10px;
border: 2px solid #ccc;
border-radius: 8px;
}
QLineEdit:focus {
border: 2px solid #6a9eda;
}
""")
self.test_input.setAlignment(Qt.AlignCenter)
self.test_input.setEnabled(False)
self.test_input.returnPressed.connect(self.check_test_answer)
layout.addWidget(self.test_input)
# 測(cè)試反饋
self.test_feedback = QLabel()
self.test_feedback.setAlignment(Qt.AlignCenter)
self.test_feedback.setStyleSheet("""
QLabel {
font-size: 16px;
min-height: 30px;
}
""")
layout.addWidget(self.test_feedback)
return widget
def setup_test_results_widget(self):
"""設(shè)置測(cè)試結(jié)果組件"""
try:
# 創(chuàng)建結(jié)果部件(如果不存在)
if not hasattr(self, 'test_results_widget'):
self.test_results_widget = QWidget()
self.test_results_layout = QVBoxLayout(self.test_results_widget)
self.test_results_layout.setContentsMargins(20, 20, 20, 20)
self.test_results_title = QLabel("測(cè)試結(jié)果")
self.test_results_title.setAlignment(Qt.AlignCenter)
self.test_results_title.setStyleSheet("""
QLabel {
font-size: 20px;
font-weight: bold;
color: #333;
padding: 10px;
}
""")
self.test_results_layout.addWidget(self.test_results_title)
self.test_results_text = QTextEdit()
self.test_results_text.setReadOnly(True)
self.test_results_text.setStyleSheet("""
QTextEdit {
font-size: 14px;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
}
""")
self.test_results_layout.addWidget(self.test_results_text)
self.save_results_btn = QPushButton("保存結(jié)果")
self.save_results_btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #2196F3;
color: white;
}
QPushButton:hover {
background-color: #0b7dda;
}
QPushButton:pressed {
background-color: #0a68b4;
}
""")
self.test_results_layout.addWidget(self.save_results_btn)
# 返回按鈕
self.return_btn = QPushButton("返回主界面")
self.return_btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #6c757d;
color: white;
}
QPushButton:hover {
background-color: #5a6268;
}
QPushButton:pressed {
background-color: #484e53;
}
""")
self.return_btn.clicked.connect(self.return_to_main)
self.test_results_layout.addWidget(self.return_btn)
# 初始隱藏結(jié)果窗口
self.test_results_widget.hide()
except Exception as e:
self.show_error_message(f"測(cè)試結(jié)果部件初始化失敗: {str(e)}")
raise
def setup_bottom_controls(self):
"""設(shè)置底部控制按鈕"""
self.bottom_layout = QHBoxLayout()
self.bottom_layout.setSpacing(20)
self.prev_btn = QPushButton("上一個(gè)")
self.next_btn = QPushButton("下一個(gè)")
self.check_btn = QPushButton("檢查")
for btn in [self.prev_btn, self.next_btn, self.check_btn]:
btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #4CAF50;
color: white;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3d8b40;
}
QPushButton:disabled {
background-color: #cccccc;
}
""")
btn.setMinimumWidth(100)
self.bottom_layout.addStretch()
self.bottom_layout.addWidget(self.prev_btn)
self.bottom_layout.addWidget(self.next_btn)
self.bottom_layout.addWidget(self.check_btn)
self.bottom_layout.addStretch()
self.main_layout.addLayout(self.bottom_layout)
def change_level(self, level):
"""切換詞庫(kù)級(jí)別"""
try:
self.current_level = level
self.current_words = self.word_db[level]
self.shuffle_words()
self.show_word()
self.clear_feedback()
except Exception as e:
self.show_error_message(f"切換級(jí)別失敗: {str(e)}")
def change_mode(self, mode):
"""切換練習(xí)模式"""
try:
self.current_mode = mode
self.stacked_widget.setCurrentIndex(mode)
if mode == 3:
self.test_mode = False
self.test_info_label.setText("準(zhǔn)備開始測(cè)試...")
self.test_input.setEnabled(False)
self.test_feedback.clear()
else:
if self.test_mode:
self.end_test()
self.show_word()
self.clear_feedback()
except Exception as e:
self.show_error_message(f"切換模式失敗: {str(e)}")
def clear_feedback(self):
"""清除所有反饋信息"""
if hasattr(self, 'mode1_feedback'):
self.mode1_feedback.clear()
if hasattr(self, 'mode2_feedback'):
self.mode2_feedback.clear()
if hasattr(self, 'mode3_feedback'):
self.mode3_feedback.clear()
self.input_error_shown = False
if hasattr(self, 'test_feedback'):
self.test_feedback.clear()
def show_word(self):
"""顯示當(dāng)前單詞"""
try:
if not self.current_words or self.current_index >= len(self.current_words):
return
word_data = self.current_words[self.current_index]
word = word_data["word"]
meaning = word_data["meaning"]
if self.current_mode == 0:
self.setup_mode1(word, meaning)
elif self.current_mode == 1:
self.setup_mode2(word, meaning)
elif self.current_mode == 2:
self.setup_mode3(meaning)
except Exception as e:
self.show_error_message(f"顯示單詞失敗: {str(e)}")
def setup_mode1(self, word, meaning):
"""設(shè)置拆分模式"""
self.mode1_meaning.setText(meaning)
self.mode1_answer.clear()
self.mode1_feedback.clear()
# 清理舊按鈕
while self.mode1_chunks_layout.count():
item = self.mode1_chunks_layout.takeAt(0)
if item.widget():
item.widget().deleteLater()
# 安全拆分單詞
if len(word) < 2:
chunks = [word]
else:
n = min(3, max(2, len(word) // 2))
split_points = sorted(random.sample(range(1, len(word)), n - 1))
chunks = []
start = 0
for point in split_points:
chunks.append(word[start:point])
start = point
chunks.append(word[start:])
# 打亂順序
random.shuffle(chunks)
# 創(chuàng)建新按鈕
self.chunk_buttons = []
self.selected_chunks = [] # 存儲(chǔ)字典:{"chunk": 片段, "button": 按鈕, "position": 位置}
for chunk in chunks:
btn = QPushButton(chunk)
btn.setStyleSheet("""
QPushButton {
font-size: 18px;
padding: 10px 15px;
background-color: #e7f3fe;
border: 1px solid #b8daff;
border-radius: 5px;
}
QPushButton:hover {
background-color: #d0e3fc;
}
QPushButton:pressed {
background-color: #b8daff;
}
QPushButton:disabled {
background-color: #f8f9fa;
color: #6c757d;
border: 1px solid #dee2e6;
}
""")
btn.clicked.connect(lambda _, c=chunk, b=btn: self.toggle_chunk(c, b))
self.mode1_chunks_layout.addWidget(btn)
self.chunk_buttons.append(btn)
self.correct_word = word
self.user_answer = ""
def toggle_chunk(self, chunk, button):
"""切換單詞塊的選擇狀態(tài)"""
try:
if button.isEnabled():
# 選擇片段
self.user_answer += chunk
self.mode1_answer.setText(self.user_answer)
button.setEnabled(False)
# 記錄片段信息
self.selected_chunks.append({
"chunk": chunk,
"button": button,
"position": len(self.user_answer) - len(chunk)
})
self.mode1_feedback.clear()
# 檢查是否完成
if len(self.user_answer) == len(self.correct_word):
if self.user_answer == self.correct_word:
self.mode1_answer.setStyleSheet("color: #28a745;")
self.mode1_feedback.setText("? 正確!")
self.mode1_feedback.setStyleSheet("color: #28a745;")
QTimer.singleShot(1000, self.next_word)
else:
self.mode1_answer.setStyleSheet("color: #dc3545;")
self.mode1_feedback.setText(f"? 錯(cuò)誤!正確答案是: {self.correct_word}")
self.mode1_feedback.setStyleSheet("color: #dc3545;")
QTimer.singleShot(1000, self.reset_mode1)
else:
# 取消選擇片段
for i, selected in enumerate(self.selected_chunks):
if selected["button"] == button:
# 從答案中移除該片段
pos = selected["position"]
chunk_len = len(selected["chunk"])
self.user_answer = self.user_answer[:pos] + self.user_answer[pos + chunk_len:]
self.mode1_answer.setText(self.user_answer)
button.setEnabled(True)
# 更新其他片段的位置
for other in self.selected_chunks[i + 1:]:
other["position"] -= chunk_len
# 從列表中移除
self.selected_chunks.pop(i)
break
except Exception as e:
self.show_error_message(f"切換單詞塊狀態(tài)失敗: {str(e)}")
def reset_mode1(self):
"""重置拆分模式"""
try:
# 重新啟用所有已選擇的塊按鈕
for selected in self.selected_chunks:
selected["button"].setEnabled(True)
# 清空已選擇塊列表
self.selected_chunks = []
# 清空答案和反饋
self.mode1_answer.clear()
self.mode1_feedback.clear()
self.user_answer = ""
except Exception as e:
self.show_error_message(f"重置拆分模式失敗: {str(e)}")
def setup_mode2(self, word, meaning):
"""設(shè)置填空模式"""
try:
self.mode2_meaning.setText(meaning)
self.mode2_feedback.clear()
# 清理舊選項(xiàng)
while self.mode2_options_layout.count():
item = self.mode2_options_layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
del item
# 創(chuàng)建填空
if len(word) <= 3:
blank_pos = len(word) // 2
blank_len = len(word) - blank_pos
correct_part = word[blank_pos:]
else:
blank_len = min(3, max(2, len(word) // 3))
blank_pos = random.randint(1, len(word) - blank_len - 1)
correct_part = word[blank_pos:blank_pos + blank_len]
blank_word = word[:blank_pos] + "_" * blank_len + word[blank_pos + blank_len:]
self.mode2_blank.setText(blank_word)
# 生成選項(xiàng)
options = [correct_part]
chars = list(set(word) - set(correct_part))
while len(options) < 4:
if chars:
option = ''.join(random.sample(chars, min(len(chars), blank_len)))
else:
option = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=blank_len))
if option != correct_part and option not in options:
options.append(option)
random.shuffle(options)
# 創(chuàng)建選項(xiàng)按鈕
self.option_buttons = [] # 保存按鈕引用防止被垃圾回收
for option in options:
btn = QPushButton(option)
btn.setStyleSheet("""
QPushButton {
font-size: 18px;
padding: 10px 15px;
background-color: #e7f3fe;
border: 1px solid #b8daff;
border-radius: 5px;
}
QPushButton:hover {
background-color: #d0e3fc;
}
QPushButton:pressed {
background-color: #b8daff;
}
""")
btn.clicked.connect(lambda _, o=option, cp=correct_part, w=word: self.check_fill_answer(o, cp, w))
self.mode2_options_layout.addWidget(btn)
self.option_buttons.append(btn) # 保存按鈕引用
except Exception as e:
self.show_error_message(f"設(shè)置填空模式失敗: {str(e)}")
def check_fill_answer(self, selected, correct, full_word):
"""檢查填空答案"""
try:
if selected == correct:
# 顯示完整單詞
self.mode2_blank.setText(full_word)
self.mode2_feedback.setText("? 正確!")
self.mode2_feedback.setStyleSheet("color: #28a745;")
QTimer.singleShot(1000, self.next_word)
else:
self.mode2_feedback.setText(f"? 錯(cuò)誤!正確答案是: {correct}")
self.mode2_feedback.setStyleSheet("color: #dc3545;")
except Exception as e:
self.show_error_message(f"檢查填空答案失敗: {str(e)}")
def setup_mode3(self, meaning):
"""設(shè)置輸入模式"""
try:
self.mode3_meaning.setText(meaning)
self.mode3_input.clear()
self.mode3_feedback.clear()
self.mode3_input.setFocus()
self.input_error_shown = False
except Exception as e:
self.show_error_message(f"設(shè)置輸入模式失敗: {str(e)}")
def check_input_mode(self):
"""檢查輸入模式答案"""
try:
user_input = self.mode3_input.text().strip().lower()
correct = self.current_words[self.current_index]["word"].lower()
if user_input == correct:
self.mode3_feedback.setText("? 正確!")
self.mode3_feedback.setStyleSheet("color: #28a745;")
QTimer.singleShot(1000, self.next_word)
else:
self.mode3_feedback.setText(f"? 錯(cuò)誤!正確答案是: {correct}")
self.mode3_feedback.setStyleSheet("color: #dc3545;")
self.input_error_shown = True
except Exception as e:
self.show_error_message(f"檢查輸入答案失敗: {str(e)}")
def check_answer(self):
"""檢查答案"""
try:
if self.current_mode == 2:
self.check_input_mode()
except Exception as e:
self.show_error_message(f"檢查答案失敗: {str(e)}")
def prev_word(self):
"""上一個(gè)單詞"""
try:
if self.current_index > 0:
self.current_index -= 1
self.show_word()
self.clear_feedback()
except Exception as e:
self.show_error_message(f"切換上一個(gè)單詞失敗: {str(e)}")
def next_word(self):
"""下一個(gè)單詞"""
try:
if self.current_index < len(self.current_words) - 1:
self.current_index += 1
self.show_word()
self.clear_feedback()
else:
if self.current_mode == 0:
self.mode1_feedback.setText("?? 已完成當(dāng)前詞庫(kù)所有單詞!")
self.mode1_feedback.setStyleSheet("color: #17a2b8;")
elif self.current_mode == 1:
self.mode2_feedback.setText("?? 已完成當(dāng)前詞庫(kù)所有單詞!")
self.mode2_feedback.setStyleSheet("color: #17a2b8;")
elif self.current_mode == 2:
self.mode3_feedback.setText("?? 已完成當(dāng)前詞庫(kù)所有單詞!")
self.mode3_feedback.setStyleSheet("color: #17a2b8;")
self.shuffle_words()
QTimer.singleShot(2000, self.show_word)
except Exception as e:
self.show_error_message(f"切換下一個(gè)單詞失敗: {str(e)}")
def start_test(self):
"""開始測(cè)試"""
try:
self.test_mode = True
self.test_results = {}
self.current_test_words = self.current_words.copy()
random.shuffle(self.current_test_words)
self.test_index = 0
for word_data in self.current_test_words:
word = word_data["word"]
self.test_results[word] = {"errors": 0, "meaning": word_data["meaning"]}
self.start_test_btn.setEnabled(False)
self.end_test_btn.setEnabled(True)
self.test_input.setEnabled(True)
self.test_input.clear()
self.test_feedback.clear()
self.show_next_test_word()
except Exception as e:
self.show_error_message(f"開始測(cè)試失敗: {str(e)}")
def show_next_test_word(self):
"""顯示下一個(gè)測(cè)試單詞"""
if self.test_index < len(self.current_test_words):
word_data = self.current_test_words[self.test_index]
self.test_info_label.setText(
f"測(cè)試中 ({self.test_index + 1}/{len(self.current_test_words)}): {word_data['meaning']}")
self.test_input.clear()
self.test_feedback.clear()
self.test_input.setFocus()
else:
self.end_test()
def check_test_answer(self):
"""檢查測(cè)試答案"""
try:
if not self.test_mode:
return
user_input = self.test_input.text().strip().lower()
current_word_data = self.current_test_words[self.test_index]
correct_word = current_word_data["word"].lower()
if user_input == correct_word:
self.test_feedback.setText("? 正確!")
self.test_feedback.setStyleSheet("color: #28a745;")
self.test_index += 1
QTimer.singleShot(1000, self.show_next_test_word)
else:
self.test_feedback.setText(f"? 錯(cuò)誤!正確答案是: {correct_word}")
self.test_feedback.setStyleSheet("color: #dc3545;")
self.test_results[correct_word]["errors"] += 1
self.test_input_error_shown = True # 標(biāo)記錯(cuò)誤已顯示
# 設(shè)置定時(shí)器,1秒后清除錯(cuò)誤信息
QTimer.singleShot(1000, self.clear_test_input)
except Exception as e:
self.show_error_message(f"檢查測(cè)試答案失敗: {str(e)}")
def clear_test_input(self):
"""清除測(cè)試輸入框和錯(cuò)誤信息"""
if hasattr(self, 'test_input_error_shown') and self.test_input_error_shown:
self.test_input.clear()
self.test_feedback.clear()
self.test_input_error_shown = False
self.test_input.setFocus()
def setup_test_mode_widget(self):
"""創(chuàng)建測(cè)試模式組件"""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(20)
# 測(cè)試模式控制按鈕
self.test_controls = QHBoxLayout()
self.start_test_btn = QPushButton("開始測(cè)試")
self.start_test_btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #4CAF50;
color: white;
}
QPushButton:hover {
background-color: #45a049;
}
QPushButton:pressed {
background-color: #3d8b40;
}
""")
self.end_test_btn = QPushButton("結(jié)束測(cè)試")
self.end_test_btn.setStyleSheet("""
QPushButton {
padding: 8px 20px;
font-weight: bold;
border-radius: 5px;
background-color: #f44336;
color: white;
}
QPushButton:hover {
background-color: #d32f2f;
}
QPushButton:pressed {
background-color: #b71c1c;
}
""")
self.end_test_btn.setEnabled(False)
self.test_controls.addWidget(self.start_test_btn)
self.test_controls.addWidget(self.end_test_btn)
layout.addLayout(self.test_controls)
# 測(cè)試模式顯示區(qū)域
self.test_info_label = QLabel("準(zhǔn)備開始測(cè)試...")
self.test_info_label.setAlignment(Qt.AlignCenter)
self.test_info_label.setStyleSheet("""
QLabel {
font-size: 18px;
font-weight: bold;
color: #333;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
border: 1px solid #ddd;
}
""")
layout.addWidget(self.test_info_label)
# 測(cè)試模式輸入?yún)^(qū)域
self.test_input = QLineEdit()
self.test_input.setStyleSheet("""
QLineEdit {
font-size: 24px;
padding: 10px;
border: 2px solid #ccc;
border-radius: 8px;
}
QLineEdit:focus {
border: 2px solid #6a9eda;
}
""")
self.test_input.setAlignment(Qt.AlignCenter)
self.test_input.setEnabled(False)
self.test_input.returnPressed.connect(self.check_test_answer)
# 添加文本變化信號(hào)連接
self.test_input.textChanged.connect(self.on_test_input_text_changed)
layout.addWidget(self.test_input)
# 測(cè)試反饋
self.test_feedback = QLabel()
self.test_feedback.setAlignment(Qt.AlignCenter)
self.test_feedback.setStyleSheet("""
QLabel {
font-size: 16px;
min-height: 30px;
}
""")
layout.addWidget(self.test_feedback)
# 初始化錯(cuò)誤顯示標(biāo)記
self.test_input_error_shown = False
# 初始化測(cè)試模式狀態(tài)
self.test_mode = False
self.test_input_error_shown = False
return widget
def on_test_input_text_changed(self):
"""處理測(cè)試輸入框文本變化事件"""
if hasattr(self, 'test_input_error_shown') and self.test_input_error_shown:
self.test_input.clear()
self.test_feedback.clear()
self.test_input_error_shown = False
def end_test(self):
"""結(jié)束測(cè)試并顯示結(jié)果"""
try:
self.test_mode = False
self.start_test_btn.setEnabled(True)
self.end_test_btn.setEnabled(False)
self.test_input.setEnabled(False)
self.test_input.clear() # 清空輸入框
self.test_feedback.clear() # 清空反饋
# 按錯(cuò)誤次數(shù)排序,錯(cuò)誤多的排在前面
sorted_results = sorted(self.test_results.items(),
key=lambda x: x[1]["errors"],
reverse=True)
result_text = f"測(cè)試完成 - {self.current_level}詞庫(kù)\n\n"
result_text += "單詞\t\t錯(cuò)誤次數(shù)\t\t釋義\n"
result_text += "-" * 60 + "\n"
for word, data in sorted_results:
result_text += f"{word.ljust(15)}\t{data['errors']}\t\t{data['meaning']}\n"
# 添加統(tǒng)計(jì)信息
total_words = len(self.test_results)
correct_words = sum(1 for word in self.test_results.values() if word["errors"] == 0)
accuracy = correct_words / total_words * 100 if total_words > 0 else 0
most_mistakes = sorted_results[0] if sorted_results else ("", {"errors": 0, "meaning": ""})
result_text += "\n統(tǒng)計(jì)信息:\n"
result_text += f"總單詞數(shù): {total_words}\n"
result_text += f"正確掌握的單詞: {correct_words}\n"
result_text += f"準(zhǔn)確率: {accuracy:.1f}%\n"
result_text += f"錯(cuò)誤最多的單詞: {most_mistakes[0]} (錯(cuò)誤 {most_mistakes[1]['errors']} 次)\n"
self.test_results_text.setText(result_text)
# 使用堆疊窗口而不是替換中央部件
if not hasattr(self, 'results_stack'):
self.results_stack = QStackedWidget()
self.results_stack.addWidget(self.central_widget)
self.results_stack.addWidget(self.test_results_widget)
self.setCentralWidget(self.results_stack)
self.results_stack.setCurrentIndex(1) # 顯示結(jié)果界面
except Exception as e:
self.show_error_message(f"結(jié)束測(cè)試失敗: {str(e)}")
def save_test_results(self):
"""保存測(cè)試結(jié)果到文件"""
try:
options = QFileDialog.Options()
file_name, _ = QFileDialog.getSaveFileName(self, "保存測(cè)試結(jié)果",
f"{self.current_level}_測(cè)試結(jié)果.txt",
"Text Files (*.txt)",
options=options)
if file_name:
with open(file_name, 'w', encoding='utf-8') as f:
f.write(self.test_results_text.toPlainText())
QMessageBox.information(self, "保存成功", "測(cè)試結(jié)果已成功保存!")
except Exception as e:
self.show_error_message(f"保存測(cè)試結(jié)果失敗: {str(e)}")
def return_to_main(self):
"""從結(jié)果界面返回到主界面"""
try:
if hasattr(self, 'results_stack'):
self.results_stack.setCurrentIndex(0) # 顯示主界面
# 重置測(cè)試模式狀態(tài)
self.test_mode = False
self.start_test_btn.setEnabled(True)
self.end_test_btn.setEnabled(False)
self.test_input.setEnabled(False)
self.test_info_label.setText("準(zhǔn)備開始測(cè)試...")
self.test_feedback.clear()
except Exception as e:
self.show_error_message(f"返回主界面失敗: {str(e)}")
def show_error_message(self, message):
"""顯示錯(cuò)誤信息"""
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setText("發(fā)生錯(cuò)誤")
msg.setInformativeText(message)
msg.setWindowTitle("錯(cuò)誤")
msg.exec_()
if __name__ == "__main__":
try:
app = QApplication(sys.argv)
app.setStyle('Fusion')
# 增加堆棧大小以防止溢出
import ctypes
ctypes.cdll.msvcrt._resetstkoflw()
window = WordPracticeApp()
window.show()
ret = app.exec_()
sys.exit(ret)
except SystemExit:
pass
except Exception as e:
print(f"致命錯(cuò)誤: {e}")
import traceback
traceback.print_exc()
sys.exit(1)


以上就是基于Python實(shí)現(xiàn)簡(jiǎn)易的單詞記憶軟件的詳細(xì)內(nèi)容,更多關(guān)于Python單詞記憶軟件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python 網(wǎng)絡(luò)編程詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了python 網(wǎng)絡(luò)編程詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-04-04
深入理解Python虛擬機(jī)之進(jìn)程、線程和協(xié)程區(qū)別詳解
在本篇文章當(dāng)中深入分析在 Python 當(dāng)中 進(jìn)程、線程和協(xié)程的區(qū)別,這三個(gè)概念會(huì)讓人非常迷惑,如果沒(méi)有深入了解這三者的實(shí)現(xiàn)原理,只是看一些文字說(shuō)明,也很難理解,在本篇文章當(dāng)中我們將通過(guò)分析部分源代碼來(lái)詳細(xì)分析一下這三者根本的區(qū)別是什么,需要的朋友可以參考下2023-10-10
關(guān)于Flask項(xiàng)目無(wú)法使用公網(wǎng)IP訪問(wèn)的解決方式
今天小編就為大家分享一篇關(guān)于Flask項(xiàng)目無(wú)法使用公網(wǎng)IP訪問(wèn)的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
Python基礎(chǔ)之字符串操作常用函數(shù)集合
這篇文章主要介紹了Python基礎(chǔ)之字符串操作常用函數(shù)集合,需要的朋友可以參考下2020-02-02
對(duì)Pyhon實(shí)現(xiàn)靜態(tài)變量全局變量的方法詳解
今天小編就為大家分享一篇對(duì)Pyhon實(shí)現(xiàn)靜態(tài)變量全局變量的方法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
手把手教你實(shí)現(xiàn)Python重試超時(shí)裝飾器
這篇文章主要為大家介紹了實(shí)現(xiàn)Python重試超時(shí)裝飾器教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2023-05-05
python socket多線程實(shí)現(xiàn)客戶端與服務(wù)器連接
這篇文章主要為大家詳細(xì)介紹了python socket多線程實(shí)現(xiàn)客戶端與服務(wù)器連接,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

