Can you rescale a PhotoImage in Python Tkinter? - python

In my program, I have two buttons. button1 calls a function (bt1) which displays an image in the window and then deletes both buttons. button2 calls a function(bt2) I would like this button to display the same image as button1 but by half the scale factor. I am thinking something like this:
scale_half_img = PhotoImage(file = 'Image.png', scale = 0.5)
Of course, this doesn't work but this is the sort of thing I am looking for.
Full code:
from tkinter import *
window = Tk()
def bt1():
img = PhotoImage(file = 'Image.png')
imglbl = Label(window, image = img, anchor = 'nw')
imglbl.place(x = 0, y = 0, width = 865, height = 800)
button1.destroy()
button2.destroy()
def bt2():
# display scaled down image
button1.destroy()
button2.destroy()
button1 = Button(window, text = 'Display Image', command = bt1)
button1.place(x = 10, y = 10, width = 200, height = 30)
button2 = Button (window, text = 'Display Rescaled Image', command = bt2)
button2.place(x = 10, y = 50, width = 200, height = 30)
window.mainloop()

Use PIL.Image to resize an image in tkinter. You can do it as follows:
from PIL import Image, ImageTk
img = Image.open('<Path to Image>')
img = img.resize((img.size[0]/2, img.size[1]/2), Image.ANTIALIAS)
resized_image = ImageTk.Photoimage(img) # use this

Related

Can't properly set an image as canvas background in tkinter

I want my Tkinter window to have four buttons, each with its own background image, disposed as a 2x2 grid. I tried using frames but you can't set an image as frame background so I had to switch to Canvas. This is the code for one of the four blocks.
from tkinter import *
from PIL import ImageTk, Image
root = Tk()
root.geometry("500x200")
root.wm_title("KnowYourArt")
styles_bg = ImageTk.PhotoImage(Image.open("images/styles_bg.png"))
canvas_styles = Canvas(root)
bg = canvas_styles.create_image(100, 100, image=styles_bg)
width = 4
height = 2
Button(canvas_styles, text="Styles", width=width, height=height).pack(side=TOP)
Label(canvas_styles, text="Gain info about styles").pack(side=BOTTOM)
canvas_styles.grid(row=0, column=0)
This is the output I get:
In my intentions, the background image dimensions should cover both the button and the label, and the image's dimensions should be independent from the dimensions of the widgets. Instead, I obtain a canvas where the height of the BG image is the same as the height of the button, and the width is the same as the label's width. I tried many alternative possibilities, including setting a width and height for the canvas, and none of them properly worked. What am I doing wrong?
I managed to find a solution. Here is the code:
from tkinter import *
from PIL import ImageTk, Image
width = 4
height = 1
def add_canvas(frame, img, x, y, text):
c = Canvas(frame, width=300, height=300)
c.focus()
c.pack(fill=BOTH, expand=True)
bg = c.create_image(x, y, image=img)
btn = Button(frame, text="Go!", width=width, height=height)
c.create_text(150, 190, text=text, font=('Helvetica', 15), fill='white')
c.create_window(100, 200, anchor="nw", window=btn)
return c
root = Tk()
root.geometry("600x600")
root.wm_title("Title")
f_1 = Frame(root)
f_2 = Frame(root)
f_3 = Frame(root)
f_4 = Frame(root)
bg1 = ImageTk.PhotoImage(Image.open("images/im1.png"))
bg2 = ImageTk.PhotoImage(Image.open("images/im2.png"))
bg3 = ImageTk.PhotoImage(Image.open("images/im3.png"))
bg4 = ImageTk.PhotoImage(Image.open("images/im4.jpg"))
canvas_1 = add_canvas(f_1, bg1, 0, 0, "foo")
canvas_2 = add_canvas(f_2, bg2, 240, 240, "foo")
canvas_3 = add_canvas(f_3, bg3, 120, 120, "foo")
canvas_4 = add_canvas(f_4, bg4, 100, 100, "foo")
f_1.grid(row=0, column=0)
f_2.grid(row=0, column=1)
f_3.grid(row=1, column=0)
f_4.grid(row=1, column=1)
And here's the output

Tkinter frame expand is only displaying half my window

I'm trying to create a phone in tkinter and i'm using frames but the image is being cut in half. Does anyone know why?
https://i.stack.imgur.com/mOtEn.png
import tkinter as tk
def raiseframe(frame):
frame.tkraise()
root = tk.Tk()
bgImage = tk.PhotoImage(file="Phone.gif")
WALogo = tk.PhotoImage(file="whatsappLogo.gif")
width = bgImage.width()
height = bgImage.height()
root.title("Phone")
root.geometry("%dx%d+0+0" % (width, height))
main = tk.Frame()
whatsapp = tk.Frame()
for frame in (main, whatsapp):
frame.pack(fill = tk.BOTH, expand = True)
#Mainscreen:
canvas = tk.Canvas(main, width = width, height = height, bg = "black")
canvas.create_image((width / 2, height / 2), image = bgImage)
canvas.place(x = 0, y = 0)
WAB = tk.Button(main, image = WALogo, command = lambda: raiseframe(whatsapp))
WAB.place(x = 35, y = 85)
raiseframe(main)
root.mainloop()
I believe you have added the picture into a frame which only takes up up to half of the screen.
You could either:
change the dimensions of the frame
add the picture to root like this:
canvas = tk.Canvas(root, width = width, height = height, bg = "black")
canvas.create_image((width / 2, height / 2), image = bgImage)

PhotoImage is not responding to tkinter Scale widget

I have a python script which allows a user to open up an image in a tkinter canvas widget. The program is printing out the size that the new image should be, but only the original image shows up. How do I refresh the canvas with the new size.
Here is the python code:
from Tkinter import *
import tkFileDialog
from PIL import ImageTk, Image
factor = 1.0
width_org = 500
height_org = 500
mGui = Tk()
mGui.geometry("600x600")
mGui.configure(background = 'yellow')
frame = Frame(mGui, width = 400, height = 400)
frame.pack(anchor = 'nw')
frame.configure(background = 'red')
canvasframe = Canvas(frame, width = 400, height = 400, scrollregion = (-500,-500,500,500))
hbar = Scrollbar(frame,orient=HORIZONTAL)
hbar.pack(side=BOTTOM, fill = X)
hbar.configure(command= canvasframe.xview)
vbar = Scrollbar(frame,orient = VERTICAL )
vbar.pack(side=RIGHT, fill = Y)
vbar.configure(command= canvasframe.yview)
canvasframe.configure(xscrollcommand= hbar.set, yscrollcommand= vbar.set)
canvasframe.pack( expand = True, fill = BOTH)
pil_img = Image.open("rose-flower-500x500.jpg")
img = ImageTk.PhotoImage(pil_img)
def openit():
in_path = tkFileDialog.askopenfilename()
try:
pil_img = Image.open(in_path)
except IOError:
pass
width_org, height_org = pil_img.size
try:
img = ImageTk.PhotoImage(pil_img)
except IOError:
pass
canvasframe.create_image(20,20, image = img)
canvasframe.img = img
valueList = [25, 50, 100, 150, 200]
def valuecheck(value):
newval = min(valueList, key=lambda x:abs(x-float(value)))
scalepic.set(newval)
factor = newval/100.0
w = int(width_org*factor)
h = int(height_org*factor)
print (w, h)
pil_img = pil_img.resize((w,h),Image.ANTIALIAS)
img = ImageTk.PhotoImage(pil_img)
canvasframe.create_image(20,20, image =img)
openpic = Button(mGui, text = "Open", command = openit).pack()
scalelabel = Label(mGui, text = "Resize Image").pack()
scalepic = Scale(mGui, from_=min(valueList), to=max(valueList), command=valuecheck, orient = HORIZONTAL)
scalepic.set(100)
scalepic.pack()
mGui.mainloop()
Also, how can I open a new image? I'm thinking of a for loop and some kind of dispose method, but I'm not sure about the syntax.
EDIT
Part of code in classes
from Tkinter import *
import tkFileDialog
from PIL import ImageTk, Image
factor = 1.0
width_org = 500
height_org = 500
class MainApp(Frame):
def createControls(self):
frame = Frame(self, width = 600, height = 500)
frame.configure(background = 'red')
frame.pack(anchor = 'nw')
canvasframe = Canvas(frame, width = 600, height = 500, scrollregion = (-600,-500,600,500))
canvasframe.configure(xscrollcommand= hbar.set, yscrollcommand= vbar.set)
canvasframe.pack(expand = True, fill = BOTH)
hbar = Scrollbar(frame, orient=HORIZONTAL)
hbar.pack(side=BOTTOM, fill=X)
hbar.configure(command= canvasframe.xview)
vbar = Scrollbar(frame, orient=VERTICAL)
vbar.pack(side=RIGHT, fill = Y)
vbar.configure(command= canvasframe.yview)
def __init__(self, parent):
Frame.__init__(self, parent, width = 800, height = 600, background = 'yellow')
self.pack()
self.createControls()
root = Tk()
app = MainApp(parent =root)
app.mainloop()
You forgot to save the reference to the new PhotoImage in canvasframe.img, so it gets garbage collected when valuecheck returns.
Also, rather than creating a new Canvas image with create_image for the rescaled image you should simply update the existing one via itemconfig. To do that, you need to save the item id number returned by create_image. Eg, in openit, do
canvasframe.imgid = canvasframe.create_image(20,20, image = img)
And then in valuecheck, after you create the new PhotoImage do
canvasframe.img = img
canvasframe.itemconfig(canvasframe.imgid, image=img)
You don't have to store img and imgid as attributes of canvasframe, but it's convenient in this case. It would get messy though if you had lots of things on the canvas that you had to track. But for larger Tkinter programs it's a good idea to put everything into one or more classes. It makes things more organized, and easier to access.

PIL/tkinter - updating label image with button images

I'm trying to implement my simple image viewer, where I have one main label and menubutton with three choices. Each of them creates different numbers of buttons with pictures inside them. I would like create one main label and then update its image with images on the buttons. So when I click on the first button with image, that image will appear on the label, then click second button and that second image will appear and so on. I've tried to create two functions one for creating label and updating its image and the second for getting current image on the button, but I wasn't able to do the second one correctly. Appreciate your help. Here's the code:
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
from functools import partial
from PIL import Image, ImageTk
class Halabala():
def __init__(self):
self.master = tk.Tk()
self.master.geometry("1100x700")
self.pictures = ["pavuk1.gif", "pavuk2.gif", "pavuk3.gif"]
self.pictures2 = ["cukor1.gif", "cukor2.gif", "cukor3.gif", "cukor4.gif", "cukor5.gif"]
self.lists_images = []
self.lists_images2 = []
self.init_image_list()
self.lists_labels = []
#self.main_label = tk.Label(self.master, image = None).grid(row = 0, column = 0)
#image with original size
self.rbutton = tk.Menubutton(self.master, text = "Choose picture")
self.picks2 = tk.Menu(self.rbutton)
self.rbutton.config(menu=self.picks2)
self.picks2.add_command(label = "Spider", command = partial(self.create_labels,3))
self.picks2.add_command(label = "Sugar", command = partial(self.create_labels,5))
self.rbutton.config(width = 30, bg = "white", bd = 5, relief = tk.RAISED)
self.rbutton.place(x = 900, y = 30)
self.master.mainloop()
def init_image_list(self):
for i in self.pictures:
picture = Image.open(i)
picture.thumbnail((130, 130))
self.lists_images.append(ImageTk.PhotoImage(picture))
for i in self.pictures2:
picture = Image.open(i)
picture.thumbnail((130, 130))
self.lists_images2.append(ImageTk.PhotoImage(picture))
def create_labels(self, num):
for label in self.lists_labels:
label.destroy()
self.lists_labels=[]
for i in range(num):
if num == 3:
but = tk.Button(self.master, image = self.lists_images[i]) #command = self.get_command
but.grid(row = 2, column = i + 1) #label will be in the first row
self.lists_labels.append(but)
else:
but = tk.Button(self.master, image = self.lists_images2[i])
but.grid(row = 2, column = i + 1)
self.lists_labels.append(but)
#def get_command(self):
#self.main_label = tk.Label(self.master, image = self.get_image).grid(row = 0, column = 0)
#def get_image(self):
# don't know how to get current image from button
myapp = Halabala()

How to update an image on a Canvas?

This is the essence of the code I'm having trouble with:
camelot = Canvas(main, width = 400, height = 300)
camelot.grid(row = 0, column = 0, rowspan = 11, columnspan = 3)
MyImage = PhotoImage(file = "sample1.gif")
camelot.create_image(0, 0, anchor = NW, image = MyImage)
This is run at the beginning. What I want to do on later in another function is replace "sample1.gif" by "sample2.gif", and, possibly later on, replace that in turn by "sample3.gif". I'm stuck and nothing I've been trying has worked so far.
Adding image to canvas:
self.image_on_canvas = self.canvas.create_image(0, 0, image = ...)
Changing image on canvas:
self.canvas.itemconfig(self.image_on_canvas, image = ...)
Full example:
from Tkinter import *
#----------------------------------------------------------------------
class MainWindow():
#----------------
def __init__(self, main):
# canvas for image
self.canvas = Canvas(main, width=60, height=60)
self.canvas.grid(row=0, column=0)
# images
self.my_images = []
self.my_images.append(PhotoImage(file="ball1.gif"))
self.my_images.append(PhotoImage(file="ball2.gif"))
self.my_images.append(PhotoImage(file="ball3.gif"))
self.my_image_number = 0
# set first image on canvas
self.image_on_canvas = self.canvas.create_image(0, 0, anchor='nw', image=self.my_images[self.my_image_number])
# button to change image
self.button = Button(main, text="Change", command=self.onButton)
self.button.grid(row=1, column=0)
#----------------
def onButton(self):
# next image
self.my_image_number += 1
# return to first image
if self.my_image_number == len(self.my_images):
self.my_image_number = 0
# change image
self.canvas.itemconfig(self.image_on_canvas, image=self.my_images[self.my_image_number])
#----------------------------------------------------------------------
root = Tk()
MainWindow(root)
root.mainloop()
Images used in example:
ball1.gif ball2.gif ball3.gif
Result:
MyImage = PhotoImage(file = "sample1.gif")
labelorbuttontodisplayit.image = MyImage
labelorbuttontodisplayit.configure(image=MyImage)
:P that should do it. I only tried to use that code on label or buttons, never as a Canvas, but i guess you can adapt that piece of code a bit.

Categories