I don't know why this didn't jump out at me sooner, but I think the problem is listed in your own question text, referring to the toggleLeds
method:
This one is called recursively through the self.after function, and at the end of the interaction function I have defined for each button.
When the program initially runs, I'm assuming that you call toggleLeds
somewhere to kick off the initial pattern for the LEDs. That sets up a single recursive loop via the self.after
call at the end of the method. However, if you also call that same method every time you click a button to change state, you're setting up a new loop with every button click, and each new loop may or may not be in sync with your initial loop.
There are a couple ways that I can think of to handle this possible conflict. One is to avoid making new calls to toggleLeds
, but that way there could be a delay between the button click and the new LED pattern. If you don't mind that delay, that's probably the best solution.
If you want the light/blink pattern to change immediately, you need to interrupt the current loop and start a new one with the new light/blink states. According to the Tkinter reference produced by New Mexico Tech, the after
method:
...returns an integer “after identifier” that can be passed to the .after_cancel() method if you want to cancel the callback.
Here's how you could take advantage of that. First make sure that you're storing that identifier when calling the after
method:
self.after_id = self.after(self._FastBlinkTime, self.toggleLeds)
Then change your toggleLeds
method definition to accept an optional "interrupt" argument, and to cancel the existing after
loop if that argument is True
:
def toggleLeds(self, interrupt=False):
if interrupt:
self.after_cancel(self.after_id)
# Existing code follows
Finally, pass True
to that argument when calling the method after a button has been clicked:
# Existing button processing code here
self.toggleLeds(interrupt=True)
With these changes in place, each button click would cancel the current after
cycle and start a new one, preventing more than one cycle from running at once, which should keep the LEDs in sync.