can't draw tkinter image in function [duplicate] - python

This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 6 years ago.
i'm making a monopoly game, & i am trying to draw image on canvas, but it will only work if not in function:
def make_image(root, location, canvas):
photo = PhotoImage(file = root)
canvas.create_image(location["X"],location["Y"], image = photo, anchor = "nw")
class something():
def start(self, controller):
self.controller = controller
#photo = PhotoImage(file = "googolopoly.png")
#self.canvas.create_image(0,0, image = photo, anchor = "nw")
make_image("googolopoly.png", {"X":0,"Y":0}, self.canvas)
make_text(self.canvas, "MONOPOLY!!!!", {"X":1050,"Y":20})
make_button(self.main_tk, self.canvas, "roll dice", lambda: self.roll_dice(), {"X":1100, "Y":50}, 100)
for i in range(controller.player_number):
self.players.append(make_text(self.canvas, str(i+1), {"X":902+i*10, "Y":946}))
self.main_tk.mainloop()
currently it won't draw a picture, but if i get down the comments it will work (no function)
it also happens after main loop, when i want to draw players
i really need it as a function. what to do? if you need i can put some more code

You were missing this line of code: myCanvas.image = photo.
And even if it would be easier to draw the image on a Label, with this code you can do it on a Canvas with the function make_image():
from Tkinter import *
def make_image(filename, location, canvas):
photo = PhotoImage(file=filename)
myCanvas.image = photo
myCanvas.create_image(0,0, image = photo, anchor = "nw")
root = Tk()
myCanvas = Canvas(root, width=100, height=100)
myCanvas.grid()
make_image("image.gif", (5,5,95,95), myCanvas)
root.mainloop()

Related

Image not seen in Tkinter (python) [duplicate]

This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 2 years ago.
I am starting to program, in which I have to make a python graphical interface, in which by means of a button the jpg file is chosen and it is displayed in the interface, but I have had a problem, since the image is not displayed and in the terminal it does not detect any error and practically I go crazy here I leave the code
from PIL import Image, ImageTk
from tkinter import Tk, Frame, Button, Label, Text, filedialog, PhotoImage
class Application_BotonPath(Frame):
def __init__(self, master = None):
super().__init__(master, width = "1300", height = "950", bg = "old lace")
self.master = master
self.pack()
self.Panel()
self.widget()
def Panel(self):
self.frame_side = Frame(self, width = '300', height = '850', bg = 'sky blue').place(x = 20, y = 50)
self.frame_show = Frame(self, width = '900', height = '850').place(x = 360, y = 50)
def widget(self):
boton = Button(self.frame_side, text = "Abrir Imagen", command = self.cargar_imagen).place(x = 85, y = 60, width = 150, height = 30)
salida = Text(self.frame_side, state = "disable").place(x = 43, y = 110, width = 250, height = 700)
def cargar_imagen(self):
self.ruta_imagen = filedialog.askopenfilename(title = "Abrir", filetypes = [('Archivo png', '*.png'), ('Archivo jpeg', '*.jpg')])
load = Image.open(self.ruta_imagen)
imagen = ImageTk.PhotoImage(load)
label = Label(self.frame_show, image = imagen)
label.place(x=0, y=0)
root = Tk()
root.wm_title("Detector de Caracteres")
app = Application_BotonPath(root)
app.mainloop()
image
This is what I get, the gray box that is in the upper right I suppose it is the image, but it does not show it. please help
Welcome to SO.
The image is created in a function, and when the function ends the reference to the image is garbage collected. Therefore the Label can not find any image.
You can save a reference to the image in the label object:
label = Label(self.frame_show, image = imagen)
label.image = imagen # Save reference to image
or you can make the reference an attribute of the instance:
self.imagen = ImageTk.PhotoImage(load)
label = Label(self.frame_show, image = self.imagen)

performing operations on images uploaded by tkinter

I am new to Tkinter and python. I am trying to upload two images and then perform some operations on them. The problem is that the Window class is loading all at once or the code in running parallel, so the images uploaded after that have been already assigned to None since they were uploaded later in the ScrollableFrame class and did not have a value earlier.
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk, ImageOps
import os
# ************************
# Scrollable Frame Class
# ************************
row=0
column=0
imagePaths = []
#Class to generate a frame to add to the GUI with vertical and horizontal scroll bars
class ScrollableFrame(Frame):
#The Constructor method for the class
def __init__(self, parent , *args, **kw):
Frame.__init__(self, parent, *args, **kw)
#Defining the position of the frame grid
self.grid(row = row , column = column)
self.image = None
self.imageFile = None
#Defining the vertical scroll bar
vscrollbar = Scrollbar(self, orient=VERTICAL)
vscrollbar.grid(row=row, column=column+1, sticky=N+S)
#Defining the horizontal scroll bar
hscrollbar = Scrollbar(self, orient = 'horizontal')
hscrollbar.grid(row=row+1, column=column, sticky=E+W)
#Defining the canvas to put the scroll bars on
canvas = Canvas(self, bd=0, highlightthickness=0, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set)
canvas.grid(row=row, column=column, sticky = N+S+E+W)
canvas.config( width=800, height = 800 )
#Defining the scrolling commands (vertically and horizontally )
vscrollbar.config(command=canvas.yview)
hscrollbar.config(command=canvas.xview)
#Defining the scroll region where the scrolling is active
canvas.config(scrollregion= (0,0,1280,1024))
self.canvas = canvas
def openImage(self):
#Getting the path of the image
imageFile = filedialog.askopenfilename(initialdir=os.getcwd(),title="Select BMP File",filetypes=[("BMP Files",("*.bmp",".png",".jpg",".jpeg",".tif",".tiff"))])
#Assigning the image value to this frame object
self.imageFile = imageFile
if not imageFile:
return
def showImage(self):
#Getting the path of the image
imageFile = filedialog.askopenfilename(initialdir=os.getcwd(),title="Select BMP File",filetypes=[("BMP Files",("*.bmp",".png",".jpg",".jpeg",".tif",".tiff"))])
#Assigning the image value to this frame object
self.imageFile = imageFile
if not imageFile:
return
#Checking for the extension of the image
filename, file_extension = os.path.splitext(imageFile)
#If it is a .bmp, this means that it is an HD image, where we can directly display it
if file_extension == '.bmp':
imageToDisplay = Image.open(imageFile)
#border = (0, 0, 0, 66) #Decide on the area you want to crop in terms of no. pixels: left, up, right, bottom
#ImageOps.crop(imageToDisplay, border)
img = ImageTk.PhotoImage(imageToDisplay)
self.image = img
#print ("Done conversion")
self.canvas.create_image(row, column, image=self.image, anchor=NW)
class Window(Frame):
def __init__(self, master=None):
global row, column,imagePaths
Frame.__init__(self, master)
self.master = master
self.pos = []
self.master.title("BMP Image GUI")
self.pack(fill=BOTH, expand=1)
self.label = Label(self, text="Instructions: \n 1. Open the HD image. \n 2. Open the EBSD image. \n 3. Open the Color Map image.", anchor=W, justify=LEFT)
self.label.place(x=1640, y=0)
menu = Menu(self.master)
self.master.config(menu=menu)
self.frame1 = ScrollableFrame(self)
row=0
column=1
self.frame2 = ScrollableFrame(self)
# File Bar
file = Menu(menu)
file.add_command(label="Open HD image", command=self.frame1.showImage)
img = Image.open("original.bmp")
HD = self.frame2.imageFile
file.add_command(label="Open EBSD image", command=self.frame2.openImage)
EBSD = self.frame2.imageFile
print (HD)
print (EBSD)
root = tk.Tk()
root.geometry("%dx%d" % (1670, 1024))
root.title("BMP Image GUI")
app = Window(root)
app.pack(fill=tk.BOTH, expand=1)
#print (HD)
root.mainloop()
So printing the HD and EBSD images is giving None. What I am aiming to to make them get the actual value assigned after the upload.
This is a lot of code and it doesn't run. It also has a few problems. When you are coding complex applications it is best to do it one little piece at a time or you'll have problems finding the problems. Here are a few:
Don't use global variables in an object oriented application. The names row, column and imagePaths should belong to either of the two classes.
The menu doesn't work because you have not implemented it correctly:
file = Menu(menu)
menu.add_cascade(label='File', menu=file) # You need this for it to work
file.add_command(label="Open HD image", command=self.frame1.showImage)
# etc...
You are packing app twice, once in it's __init__() function and once after it's been created (in the global scope).
The scrollable frames are packed in front of the Label with instructions so you can't see it.
Try fixing these problems by writing components, and when each component works then combine them. If there is a problem with any of the components, or if everything works but for one thing, come back here and we will be able to give you a better answer.

How to insert an image using Canvas in Tkinter?

I am trying to insert an image in my python application using Canvas in tkinter. The code for the same is:
class Welcomepage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
canvas = tk.Canvas(self, width = 1000, height = 1000, bg = 'blue')
canvas.pack(expand = tk.YES, fill = tk.BOTH)
image = tk.PhotoImage(file="ice_mix.gif")
canvas.create_image(480, 258, image = image, anchor = tk.NW)
The image is getting read from the source but still not getting displayed in the frame.I am new to the GUI programming someone please help me out.
The likely issue here is that the image is being garbage collected by Python and therefore not being displayed - which is what #nae's comment is suggesting. Attaching it to the self reference will stop it from being garbage collected.
self.image = tk.PhotoImage(file="ice_mix.gif") # Use self.image
canvas.create_image(480, 258, image = self.image, anchor = tk.NW)
The Tkinter Book on effbot.org explains this:
Note: When a PhotoImage object is garbage-collected by Python (e.g.
when you return from a function which stored an image in a local
variable), the image is cleared even if it’s being displayed by a
Tkinter widget.
To avoid this, the program must keep an extra reference to the image
object. A simple way to do this is to assign the image to a widget
attribute, like this:
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
If You still need to use a Canvas instead a Label for image placement inside a function or a method You can use an external link for image, and use a global specificator for this link inside a function.
You may need to use SE anchor, not NW.
This code works successfully (gets an OpenCV image from USB-camera and place it in a Tkinter Canvas):
def singleFrame1():
global imageTK # declared previously in global area
global videoPanel1 # also global declaration (initialized as "None")
videoCapture=cv2.VideoCapture(0)
success,frame=videoCapture.read()
videoCapture.release()
vHeight=frame.shape[0]
vWidth=frame.shape[1]
imageRGB=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) # OpenCV RGB-image
imagePIL=Image.fromarray(imageRGB) # PIL image
imageTK=ImageTk.PhotoImage(imagePIL) # Tkinter PhotoImage
if videoPanel1 is None:
videoPanel1=Canvas(root,height=vHeight,width=vWidth) # root - a main Tkinter object
videoPanel1.create_image(vWidth,vHeight,image=imageTK,anchor=SE)
videoPanel1.pack()
else:
videoPanel1.create_image(vWidth,vHeight,image=imageTK,anchor=SE)
About
Seems like a program that showcase a GIF. I think it is better to "fix" the code for you including step by step tutorial since you look like a beginner.
Fix
Okay, so here is your code.
class Welcomepage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
canvas = tk.Canvas(self, width = 1000, height = 1000, bg = 'blue')
canvas.pack(expand = tk.YES, fill = tk.BOTH)
image = tk.PhotoImage(file="ice_mix.gif")
canvas.create_image(480, 258, image = image, anchor = tk.NW)
First of all, you never noticed the fact that you didn't add pass to the first class.
class Welcomepage(tk.Frame):
pass
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
canvas = tk.Canvas(self, width = 1000, height = 1000, bg = 'blue')
canvas.pack(expand = tk.YES, fill = tk.BOTH)
image = tk.PhotoImage(file="ice_mix.gif")
canvas.create_image(480, 258, image = image, anchor = tk.NW)
The second fact is that since you used the double quote you should be keep using them, because then you can be a great developer. I experienced such a mess as a Python stack dev :(
class Welcomepage(tk.Frame):
pass
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
canvas = tk.Canvas(self, width = 1000, height = 1000, bg = "blue")
canvas.pack(expand = tk.YES, fill = tk.BOTH)
image = tk.PhotoImage(file="ice_mix.gif")
canvas.create_image(480, 258, image = image, anchor = tk.NW)
And try using the following code for your line 8~9.
self.image = tk.PhotoImage(file="ice_mix.gif")
canvas.create_image(480, 258, image = self.image, anchor = tk.NW)
I've also noticed that instead of your space, it is supposed to be this.
class Welcomepage(tk.Frame):
pass
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
canvas = tk.Canvas(self, width = 1000, height = 1000, bg = "blue")
canvas.pack(expand = tk.YES, fill = tk.BOTH)
self.image = tk.PhotoImage(file="ice_mix.gif")
canvas.create_image(480, 258, image = image, anchor = tk.NW)
Hope you enjoyed and have a nice day!

Tkinter resize background image to window size

Trying to set up a background for my tkinter window. I have a square background image, which fades to black around the edges, and then the main window has a black background. The image is placed over the background, and if the window is wider than it is tall, the image centers itself in the middle over the black background, and it all looks very nice.
However when the window is smaller than the image in width and height, it puts the center of the image in the center of the window, so you don't see the whole image, and it looks a little odd. Is there a way of resizing the image so that if the largest of the width and height of the window is smaller than the image, the image is adjusted to that size, keeping aspect ratio.
So say the background image is 600x600:
In a 800x400 window, the image does not resize, and centers itself vertically.
In a 500x400 window, the image resizes to 500x500, and still centers itself vertically.
In a 400x900 window, the image does not resize, and centers itself horizontally.
The centering functionality is already there, I just need the resize functionality.
Currently what I have is:
from tkinter import *
root = Tk()
root.title("Title")
root.geometry("600x600")
root.configure(background="black")
background_image = PhotoImage(file="Background.gif")
background = Label(root, image=background_image, bd=0)
background.pack()
root.mainloop()
Not sure if there is a way of doing this in tkinter? Or if perhaps I would write my own function that resizes the image according to the window size, however the image needs to resize relatively smoothly and quickly if the user resizes the window at any point.
This is example application that uses Pillow to resize image on the Label as the label changes size:
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.title("Title")
root.geometry("600x600")
root.configure(background="black")
class Example(Frame):
def __init__(self, master, *pargs):
Frame.__init__(self, master, *pargs)
self.image = Image.open("./resource/Background.gif")
self.img_copy= self.image.copy()
self.background_image = ImageTk.PhotoImage(self.image)
self.background = Label(self, image=self.background_image)
self.background.pack(fill=BOTH, expand=YES)
self.background.bind('<Configure>', self._resize_image)
def _resize_image(self,event):
new_width = event.width
new_height = event.height
self.image = self.img_copy.resize((new_width, new_height))
self.background_image = ImageTk.PhotoImage(self.image)
self.background.configure(image = self.background_image)
e = Example(root)
e.pack(fill=BOTH, expand=YES)
root.mainloop()
This is how it works using Lenna image as example:
I have modified the above code so it is not in a class
#!/usr/bin/python3.5
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk
root = Tk()
root.title("Title")
root.geometry('600x600')
def resize_image(event):
new_width = event.width
new_height = event.height
image = copy_of_image.resize((new_width, new_height))
photo = ImageTk.PhotoImage(image)
label.config(image = photo)
label.image = photo #avoid garbage collection
image = Image.open('image.gif')
copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
label = ttk.Label(root, image = photo)
label.bind('<Configure>', resize_image)
label.pack(fill=BOTH, expand = YES)
root.mainloop()
Just sugesting a slight change in the answer. Using self.master.winfo_width(),self.master.winfo_height() instead of 'event' makes he adjustment to size much quicker.
import tkinter as tk
from PIL import Image, ImageTk
class Layout:
def __init__(self,master):
self.master = master
self.rootgeometry()
self.canvas = tk.Canvas(self.master)
self.canvas.pack()
self.background_image = Image.open('image_file.PNG')
self.image_copy = self.background_image.copy()
self.background = ImageTk.PhotoImage(self.background_image)
self.loadbackground()
def loadbackground(self):
self.label = tk.Label(self.canvas, image = self.background)
self.label.bind('<Configure>',self.resizeimage)
self.label.pack(fill='both', expand='yes')
def rootgeometry(self):
x=int(self.master.winfo_screenwidth()*0.7)
y=int(self.master.winfo_screenheight()*0.7)
z = str(x) +'x'+str(y)
self.master.geometry(z)
def resizeimage(self,event):
image = self.image_copy.resize((self.master.winfo_width(),self.master.winfo_height()))
self.image1 = ImageTk.PhotoImage(image)
self.label.config(image = self.image1)
root = tk.Tk()
a = Styling.Layout(root)
root.mainloop()
i have created function for calling resize a single time with methods after et after cancel
def on_resize(self, evt):
if self.inter == 0:
self.inter = 1
self.minuteur = self.fenetrePrincipale.after(100, self.endResize)
else:
self.minuteur = self.fenetrePrincipale.after_cancel(self.minuteur)
self.minuteur = self.fenetrePrincipale.after(100, self.endResize)
def endResize(self):
self.inter = 0
self.fenetrePrincipale.background = self.fenetrePrincipale.background.resize((self.fenetrePrincipale.winfo_width(), self.fenetrePrincipale.winfo_height()))
self.pixi = ImageTk.PhotoImage(self.fenetrePrincipale.background)
self.canvas.configure(width=self.fenetrePrincipale.winfo_width(), height=self.fenetrePrincipale.winfo_height())
self.canvas.create_image(0, 0, anchor=NW, image=self.pixi)
Here is the principle, after defines a timer and a function to be recalled at the end, after_cancel cleans the timer so each iteration of the function cleans the timer and starts it, at the last iteration of resize the timer remains triggered.
for more information on cancel and timer with after:
after detailled

Tkinter custom window

I want to make a window in Tk that has a custom titlebar and frame. I have seen many questions on this website dealing with this, but what I'm looking for is to actually render the frame using a canvas, and then to add the contents to the canvas. I cannot use a frame to do this, as the border is gradiented.
According to this website: http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.create_window-method, I cannot put any other canvas items on top of a widget (using the create_window method), but I need to do so, as some of my widgets are rendered using a canvas.
Any suggestions on how to do this? I'm clueless here.
EDIT: Bryan Oakley confirmed that rendering with a canvas would be impossible. Would it then be possible to have a frame with a custom border color? And if so, could someone give a quick example? I'm sort of new with python.
You can use the canvas as if it were a frame in order to draw your own window borders. Like you said, however, you cannot draw canvas items on top of widgets embedded in a canvas; widgets always have the highest stacking order. There is no way around that, though it's not clear if you really need to do that or not.
Here's a quick and dirty example to show how to create a window with a gradient for a custom border. To keep the example short I didn't add any code to allow you to move or resize the window. Also, it uses a fixed color for the gradient.
import Tkinter as tk
class GradientFrame(tk.Canvas):
'''A gradient frame which uses a canvas to draw the background'''
def __init__(self, parent, borderwidth=1, relief="sunken"):
tk.Canvas.__init__(self, parent, borderwidth=borderwidth, relief=relief)
self._color1 = "red"
self._color2 = "black"
self.bind("<Configure>", self._draw_gradient)
def _draw_gradient(self, event=None):
'''Draw the gradient'''
self.delete("gradient")
width = self.winfo_width()
height = self.winfo_height()
limit = width
(r1,g1,b1) = self.winfo_rgb(self._color1)
(r2,g2,b2) = self.winfo_rgb(self._color2)
r_ratio = float(r2-r1) / limit
g_ratio = float(g2-g1) / limit
b_ratio = float(b2-b1) / limit
for i in range(limit):
nr = int(r1 + (r_ratio * i))
ng = int(g1 + (g_ratio * i))
nb = int(b1 + (b_ratio * i))
color = "#%4.4x%4.4x%4.4x" % (nr,ng,nb)
self.create_line(i,0,i,height, tags=("gradient",), fill=color)
self.lower("gradient")
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.wm_overrideredirect(True)
gradient_frame = GradientFrame(self)
gradient_frame.pack(side="top", fill="both", expand=True)
inner_frame = tk.Frame(gradient_frame)
inner_frame.pack(side="top", fill="both", expand=True, padx=8, pady=(16,8))
b1 = tk.Button(inner_frame, text="Close",command=self.destroy)
t1 = tk.Text(inner_frame, width=40, height=10)
b1.pack(side="top")
t1.pack(side="top", fill="both", expand=True)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Here is a rough example where the frame, titlebar and close button are made with canvas rectangles:
import Tkinter as tk
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
# Get rid of the os' titlebar and frame
self.overrideredirect(True)
self.mCan = tk.Canvas(self, height=768, width=768)
self.mCan.pack()
# Frame and close button
self.lFrame = self.mCan.create_rectangle(0,0,9,769,
outline='lightgrey', fill='lightgrey')
self.rFrame = self.mCan.create_rectangle(760,0,769,769,
outline='lightgrey', fill='lightgrey')
self.bFrame = self.mCan.create_rectangle(0,760,769,769,
outline='lightgrey', fill='lightgrey')
self.titleBar = self.mCan.create_rectangle(0,0,769,20,
outline='lightgrey', fill='lightgrey')
self.closeButton = self.mCan.create_rectangle(750,4,760, 18,
activefill='red', fill='darkgrey')
# Binds
self.bind('<1>', self.left_mouse)
self.bind('<Escape>', self.close_win)
# Center the window
self.update_idletasks()
xp = (self.winfo_screenwidth() / 2) - (self.winfo_width() / 2)
yp = (self.winfo_screenheight() / 2) - (self.winfo_height() / 2)
self.geometry('{0}x{1}+{2}+{3}'.format(self.winfo_width(),
self.winfo_height(),
xp, yp))
def left_mouse(self, event=None):
obj = self.mCan.find_closest(event.x,event.y)
if obj[0] == self.closeButton:
self.destroy()
def close_win(self, event=None):
self.destroy()
app = Application()
app.mainloop()
If I were going to make a custom GUI frame I would consider creating it with images,
made with a program like Photoshop, instead of rendering canvas objects.
Images can be placed on a canvas like this:
self.ti = tk.PhotoImage(file='test.gif')
self.aImage = mCanvas.create_image(0,0, image=self.ti,anchor='nw')
More info →here←

Categories

Resources