优化了一系列功能,背景,表情包,存储对话等等
import tkinter as tk # 用于创建图形用户界面 from tkinter import ttk, filedialog, messagebox, colorchooser # 导入 tkinter 的一些子模块 import threading # 用于实现多线程 import time # 用于处理时间相关的功能 import pyttsx3 # 用于将文本转换为语音 import speech_recognition as sr # 用于语音识别 import requests # 用于发送 HTTP 请求 import urllib.parse # 用于解析 URL import logging # 用于记录日志 from PIL import Image, ImageTk # 用于处理图像 import emoji # 用于处理表情符号 # 设置基本日志配置 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') class LoginWindow: def __init__(self, master): """初始化登录窗口""" self.master = master self.master.title("登录") # 设置窗口标题 self.master.geometry("300x150") # 设置窗口大小 self.create_widgets() def create_widgets(self): """创建登录窗口的组件""" tk.Label(self.master, text="账号:").grid(row=0, column=0, padx=10, pady=10) self.username_entry = tk.Entry(self.master) self.username_entry.grid(row=0, column=1, padx=10, pady=10) self.username_entry.bind("<Return>", self.check_login) tk.Label(self.master, text="密码:").grid(row=1, column=0, padx=10, pady=10) self.password_entry = tk.Entry(self.master, show="*") self.password_entry.grid(row=1, column=1, padx=10, pady=10) self.password_entry.bind("<Return>", self.check_login) tk.Button(self.master, text="登录", command=self.check_login).grid(row=2, column=0, columnspan=2, pady=10) def check_login(self, event=None): """检查登录账号和密码是否正确""" username = self.username_entry.get() password = self.password_entry.get() if username == "123" and password == "123": self.master.destroy() main() else: messagebox.showerror("错误", "账号或密码错误") class SpeechApp: def __init__(self, master): """初始化语音聊天机器人窗口""" self.master = master self.master.title("语音聊天机器人") self.master.geometry("600x600") self.engine = pyttsx3.init() # 初始化文本转语音引擎 self.recognizer = sr.Recognizer() # 初始化语音识别器 self.listening = False # 监听状态标志 self.voice_type = "BV001_streaming" self.user_avatar = ImageTk.PhotoImage(Image.open("wo.jpg").resize((50, 50), Image.Resampling.LANCZOS)) # 用户头像 self.bot_avatar = ImageTk.PhotoImage(Image.open("robot.png").resize((50, 50), Image.Resampling.LANCZOS)) # 机器人头像 self.setup_ui() self.background_color = None self.dialog_background_color = None self.selected_emoji = None def setup_ui(self): """设置用户界面""" frame = tk.Frame(self.master, bg='white') frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True) self.canvas = tk.Canvas(frame, bg='white') self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.scrollbar = ttk.Scrollbar(frame, command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.messages_frame = tk.Frame(self.canvas, bg='white') self.canvas.create_window((0, 0), window=self.messages_frame, anchor='nw') self.messages_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) entry_frame = tk.Frame(self.master) entry_frame.pack(fill=tk.X, padx=10, pady=10) self.text_entry = ttk.Entry(entry_frame, width=80) self.text_entry.bind("<Return>", self.on_text_entry) self.text_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) emoji_button = ttk.Button(entry_frame, text="选择表情", command=self.choose_emoji) emoji_button.pack(side=tk.LEFT) self.voice_combo = ttk.Combobox(self.master, state='readonly', values=['Male Voice', 'Female Voice']) self.voice_combo.set('Male Voice') self.voice_combo.pack(pady=5) self.voice_combo.bind("<<ComboboxSelected>>", self.on_voice_change) button_frame = tk.Frame(self.master) button_frame.pack(pady=10) self.speak_button = ttk.Button(button_frame, text="开始说话") self.speak_button.pack(side=tk.LEFT, padx=5) self.speak_button.bind("<ButtonPress>", self.on_speak_button_press) self.speak_button.bind("<ButtonRelease>", self.on_speak_button_release) ttk.Button(button_frame, text="更换界面背景", command=self.change_background_interface).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="更换对话框背景", command=self.change_background_dialog).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="保存对话", command=self.save_conversation).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="退出", command=self.on_close).pack(side=tk.LEFT, padx=5) self.master.protocol("WM_DELETE_WINDOW", self.on_close) def on_voice_change(self, event): """更改语音类型""" voice_label = self.voice_combo.get() if voice_label == 'Female Voice': self.voice_type = "BV001_streaming" elif voice_label == 'Male Voice': self.voice_type = "BV002_streaming" self.speak("Voice changed to " + voice_label) def on_speak_button_press(self, event): """开始监听语音输入""" self.listening = True threading.Thread(target=self.continuous_listen).start() def on_speak_button_release(self, event): """停止监听语音输入""" self.listening = False def continuous_listen(self): """连续监听语音输入并识别文本""" with sr.Microphone() as source: self.recognizer.adjust_for_ambient_noise(source) while self.listening: try: print("Listening...") audio = self.recognizer.listen(source, timeout=1, phrase_time_limit=5) text = self.recognizer.recognize_google(audio, language='zh-CN') print(f"Recognized: {text}") if text: self.update_conversation(text, "You") response = self.chat_with_qingyunke(text) self.update_conversation(response, "System") self.speak(response) except sr.WaitTimeoutError: print("Timeout, listening again...") except sr.UnknownValueError: print("Could not understand audio") except sr.RequestError as e: print(f"Could not request results; {e}") def chat_with_qingyunke(self, message): """与青云客 API 进行对话""" base_url = "http://api.qingyunke.com/api.php" params = {"key": "free", "appid": 0, "msg": urllib.parse.quote(message)} try: response = requests.get(base_url, params=params, timeout=10) response.raise_for_status() return response.json()['content'].replace('{br}', '\n') except requests.RequestException as e: logging.error("Network error: " + str(e)) return "网络错误,请稍后再试" def on_text_entry(self, event): """处理文本输入并进行对话""" message = self.text_entry.get() if message or self.selected_emoji: self.text_entry.delete(0, tk.END) message_with_emoji = message + (self.selected_emoji if self.selected_emoji else "") self.update_conversation(message_with_emoji, "You") self.selected_emoji = None response = self.chat_with_qingyunke(message_with_emoji) self.update_conversation(response, "System") self.speak(response) def change_background_interface(self): """更改界面背景颜色""" color = colorchooser.askcolor(title="选择界面背景颜色")[1] if color: self.master.config(bg=color) self.canvas.config(bg=color) self.messages_frame.config(bg=color) self.background_color = color def change_background_dialog(self): """更改对话框背景颜色""" color = colorchooser.askcolor(title="选择对话框背景颜色")[1] if color: self.dialog_background_color = color def choose_emoji(self): """选择表情符号""" emoji_window = tk.Toplevel(self.master) emoji_window.title("选择表情") emoji_window.geometry("500x300") emoji_frame = tk.Frame(emoji_window) emoji_frame.pack(fill=tk.BOTH, expand=True) canvas = tk.Canvas(emoji_frame) canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar = ttk.Scrollbar(emoji_frame, orient="vertical", command=canvas.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) canvas.configure(yscrollcommand=scrollbar.set) canvas.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) emojis_frame = tk.Frame(canvas) canvas.create_window((0, 0), window=emojis_frame, anchor='nw') emojis = ['😀', '😂', '🤣', '🙃', '😍', '😁', '😅', '🤔', '😮', '😛', '😢', '😠'] for emj in emojis: btn = ttk.Button(emojis_frame, text=emj, command=lambda e=emj: self.select_emoji(e, emoji_window)) btn.pack(side=tk.LEFT, padx=5, pady=5) def select_emoji(self, emj, window): """选择具体表情符号并关闭选择窗口""" self.selected_emoji = emoji.emojize(emj) window.destroy() def speak(self, text): """将文本转换为语音""" self.txt_to_voice(text) def txt_to_voice(self, msg): """文本转语音""" engine = pyttsx3.init() engine.setProperty('rate', 150) engine.setProperty('volume', 1) voices = engine.getProperty('voices') if self.voice_type == "BV001_streaming": engine.setProperty('voice', voices[0].id) else: engine.setProperty('voice', voices[1].id) engine.say(msg) engine.runAndWait() engine.stop() def update_conversation(self, text, speaker): """更新对话框内容""" def update(): time_stamp = time.strftime("%Y-%m-%d %H:%M:%S") if speaker == "You": avatar = self.user_avatar name = "You" side = 'right' else: avatar = self.bot_avatar name = "Robot" side = 'left' message_frame = tk.Frame(self.messages_frame, bg=self.background_color or 'white') message_frame.pack(fill=tk.X, pady=5, padx=10) if side == 'right': text_frame = tk.Frame(message_frame, bg=self.dialog_background_color or 'lightblue') text_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5) avatar_frame = tk.Frame(message_frame, bg=self.background_color or 'white') avatar_frame.pack(side=tk.RIGHT, padx=5) avatar_label = tk.Label(avatar_frame, image=avatar, bg=self.background_color or 'white') avatar_label.pack() else: avatar_frame = tk.Frame(message_frame, bg=self.background_color or 'white') avatar_frame.pack(side=tk.LEFT, padx=5) avatar_label = tk.Label(avatar_frame, image=avatar, bg=self.background_color or 'white') avatar_label.pack() text_frame = tk.Frame(message_frame, bg=self.dialog_background_color or 'pink') text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5) name_time_label = tk.Label(text_frame, text=f"{name} {time_stamp}", bg=self.dialog_background_color or 'white', font=("Arial", 8), anchor='w') name_time_label.pack(fill=tk.X) message_label = tk.Label(text_frame, text=text, bg=self.dialog_background_color or 'white', wraplength=400, justify='left') message_label.pack(padx=5, pady=5) self.canvas.update_idletasks() self.canvas.yview_moveto(1) self.messages_frame.after(0, update) def save_conversation(self): """保存对话内容""" messages = [] for child in self.messages_frame.winfo_children(): if isinstance(child, tk.Frame): for frame in child.winfo_children(): if isinstance(frame, tk.Frame): for widget in frame.winfo_children(): if isinstance(widget, tk.Label): messages.append(widget.cget("text")) file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")]) if file_path: with open(file_path, 'w', encoding='utf-8') as file: for message in messages: file.write(message + '\n') messagebox.showinfo("保存成功", "对话已成功保存") def on_close(self): """处理窗口关闭事件""" if messagebox.askokcancel("退出", "确定要退出吗?"): self.master.destroy() def main(): """创建并运行语音聊天机器人窗口""" root = tk.Tk() app = SpeechApp(root) root.mainloop() if __name__ == "__main__": login_root = tk.Tk() login_app = LoginWindow(login_root) login_root.mainloop()