Posts: 45
Threads: 27
Joined: Jul 2017
Hello. I just started learning Python
I wrote this code. The idea is to let the main thread to hadle the UI tasks and leave the relay control to a new thread. The relay should be on x seconds and off y sends. User can set to on/off duration wiyj the sliders. How ever the sliders never up date During runtime. I susppect I didn't do it right. Please advice. Thanks.
import serial, time, _thread #random
from tkinter import *
from time import sleep
#Prepare GPIO
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False) #disable annoying warning messages
GPIO.setup(40,GPIO.OUT)
#initially is on
GPIO.output(40,GPIO.HIGH)
g_on_slider_val = 0
g_off_slider_val = 0
# Define a function for the thread
def relay_control( threadName, delay):
while True:
#on_time = float(g_on_slider_val) #g_slder_val is a global var
#off_time = float(g_off_slider_val)
relay_on()
time.sleep(g_on_slider_val)
relay_off()
time.sleep(g_off_slider_val)
ui.update_idletasks()
ui.update()
# Define main thread functions
def read_data():
#data = "Random number {}".format(random.randint(1, 99)) #dummy data
data = ser.readline()
#time.sleep(1)
return data
def relay_on():
GPIO.output(40,GPIO.HIGH)
def relay_off():
GPIO.output(40,GPIO.LOW)
def update_value(string_var, ui_window):
data = read_data()
string_var.set(data)
ui_window.after(1000, update_value, string_var, ui_window)
#Slider function
def get_on_slider_value(on_val):
g_on_slider_val = on_val
print (g_on_slider_val)
def get_off_slider_value(off_val):
g_off_slider_val = off_val
print (g_off_slider_val)
ui = Tk()
ui.geometry("800x400+0+0")
ui.title("Command Center")
ser = serial.Serial('/dev/ttyUSB0', 9600)
var = StringVar()
var.set('Gather Sensor data.')
#slider
on_slider = Scale(ui, orient = HORIZONTAL, length = 300, width = 10, sliderlength = 60, from_ = 0, to = 100, command = get_on_slider_value)
on_slider.place(x=250, y=100)
off_slider = Scale(ui, orient = HORIZONTAL, length = 300, width = 10, sliderlength = 60, from_ = 0, to = 100, command = get_off_slider_value)
off_slider.place(x=250, y=140)
data_label = Label(ui, textvariable = var)
data_label.place(x=300, y=300)
ui.after(1000, update_value, var, ui)
# Create a threads
_thread.start_new_thread( relay_control, ("Thread-1", 2, ) )
ui.mainloop()
#END
Posts: 12,036
Threads: 486
Joined: Sep 2016
Once the thread is started, it won't communicate with the main process without coaxing.
You need to provide communications between the two processes
Watch this video: http://pyvideo.org/pycon-us-2015/python-...-live.html
Posts: 45
Threads: 27
Joined: Jul 2017
Sorry for a dumb newbie question, In the above code how to share the slider number in Python during runtime. Can you be more specific? Thanks.
Posts: 12,036
Threads: 486
Joined: Sep 2016
Aug-01-2017, 06:09 PM
(This post was last modified: Aug-01-2017, 06:09 PM by Larz60+.)
It's not simple to explain, but basically you can use a pipe as in os.pipe: https://docs.python.org/3/library/os.html#os.pipe
or a semiphore: https://docs.python.org/3/library/multip....Semaphore
or for messaging, a queue: https://docs.python.org/3/library/multip...and-queues
Doug Hellman (The python standard Library by example) does an excellent tutorial here: https://pymotw.com/3/multiprocessing/communication.html
But I think you would like David Beazley's video, it's quite enjoyable to watch.
Posts: 2,128
Threads: 11
Joined: May 2017
A minimal example with a class without any queues, using locks instead.
With queues is a better approach, but I'm to tired to wrap my mind around queues.
import time
import threading
class Foo:
def __init__(self, on_duration, off_duration):
self._on_duration = on_duration
self._off_duration = off_duration
self.lock = threading.Lock()
@property
def off_duration(self):
return self._off_duration
@off_duration.setter
def off_duration(self, value):
with self.lock: # BLOCKING
# in this block no other thread
# is allowed to aquire the lock
self._off_duration = value
@property
def on_duration(self):
return self._on_duration
@on_duration.setter
def on_duration(self, value):
with self.lock: # BLOCKING
self._on_duration = value
# in this block no other thread
# is allowed to aquire the lock
def start(self):
thread = threading.Thread(target=self.run)
thread.setDaemon(True)
thread.start()
def run(self):
while True:
with self.lock: # BLOCKING
# here we assign the two values from
# self.on_duration and self.off_duration
# during this time, the other lock will
# block, also the other assigments, which
# are realized with @porperty.setter
# will block this lock here.
# this will prevent race conditions
on_duration = self.on_duration
off_duration = self.off_duration
# during this time all other locks with
# using the same lock object have to wait
# a assigment is very short, so this
# will not affect your gui
# outside the lock block
# here we use the new variables, we have
# assigned before
# no blocking here
# and the sleep function won't block
# because it's outside of the lock
print('On', on_duration)
time.sleep(on_duration)
print('Off', off_duration)
time.sleep(on_duration)
foo = Foo(5, 5)
foo.start()
time.sleep(10)
foo.on_duration = 2
foo.off_duration = 2
time.sleep(10)
# program ends here, main thread is killd
# because there are no non-daemon threads left
Posts: 45
Threads: 27
Joined: Jul 2017
Aug-02-2017, 10:30 PM
(This post was last modified: Aug-02-2017, 10:30 PM by tony1812.)
Oh this is the code I have atfer incooperated yor class
import serial, time, threading #random
from tkinter import *
from time import sleep
#Prepare GPIO
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False) #disable annoying warning messages
GPIO.setup(40,GPIO.OUT)
GPIO.setup(38,GPIO.OUT)
#initially is off
GPIO.output(40,GPIO.LOW)
GPIO.output(38,GPIO.LOW)
# create a Relay_1 class
class Relay_1:
def __init__(self, on_duration, off_duration):
self._on_duration = on_duration
self._off_duration = off_duration
self.lock = threading.Lock()
@property
def off_duration(self):
return self._off_duration
@off_duration.setter
def off_duration(self, value):
with self.lock: # BLOCKING
# in this block no other thread
# is allowed to aquire the lock
self._off_duration = value
@property
def on_duration(self):
return self._on_duration
@on_duration.setter
def on_duration(self, value):
with self.lock: # BLOCKING
self._on_duration = value
# in this block no other thread
# is allowed to aquire the lock
def start(self):
thread = threading.Thread(target=self.run)
thread.setDaemon(True)
thread.start()
def run(self):
while True:
with self.lock: # BLOCKING
on_duration = self.on_duration
off_duration = self.off_duration
r1_relay_on()
#print('On', on_duration)
time.sleep(on_duration)
#print('Off', off_duration)
r1_relay_off()
time.sleep(off_duration)
# End Relay_1 class
# create a Relay_2 class
class Relay_2:
def __init__(self, on_duration, off_duration):
self._on_duration = on_duration
self._off_duration = off_duration
self.lock = threading.Lock()
@property
def off_duration(self):
return self._off_duration
@off_duration.setter
def off_duration(self, value):
with self.lock: # BLOCKING
# in this block no other thread
# is allowed to aquire the lock
self._off_duration = value
@property
def on_duration(self):
return self._on_duration
@on_duration.setter
def on_duration(self, value):
with self.lock: # BLOCKING
self._on_duration = value
# in this block no other thread
# is allowed to aquire the lock
def start(self):
thread = threading.Thread(target=self.run)
thread.setDaemon(True)
thread.start()
def run(self):
while True:
with self.lock: # BLOCKING
on_duration = self.on_duration
off_duration = self.off_duration
r2_relay_on()
#print('On', on_duration)
time.sleep(on_duration)
#print('Off', off_duration)
r2_relay_off()
time.sleep(off_duration)
# End Relay_2 class
def read_data():
#data = "Random number {}".format(random.randint(1, 99)) #Test dummy data
data = ser.readline()
#time.sleep(1)
return data
def r1_relay_on():
GPIO.output(40,GPIO.HIGH)
def r1_relay_off():
GPIO.output(40,GPIO.LOW)
def r2_relay_on():
GPIO.output(38,GPIO.HIGH)
def r2_relay_off():
GPIO.output(38,GPIO.LOW)
def update_value(string_var, ui_window):
data = read_data()
string_var.set(data)
ui_window.after(1000, update_value, string_var, ui_window)
#Slider function
'''
def get_on_slider_value(val):
#g_on_duration = val
print ("")#g_on_duration)
def get_off_slider_value(val):
#g_off_duration = val
print ("")#g_off_duration)
'''
ui = Tk()
ui.geometry("800x400+0+0")
ui.title("Command Center")
ser = serial.Serial('/dev/ttyUSB0', 9600)
var = StringVar()
var.set('Gather Sensor data.')
#slider
on_slider = Scale(ui, orient = HORIZONTAL, length = 300, width = 10)#, sliderlength = 60, from_ = 0, to = 100, command = get_on_slider_value)
on_slider.place(x=250, y=100)
off_slider = Scale(ui, orient = HORIZONTAL, length = 300, width = 10)#, sliderlength = 60, from_ = 0, to = 100, command = get_off_slider_value)
off_slider.place(x=250, y=140)
data_label = Label(ui, textvariable = var)
data_label.place(x=300, y=300)
r1 = Relay_1(1, 1)
r1.start()
r1.on_duration = 4 #float(g_on_duration)
r1.off_duration = 4 #float(g_off_duration)
r2 = Relay_2(1, 1)
r2.start()
r2.on_duration = 2 #float(g_on_duration)
r2.off_duration = 2 #float(g_off_duration)
ui.after(500, update_value, var, ui)
ui.mainloop()
#END
Posts: 2,128
Threads: 11
Joined: May 2017
I think my suggestion maybe tends into a wrong direction.
What do you want? Change the sliders and the thread should acquire the new values? No button to submit the changes?
The thread itself cat get the values out of the slider objects with variables. Also a Button with a command can submit the changes to the thread.
Posts: 45
Threads: 27
Joined: Jul 2017
Thank you so muuh for taking the time to hejp a dumb newbie like me. :) Your code actually works very well. does actulaay what I intend to do, it was a very good exercise for a python starter.
|