为Tkinter中DiscardFrame添加垂直滚动功能的实现方案
I'm developing a Canasta game GUI using Tkinter with the following requirements:
- When a card button in
MeldFrameis clicked, the card should be added toDiscardFrame. - When there are too many cards in
DiscardFrame, a scrollbar should allow viewing all content.
I tried implementing this with canvas2 and scrollBar2, but the scrollbar isn't working as expected. Here's a simplified version of my code:
import tkinter as tk from tkinter import ttk class CanastaGUI(tk.Tk): def __init__(self): super().__init__() self.title("Canasta Game") # Meld Frame self.meld_frame = ttk.LabelFrame(self, text="Meld") self.meld_frame.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.Y) # Sample cards in MeldFrame for i in range(10): card_btn = ttk.Button(self.meld_frame, text=f"Card {i}", command=lambda idx=i: self.add_to_discard(idx)) card_btn.pack(pady=2) # Discard Frame with attempted scrollbar self.discard_container = ttk.LabelFrame(self, text="Discard Pile") self.discard_container.pack(side=tk.RIGHT, padx=10, pady=10, fill=tk.BOTH, expand=True) self.scrollBar2 = ttk.Scrollbar(self.discard_container, orient=tk.VERTICAL) self.scrollBar2.pack(side=tk.RIGHT, fill=tk.Y) self.canvas2 = tk.Canvas(self.discard_container, yscrollcommand=self.scrollBar2.set) self.canvas2.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.discard_frame = ttk.Frame(self.canvas2) self.canvas2.create_window((0,0), window=self.discard_frame, anchor=tk.NW) self.scrollBar2.config(command=self.canvas2.yview) def add_to_discard(self, card_idx): card_btn = ttk.Button(self.discard_frame, text=f"Card {card_idx}") card_btn.pack(pady=2) # Missing scroll region update here if __name__ == "__main__": app = CanastaGUI() app.mainloop()
The issue is that when I add more cards than fit in the DiscardFrame, the scrollbar doesn't activate, and I can't scroll to see the hidden cards.
Solution
The core problem is that the canvas doesn't update its scroll region when the inner discard_frame grows as new cards are added. Here's the fixed code with key improvements:
import tkinter as tk from tkinter import ttk class CanastaGUI(tk.Tk): def __init__(self): super().__init__() self.title("Canasta Game") # Meld Frame self.meld_frame = ttk.LabelFrame(self, text="Meld") self.meld_frame.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.Y) # Sample cards in MeldFrame for i in range(20): card_btn = ttk.Button(self.meld_frame, text=f"Card {i}", command=lambda idx=i: self.add_to_discard(idx)) card_btn.pack(pady=2) # Discard Frame with working scrollbar self.discard_container = ttk.LabelFrame(self, text="Discard Pile") self.discard_container.pack(side=tk.RIGHT, padx=10, pady=10, fill=tk.BOTH, expand=True) self.scrollBar2 = ttk.Scrollbar(self.discard_container, orient=tk.VERTICAL) self.scrollBar2.pack(side=tk.RIGHT, fill=tk.Y) self.canvas2 = tk.Canvas(self.discard_container, yscrollcommand=self.scrollBar2.set) self.canvas2.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.discard_frame = ttk.Frame(self.canvas2) self.canvas2.create_window((0,0), window=self.discard_frame, anchor=tk.NW) # Bind frame size changes to update canvas scroll region self.discard_frame.bind("<Configure>", self.update_scroll_region) self.scrollBar2.config(command=self.canvas2.yview) def add_to_discard(self, card_idx): card_btn = ttk.Button(self.discard_frame, text=f"Card {card_idx}") card_btn.pack(pady=2) def update_scroll_region(self, event): # Update canvas to recognize full extent of inner frame content self.canvas2.configure(scrollregion=self.canvas2.bbox("all")) if __name__ == "__main__": app = CanastaGUI() app.mainloop()
Key Fixes:
- Bind Frame Size Changes: Added a
<Configure>event binding to the innerdiscard_framethat triggers whenever the frame's size updates (e.g., when new cards are added). - Update Scroll Region: The
update_scroll_regionmethod sets the canvas'sscrollregionto match the full bounding box of all content inside the canvas, making the scrollbar aware of all cards in the discard pile. - Proper Container Resizing: Ensured the
discard_containerusesfill=tk.BOTHandexpand=Trueto resize with the window, which keeps the scrollbar functional as the window changes size.
Now, when you add more cards than fit in the discard pile, the scrollbar will activate automatically, allowing you to scroll through all cards.
内容的提问来源于stack exchange,提问作者نظامی گنجوی

