Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Whats wrong with my thread?
#1
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

    
Reply
#2
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
Reply
#3
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.
Reply
#4
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.
Reply
#5
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
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#6
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
Reply
#7
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.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#8
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.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Am I wrong or is Udemy wrong? String Slicing! Mavoz 3 2,385 Nov-05-2022, 11:33 AM
Last Post: Mavoz
  Whats wrong with the elif? inunanimous93 3 2,403 Nov-30-2020, 03:58 AM
Last Post: deanhystad
  Whats Wrong!? rjay81 3 2,214 May-13-2020, 08:02 PM
Last Post: rjay81
  Can u see this code and tell whats wrong with it? AhmadKamal 14 5,182 Apr-29-2020, 11:09 AM
Last Post: jefsummers
  Error SQLite objects created in a thread can only be used in that same thread. binhduonggttn 3 15,385 Jan-31-2020, 11:08 AM
Last Post: DeaD_EyE
  python gives wrong string length and wrong character thienson30 2 2,940 Oct-15-2019, 08:54 PM
Last Post: Gribouillis
  Whats a good design/approach? hshivaraj 1 1,742 Sep-16-2019, 01:44 AM
Last Post: snippsat
  elevator simulator...whats the wrong at this code? tasos710 5 5,836 Jun-11-2019, 01:38 AM
Last Post: micseydel
  whats the difference between sys.exit() and break? mitmit293 1 4,081 Jan-27-2019, 09:46 PM
Last Post: ichabod801
  Whats wrong with this code? student1 1 2,371 May-18-2018, 04:19 PM
Last Post: skorpius_

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020