How do i properly attach a .PNG file into a TKINTER button? - python

I am doing a battleship game and a function below that executes to create a new button with an explosion image as it's background. I am using Mac & python 3.7
global redraw_gameboard
global Player
global AI_player
script_dir = os.path.dirname(__file__)
rel_path = "explode.png"
image = ImageTk.PhotoImage(file=os.path.join(script_dir, rel_path))
new_button = Button(redraw_gameboard,
height = 2,
width = 4,
command= already_shot,
image=image)
new_button.grid(row = row, column = column)
This is what is coming out:

I am not sure what you expect, since I don't know what does the "explode.png" image look like. Also, when asking questions on stackoverflow, please always try to post a minimal reproducible example.
However, as I understand, the problem probably comes from the fact that the image is bigger than the button, and it is cropped. Then, only the upper left part of the image is displayed in your buttons.
Suggested solution:
(You will need to install the pillow package if it is not done yet)
import os
from PIL import Image, ImageTk
import tkinter
# Sizes in pixels
BUTTON_HEIGHT = 40
BUTTON_WIDTH = 40
root = tkinter.Tk()
script_dir = os.path.dirname(__file__)
rel_path = "explode.png"
image = Image.open(os.path.join(script_dir, rel_path))
image = image.resize((BUTTON_WIDTH,BUTTON_HEIGHT))
imtk = ImageTk.PhotoImage(image)
# Using a void image for other buttons so that the size is given in pixels too
void_imtk = tkinter.PhotoImage(width=BUTTON_WIDTH, height=BUTTON_HEIGHT)
def create_button(row, column, im):
new_button = tkinter.Button(root,
height = BUTTON_HEIGHT,
width = BUTTON_WIDTH,
image=im)
new_button.grid(row = row, column = column)
create_button(0,0, imtk)
create_button(0,1, void_imtk)
create_button(1,0, void_imtk)
create_button(1,1, imtk)
root.mainloop()
Of course, you will want to make some changes for your program, e.g. using your widget architecture.

Related

Black sometimes replaces transparent when using PIL on tkinter

I have been trying to display several images (in png) on tkinter. All of them have a transparent background, but for some reason jailer.png (the handcuffs) has been displaying with a black background (see screenshot), while every other image works as it should. Why ?
Here's my code (if create_a_label_with_an_image is redudant, it's because i had a lot of problem wth garbage disposal, if anyone knows the trick, pls tell me !) :
import tkinter as tk
from tkinter import ttk
from os import walk
from PIL import Image, ImageTk
class Gui:
def __init__(self):
self.frames={"main":tk.Tk()} #every frame will be added in this dictionnary
self.l_widgets={} #all widgets
def create_notebook(self, name, config_args={}, grid_args={}, frame="main"):
self.l_widgets[name] = (ttk.Notebook(self.frames[frame], **config_args),0)
self.l_widgets[name][0].grid(**grid_args)
def create_a_label_with_an_image(self, name, filename, size=(None, None), config_args={}, grid_args={}, frame="main"):
image_temp=Image.open(filename)
if size!=(None,None):
image_temp.thumbnail(size)
image=ImageTk.PhotoImage(image_temp)
self.l_widgets[name] = (ttk.Label(self.frames[frame]),image) #Image is stored at several places to hope it will not get erased
self.l_widgets[name][0].config(**config_args, image=image)
self.l_widgets[name][0].image=image
self.l_widgets[name][0].grid(**grid_args)
def add_to_notebook(self, name, name_frame, config_args={}, grid_args={}):
self.frames[name_frame]=ttk.Frame(self.frames["main"])
self.l_widgets[name][0].add(self.frames[name_frame], **config_args)
self.l_widgets[name][0].grid(**grid_args)
#END ####
g=Gui()
#here, i get all path to icons from this directory
mypath = "./Wolvesville Notebook/icons/characters"
filenames = next(walk(mypath), (None, None, []))[2] # [] if no file
if filenames==[]:
raise Warning("no icons found")
g.create_notebook("roles")
g.add_to_notebook("roles","all", config_args={"text":"all"})
nbr_images=0
for icon in filenames:
g.create_a_label_with_an_image(icon[:-4], mypath+"/"+icon,
size=(50,50),
grid_args={"row":nbr_images//12,
"column":nbr_images%12},
frame=str(pool))
nbr_images+=1
tk.mainloop()
Here's what the final result look like : Notebook, with one onglet named all, and 5 icons, all transparent but handcuffs
Here is the file that doesn't work : jailer.png.
Thank you in advance for the help !
Check the format of PNG file first, convert to "RGBA" if not.
>>> from PIL import Image
>>> im = Image.open('d:/jailer.png')
>>> im.mode
'LA'
>>> im.getpixel((0, 0))
(0, 0)
>>> new_im = im.convert(mode="RGBA")
>>> new_im.save("D:/jailer_new.png", format="PNG")
Simple code to show the differece, left one is your original copy.
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
im1 = Image.open("D:/jailer.png")
im2 = Image.open("D:/jailer_new.png")
pic1 = ImageTk.PhotoImage(im1)
pic2 = ImageTk.PhotoImage(im2)
label_1 = Label(root, image=pic1, bg='blue')
label_2 = Label(root, image=pic2, bg='blue')
label_1.pack(side='left')
label_2.pack(side='left')
root.mainloop()
AKX commented under my question to convert the grayscale + alpha into rgb + alpha, and it works ! I don't know why it was in grayscale, but it doesn't matter. I will close the subject, thanks !
PS : I can't seem to put a comment as the answer, so I wrote another one.

My image won't show but the resolution of my image is applied

I'm learning the tkinter module and I got a problem. I did a fonction to show an image from my folder but my image won't show up but the resolution of the window is the same as my image. Anybody have a solution ?
from tkinter import *
from PIL import ImageTk, Image
root = Tk()
def my_image(img, row, column) :
pic = ImageTk.PhotoImage(Image.open("C:/Users/Mark/Downloads/test/" + img))
my_img = Label(image=pic)
return my_img.grid(row=row, column=column)
# Create image
image1 = my_image("luigi_icon.png", 0, 1)
root.mainloop()
This problem was many times on Stackoverflow.
There is bug in PhotoImage which removes image from memory when it is assigned to local variable. You have to assign it to global variable or to some object
Common solution is to assign pic to label which displays it.
my_img.pic = pic
See Note at the end of page PhotoImage
(it is archived version on Wayback Machine because original version was removed)
BTW:
You have other problem. grid() gives None. You should return my_img if you want to access label later (ie. to replace image).
Currently original tk.PhotoImage can work with png so you don't need PIL. But if you want to work with some less popular image format then you may need pillow.
import os
#from tkinter import *
import tkinter as tk # PEP8: `import *` is not preferred
from PIL import ImageTk, Image
# --- functions ---
def my_image(img, row, column) :
fullpath = os.path.join("C:/Users/Mark/Downloads/test/", img)
#pic = ImageTk.PhotoImage(Image.open(fullpath))
# or
#pic = ImageTk.PhotoImage(file=fullpath)
# or even without `PIL/pillow` for `png`
pic = tk.PhotoImage(file=fullpath)
my_img = tk.Label(image=pic)
my_img.grid(row=row, column=column)
my_img.pic = pic # solution for bug in `PhotoImage`
return my_img
# --- main ---
root = tk.Tk()
image1 = my_image("luigi_icon.png", 0, 1)
root.mainloop()

PIL: Jump to next photo after one click event

I am preparing data for deep running. So I have to get certain pixel coordinates for each picture. Only one coordinate per photo is required.
So when I use PIL to input one click, I try to implement coordinates so that I can go to the next picture.
However, when I write the code as below, the coordinates are output in duplicate to only one picture, and the next picture does not appear on the screen.
How can I make sure that only one coordinate is recorded on a single picture?
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
print(imgList)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
def callback(event):
print("clicked at: ", event.x, event.y)
window = tkinter.Tk(className='pla')
for file in sorted(imgList):
a=True
image = Image.open(os.path.join(URL, file))
print(image)
canvas = tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
tkinter.mainloop()
I am not 100% sure I understand what you need but it looks like to me you are trying to get one set of cord's for each image in a list of images.
I would do this by creating a function and a tracking variable to loop through each image on at a time and on click update a new list with the image and cord's then loop to next image.
Let me know if you have any questions.
Example:
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
tracker = 0
list_images_with_cords = [] # added list for final results
def callback(event):
# Added global's.
global tracker,list_images_with_cords
# Used to append final results to list.
list_images_with_cords.append([imgList[tracker], event.x, event.y])
# This tracker lets us go through each item on the list.
tracker += 1
# After appending list go to next image.
open_next()
window = tkinter.Tk(className='pla')
# Creates just one canvas that we can update later.
canvas = tkinter.Canvas(window)
canvas.pack()
def open_next():
# Adding global's.
global image, canvas, image_tk, tracker
# Clearing canvas before drawing new image.
canvas.delete("all")
# Checking for valid index in list.
if tracker < len(imgList):
image = Image.open(os.path.join(URL, imgList[tracker]))
# use config() to update canvas.
canvas.config(width=image.size[0], height=image.size[1])
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
else:
# This else statement is just for when we run out of images.
# It will display all the results in a textbox.
canvas.destroy()
txt = tkinter.Text(window, width=25)
txt.pack()
for item in list_images_with_cords:
txt.insert("end", "{}\n\n".format(item))
open_next()
tkinter.mainloop()

Setting up GUI with Tkinter for Image and Text inputs

Im using python 2.7 to build a satellite tracker for a cubesat project at my university. Now I've got the whole thing running as I want it to. I produce 4 images and a load of text based on the ISS's orbit parameters as our cubesat is not yet flying.
The 4 pictures are:
1) Satellite's current position on a globe based on its TLE straight from celestrak.
2) Satellite's current position on a mercator(flat) map.
3) Its current lat/lon position translated to a google earth view to physically interpret where it is.
4) Its current lat/lon position based on current weather maps, for whether or not there are clouds in the region, obscuring the satellites remote sensing imaging capabilities. (Reason why the picture looks ugly and purple, is because this region had no current weather data at the time of retrieving data)
Then I need to implement a text box with information on altitude, velocity, spatial resolution etc.
It's all nice and dandy. But I cant for the love of god figure out how to get this Tkinter GUI to play nice! I'm a geoscientist, not a computer scientist and python programming is very new to me :D
Anyway I did a quick layout in photoshop as of how I want the finished data to be produced in a Tkinter GUI. See the primitive image below:
Now here is my shameful code below:
import Tkinter as tk
from PIL import ImageTk, Image
#This creates the main window of an application
window = tk.Tk()
window.title("Satellite Control Center")
window.geometry("1000x800")
window.configure(background='grey')
#Imports the pictures.
pic1 = "Globeview.png"
pic2 = "MercatorView.png"
pic3 = "currentweathercroppedsmall.png"
pic4 = "GECurrentcroppedsmall.png"
#Creates a Tkinter-compatible photo image, which can be used everywhere Tkinter expects an image object.
img1 = ImageTk.PhotoImage(Image.open(pic1))
img2 = ImageTk.PhotoImage(Image.open(pic2))
img3 = ImageTk.PhotoImage(Image.open(pic3))
img4 = ImageTk.PhotoImage(Image.open(pic4))
#The Label widget is a standard Tkinter widget used to display a text or image on the screen.
globeview = tk.Label(window, image = img1)
mercatorview = tk.Label(window, image = img2)
currentweather= tk.Label(window, image = img3)
gearth = tk.Label(window, image = img4)
#The Pack geometry manager packs widgets in rows or columns.
globeview.pack(side = "top", fill = "both", expand = "yes")
mercatorview.pack(side = "top", fill = "both", expand = "yes")
currentweather.pack(side = "bottom", fill = "both", expand = "yes")
gearth.pack(side = "bottom", fill = "both", expand = "yes")
#Start the GUI
window.mainloop()
Which produces this horror show!
My issues are clearly:
1) Images are not aligned in any way I want. Normally in HTML I'd set up table rows, align and fit it with spacers as I want. But I don't know how to define that here and I've spend hours being frustrated of this by now.
2) I need to add a text box. Every time I've tried to add various versions of text boxes. I get weird pyimage'xx' errors and no text box seems to materialize.
3) For the future: A button under the images that will show the full size uncropped picture. But that's not imperative right now!
So I'm hoping one of you have a nice way to do this, or can point me in a direction.. or perhaps even have done something like it where I can see your code and just tweak the numbers of a bit for aligning the pixels.
Thank you in advance.
You are trying to lay everything out in a single frame. That can be done, but it's much easier to make subframes for things that fall in neat rows, columns or grids, and then put the subframes into the final arrangement.
Totally untested guess:
import Tkinter as tk
from tkFont import Font
from PIL import ImageTk, Image
#This creates the main window of an application
window = tk.Tk()
window.title("Aarhus University Satellite Control Center")
window.geometry("1000x800")
window.configure(background='grey')
#Imports the pictures.
pic1 = "Globeview.png"
pic2 = "MercatorView.png"
pic3 = "currentweathercroppedsmall.png"
pic4 = "GECurrentcroppedsmall.png"
#Creates a Tkinter-compatible photo image, which can be used everywhere Tkinter expects an image object.
img1 = ImageTk.PhotoImage(Image.open(pic1))
img2 = ImageTk.PhotoImage(Image.open(pic2))
img3 = ImageTk.PhotoImage(Image.open(pic3))
img4 = ImageTk.PhotoImage(Image.open(pic4))
header = tk.Label(window, text="Header", font=Font(size=40))
header.pack()
toprow = tk.Frame(window)
globeview = tk.Label(toprow, image = img1)
globeview.pack(side = "left") # the side argument sets this to pack in a row rather than a column
infobox = tk.Text(toprow)
infobox.pack(side = "left")
toprow.pack() # pack the toprow frame into the window
bottomrow = tk.Frame(window)
mercatorview = tk.Label(bottomrow, image = img2)
currentweather= tk.Label(bottomrow, image = img3)
gearth = tk.Label(bottomrow, image = img4)
mercatorview.pack(side = "left")
currentweather.pack(side = "left")
gearth.pack(side = "left")
bottomrow.pack()
#Start the GUI
window.mainloop()
import Tkinter as tk
from PIL import ImageTk, Image
#This creates the main window of an application
window = tk.Tk()
window.title("Aarhus University Satellite Control Center")
window.geometry("1000x800")
window.configure(background='grey')
#Imports the pictures.
pic1 = "Globeview.png"
pic2 = "MercatorView.png"
pic3 = "currentweathercroppedsmall.png"
pic4 = "GECurrentcroppedsmall.png"
#Creates a Tkinter-compatible photo image, which can be used everywhere Tkinter expects an image object.
img1 = ImageTk.PhotoImage(Image.open(pic1))
img2 = ImageTk.PhotoImage(Image.open(pic2))
img3 = ImageTk.PhotoImage(Image.open(pic3))
img4 = ImageTk.PhotoImage(Image.open(pic4))
#The Label widget is a standard Tkinter widget used to display a text or image on the screen.
globeview = tk.Label(window, image = img1).grid(row=0,column=0)
mercatorview = tk.Label(window, image = img2).grid(row=1,column=0)
currentweather= tk.Label(window, image = img3).grid(row=1,column=1)
gearth = tk.Label(window, image = img4).grid(row=1,column=2)
#Start the GUI
window.mainloop()
You need to add row and column in the label grid option. After you are creating the Label , put the grid and mention row and column . I have updated the code, just check it . If you face any problem write us back, and remove pack, which I deleted in the code :) Cheers

Tkinter - Button Image Transparent Background

I have worked out how to have an image for a button, thats positioned on top of a label (I think I maybe doing it the long-winded way because I can't install PIL on my Mac for some reason). Anyhow, it works as it should to a certain degree - the problem I have is it's adding white space either side, and then the image itself is not displaying its transparent background.
The code I am using is as follows:
from tkinter import *
#from PIL import Image
root = Tk()
#Removes the title bar and places over mac top bar
root.tk.call("::tk::unsupported::MacWindowStyle", "style", root._w, "plain", "none")
# Makes the app full screen
#root.wm_attributes('-fullscreen', 1)
root.geometry('{}x{}'.format(480, 320))
#root.attributes('-topmost', True)
def quitApp():
# mlabel = Label (root, text = 'Close').pack()
root.destroy()
background_img = PhotoImage(file="images/bg.gif")
scanBtn_img = PhotoImage(file="images/scanBtn.gif")
background = Label(root,
compound = CENTER,
quitButton = Button(image=scanBtn_img, command = quitApp).pack(),
image = background_img).pack(side="right")
background.image = background_img # keep a reference!
root.mainloop()
From what I understand tkinter natively supports transparency on images like GIF.
I chopped up your code a little but it does work for me. Maybe there is a problem with how you have set up your code. Your label also has a button in it. I don't think you need to have both. You can just created the button where you want it.
Just for reference I created a Label and a Button packed on different sides with a black background to show the transparency of the image.
Here is the code I used to test a gif I have that has transparency. I tested this on both python 3.6 and 2.7 just in case.
from tkinter import *
root = Tk()
def quitApp():
root.destroy()
background_img = PhotoImage(file="Colors/sa.gif")
scanBtn_img = PhotoImage(file="Colors/sa.gif")
background = Label(root,bg='black', image = background_img).pack(side = RIGHT)
quitButton = Button(bg='black', image=scanBtn_img, command = quitApp).pack(side = LEFT)
backgroundimage = background_img # keep a reference!
root.mainloop()
Update: I used the gif you link in the comment
Here is the result.
Update:
After doing some more digging I found what might work for Mac OS:
I don't have a Mac to test on right now so let me know if this works for you:
from tkinter import *
root = Tk()
# Hide the root window drag bar and close button
root.overrideredirect(True)
# Make the root window always on top
root.wm_attributes("-topmost", True)
# Make the window content area transparent
root.wm_attributes("-transparent", True)
# Set the root window background color to a transparent color
root.config(bg='systemTransparent')
def quitApp():
root.destroy()
background_img = PhotoImage(file="Colors/1.gif")
scanBtn_img = PhotoImage(file="Colors/1.gif")
background = Label(root,bg='black', image = background_img)
background.pack(side = RIGHT)
background.config(bg='systemTransparent')
quitButton = Button(bg='black', image=scanBtn_img, command = quitApp)
quitButton.pack(side = LEFT)
quitButton.config(bg='systemTransparent')
backgroundimage = background_img # keep a reference!
root.mainloop()

Categories

Resources