转OOP后GenerateQR类内的Tkinter组件无法显示,求助排查
问题排查与解决
核心问题分析
- GenerateQR组件未布局:创建GenerateQR实例后,没有对其执行
pack/grid/place布局操作,也未保留实例引用,导致组件无法渲染且可能被Python垃圾回收机制清理。 - 空白QR图片引用丢失:
blank_qr实例创建后未保存引用,其内部的PhotoImage对象被垃圾回收,导致初始空白QR无法显示。 - 重置方法引用错误:
reset_qr_code方法中引用了不存在的self.photo,还错误地将图片引用设为None,导致重置后QR区域完全空白。
修改后的完整代码
import tkinter as tk from tkinter import filedialog, messagebox, scrolledtext import ttkbootstrap as ttk from ttkbootstrap.constants import * from tkinterdnd2 import * import qrcode import cv2 from PIL import ImageTk, Image import ctypes class MyApp(ttk.Window): def __init__(self): super().__init__() self.title("QR Code Generator") # 窗口尺寸设置 self_height = 660 self_width = 700 screen_width = self.winfo_screenwidth() screen_height = self.winfo_screenheight() x_cordinate = int((screen_width/2) - (self_width/2)) y_cordinate = int((screen_height/2) - (self_height/2)) self.geometry(f"{self_width}x{self_height}+{x_cordinate}+{y_cordinate}") # 窗口图标 self.iconbitmap("assets/qr_icon.ico") # 创建标签页和框架 self.tabs = ttk.Notebook(self, bootstyle="light") self.tabs.pack(expand=True, fill=BOTH, pady=5, padx=5) self.generate_tab = ttk.Frame(self.tabs) self.tabs.add(self.generate_tab, text="Generate QR") # 保存GenerateQR实例并执行布局 self.generate_qr = GenerateQR(self.generate_tab) self.generate_qr.pack(expand=True, fill=BOTH, padx=10, pady=10) class blank_qr(): def __init__(self, parent): photo_0 = Image.open("assets/blank_qr_code.png") max_size = (300, 300) photo_0.thumbnail(max_size) self.photo = ImageTk.PhotoImage(photo_0) class GenerateQR(ttk.Frame): def __init__(self, parent): super().__init__(parent) # 保存空白QR实例,防止图片被垃圾回收 self.blank_qr = blank_qr(self) # 创建组件 data_label = ttk.Label(self, text="Enter your text :") data_label.pack(pady=5) self.text = scrolledtext.ScrolledText( self, wrap="word", height=4, font=('Calibri', 10) ) self.text.pack(fill="x", padx=10) self.text.focus() self.text.bind("<Button-3>", self.show_context_menu) buttons_frame = ttk.Frame(self, width=60) buttons_frame.pack(pady=15) generate_button = ttk.Button(buttons_frame, text="Generate QR Code", bootstyle="dark-outline", command=self.generate_qr_code ) generate_button.pack(side="left", padx=10) generate_button.bind("<Button-1>", self.generate_qr_code) self.bind("<Return>", self.generate_qr_code) reset_button = ttk.Button(buttons_frame, text="Reset", bootstyle="dark-outline", command=self.reset_qr_code ) reset_button.pack(side="left", padx=5) save_button = ttk.Button(buttons_frame, text="Save QR Code", bootstyle="dark-outline", command=self.save_qr_code ) save_button.pack(side="left", padx=10) qr_code_frame = ttk.Frame(self) qr_code_frame.pack() self.qr_code_label = ttk.Label(qr_code_frame, image=self.blank_qr.photo) self.qr_code_label.pack() self.status_label = ttk.Label(self, text="", font=("Calibri", 12)) self.status_label.pack() def generate_qr_code(self, event=None): data = self.text.get('1.0', 'end').strip() if not data: self.status_label.config(text="请输入内容", bootstyle="danger") return qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) qr.add_data(data) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") qr_width, qr_height = img.size max_width = 300 max_height = 300 if qr_width > max_width or qr_height > max_height: ratio = min(max_width / qr_width, max_height / qr_height) new_width = int(qr_width * ratio) new_height = int(qr_height * ratio) img = img.resize((new_width, new_height)) qr_code_img = ImageTk.PhotoImage(img) self.qr_code_label.config(image=qr_code_img) self.qr_code_label.image = qr_code_img self.status_label.config(text="Done ✔", bootstyle="success") def reset_qr_code(self): self.text.delete('1.0', 'end') # 恢复空白QR图片 self.qr_code_label.config(image=self.blank_qr.photo) self.qr_code_label.image = self.blank_qr.photo self.status_label.config(text="") def save_qr_code(self): qr_code_img = self.qr_code_label.image if qr_code_img and qr_code_img != self.blank_qr.photo: path = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("PNG files", "*.png")]) if path: try: img = ImageTk.getimage(qr_code_img) img.save(path) messagebox.showinfo("Info", "Saved") except Exception as e: messagebox.showerror("Error", f"保存失败:{str(e)}") else: messagebox.showwarning("Warning", "没有可保存的QR码") def paste_text(self): try: copied_text = self.clipboard_get() self.text.insert("insert", copied_text) except tk.TclError: self.status_label.config(text="剪贴板为空", bootstyle="warning") def show_context_menu(self, event): context_menu = ttk.Menu(self.text, tearoff=0) context_menu.add_command(label="Paste", command=self.paste_text) context_menu.post(event.x_root, event.y_root) if __name__ == '__main__': app = MyApp() app.mainloop()
关键修改点说明
- MyApp类中:将
GenerateQR(self.generate_tab)改为保存实例并调用pack布局,确保组件能正确渲染在标签页内。 - GenerateQR类中:
- 创建
self.blank_qr实例并保存,保留PhotoImage的引用,避免被垃圾回收。 - 初始化QR标签时使用
self.blank_qr.photo,确保初始空白图片正常显示。 - 修复
reset_qr_code方法,正确引用空白QR图片,避免重置后图片消失。 - 优化空内容判断、保存逻辑和剪贴板粘贴异常处理,提升程序稳定性。
- 创建
内容的提问来源于stack exchange,提问作者Vmine XI
相关产品推荐
相关产品推荐

