基于Python构建输入法应用是一个有趣的项目。下面我将为你提供一个完整的输入法应用设计方案,包含核心功能和实现代码。
设计思路
我将创建一个基于拼音的输入法,包含以下核心功能:
-
拼音到汉字的转换
-
候选词显示和选择
-
用户界面
-
词频调整和学习功能
项目结构
input_method/
├── main.py # 主程序入口
├── input_method.py # 输入法核心类
├── pinyin_dict.py # 拼音字典管理
├── ui.py # 用户界面
├── data/
│ └── pinyin.txt # 拼音-汉字映射数据
└── user_dict.py # 用户词典管理
核心代码实现
1. 拼音字典管理 (pinyin_dict.py)
import os
import re
class PinyinDict:
def __init__(self, dict_file='data/pinyin.txt'):
self.pinyin_map = {}
self.load_dict(dict_file)
def load_dict(self, dict_file):
"""加载拼音字典"""
if not os.path.exists(dict_file):
# 如果没有字典文件,创建基本字典
self.create_basic_dict(dict_file)
with open(dict_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
parts = line.split()
if len(parts) >= 2:
pinyin = parts[0]
chars = parts[1:]
self.pinyin_map[pinyin] = chars
def create_basic_dict(self, dict_file):
"""创建基本拼音字典"""
# 确保目录存在
os.makedirs(os.path.dirname(dict_file), exist_ok=True)
# 简单的拼音-汉字映射示例
basic_dict = {
'ni': ['你', '泥', '尼', '拟', '逆'],
'hao': ['好', '号', '豪', '浩', '耗'],
'wo': ['我', '窝', '握', '沃', '卧'],
'shi': ['是', '时', '十', '事', '市'],
'jie': ['解', '接', '节', '界', '姐'],
'mian': ['面', '免', '棉', '眠', '勉']
}
with open(dict_file, 'w', encoding='utf-8') as f:
for pinyin, chars in basic_dict.items():
f.write(f"{pinyin} {' '.join(chars)}\n")
def get_chars(self, pinyin):
"""根据拼音获取对应的汉字列表"""
return self.pinyin_map.get(pinyin, [])
def is_valid_pinyin(self, pinyin):
"""检查是否是有效拼音"""
# 简单的拼音格式检查
return bool(re.match(r'^[a-z]+$', pinyin))
2. 用户词典管理 (user_dict.py)
import json
import os
class UserDict:
def __init__(self, user_dict_file='data/user_dict.json'):
self.user_dict_file = user_dict_file
self.user_dict = {} # 格式: {"拼音": [("汉字", 词频), ...]}
self.load_user_dict()
def load_user_dict(self):
"""加载用户词典"""
if os.path.exists(self.user_dict_file):
try:
with open(self.user_dict_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# 转换格式
for pinyin, items in data.items():
self.user_dict[pinyin] = [(char, freq) for char, freq in items]
except:
self.user_dict = {}
def save_user_dict(self):
"""保存用户词典"""
# 确保目录存在
os.makedirs(os.path.dirname(self.user_dict_file), exist_ok=True)
# 转换为可JSON序列化的格式
save_data = {}
for pinyin, items in self.user_dict.items():
save_data[pinyin] = [(char, freq) for char, freq in items]
with open(self.user_dict_file, 'w', encoding='utf-8') as f:
json.dump(save_data, f, ensure_ascii=False, indent=2)
def add_word(self, pinyin, char):
"""添加用户词或更新词频"""
if pinyin not in self.user_dict:
self.user_dict[pinyin] = []
# 查找是否已存在
found = False
for i, (c, freq) in enumerate(self.user_dict[pinyin]):
if c == char:
self.user_dict[pinyin][i] = (char, freq + 1)
found = True
break
if not found:
self.user_dict[pinyin].append((char, 1))
# 按词频排序
self.user_dict[pinyin].sort(key=lambda x: x[1], reverse=True)
# 保存更改
self.save_user_dict()
def get_user_chars(self, pinyin):
"""获取用户词典中的汉字"""
if pinyin in self.user_dict:
return [char for char, freq in self.user_dict[pinyin]]
return []
3. 输入法核心类 (input_method.py)
import re
from pinyin_dict import PinyinDict
from user_dict import UserDict
class InputMethod:
def __init__(self):
self.pinyin_dict = PinyinDict()
self.user_dict = UserDict()
self.current_pinyin = ""
self.candidates = []
self.selected_index = 0
self.page_size = 5
self.current_page = 0
def input_pinyin(self, pinyin_char):
"""输入拼音字符"""
if pinyin_char.isalpha() and pinyin_char.islower():
self.current_pinyin += pinyin_char
self.update_candidates()
return True
return False
def backspace(self):
"""删除最后一个拼音字符"""
if self.current_pinyin:
self.current_pinyin = self.current_pinyin[:-1]
self.update_candidates()
return True
return False
def update_candidates(self):
"""更新候选词列表"""
if not self.current_pinyin:
self.candidates = []
self.current_page = 0
return
# 获取系统词典中的候选词
system_candidates = self.pinyin_dict.get_chars(self.current_pinyin)
# 获取用户词典中的候选词(优先显示)
user_candidates = self.user_dict.get_user_chars(self.current_pinyin)
# 合并候选词,用户词典的在前
all_candidates = user_candidates + [c for c in system_candidates if c not in user_candidates]
self.candidates = all_candidates
self.current_page = 0
self.selected_index = 0
def get_current_candidates(self):
"""获取当前页的候选词"""
start = self.current_page * self.page_size
end = start + self.page_size
return self.candidates[start:end]
def next_page(self):
"""翻到下一页"""
max_page = (len(self.candidates) - 1) // self.page_size
if self.current_page < max_page:
self.current_page += 1
self.selected_index = 0
return True
return False
def prev_page(self):
"""翻到上一页"""
if self.current_page > 0:
self.current_page -= 1
self.selected_index = 0
return True
return False
def select_candidate(self, index):
"""选择候选词"""
current_candidates = self.get_current_candidates()
if 0 <= index < len(current_candidates):
selected_char = current_candidates[index]
# 更新用户词典词频
self.user_dict.add_word(self.current_pinyin, selected_char)
# 清空当前拼音
self.current_pinyin = ""
self.candidates = []
return selected_char
return None
def get_display_text(self):
"""获取显示文本"""
return self.current_pinyin
def has_candidates(self):
"""是否有候选词"""
return len(self.candidates) > 0
4. 用户界面 (ui.py)
import tkinter as tk
from tkinter import ttk
class InputMethodUI:
def __init__(self, input_method):
self.input_method = input_method
self.root = tk.Tk()
self.root.title("Python 输入法")
self.root.geometry("400x200")
self.root.configure(bg='lightgray')
# 创建显示区域
self.create_widgets()
# 绑定键盘事件
self.root.bind('<KeyPress>', self.on_key_press)
# 设置窗口置顶
self.root.attributes('-topmost', True)
def create_widgets(self):
"""创建界面组件"""
# 拼音显示区域
self.pinyin_label = tk.Label(
self.root,
text="",
font=("Arial", 16),
bg='white',
width=30,
anchor='w'
)
self.pinyin_label.pack(pady=10, padx=10, fill='x')
# 候选词区域
self.candidate_frame = tk.Frame(self.root, bg='lightgray')
self.candidate_frame.pack(pady=5, padx=10, fill='x')
self.candidate_buttons = []
for i in range(5): # 最多显示5个候选词
btn = tk.Button(
self.candidate_frame,
text="",
font=("Arial", 12),
width=6,
command=lambda idx=i: self.select_candidate(idx)
)
btn.grid(row=0, column=i, padx=2)
self.candidate_buttons.append(btn)
# 翻页按钮
self.page_frame = tk.Frame(self.root, bg='lightgray')
self.page_frame.pack(pady=5, padx=10, fill='x')
self.prev_btn = tk.Button(
self.page_frame,
text="上一页",
font=("Arial", 10),
command=self.prev_page
)
self.prev_btn.pack(side='left', padx=5)
self.next_btn = tk.Button(
self.page_frame,
text="下一页",
font=("Arial", 10),
command=self.next_page
)
self.next_btn.pack(side='left', padx=5)
# 状态显示
self.status_label = tk.Label(
self.root,
text="输入拼音,按空格选择,ESC清空",
font=("Arial", 10),
bg='lightgray'
)
self.status_label.pack(pady=5)
def update_display(self):
"""更新显示"""
# 更新拼音显示
self.pinyin_label.config(text=self.input_method.get_display_text())
# 更新候选词
candidates = self.input_method.get_current_candidates()
for i, btn in enumerate(self.candidate_buttons):
if i < len(candidates):
btn.config(text=f"{i+1}.{candidates[i]}")
btn.config(state='normal')
else:
btn.config(text="")
btn.config(state='disabled')
# 更新翻页按钮状态
self.prev_btn.config(state='normal' if self.input_method.current_page > 0 else 'disabled')
max_page = (len(self.input_method.candidates) - 1) // self.input_method.page_size
self.next_btn.config(state='normal' if self.input_method.current_page < max_page else 'disabled')
def on_key_press(self, event):
"""处理键盘事件"""
key = event.char
if event.keysym == 'Escape': # ESC键清空输入
self.input_method.current_pinyin = ""
self.input_method.candidates = []
elif event.keysym == 'BackSpace': # 退格键
self.input_method.backspace()
elif event.keysym == 'space': # 空格键选择第一个候选词
if self.input_method.has_candidates():
selected = self.input_method.select_candidate(0)
if selected:
print(f"选择了: {selected}") # 实际应用中这里应该输出到目标程序
elif key.isalpha() and key.islower(): # 拼音输入
self.input_method.input_pinyin(key)
elif key.isdigit() and key != '0': # 数字键选择候选词
idx = int(key) - 1
if idx < len(self.input_method.get_current_candidates()):
selected = self.input_method.select_candidate(idx)
if selected:
print(f"选择了: {selected}") # 实际应用中这里应该输出到目标程序
self.update_display()
def select_candidate(self, index):
"""选择候选词"""
selected = self.input_method.select_candidate(index)
if selected:
print(f"选择了: {selected}") # 实际应用中这里应该输出到目标程序
self.update_display()
def prev_page(self):
"""上一页"""
if self.input_method.prev_page():
self.update_display()
def next_page(self):
"""下一页"""
if self.input_method.next_page():
self.update_display()
def run(self):
"""运行界面"""
self.update_display()
self.root.mainloop()
5. 主程序 (main.py)
from input_method import InputMethod
from ui import InputMethodUI
def main():
# 创建输入法实例
input_method = InputMethod()
# 创建并运行界面
ui = InputMethodUI(input_method)
ui.run()
if __name__ == "__main__":
main()
