""" 快速用户需求录入界面 优化用户录入流程,提高效率 """ import tkinter as tk from tkinter import ttk, messagebox import customtkinter as ctk from typing import Dict, List, Optional from datetime import datetime import json class QuickUserInputDialog: """快速用户需求录入对话框""" def __init__(self, parent, user_id: str): self.parent = parent self.user_id = user_id self.input_data = {} # 创建对话框 self.dialog = ctk.CTkToplevel(parent) self.dialog.title("快速需求录入") self.dialog.geometry("800x600") self.dialog.transient(parent) self.dialog.grab_set() # 居中显示 self.dialog.geometry("+%d+%d" % (parent.winfo_rootx() + 50, parent.winfo_rooty() + 50)) self._create_widgets() def _create_widgets(self): """创建界面组件""" # 主框架 main_frame = ctk.CTkScrollableFrame(self.dialog) main_frame.pack(fill="both", expand=True, padx=20, pady=20) # 标题 title_label = ctk.CTkLabel( main_frame, text="🚀 快速需求录入", font=ctk.CTkFont(size=24, weight="bold") ) title_label.pack(pady=20) # 步骤1:基础信息 self._create_basic_info_section(main_frame) # 步骤2:饮食偏好 self._create_preferences_section(main_frame) # 步骤3:健康目标 self._create_health_goals_section(main_frame) # 步骤4:快速餐食记录 self._create_quick_meal_section(main_frame) # 按钮区域 self._create_buttons(main_frame) def _create_basic_info_section(self, parent): """创建基础信息区域""" section_frame = ctk.CTkFrame(parent) section_frame.pack(fill="x", padx=10, pady=10) # 标题 title = ctk.CTkLabel( section_frame, text="1️⃣ 基础信息", font=ctk.CTkFont(size=18, weight="bold") ) title.pack(pady=10) # 信息网格 info_frame = ctk.CTkFrame(section_frame) info_frame.pack(fill="x", padx=20, pady=10) # 姓名 name_label = ctk.CTkLabel(info_frame, text="姓名:") name_label.grid(row=0, column=0, sticky="w", padx=10, pady=5) self.name_var = tk.StringVar() name_entry = ctk.CTkEntry(info_frame, textvariable=self.name_var, width=200) name_entry.grid(row=0, column=1, sticky="w", padx=10, pady=5) # 年龄范围 age_label = ctk.CTkLabel(info_frame, text="年龄范围:") age_label.grid(row=0, column=2, sticky="w", padx=10, pady=5) self.age_var = tk.StringVar(value="25-30岁") age_menu = ctk.CTkOptionMenu( info_frame, variable=self.age_var, values=["18-24岁", "25-30岁", "31-35岁", "36-40岁", "41-45岁", "46-50岁", "51-55岁", "56-60岁", "60岁以上"] ) age_menu.grid(row=0, column=3, sticky="w", padx=10, pady=5) # 性别 gender_label = ctk.CTkLabel(info_frame, text="性别:") gender_label.grid(row=1, column=0, sticky="w", padx=10, pady=5) self.gender_var = tk.StringVar(value="女") gender_menu = ctk.CTkOptionMenu( info_frame, variable=self.gender_var, values=["男", "女"] ) gender_menu.grid(row=1, column=1, sticky="w", padx=10, pady=5) # 身高体重范围 height_label = ctk.CTkLabel(info_frame, text="身高范围:") height_label.grid(row=1, column=2, sticky="w", padx=10, pady=5) self.height_var = tk.StringVar(value="160-165cm") height_menu = ctk.CTkOptionMenu( info_frame, variable=self.height_var, values=["150cm以下", "150-155cm", "155-160cm", "160-165cm", "165-170cm", "170-175cm", "175-180cm", "180cm以上"] ) height_menu.grid(row=1, column=3, sticky="w", padx=10, pady=5) weight_label = ctk.CTkLabel(info_frame, text="体重范围:") weight_label.grid(row=2, column=0, sticky="w", padx=10, pady=5) self.weight_var = tk.StringVar(value="50-55kg") weight_menu = ctk.CTkOptionMenu( info_frame, variable=self.weight_var, values=["40kg以下", "40-45kg", "45-50kg", "50-55kg", "55-60kg", "60-65kg", "65-70kg", "70-75kg", "75-80kg", "80kg以上"] ) weight_menu.grid(row=2, column=1, sticky="w", padx=10, pady=5) # 活动水平 activity_label = ctk.CTkLabel(info_frame, text="活动水平:") activity_label.grid(row=2, column=2, sticky="w", padx=10, pady=5) self.activity_var = tk.StringVar(value="中等") activity_menu = ctk.CTkOptionMenu( info_frame, variable=self.activity_var, values=["久坐", "轻度活动", "中等", "高度活动", "极度活动"] ) activity_menu.grid(row=2, column=3, sticky="w", padx=10, pady=5) def _create_preferences_section(self, parent): """创建饮食偏好区域""" section_frame = ctk.CTkFrame(parent) section_frame.pack(fill="x", padx=10, pady=10) # 标题 title = ctk.CTkLabel( section_frame, text="2️⃣ 饮食偏好", font=ctk.CTkFont(size=18, weight="bold") ) title.pack(pady=10) # 偏好网格 pref_frame = ctk.CTkFrame(section_frame) pref_frame.pack(fill="x", padx=20, pady=10) # 口味偏好 taste_label = ctk.CTkLabel(pref_frame, text="主要口味偏好:") taste_label.grid(row=0, column=0, sticky="w", padx=10, pady=5) self.taste_var = tk.StringVar(value="均衡") taste_menu = ctk.CTkOptionMenu( pref_frame, variable=self.taste_var, values=["均衡", "偏甜", "偏咸", "偏辣", "偏酸", "偏清淡"] ) taste_menu.grid(row=0, column=1, sticky="w", padx=10, pady=5) # 饮食类型 diet_label = ctk.CTkLabel(pref_frame, text="饮食类型:") diet_label.grid(row=0, column=2, sticky="w", padx=10, pady=5) self.diet_var = tk.StringVar(value="普通饮食") diet_menu = ctk.CTkOptionMenu( pref_frame, variable=self.diet_var, values=["普通饮食", "素食", "低脂饮食", "低糖饮食", "高蛋白饮食", "地中海饮食"] ) diet_menu.grid(row=0, column=3, sticky="w", padx=10, pady=5) # 过敏食物 allergy_label = ctk.CTkLabel(pref_frame, text="过敏食物:") allergy_label.grid(row=1, column=0, sticky="w", padx=10, pady=5) self.allergy_var = tk.StringVar(value="无") allergy_menu = ctk.CTkOptionMenu( pref_frame, variable=self.allergy_var, values=["无", "花生", "海鲜", "牛奶", "鸡蛋", "坚果", "大豆", "小麦", "其他"] ) allergy_menu.grid(row=1, column=1, sticky="w", padx=10, pady=5) # 不喜欢食物 dislike_label = ctk.CTkLabel(pref_frame, text="不喜欢食物:") dislike_label.grid(row=1, column=2, sticky="w", padx=10, pady=5) self.dislike_var = tk.StringVar(value="无") dislike_menu = ctk.CTkOptionMenu( pref_frame, variable=self.dislike_var, values=["无", "内脏", "辛辣", "油腻", "甜食", "酸味", "苦味", "其他"] ) dislike_menu.grid(row=1, column=3, sticky="w", padx=10, pady=5) def _create_health_goals_section(self, parent): """创建健康目标区域""" section_frame = ctk.CTkFrame(parent) section_frame.pack(fill="x", padx=10, pady=10) # 标题 title = ctk.CTkLabel( section_frame, text="3️⃣ 健康目标", font=ctk.CTkFont(size=18, weight="bold") ) title.pack(pady=10) # 目标选择 goals_frame = ctk.CTkFrame(section_frame) goals_frame.pack(fill="x", padx=20, pady=10) # 主要目标 main_goal_label = ctk.CTkLabel(goals_frame, text="主要目标:") main_goal_label.grid(row=0, column=0, sticky="w", padx=10, pady=5) self.main_goal_var = tk.StringVar(value="保持健康") main_goal_menu = ctk.CTkOptionMenu( goals_frame, variable=self.main_goal_var, values=["保持健康", "减重", "增重", "增肌", "改善消化", "提高免疫力", "控制血糖", "降低血压"] ) main_goal_menu.grid(row=0, column=1, sticky="w", padx=10, pady=5) # 次要目标 sub_goal_label = ctk.CTkLabel(goals_frame, text="次要目标:") sub_goal_label.grid(row=0, column=2, sticky="w", padx=10, pady=5) self.sub_goal_var = tk.StringVar(value="无") sub_goal_menu = ctk.CTkOptionMenu( goals_frame, variable=self.sub_goal_var, values=["无", "改善睡眠", "提高精力", "美容养颜", "延缓衰老", "改善皮肤", "增强记忆", "缓解压力"] ) sub_goal_menu.grid(row=0, column=3, sticky="w", padx=10, pady=5) def _create_quick_meal_section(self, parent): """创建快速餐食记录区域""" section_frame = ctk.CTkFrame(parent) section_frame.pack(fill="x", padx=10, pady=10) # 标题 title = ctk.CTkLabel( section_frame, text="4️⃣ 快速餐食记录", font=ctk.CTkFont(size=18, weight="bold") ) title.pack(pady=10) # 餐食选择 meal_frame = ctk.CTkFrame(section_frame) meal_frame.pack(fill="x", padx=20, pady=10) # 餐次选择 meal_type_label = ctk.CTkLabel(meal_frame, text="餐次:") meal_type_label.grid(row=0, column=0, sticky="w", padx=10, pady=5) self.meal_type_var = tk.StringVar(value="午餐") meal_type_menu = ctk.CTkOptionMenu( meal_frame, variable=self.meal_type_var, values=["早餐", "午餐", "晚餐", "加餐"] ) meal_type_menu.grid(row=0, column=1, sticky="w", padx=10, pady=5) # 快速食物选择 food_label = ctk.CTkLabel(meal_frame, text="快速选择食物:") food_label.grid(row=1, column=0, sticky="w", padx=10, pady=5) self.quick_food_var = tk.StringVar(value="米饭+鸡肉+蔬菜") quick_food_menu = ctk.CTkOptionMenu( meal_frame, variable=self.quick_food_var, values=[ "米饭+鸡肉+蔬菜", "面条+鸡蛋+青菜", "馒头+豆腐+白菜", "粥+咸菜+鸡蛋", "面包+牛奶+水果", "饺子+汤", "包子+豆浆", "其他" ] ) quick_food_menu.grid(row=1, column=1, sticky="w", padx=10, pady=5) # 分量选择 portion_label = ctk.CTkLabel(meal_frame, text="分量:") portion_label.grid(row=1, column=2, sticky="w", padx=10, pady=5) self.portion_var = tk.StringVar(value="正常") portion_menu = ctk.CTkOptionMenu( meal_frame, variable=self.portion_var, values=["少量", "正常", "较多", "很多"] ) portion_menu.grid(row=1, column=3, sticky="w", padx=10, pady=5) # 满意度 satisfaction_label = ctk.CTkLabel(meal_frame, text="满意度:") satisfaction_label.grid(row=2, column=0, sticky="w", padx=10, pady=5) self.satisfaction_var = tk.IntVar(value=3) satisfaction_slider = ctk.CTkSlider( meal_frame, from_=1, to=5, number_of_steps=4, variable=self.satisfaction_var ) satisfaction_slider.grid(row=2, column=1, columnspan=2, sticky="ew", padx=10, pady=5) # 满意度标签 self.satisfaction_display = ctk.CTkLabel(meal_frame, text="3分 - 一般") self.satisfaction_display.grid(row=2, column=3, sticky="w", padx=10, pady=5) # 绑定滑块事件 satisfaction_slider.configure(command=self._on_satisfaction_changed) def _create_buttons(self, parent): """创建按钮区域""" button_frame = ctk.CTkFrame(parent) button_frame.pack(fill="x", padx=10, pady=20) # 保存按钮 save_button = ctk.CTkButton( button_frame, text="💾 保存所有信息", command=self._save_all_data, width=200, height=50, font=ctk.CTkFont(size=16, weight="bold") ) save_button.pack(side="left", padx=20, pady=10) # 取消按钮 cancel_button = ctk.CTkButton( button_frame, text="❌ 取消", command=self._cancel, width=150, height=50 ) cancel_button.pack(side="right", padx=20, pady=10) def _on_satisfaction_changed(self, value): """满意度改变事件""" score = int(float(value)) score_texts = { 1: "1分 - 很不满意", 2: "2分 - 不满意", 3: "3分 - 一般", 4: "4分 - 满意", 5: "5分 - 很满意" } self.satisfaction_display.configure(text=score_texts.get(score, "3分 - 一般")) def _save_all_data(self): """保存所有数据""" try: # 收集所有数据 self.input_data = { 'basic_info': { 'name': self.name_var.get(), 'age_range': self.age_var.get(), 'gender': self.gender_var.get(), 'height_range': self.height_var.get(), 'weight_range': self.weight_var.get(), 'activity_level': self.activity_var.get() }, 'preferences': { 'taste': self.taste_var.get(), 'diet_type': self.diet_var.get(), 'allergies': self.allergy_var.get(), 'dislikes': self.dislike_var.get() }, 'health_goals': { 'main_goal': self.main_goal_var.get(), 'sub_goal': self.sub_goal_var.get() }, 'quick_meal': { 'meal_type': self.meal_type_var.get(), 'food_combo': self.quick_food_var.get(), 'portion': self.portion_var.get(), 'satisfaction': self.satisfaction_var.get() } } # 保存到数据库 if self._save_to_database(): messagebox.showinfo("成功", "所有信息保存成功!") self.dialog.destroy() else: messagebox.showerror("错误", "保存失败,请重试") except Exception as e: messagebox.showerror("错误", f"保存失败: {str(e)}") def _save_to_database(self) -> bool: """保存到数据库""" try: from modules.data_collection import collect_questionnaire_data, record_meal # 保存基础信息 basic_data = self.input_data['basic_info'] age_mapping = { "18-24岁": 21, "25-30岁": 27, "31-35岁": 33, "36-40岁": 38, "41-45岁": 43, "46-50岁": 48, "51-55岁": 53, "56-60岁": 58, "60岁以上": 65 } height_mapping = { "150cm以下": 150, "150-155cm": 152, "155-160cm": 157, "160-165cm": 162, "165-170cm": 167, "170-175cm": 172, "175-180cm": 177, "180cm以上": 180 } weight_mapping = { "40kg以下": 40, "40-45kg": 42, "45-50kg": 47, "50-55kg": 52, "55-60kg": 57, "60-65kg": 62, "65-70kg": 67, "70-75kg": 72, "75-80kg": 77, "80kg以上": 80 } activity_mapping = { "久坐": "sedentary", "轻度活动": "light", "中等": "moderate", "高度活动": "high", "极度活动": "very_high" } basic_answers = { 'name': basic_data['name'], 'age': age_mapping.get(basic_data['age_range'], 25), 'gender': basic_data['gender'], 'height': height_mapping.get(basic_data['height_range'], 165), 'weight': weight_mapping.get(basic_data['weight_range'], 55), 'activity_level': activity_mapping.get(basic_data['activity_level'], 'moderate') } collect_questionnaire_data(self.user_id, 'basic', basic_answers) # 保存口味偏好 preferences_data = self.input_data['preferences'] taste_answers = { 'taste_preference': preferences_data['taste'], 'diet_type': preferences_data['diet_type'], 'allergies': [preferences_data['allergies']] if preferences_data['allergies'] != "无" else [], 'dislikes': [preferences_data['dislikes']] if preferences_data['dislikes'] != "无" else [] } collect_questionnaire_data(self.user_id, 'taste', taste_answers) # 保存健康目标 health_data = self.input_data['health_goals'] health_answers = { 'main_goal': health_data['main_goal'], 'sub_goal': health_data['sub_goal'] } collect_questionnaire_data(self.user_id, 'health', health_answers) # 保存快速餐食记录 meal_data = self.input_data['quick_meal'] meal_type_mapping = { "早餐": "breakfast", "午餐": "lunch", "晚餐": "dinner", "加餐": "snack" } # 解析食物组合 food_combo = meal_data['food_combo'] if "+" in food_combo: foods = [food.strip() for food in food_combo.split("+")] else: foods = [food_combo] # 估算分量 portion_mapping = { "少量": "1小份", "正常": "1份", "较多": "1大份", "很多": "2份" } quantities = [portion_mapping.get(meal_data['portion'], "1份")] * len(foods) meal_record = { 'date': datetime.now().strftime('%Y-%m-%d'), 'meal_type': meal_type_mapping.get(meal_data['meal_type'], 'lunch'), 'foods': foods, 'quantities': quantities, 'satisfaction_score': meal_data['satisfaction'] } record_meal(self.user_id, meal_record) return True except Exception as e: print(f"保存到数据库失败: {e}") return False def _cancel(self): """取消录入""" self.dialog.destroy() # 便捷函数 def show_quick_user_input_dialog(parent, user_id: str): """显示快速用户需求录入对话框""" dialog = QuickUserInputDialog(parent, user_id) parent.wait_window(dialog.dialog) if __name__ == "__main__": # 测试快速录入对话框 root = tk.Tk() root.title("测试快速录入") def test_dialog(): show_quick_user_input_dialog(root, "test_user") test_button = tk.Button(root, text="测试快速录入", command=test_dialog) test_button.pack(pady=20) root.mainloop()