微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

tkinter可滚动框架仅使用鼠标滚轮滚动笔记本中最后添加的页面

如何解决tkinter可滚动框架仅使用鼠标滚轮滚动笔记本中最后添加的页面

我正在尝试制作带有标签的应用。我的选项卡上会有很多按钮,因此每个选项卡上都需要一个可滚动的框架。以下是我从here获取代码

import tkinter as tk
from tkinter import ttk

# ************************
# Scrollable Frame Class
# ************************
class ScrollFrame(tk.Frame):
    def __init__(self,parent):
        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.canvas.bind_all("<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):
        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):

        tk.Frame.__init__(self,root)
        self.scrollFrame = ScrollFrame(self) # 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
            tk.Label(self.scrollFrame.viewPort,text="%s" % row,width=3,borderwidth="1",relief="solid").grid(row=row,column=0)
            t="this is the second column for row %s" %row
            tk.Button(self.scrollFrame.viewPort,text=t,command=lambda x=a: self.printMsg("Hello " + str(x))).grid(row=row,column=1)

        # when packing the scrollframe,we pack scrollFrame itself (NOT the viewPort)
        self.scrollFrame.pack(side="top",expand=True)
    
    def printMsg(self,msg):
        print(msg)

if __name__ == "__main__":
    root=tk.Tk()
    main_frame = tk.Frame(root)
    nb = ttk.Notebook(main_frame)
    frame = Example(nb) 
    #frame = tk.Frame(main_frame)
    frame.pack()
    nb.add(frame,text = "frame1")
    frame1 = Example(nb) 
    frame1.pack()
    nb.add(frame1,text = "frame2")
    nb.pack(side = "left")
    main_frame.pack()
    root.mainloop()

问题是我只能使用鼠标滚轮滚动笔记本中最后添加标签页。可滚动框架按预期工作于单个选项卡。但是,当添加多个标签时,我只能通过鼠标滚轮滚动最后添加标签

解决方法

类似于画布小部件上的bind_all调用的问题,每次在ScrollableFrame中被调用时,bind_all都会替换回调。我为每个选项卡使用bindtags和bind_class来分隔回调。下面是更新的代码:

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",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()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。