Python 3.x tkinter仅可滚动框架使用鼠标滚轮滚动笔记本中最后添加的页面
我正在尝试制作一个带有标签的应用程序。我会在标签上有很多按钮,所以我需要在每个标签上滚动框。以下是我从中获取的代码:Python 3.x tkinter仅可滚动框架使用鼠标滚轮滚动笔记本中最后添加的页面,python-3.x,tkinter,tkinter-canvas,Python 3.x,Tkinter,Tkinter Canvas,我正在尝试制作一个带有标签的应用程序。我会在标签上有很多按钮,所以我需要在每个标签上滚动框。以下是我从中获取的代码: 将tkinter作为tk导入 从tkinter导入ttk # ************************ #可滚动框架类 # ************************ 类ScrollFrame(tk.Frame): 定义初始化(自身,父级): super().uuu init_uuuuuu(父)#创建一个框架(自) self.canvas=tk.canvas(sel
将tkinter作为tk导入
从tkinter导入ttk
# ************************
#可滚动框架类
# ************************
类ScrollFrame(tk.Frame):
定义初始化(自身,父级):
super().uuu init_uuuuuu(父)#创建一个框架(自)
self.canvas=tk.canvas(self,borderwidth=0,background=“#ffffff”)#将画布放在self上
self.viewPort=tk.Frame(self.canvas,background=“#ffffffff”)#在画布上放置一个框架,该框架将容纳子小部件
self.vsb=tk.Scrollbar(self,orient=“vertical”,command=self.canvas.yview)#在self上放置一个滚动条
self.canvas.configure(yscrollcommand=self.vsb.set)#将滚动条操作附加到画布的滚动
self.vsb.pack(side=“right”,fill=“y”)#self右侧的pack滚动条
self.canvas.pack(side=“left”,fill=“both”,expand=True)#将画布打包到self的左侧并展开到fil
self.canvas_window=self.canvas.create_window((4,4),window=self.viewPort,anchor=“nw”,#将视图端口框架添加到画布
tags=“self.viewPort”)
self.viewPort.bind(“,self.onFrameConfigure)#每当视口帧的大小更改时绑定事件。
self.canvas.bind(“,self.onCanvasConfigure)#每当视口帧的大小改变时绑定事件。
self.canvas.bind_all(“,self.onScroll)#每当视口帧的大小改变时绑定事件。
self.onFrameConfigure(None)#在渲染时执行初始拉伸,否则在第一次调整大小之前,滚动区域的边框很小
def onFrameConfigure(自我,事件):
''重置滚动区域以包含内部框架''
self.canvas.configure(scrollregion=self.canvas.bbox(“全部”))#每当帧大小改变时,分别改变滚动区域。
def onCanvasConfigure(自我、事件):
''在需要时重置画布窗口以包含内部框架''
画布宽度=event.width
self.canvas.itemconfig(self.canvas_window,width=canvas_width)#每当画布的大小改变时,分别改变窗口区域。
def onScroll(自身、事件):
如果event.state==0:
self.canvas.yview_滚动条(int(-1*(event.delta/120)),“单位”)
返回“中断”
elif event.state==1:
self.canvas.xview_卷轴(int(-1*(event.delta/120)),“单位”)
返回“中断”
# ********************************
#上述类的示例用法
# ********************************
类示例(tk.Frame):
定义初始化(自,根):
tk.Frame.\uuuu init\uuuu(self,root)
self.scrollFrame=scrollFrame(self)#添加新的可滚动框架。
#现在向滚动框添加一些控件。
#注意:子控件添加到视图端口(scrollFrame.viewPort,而不是scrollFrame本身)
对于范围(100)中的行:
a=行
标签(self.scrollFrame.viewPort,text=“%s”%row,width=3,borderwidth=“1”,
relief=“solid”).grid(行=行,列=0)
t=“这是第%s行“%row”的第二列
tk.Button(self.scrollFrame.viewPort,text=t,command=lambda x=a:self.printMsg(“Hello”+str(x))).grid(行=行,列=1)
#打包scrollframe时,我们打包scrollframe本身(而不是视口)
self.scrollFrame.pack(side=“top”,fill=“both”,expand=True)
def printMsg(自我,msg):
打印(msg)
如果名称=“\uuuuu main\uuuuuuuu”:
root=tk.tk()
主框架=传统框架(根)
nb=ttk.笔记本(主框架)
帧=示例(nb)
#框架=传统框架(主框架)
frame.pack()
注意:添加(frame,text=“frame1”)
frame1=示例(nb)
frame1.pack()
注意:添加(frame1,text=“frame2”)
注意:包装(侧面=“左侧”)
主框架包()
root.mainloop()
问题是我只能用鼠标滚轮滚动笔记本中最后添加的选项卡。对于单个选项卡,可滚动框架可以按预期工作。但当添加多个选项卡时,我只能通过鼠标滚轮滚动上次添加的选项卡。看起来画布上的bind_all调用小部件存在问题,每次在ScrollableFrame中调用时,bind_all都会替换回调。我为每个选项卡使用了bindtags和bind_类来分隔回调。更新代码如下:
import tkinter as tk
from tkinter import ttk
# ************************
# Scrollable Frame Class
# ************************
class ScrollFrame(tk.Frame):
def __init__(self, parent,frame_name):
super().__init__(parent) # create a frame (self)
self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") #place canvas on self
self.viewPort = tk.Frame(self.canvas, background="#ffffff") #place a frame on the canvas, this frame will hold the child widgets
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) #place a scrollbar on self
self.canvas.configure(yscrollcommand=self.vsb.set) #attach scrollbar action to scroll of canvas
self.vsb.pack(side="right", fill="y") #pack scrollbar to right of self
self.canvas.pack(side="left", fill="both", expand=True) #pack canvas to left of self and expand to fil
self.canvas_window = self.canvas.create_window((4,4), window=self.viewPort, anchor="nw", #add view port frame to canvas
tags="self.viewPort")
self.viewPort.bind("<Configure>", self.onFrameConfigure) #bind an event whenever the size of the viewPort frame changes.
self.canvas.bind("<Configure>", self.onCanvasConfigure) #bind an event whenever the size of the viewPort frame changes.
self.viewPort.bindtags((frame_name,)+(self.viewPort.bindtags()))
self.viewPort.bind_class(frame_name,"<MouseWheel>", self.onScroll) #bind an event whenever the size of the viewPort frame changes.
self.onFrameConfigure(None) #perform an initial stretch on render, otherwise the scroll region has a tiny border until the first resize
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all")) #whenever the size of the frame changes, alter the scroll region respectively.
def onCanvasConfigure(self, event):
'''Reset the canvas window to encompass inner frame when required'''
canvas_width = event.width
self.canvas.itemconfig(self.canvas_window, width = canvas_width) #whenever the size of the canvas changes alter the window region respectively.
def onScroll(self,event):
print(event)
if event.state == 0:
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
return "break"
elif event.state == 1:
self.canvas.xview_scroll(int(-1*(event.delta/120)), "units")
return "break"
# ********************************
# Example usage of the above class
# ********************************
class Example(tk.Frame):
def __init__(self, root,frame):
tk.Frame.__init__(self, root)
self.scrollFrame = ScrollFrame(self,frame) # add a new scrollable frame.
# Now add some controls to the scrollframe.
# NOTE: the child controls are added to the view port (scrollFrame.viewPort, NOT scrollframe itself)
for row in range(100):
a = row
label = tk.Label(self.scrollFrame.viewPort, text="%s" % row, width=3, borderwidth="1",
relief="solid")
label.grid(row=row, column=0)
label.bindtags((frame,)+(label.bindtags()))
t="this is the second column for row %s" %row
button = tk.Button(self.scrollFrame.viewPort, text=t, command=lambda x=a: self.scrollFrame.canvas.yview_scroll(1,"units"))
button.grid(row=row, column=1)
button.bindtags((frame,)+(button.bindtags()))
# when packing the scrollframe, we pack scrollFrame itself (NOT the viewPort)
self.scrollFrame.pack(side="top", fill="both", expand=True)
def printMsg(self, msg):
print(msg)
if __name__ == "__main__":
root=tk.Tk()
main_frame = tk.Frame(root)
nb = ttk.Notebook(main_frame)
frame1 = Example(nb,"frame1")
frame1.pack()
nb.add(frame1,text = "frame1")
frame2 = Example(nb,"frame2")
frame2.pack()
nb.add(frame2,text = "frame2")
nb.pack(side = "left")
main_frame.pack()
root.bind_all("<FocusIn>", lambda e : print(root.focus_get(),flush=True))
root.mainloop()
将tkinter作为tk导入
从tkinter导入ttk
# ************************
#可滚动框架类
# ************************
类ScrollFrame(tk.Frame):
定义初始化(自、父、帧名称):
super().uuu init_uuuuuu(父)#创建一个框架(自)
self.canvas=tk.canvas(self,borderwidth=0,background=“#ffffff”)#将画布放在self上
self.viewPort=tk.Frame(self.canvas,background=“#ffffffff”)#在画布上放置一个框架,该框架将容纳子小部件
self.vsb=tk.Scrollbar(self,orient=“vertical”,command=self.canvas.yview)#在self上放置一个滚动条
self.canvas.configure(yscrollcommand=self.vsb.set)#将滚动条操作附加到画布的滚动
self.vsb.pack(side=“right”,fill=“y”)#self右侧的pack滚动条
self.canvas.pack(side=“left”,fill=“both”,expand=True)#将画布打包到self的左侧并展开到fil
self.canvas\u window=self.canvas.create\u window((4,4),window=self.viewPort,anchor=“nw”
import tkinter as tk
from tkinter import ttk
# ************************
# Scrollable Frame Class
# ************************
class ScrollFrame(tk.Frame):
def __init__(self, parent,frame_name):
super().__init__(parent) # create a frame (self)
self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") #place canvas on self
self.viewPort = tk.Frame(self.canvas, background="#ffffff") #place a frame on the canvas, this frame will hold the child widgets
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) #place a scrollbar on self
self.canvas.configure(yscrollcommand=self.vsb.set) #attach scrollbar action to scroll of canvas
self.vsb.pack(side="right", fill="y") #pack scrollbar to right of self
self.canvas.pack(side="left", fill="both", expand=True) #pack canvas to left of self and expand to fil
self.canvas_window = self.canvas.create_window((4,4), window=self.viewPort, anchor="nw", #add view port frame to canvas
tags="self.viewPort")
self.viewPort.bind("<Configure>", self.onFrameConfigure) #bind an event whenever the size of the viewPort frame changes.
self.canvas.bind("<Configure>", self.onCanvasConfigure) #bind an event whenever the size of the viewPort frame changes.
self.viewPort.bindtags((frame_name,)+(self.viewPort.bindtags()))
self.viewPort.bind_class(frame_name,"<MouseWheel>", self.onScroll) #bind an event whenever the size of the viewPort frame changes.
self.onFrameConfigure(None) #perform an initial stretch on render, otherwise the scroll region has a tiny border until the first resize
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all")) #whenever the size of the frame changes, alter the scroll region respectively.
def onCanvasConfigure(self, event):
'''Reset the canvas window to encompass inner frame when required'''
canvas_width = event.width
self.canvas.itemconfig(self.canvas_window, width = canvas_width) #whenever the size of the canvas changes alter the window region respectively.
def onScroll(self,event):
print(event)
if event.state == 0:
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
return "break"
elif event.state == 1:
self.canvas.xview_scroll(int(-1*(event.delta/120)), "units")
return "break"
# ********************************
# Example usage of the above class
# ********************************
class Example(tk.Frame):
def __init__(self, root,frame):
tk.Frame.__init__(self, root)
self.scrollFrame = ScrollFrame(self,frame) # add a new scrollable frame.
# Now add some controls to the scrollframe.
# NOTE: the child controls are added to the view port (scrollFrame.viewPort, NOT scrollframe itself)
for row in range(100):
a = row
label = tk.Label(self.scrollFrame.viewPort, text="%s" % row, width=3, borderwidth="1",
relief="solid")
label.grid(row=row, column=0)
label.bindtags((frame,)+(label.bindtags()))
t="this is the second column for row %s" %row
button = tk.Button(self.scrollFrame.viewPort, text=t, command=lambda x=a: self.scrollFrame.canvas.yview_scroll(1,"units"))
button.grid(row=row, column=1)
button.bindtags((frame,)+(button.bindtags()))
# when packing the scrollframe, we pack scrollFrame itself (NOT the viewPort)
self.scrollFrame.pack(side="top", fill="both", expand=True)
def printMsg(self, msg):
print(msg)
if __name__ == "__main__":
root=tk.Tk()
main_frame = tk.Frame(root)
nb = ttk.Notebook(main_frame)
frame1 = Example(nb,"frame1")
frame1.pack()
nb.add(frame1,text = "frame1")
frame2 = Example(nb,"frame2")
frame2.pack()
nb.add(frame2,text = "frame2")
nb.pack(side = "left")
main_frame.pack()
root.bind_all("<FocusIn>", lambda e : print(root.focus_get(),flush=True))
root.mainloop()