Using a generated image in a tkinter GUI with PIL - python

I am trying to use a tkinter button to display a ERD diagram using sqlalchemy and PIL.
I have managed to get this to work by saving the picture generated to a file and then re-opening that file in order to display in a label.
Is there a way to get the image to display without having to save it first?
import tkinter as tk
from sqlalchemy_schemadisplay import create_schema_graph
import urllib
from sqlalchemy import MetaData, create_engine
from PIL import Image, ImageTk
root = tk.Tk()
def erd_gen(pr):
global erd_img
conn = create_engine("mssql+pyodbc:///?odbc_connect={}".format(pr))
grph_data = MetaData(bind=conn)
graph = create_schema_graph(metadata=grph_data, show_datatypes=False, show_indexes=False, rankdir='LR', concentrate=False)
graph.write_png('dbschema.png') # write file to folder
erd_img = ImageTk.PhotoImage(Image.open("dbschema.png")) #open file from folder, would like to do this by just referring to 'graph' and not the saved file
panel.configure(image = erd_img)
conn.close()
params = urllib.parse.quote_plus("DRIVER={SQL Server Native Client 11.0};"
"SERVER=(localdb)\ProjectsV13;"
"DATABASE=Test;"
"Trusted_Connection=yes")
tk.Button(root, text='Generate', command=lambda: erd_gen(params)).pack()
panel = tk.Label(root)
panel.pack()
root.mainloop()

You can use create_png() instead of write_png() to create a PNG image data buffer, then use io.BytesIO to simulate a file input stream:
from io import BytesIO
def erd_gen(pr):
global erd_img
conn = create_engine("mssql+pyodbc:///?odbc_connect={}".format(pr))
grph_data = MetaData(bind=conn)
graph = create_schema_graph(metadata=grph_data, show_datatypes=False, show_indexes=False, rankdir='LR', concentrate=False)
iostream = BytesIO(graph.create_png())
erd_img = ImageTk.PhotoImage(file=iostream)
panel.configure(image=erd_img)
conn.dispose()

You can save it to a file-like object with io, see example:
from tkinter import *
from PIL import Image, ImageTk
import urllib.request
import io
root = Tk()
show = Label(root)
show.pack()
img_url = 'http://hem.bredband.net/b235373/kroppkakor/DSCF0002.JPG'
with urllib.request.urlopen(img_url) as url:
temp_file = io.BytesIO(url.read())
img = Image.open(temp_file)
tkimg = ImageTk.PhotoImage(image=img)
show.config(image=tkimg)
root.mainloop()

Related

How can I close an image in a tkinter window without closing the window?

I am writing an application that consists of three tkinter windows on the same page: a calendar, a notepad and a 'picture of the day' which is fetched from a bank of images when initiated by the user. The image is named after the calendar date + .jpg. It works fine...the first time. When I select another date and retrieve the image for that date, it comes up behind the first image in the 'picture of the day' window. The problem is that the first image does not disappear when replaced by another. I do not want to close the window, just close the current picture and replace it by the new one. There might a simple way, but I spent hours searching for it. The code below of part of the application. Hopefully, it shows where the problem is. Can you point me in the right direction? Thanks
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog
import os
photo = Toplevel()
photo.geometry("300x250+1300+150")
photo.resizable(width=True, height=True)
photo.attributes("-topmost", True)
def openfn(): # To go fetch a new image
filename = filedialog.askopenfilename(title='open')
return filename
root.destroy()
def open_img(): # To open a new image
x = openfn()
img = Image.open(x)
img = img.resize((225,200), Image.ANTIALIAS)
im = ImageTk.PhotoImage(img)
panel = Label(photo, image=im)
panel.image = im
panel.pack()
img=img.save(cal.calphoto) # Saves the image (calphoto is date + .jpg)
def retrieve_photo(): # To open an existing image
img=Image.open(cal.calphoto)
im = ImageTk.PhotoImage(img)
panel = Label(photo, image=im)
panel.image = im
panel.pack()
I changed a few things in your code. The main thing is the take the label with the image into the global scope so the function open_img() can make changes to it instead of creating a new label each time it is called. Below open_img() now configures the label to show the image selected each time it is called. (This is basically illustrating what Delrius said.)
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog
import os
photo = Toplevel()
photo.geometry("300x250+1300+150")
photo.resizable(width=True, height=True)
photo.attributes("-topmost", True)
panel = Label(photo) # panel Label moved into global scope so the
# functions below can access it
def openfn():
filename = filedialog.askopenfilename(title='open')
return filename
root.destroy()
def open_img():
x = openfn()
img = Image.open(x)
img = img.resize((225,200), Image.ANTIALIAS)
im = ImageTk.PhotoImage(img)
panel.config(image=im) # panel label is configured to show image selected
panel.image = im
panel.pack()

minsize() and maxsize() methods doesn't work in tkinter python(Toplevel)

I was recently trying to make a pdf viewer in python, so I set the minsize() and maxsize() of the window but it doesn't seem to work. Here is my code.
from pdf2image import convert_from_path
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
root4=tk.Toplevel()
root4.geometry("800x900")
root4.title("PDF Viewer")
root4.configure(bg="#202020")
root4.minsize(300, 200)
root4.maxsize(900, 800)
pdf_frame = tk.Frame(root4)
pdf_frame.pack(fill=tk.BOTH,expand=1)
scrol_y = tk.Scrollbar(pdf_frame,orient=tk.VERTICAL)
pdf = tk.Text(pdf_frame,yscrollcommand=scrol_y.set,bg="grey")
scrol_y.pack(side=tk.RIGHT,fill=tk.Y)
scrol_y.config(command=pdf.yview)
pdf.pack(fill=tk.BOTH,expand=1)
pages = convert_from_path(filedialog.askopenfilename(initialdir = "/",title = "Select file",filetypes = (("pdf files","*.pdf"),("all files","*.*"))),size=(800,900))
photos = []
for i in range(len(pages)):
photos.append(ImageTk.PhotoImage(pages[i]))
for photo in photos:
pdf.image_create(tk.END,image=photo)
pdf.insert(tk.END,'\n\n')
root4.update()
root4.mainloop()
When your creating a parent window use Tk(), else using Toplevel() will create a Tk()(for which the minsize and maxsize doesnt apply) window without you asking for it and you mightve misunderstood that for your root4 window, so just say:
tk.Tk().withdraw()
root4 = tk.Tk()
This fixed the issue of two windows popping up, for me. Keep in mind the minsize() and maxsize() works, its just that you were trying it on the wrong window.

How do I display and image from a data url in python?

I have an image that's encoded as a data url.
How do I display the original image from this in python?
You can use Tkinter to open a window for viewing the image and urllib to read the image data e.g;
import io
import base64
try:
import Tkinter as tk
from urllib2 import urlopen
except ImportError:
import tkinter as tk
from urllib.request import urlopen
root = tk.Tk()
image_url = "data:image/png;base64,iVB........"
image_byt = urlopen(image_url).read()
image_b64 = base64.encodestring(image_byt)
photo = tk.PhotoImage(data=image_b64)
cv = tk.Canvas(bg='white')
cv.pack(side='top', fill='both', expand='yes')
cv.create_image(10, 10, image=photo, anchor='nw')
root.mainloop()

Reading an image from a URL, resizing it and applying antialiasing

I'm trying to resize and apply antialiasing to an image I previously displayed in Tkinter. I'm reading it from a url. The problem is, I opened the image with tkinter.PhotoImage, and the resize() function I need is in PIL.Image. I'd like to know if there's a way to convert from one to another, or some other way I can resolve this issue.
Here's the code:
import tkinter
from urllib.request import urlopen
import base64
from PIL import Image
window = tkinter.Tk()
url = "https://s.yimg.com/os/weather/1.0.1/shadow_icon/60x60/partly_cloudy_night#2x.png"
b64_data = base64.encodestring(urlopen(url).read())
image = tkinter.PhotoImage(data=b64_data)
# Function I need:
#image = image.resize((100, 100), Image.ANTIALIAS)
label = tkinter.Label(image=image)
label.pack()
window.mainloop()
If there's a completely different way I can achieve this, I'd like to hear it.
Ok, well you first use PIL and then use PIL's TKinter Format to convert it to a TKinter Image. I don't have the urllib on my system so I've used Requests, but that part should be exchangeable.
import tkinter
import base64
from PIL import Image, ImageTk
import requests
from PIL import Image
from StringIO import StringIO
url = "https://s.yimg.com/os/weather/1.0.1/shadow_icon/60x60/partly_cloudy_night#2x.png"
r = requests.get(url)
pilImage = Image.open(StringIO(r.content))
pilImage.resize((100, 100), Image.ANTIALIAS)
window = tkinter.Tk()
image = ImageTk.PhotoImage(pilImage)
label = tkinter.Label(image=image)
label.pack()
window.mainloop()
There is one whole page dedicated for PIL and Tkinter: http://effbot.org/tkinterbook/photoimage.htm
I edited #user1767754 code since I had some problems with it, but it did help me greatly.
Note: I used BytesIO instead of StringIO. Also, I added image mode to 'RGBA' since I have problems when displaying grey-scale images. Also, minor fixes.
Code:
import tkinter
from PIL import Image, ImageTk
from io import BytesIO
import requests
window = tkinter.Tk()
url = "https://s.yimg.com/os/weather/1.0.1/shadow_icon/60x60/partly_cloudy_night#2x.png"
r = requests.get(url)
pilImage = Image.open(BytesIO(r.content))
pilImage.mode = 'RGBA'
pilImage = pilImage.resize((50, 50), Image.ANTIALIAS)
image = ImageTk.PhotoImage(pilImage)
label = tkinter.Label(image=image)
label.pack()
window.mainloop()

Not being able to load an image in python

I am trying to load an image into a canvas in python. However I am getting the error: TclError: couldn't recognize data in image file "C:\testimage\spongesea.jpg"
import Tkinter
from Tkinter import *
import time
import inspect
import os
from PIL import Image, ImageTk
class game_one:
def __init__(self):
global root
global canvas_one
root = Tk()
root.title(" Thanks Josh Dark
canvas_one = Tkinter.Canvas(root, bg="BLACK")
canvas_one.pack(expand= YES, fill= BOTH)
canvas_one.focus_set() #allows keyboard events
p = PhotoImage(file="C:\testimage\spongesea.jpg")
canvas_one.create_image(0, 0, image = p, anchor=NW)
root.grab_set()#I forget what this does. Don't change it.
root.lift()#This makes root appear in front of the other applications
ObjectExample = game_one()# starts the animation
I can open the image manually from the file, so it is not corrupted, and it is calling the correct place. Any ideas? Thanks
PhotoImage works only with GIF and PGM/PPM.
You have to use Image, ImageTk to work with other formats
from PIL import Image, ImageTk
image = Image.open("lenna.jpg")
photo = ImageTk.PhotoImage(image)
BTW: read PhotoImage and see "Garbage Collection problem" in note.

Categories