You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

转OOP后GenerateQR类内的Tkinter组件无法显示,求助排查

问题排查与解决

核心问题分析

  1. GenerateQR组件未布局:创建GenerateQR实例后,没有对其执行pack/grid/place布局操作,也未保留实例引用,导致组件无法渲染且可能被Python垃圾回收机制清理。
  2. 空白QR图片引用丢失:blank_qr实例创建后未保存引用,其内部的PhotoImage对象被垃圾回收,导致初始空白QR无法显示。
  3. 重置方法引用错误: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类中:
    1. 创建self.blank_qr实例并保存,保留PhotoImage的引用,避免被垃圾回收。
    2. 初始化QR标签时使用self.blank_qr.photo,确保初始空白图片正常显示。
    3. 修复reset_qr_code方法,正确引用空白QR图片,避免重置后图片消失。
    4. 优化空内容判断、保存逻辑和剪贴板粘贴异常处理,提升程序稳定性。

内容的提问来源于stack exchange,提问作者Vmine XI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:24:53