Whats wrong with my thread? - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Whats wrong with my thread? (/thread-4227.html) |
Whats wrong with my thread? - tony1812 - Aug-01-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 RE: Whats wrong with my thread? - Larz60+ - Aug-01-2017 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-concurrency-from-the-ground-up-live.html RE: Whats wrong with my thread? - tony1812 - Aug-01-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. RE: Whats wrong with my thread? - Larz60+ - Aug-01-2017 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/multiprocessing.html#multiprocessing.Semaphore or for messaging, a queue: https://docs.python.org/3/library/multiprocessing.html#pipes-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. RE: Whats wrong with my thread? - DeaD_EyE - Aug-01-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 RE: Whats wrong with my thread? - tony1812 - Aug-02-2017 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 RE: Whats wrong with my thread? - DeaD_EyE - Aug-03-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. RE: Whats wrong with my thread? - tony1812 - Aug-04-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. |