Reputation: 125
I'm learning python with the raspberry pi and a pi-face expansion board. Using Tkinter I've created a Gui with buttons to operate the pi-face LED's. In one part of the code I open a new window which shows a button and a row of images of a LED in the 'off' state. I'm trying to add some code to make the row of LED images stream an LED image in the 'on' state left to right along the row of images, like the Knight Rider car's front lights. I've tried a few things in the while loop but can't quite see how to achieve it without a lot of lines of code. I think there must be a way to do it in the same way that the digital write is incremented to create the streaming LED on the piface expansion board. Here is my code...
class App2:
def __init__(self, master):
self.signal = False #added to stop thread
print('self.signal', self.signal)
self.master=master # I added this line to make the exit button work
frame = Frame(master)
frame.pack()
Label(frame, text='Turn LED ON').grid(row=0, column=0)
Label(frame, text='Turn LED OFF').grid(row=0, column=1)
self.button0 = Button(frame, text='Knight Rider OFF', command=self.convert0)
self.button0.grid(row=2, column=0)
self.LED0 = Label(frame, image=logo2) #added to create a row of images
self.LED1 = Label(frame, image=logo2)
self.LED2 = Label(frame, image=logo2)
self.LED3 = Label(frame, image=logo2)
self.LED4 = Label(frame, image=logo2)
self.LED5 = Label(frame, image=logo2)
self.LED6 = Label(frame, image=logo2)
self.LED7 = Label(frame, image=logo2)
self.LED0.grid(row=2, column=1)
self.LED1.grid(row=2, column=2)
self.LED2.grid(row=2, column=3)
self.LED3.grid(row=2, column=4)
self.LED4.grid(row=2, column=5)
self.LED5.grid(row=2, column=6)
self.LED6.grid(row=2, column=7)
self.LED7.grid(row=2, column=8)
self.button9 = Button(frame, text='Exit', command=self.close_window)
self.button9.grid(row=3, column=0)
def convert0(self, tog=[0]):
tog[0] = not tog[0]
if tog[0]:
print('Knight Rider ON')
self.button0.config(text='Knight Rider ON')
t=threading.Thread(target=self.LED)
t.start()
self.signal = True #added to stop thread
print('self.signal', self.signal)
print('tog[0]', tog[0])
self.LED0.config(image = logo)
else:
print('Knight Rider OFF')
self.button0.config(text='Knight Rider OFF')
self.signal = False #added to stop thread
print('self.signal', self.signal)
print('tog[0]', tog[0])
self.LED0.config(image = logo2)
def LED(self):
while self.signal: #added to stop thread
a=0
while self.signal: #added to stop thread
pfio.digital_write(a,1) #turn on
sleep(0.05)
pfio.digital_write(a,0) #turn off
sleep(0.05)
a=a+1
if a==7:
break
while self.signal: #added to stop thread
pfio.digital_write(a,1) #turn on
sleep(0.05)
pfio.digital_write(a,0) #turn off
sleep(0.05)
a=a-1
if a==0:
break
def close_window(self):
print('Knight Rider OFF')
print('self.signal', self.signal)
self.button0.config(text='Knight Rider OFF')
self.LED0.config(image = logo2)
self.signal = False #added to stop thread
print('self.signal', self.signal)
sleep(1)
print('Close Child window')
self.master.destroy() # I added this line to make the exit button work
root = Tk()
logo2 = PhotoImage(file="/home/pi/Off LED.gif")
logo = PhotoImage(file="/home/pi/Red LED.gif")
root.wm_title('LED on & off program')
app = App(root)
root.mainloop()
Upvotes: 0
Views: 1064
Reputation: 1701
Might not be exactly what you are looking for, but you can get some inspiration to set up this "cylon" algorithm on the terminal first. LEDs don't have intermediate color values, but I guess the remnant perception of light should do the trick.
import sys,time
shift = lambda l, n=1: l[n:]+l[:n]
c = u' ▁▂▃▄▅▆▇' # possible color values
L = 8 # number of items
B = L*[0] # indices of items
A = [0] + list(range(7)) + list(range(7,0,-1)) + 6*[0] # light sequence
C = L*[' '] # start blank
while 1:
B[A[0]]=L # set the most brilliant light
for x in range(L):
B[x]-= 1 # decrease all lights values
B[x] = max(0,B[x]) # not under 0
C[x] = c[B[x]] # get the corresponding 'color'
A = shift(A,-1) # shift the array to the right
sys.stdout.write(('%s%s%s')%(' ',''.join(C[1:]),'\r'))
time.sleep(0.1)
Or try that one https://www.trinket.io/python/79b8a588aa
Upvotes: 0
Reputation: 385970
You don't need threads for such a simple task. It's very easy to set up a persistent repeating task in tkinter, if the task doesn't take more than a couple hundred milliseconds (if it takes much longer, your UI will start to lag).
The basic pattern is to write a function that does some work, and then have that function cause itself to be called again using after
. For example:
def animate():
# do something, such as turn an led on or off
<some code here to turn one led on or off>
# run this again in 100 ms
root.after(100, animate)
The above will create an infinite loop that runs inside tkinter's mainloop. As long as <some code here... >
doesn't take too long, the animation will appear fluid and your UI won't be laggy.
Here's a simple working example of the technique. It uses a simple iterator to cycle through the leds, but you can use any algorithm you want to pick the next led to light up. You could also turn on or off both the on screen and hardware led at the same time, or turn on or off multiple leds, etc.
To make this code copy/paste-able, it uses colored frames rather than images, but you can use images if you want.
import tkinter as tk # Tkinter for python 2
from itertools import cycle
class LEDStrip(tk.Frame):
segments = 16
speed = 100 # ms
def __init__(self, parent):
tk.Frame.__init__(self, parent)
leds = []
for i in range(self.segments):
led = tk.Frame(self, width=12, height=8, borderwidth=1,
relief="raised", background="black")
led.pack(side="left", fill="both", expand=True)
leds.append(led)
self.current_segment = None
self.iterator = cycle(leds)
def animate(self):
# turn off the current segment
if self.current_segment:
self.current_segment.configure(background="black")
# turn on the next segment
self.current_segment = next(self.iterator) # self.iterator.next() for python 2
self.current_segment.configure(background="red")
# run again in the future
self.after(self.speed, self.animate)
root = tk.Tk()
strip = LEDStrip(root)
strip.pack(side="top", fill="x")
# start the loop
strip.animate()
root.mainloop()
Upvotes: 1