求助:如何为含大量按钮的CTkFrame添加可用Scrollbar
解决CustomTkinter销售页面滚动条失效问题
问题说明
使用CustomTkinter和Tkinter开发销售页面时,点击ShowAllSales按钮会在self.AllInsideThisFrame中生成大量销售按钮,但添加滚动条后仅显示灰色不可用状态。此前已实现文本内容的可滚动框架,现需适配按钮场景,将self.AllInsideThisFrame改造为可滚动框架。
解决方案
核心问题是普通CTkFrame无法自动识别内部超出视口的内容,且使用place布局不会触发容器的高度计算。CustomTkinter提供了CTkScrollableFrame组件,专门用于处理滚动场景,只需调整布局方式即可激活滚动条:
- 用
ctk.CTkScrollableFrame替代普通CTkFrame,组件自带滚动条,无需手动配置 - 内部按钮改用
pack布局(而非place),让容器自动计算内容总高度 - 每次点击按钮时先销毁旧的滚动框架,避免重复创建
- 避免循环中重复创建标题标签,仅初始化一次
修改后的完整代码
class sales_page(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.config(background="#333333") TopOfWindowFrame = ctk.CTkFrame(self, bg_color="#333333", fg_color="grey", height=100) TopOfWindowFrame.pack(padx=0, pady=0, fill='x', expand=True, anchor='n') labelaoralogo = ctk.CTkLabel(TopOfWindowFrame, image=aoralogo, text='') labelaoralogo.pack(padx=5, pady=5, anchor='center', expand=True) # SalesPageFramesAndUI self.ShowOptionsFrame = ctk.CTkFrame(self, fg_color='white', bg_color='#333333', width=400, height=600) self.ShowOptionsFrame.pack(padx=5, pady=5, fill='y', expand=True, side='top', anchor='nw') ShowAllSales = ctk.CTkButton(self.ShowOptionsFrame, width=400, height=50, text='Όλες οι πωλήσεις', font=('robotic bold', 20), hover_color='#4B0082', fg_color='#a881af', command=self.AllSalesOrders, border_width=3, border_color='black') ShowAllSales.place(x=0, y=0) ShowSalesByCustomer = ctk.CTkButton(self.ShowOptionsFrame, width=400, height=50, text='Πωλήσεις ανά Πελάτη', font=('robotic bold', 20), hover_color='#4B0082', fg_color='#a881af', border_width=3, border_color='black') ShowSalesByCustomer.place(x=0, y=47) # 初始化滚动框架变量,避免重复创建时报错 self.AllInsideThisFrame = None def AllSalesOrders(self): # 销毁旧的滚动框架(如果存在) if self.AllInsideThisFrame: self.AllInsideThisFrame.destroy() CallDB = aoracandlestudiodb.cursor() CallDB.execute('SELECT orders_id FROM sales ORDER BY _date') AllSales = CallDB.fetchall() # 替换为CTkScrollableFrame,设置滚动方向为垂直 self.AllInsideThisFrame = ctk.CTkScrollableFrame(self, width=1000, height=750, bg_color="#333333", border_width=8, border_color="purple", fg_color='white') self.AllInsideThisFrame.place(x=450, y=150) # 只创建一次标题标签,放在滚动框架顶部 SalesTypeLabel = ctk.CTkLabel(self.AllInsideThisFrame, width=980, height=50, text='Όλες οι πωλήσεις', font=('arial bold', 25), text_color='black') SalesTypeLabel.pack(pady=(10, 20), padx=10, fill='x') for sale_id in AllSales: get_ID_str = str(sale_id) get_sales_ID = get_ID_str[2:-3] get_sales_date = get_DateOfSale(get_sales_ID) get_sales_price = get_TotalPriceOfSale(get_sales_ID) get_sales_customer = get_CustomerOfSale(get_sales_ID) # 传递必要参数,无需distance(pack会自动排列) self.MakeButtonsForSales(get_sales_date, get_sales_ID, get_sales_price, get_sales_customer) def MakeButtonsForSales(self, Sales_Date, Sales_ID, Sales_price, Sales_Customer): self.SaleButton = ctk.CTkButton(self.AllInsideThisFrame, width=980, height=50, hover_color='#4B0082', fg_color='#a881af', text=( 'Ημερομηνία: ' + Sales_Date + " " "Σύνολο Παραγγελίας: " + Sales_price + " Πελάτης: " + Sales_Customer), command=lambda id=Sales_ID, cust=Sales_Customer, price=Sales_price, date=Sales_Date: self.MakeLabelsForItemsInSales(id, cust, price, date), font=('Arial', 15), border_width=5, border_color='black') # 用pack布局,自动垂直排列,设置上下间距 self.SaleButton.pack(pady=5, padx=10, fill='x')
关键改动说明
- 替换滚动容器:用
CTkScrollableFrame替代普通CTkFrame,组件自动处理滚动条逻辑 - 优化布局方式:按钮改用
pack布局,让容器自动计算总高度,触发滚动条激活 - 避免重复创建:每次点击按钮先销毁旧框架,防止界面叠加
- 标题标签优化:将标题标签移到循环外,避免重复创建
- Lambda参数修正:绑定命令时显式传递参数,避免循环变量引用问题
内容的提问来源于stack exchange,提问作者Pavlos Katsioulis
相关产品推荐
相关产品推荐

