Whenever I try to save an image selected from tkinter, I get an error like this:
raise ValueError("unknown file extension: {}".format(ext)) from e
ValueError: unknown file extension:
I'm using tkinter to open up the file browser to select an image file. The user can choose to flip the image horizontally and vertically. After that, they can choose to save as a variety of image formats. However, this returns the above error. I don't really see what is wrong. The name variable in the save() function contains the name after the file is picked. PIL's save function should be able to take that name and save it in the current working directory.
from tkinter import *
from tkinter import filedialog
from PIL import Image
def open_image():
global img
img = Image.open(
filedialog.askopenfilename(title="Select file", filetypes=(("jpeg files", "*.jpg"), ("all files", "*.*"))))
save_button.config(bg=default_color)
flip_horizontal_button.config(bg=default_color)
flip_vertical_button.config(bg=default_color)
def flip_horizontal():
global img
if img:
img = img.transpose(Image.FLIP_LEFT_RIGHT)
def flip_vertical():
global img
if img:
img = img.transpose(Image.FLIP_TOP_BOTTOM)
def save():
global img
if img:
#os.chdir("/")
default_name = "Untitled"
""" print(default_name+"."+img.format)
print(os.path.isfile(default_name+"."+img.format))
print(os.path)
if os.path.isfile(default_name+"."+img.format):
expand = 1
while True:
expand += 1
expanded_name = default_name+str(expand)
if os.path.isfile(expanded_name):
continue
else:
default_name = expanded_name
break"""
name = filedialog.asksaveasfilename(title="Save As", filetypes=(
('JPEG', ('*.jpg', '*.jpeg', '*.jpe')), ('PNG', '*.png'), ('GIF', '*.gif')),
initialfile=default_name+"."+img.format)
img.save(name)
img = None
root = Tk()
root.title("Meme Deep Fryer")
root.geometry('600x500')
default_color = root.cget('bg')
open_button = Button(text='Open Image', font=('Arial', 20), command=open_image)
flip_horizontal_button = Button(text='Flip Horizontal', font=('Arial', 10), command=flip_horizontal, bg="gray")
flip_vertical_button = Button(text='Flip Vertical', font=('Arial', 10), command=flip_vertical, bg="gray")
save_button = Button(text='Save', font=('Arial', 20), command=save, bg="gray")
open_button.pack(anchor='nw', side=LEFT)
save_button.pack(anchor='nw', side=LEFT)
flip_horizontal_button.pack(anchor='w')
flip_vertical_button.pack(anchor='w')
root.mainloop()
You could pass argument typevariable in asksaveasfilename:
ext = tkinter.StringVar()
name = filedialog.asksaveasfilename(title="Select file", typevariable=ext, filetypes=(('JPEG', ('*.jpg', '*.jpeg', '*.jpe')), ('PNG', '*.png'), ('BMP', ('*.bmp', '*.jdib')), ('GIF', '*.gif')))
if name:
img.save(os.path.basename(name)+"."+ext.get().lower()) # splice the string and the extension.
Related
I am trying to convert colour images into Grayscale images and then save the output.
The code works fine as expected (I am able to import images and perfrom grayscale operations on it)
However, I am unable to save the tk PhotoImage, hence, getting the error
'PhotoImage' object has no attribute 'save'
This is the code
from tkinter import *
from PIL import Image
from PIL import ImageTk
from tkinter import filedialog
import cv2
gray = None
def select_image():
global left_side, right_side
f_types = [
("Jpg Files", "*.jpg"),
("PNG Files", "*.png"),
] # type of files to select
path = filedialog.askopenfilename(multiple=False, filetypes=f_types)
# ensure a file path was selected
if len(path) > 0:
image = cv2.imread(path)
print(path)
global gray
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# convert the images to PIL format...
image = Image.fromarray(image)
gray = Image.fromarray(gray)
# ...and then to ImageTk format
image = ImageTk.PhotoImage(image)
gray = ImageTk.PhotoImage(gray)
# if the sections are None, initialize them
if left_side is None or right_side is None:
# the first section will store our original image
left_side = Label(image=image)
left_side.image = image
left_side.pack(side="left", padx=10, pady=10)
# while the second section will store the edge map
right_side = Label(image=gray)
right_side.image = gray
right_side.pack(side="right", padx=10, pady=10)
# otherwise, update the image sections
else:
# update the sections
left_side.configure(image=image)
right_side.configure(image=gray)
left_side.image = image
right_side.image = gray
# save bw
def savefile():
filename = filedialog.asksaveasfile(mode="w", defaultextension=".jpg")
if not filename:
return
gray.save(filename)
# print(gray)
window = Tk()
window.geometry("1080x720")
window.title("Colored Image To Black & White")
icon = PhotoImage(file="logo1.png")
window.iconphoto(True, icon)
image = PhotoImage(file="logo1.png")
intro = Label(
window,
text="This program converts Colored Images into Grayscale Images",
font=("Comic Sans", 20,),
fg="#FF6405",
bg="black",
compound="right",
)
intro.pack()
left_side = None
right_side = None
btn = Button(
window,
text="select image",
command=select_image,
font=("Comic Sans", 30),
fg="red",
bg="black",
)
btn1 = Button(
window,
text="select image",
command=savefile,
font=("Comic Sans", 30),
fg="red",
bg="black",
)
img = PhotoImage(file="select.png")
btn.config(image=img)
btn.pack(side="left", fill="both", expand="no", padx="10", pady="10")
img2 = PhotoImage(file="save.png")
btn1.config(image=img2)
btn1.pack(side="right", fill="both", expand="no", padx="10", pady="10")
window.config(background="#FF6405")
# window.resizable(False, False)
window.mainloop()
I tried to make a pop-up save qr code image window using asksaveasfile, but it gives me a corrupted image file.
This is what happens if you open it
My code:
root = Tk()
root.geometry("400x350")
root.title("QR Code Generator")
label = Label(text="QR Code Generator", fg="black", font="NexaHeavy")
label.pack(pady=20)
link_entry = Entry()
link_entry.insert(0, 'Link')
link_entry.pack(pady=50)
name_entry = Entry()
name_entry.insert(0, 'QR Code name')
name_entry.pack(pady=50)
def savefile():
asksaveasfile(defaultextension='*.jpg', filetypes=[
("All types", '.*'),
("JPG File", ".jpg")
])
# Generate QR Code
def make():
qr_code = qrcode.make(link_entry.get())
qr_codename = (f"{name_entry.get()}.png")
qr_code.save(qr_codename, savefile())
make_button = Button(text="Make QR Code", command=make)
make_button.pack(pady=1)
root.mainloop()
If you want to use asksaveasfile() to select the output filename, you should not construct the filename using qr_codename = (f"{name_entry.get()}.png"):
def savefile():
# return the selected file object
return asksaveasfile(defaultextension='*.jpg', filetypes=[
("All types", '.*'),
("JPG File", ".jpg")
], mode="wb") # must use binary mode for image data
# Generate QR Code
def make():
qr_codename = savefile()
if qr_codename:
qr_code = qrcode.make(link_entry.get())
qr_code.save(qr_codename)
I have bunch of links and labels in list to view those I want to make a image viewer with next, previous functionality. My links and labels for respective images are in list. Up to now I had tried this :
urls=[url,url1,url2,url3]
labels=["label 1","label 2","label 3","label 4"]
images=[]
for ur in urls:
raw_data = urllib.request.urlopen(ur).read()
im = Image.open(io.BytesIO(raw_data))
image = ImageTk.PhotoImage(im)
images.append(image)
Now I have images ready in images and now I want to display it in image viewer but only last image is visible in image viewer.
Label(root).grid(row=1,column=1)
for i in range(len(images)):
image_label=Label(root,image=images[i])
image_label.grid(row=1,column=2)
name=Label(root,text=labels[i])
name.grid(row=2,column=2)
def left():
image_label=Label(root,image=images[i-1])
image_label.grid(row=1,column=2)
def right():
image_label=Label(root,image=images[i+1])
image_label.grid(row=1,column=2)
left_button=Button(root,text="Left",command=left)
left_button.grid(row=2,column=1)
right_button=Button(root,text="Right",command=right)
right_button.grid(row=2,column=3)
Right button is not working and left button is working but for one time only. When I click left button second time then nothing is working.
Error while clicking in right button :
line 45, in right
image_label=Label(root,image=images[i+1])
IndexError: list index out of range
The code is pretty close to original posted example.
There is no lambda in Button, just straight forward command that gives access to left and right functions.
The name label is unnecessary as label objects already have a text attribute so I've integrated image and text into single label and increased the font size.
label compound is used to control where the image is display relative to the text, I've chosen top. That is, the image will be place above the text.
The use of global in functions keeps track of count
from tkinter import Tk, Label, Frame, Button
from PIL import ImageTk, Image
import io
import requests
import urllib
root = Tk()
urls = [url, url1, url2, url3]
labels = ["label 1","label 2","label 3","label 4"]
count = -1
images = []
for ur in urls:
raw_data = urllib.request.urlopen( ur ).read()
im = Image.open(io.BytesIO( raw_data ))
image = ImageTk.PhotoImage(im)
images.append(image)
def left( ):
global count
count = (count - 1) % len(images)
image_label.config(image = images[count], text = labels[count])
def right( ):
global count
count = (count + 1) % len(images)
image_label.config(image = images[count], text = labels[count])
image_label = Label(
root, image = images[count], font = "Helvetica 20 normal",
text = labels[count], compound = "top")
image_label.grid(row = 0, column = 0, columnspan = 2, sticky = "nsew")
Button(
root, text = "<< LEFT", command = left).grid( row = 1, column = 0, sticky = "ew")
Button(
root, text = "RIGHT >>", command = right).grid(row = 1, column = 1, sticky = "ew")
right( ) # display first image
root.mainloop()
Here's a way to do it by showing and hiding pairs of Labels with the image and it name on them. For development and testing purposes, it loads the images from disk instead of downloading them, but you can easily replace that with what you have in your question.
from pathlib import Path
from PIL import Image, ImageTk
from tkinter import Button, Tk, Label
root = Tk()
root.title('Image Viewer')
# Create thumbnails of all the images.
image_folder = '/path/to/image/folder'
images = []
for filepath in Path(image_folder).iterdir():
img = Image.open(filepath)
w, h = img.size
if w > h:
f = 100 / w
nw, nh = 100, int(f*h)
else:
f = 100 / h
nw, nh = int(f*w), 100
# Offsets to center thumbnail within (100, 100).
woff, hoff = (100-nw) // 2, (100-nh) // 2
img = img.resize((nw, nh), Image.BICUBIC) # Shrink so largest dimen is 100.
thumbnail = Image.new('RGBA', (100, 100), (255, 255, 255, 0)) # Blank.
thumbnail.paste(img, (woff, hoff)) # Offset keep it centered.
images.append(ImageTk.PhotoImage(thumbnail))
names = ['Name 1', 'Name 2', 'Name 3', 'Name 4']
def get_images():
"""Create all image and name Label pairs but initially hidden."""
global image_labels, name_labels
image_labels, name_labels = [], []
for i, (img, name) in enumerate(zip(images, names)):
image_label = Label(root, image=img)
image_label.grid(row=1, column=2)
image_labels.append(image_label)
name_label = Label(root, text=name)
name_label.grid(row=2, column=2)
name_labels.append(name_label)
hide(i)
def hide(i):
"""Hide specified image and name pair."""
image_labels[i].grid_remove()
name_labels[i].grid_remove()
def show(i):
"""Unhide specified image and name pair."""
image_labels[i].grid()
name_labels[i].grid()
def shift(direction):
"""Display the image and pair before or after the current one that is
`direction` steps away (usually +/-1).
"""
global current
hide(current)
current = (current + direction) % len(images)
show(current)
left_button = Button(root, text="Left", command=lambda: shift(-1))
left_button.grid(row=2, column=1)
right_button = Button(root, text="Right", command=lambda: shift(+1))
right_button.grid(row=2, column=3)
get_images()
current = 0 # Currently displayed image.
show(current)
root.mainloop()
Screenshot of it running:
Should just update a image label when shift left or right, not create a new label.
Using modulus % to find which one next turn.
To reduce code and executable, I use the image data in PySimpleGUI and simple name for image here.
import tkinter as tk
import PySimpleGUI as sg
def shift(num):
global index
index = (index + num) % size
label1.configure(image=images[index])
label2.configure(text =labels[index])
root = tk.Tk()
images = [tk.PhotoImage(data=image) for image in sg.EMOJI_BASE64_HAPPY_LIST]
size = len(images)
labels = [f'Image {i:0>2d}' for i in range(size)]
index = 0
label1 = tk.Label(root, image=images[index])
label1.grid(row=1, column=2)
label2 = tk.Label(root, text=labels[index])
label2.grid(row=2, column=2)
button1 = tk.Button(root, text="<LEFT", width=6, anchor='center', command=lambda num=-1:shift(num))
button1.grid(row=1, column=1)
button2 = tk.Button(root, text="RIGHT>", width=6, anchor='center', command=lambda num=+1:shift(num))
button2.grid(row=1, column=3)
root.mainloop()
You can try this :
from tkinter import Tk,Label,Button
from PIL import ImageTk,Image
import io
import requests
import urllib
root=Tk()
count=0 # To know the current image and label
urls=[url,url1,url2,url3]
labels=["label 1","label 2","label 3","label 4"]
images=[]
for ur in urls:
raw_data = urllib.request.urlopen(ur).read()
im = Image.open(io.BytesIO(raw_data))
image = ImageTk.PhotoImage(im)
images.append(image)
def change(direction): # Function to change image
global count
if direction=="left":
if count<=0:
count=len(urls)-1
else:
count-=1
else:
if count>=len(urls)-1:
count=0
else:
count+=1
name.config(text=labels[count])
image_label.config(image=images[count])
Label(root).grid(row=1,column=1)
image_label=Label(root,image=images[count])
image_label.grid(row=1,column=2)
left_button=Button(root,text="Left",command=lambda : change("left"))
left_button.grid(row=2,column=1)
name=Label(root,text=labels[count])
name.grid(row=2,column=2)
right_button=Button(root,text="Right",command=lambda : change("right"))
right_button.grid(row=2,column=3)
root.mainloop()
I would like to dynamically display image of selected item in listbox.
The name of the image store in folder is exactly like item with index [0] from my tuple in listbox
list1= Listbox(ViewFrame, height=15, width=75)
files = glob.glob('img\\*.jpg')
ImageFrame = LabelFrame(page1, text="Podgląd i parametry")
ImageFrame.grid(row=6, column=6, pady=10, padx=5)
path = files[list1.curselection()[0]]
img = ImageTk.PhotoImage(Image.open(path))
label = Label(ImageFrame)
label.image = img
label.configure(image=img)
Error:
path = files[list1.curselection()[0]]
IndexError: tuple index out of range
It seems to me that before I open the app nothing is selected, but I do not know how to fix it.
check is something is selected before load images.
when create the listbox add
list1.bind("<<ListboxSelect>>", on_item_selected)
then add the function
def (on_item_selected):
path = files[list1.curselection()[0]]
img = ImageTk.PhotoImage(Image.open(path))
label = Label(ImageFrame)
label.image = img
label.configure(image=img)
on open....
if list1.curselection():
path = files[list1.curselection()[0]]
img = ImageTk.PhotoImage(Image.open(path))
label = Label(ImageFrame)
label.image = img
label.configure(image=img)
Here's runnable code, but it's merely a more complete version of #1966bc's answer which I created because you have in your question isn't a MCVE:
import glob
from tkinter import *
from PIL import Image, ImageTk
def on_item_selected(event):
path = files[list1.curselection()[0]]
img = ImageTk.PhotoImage(Image.open(path))
label.image = img
label.configure(image=img)
root = Tk()
page1 = Frame(root)
page1.grid(row=0, column=0)
ViewFrame = Frame(page1)
ViewFrame.grid(row=0, column=0)
files = glob.glob('*.jpg')[:10] # Limit to first 10 for development.
listvar = StringVar(value=files)
list1= Listbox(ViewFrame, height=15, width=75, listvariable=listvar)
list1.grid()
ImageFrame = LabelFrame(page1, text="Podgląd i parametry")
ImageFrame.grid(row=6, column=6, pady=10, padx=5)
label = Label(ImageFrame) # Create placeholder.
label.grid()
list1.bind("<<ListboxSelect>>", on_item_selected)
root.mainloop()
I want to print the mean, height & width of an image in python openCV. Where i used two button (get photo and analysis image) and different GUI,one for getting the photo(def openphoto(): ) and another for printing those features(def feature(): ). But I'm getting error.
N.B. full code is too long.so, i used some part of it.
I've tried it in python openCV.
import tkinter as tk
from tkinter.filedialog import askopenfilename
import shutil
import os
from PIL import Image, ImageTk
window = tk.Tk()
window.title("Dr. Papaya")
window.geometry("500x510")
window.configure(background ="lightgreen")
title = tk.Label(text="Click below to choose picture for testing disease....", background = "lightgreen", fg="Brown", font=("", 15))
title.grid()
def feature():
window.destroy()
window1 = tk.Tk()
window1.title(" ")
window1.geometry("650x510")
window1.configure(background="lightgreen")
def exit():
window1.destroy()
#i want to print here
print("Mean : ",mean)
print("Heigth : ",heigth)
print("Width : ",width)
button = tk.Button(text="Exit", command=exit)
button.grid(column=0, row=9, padx=20, pady=20)
window1.mainloop()
def openphoto():
import cv2
import numpy as np
fileList = os.listdir(dirPath)
for fileName in fileList:
os.remove(dirPath + "/" + fileName)
fileName = askopenfilename(initialdir='', title='Select image for analysis ',
filetypes=[('image files', '.jpg')])
dst = " "
shutil.copy(fileName, dst)
load = Image.open(fileName)
#calculate the mean
mean=np.mean(load)
#calculate the height & width
height = np.size(load, 0)
width = np.size(load, 1)
render = ImageTk.PhotoImage(load)
img = tk.Label(image=render, height="250", width="500")
img.image = render
img.place(x=0, y=0)
img.grid(column=0, row=1, padx=10, pady = 10)
title.destroy()
button1.destroy()
button2 = tk.Button(text="Analyse Image", command=feature)
button2.grid(column=0, row=2, padx=10, pady = 10)
button1 = tk.Button(text="Get Photo", command = openphoto)
button1.grid(column=0, row=1, padx=10, pady = 10)
window.mainloop()
The variables are not in scope when you try to print them. This is an important programming principle so I suggest you read this introduction
You can make the variable global to make the them accessible outside of the function:
def openphoto():
global width, height, mean
[rest of code]