【问题标题】:resizeable scrollable canvas with tkinter使用 tkinter 可调整大小的可滚动画布
【发布时间】:2016-07-26 13:14:56
【问题描述】:

这是一个非常简单的 gui 代码:

from Tkinter import *

class my_gui(Frame):

    def __init__(self):
        # main tk object
        self.root = Tk()

        # init Frame
        Frame.__init__(self, self.root)

        # create frame (gray window)
        self.frame=Frame(self.root,width=100,height=100)
        self.frame.grid(row=0,column=0)

        self.__add_scroll_bars()

        self.__create_canvas()

        self.__add_plot()

    def __create_canvas(self):
        # create white area in the window for plotting
        # width and height are only the visible size of the white area, scrollregion is the area the user can see by scrolling
        self.canvas = Canvas(self.frame,bg='#FFFFFF',width=300,height=300,scrollregion=(0,0,500,500))

        # with this command the window is filled with the canvas
        self.canvas.pack(side=LEFT,expand=True,fill=BOTH)

        # position and size of the canvas is used for configuration of the scroll bars
        self.canvas.config(xscrollcommand=self.hbar.set, yscrollcommand=self.vbar.set)

        # add command to the scroll bars to scroll the canvas
        self.hbar.config(command = self.canvas.xview)
        self.vbar.config(command = self.canvas.yview)

    def __add_scroll_bars(self):
        # add scroll bars
        self.hbar=Scrollbar(self.frame,orient=HORIZONTAL)
        self.hbar.pack(side=BOTTOM,fill=X)
        self.vbar=Scrollbar(self.frame,orient=VERTICAL)
        self.vbar.pack(side=RIGHT,fill=Y)

    def __add_plot(self):
        # create a rectangle
        self.canvas.create_polygon(10, 10, 10, 150, 200, 150, 200, 10,  fill="gray", outline="black")

    def mainLoop(self):
        # This function starts an endlos running thread through the gui
        self.root.mainloop()

    def __quit(self):
        # close everything
        self.root.quit()

    def mainLoop(self):
        # This function starts an endlos running thread through the gui
        self.root.mainloop()

# init gui
my_gui = my_gui()
# execute gui
my_gui.mainLoop()

我有两个问题:

1) 如果我调整 gui 的大小,我希望滚动条始终位于 gui 的末端,然后我调整画布的大小。

2) 如果我调整 GUI 和画布的大小,那么画布中的矩形应该被调整大小(例如,如果 gui 和画布的新大小是旧大小的四倍,那么矩形的新大小是 twize旧尺寸)。

我分别为第一个问题和第二个问题寻找解决方案。

感谢您的帮助。

【问题讨论】:

    标签: python canvas tkinter resize scrollable


    【解决方案1】:

    您可以使用以下方式将我的框架集成到您的 gui 类中:

    from Tkinter import *
    
    
    class ScrollableFrame(Frame):
        def __init__(self, parent, *args, **kw):
            '''
            Constructor
            '''
    
            Frame.__init__(self, parent, *args, **kw)
    
            # create a vertical scrollbar
            vscrollbar = Scrollbar(self, orient = VERTICAL)
            vscrollbar.pack(fill = Y, side = RIGHT, expand = FALSE)
    
            # create a horizontal scrollbar
            hscrollbar = Scrollbar(self, orient = HORIZONTAL)
            hscrollbar.pack(fill = X, side = BOTTOM, expand = FALSE)
    
            #Create a canvas object and associate the scrollbars with it
            self.canvas = Canvas(self, bd = 0, highlightthickness = 0, yscrollcommand = vscrollbar.set, xscrollcommand = hscrollbar.set)
            self.canvas.pack(side = LEFT, fill = BOTH, expand = TRUE)
    
            #Associate scrollbars with canvas view
            vscrollbar.config(command = self.canvas.yview)
            hscrollbar.config(command = self.canvas.xview)
    
    
            # set the view to 0,0 at initialization
    
            self.canvas.xview_moveto(0)
            self.canvas.yview_moveto(0)
    
            # create an interior frame to be created inside the canvas
    
            self.interior = interior = Frame(self.canvas)
            interior_id = self.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())
                self.canvas.config(scrollregion='0 0 %s %s' % size)
                if interior.winfo_reqwidth() != self.canvas.winfo_width():
                    # update the canvas's width to fit the inner frame
                    self.canvas.config(width = interior.winfo_reqwidth())
            interior.bind('<Configure>', _configure_interior)
    
    
    class my_gui(Frame):
    
        def __init__(self):
            # main tk object
            self.root = Tk()
    
            # init Frame
            Frame.__init__(self, self.root)
    
            # create frame (gray window)
    
            self.frame = ScrollableFrame(self.root)
            self.frame.pack(fill=BOTH, expand=YES)
    
    
            #self.__add_scroll_bars()
    
            #self.__create_canvas()
    
            self.__add_plot()
    
        def __add_plot(self):
            # create a rectangle
            self.frame.canvas.create_polygon(10, 10, 10, 150, 200, 150, 200, 10,  fill="gray", outline="black")
    
        def mainLoop(self):
            # This function starts an endlos running thread through the gui
            self.root.mainloop()
    
        def __quit(self):
            # close everything
            self.root.quit()
    
    
    # init gui
    my_gui = my_gui()
    # execute gui
    my_gui.mainLoop()
    

    这应该基本上解决了您的第一个问题。至于第二个问题,您需要创建一个函数来在每次调整画布大小时重新渲染画布。方式类似于 _configure_interior 函数。

    【讨论】:

    • 你好,首先它可以工作,但在这个例子中,滚动条没有意义,没有什么可滚动的,画布总是与滚动条完全相同,我永远无法滚动。我想在 GUI 足够小的时候滚动(也许当 gui 小于 500 乘以 500 时,然后我想滚动这个大小。第二个当你让 gui 变大时,滚动条看起来如此,你可以不滚动。但是当您单击底部滚动条的左箭头时,您可以看到,矩形向右移动。这是一个错误吗?
    • 在某些情况下,将“size = (interior.winfo_reqwidth(),interior.winfo_reqheight())”行替换为“size = (max(500,interior.winfo_reqwidth()), max( 500, interior.winfo_reqheight() ) ) " 在大多数情况下,获得最小大小为 500 乘以 500 的可滚动画布。但是 _configure_interior 函数似乎不是在每种情况下都被调用,当 gui 的大小发生变化时。
    • 我执行的时候没有遇到这个bug。至于滚动条没有意义,那是因为您只有一个带有多边形的普通画布。您可以在其他元素(如标签、复选框、按钮等)中 pack 看到一旦窗口大小小于显示所有元素所需的大小,滚动条就会被激活。
    【解决方案2】:

    您可以使用以下示例,或将其集成到您的课程中。 您可以通过调用来创建这样的框架。

    self.frame = ScrollableFrame(self.root)
    self.frame.pack(fill=BOTH, expand=YES)
    

    为你的框架创建一个这样的类:

    from Tkinter import *    
    class ScrollableFrame(Frame):
    '''
    Creates a scrollable frame
    '''
    
      def __init__(self, parent, *args, **kw):
        '''
        Constructor
        '''
    
        Frame.__init__(self, parent, *args, **kw)
    
        # create a vertical scrollbar
        vscrollbar = Scrollbar(self, orient = VERTICAL)
        vscrollbar.pack(fill = Y, side = RIGHT, expand = FALSE)
    
        # create a horizontal scrollbar
        hscrollbar = Scrollbar(self, orient = HORIZONTAL)
        hscrollbar.pack(fill = X, side = BOTTOM, expand = FALSE)
    
        #Create a canvas object and associate the scrollbars with it
        canvas = Canvas(self, bd = 0, highlightthickness = 0, yscrollcommand = vscrollbar.set, xscrollcommand = hscrollbar.set)
        canvas.pack(side = LEFT, fill = BOTH, expand = TRUE)
    
        #Associate scrollbars with canvas view
        vscrollbar.config(command = canvas.yview)
        hscrollbar.config(command = canvas.xview)
    
    
        # set the view to 0,0 at initialization
    
        canvas.xview_moveto(0)
        canvas.yview_moveto(0)
    
        # create an interior frame to be created inside the canvas
    
        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)
    

    您可以使用它来获得您想要的结果。此帧同时启用水平和垂直滚动,并且可以使用“side”字段设置滚动条位置。 对于您问题的第二部分,您能否进一步说明。

    参考:奇闻趣事的回答 Python Tkinter scrollbar for frame

    【讨论】:

    • 你有一个简单的例子,比如这个类 ScrollableFrame 与内部的画布一起使用?我真的不明白如何在我的示例中集成这个类。我明白了,很多代码都和滚动条一样,我明白了,可调整大小滚动条的技巧似乎是 _configure_interior 函数和 create_window 函数,但我真的不明白为什么。
    • 这个例子制作了一个带有水平和垂直滚动条的画布,但据我所知,没有滑块出现,即使框架的大小超过了它所在的画布的大小。 pastebin.com/J7d0ChzX
    【解决方案3】:

    这很好用,用最小的可滚动画布大小得到我想要的。但是还是有bug,当gui变大的时候,看起来不能滚动的时候,有可能点击滚动条的左箭头或上箭头来滚动画布,这是怎么回事不可能。

    from Tkinter import *
    
    
    class ScrollableFrame(Frame):
        def __init__(self, parent, minimal_canvas_size, *args, **kw):
            '''
            Constructor
            '''
    
            Frame.__init__(self, parent, *args, **kw)
    
            self.minimal_canvas_size = minimal_canvas_size
    
            # create a vertical scrollbar
            vscrollbar = Scrollbar(self, orient = VERTICAL)
            vscrollbar.pack(fill = Y, side = RIGHT, expand = FALSE)
    
            # create a horizontal scrollbar
            hscrollbar = Scrollbar(self, orient = HORIZONTAL)
            hscrollbar.pack(fill = X, side = BOTTOM, expand = FALSE)
    
            #Create a canvas object and associate the scrollbars with it
            self.canvas = Canvas(self, bd = 0, highlightthickness = 0, yscrollcommand = vscrollbar.set, xscrollcommand = hscrollbar.set)
            self.canvas.pack(side = LEFT, fill = BOTH, expand = TRUE)
    
            #Associate scrollbars with canvas view
            vscrollbar.config(command = self.canvas.yview)
            hscrollbar.config(command = self.canvas.xview)
    
    
            # set the view to 0,0 at initialization
    
            self.canvas.xview_moveto(0)
            self.canvas.yview_moveto(0)
    
            self.canvas.config(scrollregion='0 0 %s %s' % self.minimal_canvas_size)
    
            # create an interior frame to be created inside the canvas
    
            self.interior = interior = Frame(self.canvas)
            interior_id = self.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 = (max(interior.winfo_reqwidth(), self.minimal_canvas_size[0]), max(interior.winfo_reqheight(), self.minimal_canvas_size[1]))
                self.canvas.config(scrollregion='0 0 %s %s' % size)
                if interior.winfo_reqwidth() != self.canvas.winfo_width():
                    # update the canvas's width to fit the inner frame
                    self.canvas.config(width = interior.winfo_reqwidth())
            interior.bind('<Configure>', _configure_interior)
    
    
    class my_gui(Frame):
    
        def __init__(self):
            # main tk object
            self.root = Tk()
    
            # init Frame
            Frame.__init__(self, self.root)
    
            minimal_canvas_size = (500, 500)
    
            # create frame (gray window)
    
            self.frame = ScrollableFrame(self.root, minimal_canvas_size)
            self.frame.pack(fill=BOTH, expand=YES)
    
            self.__add_plot()
    
        def __add_plot(self):
            # create a rectangle
            self.frame.canvas.create_polygon(10, 10, 10, 150, 200, 150, 200, 10,  fill="gray", outline="black")
    
        def mainLoop(self):
            # This function starts an endlos running thread through the gui
            self.root.mainloop()
    
        def __quit(self):
            # close everything
            self.root.quit()
    
    
    # init gui
    my_gui = my_gui()
    # execute gui
    my_gui.mainLoop()
    

    【讨论】:

      猜你喜欢
      • 2021-12-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-12
      • 2014-05-15
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      相关资源
      最近更新 更多