Tkinter after() getting faster whenever my timer object is reset - python

I'm trying to write a timer with tkinter.
And here's my class program of timer
class Timer:
def __init__(self, root, cv):
self.sec = 0
self.stop = 0
self.label = tk.Label(root, width = 15, height = 1, text = "time:"+"{}".format(str(self.sec))+"s", bg= "GhostWhite")
self.label.config(font = ("helvetica", 8))
cv.create_window(300, 14, window = self.label)
self.label.after(1000, self.refresh)
def refresh(self):
self.sec += 1
self.label.configure(text="time:"+"{}".format(str(self.sec))+"s")
if self.stop == 0:
self.label.after(1000, self.refresh)
def halt(self):
self.stop = 1
def reset(self):
self.sec = 0
self.stop = 0
self.label.after(1000, self.refresh)
The problem is, the timer runs faster each time the reset method is called.
I try to figure out the problem but I just don't see anything wrong.
Any idea?

Related

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

OpenCV Video with Tkinter and threading Crash

I'm trying to make a multithreaded program with Python, OpenCV, and Tkinter.
My program has some general point.
Load Video from file
Create 2 thread
1st thread to fetch frames from capture object and put it to python Queue
2nd thread to get the frames from Queue
At last, if possible, start and stop the capture object
However, my script seems to behave weirdly. Sometimes it can finish playing video until the end, but sometimes it also crash at some point of the video. Here is what I've got so far.
from Tkinter import Tk, Text
from Tkinter import PhotoImage
from ttk import Frame, Scrollbar, Button, Label
from PIL import Image, ImageTk
import cv
import time
import Queue
import threading
def writeToLog(log, msg):
numlines = log.index('end - 1 line').split('.')[0]
if log.index('end-1c')!='1.0': log.insert('end', '\n')
log.insert('end', msg)
log.see('end')
def GetIplImageMode(img):
orientation = 1 if img.origin == 0 else -1
mode_list = {(1, cv.IPL_DEPTH_8U) : ("L", "L", 1),\
(3, cv.IPL_DEPTH_8U) : ("BGR", "RGB", 3),\
(1, cv.IPL_DEPTH_32F) : ("F", "F", 4)}
key = (img.nChannels, img.depth)
modes = mode_list[key]
return [modes[0], modes[1], orientation]
def IplImage2PIL(img, mode):
return Image.fromstring(mode[1], (img.width, img.height),\
img.tostring(), "raw", mode[0],\
img.width * img.channels,\
mode[2])
def ResizePILImage(pil, width = 260, height = 180):
return pil.resize((width, height), Image.ANTIALIAS)
def PIL2TkImage(pil):
return ImageTk.PhotoImage(pil)
def setImageLabelFromIplImage(label, img_ipl):
mode = GetIplImageMode(img_ipl)
img_pil = IplImage2PIL(img_ipl, mode)
img_resized = ResizePILImage(img_pil)
img_tk = PIL2TkImage(img_resized)
label.configure(image = img_tk)
label.image = img_tk
def setImageLabelFromFile(label, szFileName):
img_ipl = cv.LoadImage(szFileName)
setImageLabelFromIplImage(label, img_ipl)
def mat_from_ipl(ipl):
return cv.GetMat(ipl)
def ipl_from_mat(mat):
ipl = cv.CreateImageHeader((mat.width, mat.height),\
cv.IPL_DEPTH_8U, mat.channels)
cv.SetData(ipl, mat.tostring())
return ipl
class asdf(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack(fill='both', expand=True)
self.parent = parent
self.variables()
self.ui()
def variables(self):
self.ctr = 0
self.fps = 0
self.video = None
self.image = None
self.putProc = None
self.getProc = None
self.isRunning = False
self.queue = Queue.Queue()
def ui(self):
f1 = Frame(self)
frm1 = Frame(f1)
self.lbl1 = Label(frm1, image=None)
setImageLabelFromFile(self.lbl1, '../image.bmp')
self.txt1 = Text(frm1, width=30, height=8)
sb1 = Scrollbar(frm1, orient='vertical', command=self.txt1.yview)
self.txt1.configure(yscrollcommand = sb1.set)
self.lbl1.pack()
self.txt1.pack(side='left')
sb1.pack(side='left', fill='y')
frm1.pack(side='left')
frm2 = Frame(f1)
self.lbl2 = Label(frm2, image=None)
setImageLabelFromFile(self.lbl2, '../image.bmp')
self.txt2 = Text(frm2, width=30, height=8)
sb2 = Scrollbar(frm2, orient='vertical', command=self.txt2.yview)
self.txt2.configure(yscrollcommand = sb2.set)
self.lbl2.pack()
self.txt2.pack(side='left')
sb2.pack(side='left', fill='y')
frm2.pack(side='left')
f1.pack()
f2 = Frame(self)
Button(f2, text='Run', command=self.run).pack(side='left')
Button(f2, text='Stop', command=self.stop).pack(side='left')
f2.pack()
def put_to_queue(self):
while self.isRunning:
self.ctr = self.ctr + 1
self.image = cv.QueryFrame(self.video)
time.sleep(1 / self.fps)
try:
writeToLog(self.txt1, '\nPut to queue .. %d' % (self.ctr))
temp1 = cv.CloneImage(self.image)
setImageLabelFromIplImage(self.lbl1, temp1)
temp2 = mat_from_ipl(temp1)
self.queue.put([self.ctr, temp2])
except:
writeToLog(self.txt1, '\nReach end of video ..')
break
def get_from_queue(self):
while self.isRunning:
from_queue = self.queue.get()
self.ctr_fr = from_queue[0]
if self.ctr_fr == self.ctr: time.sleep(30 / self.fps)
temp1 = ipl_from_mat(from_queue[1])
setImageLabelFromIplImage(self.lbl2, temp1)
writeToLog(self.txt2, '\nGet from queue .. %d' % (self.ctr_fr))
time.sleep(1 / self.fps)
def run(self):
self.isRunning = True
self.video = cv.CreateFileCapture('../video.avi')
self.fps = cv.GetCaptureProperty(self.video, cv.CV_CAP_PROP_FPS)
writeToLog(self.txt1, '\nStart put_queue ..')
self.putProc = threading.Thread(target=self.put_to_queue)
self.putProc.start()
time.sleep(1)
writeToLog(self.txt2, '\nStart get_queue ..')
self.getProc = threading.Thread(target=self.get_from_queue)
self.getProc.start()
def stop(self):
self.isRunning = False
if self.putProc.isAlive():
self.putProc._Thread__stop()
writeToLog(self.txt1, '\nputProc still alive, stopping ..')
self.putProc = None
if self.getProc.isAlive():
self.getProc._Thread__stop()
writeToLog(self.txt2, '\ngetProc still alive, stopping ..')
self.getProc = None
self.ctr_fr = 0
self.ctr = 0
if __name__ == '__main__':
root = Tk()
c = asdf(root)
root.mainloop()
Am I doing it wrong?
Any ideas will be very appreciated.
Thanks

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.

Python- Custom speed digital Clock

I am writing a program in python, and i need a digital clock with custom speed. right now, i am using a clock which is working at a normal pace and sync with the windows time clock. How to write a digital clock with custom speed?
Here's a somewhat over-engineered working example:
import Tkinter as tk
import functools
class UnconventionalClock(tk.Label):
'''A label that displays a clock that runs at an unconvetional rate
usage: x=UnconventionalClock(root, hours, minutes, seconds, interval)
x.start(); ... ; x.stop()
'interval' determines how fast one second is, expressed as milliseconds.
1000 means the clock runs normally; 2000 means it runs at half speed,
500 means it runs at double speed, etc.
'''
def __init__(self, parent, hours=0, minutes=0, seconds=0, interval=1000):
tk.Label.__init__(self, parent)
self._after_id = None
self.reset(hours, minutes, seconds, interval)
def start(self):
self._schedule()
def stop(self):
if self._after_id is not None:
self.after_cancel(self._after_id)
self._after_id = None
def reset(self, hours=0, minutes=0, seconds=0, interval=1000):
self.interval = interval
self.hours = hours
self.minutes = minutes
self.seconds = seconds
self.configure(text=self._format())
def _tick(self):
self.seconds += 1
if self.seconds >= 60:
self.seconds = 0
self.minutes += 1
if self.minutes >= 60:
self.minutes = 0
self.hours += 1
if self.hours >= 24:
self.hours = 0
self.configure(text=self._format())
self._schedule()
def _format(self):
return "%02d:%02d:%02d" % (self.hours, self.minutes, self.seconds)
def _schedule(self):
self._after_id = self.after(self.interval, self._tick)
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.clock = UnconventionalClock(self, 23, 59, 40, 500)
toolbar = tk.Frame(self)
start = tk.Button(toolbar, text="Start", command=self.clock.start)
stop = tk.Button(toolbar, text="Stop", command=self.clock.stop)
start.pack(side="left")
stop.pack(side="left")
toolbar.pack(side="top", fill="x")
self.clock.pack(side="top", fill="x")
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
custom_time = custom_time0 + (win_time - win_time0)*alpha
where alpha is the custom factor.

pyHook + Tkinter = crash?

Consider the following example:
from Tkinter import *
import pyHook
class Main:
def __init__(self):
self.root = Tk()
self.root.protocol("WM_DELETE_WINDOW", self.onClose)
self.root.title("Timer - 000")
self.timerView = Text(self.root,
background="#000",
foreground="#0C0",
font=("Arial", 200),
height=1,
width=3)
self.timerView.pack(fill=BOTH, expand=1)
self.timer = 0
self.tick()
self.createMouseHooks()
self.root.mainloop()
def onClose(self):
self.root.destroy()
def createMouseHooks(self):
self.mouseHook = pyHook.HookManager()
self.mouseHook.SubscribeMouseAllButtons(self.mouseClick)
self.mouseHook.HookMouse()
def mouseClick(self, event):
self.timer = 300
return True
def tick(self):
self.timerView.delete(1.0, END)
self.timerView.insert(END, self.threeDigits(self.timer))
self.root.title("Timer - " + str(self.threeDigits(self.timer)))
self.timer = self.timer - 1 if self.timer > 0 else 0
self.root.after(1000, self.tick)
def threeDigits(self, number):
number = str(number)
while len(number) < 3:
number = "0" + number
return number
if __name__ == "__main__":
Main()
This will display a window and asynchronously update a text widget every second. It's simply a timer that will count down, and reset to 300 whenever the user clicks a mouse button.
This does work, but there's a weird bug. When the program is running, and you move the window, the mouse and program will freeze for 3-4 seconds, and then the program stops responding.
If you remove the hook or the asynchronous update, the bug won't happen.
What could be the cause of this problem?
EDIT:
I've been testing in Windows 7 with Python 2.6.

Categories