Tkinter Background Image - python-3.x

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.

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!

How to add background image to an tkinter application?

I recently made a Python watch application. But it looks very boring because of the one-color background and just nothing except of the time and the date. So I wanted to add background to the application. And I ran into a problem: If I just pack the image into a label, the other labels won't be transparent and will have their own background. I tried to use tkinter.Canvas, but it doesn't work with labels. Any suggestions?
That's the code for the first version of the app:
from tkinter import *
from time import sleep
from random import choice
import datetime
root=Tk()
BGCOLOR='#000000'
FGCOLOR='#FFFFFF'
root.overrideredirect(True)
w = root.winfo_screenwidth()
h = root.winfo_screenheight()
root.geometry(str(w)+'x'+str(h)+'+0+0')
root.configure(bg=BGCOLOR)
date=str(datetime.datetime.now()).split()[0].split('-')
tl=Label(root,text=str(datetime.datetime.now()).split()[1].split('.')[0],font=['URW Gothic L',300], fg=FGCOLOR, bg=BGCOLOR)
dl=Label(root,text=date[2]+'.'+date[1]+'.'+date[0],font=['URW Gothic L',100],bg=BGCOLOR,fg=FGCOLOR)
tl.pack(expand=True, fill=BOTH)
dl.pack(expand=True,fill=BOTH)
Button(master=root,text='X',bg=BGCOLOR,fg=FGCOLOR,command=root.destroy).pack(side=RIGHT)
while True:
tl.config(text=str(datetime.datetime.now()).split()[1].split('.')[0])
if date!=str(datetime.datetime.now()).split()[0].split('-'):
date=str(datetime.datetime.now()).split()[0].split('-')
dl.config(text=date[2]+'.'+date[1]+'.'+date[0])
dl.update()
tl.update()
sleep(1)
Ok, so the solution itself is, to create a Canvas object and use create_text() function with "fill" and "font" arguments. The best thing about this is that you can change the position of the text

Updating bqplot image widget

I'm working on a project that uses ipywidgets and bqplot to display and interact with an image.
Using ipywidgets and open cv I can modify an image, save it and update the value of the widget. But I also need the on_click_element aspect of bqplot, so I use the widget from the last one. I still have problems figuring out how to do the same thing with the widget in bqplot.
I would like to avoid to redraw the hole thing, but if needed it would have to close and redraw only the widget image since this is part of a bigger set of widgets. For example I would like to binarize the image using an arbitrary treshold.
From here I got the information on how to use the bqplot image widget: https://github.com/bqplot/bqplot/blob/master/examples/Marks/Object%20Model/Image.ipynb
I use something very similar to this to create the widget that I display.
from IPython.display import display
import ipywidgets as widgets
import bqplot as bq
with open('chelsea.png', 'rb') as f:
raw_image = f.read()
ipyimage = widgets.Image(value=raw_image, format='png')
x_sc = bq.LinearScale()
y_sc = bq.LinearScale()
bq_image = bq.Image(image=ipyimage, scales={'x':x_sc, 'y':y_sc})
img_ani = bq.Figure(marks=[bq_image], animation_duration=400)
display(img_ani)
After this I can't update the figure without redrawing the hole thing.
Any ideas?
jupyter 5.7.8,
ipython 7.5.0,
ipywidgets 7.5.1,
bqplot 0.12.10
Update the bqplot image mark by assigning a new image....
with open("chelsea2.png", 'rb') as f:
raw_image2 = f.read()
# ipyimage.value = raw_image2 # This doesn't seems to sync with widget display. Would require a redisplay of bqplot figure
# create new ipywidgets image and assign it to bqplot image
ipyimage2 = widgets.Image(value=raw_image2, format='png')
bq_image.image = ipyimage2

Receiving "NoneType Object is not Callable" error when calling function via .trace()

Okay, so I'm trying to put together a really simple app that essentially just takes a scanned barcode value, which is tied to an image file, and goes into a dictionary of images, finds the image whose key matches the barcode value, and displays that image in the Tkinter window.
I actually got it working consistently when just using a raw input() value, but when I tried to incorporate an Entry box into the window to take the barcode value, that's when I ran into problems.
I want the Entry widget to kick off a function whenever it's edited, so that all that needs to be done is scan the barcode and the image will appear. I looked up solutions to this and the most common one I found was to use a StringVar, tie it to the Entry widget, and then use .trace() to kick off the desired function whenever the value in the Entry widget is altered.
The snag is that whenever I scan the barcode into the Entry box, I get the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "c:\program files\python37\Lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: 'NoneType' object is not callable
This is my full code. I tried to comment out and explain the process as best I could. Naturally, it won't be able to grab the image files and populate the dictionary with them, but hopefully just looking at it, you'll be able to tell me where I went wrong.
from PIL import Image
from PIL import ImageTk as itk
import tkinter as tk
import cv2
import glob
import os, os.path
# INITIALIZE TKINTER WINDOW #
window = tk.Tk()
window.title('Projector Test')
#window.overrideredirect(1)
# Function to kick off whenever Tkinter Entry value is edited. Grabs value of StringVar and assigns it to variable 'barcode'. Checks to see if the barcode
# value is in 'images' dictionary. If so, grab image and display on Tkinter Canvas. If not, display Error message image.
def barcodeScanImage():
barcode = str(sv.get())
if barcode in images:
image_file = images.get(barcode)
scanImage = itk.PhotoImage(image_file)
width, height = image_file.size
canvas.create_image(0, 0, image = scanImage, anchor = tk.NW)
else:
image_file = images.get('error.gif')
errorImage = itk.PhotoImage(image_file)
width, height = image_file.size
canvas.create_image(0, 0, image = errorImage, anchor = tk.NW)
# Create Dictionary 'images' to store image files in. #
images = {}
# Iterate through projectorImages folder in directory and store each image found there in the 'images' dictionary, with its Key as its filename. #
for filename in os.listdir('projectorImages\\'):
image = Image.open(os.path.join('projectorImages\\', filename))
images[filename] = image
# Create startImage variable. Use .size function to get its width and height, which will be plugged into the tk.Canvas width and height arguments.
# This ensures the displayed image will be displayed in its entirety.
startImage = images.get('start.gif')
width, height = startImage.size
canvas = tk.Canvas(master = window, width = width, height = height)
# Create startImageReady variable reference to the same image file, using the itk.PhotoImage function to convert it into a readable format for Tkinter.
# Then, use canvas.create_image to actually display the image in the Tkinter canvas.
startImageReady = itk.PhotoImage(images.get('start.gif'))
canvas.pack()
canvas.create_image(0, 0, image = startImageReady, anchor = tk.NW)
sv = tk.StringVar()
entry = tk.Entry(master = window, textvariable = sv)
sv.trace("w", callback = barcodeScanImage())
entry.pack()
window.mainloop()
Thanks much for your time. I've been trying to figure out what's causing this issue, but I'm at the end of my beginner's rope! Lol
Consider this line of code:
sv.trace("w", callback = barcodeScanImage())
It is functionally identical to this code:
result = barcodeScanImage()
sv.trace("w", callback=result)
Since barcodeScanImage() returns None, it's the same as this:
sv.trace("w", callback=None)
When you call trace, you must give it a reference to a function (note the missing ()):
sv.trace("w", callback=barcodeScanImage)
However, when you set up a trace, tkinter will pass some additional arguments to the function which you need to be prepared to accept. Since you're not using them, you can just ignore them:
def barcodeScanImage(*args):
...
For more information on the arguments that are passed in, see this question: What are the arguments to Tkinter variable trace method callbacks?

Tkinter Create buttons from list with different image

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()

Resources