Tkinter子框架自动适配实现及引用子框架报错解决
Hey there! Let's work through your issues one by one to get your Tkinter GUI running smoothly, efficiently, and without those frustrating errors.
1. Fixing the AttributeError: 'Application' object has no attribute 'side_frame'
The root cause here is straightforward: in your create_frames method, you’re creating video_frame and side_frame as local variables instead of assigning them to your Application instance. That means when you try to reference self.side_frame in create_images, Python has no idea what you’re talking about.
Fix this by updating create_frames to store the frames as instance attributes, and switch to using grid layout for automatic resizing:
def create_frames(self): # Create frames as instance attributes so other methods can access them self.video_frame = tk.Frame(master=self, bg="blue") self.side_frame = tk.Frame(master=self, bg="red") # Configure the main application frame's grid to handle resizing self.grid_rowconfigure(0, weight=1) # Make row 0 expand with window height self.grid_columnconfigure(0, weight=3) # Video frame takes 75% of width self.grid_columnconfigure(1, weight=1) # Side frame takes 25% of width # Place frames with grid, using sticky to fill all available space self.video_frame.grid(row=0, column=0, sticky="nsew") self.side_frame.grid(row=0, column=1, sticky="nsew") # Optional: Configure side_frame's grid for its own components self.side_frame.grid_rowconfigure(0, weight=1) self.side_frame.grid_columnconfigure(0, weight=1)
I also changed the frame master to self (since your Application is a Frame itself) instead of directly using pc_gui—this keeps your code more encapsulated and easier to maintain.
2. Switching to Grid Layout for Side Frame Components
Now that self.side_frame exists, you can create your image labels directly inside it using grid. Instead of recreating labels every update cycle (which causes flicker), create them once during initialization, then only update their image content when needed.
Update your __init__ and add dedicated methods for creating and updating image labels:
def __init__(self, pc_gui): tk.Frame.__init__(self, pc_gui) self.pc_gui = pc_gui # Store reference to main window self.pc_gui.geometry("1280x720") # Make the main window expand with resize self.pc_gui.grid_rowconfigure(0, weight=1) self.pc_gui.grid_columnconfigure(0, weight=1) self.grid(sticky="nsew") # Make application frame fill main window self.create_frames() self.create_image_labels() # Create labels once self.pc_gui.bind("<Configure>", self.on_window_resize) # Handle resizing self.updater() def create_image_labels(self): # Initialize your image label in the side frame self.fp1_lbl = tk.Label(master=self.side_frame, bg="red") self.fp1_lbl.grid(row=0, column=0, padx=20, pady=20, sticky="nw") # Store image reference to prevent garbage collection self.fp1_image = None def update_images(self): # Replace this with your actual image loading/update logic # Example: If fp1 is your updated image object self.fp1_image = fp1 # Assume fp1 is a PhotoImage instance self.fp1_lbl.config(image=self.fp1_image)
3. Fixing Image Flicker & Improving Efficiency
Your current updater calls create_images, which in turn calls create_frames—this means you’re recreating the entire frame structure and labels every second, which is why you see flicker and poor performance.
Fix this by:
- Creating frames and labels only once during initialization
- Using the
<Configure>event to handle window resizing instead of rebuilding components - Updating only the image content in your updater, not the entire UI
Update your updater and add a resize handler:
def updater(self): self.update_images() # Only update the image, not the whole UI self.after(UPDATE_RATE, self.updater) def on_window_resize(self, event): # Optional: Add logic here if you need to adjust component sizes on resize # For basic grid layout, resizing is handled automatically via weight settings pass
Full Modified Code Example
Putting it all together, here’s your cleaned-up, efficient code:
import tkinter as tk UPDATE_RATE = 1000 class Application(tk.Frame): def __init__(self, pc_gui): tk.Frame.__init__(self, pc_gui) self.pc_gui = pc_gui self.pc_gui.geometry("1280x720") # Configure main window to expand with resize self.pc_gui.grid_rowconfigure(0, weight=1) self.pc_gui.grid_columnconfigure(0, weight=1) self.grid(sticky="nsew") self.create_frames() self.create_image_labels() self.pc_gui.bind("<Configure>", self.on_window_resize) self.updater() def create_frames(self): # Create frames as instance attributes self.video_frame = tk.Frame(master=self, bg="blue") self.side_frame = tk.Frame(master=self, bg="red") # Configure main app grid self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=3) self.grid_columnconfigure(1, weight=1) # Place frames self.video_frame.grid(row=0, column=0, sticky="nsew") self.side_frame.grid(row=0, column=1, sticky="nsew") # Configure side frame grid for components self.side_frame.grid_rowconfigure(0, weight=1) self.side_frame.grid_columnconfigure(0, weight=1) def create_image_labels(self): # Initialize image label (replace with your default image if needed) self.fp1_lbl = tk.Label(self.side_frame, bg="red") self.fp1_lbl.grid(row=0, column=0, padx=20, pady=20, sticky="nw") self.fp1_image = None def update_images(self): # Example: Replace this with your actual image update logic if self.fp1_image is None: # Dummy image for demonstration self.fp1_image = tk.PhotoImage(width=100, height=100) self.fp1_image.put("#ffffff", to=(0,0,100,100)) self.fp1_lbl.config(image=self.fp1_image) def on_window_resize(self, event): # No action needed for basic grid resizing, but add logic if required pass def updater(self): self.update_images() self.after(UPDATE_RATE, self.updater) # Initialize and run the app if __name__ == "__main__": pc_gui = tk.Tk() app = Application(pc_gui) pc_gui.mainloop()
Key Improvements Summary
- Eliminated AttributeError: Frames are now stored as instance attributes (
self.video_frame,self.side_frame) - No More Flicker: Components are created once, only image content is updated
- Better Efficiency: Avoids recreating frames/labels every update cycle
- Automatic Resizing: Grid layout with weight configurations handles window resizing seamlessly
- Cleaner Structure: Separated initialization, frame creation, component creation, and update logic
内容的提问来源于stack exchange,提问作者Jeff

