I'm having a problem that when i try to create an image on the canvas i am unable to produce the image. However before my after() loop is initiated i am able to create and configure my images. Also, i am able to remove objects from my canvas using canvas.delete() in my after() loop so i do still have some level of control.
I am on windows 8.1, using Python 3.5.4
import tkinter as tk
from PIL import Image, ImageTk
from math import floor
import numpy as np
from scipy import ndimage
root = tk.Tk()
HEIGHT = 600
WIDTH = 600
CANVAS_MID_X = WIDTH / 2
CANVAS_MID_Y = HEIGHT / 2
def my_mainloop():
#THIS WORKS!!! REMOVES THE DIAL CREATED BEFORE THE MAINLOOP
canvas.delete(dial_1)
#THIS WORKS BELOW BEFORE MAINLOOP, BUT NOT IT WONT WORK! (use opencv?)
img = dial_1_img_resized
img2 = img.rotate(45, expand=True)
dial_1_photo_new = ImageTk.PhotoImage(img2)
dial_2 = canvas.create_image((dial_1_center), image=dial_1_photo_new, anchor=tk.E)
'''CANT DRAW TO CANVAS IN AFTER LOOP'''
print("loop!")
root.after(4000,my_mainloop)
'''-------------------Create Canvas, and starting dials in their starting positions---------------------'''
canvas = tk.Canvas(root, width=HEIGHT, height=WIDTH, bg="black")
canvas.grid(row=0, column=0)
dial_1_path = "gauge1.png"
dial_1_width = 400
dial_1_img = Image.open(dial_1_path, 'r') #open image
dial_1_img_ratio = int(dial_1_img.size[1]) / int(dial_1_img.size[0])
dial_1_img_resized = dial_1_img.resize((dial_1_width, floor(dial_1_img_ratio * dial_1_width)), 1)
dial_1_photo = ImageTk.PhotoImage(dial_1_img_resized)
dial_1_center = (CANVAS_MID_X, CANVAS_MID_Y)
#CREATE DIAL ON CANVAS, THIS WORKS!!
dial_1 = canvas.create_image((dial_1_center), image=dial_1_photo)
'''Start Main Loop'''
root.after(0, my_mainloop)
root.mainloop()
Therefore my question is: is there a way to manipulate and create canvas images in the after() loop? (called my_mainloop) any help is appreciated!
You'll need to save a reference to your photo because it is being garbage collected after my_mainloop runs. You can add it to your canvas object for instance:
canvas.dial_1_photo_new = ImageTk.PhotoImage(img2)
dial_2 = canvas.create_image((dial_1_center), image=canvas.dial_1_photo_new, anchor=tk.E)
Related
I'm trying to make a video player that play's a video full screen. The user is also able to toggle onto another frame that I want to take up the full GUI and to be able to toggle in between them.. I have written some code and It works fine when I pack the video player to root but then when I put it inside a frame it shrinks down to a much smaller size than the GUI. Why is this happening and how can I fix it?
This is the code that has the video playing and resizing to the GUI size dynamically
import datetime
import tkinter as tk
from tkinter import *
from turtle import width
from tkVideoPlayer import TkinterVideo
#changing the visible frame on the screen
def showFrame (frame):
frame.tkraise()
root = tk.Tk()
root.title("Hology")
width1, height1= int(root.winfo_screenwidth()), int(root.winfo_screenheight())
root.geometry(str(width1-200) + "x" + str(height1-200))
vid_player = TkinterVideo(master=root, scaled=True)
# vid_player.set_size((width1-200, height1-200))
vid_player.pack(expand=True, fill=BOTH)
progress_value = tk.IntVar(root)
vid_player.bind("<<SecondChanged>>", loopVid)
load_file= "hol.mp4"
vid_player.load(load_file)
vid_player.play()
# vid_player.pack(expand=True, fill="both")
holo.tkraise()
root.mainloop()
This is where it is placed inside a frame
import datetime
import tkinter as tk
from tkinter import *
from turtle import width
from tkVideoPlayer import TkinterVideo
global vidVal
vidVal = 0
#changing the visible frame on the screen
def showFrame (frame):
frame.tkraise()
root = tk.Tk()
root.title("Hology")
width1, height1= int(root.winfo_screenwidth()), int(root.winfo_screenheight())
root.geometry(str(width1-200) + "x" + str(height1-200))
holo = Frame(root)
timeline = Frame(root)
holo.grid(row=0,column=0)
timeline.grid(row=0,column=0)
# root.attributes('-fullscreen',True)
# for frame in (holo, timeline):
# frame.master.geometry(str(width1-200) + "x" + str(height1-200))
# # frame.grid(row=1, column=1, columnspan=10, rowspan=8, sticky=tk.EW)
# # frame.pack_propagate(False)
# frame.pack()
vid_player = TkinterVideo(master=holo, scaled=True)
# vid_player.set_size((width1-200, height1-200))
vid_player.pack(expand=True, fill=BOTH)
load_file= "hol.mp4"
vid_player.load(load_file)
vid_player.play()
# vid_player.pack(expand=True, fill="both")
holo.tkraise()
root.mainloop()
Any help enabling me to keep the video at full size when inside a frame would be greatly appreciated. I have spent some time looking through the documentation and I know it will be a simple fix or something silly, I'm just stuck! :(
Thank you
I'm making a hangman-like game; for that, I need all the different stages of the man to be visualized, hence images. I want the entire tkinter window to be an image, when I change the size it pushes the image right.
from tkinter import *
root=Tk()
root.geometry("600x350")
canvas = Canvas(root, width=1600, height=900)
canvas.pack()
img = PhotoImage(file="4.png")
canvas.create_image(470,190, image=img, )
root.mainloop()
If canvas is bigger than window then when you resize then it show more canvas but and it can looks like it moves image.
But if you use smaller canvas then pack() will try to keep centered horizontally. And if you add pack(expand=True) then it will try to keep it centered vertically.
In example code I added red background to window to show where is canvas
import tkinter as tk # PEP8: import *
root = tk.Tk()
root.geometry("600x350")
root['bg'] = 'red'
img = tk.PhotoImage(file="lenna.png")
canvas = tk.Canvas(root, width=600, height=350)
canvas.pack(expand=True)
canvas.create_image(300, 175, image=img)
root.mainloop()
Image Lenna from Wikipedia
PEP 8 -- Style Guide for Python Code
Before resizing:
After resizing:
If you want to draw only image then you could use Label(image=img)
import tkinter as tk # PEP8: import *
root = tk.Tk()
root.geometry("600x350")
root['bg'] = 'red'
img = tk.PhotoImage(file="lenna.png")
label = tk.Label(root, image=img)
label.pack(expand=True)
root.mainloop()
Before resizing:
After resizing:
BTW:
tkinter can bind() some function to event <Configure> and it will execute this function everytime when you resize window (and/or move window) - and this function may also move or resize image in window.
import tkinter as tk # PEP8: import *
from PIL import Image, ImageTk
def resize(event):
global img
lower = min(event.width, event.height)
#print(event.width, event.height, 'lower:', lower)
new_image = original_image.resize((lower, lower))
img = ImageTk.PhotoImage(new_image) # need to assign to global variable because there is bug in PhotoImage
label['image'] = img
# --- main ---
root = tk.Tk()
root.geometry("350x350")
root['bg'] = 'red'
original_image = Image.open("lenna.png")
img = ImageTk.PhotoImage(original_image)
label = tk.Label(root, image=img)
label.pack(expand=True)
root.bind('<Configure>', resize)
root.mainloop()
Before (it resized image at start to fit window):
After (it resized image to fit window):
I'm developing a GUI in Tkinter and want to apply animation in the below GIF on the image when it appears.
Here is my code,
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
frame = Frame(root)
frame.pack()
canvas = Canvas(frame, width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
canvas.pack()
background = PhotoImage(file="background.png")
canvas.create_image(300,300,image=background)
my_pic = PhotoImage(file="start000-befored.png")
frame.after(1000, lambda: (canvas.create_image(50,50,image=my_pic, anchor=NW))) #and on this image, I want to give the effect.
root.mainloop()
Instead of clicking on the play button as shown in GIF, the image should automatically appears after 1 second like this animation and stays on screen. (No closing option).
I'm not 100% sure I understood the problem, but I'll describe how to animate an image.
Tkinter does not contain functions for animating images so you'll have to write them yourself. You will have to extract all subimages, subimage duration and then build a sequencer to swap subimages on your display.
Pillow can extract image sequences. WEBP images seems to only support one frame duration whereas GIF images may have different frame duration for each subimage. I will use only the first duration for GIF images even if there is many. Pillow does not support getting frame duration from WEBP images as far as I have seen but you gan read it from the file, see WebP Container Specification.
Example implementation:
import tkinter as tk
from PIL import Image, ImageTk, ImageSequence
import itertools
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
filename = 'images/animated-nyan-cat.webp'
pil_image = Image.open(filename)
no_of_frames = pil_image.n_frames
# Get frame duration, assuming all frame durations are the same
duration = pil_image.info.get('duration', None) # None for WEBP
if duration is None:
with open(filename, 'rb') as binfile:
data = binfile.read()
pos = data.find(b'ANMF') # Extract duration for WEBP sequences
duration = int.from_bytes(data[pos+12:pos+15], byteorder='big')
# Create an infinite cycle of PIL ImageTk images for display on label
frame_list = []
for frame in ImageSequence.Iterator(pil_image):
cp = frame.copy()
frame_list.append(cp)
tkframe_list = [ImageTk.PhotoImage(image=fr) for fr in frame_list]
tkframe_sequence = itertools.cycle(tkframe_list)
tkframe_iterator = iter(tkframe_list)
def show_animation():
global after_id
after_id = root.after(duration, show_animation)
img = next(tkframe_sequence)
display.config(image=img)
def stop_animation(*event):
root.after_cancel(after_id)
def run_animation_once():
global after_id
after_id = root.after(duration, run_animation_once)
try:
img = next(tkframe_iterator)
except StopIteration:
stop_animation()
else:
display.config(image=img)
root.bind('<space>', stop_animation)
# Now you can run show_animation() or run_animation_once() at your pleasure
root.after(1000, run_animation_once)
root.mainloop()
There are libraries, like imgpy, which supports GIF animation but I have no experience in usig any such library.
Addition
The duration variable sets the animation rate. To slow the rate down just increase the duration.
The simplest way to put the animation on a canvas it simply to put the label on a canvas, see example below:
# Replace this code
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
# with this code
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack(padx=10, pady=10)
display = tk.Label(canvas)
window = canvas.create_window(250, 250, anchor='center', window=display)
Then you don't have to change anything else in the program.
I have a code that is working perfectly but it's not giving me transparent background, (here is the image ) after a research on web, I found the solution by using canvas widget, we can us images with transparent background.
Here is my code,
import tkinter as tk
from PIL import Image, ImageTk
def work(progress=1):
if progress > 300: # define the width by yourself
return
tmp_images = ImageTk.PhotoImage(progress_images.resize((progress, 10))) # the image size
lb.image = tmp_images # keep reference
lb["image"] = tmp_images # change the image
root.add = root.after(100, work, progress+10) # define the amplitude by yourself
root = tk.Tk()
progress_images = Image.open("path.png")
lb = tk.Label(root, bg="black")
lb.pack(side="left")
work()
root.mainloop()
but I am confused how to change Label widget into Canvas ? can anyone help me please ? I am noob in Tkinter still!!!
You can use canvas.create_text(...) to replace the labels and then use canvas.itemconfig(...) to update the labels. I got this from an other stack over flow question
reference link :-
add label ..
Below is a modified code using Canvas:
import tkinter as tk
from PIL import Image, ImageTk
def work(progress=10):
if progress > 300: # define the width by yourself
return
canvas.image = ImageTk.PhotoImage(progress_images.resize((progress, 30))) # the image size
canvas.itemconfig(pbar, image=canvas.image) # update image item
root.add = root.after(100, work, progress+10) # define the amplitude by yourself
root = tk.Tk()
progress_images = Image.open("path.png")
canvas = tk.Canvas(root, width=300, height=30, bg='black', highlightthickness=0)
canvas.pack()
pbar = canvas.create_image(0, 0, anchor='nw') # create an image item
work()
root.mainloop()
I would like to give layers to tkinter images when we created them below code:
from tkinter import *
canvas_width = 300
canvas_height =300
master = Tk()
canvas = Canvas(master,
width=canvas_width,
height=canvas_height)
canvas.pack()
mylist = []
img = PhotoImage(file="./Images/screwsmall.png")
mylist.append (canvas.create_image(20,20, anchor=NW, image=img))
img_2 = PhotoImage(file="./Images/screwsmall.png")
mylist.append (canvas.create_image(50,20, anchor=NW, image=img_2))
mainloop()
As you can see two images are created, however the second image is on top of first image. How can I decide their layers by the code? In other words can I make the first image go on top of other with out creating order?
Use the tag_lower() and tag_raise() methods for the Canvas object:
canvas.tag_raise(mylist[0]) # move the first image to the top