1

Simply, I upload image by tkinter.filedialog functions and wanted the image to be stored to the database simultaneous with the name I provide by clicking another the button, the name stored well but not the image.

Here is the code.

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 , font= ("Arial",15,), bg="lightyellow" ).place(x=150, y=40, width=250)

lbl_Std_photo = Label(root, text="Student Photo: ", font= ("Arial",15,)).place(x=10,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", bg="green", command=add).place(x=200, y=330, width= 150 , height=40)

mainloop()
TheLizzard
  • 7,248
  • 2
  • 11
  • 31
Murad Amer
  • 11
  • 1
  • 2
    Where do you set the `StringVar` named `var_photo` and what do you expect to get at `var_photo.get()` ? – Thingamabobs May 13 '21 at 16:44
  • You can store the path of the image –  May 13 '21 at 17:11
  • The problem is that `var_photo` is going out of scope. When it does it also deletes `show_img` which remove the image from the screen. To solve this problem create a list of all of the `var_photo` labels and make sure it's global – TheLizzard May 13 '21 at 18:09
  • it can be more useful to keep original file bytes `open(filename, 'rb').read()` instead of keeping `pillow` image or `tkinter` `PhotoImage`. – furas May 13 '21 at 19:27
  • @furas OP needs to keep the `PhotoImage`s alive otherwise they aren't going to appear on the screen – TheLizzard May 13 '21 at 21:07
  • @TheLizzard PhotoImage can make problem to save it but as for me it would be more useful to keep original data from file instead of PhotoImage. – furas May 13 '21 at 21:34
  • `var_photo` inside `uploadImg()` is a local variable, and `var_photo` inside `add()` is the global one which is never updated so it is always empty. – acw1668 May 14 '21 at 06:32

2 Answers2

0

Try changing this:

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

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

into this:

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)

This makes sure that var_photo doesn't go out of scope. If var_photo does out of scope, show_img also goes out of scope and is deleted by python. So it also disappears from the tkinter world.

TheLizzard
  • 7,248
  • 2
  • 11
  • 31
0

The var_photo used inside uploadImg() is a local variable so any change on it inside uploadImg() will not update the global var_photo. Therefore, when add() is executed, var_photo.get() will return empty string as it is never updated.

In order to update global var_photo, you need to use var_photo.set().

I have modified your code:

  • change var_photo = StringVar() to var_photo = Variable(), so it can hold the image data which is bytes data

  • create label_photo in global scope and use it inside uploadImg() function instead

  • add code to save resize_image into var_photo so that it can be used in add() to save the image to database

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()

...


After you retrieve the image from database, use below sample code to show the image:

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
acw1668
  • 40,144
  • 5
  • 22
  • 34