CustomTkinter中Entry点击显示ScrollableFrame及外部点击隐藏问题排查
问题分析与解决方案
核心问题点
- 事件绑定错误:原代码使用
<<Button-1>>作为点击事件,这不是Tkinter标准鼠标点击事件,正确的左键点击事件应为<Button-1> - 布局方式不当:用
pack放置滚动框架会打乱现有网格布局,应使用place定位到Entry组件下方,保证显示位置合理 - 未实现外部点击隐藏:缺少主窗口点击事件监听,无法判断点击区域并自动隐藏框架
- 重复创建框架:每次点击Entry都会新建滚动框架,导致界面冗余
修改后的完整代码
import tkinter as tk import customtkinter as ctk from tkinter import ttk def user_window(): user_window = ctk.CTk() user_window.geometry("900x900") user_window.title("Sadiq And Antony's Airlines") search_ticket_frame = ctk.CTkFrame(user_window, corner_radius=15, width=500, height=250, fg_color="gray") search_ticket_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER) # 标签与分隔线部分保留原逻辑 from_label = ctk.CTkLabel(search_ticket_frame, text="From (Departure)", font=("Arial", 20)) from_label.grid(row=0, column=0, padx=10, pady=20) sep1 = ttk.Separator(search_ticket_frame, orient="vertical") sep1.grid(row=0, column=1, sticky="ns", rowspan=2) to_label = ctk.CTkLabel(search_ticket_frame, text="To(Arrival)", font=("Arial", 20)) to_label.grid(row=0, column=2, padx=10, pady=20) sep2 = ttk.Separator(search_ticket_frame, orient="vertical") sep2.grid(row=0, column=3, sticky="ns", rowspan=2) d_date = ctk.CTkLabel(search_ticket_frame, text="Departure Date", font=("Arial", 20)) d_date.grid(row=0, column=4, padx=10, pady=20) sep3 = ttk.Separator(search_ticket_frame, orient="vertical") sep3.grid(row=0, column=5, sticky="ns", rowspan=2) a_date = ctk.CTkLabel(search_ticket_frame, text="Arrival Date", font=("Arial", 20)) a_date.grid(row=0, column=6, padx=10, pady=20) sep4 = ttk.Separator(search_ticket_frame, orient="vertical") sep4.grid(row=0, column=7, sticky="ns", rowspan=2) n_pass = ctk.CTkLabel(search_ticket_frame, text="Travellers", font=("Arial", 20)) n_pass.grid(row=0, column=8, padx=10, pady=20) sep5 = ttk.Separator(search_ticket_frame, orient="vertical") sep5.grid(row=0, column=9, sticky="ns", rowspan=2) class_label = ctk.CTkLabel(search_ticket_frame, text="Class", font=("Arial", 20)) class_label.grid(row=0, column=10, padx=10, pady=20) # 全局变量存储滚动框架,方便后续销毁或隐藏 country_frame = None def hide_country_frame(event): nonlocal country_frame # 判断点击位置是否在Entry或滚动框架外 if country_frame and not s1.winfo_containing(event.x_root, event.y_root) and not country_frame.winfo_containing(event.x_root, event.y_root): country_frame.destroy() country_frame = None def display_countries(event): nonlocal country_frame # 如果已有框架,先销毁避免重复显示 if country_frame: country_frame.destroy() # 获取Entry组件的屏幕坐标,定位滚动框架到Entry下方 x = s1.winfo_rootx() y = s1.winfo_rooty() + s1.winfo_height() country_frame = ctk.CTkScrollableFrame(user_window, corner_radius=10, width=200, height=200) country_frame.place(x=x, y=y) countries = ["Country 1", "Country 2", "Country 3", "Country 4", "Country 5", "Country 6", "Country 7", "Country 8", "Country 9", "Country 10"] for country in countries: # 绑定单选按钮点击事件,选择后更新Entry内容并隐藏框架 def select_country(country_name=country): s1.delete(0, tk.END) s1.insert(0, country_name) country_frame.destroy() nonlocal country_frame country_frame = None radio_btn = ctk.CTkRadioButton(country_frame, text=country, font=("Arial", 16), command=lambda c=country: select_country(c)) radio_btn.pack(fill=tk.X, padx=5, pady=2) s1 = ctk.CTkEntry(search_ticket_frame, placeholder_text="Your Choice", font=("Arial",20), width=200) s1.grid(row=1, column=0, padx=30) # 绑定正确的鼠标左键点击事件 s1.bind("<Button-1>", display_countries) # 给主窗口绑定点击事件,实现外部点击隐藏功能 user_window.bind("<Button-1>", hide_country_frame) user_window.mainloop() user_window()
关键修改说明
- 修正事件绑定:将错误的
<<Button-1>>改为Tkinter标准的<Button-1>事件 - 精准定位框架:通过
winfo_rootx()和winfo_rooty()获取Entry的屏幕坐标,用place将滚动框架放在Entry正下方,避免干扰原有布局 - 外部点击隐藏:给主窗口绑定点击事件,通过
winfo_containing()判断点击区域,若不在Entry或滚动框架内则销毁框架 - 避免重复创建:用变量存储滚动框架实例,每次点击Entry前先销毁旧框架
- 单选按钮交互优化:给每个单选按钮添加点击事件,选择后自动更新Entry内容并隐藏框架
内容的提问来源于stack exchange,提问作者Sadiq
相关产品推荐
相关产品推荐

