PhotoImage won't Display in tkinter treeview - python

I am trying to display an image in tkinter treeview. I have been reading possible solutions but none seems to work, I keep getting a blank tree only with the column heading. For what I have been reading, I have to keep a reference to the PhotoImage, but regardless of how much I try it just won't happen. A simple code example can be found below:
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk as itk
import PIL.Image
import io
s= tk.Tk()
s.title('No *£/**##* image showing')
s.geometry('400x400')
s.rowconfigure(1, weight = 1)
s.columnconfigure(1,weight=1)
headings=['Image']
p = '032f8072.gif'
img1 = PIL.Image.open('032f8072.gif')
#img1 = img1.resize((10,10))
img = itk.PhotoImage(img1)
tree = ttk.Treeview(s)
tree.grid(column=1,row=1,sticky='NSEW')
tree['columns']=headings
tree['show']='headings'
for i in headings:
tree.heading(i,text=i)
tree.column(0, width=125,stretch=True)
#tree.column(1, width=125,stretch=True)
tree.insert('','end','0', open =True, image= img)
tree.image = img
s.mainloop()
I have tried with .gif and .png, i have tried with both PIL.Image and Imagetk.PhotoImage togeter and individually. I have also tried keeping img inside a list to be called from the list to avoid a missing reference.
I really need to get this small piece of code right and I am really frustrated with this little piece holding me back. I would really appreciate if someone could help me with this.
kind regards

This took awhile to figure out! I eventually found this answer to a similar question from 10 months ago (not officially an answer but look in the comments under the question):
Treeview Image not displaying
The user said the comment solved their problem, so I tried applying it to your example. What the commenter means is that the line tree['show'] = 'headings' forces the Treeview to only display the headings, and not the main body of the tree. To fix this, replace that line with the following:
tree['show'] = ('headings', 'tree')
to show all of the tree, and the image should start showing up.

You can create the columns using tree['columns'], but as ne0n p1atypus say, you can't display them using tree['show']. You can still enter the name for the columns manually with explorer_tree.heading("#0",text="Image",anchor= 'center') note that '#0' refers to the column in which the image will be displayed. I didn't try entering the column name using for loop.
Also, when trying to construct a treeview to display images in different rows using a for loop, this algorithm has to be within the function that defines the treeview and all the PhotoImage objects must be append to a list to keep its reference, otherwise they'll be collected as garbage. I'll leave an example below.
global eow
global dffile
global temp_result
global explorer_tree
#Define window and treeview
explorer_headings = ["Name",'Code', 'Supplier Code']
temp_list=[]
eow= Toplevel(acm)
eow.title('Stock Explorer')
eow.geometry('400x650')
eow.geometry("+0+0")
eow.minsize(400,650)
eow.state('zoomed')
eow.grab_set()
#style
style2 = ttk.Style()
style2.theme_use("awdark")
style2.configure("2style.Treeview.Heading",font=('Calibri', 18,'bold')) # Modify the font of the headings
style2.configure("2style.Treeview", font=('Calibri', 20),rowheight=100)
eow['bg']='black'
#Columns and rows
eow.columnconfigure(0, weight=1)
eow.columnconfigure(1, weight =2)
eow.columnconfigure(2, weight =2)
eow.columnconfigure(3, weight =1)
eow.rowconfigure(1, weight=4)
eow.rowconfigure(2, weight=2)
eow.rowconfigure(3, weight=2)
eow.rowconfigure(4, weight=2)
eow.rowconfigure(5, weight=1)
#Treeview to display data
explorer_tree = ttk.Treeview(eow, style='2style.Treeview',height=3)
explorer_tree.grid(column=1,row=2,sticky='NSEW', columnspan=2, rowspan=2)
#treeview scrollbars
xscroll_file_data= tk.Scrollbar(eow, orient='horizontal', command= explorer_tree.xview)
yscroll_file_data= tk.Scrollbar(eow, orient='vertical', command= explorer_tree.yview)
xscroll_file_data.grid(column=1,row=5,sticky='NEW',columnspan=2)
yscroll_file_data.grid(column=3,row=2,sticky='WNS',rowspan= 3)
explorer_tree.configure(yscrollcommand=yscroll_file_data.set)
add_part_button = Button(eow, text='Add Part',font=('Calibri', 14,'bold'),
activebackground='white', activeforeground='black',relief='raised', borderwidth=5
, command = add_part_from_pic_list)
add_part_button.grid(column=1, row=5, sticky='NSEW', padx=100, pady=40)
notfound_button = Button(eow, text='Part Not In List',font=('Calibri', 14,'bold'),
activebackground='white', activeforeground='black',relief='raised', borderwidth=5)
notfound_button.grid(column=2, row=5, sticky='NSEW', padx=100, pady=40)
code_label = Label(eow, text='Code: '+str(code)+' Name: '+online_name,background='black',
foreground='white',font=('Calibri', 18))
code_label.grid(column=1,row=1, sticky = 'NSEW',pady=10, padx=20)
#Name the headings
explorer_tree['columns']=explorer_headings
explorer_tree.heading("#0",text="Image",anchor= 'center')
explorer_tree.heading(0,text="Name",anchor= 'center')
explorer_tree.heading(1,text="Code",anchor= 'center')
explorer_tree.heading(2,text="Supplier Code",anchor= 'center')
#Format the columns
explorer_tree.column('#0', width=130,stretch=False)
explorer_tree.column(0,width=200, anchor='center',stretch=True)
explorer_tree.column(1,width= 200, anchor='center', stretch=False)
explorer_tree.column(2, anchor='center',stretch=False)
explorer_tree.tag_configure('even',foreground='black',background='white')
children = explorer_tree.get_children()
eow.protocol("WM_DELETE_WINDOW",Instant_exit)
explorer_tree.bind("<Double-1>", add_part_from_pic_list)
#construct tree
temp_list=[]
for i in range(len(temp_result)):
for j in range(len(dffile['Name'])):
if temp_result[i] == dffile['Name'][j]:
children1 = explorer_tree.get_children()
temp_row = [dffile['Name'][j],dffile['Code'][j],dffile['Supplier Code'][j]]
p = temp_row[1]+".png"
pp = "images/"+temp_row[1]+".png"
np = 'images/noimage.png'
try:#Append the PhotoImage object to a list rather than to a variable This will avoid the image being collected as garbage
temp_list.append(ImageTk.PhotoImage(Image.open(pp).resize((100,100),Image.ANTIALIAS)))
except FileNotFoundError:
temp_list.append(ImageTk.PhotoImage(Image.open(np).resize((100,100),Image.ANTIALIAS)))
continue
if len(children1)%2 == 0: #When calling the image for tree.insert, call the image from the list i.e. temp_list[i]
explorer_tree.insert('','end',iid=(len(children1)), image=temp_list[i], values=(temp_row[0],temp_row[1],
temp_row[2]),tags=('even'))
else:
explorer_tree.insert('','end',iid=(len(children1)), image=temp_list[i], values=(temp_row[0], temp_row[1],
temp_row[2]),tags=('odd'))
eow.mainloop() ```

try this, for me worked.
tree = ttk.Treeview(master=canvas, columns=car_header, show="tree headings", height=11)
img = tk.PhotoImage(file=r"frames/imgs/image.gif")
tree.insert(parent="",index="end", image=img, text='Information', values=collection_tree[i], tags=('oddrow'))
tk.Label.image = img

Related

How to properly use buttons in tkinter with same window

This is my first day using tkinter, and I wanted to know how to properly code what I have done.
What I wanted to make was a kind of diaporama so I wanted to find a way to display pictures in a canvas and have two buttons so I could go to the previous picture or the following one. The way I stored pictures is using a list lof numpy arrays, with l of size n, so I have n pictures and they are the same size. So I wrote the following code:
from tkinter import *
import numpy as np
from PIL import Image, ImageTk
import sys
sys.setrecursionlimit(10000) #I increased the recursion limit because I had some issues
l = [(255*np.random.rand(50,50)).astype(np.uint8) for i in range(105)] #this is a random list in case you want to test the code
def next(i,n):
if i == n-1:
return 0
else:
return i+1
def previous(i,n):
if i==0:
return n-1
else:
return i-1
window = Tk()
window.geometry("200x100+900+500")
def display(i):
#This is to clear my window at each iteration because I would like to keep the same window
for widget in window.winfo_children():
widget.destroy()
array = l[i] #this is the i-th picture
img = ImageTk.PhotoImage(image=Image.fromarray(array),master = window)
canvas = Canvas(window, width=48, height=48)
canvas.place(x=10, y=20)
canvas.create_image(0,0, anchor="nw", image=img)
Label(window, text="Picture n°"+str(i), fg="black").place(x=5, y=0)
Button(window, text ='Next',command=lambda: display(next(i,len(l)))).place(x=140, y=35)
Button(window, text ='Previous',command = lambda: display(previous(i,len(l)))).place(x=70, y=35)
window.mainloop()
display(0)
I know that is bad code and not the way it should be written. It is working fine but I need help to improve the code please.
You should only put the code of updating the image inside display() and create the interface outside the function. Then recursion is not needed.
Also a global variable is required to keep track the current index to the image list. This variable should be updated when Next or Previous button is clicked.
Below is a modified example:
from tkinter import *
import numpy as np
from PIL import Image, ImageTk
l = [(255*np.random.rand(50,50)).astype(np.uint8) for i in range(105)] #this is a random list in case you want to test the code
current = 0
def display(dir):
global current
# update "current" based on "dir"
current = (current + dir) % len(l)
# show the "current" image
image = ImageTk.PhotoImage(Image.fromarray(l[current]))
imgbox.config(image=image, text=f"Picture n°{current}")
imgbox.image = image # keep a reference to avoid "image" from being garbage collected
window = Tk()
window.geometry("200x100+900+500")
# use a label for showing image and text together
imgbox = Label(window, fg="black", compound="bottom", width=70)
imgbox.place(x=5, y=0)
Button(window, text ='Next', command=lambda: display(+1)).place(x=150, y=35)
Button(window, text ='Previous', command=lambda: display(-1)).place(x=80, y=35)
display(0) # show first image
window.mainloop()

Change Label and Image with Button, Tkinter - Error

I'm playing a little bit with Python and tkinter in order to learn a little bit of programming. I wanted to try and create a Frame with a label, an image and a button. By clicking the button label-text and image should change, where a specific text and image are matched.
Changing the text was no problem, but changing the image doesnt work, and I dont really get why. I add the code to show how I've tried it.
I get the error:
"_tkinter.TclError: image "dummy_logo2" doesn't exist"
What is the problem here?
What am I overlooking?
root = tk.Tk()
dummy_logo2 = tk.PhotoImage(master=root, file='bell2.gif')
dummy_logo2 = dummy_logo2.subsample(5)
dummy_logo = tk.PhotoImage(master=root, file='bell.gif')
dummy_logo = dummy_logo.subsample(5)
dict_01 = {'eins':'dummy_logo',
'zwei':'dummy_logo',
'drei':'dummy_logo2',
'vier':'dummy_logo2'}
def choose():
exerc = random.choice(list(dict_01.keys()))
label_01.config(text=exerc)
label_02.config(image=dict_01[exerc])
label_01 = tk.Label(root, text='Dummy Text')
label_01.grid(row=1, column=1)
label_02 = tk.Label(root, image=dummy_logo)
label_02.grid(row=1, column=2)
button_01 = tk.Button(root, text='Choose', command=choose)
button_01.grid(row=1, column=0)
root.mainloop()
dict_01 is mapping names to the names of your images, as opposed to the images themselves. Remove the quotes around them, like so:
dict_01 = {'eins':dummy_logo,
'zwei':dummy_logo,
'drei':dummy_logo2,
'vier':dummy_logo2}

TKinter Geometry Manager displaying multiple rows of widgets

I wish to display a list of sentences with missing words. The basic, one-line, idea is the following:
The construction of the above is a "label + entry + label + spacing + label". In order to make sure that the widgets were aligned left, I used the following code:
phraseLabel1 = tk.Label(questionFrame)
phraseLabel1.pack(side=tk.LEFT)
keyWordEntry = tk.Entry(questionFrame)
keyWordEntry.pack(side=tk.LEFT)
phraseLabel2 = tk.Label(questionFrame)
phraseLabel2.pack(side=tk.LEFT)
keyWordLabel = tk.Label(questionFrame)
keyWordLabel.pack(side=tk.LEFT,padx=30)
My objective is to present the users with an input screen for multiple sentence. As for example given in the following drawing:
Whilst I manage to create the labels via the underneath (experimental) code, I lack understanding to manage the geometry.
root = tk.Tk()
root.title("myTest")
root.geometry("700x700")
questionFrame = tk.Frame(root)
resultFrame = tk.Frame(root)
for frame in (questionFrame, resultFrame):
frame.grid(row=0, column=0, sticky='news')
#DB Query returning a set of phrases and Keywords
(zinPhrase1, zinPhrase2, keyWordFR, keyWordNL)=getPhrase()
#Init
lab1 = []
keyWordEntry = []
lab2 = []
keyWord = []
for i in range(4): #4 is entered as a dummy value
lab1.append(tk.Label(questionFrame))
lab1[i].pack()
keyWordEntry.append(tk.Entry(questionFrame))
keyWordEntry[i].pack()
lab2.append(tk.Label(questionFrame))
lab2[i].pack()
keyWord.append(tk.Label(questionFrame))
keyWord[i].pack()
lab1[i].config(text=zinPhrase1[i])
keyWordEntry[i].config(width=8)
lab2[i].config(text=zinPhrase2[i])
keyWord[i].config(text=keyWordNL[i],fg="red")
questionFrame.tkraise()
root.mainloop()
How can I manage the placement of the widgets line by line, as shown in the drawing above? Any help would gratefully appreciated.
Since you don't seem to want to organize your widgets in a grid, the most common solution to this problem is to create a frame for each row. The frames stack top-to-bottom, and the widgets inside the frame stack left-to-right.
In my experience, GUI code is much easier to visualize when you separate widget creation from widget layout, so I've done that in the following example to hopefully make it easier to comprehend.
for i in range(4): #4 is entered as a dummy value
rowFrame = tk.Frame(questionFrame)
rowFrame.pack(side="top", fill="x")
lab1.append(tk.Label(rowFrame))
keyWordEntry.append(tk.Entry(rowFrame))
lab2.append(tk.Label(rowFrame))
keyWord.append(tk.Label(rowFrame))
lab1[i].pack(side="left")
keyWordEntry[i].pack(side="left")
lab2[i].pack(side="left", padx=(0, 40))
keyWord[i].pack(side="left")
lab1[i].config(text=zinPhrase1[i])
keyWordEntry[i].config(width=8)
lab2[i].config(text=zinPhrase2[i])
keyWord[i].config(text=keyWordNL[i],fg="red")
The above code results in something like this:
Would something like the following suffice?
for i in range(4): #4 is entered as a dummy value
frames.append(tk.Frame(questionFrame))
frames[i].grid(row=i, column=0, stick='W')
for i in range(4):
lab1.append(tk.Label(frames[i]))
lab1[i].grid(row=0, column=0)
keyWordEntry.append(tk.Entry(frames[i]))
keyWordEntry[i].grid(row=0, column=1)
lab2.append(tk.Label(frames[i]))
lab2[i].grid(row=0, column=2)
keyWord.append(tk.Label(frames[i]))
keyWord[i].grid(row=0, column=3)
lab1[i].config(text=zinPhrase1[i])
keyWordEntry[i].config(width=8)
lab2[i].config(text=zinPhrase2[i])
keyWord[i].config(text=keyWordNL[i],fg="red")

Trying to Load an Image with Dialogue Box in Python and Display it in a Layer - PYTHON 3.4 using Tkinter and PIL

I am trying to Make a simple Photo editor. I already got all the functions for editing photos. Just trying to make it look nice with a GUI. When the user presses file>open, the open dialogue box comes up and they can choose an image. Then I want that image to load inside of a Layer in the GUI.
Here is my code so far:
p.s. When I put the code that is inside the open_img function outise the function, it works, but when i put it inside the function, no image loads
import os
from tkinter import *
import tkinter.messagebox
from PIL import Image, ImageTk
from tkinter.filedialog import askopenfilename
def g_quit():
mExit=tkinter.messagebox.askyesno(title="Quit", message="Are You Sure?")
if mExit>0:
mGui.destroy()
return
#open menu
def open_img():
file = tkinter.filedialog.askopenfilename(initialdir='D:/Users/')
w_box = 500
h_box = 500
pil_image = Image.open(file)
w, h = pil_image.size
pil_image_resized = resize(w, h, w_box, h_box, pil_image)
# wr, hr = pil_image_resized.size
tk_image = ImageTk.PhotoImage(pil_image_resized)
label2.config(image=tk_image, width=w_box, height=h_box)
def resize(w, h, w_box, h_box, pil_image):
'''
resize a pil_image object so it will fit into
a box of size w_box times h_box, but retain aspect ratio
'''
f1 = 1.0*w_box/w # 1.0 forces float division in Python2
f2 = 1.0*h_box/h
factor = min([f1, f2])
#print(f1, f2, factor) # test
# use best down-sizing filter
width = int(w*factor)
height = int(h*factor)
return pil_image.resize((width, height), Image.ANTIALIAS)
mGui = Tk()
mGui.title('Photo Filters')
mGui.geometry('650x500')
mGui.resizable(0, 0) #Disable Resizeability
photoFrame = Frame(mGui, bg="orange", width=500, height=500)
photoFrame.pack(side=LEFT)
filtersFrame = Frame(mGui, bg="yellow", width=150, height=500)
filtersFrame.pack(side=LEFT, fill=Y)
label2 = Label(photoFrame)
#Create Buttons for All the Possible Filters
negative_btn = Button(filtersFrame, text="Negative")
negative_btn.pack()
weighted_grayscale_btn = Button(filtersFrame, text="weighted Grayscale")
weighted_grayscale_btn.pack()
solarize_btn = Button(filtersFrame, text="Solarize")
solarize_btn.pack()
black_and_white_btn = Button(filtersFrame, text="Black and White")
black_and_white_btn.pack()
black_and_white_and_gray_btn = Button(filtersFrame, text="Black and White and Gray")
black_and_white_and_gray_btn.pack()
extreme_contrast_btn = Button(filtersFrame, text="Extreme Contrast")
extreme_contrast_btn.pack()
sepia_tint_btn = Button(filtersFrame, text="Sepia Tint")
sepia_tint_btn.pack()
adjust_component_btn = Button(filtersFrame, text="Adjusts Components")
adjust_component_btn.pack()
posterize_btn = Button(filtersFrame, text="Posterize")
posterize_btn.pack()
simplify_btn = Button(filtersFrame, text="Simplify")
simplify_btn.pack()
detect_edges_btn = Button(filtersFrame, text="Detect Edges")
detect_edges_btn.pack()
detect_edges_better_btn = Button(filtersFrame, text="Detect Edges Better")
detect_edges_better_btn.pack()
blur_btn = Button(filtersFrame, text="Blur")
blur_btn.pack()
flip_image = Button(filtersFrame, text="Flip Horizontal")
flip_image.pack()
#Menu Bar
menubar = Menu(mGui)
filemenu = Menu(menubar)
#Create the Menu Options that go under drop down
filemenu.add_command(label="New")
filemenu.add_command(label="Open", command=open_img)
filemenu.add_command(label="Save As")
filemenu.add_command(label="Close", command=g_quit)
#Create the Main Button (e.g file) which contains the drop down options
menubar.add_cascade(label="File", menu=filemenu)
mGui.config(menu=menubar)
mGui.mainloop()
You've actually got two problems here. One of them, you found for yourself—you never call label2.pack(), so the label itself never gets displayed. But, even after you fix that, the label is blank.
As the Label documentation says:
You can use the label to display PhotoImage and BitmapImage objects. When doing this, make sure you keep a reference to the image object, to prevent it from being garbage collected by Python’s memory allocator.
You're not doing that. You store the PhotoImage in a local variable, tk_image, which goes away as soon as the function exits.
That should explain why it works if you move the code outside a function. If you do that, tk_image becomes a global variable, which doesn't go away until the whole program exits. And, since you're already using global variables all over the place implicitly, one possible fix is to use an explicit global for this:
def open_img():
global tk_image
# the rest of your code
However, a much better trick is to just cram the image into an attribute of something else that's going to stick around, like the Label object itself:
def open_img():
# your existing code
label2.tk_image = ImageTk.PhotoImage(pil_image_resized)
label2.config(image=label2.tk_image, width=w_box, height=h_box)
Now, the image can't be garbage collected until the label that's using it gets garbage collected (or until you replace it with a new image).
In fact, now that I look at the docs, this is exactly what the example of using a PhotoImage with a Label does:
photo = PhotoImage(file="icon.gif")
w = Label(parent, image=photo)
w.photo = photo
w.pack()

How to increase multiple values from one tkinter Scale widget

I have a button which recalls 24 values from a text file. I now want to recall these values using a tkinter Scale widget but obviously I will want these values to increase in increments of 1 up to the value that is stored in the text file, these values will all be different.
So, for example, a shortened version of my text file is:
C1=255
C2=254
C3=120
C4=60
C5=153
So at the minute, my button will instantly recall these values, but I no want to slowly increment all of them using a scale so that when the scale is at the top of its travel it will represent all of the numbers shown above.
Here is my code which will do what I want but using a button, I now need to change the button for a slider:
from Tkinter import *
master= Tk()
master.geometry('500x500+0+0')
def print_value(val):
print ("c1="+str (c1v.get()))
print ("c2="+str(c2v.get()))
c1v=DoubleVar()
c2v=DoubleVar()
c1 = Scale(master, from_=255, to=0, length =400,width =100, troughcolor = 'blue',command=print_value, variable =c1v)
c1.grid(row=1,column=1)
c2 = Scale(master, from_=255, to=0, length =400,width =100, troughcolor = 'blue',command=print_value, variable =c2v)
c2.grid(row=1,column=2)
def load_p1():
pass
lp1 = open("/home/pi/Desktop/IEP/test/preset_test.txt")
val1, val2 = (x.split("=")[1] for x in lp1)
c1.set(val1)
c2.set(val2)
lp1.close()
def record():
save_path = '/home/pi/Desktop/IEP/test'
name_of_file = ("preset_test")
completeName = os.path.join(save_path, name_of_file+".txt")
file1 = open(completeName , "w")
toFile = ("c1="+str (c1.get())+ "\n""c2="+str(c2.get()))
file1.write(toFile)
file1.close()
rec=Button(master, text="Record",width=20, height=10, bg='Red', command=record)
rec.grid(row=2, column=1)
load=Button(master, text="Load",width=20, height=10, bg='gold',command=load_p1)
load.grid(row=2, column=2)
master.mainloop()
The best place to start is to find a tkinter tutorial and learn the basics of tkinter, completely ignoring the problem you are trying to solve. Then, using what you've learned, try to solve your problem. When you get stuck on a specific problem come back here and show us what you've tried.

Categories

Resources