diff --git a/test/aola.py b/test/aola.py index 55c3385..49d1aa0 100644 --- a/test/aola.py +++ b/test/aola.py @@ -19,43 +19,47 @@ class TeamSwitchPacketGenerator: self.root.title("阵容切换封包生成器") # 获取所有阵容名称和 spell_code - self.team_data = self.get_team_data() # 存储 team_name 和 spell_code 的列表 - self.team_names = [item[0] for item in self.team_data] # 仅 team_name 列表用于显示 - - # 创建搜索框(替换下拉框) - tk.Label(self.root, text="搜索阵容(输入拼音缩写):").pack(anchor=tk.W, padx=5) - self.search_var = tk.StringVar() - self.search_var.trace("w", self.update_combobox) # 监听输入变化 - self.search_entry = tk.Entry(self.root, textvariable=self.search_var) - self.search_entry.pack(pady=5, padx=5, fill=tk.X) + self.team_data = self.get_team_data() + self.team_names = [item[0] for item in self.team_data] + self.last_filtered_teams = self.team_names # 创建可搜索的下拉框 - self.team_var = tk.StringVar() + tk.Label(self.root, text="选择或搜索阵容(输入拼音缩写):").pack(anchor=tk.W, padx=5) + self.team_var = tk.StringVar(value="请选择阵容") self.team_dropdown = ttk.Combobox(self.root, textvariable=self.team_var) self.team_dropdown['values'] = self.team_names self.team_dropdown.pack(pady=10) - self.team_dropdown.set("请选择阵容") + + # 防抖定时器和状态 + self.debounce_timer = None + self.debounce_delay = 150 + self.last_search_text = "" + self.has_typed = False + self.is_dropdown_open = False # 跟踪下拉框状态 + + # 绑定事件 + self.team_dropdown.bind('', self.schedule_update_combobox) + self.team_dropdown.bind('', self.clear_default_text) + self.team_dropdown.bind('', self.close_dropdown) + self.team_dropdown.bind('<>', self.on_combobox_select) + + # 获取 Combobox 的内部 Entry 控件 + self.entry = self.team_dropdown # ttk.Combobox 本身支持 Entry 方法 + # 绑定下拉框展开/收起事件 + self.team_dropdown.bind('', self.toggle_dropdown_state) # 创建按钮框架,包含“刷新”和“生成封包”按钮 button_frame = tk.Frame(self.root) button_frame.pack(pady=5) - - # 刷新按钮 tk.Button(button_frame, text="刷新", command=self.refresh_dropdown).pack(side=tk.LEFT, padx=5) - - # 生成按钮 tk.Button(button_frame, text="生成封包(第一次生成请先解锁二级密码)", command=self.generate_packets).pack(side=tk.LEFT, padx=5) - # 创建 Frame 包含封包输出框和复制按钮 + # 创建封包输出和复制按钮框架 output_frame = tk.Frame(self.root) output_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) - - # 封包输出框 tk.Label(output_frame, text="封包输出:").pack(anchor=tk.W) self.output_text = tk.Text(output_frame, height=15, width=60) self.output_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) - - # 复制按钮(放在右侧) tk.Button(output_frame, text="复制脚本", command=self.copy_output).pack(side=tk.RIGHT, padx=5) # 信息提示框 @@ -64,49 +68,94 @@ class TeamSwitchPacketGenerator: self.info_text.pack(pady=5) def get_team_data(self): - """从数据库获取所有阵容名称和 spell_code""" try: self.cursor.execute("SELECT team_name, spell_code FROM aolaer_team ORDER BY team_name") - return self.cursor.fetchall() # 返回 [(team_name, spell_code), ...] + return [(row[0], row[1] or "") for row in self.cursor.fetchall()] except sqlite3.Error as e: self.info_text.insert(tk.END, f"获取阵容数据失败: {e}\n") return [] - def update_combobox(self, *args): - """根据搜索框输入更新下拉框内容""" - search_text = self.search_var.get().strip().lower() - if not search_text: - # 如果搜索框为空,显示所有阵容 - self.team_dropdown['values'] = self.team_names - self.team_dropdown.set("请选择阵容") + def clear_default_text(self, event): + if self.team_var.get() == "请选择阵容": + self.team_var.set("") + self.has_typed = True + self.entry.focus_set() + self.entry.icursor(tk.END) + + def schedule_update_combobox(self, event): + if event.keysym in ('Shift', 'Control', 'Alt', 'Return', 'Tab', 'Left', 'Right', 'Up', 'Down'): + return + + self.has_typed = True + if self.debounce_timer is not None: + self.root.after_cancel(self.debounce_timer) + self.debounce_timer = self.root.after(self.debounce_delay, self.update_combobox) + + def toggle_dropdown_state(self, event): + """跟踪下拉框的展开/收起状态""" + # 简单假设点击切换状态(实际可能需要更复杂的状态检测) + self.is_dropdown_open = not self.is_dropdown_open + + def update_combobox(self): + """优化后的更新下拉框逻辑""" + search_text = self.team_var.get().strip().lower() + if search_text == self.last_search_text: + return + + self.last_search_text = search_text + if not search_text or search_text == "请选择阵容": + filtered_teams = self.team_names else: - # 筛选 spell_code 包含搜索文本的阵容 filtered_teams = [ team_name for team_name, spell_code in self.team_data if spell_code and search_text in spell_code.lower() ] + if not filtered_teams: + filtered_teams = ["无匹配阵容"] + + # 更新下拉框选项 + if filtered_teams != self.last_filtered_teams: self.team_dropdown['values'] = filtered_teams - if filtered_teams: - self.team_dropdown.set(filtered_teams[0]) # 默认选中第一个匹配项 - else: - self.team_dropdown.set("无匹配阵容") - self.team_dropdown['values'] = [] + self.last_filtered_teams = filtered_teams + + # 打开下拉框(仅在有输入且未打开时) + if self.has_typed and search_text and not self.is_dropdown_open: + self.team_dropdown.event_generate('') + self.is_dropdown_open = True + + # 延迟设置焦点和光标位置 + def set_focus_and_cursor(): + self.entry.focus_set() + self.entry.icursor(tk.END) + # 调试日志 + self.info_text.insert(tk.END, f"焦点设置: {self.root.focus_get() == self.entry}\n") + + self.root.after(10, set_focus_and_cursor) + + def close_dropdown(self, event): + self.has_typed = False + if self.is_dropdown_open: + self.team_dropdown.event_generate('') + self.is_dropdown_open = False + + def on_combobox_select(self, event): + self.has_typed = False + self.is_dropdown_open = False + self.entry.focus_set() + self.entry.icursor(tk.END) def refresh_dropdown(self): - """刷新下拉框数据""" - # 重新获取阵容数据 self.team_data = self.get_team_data() self.team_names = [item[0] for item in self.team_data] - # 更新下拉框选项 self.team_dropdown['values'] = self.team_names - self.team_dropdown.set("请选择阵容") - # 清空搜索框 - self.search_var.set("") - # 在提示框中显示刷新成功 + self.last_filtered_teams = self.team_names + self.team_var.set("请选择阵容") + self.last_search_text = "" + self.has_typed = False + self.is_dropdown_open = False self.info_text.insert(tk.END, "下拉框数据已刷新!\n") def copy_output(self): - """复制脚本输出框的内容到剪贴板""" content = self.output_text.get(1.0, tk.END).strip() if content: self.root.clipboard_clear() @@ -116,7 +165,6 @@ class TeamSwitchPacketGenerator: self.info_text.insert(tk.END, "脚本输出框为空,无法复制!\n") def validate_ids(self, ids_str): - """验证 ids 数组,确保格式正确""" try: ids = json.loads(ids_str) if not isinstance(ids, list) or len(ids) != 5: @@ -129,7 +177,6 @@ class TeamSwitchPacketGenerator: return False, "ids 格式不正确" def generate_packets(self): - """生成封包代码""" self.output_text.delete(1.0, tk.END) self.info_text.delete(1.0, tk.END) packets = [] @@ -140,7 +187,6 @@ class TeamSwitchPacketGenerator: return try: - # 获取 team_code 和 team_set self.cursor.execute("SELECT team_code, team_set FROM aolaer_team WHERE team_name = ?", (selected_team,)) result = self.cursor.fetchone() if not result: @@ -149,21 +195,16 @@ class TeamSwitchPacketGenerator: team_code, team_set = result packets.append(f'|#send={{"id":13,"param":{{"pms":"{team_code}"}},"cmd":"1222"}}|') - # 处理 is_set = 1 的记录(重置魂卡) self.cursor.execute("SELECT id FROM aolaer_id WHERE is_set = 1") set_ids = [row[0] for row in self.cursor.fetchall()] - for set_id in set_ids: packets.append( - f'|#send={{"id":42,"param":{{"bk":1,"petId":{set_id},"ids":[0,0,0,0,0]}},"cmd":"ASC221104_2"}}|') + f'|#send={{"id":42,"param":{{"bk":1,"petId":{set_id},"ids":[0,0,0,0,0]}},"cmd":"ASC221104_2"}}|#time=10|') self.cursor.execute("UPDATE aolaer_id SET is_set = 0 WHERE id = ?", (set_id,)) self.conn.commit() - # 处理 team_code 中的 ID team_ids = team_code.split('#') team_ids = [id for id in team_ids if id] - - # 处理 team_set team_set_codes = None if team_set: team_set_codes = team_set.split('#') @@ -172,17 +213,13 @@ class TeamSwitchPacketGenerator: f"警告:team_set 的长度 ({len(team_set_codes)}) 与 team_code 的 ID 数量 ({len(team_ids)}) 不匹配!\n") return - # 查询 aolaer_id 表 self.cursor.execute( "SELECT id, div_weapon, aolaer_typecode FROM aolaer_id WHERE id IN ({})".format( ','.join('?' for _ in team_ids) ), team_ids ) - id_to_typecode = {row[0]: row for row in self.cursor.fetchall()} - - # 分组生成封包,确保顺序:子宠物 → 魂艺 → 魂卡 sub_pet_packets = [] soul_art_packets = [] soul_card_packets = [] @@ -198,14 +235,12 @@ class TeamSwitchPacketGenerator: if team_set_codes and team_set_codes[idx] != '0': typecode = team_set_codes[idx] - # 生成子宠物封包(仅当 div_weapon 非空时) if div_weapon and str(div_weapon).strip(): sub_pet_packets.append( f'|#send={{"id":42,"param":{{"subPetId":{id},"petId":{div_weapon}}},"cmd":"ASBS230623_con"}}|') else: self.info_text.insert(tk.END, f"提示:ID {id} 的 div_weapon 为空,跳过生成子宠物封包!\n") - # 获取 soul_art 和 soul_card self.cursor.execute("SELECT soul_art, soul_card FROM aolaer_type WHERE aolaer_typecode = ?", (typecode,)) result = self.cursor.fetchone() @@ -214,7 +249,6 @@ class TeamSwitchPacketGenerator: continue soul_art, soul_card = result - # 生成魂艺封包(仅当 soul_art 非空时) if soul_art and soul_art.strip(): try: sid1, sid2 = soul_art.split(',') @@ -229,12 +263,11 @@ class TeamSwitchPacketGenerator: self.info_text.insert(tk.END, f"提示:typecode {typecode} 的 soul_art 为空,跳过生成魂艺封包!\n") - # 生成魂卡封包 if soul_card and soul_card.strip(): is_valid, ids_or_error = self.validate_ids(soul_card) if is_valid: soul_card_packets.append( - f'|#send={{"id":42,"param":{{"bk":1,"petId":{id},"ids":{soul_card}}},"cmd":"ASC221104_2"}}|') + f'|#send={{"id":42,"param":{{"bk":1,"petId":{id},"ids":{soul_card}}},"cmd":"ASC221104_2"}}|#time=10|') self.cursor.execute("UPDATE aolaer_id SET is_set = 1 WHERE id = ?", (id,)) self.conn.commit() else: @@ -243,7 +276,6 @@ class TeamSwitchPacketGenerator: else: self.info_text.insert(tk.END, f"提示:typecode {typecode} 的 soul_card 为空,已跳过处理!\n") - # 按顺序添加封包:子宠物 → 魂艺 → 魂卡 packets.extend(sub_pet_packets) packets.extend(soul_art_packets) packets.extend(soul_card_packets) @@ -256,11 +288,9 @@ class TeamSwitchPacketGenerator: self.info_text.insert(tk.END, f"数据库操作失败: {e}\n") def run(self): - """运行程序""" self.root.mainloop() def __del__(self): - """清理数据库连接""" self.conn.close() diff --git a/test/aola.sqlite b/test/aola.sqlite index 9d6f605..a24bba0 100644 Binary files a/test/aola.sqlite and b/test/aola.sqlite differ