[英]python tkinter canvas not resizing
我正在嘗試對一些 tkinter 控件進行子類化以制作一個自動滾動框架以供在我的程序中使用,我只希望它垂直滾動,並且當添加需要它水平滾動而不是調整父級大小以適合的小部件時。 這個框架中的 go 的 widgets 並不是都是在運行時定義的,是動態創建的,所以它必須對事件做出反應。 我有一些部分工作的代碼,因為我運行它,我可以使用底部的按鈕動態添加和刪除標簽,如果我這樣做而不先手動調整 window 的大小,那么它會按預期工作。 但是,如果我手動調整 window 的大小,水平調整將停止工作:
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 但沒有變化。
為什么調整 window 會停止調用:
self.canvas.config(width=self.interior.winfo_reqwidth())
從工作?
編輯:布賴恩奧克利的回應是有道理的,所以我可以理解這種行為,但是我現在已經更改了我的代碼以在調整 window 大小時也處理對 canvas 大小的更改:
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()
我現在看到的問題是,如果我在調整 window 大小之前垂直添加項目,則會為添加的每個小部件調用_configure_interior
function,並且 window 要么垂直調整大小以適應(如果在 window 大小調整之前),它應該更新scrollregion(和滾動條)如果沒有,但是在調整 window 的大小后,隨着每個小部件的添加, _configure_interior
停止被調用,如果我手動調用它,滾動條仍然沒有更新?
調整窗口大小的原因為何會阻止對以下內容的調用:
self.canvas.config(寬度= self.interior.winfo_reqwidth())
那是因為您的代碼已綁定到框架更改。 當用戶調整窗口大小時,您調整窗口大小,而不是frame大小。 如果希望在調整窗口大小時調用該函數,則除了自動調整內部框架的大小外,還必須將其或類似的函數綁定到調整窗口或畫布的大小。
您還需要注意,如果用戶調整窗口大小,則在畫布中向框架添加對象不會使窗口變大。 用戶選擇的窗口大小將覆蓋正在調整大小的內部窗口小部件的計算大小。 您必須將幾何圖形重置為空字符串,以使其自動增長。
在所有 tkinter 個“可滾動”框架示例中,這是唯一一個實際使用動態添加的小部件的示例。 關鍵是更新 canvas 和子窗口的框架高度以響應子框架的請求高度。
框架"<Configure>"
處理程序中的以下代碼段
self.canvas.config(height=self.interior.winfo_reqheight())
self.canvas.itemconfig(self.interior_id, height=self.interior.winfo_reqheight())
修復 canvas 大小,以便內部框架將填充它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.