如何将Bryan Oakley的Tkinter PageOne类设为Mikhail ScrolledWindow的子类
问题核心原因
- 你直接把
SampleApp类本身当parent参数传递是错误的,parent需要接收父组件的实例,不是类本身 SampleApp.container写法错误,原代码里container是SampleApp.__init__方法内的局部变量,既不是类属性也不是实例属性,外部无法直接访问- 你想让
PageOne获得滚动能力,应该用类继承的方式让PageOne成为ScrolledWindow的子类,而不是直接给PageOne赋值ScrolledWindow实例,语法逻辑完全错误 - 原ScrolledWindow代码存在bug:滚动条、画布都挂载到了外层父容器上,会导致多页面切换时滚动条串页,必须先修复该问题才能正常使用
可行修改步骤
步骤1:修复ScrolledWindow的挂载逻辑
class ScrolledWindow(tk.Frame): """ 1. Master widget gets scrollbars and a canvas. Scrollbars are connected to canvas scrollregion. 2. self.scrollwindow is created and inserted into canvas Usage Guideline: Assign any widgets as children of <ScrolledWindow instance>.scrollwindow to get them inserted into canvas """ def __init__(self, parent, controller, canv_w = 400, canv_h = 400, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.controller = controller self.parent = parent # 给当前滚动窗口设置行列权重,让滚动区域自适应窗口大小 self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) # 滚动条、画布全部挂载到当前ScrolledWindow实例,不是外层parent self.xscrlbr = ttk.Scrollbar(self, orient = 'horizontal') self.xscrlbr.grid(column = 0, row = 1, sticky = 'ew') self.yscrlbr = ttk.Scrollbar(self) self.yscrlbr.grid(column = 1, row = 0, sticky = 'ns') # 创建画布 self.canv = tk.Canvas(self, width=canv_w, height=canv_h) self.canv.config(relief = 'flat', bd = 2) self.canv.grid(column = 0, row = 0, sticky = 'nsew') # 关联滚动条和画布 self.xscrlbr.config(command = self.canv.xview) self.yscrlbr.config(command = self.canv.yview) # 创建滚动内容区 self.scrollwindow = ttk.Frame(self.canv) self.canv.create_window(0, 0, window = self.scrollwindow, anchor = 'nw') self.canv.config(xscrollcommand = self.xscrlbr.set, yscrollcommand = self.yscrlbr.set, scrollregion = (0, 0, 100, 100)) self.scrollwindow.bind('<Configure>', self._configure_window) self.scrollwindow.bind('<Enter>', self._bound_to_mousewheel) self.scrollwindow.bind('<Leave>', self._unbound_to_mousewheel) # 下面的方法和原代码完全一致,无需修改 def _bound_to_mousewheel(self, event): self.canv.bind_all("<MouseWheel>", self._on_mousewheel) def _unbound_to_mousewheel(self, event): self.canv.unbind_all("<MouseWheel>") def _on_mousewheel(self, event): self.canv.yview_scroll(int(-1*(event.delta/120)), "units") def _configure_window(self, event): size = (self.scrollwindow.winfo_reqwidth(), self.scrollwindow.winfo_reqheight()) self.canv.config(scrollregion='0 0 %s %s' % size) if self.scrollwindow.winfo_reqwidth() != self.canv.winfo_width(): self.canv.config(width = self.scrollwindow.winfo_reqwidth()) if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height(): self.canv.config(height= self.scrollwindow.winfo_reqheight())
步骤2:修改PageOne的继承关系
让PageOne直接继承ScrolledWindow,所有页面内容挂载到self.scrollwindow即可:
class PageOne(ScrolledWindow): def __init__(self, parent, controller): # 调用父类ScrolledWindow的初始化逻辑 super().__init__(parent, controller) # 所有页面内容全部放到self.scrollwindow中,不要放到self上 label = tk.Label(self.scrollwindow, text="这是带滚动功能的Page 1", font=controller.title_font) label.pack(side="top", fill="x", pady=10) button = tk.Button(self.scrollwindow, text="回到首页", command=lambda: controller.show_frame("StartPage")) button.pack() # 可以加大量内容测试滚动效果 for i in range(50): tk.Label(self.scrollwindow, text=f"测试内容行 {i+1}").pack(pady=3)
步骤3:调整SampleApp的页面注册逻辑
如果你不需要单独的ScrolledWindow测试页面,可以把循环里的ScrolledWindow去掉:
# 原代码 for F in (StartPage, PageOne, ScrolledWindow): # 修改后 for F in (StartPage, PageOne):
效果验证
运行代码后点击首页的「Go to Page One」按钮,即可看到带滚动功能的PageOne页面,内容超过窗口高度时会自动触发滚动条,页面切换不会出现滚动条残留的问题。
内容的提问来源于stack exchange,提问作者pkanda
相关产品推荐
相关产品推荐

