未显示所有复选框的Python Tkinter滚动条和框架
问题描述
有一些tkinter代码的问题,我相信我只是太接近它了,看不到问题摆在我面前。我正在将复选框加载到框架中,并将滚动条附加到该位置。
在我看到1000多个复选框之前,这个方法运行得很好。然后它似乎被切断了,即使框架延伸了一个适合所有复选框的高度,它也不会在gui中显示它们。您可以在此处的图像中看到它们停止显示Checkbox Malfunction
以下是我的代码:(请原谅它看起来有多乱,它是一个大得多的代码集的子集,我刚刚隔离了错误)
from tkinter import *
build_vars = {}
build_Radios = []
parent = Tk()
center_container = Frame(parent, width=5, height=5)
center_container.grid(row=1, sticky="nsew")
# Center Row Columns
center_center_container = Frame(center_container, width=150, height=200)
center_center_container.grid(row=0, column=2, sticky="ns")
build_canvas = Canvas(center_center_container, background='green')
build_canvas.grid(row=0, column=0, sticky=N+E+W+S)
# Create a vertical scrollbar linked to the canvas.
vsbar = Scrollbar(center_center_container, orient=VERTICAL, command=build_canvas.yview)
vsbar.grid(row=0, column=1, sticky=NS)
build_canvas.configure(yscrollcommand=vsbar.set)
# Create a frame on the canvas to contain the buttons.
frame_buttons = Frame(build_canvas, bd=2, background='red')
def create_build_radios():
# for index, item in enumerate(filtered_builds):
for index, item in enumerate(list(range(3000))):
build_vars[item] = IntVar()
radio = Checkbutton(frame_buttons, text=item, variable=build_vars[item], onvalue=1,
offvalue=0,
command=lambda item=item: sel(item))
radio.grid(row=index, column=0, sticky=W)
build_Radios.append(radio)
# Create canvas window to hold the buttons_frame.
build_canvas.create_window((0, 0), window=frame_buttons, anchor=NW)
build_canvas.update_idletasks() # Needed to make bbox info available.
bbox = build_canvas.bbox(ALL) # Get bounding box of canvas with Buttons.
build_canvas.configure(scrollregion=bbox, width=150, height=400)
def sel(item):
print(item)
create_build_radios()
parent.mainloop()
解决方案
所以这是一个更好的解决方案(比其他解决方案好得多,可以很容易地在上面放置更多的小部件,但请注意,可能有某种限制(至少是CPU的能力):
from tkinter import Tk, Canvas, Frame, Label, Scrollbar, Button, DoubleVar, StringVar, Entry
from tkinter.ttk import Progressbar
class PagedScrollFrame(Frame):
def __init__(self, master, items_per_page=100, **kwargs):
Frame.__init__(self, master, **kwargs)
self.master = master
self.items_per_page = items_per_page
self.pages = None
self.id_list = []
self.bbox_tag = 'all'
self._loading_frame = Frame(self)
self.__load_progress_tracker = DoubleVar(master=self.master, value=0.0)
self.__percent_tracker = StringVar(master=self.master, value='0.00%')
self.frame = Frame(self)
self.frame.pack(side='top', padx=20, pady=20)
self.canvas = Canvas(self.frame)
self.canvas.pack(side='left')
self.bg_label = Label(self.canvas)
self.bg_label.place(x=0, y=0, relwidth=1, relheight=1)
self.scrollbar = Scrollbar(self.frame, orient='vertical', command=self.canvas.yview)
self.scrollbar.pack(side='right', fill='y')
self.canvas.config(yscrollcommand=self.scrollbar.set)
self.canvas.bind('<Configure>',
lambda e: self.canvas.config(
scrollregion=self.canvas.bbox(self.bbox_tag)
))
self.button_frame = Frame(self)
self.button_frame.pack(fill='x', side='bottom', padx=20, pady=20)
self.canvas_frame = Frame(self.button_frame)
self.button_canvas = Canvas(self.canvas_frame, height=20)
self.button_canvas.pack(expand=True)
self.inner_frame = Frame(self.button_canvas)
self.button_canvas.create_window(0, 0, window=self.inner_frame, anchor='nw')
self.button_scrollbar = Scrollbar(self.canvas_frame,
orient='horizontal',
command=self.button_canvas.xview)
self.button_scrollbar.pack(fill='x')
self.button_canvas.config(xscrollcommand=self.button_scrollbar.set)
self.button_canvas.bind(
'<Configure>', lambda e: self.button_canvas.config(
scrollregion=self.button_canvas.bbox('all')
)
)
def pack_items(self):
if not self.pages:
return
self._loading_frame.place(x=0, y=0, relwidth=1, relheight=1)
self._loading_frame.lift()
self._loading_frame.update_idletasks()
self.after(100, self._pack_items)
def _pack_items(self):
Label(self._loading_frame, text='Loading...').pack(expand=True)
Progressbar(self._loading_frame,
orient='horizontal',
variable=self.__load_progress_tracker,
length=self._loading_frame.winfo_width()
- self._loading_frame.winfo_width() // 10).pack(expand=True)
Label(self._loading_frame, textvariable=self.__percent_tracker).pack(expand=True)
self.update_idletasks()
widgets = [widget for page in self.pages for widget in page.winfo_children()]
length = len(widgets)
self.after(100, self.__pack_items, widgets, 0, length)
def __pack_items(self, widgets, index, length):
if index >= length:
self._loading_frame.destroy()
self.canvas.config(scrollregion=self.canvas.bbox('all'))
return
widgets[index].pack()
percent = (index + 1) * 100 / length
self.__load_progress_tracker.set(value=percent)
self.__percent_tracker.set(value=f'{percent: .2f}%')
self.after(1, self.__pack_items, widgets, index + 1, length)
def change_frame(self, index):
if not self.pages:
return
self.bbox_tag = self.id_list[index]
self.canvas.config(scrollregion=self.canvas.bbox(self.bbox_tag))
self.bg_label.lift()
self.pages[index].lift()
def create_pages(self, num_of_items, items_per_page=None):
self.pages = None
if not items_per_page:
items_per_page = self.items_per_page
num_of_pages = num_of_items // items_per_page
if num_of_items % items_per_page != 0:
num_of_pages += 1
start_indexes = [items_per_page * page_num for page_num in range(num_of_pages)]
end_indexes = [num + items_per_page for num in start_indexes]
end_indexes[-1] += (num_of_items % items_per_page
- (items_per_page if num_of_items % items_per_page != 0 else 0))
self.pages = [Frame(self.canvas) for _ in range(num_of_pages)]
self.id_list = []
for page, frame in enumerate(self.pages):
self.id_list.append(self.canvas.create_window(0, 0, window=frame, anchor='nw'))
self.pages[0].lift()
if num_of_pages >= 2:
Button(self.button_frame, text='1',
command=lambda: self.change_frame(0)).pack(
side='left', expand=True, fill='both', ipadx=5
)
if num_of_pages > 2:
self.canvas_frame.pack(fill='x', expand=True, side='left')
for page_num in range(1, num_of_pages - 1):
Button(self.inner_frame, text=page_num + 1,
command=lambda index=page_num: self.change_frame(index)).pack(
expand=True, fill='both', side='left', ipadx=5
)
Button(self.button_frame, text=num_of_pages,
command=lambda: self.change_frame(num_of_pages - 1)).pack(
side='right', fill='both', expand=True, ipadx=5
)
return zip(start_indexes, end_indexes, self.pages)
def create_paged_canvas():
scroll = PagedScrollFrame(root)
scroll.pack()
lst = tuple(range(3000))
for start, end, parent in scroll.create_pages(len(lst)):
for i in lst[start:end]:
frame_ = Frame(parent)
Label(frame_, text=str(i).zfill(4)).pack(side='left')
scroll.pack_items()
root = Tk()
root.protocol('WM_DELETE_WINDOW', exit)
create_paged_canvas()
root.mainloop()
主要信息:
基本上,这会创建分页的可滚动画布。所有需要做的就是调整create_paged_canvas()
函数中的内循环和迭代值。您还可以调整每页显示的项目数(这也允许稍后进行配置,例如,在菜单中,您可以调用类似于create_paged_canvas()
函数并将items_per_page
参数更改为其他内容(将不得不再次加载所有内容,但...tkinter
是tkinter
,它相当慢,更糟糕的是,它不允许直接使用线程,甚至不允许谈论进程(这样做会大大加快速度,但根本无法完成)
重要信息(建议):
我强烈建议不要使用通配符(*
)在导入某些内容时,您应该导入您需要的内容,例如from module import Class1, func_1, var_2
等等,或者导入整个模块:import module
然后您也可以使用别名:import module as md
或类似的东西,关键是,除非您真正知道自己在做什么,否则不要导入所有内容;名称冲突是问题所在。
其他:
为了获得更好的性能,最好不是直接在画布上创建文本(使用另一种解决方案)或使用列表框创建标签,这是在您需要显示大量数据的情况下,因为无需创建小部件(这也意味着您只能查看几乎所有的数据),所以这样会加快速度。
如果您有任何问题,请务必提出来!
相关文章