Search code examples
pythontkinterttk

How to add pictures to a Tkinter Treeview as values?


How can I add picture to ttk Treeview? Example Code:

from tkinter import *
from ttk import *
from tkinter import ttk
import json
from PIL import Image, ImageTk
okno = Tk()

def maketable():
    style = ttk.Style(okno)
    style.configure("TVS.Treeview", rowheight=40)
    tv = Treeview(okno, style="TVS.Treeview")

    tv['columns'] = ('LName', 'Pic')
    tv.heading("#0", text='Jméno', anchor='w')
    tv.column("#0", anchor="w", width=200)

    tv.heading('LName', text='Příjmení')
    tv.column('LName', anchor='center', width=200)

    tv.heading('Pic', text='Obrazek')
    tv.column('Pic', anchor='center', width=200)

    dbf = open("xxx.json", 'r')
    db = json.loads(dbf.read())
for i in range(0, len(db)):
    root_pic1 = Image.open(db[i]["Pic"])
    root_pic2 = ImageTk.PhotoImage(root_pic1) 
    tv.insert('', 'end', image=root_pic2, text=db[i]['Name'], values=(db[i]['LName'])) 


tv.pack()


def main():
    okno.mainloop()

if __name__ == '__main__':
    maketable()
    main()

I want the "Pic" Column to have pictures - this means a picture shouldn't be in #0 because it is a value. In db[i]["Pic"] there is a file path. In the values=() I can't add images—it doesn't show anything.

EDIT: Thanks for solution but doesnt work on multiple images:

for i in range(0, len(db)):
    tv._image = Image
    tv._image = Image.open(db[i]["Pic"])
    tv._image.thumbnail((100, 200), PIL.Image.ANTIALIAS)
    tv._image = ImageTk.PhotoImage(tv._image)
    tv.insert('', 'end', image=tv._image, values=(db[i]['Name'], db[i]['LName'])) 


tv.pack()

Solution

  • Like @Nae and @furas pointed out, local variables are removed when function finishes and your images get garbage collected.

    you would need to do something like:

    tv._image = Image.open('test.png')
    tv._image = ImageTk.PhotoImage(tv._image)
    
    for i in range(0, len(db)):
    
        tv.insert('', 'end', image=tv._image, text=db[i]['Name'], values=(db[i]['LName']))
    

    or define them outside the function. It would be ideal if you were using classes and you can call self.root_pic = ...

    EDIT: As for more images, you would need to create a list:

    tv._images = []    
    
    for i in range(0, len(db)):
        image = Image.open(db[i]["Pic"])
        image = ImageTk.PhotoImage(image)
        image.thumbnail((100, 200), PIL.Image.ANTIALIAS) # resize
        tv._images.append(image)
    
        tv.insert('', 'end', image=tv._images[-1], text=db[i]['Name'], values=(db[i]['LName']))