可调整大小的可滚动 canvas 与 tkinter

resizeable scrollable canvas with tkinter

这是我的一个非常简单的 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 的末端并且我调整 canvas.

的大小

2) 如果我调整 GUI 和 canvas 的大小,则 canvas 中的矩形也应调整大小(例如,如果 gui 和 canvas 的新大小为四乘以旧尺寸,则矩形的新尺寸是旧尺寸的两倍)。

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

感谢您的帮助。

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

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

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

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

您可以使用以下方式将我的框架集成到您的图形用户界面中class:

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

这基本上应该可以解决您的第一个问题。至于第二个问题,您需要创建一个函数来在每次调整 canvas 大小时重新呈现它。以类似于 _configure_interior 函数的方式。

这非常有效,以最小的可滚动 canvas 大小获得我想要的内容。但是仍然存在错误,当 gui 变大并且看起来如此时,无法滚动,可以单击滚动条的左箭头或上箭头,然后滚动 canvas, 什么是不可能的。

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