Python 用于帧的Tkinter滚动条

Python 用于帧的Tkinter滚动条,python,tkinter,scrollbar,frame,Python,Tkinter,Scrollbar,Frame,我的目标是添加一个垂直滚动条到一个框架,其中有几个标签。一旦框架内的标签超过框架高度,滚动条应自动启用。经过搜索,我找到了有用的帖子。基于这篇文章,我明白为了实现我想要的(如果我错了,请纠正我,我是初学者),我必须先创建一个框架,然后在该框架内创建一个画布,并将滚动条粘贴到该框架上。之后,创建另一个框架并将其作为窗口对象放在画布中。所以,我终于想到了这个: from Tkinter import * def data(): for i in range(50): Labe

我的目标是添加一个垂直滚动条到一个框架,其中有几个标签。一旦框架内的标签超过框架高度,滚动条应自动启用。经过搜索,我找到了有用的帖子。基于这篇文章,我明白为了实现我想要的(如果我错了,请纠正我,我是初学者),我必须先创建一个
框架
,然后在该框架内创建一个
画布
,并将滚动条粘贴到该框架上。之后,创建另一个框架并将其作为窗口对象放在画布中。所以,我终于想到了这个:

from Tkinter import *

def data():
    for i in range(50):
       Label(frame,text=i).grid(row=i,column=0)
       Label(frame,text="my text"+str(i)).grid(row=i,column=1)
       Label(frame,text="..........").grid(row=i,column=2)

def myfunction(event):
    canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)

root=Tk()
sizex = 800
sizey = 600
posx  = 100
posy  = 100
root.wm_geometry("%dx%d+%d+%d" % (sizex, sizey, posx, posy))

myframe=Frame(root,relief=GROOVE,width=50,height=100,bd=1)
myframe.place(x=10,y=10)

canvas=Canvas(myframe)
frame=Frame(canvas)
myscrollbar=Scrollbar(myframe,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)

myscrollbar.pack(side="right",fill="y")
canvas.pack(side="left")
canvas.create_window((0,0),window=frame,anchor='nw')
frame.bind("<Configure>",myfunction)
data()
root.mainloop()
从Tkinter导入*
def data():
对于范围(50)内的i:
标签(框架,文本=i).网格(行=i,列=0)
标签(frame,text=“my text”+str(i)).grid(行=i,列=1)
标签(框,文本=“……”)。网格(行=i,列=2)
def myfunction(事件):
configure(scrollregion=canvas.bbox(“全部”),宽度=200,高度=200)
root=Tk()
sizex=800
sizey=600
posx=100
posy=100
root.wm_几何体(“%dx%d+%d+%d”%(sizex、sizey、posx、posy))
myframe=框架(根部,浮雕=凹槽,宽度=50,高度=100,bd=1)
myframe.place(x=10,y=10)
画布=画布(myframe)
框架=框架(画布)
myscrollbar=Scrollbar(myframe,orient=“vertical”,command=canvas.yview)
configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side=“right”,fill=“y”)
帆布包装(side=“left”)
canvas.create_window((0,0),window=frame,anchor='nw')
frame.bind(“,myfunction)
数据()
root.mainloop()
  • 我做得对吗?有没有更好/更聪明的方法来实现这段代码给我的输出
  • 为什么我必须使用网格方法?(我尝试了place方法,但画布上没有显示任何标签。)
  • 在画布上创建窗口时,使用
    anchor='nw'
    有什么特别之处
  • 请保持您的答案简单,因为我是初学者。

    请注意,建议的代码仅对Python 2有效

    以下是一个例子:

    from Tkinter import *   # from x import * is bad practice
    from ttk import *
    
    # http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
    
    class VerticalScrolledFrame(Frame):
        """A pure Tkinter scrollable frame that actually works!
        * Use the 'interior' attribute to place widgets inside the scrollable frame
        * Construct and pack/place/grid normally
        * This frame only allows vertical scrolling
    
        """
        def __init__(self, parent, *args, **kw):
            Frame.__init__(self, parent, *args, **kw)            
    
            # create a canvas object and a vertical scrollbar for scrolling it
            vscrollbar = Scrollbar(self, orient=VERTICAL)
            vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
            canvas = Canvas(self, bd=0, highlightthickness=0,
                            yscrollcommand=vscrollbar.set)
            canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
            vscrollbar.config(command=canvas.yview)
    
            # reset the view
            canvas.xview_moveto(0)
            canvas.yview_moveto(0)
    
            # create a frame inside the canvas which will be scrolled with it
            self.interior = interior = Frame(canvas)
            interior_id = canvas.create_window(0, 0, window=interior,
                                               anchor=NW)
    
            # track changes to the canvas and frame width and sync them,
            # also updating the scrollbar
            def _configure_interior(event):
                # update the scrollbars to match the size of the inner frame
                size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
                canvas.config(scrollregion="0 0 %s %s" % size)
                if interior.winfo_reqwidth() != canvas.winfo_width():
                    # update the canvas's width to fit the inner frame
                    canvas.config(width=interior.winfo_reqwidth())
            interior.bind('<Configure>', _configure_interior)
    
            def _configure_canvas(event):
                if interior.winfo_reqwidth() != canvas.winfo_width():
                    # update the inner frame's width to fill the canvas
                    canvas.itemconfigure(interior_id, width=canvas.winfo_width())
            canvas.bind('<Configure>', _configure_canvas)
    
    
    if __name__ == "__main__":
    
        class SampleApp(Tk):
            def __init__(self, *args, **kwargs):
                root = Tk.__init__(self, *args, **kwargs)
    
    
                self.frame = VerticalScrolledFrame(root)
                self.frame.pack()
                self.label = Label(text="Shrink the window to activate the scrollbar.")
                self.label.pack()
                buttons = []
                for i in range(10):
                    buttons.append(Button(self.frame.interior, text="Button " + str(i)))
                    buttons[-1].pack()
    
        app = SampleApp()
        app.mainloop()
    
    从Tkinter导入*#从x导入*是不好的做法
    从ttk导入*
    # http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame
    类垂直滚动框架(框架):
    “”“一个真正有效的纯Tkinter可滚动框架!
    *使用“内部”属性将小部件放置在可滚动框架内
    *正常建造和包装/放置/网格
    *此框架仅允许垂直滚动
    """
    定义初始值(自、父、*args、**kw):
    帧。\uuuuu初始化(自,父,*args,**kw)
    #创建画布对象和用于滚动的垂直滚动条
    vscrollbar=滚动条(自,方向=垂直)
    vscrollbar.pack(填充=Y,侧边=RIGHT,展开=FALSE)
    画布=画布(自我,bd=0,highlightthickness=0,
    yscrollcommand=vscrollbar.set)
    canvas.pack(side=LEFT,fill=BOTH,expand=TRUE)
    vscrollbar.config(命令=canvas.yview)
    #重置视图
    canvas.xview_moveto(0)
    canvas.yview_moveto(0)
    #在画布内创建一个框架,该框架将与其一起滚动
    self.interior=内部=框架(画布)
    内部=画布。创建窗口(0,0,窗口=内部,
    锚点=西北)
    #跟踪画布和帧宽度的更改并同步它们,
    #同时更新滚动条
    def_配置_内部(事件):
    #更新滚动条以匹配内部框架的大小
    大小=(interior.winfo_reqwidth(),interior.winfo_reqheight())
    canvas.config(scrollregion=“0%s%s”%size)
    if interior.winfo_reqwidth()!=canvas.winfo_width():
    #更新画布的宽度以适合内部框架
    canvas.config(width=interior.winfo_reqwidth())
    内部绑定(“”,\u配置\u内部)
    定义配置画布(事件):
    if interior.winfo_reqwidth()!=canvas.winfo_width():
    #更新内部框架的宽度以填充画布
    canvas.itemconfigure(interior\u id,width=canvas.winfo\u width())
    canvas.bind(“”,\u configure\u canvas)
    如果名称=“\uuuuu main\uuuuuuuu”:
    类样本应用程序(Tk):
    定义初始化(self,*args,**kwargs):
    root=Tk.\uuuu init\uuuu(self,*args,**kwargs)
    self.frame=垂直滚动帧(根)
    self.frame.pack()
    self.label=label(text=“收缩窗口以激活滚动条”)
    self.label.pack()
    按钮=[]
    对于范围(10)内的i:
    buttons.append(按钮(self.frame.interior,text=“Button”+str(i)))
    按钮[-1].pack()
    app=SampleApp()
    app.mainloop()
    
    它还没有将鼠标滚轮绑定到滚动条,但这是可能的。不过,用滚轮滚动可能会有点颠簸

    编辑:

    到1)
    在Tkinter中,IMHO滚动帧有点棘手,似乎做得不多。似乎没有优雅的方式来做这件事。
    代码的一个问题是必须手动设置画布大小——这就是我发布的示例代码所解决的问题

    到2)
    你说的是数据函数?这个地方也适合我。(一般来说,我更喜欢网格)

    到3)
    嗯,它将窗口定位在画布上

    我注意到的一点是,您的示例默认处理鼠标滚轮滚动,而我发布的示例则不处理。总有一天会看到的

    “我做得对吗?有没有更好/更聪明的方法来实现这段代码给我的输出?”

    一般来说,是的,你做得对。Tkinter除了画布之外没有本机可滚动容器。正如你所看到的,设置起来真的没有那么困难。正如您的示例所示,它只需要5或6行代码就可以工作——这取决于您如何计算行数

    为什么必须使用网格方法?(我尝试了place方法,但画布上没有显示任何标签?)

    你问我为什么你必须离开
    f = Tkinter.Frame(self.master,width=3)
    f.grid(row=2, column=0, columnspan=8, rowspan=10, pady=30, padx=30)
    f.config(width=5)
    self.tree = ttk.Treeview(f, selectmode="extended")
    scbHDirSel =tk.Scrollbar(f, orient=Tkinter.HORIZONTAL, command=self.tree.xview)
    scbVDirSel =tk.Scrollbar(f, orient=Tkinter.VERTICAL, command=self.tree.yview)
    self.tree.configure(yscrollcommand=scbVDirSel.set, xscrollcommand=scbHDirSel.set)           
    self.tree["columns"] = (self.columnListOutput)
    self.tree.column("#0", width=40)
    self.tree.heading("#0", text='SrNo', anchor='w')
    self.tree.grid(row=2, column=0, sticky=Tkinter.NSEW,in_=f, columnspan=10, rowspan=10)
    scbVDirSel.grid(row=2, column=10, rowspan=10, sticky=Tkinter.NS, in_=f)
    scbHDirSel.grid(row=14, column=0, rowspan=2, sticky=Tkinter.EW,in_=f)
    f.rowconfigure(0, weight=1)
    f.columnconfigure(0, weight=1)
    
    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
    
        __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs)
        docstring:
        Parent = master of scrolled window
        canv_w - width of canvas
        canv_h - height of canvas
    
        """
    
    
        def __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs):
            """Parent = master of scrolled window
            canv_w - width of canvas
            canv_h - height of canvas
    
           """
            super().__init__(parent, *args, **kwargs)
    
            self.parent = parent
    
            # creating a scrollbars
            self.xscrlbr = ttk.Scrollbar(self.parent, orient = 'horizontal')
            self.xscrlbr.grid(column = 0, row = 1, sticky = 'ew', columnspan = 2)         
            self.yscrlbr = ttk.Scrollbar(self.parent)
            self.yscrlbr.grid(column = 1, row = 0, sticky = 'ns')         
            # creating a canvas
            self.canv = tk.Canvas(self.parent)
            self.canv.config(relief = 'flat',
                             width = 10,
                             heigh = 10, bd = 2)
            # placing a canvas into frame
            self.canv.grid(column = 0, row = 0, sticky = 'nsew')
            # accociating scrollbar comands to canvas scroling
            self.xscrlbr.config(command = self.canv.xview)
            self.yscrlbr.config(command = self.canv.yview)
    
            # creating a frame to inserto to canvas
            self.scrollwindow = ttk.Frame(self.parent)
    
            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.yscrlbr.lift(self.scrollwindow)        
            self.xscrlbr.lift(self.scrollwindow)
            self.scrollwindow.bind('<Configure>', self._configure_window)  
            self.scrollwindow.bind('<Enter>', self._bound_to_mousewheel)
            self.scrollwindow.bind('<Leave>', self._unbound_to_mousewheel)
    
            return
    
        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):
            # update the scrollbars to match the size of the inner frame
            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():
                # update the canvas's width to fit the inner frame
                self.canv.config(width = self.scrollwindow.winfo_reqwidth())
            if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height():
                # update the canvas's width to fit the inner frame
                self.canv.config(height = self.scrollwindow.winfo_reqheight())