通过 TKinter Filedialog 功能上传图像并由另一个按钮存储

问题描述

简单地说,我通过 tkinter.filedialog 函数上传图像,并希望图像与我通过单击另一个按钮提供的名称同时存储到数据库中,名称存储良好,但不是图像。

这是代码

from tkinter import *
from tkinter import ttk
from PIL import Image,ImageTk
from tkinter import ttk,messageBox
from tkinter import filedialog
import sqlite3

root=Tk()
root.geometry("600x400")

#==========Database================
con =  sqlite3.connect(database="std.db")
cur = con.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS std (name TEXT,photo BLOB )")

#==========Variblels================
var_name = StringVar()
var_photo = StringVar()

#==========Method to Upload Image ================
def uploadImg():
    filename = filedialog.askopenfilename(initialdir =  "/",title = "Select an Image",filetype = (("jpeg files","*.jpg"),("PNG  files","*.png")))
    image = Image.open(filename) # Read the Image
            
    resize_image = image.resize((200,150)) # Reszie the image using resize() method
            
    show_img = ImageTk.PhotoImage(resize_image) # create label and to add the resize image

    var_photo = Label(img_LabelFrame,image=show_img)

    var_photo.image = show_img 
    var_photo.pack()


#==========Method to add The Name and  Image  to Database ================
def add():
    con=sqlite3.connect(database="std.db")
    cur=con.cursor()
    try:
        if var_name.get()=="":
            messageBox.showerror("Error","Student Name is required")
        else:
            cur.execute("select * from std where name =? ",( var_name.get(),) )
            row=cur.fetchone()
            if row!=None:
                messageBox.showerror("Error","Student name is already exists")
            else:
                cur.execute("insert into std (name,photo) values (?,?)",( 
                    var_name.get(),var_photo.get()
                ))
            con.commit()
            messageBox.showinfo("Success","Student Add Successfully")
    except Exception as ex:messageBox.showerror("Error",f"Error duo to {str(ex)}")


#==========Entry Fileds ================
bl_Name=Label(root,text="Student Name:",font= ("Arial",15,)).place(x=10,y=40 )
En_Name= Entry( textvariable=var_name,),bg="lightyellow" ).place(x=150,y=40,width=250)

lbl_Std_photo = Label(root,text="Student Photo: ",y=90 )
img_LabelFrame = ttk.LabelFrame(root,text="")
img_LabelFrame.place(x=150,y=90,width=200,height=150)


btn_upload_img = Button(text="Upload Image",bg="green",command= uploadImg).place(x=200,y=280,width= 150,height=40)
btn_save = Button( text="Save",command=add).place(x=200,y=330,height=40)

mainloop()

解决方法

尝试改变这个:

def uploadImg():
    ...
    show_img = ImageTk.PhotoImage(resize_image)

    var_photo = Label(img_LabelFrame,image=show_img)
    var_photo.image = show_img
    var_photo.pack()

进入这个:

var_photo_list = []

def uploadImg():
    ...
    show_img = ImageTk.PhotoImage(resize_image)

    var_photo = Label(img_LabelFrame,image=show_img)
    var_photo.image = show_img
    var_photo.pack()

    var_photo_list.append(var_photo)

这可确保 var_photo 不会超出范围。如果 var_photo 超出范围,show_img 也会超出范围并被 python 删除。所以它也从 tkinter 世界消失了。

,

var_photo 中使用的 uploadImg() 是一个局部变量,因此在 uploadImg() 中对其进行的任何更改都不会更新全局 var_photo。因此,当执行 add() 时,var_photo.get() 将返回空字符串,因为它永远不会更新。

为了更新全局 var_photo,您需要使用 var_photo.set()

我修改了你的代码:

  • var_photo = StringVar()改成var_photo = Variable(),这样就可以保存图片数据,即字节数据

  • 在全局范围内创建 label_photo 并在 uploadImg() 函数中使用它

  • 添加代码将resize_image保存到var_photo中,以便在add()中使用它来将图像保存到数据库

from io import BytesIO

...

var_photo = Variable()  # use Variable() instead of StringVar()

def uploadImg():
    ...
    resize_image = image.resize((200,150))
    show_img = ImageTk.PhotoImage(resize_image)
    # show the resized image
    label_photo.config(image=show_img)
    label_photo.image = show_img
    # save the image data into var_photo
    outfile = BytesIO()
    resize_image.save(outfile,"PNG")
    var_photo.set(outfile.getvalue())

...

# create the label_photo inside img_LabelFrame
label_photo = Label(img_LabelFrame)
label_photo.pack()

...


从数据库中检索图像后,使用以下示例代码显示图像:

cur.execute("SELECT photo FROM std WHERE name = ?",(var_name.get(),))
row = cur.fetchone()
if row:
    photo = ImageTk.PhotoImage(data=row[0])
    label_photo.config(image=photo)
    label_photo.image = photo