Tkinter Create buttons from list with different image - python-3.x

I want to create button from a list and I want them to have their own image.
I have tried this but only the last created button work.
liste_boutton = ['3DS','DS','GB']
for num,button_name in enumerate(liste_boutton):
button = Button(type_frame)
button['bg'] = "grey72"
photo = PhotoImage(file=".\dbinb\img\\{}.png".format(button_name))
button.config(image=photo, width="180", height="50")
button.grid(row=num, column=0, pady=5, padx=8)

Only your last button has an image as it is the only one that has reference in the global scope, or in any scope for that matter in your specific case. As is, you have only one referable button and image objects, namely button and photo.
Short answer would be:
photo = list()
for...
photo.append(PhotoImage(file=".\dbinb\img\\{}.png".format(button_name)))
But this would still have bad practice written all over it.

Your code appears okay, but you are need to keep a reference of the image effbot and also I do believe PhotoImage can only read GIF and PGM/PPM images from files, so you need another library PIL seems to be a good choice. I used a couple images from a google search for the images, they were placed in the same directory as my .py file, so i changed the code a little bit. Also the button width and height can cut off parts of the image if not careful.
from Tkinter import *
from PIL import Image, ImageTk
type_frame = Tk()
liste_boutton = ['3DS','DS','GB']
for num,button_name in enumerate(liste_boutton):
button = Button(type_frame)
button['bg'] = "grey72"
# this example works, if .py and images in same directory
image = Image.open("{}.png".format(button_name))
image = image.resize((180, 100), Image.ANTIALIAS) # resize the image to ratio needed, but there are better ways
photo = ImageTk.PhotoImage(image) # to support png, etc image files
button.image = photo # save reference
button.config(image=photo, width="180", height="100")
# be sure to check the width and height of the images, so there is no cut off
button.grid(row=num, column=0, pady=5, padx=8)
mainloop()
Output:
[https://i.stack.imgur.com/lxthT.png]

With all your comments I was able to achieve what I expected !
Thank ! I'm new to programming so this will not necessarily be the best solution but it works
import tkinter as tk
root = tk.Tk()
frame1 = tk.Frame(root)
frame1.pack(side=tk.TOP, fill=tk.X)
liste_boutton = ['3DS','DS','GB']
button = list()
photo = list()
for num,button_name in enumerate(liste_boutton):
button.append(tk.Button(frame1))
photo.append(tk.PhotoImage(file=".\dbinb\img\\{}.png".format(button_name)))
button[-1].config(bg="grey72",image=photo[-1], width="180", height="50")
button[-1].grid(row=num,column=0)
root.mainloop()

Related

Embedding Images and matplotlib Plots together on on tk canvas

A few years ago I wrote a script in python2 that would pull images and generate plots and show them together in one gui, following this old documentation. However, I'm trying to update this to python3, and I've run into an issue with the way that plots are put on tk windows in the latest version of matplotlib. There are updated guides for how to embed matplotlib graphs in tk windows, of course, but these don't seem to be exactly what I want, since I want to plot various images and graphs together.
Currently:
# Start by creating the GUI as root
root=Tk()
root.wm_title("JADESView")
# Create the canvas
canvas=Canvas(root, height=canvasheight, width=canvaswidth)
# Plot the EAZY SED
#image = Image.open(EAZY_files+str(current_id)+"_EAZY_SED.png")
image = getEAZYimage(current_id)
# Crop out the thumbnails
image = cropEAZY(image)
photo = resizeimage(image)
item4 = canvas.create_image(eazy_positionx, eazy_positiony, image=photo)
Label(root, text="EAZY FIT", font=('helvetica', int(textsizevalue*1.5))).place(x=eazytext_positionx, y = eazytext_positiony)
# Plot the BEAGLE SED
#new_image = Image.open(BEAGLE_files+str(current_id)+"_BEAGLE_SED.png")
new_image = getBEAGLEimage(current_id)
new_photo = resizeimage(new_image)
item5 = canvas.create_image(beagle_positionx, beagle_positiony, image=new_photo)
Label(root, text="BEAGLE FIT", font=('helvetica', int(textsizevalue*1.5))).place(x=beagletext_positionx, y = beagletext_positiony)
canvas.pack(side = TOP, expand=True, fill=BOTH)
# Plot the thumbnails
fig_photo_objects = np.empty(0, dtype = 'object')
fig_photo_objects = create_thumbnails(canvas, fig_photo_objects, current_id, current_index, defaultstretch)
I create a tk.canvas object, and then use canvas.create_image to place two PIL photo objects, with labels (the "EAZY SED" and BEAGLE SED"), and this still works with python3. However, the create_thumbnails function I've written creates individual figure objects and then calls a function "draw_figure" to embed them, which I'll post below, but it's from the tk example I linked above:
def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas
loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(figure_w), int(figure_h)
photo = PhotoImage(master=canvas, width=figure_w, height=figure_h)
# Position: convert from top-left anchor to center anchor
canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
# Unfortunately, there's no accessor for the pointer to the native renderer
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)
# Return a handle which contains a reference to the photo object
# which must be kept live or else the picture disappears
return photo
And this breaks in all sorts of ways.
I'm open to overhauling how everything is done, but I don't see examples that show how this sort of thing might work. The examples for the latest version of matplotlib all explain that you should use FigureCanvasTkAgg, but I don't exactly know how to use this if I already have an existing canvas with photos embedded.
Any help would be appreciated, and I can also explain more if necessary!

Naming a widget to auto-destroy-replace it

I'm making a picture gallery that exists independently in several places within the app. When any of the galleries close, the current picture displayed in it is posted to a central graphics edit area. So the pictures were gridding on top of each other and I was planning to add code to delete the one that was already there when I stumbled on some unexpected behavior that seems to make this unnecessary. When I used the name option to give the container a name, instead of gridding a new container and picture on top of the existing one, apparently the old one was destroyed and replaced automatically.
My question is, can I rely on this behavior, or is it some kind of fluke? I can't find any documentation on it. From Fredrick Lundh, one of Tkinter's authors, "If you really need to specify the name of a widget, you can use the name option when you create the widget. One (and most likely the only) reason for this is if you need to interface with code written in Tcl." So I'm worried that I'll be relying on a fluke but so far it's a good fluke because I don't have to figure out how to keep widgets from gridding on top of each other if tkinter is going to auto-destroy the old one for me. It's not that hard but this name option thing is easier.
The example shows this behavior in action. If you remove the name option from the functions that create the widgets, the widgets grid on top of each other. But with the name option used, the widgets replace each other.
import tkinter as tk
def create():
f1 = tk.Frame(root, bg='red', name='exists') #
f1.grid(column=0, row=0)
l1 = tk.Label(f1, bg='yellow', text='This is a very long sentence.')
l1.grid(column=0, row=0)
count_labels()
def replace():
f1 = tk.Frame(root, bg='green', name='exists') #
f1.grid(column=0, row=0)
l1 = tk.Label(f1, bg='tan', text='short sentence')
l1.grid(column=0, row=0)
count_labels()
def count_labels():
root.update_idletasks()
for child in root.winfo_children():
x = child
print(len(x.winfo_children()))
root = tk.Tk()
b1 = tk.Button(root, text='create', command=create)
b1.grid(column=0, row=1)
b2 = tk.Button(root, text='replace', command=replace)
b2.grid(column=1, row=1)
root.mainloop()

Tkinter Background Image

I am not able to display an image as the background of my GUI window with the below code, any ideas? I have changed files extension types many times.
from tkinter import *
from PIL import ImageTk, Image
window = Tk()
window.title("My Application")
lbl = Label(window, text="Hello", font=("arial italic", 25))
lbl.grid(column=0, row=0)
backgroundImage = PhotoImage(file = "C:\\Users\\User
Person\\Desktop\\months.gif")
label = Label(master=window,
image = backgroundImage,
text='This is a test for stackflow',
height = 2
)
label.place(x=0, y=0, relwidth=1, relheight=1)
window.mainloop()
I am able to display an image with your code but I used a raw string to define the path for the gif image. You should use it like this:
backgroundImage = PhotoImage(file = r"C:\Users\User Person\Desktop\months.gif")
Notice the r" " which makes the image path a raw string, so you don't need to escape the \.
I also defined a window geometry:
window.title("My Application")
window.geometry("320x240")
Update:
As per comment, you can also use the PIL library to open the image first to fix the error Tkinter error: Couldn't recognize data in image file:
from PIL import ImageTk, Image
backgroundImage = ImageTk.PhotoImage(Image.open(r"C:\Users\User Person\Desktop\months.gif"))
Note:
You must also verify that this is a valid gif file. Try opening the file in Windows Picture viewer or similar software. Also, you mentioned that you have changed the file extension, this could make the file unstable and render it invalid. Therefore, make sure you use the original file extension. If you'd like to convert the file to a different format, use specialized software for that or write a simple Python script(although I've never tried this option) that could perform this conversion without breaking the file.

Hiding/displaying a canvas

How do you hide a canvas so it only shows when you want it displayed?
self.canvas.config(state='hidden')
just gives the error saying you can only use 'disabled' or 'normal'
In the comments you say you are using pack. In that case, you can make it hidden by using pack_forget.
import tkinter as tk
def show():
canvas.pack()
def hide():
canvas.pack_forget()
root = tk.Tk()
root.geometry("400x400")
show_button = tk.Button(root, text="show", command=show)
hide_button = tk.Button(root, text="hide", command=hide)
canvas = tk.Canvas(root, background="pink")
show_button.pack(side="top")
hide_button.pack(side="top")
canvas.pack(side="top")
root.mainloop()
However, it's usually better to use grid in such a case. pack_forget() doesn't remember where the widget was, so the next time you call pack the widget may end up in a different place. To see an example, move canvas.pack(side="top") up two lines, before show_button.pack(side="top")
grid, on the other hand, has a grid_remove method which will remember all of the settings, so that a subsequent call to grid() with no options will put the widget back in the exact same spot.

Display and Hide PNG image in Tkinter Python3

Like the topic,
how can I display a .png image and hide it? And what is the difference between canvas and photoimage?
I wrote a bit of code which hides/shows an image on the click of a button. You can edit it to fit your needs.
NOTE: At the moment, I'm using pack(), and pack_forget(), if you want to use grid or place, you must use grid_forget() or place_forget()
import tkinter
def hideBG():
global state
if state == "Hidden":
background_label.pack()
state = "Showing"
elif state == "Showing":
background_label.pack_forget()
state = "Hidden"
window = tkinter.Tk()
background_image=tkinter.PhotoImage(file="BG.png")
background_label = tkinter.Label(window, image=background_image)
hideBttn = tkinter.Button(window, text="Hide Background", command=hideBG)
state = "Showing"
hideBttn.pack()
background_label.pack()
window.mainloop()
This creates an image within a label, and a button. The button takes the current "state" of the image (whether it is hidden or showing), and changes it to the opposite, by calling the hideBG function when the button is pressed.
Hope this helps!

Resources