Timer that prints elapsed time and resets with a button click - python

I have a GUI where the user can click a button named "next set" that allows them to move onto the next task. I wanted to add a timer that starts as soon as they start the application and run the timer until they press the button "next set". When clicked, I want the time elapsed to print and the timer to restart until they press "next set" button again. I would like the timer to start automatically when the code runs. Currently, the "next set" button has two actions, one is to retrieve the next set of images and the other action I am trying to include is to reset the timer and print time elapsed. I also only included part of the code that felt relevant because it is long.
import time
import tkinter as tk
import csv
from pathlib import Path
import PIL.Image
import PIL.ImageDraw
import PIL.ImageTk
MAX_HEIGHT = 500
IMAGES_PATH = Path("Images")
CSV_LABELS_KEY = "New Labels"
CSV_FILE_NAME_KEY = "FolderNum_SeriesNum"
CSV_BOUNDING_BOX_KEY = "correct_flip_bbox"
counter = 0
timer_id = None
class App(tk.Frame):
def __init__(self, master=None):
super().__init__(master) # python3 style
self.config_paths = ["config 1.yaml", "config 2.yaml", "config 3.yaml"]
self.config_index = 0
self.clickStatus = tk.StringVar()
self.loadedImages = dict()
self.loadedBoxes = dict() # this dictionary will keep track of all the boxes drawn on the images
self.master.title('Slideshow')
frame = tk.Frame(self)
tk.Button(frame, text=" Next set ", command=lambda:[self.get_next_image_set(), self.reset()]).pack(side=tk.RIGHT)
tk.Button(frame, text=" Exit ", command=self.destroy).pack(side=tk.RIGHT)
frame.pack(side=tk.TOP, fill=tk.BOTH)
self.canvas = tk.Canvas(self)
self.canvas.pack()
self._load_dataset()
self.reset()
self.start_timer = None
t = time()
t.start()
def start_timer(self, evt=None):
if self._start_timer is not None:
self._start_timer = time.perf_counter()
# global counter
# counter += 1
# label.config(text=str(counter))
# label.after(1000, count)
def reset(self):
if self._start_timer is None:
elapsed_time = time.perf_counter() - self._start_timer
self._start_timer = None
print('Time elapsed (hh:mm:ss.ms) {}'.format(elapsed_time))
def _load_dataset(self):
try:
config_path = self.config_paths[self.config_index]
self.config_index += 1
except IndexError:
return
image_data = loadData(config_path)
# drawing the image on the label
self.image_data = image_data
self.currentIndex = 0
# start from 0th image
self._load_image()
def _load_image(self):
imgName = self.image_data[self.currentIndex]['image_file']
if imgName not in self.loadedImages:
self.im = PIL.Image.open(self.image_data[self.currentIndex]['image_file'])
ratio = MAX_HEIGHT / self.im.height
# ratio divided by existing height -> to get constant amount
height, width = int(self.im.height * ratio), int(self.im.width * ratio)
# calculate the new h and w and then resize next
self.canvas.config(width=width, height=height)
self.im = self.im.resize((width, height))
if self.im.mode == "1":
self.img = PIL.ImageTk.BitmapImage(self.im, foreground="white")
else:
self.img = PIL.ImageTk.PhotoImage(self.im)
imgData = self.loadedImages.setdefault(self.image_data[self.currentIndex]['image_file'], dict())
imgData['image'] = self.img
imgData['shapes'] = self.image_data[self.currentIndex]['shapes']
# for next and previous so it loads the same image adn don't do calculations again
self.img = self.loadedImages[self.image_data[self.currentIndex]['image_file']]['image']
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.img)
self.show_drag_box()
def loadData(fname):
with open(fname, mode='r') as f:
return yaml.load(f.read(), Loader=yaml.SafeLoader)
if __name__ == "__main__":
data = loadData('config 1.yaml')
app = App(data)
app.pack() # goes here
app.mainloop()

I have used datetime instead of time, as subtracting two datetime objects will give an output with hours and minutes included, whereas subtracting two time objects only gives seconds. However, both will work, you may just need to do more reformatting using time.
Read the current time when the application starts and store it. Each time you press the button, subtract your stored time from the current time which gives you your time elapsed. Then simply store your new current time until the next button press. The code below demonstrates this.
import tkinter as tk
import datetime as dt
class TimeButton(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# Start timer
self.current_time = dt.datetime.today()
# Button
self.next_set = tk.Button(self, text='Next Set', command = self.clicked)
self.next_set.pack()
def clicked(self):
now = dt.datetime.today()
time_elapsed = now - self.current_time
print(time_elapsed)
self.current_time = now
if __name__ == "__main__":
window = tk.Tk()
button = TimeButton(window)
button.pack()
window.mainloop()

Related

Fullscreen slideshow with images, text and video

I'm completely new at python, learned not much yet. My goal is, to build a slideshow app, which should display images, images with transparent text frames and also videos. Playing a video is my main problem at the moment.
A little test script with static content. Later, all content should be loaded from a web service.
import tkinter as tk
import tkvideo as tv
from PIL import Image
from PIL import ImageTk
import time
# fullscreen window
window = tk.Tk()
window.attributes('-fullscreen', True)
# dimensions
window_width = window.winfo_screenwidth()
window_height = window.winfo_screenheight()
text_width = int(window_width * 0.4)
text_x = int(window_width - text_width)
# transparent rectangle for text
imgs=[]
def create_rectangle(x,y,a,b,**options):
if 'alpha' in options:
# Calculate the alpha transparency for every color(RGB)
alpha = int(options.pop('alpha') * 255)
# Use the fill variable to fill the shape with transparent color
fill = options.pop('fill')
fill = window.winfo_rgb(fill) + (alpha,)
img = Image.new('RGBA', (a-x, b-y), fill)
imgs.append(ImageTk.PhotoImage(img, master=canvas))
canvas.create_image(x, y, image=imgs[-1], anchor='nw')
canvas.create_rectangle(x, y, a, b, **options)
# fullscreen canvas
canvas = tk.Canvas(window, bg="white", bd=0)
canvas.pack(fill=tk.BOTH, expand=True)
# only image slideshow
images = ['quiz1.jpg', 'quiz2.jpg', 'quiz3.jpg', 'quiz4.jpg']
for img in images:
image = Image.open("/home/jpm/Bilder/" + img)
newimage = image.resize((window_width, window_height))
photo = ImageTk.PhotoImage(newimage, master=canvas)
canvas.create_image(0, 0, anchor="nw", image=photo)
canvas.update()
time.sleep(5)
# image with text slideshow
images = ['1658724794aff.jpg', '1658724768kar.jpg']
headlines = ['Headline 1',
'Headline 2']
paragraphs = [['paragraph 1',
'paragraph 2',
'paragraph 3',
'paragraph 4'],
['paragraph 1',
'paragraph 2',
'paragraph 3',
'paragraph 4']]
i=0
for img in images:
image = Image.open("/home/jpm/Bilder/" + img)
newimage = image.resize((window_width, window_height))
photo = ImageTk.PhotoImage(newimage, master=canvas)
canvas.create_image(0, 0, anchor="nw", image=photo)
create_rectangle(text_x, 0, window_width, window_height, fill= "white", alpha=.80, width=0)
head = canvas.create_text(text_x+20, 20, text=headlines[i], fill="#72B765", font=('Helvetica 34 bold'), anchor='nw', width=text_width-20)
canvas.update()
x0, y0, x1, y1 = canvas.bbox(head)
for paragraph in paragraphs[i]:
time.sleep(4)
line = canvas.create_text(text_x+20, y1+10, text=paragraph, fill="#000000", font=('Helvetica 18 bold'), anchor='nw', width=text_width-20)
x0, y0, x1, y1 = canvas.bbox(line)
canvas.update()
canvas.update()
time.sleep(8)
i = i + 1
canvas.destroy()
my_label = tk.Label(window)
my_label.pack(fill='both', expand=True)
player = tv.tkvideo("/home/jpm/Bilder/docsite_promotion.mp4", my_label, loop = 0, size = (window_width,window_height))
player.play()
window.mainloop()
The image and image with text parts running fine. My problem is the video. First of all, it is only displayed at the end of the script. If I put the Label with the video between the two loops or in front of the first, nothing is shown. I also found out, that the program is not waiting till the video ends.
It's all too confusing for me, to explain it better. Maybe I don't understand the flow control of python programs at all. Maybe tkvideo is not the best option. I played also with ffpyplayer, but can't find out, how use it with tkinter.
I only want to display these three types of content in any order and finally also in an endless loop.
Hope you understand me and can give me some tips.
.play() doesn't wait for end of video (it runs code in separated thread) and next image may replace it (hide it) before you see video.
I tried to use sleep() to wait for end of video but it makes problem - it raises error because function doesn't run in main thread.
You would need to use .after(millisecond, my_function) to check periodically if there is end of video and later display images.
If in tkvideo you use self.thread instead of thread then you can check periodically self.thread.is_alive() to catch end of thread. But this need also some method to run next code which will change images. First idea is to create function which changes images and send it as callback to function which check end of video - and it would run this callback() when is_alive() is False. But all this makes problem when you would like to create something more complex.
Here example which uses window.after() to check self.thread.is_alive() and later it runs callback()
I uses tkvideo from my other answer - I added self.running to have method to stop video in any moment.
import time
import tkinter as tk
import threading
from time import perf_counter, sleep
import imageio
from PIL import Image, ImageTk
import os
class tkvideo():
def __init__(self, path, label, loop=False, size=(640,360), hz=0):
self.path = path
self.label = label
self.loop = loop
self.size = size
self.hz = hz
self.running = True # <-- variable to control loop
def load(self, path, label, loop, hz):
"""
Loads the video's frames recursively onto the selected label widget's image parameter.
Loop parameter controls whether the function will run in an infinite loop
or once.
"""
frame_data = imageio.get_reader(path)
if hz > 0:
frame_duration = float(1 / hz)
else:
frame_duration = float(0)
if loop:
while True:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
if not self.running: # <-- variable to control loop
return # exit function and stop thread
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
print('[load] end of loop')
else:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
if not self.running: # <-- variable to control loop
return # exit function and stop thread
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
print('[load] end of loop')
def play(self):
"""
Creates and starts a thread as a daemon that plays the video by rapidly going through
the video's frames.
"""
# uses `self.thread` instead of `thread` to have access to `self.thread.is_alive()`
self.thread = threading.Thread(target=self.load, args=(self.path, self.label, self.loop, self.hz))
self.thread.daemon = True
self.thread.start()
def show_video(filename, size, callback=None):
global player
global my_label
fullpath = os.path.join(folder, filename)
my_label = tk.Label(window)
my_label.pack(fill='both', expand=True)
player = tkvideo(fullpath, my_label, loop=False, size=size)
player.play()
# check after 5ms
window.after(5, check_end_video, callback)
def check_end_video(callback):
global my_label
if player.thread.is_alive(): # check if it end of thread
# check again after 5ms
window.after(5, check_end_video, callback)
else:
# remove label
print('The End')
my_label.destroy()
# run next function
if callback:
callback()
def show_image(filename, size, callback=None):
global my_label
fullpath = os.path.join(folder, filename)
frame_image = ImageTk.PhotoImage(Image.open(fullpath).resize(size))
my_label = tk.Label(window)
my_label.pack(fill='both', expand=True)
my_label['image'] = frame_image # show image on label
my_label.image = frame_image # solution for bug in PhotoImage
# check after 5000ms (5s)
window.after(5000, check_end_image, callback)
def check_end_image(callback):
global my_label
# remove label
print('The End')
my_label.destroy()
# run next function
if callback:
callback()
def other_function():
button = tk.Button(window, text='Close', command=window.destroy)
button.pack(fill='both', expand=True, padx=250, pady=250)
# --- main ---
folder = "/home/jpm/Bilder/"
window = tk.Tk()
window.update()
#window.attributes('-fullscreen', True)
#window_width = window.winfo_screenwidth()
#window_height = window.winfo_screenheight()
window.geometry('800x600+800+300')
window_width = 800
window_height = 600
# ---
size = (window_width, window_height)
# run video and it will later run `show_image`
show_video("docsite_promotion.mp4", size,
lambda:show_image('quiz1.jpg', size, other_function))
# ---
window.mainloop()

VLC causes buffer when embedded in Python Tkinter Frame

I have been working on a music player app that uses VLC to play songs directly from the internet and has features like seeking and a progress bar. But as the normal tkinter progress bar looks kinda old so I used customtkinter.CTkSlider widget but using this causes buffering. The song plays smoothly on using Tk.Scale.
Here, is the code:
# import external libraries
import vlc
import tkinter as Tk
from tkinter import ttk
import pyautogui
import customtkinter
import pafy
# import standard libraries
import os
from threading import Thread, Event
import time
import platform
import requests
class ttkTimer(Thread):
"""a class serving same function as wxTimer... but there may be better ways to do this
"""
def __init__(self, callback, tick):
Thread.__init__(self)
self.callback = callback
self.stopFlag = Event()
self.tick = tick
self.iters = 0
def run(self):
while not self.stopFlag.wait(self.tick):
self.iters += 1
self.callback()
def stop(self):
self.stopFlag.set()
def get(self):
return self.iters
class Player(Tk.Frame):
"""The main window has to deal with events.
"""
def __init__(self, parent, title=None):
Tk.Frame.__init__(self, parent)
self.parent = parent
if title == None:
title = "tk_vlc"
self.parent.title(title)
# Menu Bar
# File Menu
menubar = Tk.Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Tk.Menu(menubar)
fileMenu.add_command(label="Open", underline=0, command=self.OnOpen)
fileMenu.add_command(label="Exit", underline=1, command=_quit)
menubar.add_cascade(label="File", menu=fileMenu)
# The second panel holds controls
self.player = None
self.videopanel = ttk.Frame(self.parent)
self.canvas = Tk.Canvas(self.videopanel).pack(fill=Tk.BOTH,expand=1)
self.videopanel.pack(fill=Tk.BOTH,expand=1)
ctrlpanel = ttk.Frame(self.parent)
pause = ttk.Button(ctrlpanel, text="Pause", command=self.OnPause)
play = ttk.Button(ctrlpanel, text="Play", command=self.OnPlay)
stop = ttk.Button(ctrlpanel, text="Stop", command=self.OnStop)
volume = ttk.Button(ctrlpanel, text="Volume", command=self.OnSetVolume)
pause.pack(side=Tk.LEFT)
play.pack(side=Tk.LEFT)
stop.pack(side=Tk.LEFT)
volume.pack(side=Tk.LEFT)
self.volume_var = Tk.IntVar()
self.volslider = Tk.Scale(ctrlpanel, variable=self.volume_var, command=self.volume_sel,
from_=0, to=100, orient=Tk.HORIZONTAL, length=100)
self.volslider.pack(side=Tk.LEFT)
ctrlpanel.pack(side=Tk.BOTTOM)
ctrlpanel2 = ttk.Frame(self.parent)
self.scale_var = Tk.DoubleVar()
self.timeslider_last_val = ""
self.timeslider = customtkinter.CTkSlider(ctrlpanel2, variable=self.scale_var, command=self.scale_sel,
from_=0, to=1000, orient=Tk.HORIZONTAL) # This causes buffer
self.timeslider.pack(side=Tk.BOTTOM, fill=Tk.X,expand=1)
self.timeslider_last_update = time.time()
ctrlpanel2.pack(side=Tk.BOTTOM,fill=Tk.X)
# VLC player controls
self.Instance = vlc.Instance()
self.player = self.Instance.media_player_new()
self.timer = ttkTimer(self.OnTimer, 1.0)
self.timer.start()
self.parent.update()
#self.player.set_hwnd(self.GetHandle()) # for windows, OnOpen does does this
def Extract(self,topic):
"""Will play video on following topic, takes about 10 to 15 seconds to load"""
url = 'https://www.youtube.com/results?q=' + topic
count = 0
cont = ''
try:
cont = requests.get(url)
except:
print('Error','Cannot Connect.. Internet not connected or invalid URL or id.')
cont = ''
data = cont.content
data = str(data)
lst = data.split('"')
for i in lst:
count+=1
if i == 'WEB_PAGE_TYPE_WATCH':
break
if lst[count-5] == "/results":
print("Error","No video found.")
return "https://www.youtube.com"+lst[count-5]
def OnExit(self, evt):
"""Closes the window.
"""
self.Close()
def OnOpen(self):
"""Pop up a new dialow window to choose a file, then play the selected file.
"""
# if a file is already running, then stop it.
self.OnStop()
fullname = pafy.new(self.Extract(pyautogui.password(mask="", title="Enter Song Name:", text="Enter Song Name:"))).getbest().url
self.Media = self.Instance.media_new(fullname)
self.player.set_media(self.Media)
# set the window id where to render VLC's video output
if platform.system() == 'Windows':
self.player.set_hwnd(self.GetHandle())
else:
self.player.set_xwindow(self.GetHandle()) # this line messes up windows
# FIXME: this should be made cross-platform
self.OnPlay()
# set the volume slider to the current volume
#self.volslider.SetValue(self.player.audio_get_volume() / 2)
self.volslider.set(self.player.audio_get_volume())
def OnPlay(self):
"""Toggle the status to Play/Pause.
If no file is loaded, open the dialog window.
"""
# check if there is a file to play, otherwise open a
# Tk.FileDialog to select a file
if not self.player.get_media():
self.OnOpen()
else:
# Try to launch the media, if this fails display an error message
if self.player.play() == -1:
self.errorDialog("Unable to play.")
def GetHandle(self):
return self.videopanel.winfo_id()
#def OnPause(self, evt):
def OnPause(self):
"""Pause the player.
"""
self.player.pause()
def OnStop(self):
"""Stop the player.
"""
self.player.stop()
# reset the time slider
self.timeslider.set(0)
def OnTimer(self):
"""Update the time slider according to the current movie time.
"""
if self.player == None:
return
# since the self.player.get_length can change while playing,
# re-set the timeslider to the correct range.
length = self.player.get_length()
dbl = length * 0.001
self.timeslider.config(to=dbl)
# update the time on the slider
tyme = self.player.get_time()
if tyme == -1:
tyme = 0
dbl = tyme * 0.001
self.timeslider_last_val = ("%.0f" % dbl) + ".0"
# don't want to programatically change slider while user is messing with it.
# wait 2 seconds after user lets go of slider
if time.time() > (self.timeslider_last_update + 2.0):
self.timeslider.set(dbl)
def scale_sel(self, evt):
if self.player == None:
return
nval = self.scale_var.get()
sval = str(nval)
if self.timeslider_last_val != sval:
self.timeslider_last_update = time.time()
mval = "%.0f" % (nval * 1000)
self.player.set_time(int(mval)) # expects milliseconds
def volume_sel(self, evt):
if self.player == None:
return
volume = self.volume_var.get()
if volume > 100:
volume = 100
if self.player.audio_set_volume(volume) == -1:
self.errorDialog("Failed to set volume")
def OnToggleVolume(self, evt):
"""Mute/Unmute according to the audio button.
"""
is_mute = self.player.audio_get_mute()
self.player.audio_set_mute(not is_mute)
# update the volume slider;
# since vlc volume range is in [0, 200],
# and our volume slider has range [0, 100], just divide by 2.
self.volume_var.set(self.player.audio_get_volume())
def OnSetVolume(self):
"""Set the volume according to the volume sider.
"""
volume = self.volume_var.get()
# vlc.MediaPlayer.audio_set_volume returns 0 if success, -1 otherwise
if volume > 100:
volume = 100
if self.player.audio_set_volume(volume) == -1:
self.errorDialog("Failed to set volume")
def errorDialog(self, errormessage):
"""Display a simple error dialog.
"""
Tk.tkMessageBox.showerror(self, 'Error', errormessage)
def Tk_get_root():
if not hasattr(Tk_get_root, "root"): #(1)
Tk_get_root.root= Tk.Tk() #initialization call is inside the function
return Tk_get_root.root
def _quit():
print("_quit: bye")
root = Tk_get_root()
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
os._exit(1)
if __name__ == "__main__":
# Create a Tk.App(), which handles the windowing system event loop
root = Tk_get_root()
root.protocol("WM_DELETE_WINDOW", _quit)
player = Player(root, title="tkinter vlc")
# show the player window centred and run the application
root.mainloop()
Kindly help
Regards.

Tkinter: image loop slows down

I wrote a Tkinter GUI that is supposed to show display 3D images by looping through the 2D slices of each 3D image. I read up on how to implement such an image loop, and it was recommended to use the after() function with recursion. This is, in principle, my implementation of that:
def load_image(self):
self.stack = read_3D_image(path)
slice = self.stack[self.slice_no]
im = Image.fromarray(slice)
self.photo = ImageTk.PhotoImage(image=im)
self.canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
if self.forward is True:
self.slice_no += 1
if self.slice_no == 21:
self.forward = False
if self.forward is False:
self.slice_no -= 1
if self.slice_no == 0:
self.forward = True
root.after(10, self.load_image)
This works well for some time, but after a couple of minutes, the loop notably slows down. I guess that is because of the high number of iterations. Is there a way to fix this?
Edit: I noticed this: when the program runs, the image loop will slow down to about half the original frequency after about 10 minutes. When I run a second instance, its loop runs equally slow. Then when I close the first instance, the second instance loop immediately runs faster. I launch from Eclipse.
Updated full code
import glob
import os.path
import tkinter as tk
from PIL import Image, ImageTk
import numpy as np
import helpers
class App():
def __init__(self, master):
super().__init__()
self.frame = tk.Frame(master)
master.bind("<KP_1>", lambda e: self.is_cell())
master.bind("<KP_2>", lambda e: self.is_not_cell())
self.frame.pack()
self.goto_button = tk.Button(
self.frame, text="Go to:", command=self.goto)
self.goto_button.pack(side=tk.TOP)
self.goto_entry = tk.Entry(self.frame, width=5)
self.goto_entry.pack(side=tk.TOP)
self.img_side_length = 100
self.canvas = tk.Canvas(
master=self.frame, width=self.img_side_length, height=self.img_side_length)
self.canvas.pack()
self.img_label = tk.Label(self.frame, text="Bla")
self.img_label.pack(side=tk.TOP)
self.no_cell_button = tk.Button(
self.frame, text="2: Not cell!", command=self.is_not_cell)
self.no_cell_button.pack(side=tk.RIGHT)
self.cell_button = tk.Button(
self.frame, text="1: Cell!", command=self.is_cell)
self.cell_button.pack(side=tk.RIGHT)
self.img_path = "/storage/images/"
self.img_list = glob.glob(os.path.join(self.img_path, "*"))
self.img_list.sort()
self.slice_no = 0
self.img_no = 0
self.forward = True
self.no_of_imgs = len(self.img_list)
self.stack = []
self.image_id = self.canvas.create_image(0, 0, anchor=tk.NW)
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.classifications = np.zeros(self.no_of_imgs)
self.out_path = "/dev/null"
self.loop_image()
def loop_image(self):
data = self.stack[self.slice_no]
im = Image.fromarray(data)
im = im.resize((self.img_side_length, self.img_side_length))
self.photo = ImageTk.PhotoImage(image=im)
self.canvas.itemconfigure(self.image_id, image=self.photo)
if self.forward is True:
self.slice_no += 1
if self.slice_no == 21:
self.forward = False
if self.forward is False:
self.slice_no -= 1
if self.slice_no == 0:
self.forward = True
root.after(10, self.loop_image)
def next_image(self):
self.img_no += 1
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.img_label['text'] = self.img_list[self.img_no].split("/")[-1]
def previous_image(self):
self.img_no -= 1
self.stack = helpers.read_image_sequence(self.img_list[self.img_no])
self.img_label['text'] = self.img_list[self.img_no].split("/")[-1]
def is_cell(self):
self.classifications[self.img_no] = 1
with open(self.out_path, "a") as f:
f.write(str(self.img_no) + "," + str(1) + "\n")
print(self.classifications)
self.next_image()
def is_not_cell(self):
self.classifications[self.img_no] = 2
with open(self.out_path, "a") as f:
f.write(str(self.img_no) + "," + str(2) + "\n")
print(self.classifications)
self.next_image()
def goto(self):
self.img_no = int(self.goto_entry.get())
root = tk.Tk()
app = App(root)
root.mainloop()
You are creating 100 images per second and are stacking them on top of each other. After 10 minutes, that's 60,000 images stacked on top of each other. The canvas has performance issues when it has tens of thousands of items on it, even if all but one is invisible.
Instead of creating more and more images on the canvas, just modify the existing image:
def __init__(self):
...
self.canvas = tk.Canvas(...)
...
# create the image object which will display the image
self.image_id = self.canvas.create_image(0, 0, anchor=tk.NW)
def load_image(self):
...
self.photo = ImageTk.PhotoImage(image=im)
# reconfigure the canvas item to show the new image
canvas.itemconfigure(self.image_id, image=self.photo)
...

Run Images in Loop Tkinter

I am creating an app that will allow users to scan a ticket and a message will be displayed. I have created a short GIF animation to play when the app starts to show users where to scan their ticket.
I am having trouble understanding how to play a GIF image using tkinter in Python 3. I have tried many solutions and I came across a piece of code where you select the folder and the images in that folder will play in a loop but it's not working.
I think I'm not understanding the code. Here is my code for my app:
from tkinter import *
from tkinter import messagebox
import tkinter.filedialog
from tkinter.filedialog import askdirectory
import requests
import simplejson as json
import os
#from json import JSONEncoder
#class MyEncoder(JSONEncoder):
#def default(self, o):
#return o.__dict__
#Connect to API function
def apiconnect(statusvar):
ticektid = e1.get()
def to_serializable(ticketid):
return str(ticketid)
url = "https://staging3.activitar.com/ticket_api/tickets"
data = {'ticket_id':e1.get(),'direction': 'up'}
headers = {'Content-Type': 'application/json','Authorization' :'J0XDvDqVRy9hMF9Fo7j5'}
r = requests.post(url,data=json.dumps(data), headers=headers)
requestpost = requests.post(url, headers=headers, json=data)
response_data = requestpost.json()
statusvar = (response_data["status"])
messagevar = (response_data["message"])
json.dumps(url,data)
# MyEncoder().encode(ticketid)
#'{"ticekt_id": "/foo/bar"}'
#19 February 2018
#def from_json(json_object):
# if 'ticket_id' in json_object:
# return FileItem(json_object['ticket_id'])
# ticketid = JSONDecoder(object_hook = from_json).decode('{"ticket_id": "/foo/bar"}')
#Including GPIO config
if statusvar == "failed":
messagebox.showinfo("Cape Point", messagevar)
else: statusvar == "successful"
#Run at full screen automatically:
#---------------Function & Class--------------------------------#
class FullScreenApp(object):
def __init__(self, master, **kwargs):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry("{0}x{1}+0+0".format(
master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad))
master.bind('<Escape>',self.toggle_geom)
def toggle_geom(self,event):
geom=self.master.winfo_geometry()
print(geom,self._geom)
self.master.geometry(self._geom)
self._geom=geom
#--------------------------------------------------------------------#
def next_img():
img_label.img = PhotoImage(file=next(imgs))
img_label.config(image=img_label.img)
#create a textbox on a form
root = Tk()
#-----Full Screen-------#
app = FullScreenApp(root)
root.title("Cape Point")
root.configure(background = 'White')
#________ this code below was the original that displayed a static image _____#
#titlepic = PhotoImage(file = "ScanPlease.gif")
#shownpic = titlepic
#filename = shownpic
#Label(root, image = filename).grid(row=0, sticky=W)
img_dir = askdirectory(parent=root, initialdir= "C:/Users/Nickitaes/Desktop", title='Where To Scan')
os.chdir(img_dir)
imgs = iter(os.listdir(img_dir))
img_label = Label(root)
img_label.bind("<Return>",next_img())
next_img()
e1 = Entry(root)
e1.grid(row=1, column=0)
e1.focus_set() #set cursor focus to textbox
e1.bind("<Return>", apiconnect) #Return function
root.mainloop( )
Thanks for the help!
Well..., it was not hard to find other questions about this on StackOverflow. Here are some: Play Animations in GIF with Tkinter and Play an Animated GIF in python with tkinter.
I have combined the answers to take care of different number of subpictures an also commented the code a bit more.
from tkinter import *
import time
root = Tk()
framelist = [] # List to hold all the frames
for ix in range(1000): # range > frames in largest GIF
part = 'gif -index {}'.format(ix)
try: frame = PhotoImage(file='giphy.gif', format=part)
except:
last = len(framelist) - 1 # Save index for last frame
break # Will break when GIF index is reached
framelist.append(frame)
def update(ix):
if ix > last: ix = 0 # Reset frame counter if too big
label.configure(image=framelist[ix]) # Display frame on label
ix += 1 # Increase framecounter
root.after(100, update, ix) # Run again after 100 ms.
label = Label(root)
label.pack()
root.after(0, update, 0) # Start update(0) after 0 ms.
root.mainloop()
Adjust the for-loop for the GIF size you use, or rewrite as a while-loop.
I don't know how to read the frame delay from the GIF. You'll have to try different values in after() until it looks good.

Error when trying to display a timer into button text

When i run this code everything seems to work fine, then when i click my blue buff button where I'm expecting my time countdown to start being displayed the button text instead changes to function my_time at 0x000000... etc. some memory location im struggling to find a solution. If its unclear what im trying to do is have the text Blue Buff change to the to a countdown.
from Tkinter import *
import os
import time
class Application(Frame):
#staticmethod
def my_time(self): # creates a timer starting at 5 min , counts down to 0 then repeats
min = 4
sec = 59
while sec <=60:
os.system('cls')
print min, "Minutes", sec, "Seconds"
time.sleep(1)
sec -= 1
if sec == 0:
min -= 1
sec = 59
elif min == 0:
min = 4
def my_time2(self): # tries to display my_time method into the button font, doesnt seem to work.
self.Button1["text"] = str(self.my_time)
def createButtons(self): # creats a button
self.Button1 = Button(self)
self.Button1["text"] = "Blue Buff"
self.Button1["fg"] = "Blue"
self.Button1["command"] = self.my_time2 # suppose to implement countdown in button text when click.
# currently problematic?
self.Button1.pack({"side": "left"})
def __init__(self, master=None):
Frame.__init__(self, master) # initializes window
self.pack()
self.createButtons()
root = Tk()
app = Application(master=root)
app.mainloop()
This should do what you need:
from Tkinter import *
import os
import time
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master) # initializes window
self.Button1 = Button(self, text="Blue Buff", command=self.my_time)
self.Button1.pack()
self.pack()
def my_time(self): # creates a timer starting at 5 min , counts down to 0 then repeats
min = 4
sec = 59
while sec <=60:
os.system('cls')
print min, "Minutes", sec, "Seconds"
self.Button1.configure(text="{}:{}".format(min,sec))
self.Button1.update()
time.sleep(1)
sec -= 1
if sec == 0:
min -= 1
sec = 59
elif min == 0:
min = 1
root = Tk()
app = Application(master=root)
app.mainloop()#
Using your own code just set command to my_time and update the Button1:
class Application(Frame):
def my_time(self): # creates a timer starting at 5 min , counts down to 0 then repeats
min = 4
sec = 59
while sec <=60:
self.Button1.configure(text="{}:{}".format(min,sec))
self.Button1.update()
os.system('cls')
print min, "Minutes", sec, "Seconds"
time.sleep(1)
sec -= 1
if sec == 0:
min -= 1
sec = 59
elif min == 0:
min = 4
def my_time2(self): # tries to display my_time method into the button font, doesnt seem to work.
self.Button1["text"] = str(self.my_time)
def createButtons(self): # creats a button
self.Button1 = Button(self)
self.Button1["text"] = "Blue Buff"
self.Button1["fg"] = "Blue"
self.Button1["command"] = self.my_time # suppose to implement countdown in button text when click.
# currently problematic?
self.Button1.pack({"side": "left"})
def __init__(self, master=None):
Frame.__init__(self, master) # initializes window
self.pack()
self.createButtons()
It looks like you are setting self.Button1["command"] = self.my_time2 in which self.my_time2 is not a an attribute, but rather a function. That would be why the numbers look all wonky, is because its displaying a memory address and not the str(self.my_time) that you would expect. Perhaps you meant to invoke my_time2()
*EDIT 1
changing line 25 to
self.Button1["text"] = str(self.my_time(self))
Also what you have written in my_time will print to stdout, and not actually update the button (which is what I think you want?), but i'll let you figure that out as the designer.

Categories