如何在 TKinter 中创建可滚动的单选按钮列表
How to create a scrollable radiobutton list in TKinter
我想在 tkinter 中创建一个可滚动的单选按钮列表,但我没有让它工作。我已经尝试将我的单选按钮列表嵌入各种 tkinter 容器中,但我得到了相同的结果,即滚动条不起作用并且单选按钮列表没有从容器中清除(例如,所以我可以用不同的替换它单选按钮列表)。我已经搜索了如何执行此操作,但找不到任何内容。这可能吗?
这是我的示例应用程序:
import tkinter as tk
from tkinter import ttk
class CodeSampleForWhosebug:
def __init__(self, window):
self.main_window = window
self.mainframe = ttk.Frame(self.main_window, padding = '15 3 12 12')
self.mainframe.grid(column = 0, row = 0, sticky = "W, E, N, S")
self.file_choice = tk.StringVar()
self.contents_list = list()
self.display_folder_btn = ttk.Button(self.mainframe, text = "Display list of choices", width = 20)
self.display_folder_btn.grid(row = 1, column = 0, columnspan = 2)
self.display_folder_btn.bind("<Button-1>", self.list_folder_contents)
self.folder_contents_canvas = tk.Canvas(self.mainframe)
self.scroll_y = tk.Scrollbar(self.folder_contents_canvas, orient="vertical")
self.scroll_y.pack(fill = 'y', side = 'right')
self.folder_contents_canvas.grid(row=2, column = 0, columnspan = 2)
self.folder_contents_frame = tk.Text(self.folder_contents_canvas, height = 7, width = 50, yscrollcommand = self.scroll_y.set)
self.folder_contents_frame.pack(side = "top", fill = "x", expand = False, padx = 20, pady = 20)
def list_folder_contents(self, event):
try:
self.contents_list = ['A dictum nulla auctor id.', 'A porttitor diam iaculis quis.', 'Consectetur adipiscing elit.', \
'Curabitur in ante iaculis', 'Finibus tincidunt nunc.', 'Fusce elit ligula', \
'Id sollicitudin arcu semper sit amet.', 'Integer at sapien leo.', 'Lorem ipsum dolor sit amet', \
'Luctus ligula suscipit', 'Nam vitae erat a dolor convallis', \
'Praesent feugiat quam ac', 'Pretium diam.', 'Quisque accumsan vehicula dolor', \
'Quisque eget arcu odio.', 'Sed ac elit id dui blandit dictum', 'Sed et eleifend leo.', \
'Sed vestibulum fermentum augue', 'Suspendisse pharetra cursus lectus', 'Ultricies eget erat et', \
'Vivamus id lorem mi.']
contents_dict = dict()
self.folder_contents_frame.delete(1.0, 'end')
counter = 0
for i in self.contents_list:
contents_dict[str(counter+1)] = i
counter+=1
for (text, value) in contents_dict.items():
#self.folder_contents_frame.insert(1.0, text+"\t"+value+"\n")
ttk.Radiobutton(self.folder_contents_frame, text = value, variable = self.file_choice, value = text, style = "TRadiobutton").grid(column = 0, columnspan = 2, sticky = tk.W)
self.scroll_y.config(command = self.folder_contents_frame.yview)
except Exception as exc:
print(exc)
#-----------------------------------------
def main():
root = tk.Tk()
root.title('Scrollable radiobutton list')
root.geometry("500x600")
tabs = ttk.Notebook(root)
tabs.pack(fill = "both")
scrollable_radiobutton_list_frame = ttk.Frame(tabs)
tabs.add(scrollable_radiobutton_list_frame, text = "Scrollable radiobutton list")
my_checker = CodeSampleForWhosebug(window = scrollable_radiobutton_list_frame)
root.mainloop()
if __name__ == '__main__':
main()
如果您在 list_folder_contents 方法中注释掉创建单选按钮的行并取消注释其上方的行,您可以看到我希望它如何运行,但使用单选按钮而不是纯文本。
编辑:附带问题:如果有人知道为什么(在文本版本中)以相反的顺序添加项目,我也很感激那里的指导。
感谢您提供的任何帮助!
解决这个问题的一种方法是创建一个可滚动的框架 class 并使用它。我有一个可滚动的框架 class 取自 SO(我不记得确切的答案)并根据我的需要进行了修改,这应该适用于您的应用程序。
import tkinter as tk
from tkinter import ttk
class ScrollFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent) # create a frame (self)
self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") # Canvas to scroll
self.viewPort = tk.Frame(self.canvas, background="#ffffff") # This frame will hold the child widgets
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set) # Attach scrollbar action to scroll of canvas
self.vsb.pack(side="right", fill="y") # Pack scrollbar to right - change as needed
self.canvas.pack(side="left", fill="both", expand=True) # Pack canvas to left and expand to fill - change as needed
self.canvas_window = self.canvas.create_window(
(0,0),
window=self.viewPort,
anchor="nw",
tags="self.viewPort",
) # Add view port frame to canvas
self.viewPort.bind("<Configure>", self.onFrameConfigure)
self.canvas.bind("<Configure>", self.onCanvasConfigure)
self.first = True
self.onFrameConfigure(None) # Initial stretch on render
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def onCanvasConfigure(self, event):
'''Reset the canvas window to encompass inner frame when required'''
canvas_width = event.width
self.canvas.itemconfig(self.canvas_window, width = canvas_width)
def on_mousewheel(self, event):
'''Allows the mousewheel to control the scrollbar'''
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
def bnd_mousewheel(self):
'''Binds the mousewheel to the scrollbar'''
self.canvas.bind_all("<MouseWheel>", self.on_mousewheel)
def unbnd_mousewheel(self):
'''Unbinds the mousewheel from the scrollbar'''
self.canvas.unbind_all("<MouseWheel>")
def delete_all(self):
'''Removes all widgets from the viewPort, only works if grid was used'''
children = self.viewPort.winfo_children()
for child in children:
child.grid_remove()
class CodeSampleForWhosebug:
def __init__(self, window):
self.main_window = window
self.mainframe = ttk.Frame(self.main_window, padding = '15 3 12 12')
self.mainframe.grid(column = 0, row = 0, sticky = "W, E, N, S")
self.file_choice = tk.StringVar()
self.contents_list = list()
self.display_folder_btn = ttk.Button(self.mainframe, text = "Display list of choices", width = 20, command=self.list_folder_contents)
self.display_folder_btn.pack(side="top")
self.folder_contents_frame = ScrollFrame(self.mainframe)
self.folder_contents_frame.pack(side = "top", fill = "x", expand = False, padx = 20, pady = 20)
def list_folder_contents(self):
try:
self.contents_list = ['A dictum nulla auctor id.', 'A porttitor diam iaculis quis.', 'Consectetur adipiscing elit.', \
'Curabitur in ante iaculis', 'Finibus tincidunt nunc.', 'Fusce elit ligula', \
'Id sollicitudin arcu semper sit amet.', 'Integer at sapien leo.', 'Lorem ipsum dolor sit amet', \
'Luctus ligula suscipit', 'Nam vitae erat a dolor convallis', \
'Praesent feugiat quam ac', 'Pretium diam.', 'Quisque accumsan vehicula dolor', \
'Quisque eget arcu odio.', 'Sed ac elit id dui blandit dictum', 'Sed et eleifend leo.', \
'Sed vestibulum fermentum augue', 'Suspendisse pharetra cursus lectus', 'Ultricies eget erat et', \
'Vivamus id lorem mi.']
contents_dict = dict()
self.folder_contents_frame.delete_all()
counter = 0
for i in self.contents_list:
contents_dict[str(counter+1)] = i
counter+=1
for (text, value) in contents_dict.items():
ttk.Radiobutton(self.folder_contents_frame.viewPort, text = value, variable = self.file_choice, value = text, style = "TRadiobutton").grid(column = 0, columnspan = 2, sticky = tk.W)
except Exception as exc:
print(exc)
#-----------------------------------------
def main():
root = tk.Tk()
root.title('Scrollable radiobutton list')
root.geometry("500x600")
tabs = ttk.Notebook(root)
tabs.pack(fill = "both")
scrollable_radiobutton_list_frame = ttk.Frame(tabs)
tabs.add(scrollable_radiobutton_list_frame, text = "Scrollable radiobutton list")
my_checker = CodeSampleForWhosebug(window = scrollable_radiobutton_list_frame)
root.mainloop()
if __name__ == '__main__':
main()
我想在 tkinter 中创建一个可滚动的单选按钮列表,但我没有让它工作。我已经尝试将我的单选按钮列表嵌入各种 tkinter 容器中,但我得到了相同的结果,即滚动条不起作用并且单选按钮列表没有从容器中清除(例如,所以我可以用不同的替换它单选按钮列表)。我已经搜索了如何执行此操作,但找不到任何内容。这可能吗?
这是我的示例应用程序:
import tkinter as tk
from tkinter import ttk
class CodeSampleForWhosebug:
def __init__(self, window):
self.main_window = window
self.mainframe = ttk.Frame(self.main_window, padding = '15 3 12 12')
self.mainframe.grid(column = 0, row = 0, sticky = "W, E, N, S")
self.file_choice = tk.StringVar()
self.contents_list = list()
self.display_folder_btn = ttk.Button(self.mainframe, text = "Display list of choices", width = 20)
self.display_folder_btn.grid(row = 1, column = 0, columnspan = 2)
self.display_folder_btn.bind("<Button-1>", self.list_folder_contents)
self.folder_contents_canvas = tk.Canvas(self.mainframe)
self.scroll_y = tk.Scrollbar(self.folder_contents_canvas, orient="vertical")
self.scroll_y.pack(fill = 'y', side = 'right')
self.folder_contents_canvas.grid(row=2, column = 0, columnspan = 2)
self.folder_contents_frame = tk.Text(self.folder_contents_canvas, height = 7, width = 50, yscrollcommand = self.scroll_y.set)
self.folder_contents_frame.pack(side = "top", fill = "x", expand = False, padx = 20, pady = 20)
def list_folder_contents(self, event):
try:
self.contents_list = ['A dictum nulla auctor id.', 'A porttitor diam iaculis quis.', 'Consectetur adipiscing elit.', \
'Curabitur in ante iaculis', 'Finibus tincidunt nunc.', 'Fusce elit ligula', \
'Id sollicitudin arcu semper sit amet.', 'Integer at sapien leo.', 'Lorem ipsum dolor sit amet', \
'Luctus ligula suscipit', 'Nam vitae erat a dolor convallis', \
'Praesent feugiat quam ac', 'Pretium diam.', 'Quisque accumsan vehicula dolor', \
'Quisque eget arcu odio.', 'Sed ac elit id dui blandit dictum', 'Sed et eleifend leo.', \
'Sed vestibulum fermentum augue', 'Suspendisse pharetra cursus lectus', 'Ultricies eget erat et', \
'Vivamus id lorem mi.']
contents_dict = dict()
self.folder_contents_frame.delete(1.0, 'end')
counter = 0
for i in self.contents_list:
contents_dict[str(counter+1)] = i
counter+=1
for (text, value) in contents_dict.items():
#self.folder_contents_frame.insert(1.0, text+"\t"+value+"\n")
ttk.Radiobutton(self.folder_contents_frame, text = value, variable = self.file_choice, value = text, style = "TRadiobutton").grid(column = 0, columnspan = 2, sticky = tk.W)
self.scroll_y.config(command = self.folder_contents_frame.yview)
except Exception as exc:
print(exc)
#-----------------------------------------
def main():
root = tk.Tk()
root.title('Scrollable radiobutton list')
root.geometry("500x600")
tabs = ttk.Notebook(root)
tabs.pack(fill = "both")
scrollable_radiobutton_list_frame = ttk.Frame(tabs)
tabs.add(scrollable_radiobutton_list_frame, text = "Scrollable radiobutton list")
my_checker = CodeSampleForWhosebug(window = scrollable_radiobutton_list_frame)
root.mainloop()
if __name__ == '__main__':
main()
如果您在 list_folder_contents 方法中注释掉创建单选按钮的行并取消注释其上方的行,您可以看到我希望它如何运行,但使用单选按钮而不是纯文本。
编辑:附带问题:如果有人知道为什么(在文本版本中)以相反的顺序添加项目,我也很感激那里的指导。
感谢您提供的任何帮助!
解决这个问题的一种方法是创建一个可滚动的框架 class 并使用它。我有一个可滚动的框架 class 取自 SO(我不记得确切的答案)并根据我的需要进行了修改,这应该适用于您的应用程序。
import tkinter as tk
from tkinter import ttk
class ScrollFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent) # create a frame (self)
self.canvas = tk.Canvas(self, borderwidth=0, background="#ffffff") # Canvas to scroll
self.viewPort = tk.Frame(self.canvas, background="#ffffff") # This frame will hold the child widgets
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set) # Attach scrollbar action to scroll of canvas
self.vsb.pack(side="right", fill="y") # Pack scrollbar to right - change as needed
self.canvas.pack(side="left", fill="both", expand=True) # Pack canvas to left and expand to fill - change as needed
self.canvas_window = self.canvas.create_window(
(0,0),
window=self.viewPort,
anchor="nw",
tags="self.viewPort",
) # Add view port frame to canvas
self.viewPort.bind("<Configure>", self.onFrameConfigure)
self.canvas.bind("<Configure>", self.onCanvasConfigure)
self.first = True
self.onFrameConfigure(None) # Initial stretch on render
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def onCanvasConfigure(self, event):
'''Reset the canvas window to encompass inner frame when required'''
canvas_width = event.width
self.canvas.itemconfig(self.canvas_window, width = canvas_width)
def on_mousewheel(self, event):
'''Allows the mousewheel to control the scrollbar'''
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
def bnd_mousewheel(self):
'''Binds the mousewheel to the scrollbar'''
self.canvas.bind_all("<MouseWheel>", self.on_mousewheel)
def unbnd_mousewheel(self):
'''Unbinds the mousewheel from the scrollbar'''
self.canvas.unbind_all("<MouseWheel>")
def delete_all(self):
'''Removes all widgets from the viewPort, only works if grid was used'''
children = self.viewPort.winfo_children()
for child in children:
child.grid_remove()
class CodeSampleForWhosebug:
def __init__(self, window):
self.main_window = window
self.mainframe = ttk.Frame(self.main_window, padding = '15 3 12 12')
self.mainframe.grid(column = 0, row = 0, sticky = "W, E, N, S")
self.file_choice = tk.StringVar()
self.contents_list = list()
self.display_folder_btn = ttk.Button(self.mainframe, text = "Display list of choices", width = 20, command=self.list_folder_contents)
self.display_folder_btn.pack(side="top")
self.folder_contents_frame = ScrollFrame(self.mainframe)
self.folder_contents_frame.pack(side = "top", fill = "x", expand = False, padx = 20, pady = 20)
def list_folder_contents(self):
try:
self.contents_list = ['A dictum nulla auctor id.', 'A porttitor diam iaculis quis.', 'Consectetur adipiscing elit.', \
'Curabitur in ante iaculis', 'Finibus tincidunt nunc.', 'Fusce elit ligula', \
'Id sollicitudin arcu semper sit amet.', 'Integer at sapien leo.', 'Lorem ipsum dolor sit amet', \
'Luctus ligula suscipit', 'Nam vitae erat a dolor convallis', \
'Praesent feugiat quam ac', 'Pretium diam.', 'Quisque accumsan vehicula dolor', \
'Quisque eget arcu odio.', 'Sed ac elit id dui blandit dictum', 'Sed et eleifend leo.', \
'Sed vestibulum fermentum augue', 'Suspendisse pharetra cursus lectus', 'Ultricies eget erat et', \
'Vivamus id lorem mi.']
contents_dict = dict()
self.folder_contents_frame.delete_all()
counter = 0
for i in self.contents_list:
contents_dict[str(counter+1)] = i
counter+=1
for (text, value) in contents_dict.items():
ttk.Radiobutton(self.folder_contents_frame.viewPort, text = value, variable = self.file_choice, value = text, style = "TRadiobutton").grid(column = 0, columnspan = 2, sticky = tk.W)
except Exception as exc:
print(exc)
#-----------------------------------------
def main():
root = tk.Tk()
root.title('Scrollable radiobutton list')
root.geometry("500x600")
tabs = ttk.Notebook(root)
tabs.pack(fill = "both")
scrollable_radiobutton_list_frame = ttk.Frame(tabs)
tabs.add(scrollable_radiobutton_list_frame, text = "Scrollable radiobutton list")
my_checker = CodeSampleForWhosebug(window = scrollable_radiobutton_list_frame)
root.mainloop()
if __name__ == '__main__':
main()