I am trying to write a code that will make buttons out of Images. Whenever you click that a button It will display that image.
import os
from PIL import ImageTk, Image
import tkinter
root = tkinter.Tk()
folder_list = os.listdir(r"C:\Users\Teknoloji\Desktop\Phyton\Jack\Selam")
def Imagen():
image = tkinter.Canvas(root,width=300,height=300)
for b in folder_list:
if b == my_text:
img = ImageTk.PhotoImage(Image.open(f"Images\{b}"))
label = tkinter.Label(image=img)
print(my_text)
label.image = img
image.create_image(120,120,image=img)
image.pack()
for i in folder_list:
button = tkinter.Button(root,text=i,command=Imagen)
my_text = button.cget("text")
print(my_text)
button.pack()
root.mainloop()
I have took your code and made some changes here:
import os
from PIL import ImageTk, Image
import tkinter
root = tkinter.Tk()
folder_list = os.listdir(r"C:\Users\Teknoloji\Desktop\Phyton\Jack\Selam")
def Imagen(img_path):
img = ImageTk.PhotoImage(Image.open(f'images/{img_path}'))
#label = tkinter.Label(image=img)
image.whatever = img #keeping a reference, can be anything
image.itemconfigure('image',image=img)
image.pack()
for i in folder_list:
button = tkinter.Button(root,text=i,command=lambda i=i:Imagen(i),width=10)
button.pack(pady=5)
image = tkinter.Canvas(root,width=300,height=300)
image.create_image(120,120,tag='image')
root.mainloop()
What all have I done?
I've removed some unwanted part from your code, like unnecessary looping inside of function and also it seems like your not using your label to display image instead using canvas, either way ive made sure that it does not overwrite the existing image, when you press button.
Also I have passed the image path as a parameter onto the function which opens the image, so that lambda can have a parameter to pass on to the function.
How does the loop button work?
(Taken from here)
This may look magical, but here's what's happening. When you use that lambda to define your function, the open_this call doesn't get the value of the variable i at the time you define the function. Instead, it makes a closure, which is sort of like a note to itself saying "I should look for what the value of the variable i is at the time that I am called". Of course, the function is called after the loop is over, so at that time i will always be equal to the last value from the loop.
Using the i=i trick causes your function to store the current value of i at the time your lambda is defined, instead of waiting to look up the value of i later.
Do let me know if this works.
Do let me know if you have any doubts or error regarding this.
Cheers
Related
I wish to save the filepath we get from filedialog() in a variable outside the defined function openfile().
Below is the code snippet I am using:
import tkinter as tk
from tkinter import filedialog, Button
root = tk.Tk()
def openfile():
path = filedialog.askopenfilename()
return path
Button(root, text = "click to open the stock file", command=openfile).pack(pady=20)
file_path = openfile() # this seems to be causing the issue
The problem is that the filedialog() is getting executed without even getting clicked on.
You're correct about the cause of the filedialog getting executed. Callback functions like openfile() can't return values because it's tkinter that calls them (and it ignores whatever they return). GUI programs require a different programming paradigm than you're probably used to utilized — they're event-driven. This means that they (mostly) only do things as a result of processing user input. For that reason you will need to save the result of calling the askopenfilename() function in a global variable for use later if the value isn't going to be used immediately.
tkinter provides several different kinds of variable classes — BooleanVar, DoubleVar, IntVar, and StringVar — that are good for this sort of thing. In the code below, I show how to use a StringVar to store the path.
The next step will be adding code that does something with the value getting stored in file_path. One possibility would be to add another GUI element, like a Button, that calls another function that does something with the value.
import tkinter as tk
from tkinter import filedialog, Button
root = tk.Tk()
file_path = tk.StringVar()
def openfile():
path = filedialog.askopenfilename()
file_path.set(path) # Save value returned.
Button(root, text="click to open the stock file", command=openfile).pack(pady=20)
root.mainloop()
Here’s what you can do:
def open_file():
global path
path = filedialog.askopenfilename()
And then you can access the path variable wherever you want in the program(after the function is ran, obviously.)
I want to put an image in the second window using tkinter, in the first window the code works good, but the second window shows nothing.
In this part I import necessary modules:
from tkinter import filedialog, Tk, Frame, Label, PhotoImage, Button
from PIL import Image
from tkinter import*
import tkinter as tk
Then create the principal window:
raiz = Tk()
raiz.title("ventana")
Then I create the frame and put the image in the frame:
miFrame = Frame()
miFrame.pack()
miFrame.config(width="1400", heigh=("1200"))
fondo=tk.PhotoImage(file="fondoF.png")
fondo=fondo.subsample(1,1)
label=tk.Label(miFrame,image=fondo)
label.place(x=0,y=0,relwidth=1.0,relheight=1.0)
Then a button that will call the second window function:
btn3 = Button(raiz, text="boton")
btn3.place(x=500, y=500)
btn3.config(command=abrirventana2)
Here we have the function which opens the second window and here (I guess) is where I want to put the image.
This part also has two buttons named mih which does nothing in the meantime and ok which calls the function to close the second window:
def abrirventana2():
raiz.deiconify()
ventana2=tk.Toplevel()
ventana2.geometry('500x500')
ventana2.title("ventana2")
ventana2.configure(background="white")
fondov=tk.PhotoImage(file="xxx.gif")
label1=tk.Label(ventana2,image=fondov)
label1.place(x=50,y=50,relwidth=5.0,relheight=5.0)
mensaje=tk.Label(ventana2,text="funciona")
mensaje.pack(padx=5,pady=5,ipadx=5,ipady=5,fill=tk.X)
boton1=tk.Button(ventana2,text='mih')
boton1.pack(side=tk.TOP)
boton2=tk.Button(ventana2,text='ok',command=ventana2.destroy)
boton2.pack(side=tk.TOP)
Function to close the second window:
def cerrarventana2():
ventana.destroy()
I use the mainloop to keep the window open
raiz.mainloop()
Note: I had already tried creating a frame in the second window, but it didn't work.
Apologies for my previously incorrect answer.
The reason the image is not showing is due to the fact that you did not create a reference to it. If you don't create a reference, the image is garbage collected, which doesn't remove it, but in a sense just renders a blank placeholder on the GUI.
In order to display the image correctly you need to add a reference to the image within the code that displays the image.
You therefore now have:
fondov=tk.PhotoImage(file="giphy.gif")
label1=tk.Label(ventana2,image=fondov)
label1.image = fondov
label1.pack()
(label1.image = fondov is the reference)
Sorry for the confusion there. This should work.
I have a folder which has some images. I want to display all the images using tkinter in a single window. Also whenever I click any image displayed in the window, I need to display the path of the image. I tried using for loop but it prints all the image file path. Here is the code I tried,
from Tkinter import *
import os
from PIL import ImageTk, Image
def getFileName(image):
print str(image)
gtk = Tk()
def showImages(folder):
for images in os.listdir(os.getcwd()):
if images.endswith("png"):
im = Image.open(images)
tkimage = ImageTk.PhotoImage(im)
imageButton = Button(gtk, image=tkimage, command=getFileName(images)
imageButton.image=tkimage
imageButton.pack()
gtk.mainloop()
Can anyone say what I'm doing wrong?
for images in os.listdir(os.getcwd()):
if images.endswith("png"):
im = Image.open(images)
tkimage = ImageTk.PhotoImage(im)
handler = lambda img = images: getFileName(img) #here modify
imageButton = Button(gtk, image=tkimage, command=handler)#here
imageButton.image=tkimage
imageButton.pack()
Because button-press callbacks are run with no arguments, if we
need to pass extra datato the handler, it must be wrapped in an object
that remembers that extra data and passes it along, by deferring the
call to the actual handler. Here, a button press runs the function
generated by the lambda, an indirect call layer that retains
information from the enclosing scope. The net effect is that the real
handler.
-- << Programming Python>> page 435
How can I add an image to a widget in tkinter?
Why when I use this code it does not work:
some_widget.config(image=PhotoImage(file="test.png"), compound=RIGHT)
but this does work?:
an_image=PhotoImage(file="test.png")
some_widget.config(image=anImage, compound=RIGHT)
Your image is getting garbage collected when you try to use it in the first version.
effbot is ancient, but here's good snippet:
You must keep a reference to the image object in your Python program, either by storing it in a global variable, or by attaching it to another object.
In the second version the image is declared at the global level.
Here's another example that would demonstrate this issue, that you would expect to also work, after all it's the same code just in a function
Doesn't work:
import tkinter as tk
from PIL import ImageTk
root = tk.Tk()
def make_button():
b = tk.Button(root)
image = ImageTk.PhotoImage(file="1.png")
b.config(image=image)
b.pack()
make_button()
root.mainloop()
Does work:
import tkiner as tk
from PIL import ImageTk
root = tk.Tk()
def make_button():
b = tk.Button(root)
image = ImageTk.PhotoImage(file="1.png")
b.config(image=image)
b.image = image
b.pack()
make_button()
root.mainloop()
Why? The variables in make_button are local to that function. Same idea if you run into this type of problem inside a class.
I am making a Tkinter GUI to do nothing except call images - and of course, I have struggled to find decent tkinter documentation all along.
There is a line of my code which cannot seem to do as asked - I want to call up all the values in a dictionary and individually print and pull an image by the same name for each one before the next value is called up. I have tried dict.itervalues() and dict.values() and can't seem to figure anything out altogether...
Anyway, here is the snippet:
for key in ansDict.iterkeys(): #using the iterkeys function... kind of
x=key
root = tk.Tk() # root window created (is this in the right place?)
root.title('C H E M I S T R Y A B C\'s')
frameAns=tk.Frame(root)
frameAns.grid(row=0, column=0, sticky=tk.NW)
for i in range(len(ansDict[x])):
print '-->' + ansDict[x][i]
for value in ansDict.itervalues(): #This is the most important part
for i in range(len(value)): #pulls value list from dictionary named ansDict
picRef1 = Image.open(value[i] + '.jpg') #calls image file by the same name using PIL
photo1 = ImageTk.PhotoImage(picRef1, master=root)
button1 = tk.Button(frameAns, compound=tk.TOP, image=photo1, text=str(value[i]) + '\nClose me!', bg='white') #pulls up button onto which the image is pasted
button1.grid(sticky=tk.NW, padx=2, pady=2) #places button on grid
button1.image=photo1
root.mainloop()
Finally, at the end, it pulls up one or two images and then I get the following error:
TclError: can't invoke "image" command: application has been destroyed
and I can't figure out what is wrong. I can't move the image command, and somehow I need to "save" it so it isn't destroyed. I know there are other code errors here, but I think that if I figure out the TclError that I am getting that I can set everything else straight.
If there is an easier way to do all this please do tell!
I have looked around for a good solution to this but have yet to find the proper solution. Looking at the Tkinter.py class it looks like the Image del value is:
def __del__(self):
if self.name:
try:
self.tk.call('image', 'delete', self.name)
except TclError:
# May happen if the root was destroyed
pass
This means if you wanted to do a BRUTAL hack you could setup a PhotoImage as described in jtp's link.
photo = tk.PhotoImage(file="C:/myimage.gif")
widget["image"] = photo
widget.image = photo
Then you could just before the program exited do the following hack:
photo.name = None
This would prevent it from trying to clean itself up in the PhotoImage delete and prevent the exception from being called in the del method. I do not really recommend you do this unless your back is up against the wall, and you have no alternative.
I will continue to look into this and if I find a better solution will edit this post with a better one (hopefully someone will give the correct solution before then).
Here is one possibility, although it is structured differently than your example. It stacks the four 100 pixel square images on top of one another. I believe you need to keep a separate reference to each Image object, so I tucked them away in the images dictionary.
from Tkinter import *
import os
from PIL import Image, ImageTk
image_names = { '1':'one','2':'two','3':'three','4':'four' }
images = {}
root = Tk()
root.title("HELLO")
frm = Frame(root)
for v in image_names.itervalues():
images[v] = {}
images[v]['i'] = Image.open("%s%s.jpg" % (os.path.dirname(__file__), v))
images[v]['pi'] = ImageTk.PhotoImage(images[v]['i'])
images[v]['b'] = Button(frm, image=images[v]['pi'])
images[v]['b'].pack()
frm.pack()
mainloop()
Here is a good link discussing the PhotoImage class.
http://effbot.org/tkinterbook/photoimage.htm
It seems that you did not get the idea of Event-driven programming. You should create whole GUI once, fill it with widgets, setup the events and then enter infinite loop. The GUI should call callback functions based on your event to function binding. So those parts of your program should definitely be called just once: root = tk.Tk(), root.mainloop().
Edit: Added Event-driven programming "idea example".
from Tkinter import *
master = Tk()
def callback():
print "click!"
b = Button(master, text="OK", command=callback)
b.pack()
mainloop()