python tkinter画布不调整大小 [英] python tkinter canvas not resizing

查看:42
本文介绍了python tkinter画布不调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将一些 tkinter 控件子类化以制作一个自动滚动框架以在我的程序中使用,我只希望它垂直滚动,并且当添加需要它水平滚动而不是调整父级大小以适合的小部件时.进入这个框架的小部件并非都是在运行时定义的,而是动态创建的,因此它必须对事件做出反应.我有一些部分工作的代码,因为我运行它,我可以使用底部的按钮动态添加和删除标签,如果我这样做而不先手动调整窗口大小,那么它会按预期工作.但是,如果我手动调整窗口大小,则水平调整大小将停止工作:

I'm trying to subclass some tkinter controls to make an auto scrolling frame for use in my program, i only want it to scroll vertically, and when widgets are added that need it to scroll horizontally instead to resize the parent to fit. widgets that go in this frame are not all defined at runtime and are created dynamically, so it must react to events. I have some code that is partly working, in that it i run it, i can use the buttons at the bottom to add and remove labels on the fly, if i do this without manually resizing the window first then it works as expected. however if i manually resize the window the horizontal resizing stops working:

from tkinter import *

__all__ = ["VerticalScrolledFrame"]

class VerticalScrolledFrame(Frame):
    def __init__(self, parent, *args, **kw):
        Frame.__init__(self, parent, *args, **kw)            
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(1, weight=1)
        # create a canvas object and a vertical scrollbar for scrolling it
        self.vscrollbar = Scrollbar(self, orient=VERTICAL)
        self.vscrollbar.grid(column=2, row=1, sticky="nesw")
        self.canvas = Canvas(self, bd=0, highlightthickness=0, yscrollcommand=self.vscrollbar.set, height=10, width=10)
        self.canvas.grid(column=1, row=1, sticky="nesw")
        self.vscrollbar.config(command=self.canvas.yview)

        # reset the view
        self.canvas.xview_moveto(0)
        self.canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = Frame(self.canvas)
        self.interior_id = self.canvas.create_window(0, 0, window=self.interior, anchor=NW)

        self.interior.bind('<Configure>', self._configure_interior)

        # track changes to the canvas and frame width and sync them,
        # also updating the scrollbar
    def _configure_interior(self, event=None):
        print("config interior")
        # update the scrollbars to match the size of the inner frame
        size = (self.interior.winfo_width(), self.interior.winfo_height())
        self.canvas.config(scrollregion="0 0 %s %s" % size)
        if self.interior.winfo_reqwidth() > self.canvas.winfo_width():
            # update the canvas's width to fit the inner frame
            self.canvas.config(width=self.interior.winfo_reqwidth())

if __name__ == "__main__":
    labels = []
    labels2 = []
    def add1():
        l = Label(f.interior, text=str(len(labels)+1))
        l.grid(column=len(labels)+1, row=1)
        labels.append(l)

    def remove1():
        l = labels.pop()
        l.grid_forget()
        l.destroy()

    def add2():
        l = Label(f.interior, text=str(len(labels2)+1))
        l.grid(column=1, row=len(labels2)+1)
        labels2.append(l)

    def remove2():
        l = labels2.pop()
        l.grid_forget()
        l.destroy()

    app = Tk()
    app.grid_columnconfigure(1, weight=1)
    app.grid_columnconfigure(2, weight=1)
    app.grid_rowconfigure(1, weight=1)
    f = VerticalScrolledFrame(app)
    f.grid(column=1, row=1, columnspan=2, sticky="nesw")
    Button(app, text="Add-H", command=add1).grid(column=1, row=2, sticky="nesw")
    Button(app, text="Remove-H", command=remove1).grid(column=2, row=2, sticky="nesw")
    Button(app, text="Add-V", command=add2).grid(column=1, row=3, sticky="nesw")
    Button(app, text="Remove-V", command=remove2).grid(column=2, row=3, sticky="nesw")
    app.mainloop()

我只在 Windows 上工作并使用 python 3.3,我也尝试过 3.4,但没有改变.

I'm working solely on windows and using python 3.3, I tried 3.4 as well but no change.

为什么调整窗口大小会停止调用:

why does resizing the window stop the call to:

self.canvas.config(width=self.interior.winfo_reqwidth())

工作?

Brian Oakley 的回应是有道理的,所以我可以理解这种行为,但是我现在已经更改了我的代码,以便在调整窗口大小时也处理画布大小的更改:

Brian Oakley's response makes sense so I can understand that behavior, however i have now changed my code to also handle changes to the canvas size when the window is resized:

from tkinter import *

__all__ = ["VerticalScrolledFrame"]

class VerticalScrolledFrame(Frame):
    def __init__(self, parent, *args, **kw):
        Frame.__init__(self, parent, *args, **kw)
        p = parent
        while True:
            if p.winfo_class() in ['Tk', 'Toplevel']:
                break
            else:
                p = self._nametowidget(p.winfo_parent())
        self.root_window = p
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(1, weight=1)
        # create a canvas object and a vertical scrollbar for scrolling it
        self.vscrollbar = Scrollbar(self, orient=VERTICAL)
        self.vscrollbar.grid(column=2, row=1, sticky="nesw")
        self.canvas = Canvas(self, bd=0, highlightthickness=0, yscrollcommand=self.vscrollbar.set, height=10, width=10)
        self.canvas.grid(column=1, row=1, sticky="nesw")
        self.vscrollbar.config(command=self.canvas.yview)

        # reset the view
        self.canvas.xview_moveto(0)
        self.canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = Frame(self.canvas)
        self.interior_id = self.canvas.create_window(0, 0, window=self.interior, anchor=NW)

        self.interior.bind('<Configure>', self._configure_interior)

        self.canvas.bind('<Configure>', self._configure_canvas)

        # track changes to the canvas and frame width and sync them,
        # also updating the scrollbar
    def _configure_interior(self, event=None):
        print("config interior")
        # update the scrollbars to match the size of the inner frame
        size = (self.interior.winfo_width(), self.interior.winfo_height())
        self.canvas.config(scrollregion="0 0 %s %s" % size)
        if self.interior.winfo_reqwidth() >= self.canvas.winfo_width():
            # update the canvas's width to fit the inner frame
            # only works before mainloop
            self.canvas.config(width=self.interior.winfo_reqwidth())
        screen_h = self.winfo_screenheight()
        if ((self.root_window.winfo_rooty() + self.root_window.winfo_height() - self.canvas.winfo_height() + self.interior.winfo_reqheight()) < screen_h):
            self.canvas.configure(height=self.interior.winfo_reqheight())

    def _configure_canvas(self, event=None):
        print("config canvas")
        if self.interior.winfo_reqwidth() < self.canvas.winfo_width():
            self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width())
        elif self.interior.winfo_reqwidth() > self.canvas.winfo_width():
            self.canvas.config(width=self.interior.winfo_reqwidth())

        if (self.interior.winfo_reqheight() < self.canvas.winfo_height()) or (self.interior.winfo_height() < self.canvas.winfo_height()):
            self.canvas.itemconfigure(self.interior_id, height=self.canvas.winfo_height())

if __name__ == "__main__":
    labels = []
    labels2 = []
    def add1():
##        app.geometry("")
        l = Label(f.interior, text=str(len(labels)+1))
        l.grid(column=len(labels)+1, row=1)
        f.interior.grid_columnconfigure(len(labels)+1, weight=1, minsize=l.winfo_reqwidth())
        labels.append(l)

    def remove1():
##        app.geometry("")
        l = labels.pop()
        l.grid_forget()
        f.interior.grid_columnconfigure(len(labels)+2, weight=0, minsize=0)
        l.destroy()

    def add2():
        l = Label(f.interior, text=str(len(labels2)+1))
        l.grid(column=1, row=len(labels2)+1)
        f.interior.grid_rowconfigure(len(labels2)+1, weight=1, minsize=l.winfo_reqheight())
        labels2.append(l)

    def remove2():
        l = labels2.pop()
        l.grid_forget()
        f.interior.grid_rowconfigure(len(labels2)+2, weight=0, minsize=0)
        l.destroy()

    app = Tk()
    app.grid_columnconfigure(1, weight=1)
    app.grid_columnconfigure(2, weight=1)
    app.grid_rowconfigure(1, weight=1)
    f = VerticalScrolledFrame(app)
    f.grid(column=1, row=1, columnspan=2, sticky="nesw")
    Button(app, text="Add-H", command=add1).grid(column=1, row=2, sticky="nesw")
    Button(app, text="Remove-H", command=remove1).grid(column=2, row=2, sticky="nesw")
    Button(app, text="Add-V", command=add2).grid(column=1, row=3, sticky="nesw")
    Button(app, text="Remove-V", command=remove2).grid(column=2, row=3, sticky="nesw")
    for i in range(0,10):
        add1()
        add2()
    app.mainloop()

我现在看到的问题是,如果我在调整窗口大小之前垂直添加项目,则会为每个添加的小部件调用 _configure_interior 函数,并且窗口会垂直调整大小以适应(如果在窗口大小调整之前),如果没有,它应该更新滚动区域(和滚动条),但是在调整窗口大小后, _configure_interior 在添加每个小部件时停止调用,如果我手动调用滚动条还没更新?

the problem I am now seeing is that if I add items vertically before resizing the window, the _configure_interior function is called for each widget that gets added and the window is either resized vertically to fit (if before window sizing) and it is supposed to update the scrollregion (and scrollbar) if not, however after resizing the window the _configure_interior stops being called as each widget is added, and if i call it manually the scrollbar is still not updated?

推荐答案

为什么调整窗口大小会停止调用:

why does resizing the window stop the call to:

self.canvas.config(width=self.interior.winfo_reqwidth())

self.canvas.config(width=self.interior.winfo_reqwidth())

那是因为您的代码绑定到 frame 更改.当用户调整窗口大小时,您调整的是窗口,而不是框架.如果您希望在调整窗口大小时调用该函数,除了内部框架的自动调整大小之外,您还必须将它或类似的函数绑定到窗口或画布的调整大小.

That is because your code is bound to the frame changing. When the user resizes the window, you resize the window, not the frame. If you want the function to be called when the window is resized you must bind it or a similar function to the resizing of the window or canvas in addition to the automatic resizing of the interior frame.

您还需要注意,如果用户调整窗口大小,将对象添加到画布中的框架不会使窗口变大.用户对窗口大小的选择将覆盖正在调整大小的内部小部件的计算大小.您必须将几何图形重置为空字符串才能使其自动增长.

You'll also need to be aware that if the user resizes the window, adding objects to the frame in the canvas won't make the window bigger. The user's choice of window size will override the computed size from the interior widgets being resized. You'll have to reset the geometry to the empty string to get it to automatically grow.

这篇关于python tkinter画布不调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆