Python + Tkinter + pyttsx3实现的桌面版英语学习工具

Python + Tkinter + pyttsx3实现的桌面版英语学习工具

在多行文本框输入英文句子,双击其中的英文单词,给出英文读音和中文含义和音标。

本程序查询本地词典数据。通过菜单栏"文件"->"打开词典编辑器"进入编辑界面。

词典数据存储在独立的dictionary.json文件中,这个文件需要和程序文件在同一文件夹中,否则将自动建立一个。

编辑器实现了完整的CRUD(创建、读取、更新、删除)功能

添加单词:直接填写所有字段后点击"添加单词"

编辑单词:从列表选择单词 -> 修改字段 -> 点击"保存修改"

删除单词:从列表选择单词 -> 点击"删除选中"

pyttsx3 是一个文本到语音(Text-to-Speech, TTS)转换库,它允许你在 Python 程序中将文本转换为语音输出。它是第三方库需要安装使用。

在cmd中,使用 Python 包安装命令安装:pip install pyttsx3

若安装了多个python版本,X.Y代表Python版本,多余的部分舍弃如3.8.1取3.8,3.10.5取3.10,即只取第二个点前的部分。仅安装了一个python版本不需要。pip命令默认会连接在国外的python官方服务器下载,速度可能比较慢,使用镜像网址可以加快速度:-i 镜像网址

py -3.12 -m pip install 第三方库名 -i https://pypi.tuna.tsinghua.edu.cn/simple

py -3.12 -m pip install pyttsx3 -i https://pypi.tuna.tsinghua.edu.cn/simple

运行效果:

源码如下:

import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import json
import os
import pyttsx3

class DictionaryEditor:
    def __init__(self, master, dictionary, save_callback):
        self.master = master
        self.dictionary = dictionary
        self.save_callback = save_callback
        self.selected_word = None
        
        master.title("词典编辑器")
        master.geometry("600x400")
        
        # 创建界面组件
        self.create_widgets()
        self.update_word_list()

    def create_widgets(self):
        # 单词列表框架
        list_frame = ttk.Frame(self.master)
        list_frame.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.BOTH, expand=True)

        self.word_list = tk.Listbox(list_frame, selectmode=tk.SINGLE)
        self.word_list.pack(fill=tk.BOTH, expand=True)
        self.word_list.bind('<<ListboxSelect>>', self.on_word_select)

        # 控制按钮框架
        btn_frame = ttk.Frame(list_frame)
        btn_frame.pack(pady=5)
        
        ttk.Button(btn_frame, text="刷新列表", command=self.update_word_list).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="删除选中", command=self.delete_word).pack(side=tk.LEFT, padx=2)

        # 编辑区框架
        edit_frame = ttk.Frame(self.master)
        edit_frame.pack(side=tk.RIGHT, padx=10, pady=10, fill=tk.BOTH, expand=True)

        # 输入字段
        ttk.Label(edit_frame, text="单词:").grid(row=0, column=0, sticky=tk.W)
        self.word_entry = ttk.Entry(edit_frame)
        self.word_entry.grid(row=0, column=1, pady=5, sticky=tk.EW)

        ttk.Label(edit_frame, text="音标:").grid(row=1, column=0, sticky=tk.W)
        self.phonetic_entry = ttk.Entry(edit_frame)
        self.phonetic_entry.grid(row=1, column=1, pady=5, sticky=tk.EW)

        ttk.Label(edit_frame, text="翻译:").grid(row=2, column=0, sticky=tk.W)
        self.translation_entry = ttk.Entry(edit_frame)
        self.translation_entry.grid(row=2, column=1, pady=5, sticky=tk.EW)

        # 操作按钮
        btn_frame = ttk.Frame(edit_frame)
        btn_frame.grid(row=3, column=0, columnspan=2, pady=10)
        
        ttk.Button(btn_frame, text="添加单词", command=self.add_word).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="保存修改", command=self.save_changes).pack(side=tk.LEFT, padx=5)

    def update_word_list(self):
        self.word_list.delete(0, tk.END)
        for word in sorted(self.dictionary.keys()):
            self.word_list.insert(tk.END, word)

    def on_word_select(self, event):
        selection = self.word_list.curselection()
        if selection:
            self.selected_word = self.word_list.get(selection[0])
            self.load_word_data()

    def load_word_data(self):
        data = self.dictionary.get(self.selected_word, {})
        self.word_entry.delete(0, tk.END)
        self.phonetic_entry.delete(0, tk.END)
        self.translation_entry.delete(0, tk.END)
        
        self.word_entry.insert(0, self.selected_word)
        self.phonetic_entry.insert(0, data.get('phonetic', ''))
        self.translation_entry.insert(0, data.get('translation', ''))

    def validate_input(self):
        word = self.word_entry.get().strip().lower()
        phonetic = self.phonetic_entry.get().strip()
        translation = self.translation_entry.get().strip()
        
        if not word:
            messagebox.showwarning("输入错误", "单词不能为空")
            return False
        if not translation:
            messagebox.showwarning("输入错误", "翻译不能为空")
            return False
        return True

    def add_word(self):
        if not self.validate_input():
            return
        
        new_word = self.word_entry.get().strip().lower()
        if new_word in self.dictionary:
            messagebox.showwarning("添加失败", "该单词已存在")
            return
            
        self.dictionary[new_word] = {
            'phonetic': self.phonetic_entry.get().strip(),
            'translation': self.translation_entry.get().strip()
        }
        self.save_data()
        self.update_word_list()
        messagebox.showinfo("成功", "单词已添加")

    def save_changes(self):
        if not self.selected_word or not self.validate_input():
            return
            
        new_word = self.word_entry.get().strip().lower()
        # 检查是否修改了单词
        if new_word != self.selected_word:
            if new_word in self.dictionary:
                messagebox.showwarning("修改失败", "目标单词已存在")
                return
            # 删除旧条目并创建新条目
            del self.dictionary[self.selected_word]
            self.selected_word = new_word
            
        self.dictionary[new_word] = {
            'phonetic': self.phonetic_entry.get().strip(),
            'translation': self.translation_entry.get().strip()
        }
        self.save_data()
        self.update_word_list()
        messagebox.showinfo("成功", "修改已保存")

    def delete_word(self):
        if not self.selected_word:
            return
            
        if messagebox.askyesno("确认删除", f"确定要删除 '{self.selected_word}' 吗?"):
            del self.dictionary[self.selected_word]
            self.save_data()
            self.selected_word = None
            self.update_word_list()
            messagebox.showinfo("成功", "单词已删除")

    def save_data(self):
        self.save_callback()

class EnglishLearningApp:
    def __init__(self, master):
        self.master = master
        master.title("英语学习助手")
        master.geometry("800x600")

        # 初始化语音引擎
        self.engine = pyttsx3.init()
        self.engine.setProperty('rate', 150)

        # 创建菜单
        self.create_menu()

        # 加载词典数据
        self.dictionary_file = "dictionary.json"
        self.dictionary = self.load_dictionary()

        # 创建界面组件
        self.create_widgets()

    # 添加缺失的 load_dictionary 方法
    def load_dictionary(self):
        """从JSON文件加载词典数据"""
        try:
            if not os.path.exists(self.dictionary_file):
                # 如果文件不存在,创建默认词典
                default_data = {
                    "hello": {"translation": "你好", "phonetic": "/həˈloʊ/"},
                    "world": {"translation": "世界", "phonetic": "/wɜːrld/"}
                }
                with open(self.dictionary_file, 'w', encoding='utf-8') as f:
                    json.dump(default_data, f, indent=2, ensure_ascii=False)
                return default_data

            with open(self.dictionary_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
                # 验证数据格式
                if not all(['translation' in v and 'phonetic' in v for v in data.values()]):
                    raise ValueError("词典文件格式不正确")
                return data
        except Exception as e:
            messagebox.showerror("初始化错误", f"无法加载词典:{str(e)}")
            self.master.destroy()  # 关闭程序
            exit(1)
            
    def create_widgets(self):
        # 输入文本框
        self.text_input = tk.Text(
            self.master,
            height=10,
            wrap=tk.WORD,
            font=('Arial', 12),
            borderwidth=2,
            relief="groove"
        )
        self.text_input.pack(pady=10, padx=20, fill=tk.BOTH, expand=True)
        self.text_input.bind("<Double-Button-1>", self.on_double_click)

        # 结果显示区域
        self.result_frame = ttk.Frame(self.master)
        self.result_frame.pack(pady=10, padx=20, fill=tk.BOTH, expand=True)

        self.result_label = ttk.Label(
            self.result_frame,
            text="双击单词,出现查询结果:",
            font=('Arial', 12, 'bold')
        )
        self.result_label.pack(anchor=tk.W)

        self.result_text = tk.Text(
            self.result_frame,
            height=6,
            wrap=tk.WORD,
            font=('Arial', 12),
            bg='#f0f8ff',
            borderwidth=2,
            relief="groove"
        )
        self.result_text.pack(fill=tk.BOTH, expand=True)

        # 发音按钮
        self.speak_button = ttk.Button(
            self.master,
            text="播放发音",
            command=self.speak_word
        )
        self.speak_button.pack(pady=5)

    def on_double_click(self, event):
        try:
            # 获取选中的单词
            self.text_input.tag_remove("sel", "1.0", tk.END)
            self.text_input.tag_add("sel", "current wordstart", "current wordend")
            selected_word = self.text_input.get("sel.first", "sel.last").lower()

            if selected_word:
                # 查询词典
                result = self.dictionary.get(selected_word)
                if result:
                    self.show_result(selected_word, result)
                    self.current_word = selected_word
                else:
                    self.result_text.delete(1.0, tk.END)
                    self.result_text.insert(tk.END, f"未找到 '{selected_word}' 的释义")
        except Exception as e:
            messagebox.showerror("错误", f"发生错误:{str(e)}")

    def show_result(self, word, data):
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        
        result_str = (
            f"单词:{word}\n\n"
            f"音标:{data['phonetic']}\n\n"
            f"中文释义:{data['translation']}"
        )
        
        self.result_text.insert(tk.END, result_str)
        self.result_text.config(state=tk.DISABLED)

    def speak_word(self):
        if hasattr(self, 'current_word'):
            self.engine.say(self.current_word)
            self.engine.runAndWait()        

    def create_menu(self):
        menubar = tk.Menu(self.master)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="打开词典编辑器", command=self.open_editor)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.master.destroy) 
        menubar.add_cascade(label="文件", menu=file_menu)
        
        self.master.config(menu=menubar)

    def open_editor(self):
        editor_window = tk.Toplevel(self.master)
        DictionaryEditor(editor_window, self.dictionary, self.save_dictionary)

        
    def save_dictionary(self):
        try:
            with open(self.dictionary_file, 'w', encoding='utf-8') as f:
                json.dump(self.dictionary, f, indent=2, ensure_ascii=False)
        except Exception as e:
            messagebox.showerror("保存失败", f"无法保存词典文件:{str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = EnglishLearningApp(root)
    root.mainloop()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值