I am trying to implement virtual LEDs on a Python window. I turn the LED "on" by drawing a green oval. I then use root.after() to schedule another call that turns the LED "off" by drawing a dark green oval. I use a 250ms delay. There are 4 active LEDs on the display. When I allow them to run (I have a global boolean to indicate if I want LEDs to be active), the processor usage starts to climb, going up a percent or two a minute, steadily climbing. I know root.after() returns a handle and you can use that to delete the action, but I don't think I'm overloading the system with too many of these calls.
Location is a tuple with 2 pairs of X,Y values for bounding box. LED() draws the LED immediately. ServiceLEDs() is called at 8 Hrz, and checks the time for all active LEDs to see if the time has passed; this is my second attempt, as my first attempt did a simpler root.after() call for every LED that needed to be handled. I thought this would be faster. Commented out ScheduleLED implements setting an "after" for each LED. The active ScheduleLED sets an array entry with a True flag, the desired state and the time it should expire.
Is it the after() that is causing me time proplems? Is it the tuples? All I know, is if I set UseLEDs to False, the progam runs along at about 3 percent of CPU, and if True, it grows and grows...
Here's code snippets:
def LED(self, location, state):
self.LEDCanvas.create_oval( location, fill=LEDColors[state])
if (not UseLEDs): return
FLAG = 0
STATE = 1
TIME = 2
theTime = getTime()
for ii in range(0,4):
if ( theTime > LEDTimes[ii][TIME] ):
self.LEDCanvas.create_oval( LEDTuples[ii], fill=LEDColors[LEDTimes[ii][STATE]])
LEDTimes[ii] = ( False, 0, 0 )
# def ScheduleLED(self, delay, tuple, state):
# if (UseLEDs):
# print "DEBUG", "LED", delay, state, tuple
# root.after( delay, lambda l=tuple, s=state : self.LED( l, s ))
def ScheduleLED(self, delay, theLED, state):
LEDTimes[theLED] = (True, state, getTime()+(delay/1000))
#root.after( delay, lambda l=tuple, s=state : self.LED( l, s ))
You are continually drawing one oval on top of another, so Tkinter has to keep track of more and more widgets, not to mention that you could reach the recursion limit. Change the color instead. This code is a little crude, but I don't have any time right now.
from Tkinter import *
def change_color(canvas, ov, color):
print "color =", color
if color == 'blue':
color = 'yellow'
color = 'blue'
root = Tk()
canvas = Canvas(root, width=200, height=200)
color = 'black'
for x in range(5):
color = change_color(canvas, ov, color)
Woooee, your handle looks like a bad Scrabble rack of letters, but you are a genius my friend! I've only been writing Python for a few months now and I'm still learning how to do things efficiently. I didn't consider that the objects still live on, even though I wasn't tracking the specific handles of the objects I was creating. Changing the colors did the trick, I can now blink them all day long and my CPU usage stays nice and low. I'll also look elsewhere in my application to see if I'm doing something similar in other places. Thank you for taking the time to read and solve my problem.
Those are good links. I've learned Python by the Google method, and I've been to each of those sites for at least a nugget of information. There is so much to this language, so many little things that can affect how programs work. I read through "Thinking in Tkinter", and as it talks about the pack'er, it says you can't really control it. However, another place I found pack_propagate(0) to inhibit the pack'er from going overboard on rearranging a frame. (Also works with grid()) I knew the create_ methods made an object, but I didn't think it through, that those objects are all still there, even if I'm not tracking them.
How would the examples above hit a recursion limit? I don't see the recursion; it repeats, but there is no 'go deeper or exit' logic, it just repeats itself. As I understand the construct, .after() just sets a timer. I suppose if the timer was reset before the function ended, it would reenter (which can be bad if you don't plan for it), this one looks pretty benign.
And yes, the Padres collapse was epic. They could do no wrong until August, when they could do no right. Having the Chargers trip and fall out of the gate has not made San Diego a very happy pro sports town. Of course, it's finally sunny again so who cares!
Yeah, Tonyjv is correct. To call yourself, you have to do it directly; this is scheduling a call to itself, which is different. Once you call the timer, you exit the function, which you could do forever.
I didn't know you could add parameters to after(). Thanks for that nugget!