PhotoImage is not responding to tkinter Scale widget - python

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.

Related

tkinter strange phenomenon with buttons and images

I don't know what to say because I am clueless on why this is not working.
The first button appears but the image does not the second and third button does not appear.
from tkinter import *
Master = Tk()
Master.geometry("1408x768")
Master.configure(background = "#000000")
# Top
Top = Frame(Master)
Top.configure(background = "#1C1C1C", width = 1024.0, height = 384.0)
Top.place(x = 0.0, y = -5.684341886080802e-14)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Top, background = "#0084FF", image = Nextimg)
Next.place(x = 624.0, y = 551.0, width = 100, height = 50)
# Bottom
Bottom = Frame(Master)
Bottom.configure(background = "#8C8C8C", width = 1024.0, height = 384.0)
Bottom.place(x = 0.0, y = 384.0)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Bottom, background = "#0084FF", image = Nextimg)
Next.place(x = 624.0, y = 551.0, width = 100, height = 50)
# Dashboard
Dashboard = Frame(Master)
Dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
Dashboard.place(x = 1024.0, y = 0.0)
Continueimg = PhotoImage(file = "Continue.png")
Continue = Button(master = Dashboard, background = "#FF8900", image = Continueimg)
Continue.place(x = 1091.0, y = 359.0, width = 250, height = 50)
Your primary issue here appears that you are expecting your widget placement to be relative to the root window but in fact they are relative to the frames they are placed in.
Try this. Change all your buttons X/Y to 20 and see what I mean.
You will see all the buttons show up in the top left corner of each frame they are assigned to.
One issue is that you are using place to manage widgets and this is very bad for code maintenance. It is much better to use grid() and/or pack() to build your windows for several reasons.
Automatic weights to adjust placement based on window size and easy of maintainability as your code grows.
The issue you will see with your 1st and 2nd buttons is the 1st button does not show the image due to the reference being reassigned.
If you want to use the same image for both just save a reference once.
In this case you can remove the 2nd Nextimg = from your code.
See what happens when we change the placement to 20's
from tkinter import *
Master = Tk()
Master.geometry("1408x768")
Master.configure(background = "#000000")
Top = Frame(Master)
Top.configure(background = "#1C1C1C", width = 1024.0, height = 384.0)
Top.place(x = 0.0, y = -5.684341886080802e-14)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Top, background = "#0084FF", image = Nextimg)
Next.place(x = 20, y = 20, width = 100, height = 50)
Bottom = Frame(Master)
Bottom.configure(background = "#8C8C8C", width = 1024.0, height = 384.0)
Bottom.place(x = 0.0, y = 384.0)
# Nextimg = PhotoImage(file = "Next.png") Removed this as it is why your image does not show up on the first button.
Next = Button(master = Bottom, background = "#0084FF", image = Nextimg)
Next.place(x = 20, y = 20, width = 100, height = 50)
Dashboard = Frame(Master)
Dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
Dashboard.place(x = 1024.0, y = 0.0)
Continueimg = PhotoImage(file = "Continue.png")
Continue = Button(master = Dashboard, background = "#FF8900", image = Continueimg)
Continue.place(x = 20, y = 20, width = 250, height = 50)
Master.mainloop()
Note that I replaced your images with grey squares since I did not have your image to use.
Results:
A more conventional way to write this would be to use grid() or pack().
Also I have rewritten the code to more closely fit PEP* standards.
See below example:
import tkinter as tk
master = tk.Tk()
master.geometry("1408x768")
master.configure(background="#000000")
top = tk.Frame(master)
top.grid_propagate(False)
top.configure(background="#1C1C1C", width=1024.0, height=384.0)
top.grid(row=0, column=0)
nextimg = tk.PhotoImage(file="gray.png")
tk.Button(master=top, background="#0084FF", image=nextimg).grid(row=0, column=0)
bottom = tk.Frame(master)
bottom.grid_propagate(False)
bottom.configure(background="#8C8C8C", width=1024.0, height=384.0)
bottom.grid(row=1, column=0)
tk.Button(master=bottom, background="#0084FF", image=nextimg).grid(row=0, column=0)
dashboard = tk.Frame(master)
dashboard.grid_propagate(False)
dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
dashboard.grid(row=0, column=1, rowspan=2)
continueimg = tk.PhotoImage(file="gray.png")
tk.Button(master=dashboard, background="#FF8900", image=continueimg).grid(row=0, column=0)
master.mainloop()
Results:
You can also manage spacing of the widgets from the edges if you like. That can be done by providing a padx/pady parameter to the grid().

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)

Can you rescale a PhotoImage in Python Tkinter?

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

Python 2: Replace word in string with icon image.

I am trying to create a weather application. I have a section of code that retrieved weather information from DarkSkyAPI for the next 7 days. The String that is print out looks like Sat clear-day 10. What I am trying to do is display the text I'm a label but replace the clear-sky section of the string for the clear-sky icon I have.
Format I am looking for is Sat {icon} 10
from Tkinter import *
from datetime import timedelta, datetime
from PIL import Image, ImageTk
import requests
import json
## Location of Icons used
icon_lookup = {
'clear-day' : "Icons/WeatherIcons/WeatherIcon.png",
}
class Weather(Frame):
def __init__(self, parent, *args, **kwargs):
Frame.__init__(self, parent, bg = 'black')
self.iconLbl = Label(self, bg="black")
self.iconLbl.grid(row = 0, column = 1, sticky = W)
self.icon = ''
self.get_weather()
def get_weather(self):
try:
## Get Week's Weather Values
todayPlusOne = 'Sat clear-day 10'
## Get Icon
icon_id = 'clear-day'
icon2 = None
if icon_id in icon_lookup:
icon2 = icon_lookup[icon_id]
if icon2 is not None:
if self.icon != icon2:
self.icon = icon2
image = Image.open(icon2)
image = image.resize((100, 100), Image.ANTIALIAS)
image = image.convert('RGB')
photo = ImageTk.PhotoImage(image)
self.iconLbl.config(image=photo)
self.iconLbl.image = photo
else:
self.iconLbl.config(image='')
except Exception as e:
print "Error: %s. Cannot get weather." % e
self.after(900000, self.get_weather)
class FullscreenWindow:
def __init__(self):
self.tk = Tk()
self.tk.configure(background='black')
self.topFrame = Frame(self.tk, background = 'black', height = 240)
self.middleFrame = Frame(self.tk, background = 'black', height = 240)
self.bottomFrame = Frame(self.tk, background = 'black', height = 240)
self.topFrame.pack(side = TOP, fill = BOTH)
self.middleFrame.pack(side = TOP, fill = BOTH)
self.bottomFrame.pack(side = TOP, fill = BOTH)
self.state = False
self.tk.bind('<Return>', self.toggle_fullscreen)
#weather
self.weather = Weather(self.topFrame)
self.weather.pack(side = LEFT, anchor = NW, padx = 25, pady = 25)
def toggle_fullscreen(self, event=None):
self.state = not self.state
self.tk.attributes('-fullscreen', self.state)
return 'break'
if __name__ == '__main__':
w = FullscreenWindow()
w.tk.resizable(width=False, height=False)
w.tk.geometry('720x480')
w.tk.mainloop()
Is this possible to do?
You cannot put an image in the middle of text in a label; you can only put an image on one side (top, bottom, left, right) or centered. To have both text and an image you need to use the compound attribute of the label.
To have an image in the middle of text, there are at least three options:
create a frame with a label for the text, a label for the icon, and another label for more text
use a canvas, and create text and image items on the canvas
use a one-line text widget, which supports embedded images.

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

Categories