Python - breaking while loop with function (Raspberry Pi) - python

I have a project that involves a raspberry pi, the dothat 16x2 lcd display and some python code. I am essentially trying to make a dynamic while loop that displays information on the lcd. I am also trying to add a function that cancels the while loop by pressing one of the touch buttons (reference: https://github.com/pimoroni/dot3k/blob/master/python/REFERENCE.md)
This is what I got so far:
import dothat.lcd as l
import dothat.backlight as b
import dothat.touch as t
from time import sleep
import signal
import os
def main():
i=0
k=0
while True:
l.clear() # Clear LCD screen
b.hue(1.5) # Set background color
l.set_cursor_position(0, 1) # Set cursor position on LCD
l.write("%s" % k) # Write variable "k" to LCD
#t.on(t.CANCEL) # When CANCEL button is pressed then go to function
def cancel(ch, evt):
i=1 # Set variable "i" as 1
return
if i == 1:
break
k=k+1
sleep(1)
l.clear() # Clear LCD screen
b.off() # Turn the LCD Backlight off
cmd='pkill python' #
os(cmd) # Kill all python processes
signal.pause()
main()
The while loop is running but it won't break when the button is pressed. Ideas?

I fixed it, though I'm getting errors about the 'module' object not being callable regarding os(cmd).
The code:
def main():
global i
i=0
k=0
while True:
l.clear()
b.hue(1.5)
l.set_cursor_position(0, 1)
l.write("%s" % k)
#t.on(t.CANCEL)
def cancel(ch, evt):
global i
i=1
return
if i == 1:
break
k=k+1
sleep(1)
l.clear()
b.off()
cmd='pkill python'
os(cmd)
signal.pause()
main()

I don't have a dothat LCD display, so I can't test your code. But I think #Pigface333 was right, the i inside cancel is a local variable, thus the i in your if statement is not set to 1 after pressing Cancel. The following code demonstrates that:
from time import sleep
def main():
i = 0
k = 0
while True:
def cancel():
print "inside cancel"
i = 1
return
cancel()
if i == 1:
break
k = k+1
sleep(1)
exit(0)
main()
This will print inside cancel every 1 second, but won't exit, showing that the i inside cancel is a local variable. To fix it, you can create a class that stores the cancellation status:
from time import sleep
class Cancel(object):
def __init__(self):
self.is_cancelled = False
def cancel(self):
self.is_cancelled = True
def main():
canceller = Cancel()
while True:
canceller.cancel()
if canceller.is_cancelled:
break
sleep(1)
exit(0)
main()
The same method can be applied to your code:
import dothat.lcd as l
import dothat.touch as t
import dothat.backlight as b
from time import sleep
import signal
class Cancel(object):
def __init__(self,):
self.is_cancelled = False
#t.on(t.CANCEL)
def cancel(self, ch, evt):
self.is_cancelled = True
return
def main():
k = 0
cancel = Cancel()
while True:
l.clear() # Clear LCD screen
b.hue(1.5) # Set background color
l.set_cursor_position(0, 1) # Set cursor position on LCD
l.write("%s" % k) # Write variable "k" to LCD
if cancel.is_cancelled:
break
k = k+1
sleep(1)
l.clear() # Clear LCD screen
b.off() # Turn the LCD Backlight off
signal.pause()
exit(0)
main()
To help understanding why the original code didn't work and why using a class is a good idea, I suggest reading about Python's variable's scope and Object-Oriented Prograaming in Python.

Related

Run multiple function in python

I am writing a program for Raspberry Pi that is utilizing multiple function in classes. I have one class that is dedicated to determining when a capacitive touch sensor is clicked and another one class dedicated to determining the weight from HX711 chip. I want to automatize all: when click the capacitive sensor the cell start the weight scale and then, when I click the capacitive sensor for the second times the system call the function cleanAndExit() but I don't know how I can do this stuff.
import time
import sys
import RPi.GPIO as GPIO
from hx711 import HX711
GPIO.setmode(GPIO.BCM)
padPin = 21
GPIO.setup(padPin, GPIO.IN)
class WeightScale:
def cleanAndExit(self):
print "Clean GPIO..."
GPIO.cleanup()
print "Complete!"
sys.exit()
def cliccato(self):
hx = HX711(5, 6)
hx.set_reading_format("LSB", "MSB")
hx.reset()
hx.tare()
hx.set_reference_unit(-421)
while True:
time.sleep(0.1)
n = 100
//Run for max 100 seconds and create an array with the last value from weight scale sensor
for i in range (1, n):
val = max(0, int (hx.get_weight(5)))
add = []
hx.power_down()
hx.power_up()
time.sleep(0.6)
add.append(val)
add1 = []
add1.append(add[-1:])
print add1
time.sleep(1)
class TouchSensor:
def click(self):
alreadyPressed = False
while True:
padPressed = GPIO.input(padPin)
if padPressed and not alreadyPressed:
print "click"
time.sleep(2)
c=WeightScale()
t=TouchSensor()
In order you to correctly create a class, you need to define an init method with in the class definition. So for example:
class TouchSensor:
def __init__(self):
self.padPin = 21
def click(self):
alreadyPressed = False
while True:
padPressed = GPIO.input(padPin)
if padPressed and not alreadyPressed:
print "click"
time.sleep(2)
Then once you create your object of that class, it is initialized with padPin = 21. Then you use the methods within each class to perform what you're calling 'functions.'
t = TouchSensor()
t.click()
Try making adjustments and post whatever errors you end up with!
I make some adjustments to my code:
import time
import sys
from multiprocessing import Process
import RPi.GPIO as GPIO
from hx711 import HX711
GPIO.setmode(GPIO.BCM)
padPin = 21
GPIO.setup(padPin, GPIO.IN)
class WeightScale:
def cleanAndExit(self):
print ("Clean GPIO...")
GPIO.cleanup()
print ("Complete!")
sys.exit()
def pesatura(self):
hx = HX711(5, 6)
hx.set_reading_format("LSB", "MSB")
hx.reset()
hx.tare()
hx.set_reference_unit(-421)
while t.status==True:
time.sleep(0.1)
n = 100
for i in range (1, n):
val = max(0, int (hx.get_weight(5)))
add = []
hx.power_down()
hx.power_up()
time.sleep(0.6)
add.append(val)
add1 = []
add1.append(add[-1:])
print (add1)
time.sleep(1)
cleanAndExit()
class TouchSensor:
def __init__(self):
self.status = False
def changeStatus(self):
if self.status == False:
self.status = True
print (self.status)
else:
self.status = False
print (self.status)
def click(self):
while True:
padPressed = GPIO.input(padPin)
if padPressed:
print ("click")
t.changeStatus()
alreadyPressed = padPressed
time.sleep(0.5)
c=WeightScale()
t = TouchSensor()
if __name__ == '__main__':
p1 = Process (target = t.click())
p1.start()
p2 = Process (target = c.pesatura())
p2.start()
I have added a multiprocessing function and method changeStatus for the button click,I want to use it to start the loop of pesatura(self) when self.status==True and after, when self.Status== False (another click) start cleanAndExit(). But when I start the script it gives me the error ''cleanAndExit() takes exactly 1 argument (0 given)''.

tkinter change stringvar in loop but no effect on window

I want to change label every sec when i press the button in tk:
# --coding:utf-8 -----
from Tkinter import *
import time
import random
def test(a):
begin =time.time()
end =time.time()
while True:
ran = random.random()
after = time.time()
if(after-begin >1):
a.set(str(ran))
print a.get()
begin =after
if(after-end>10):
a.set('over')
break
t = Tk()
a = StringVar()
a.set('0')
b = Label(t,textvariable = a)
b.pack()
Button(t,text ='test',command = lambda x=a:test(a)).pack()
t.mainloop()
My console output is right,but it doesnot effect on windows.WHY?
You can start the test() function in a separate thread, like this:
# --coding:utf-8 -----
from Tkinter import *
import time
import random
import threading
def startThread(a):
threading.Thread(target=test, args=(a,)).start()
def test(a):
begin =time.time()
end =time.time()
while True:
ran = random.random()
after = time.time()
if(after-begin >1):
a.set(str(ran))
print a.get()
begin =after
if(after-end>10):
a.set('over')
break
t = Tk()
a = StringVar()
a.set('0')
b = Label(t,textvariable = a)
b.pack()
Button(t,text ='test',command = lambda x=a:startThread(a)).pack()
t.mainloop()
However, the thread won't end until the end of the 10 seconds, even if you close the window. You'll need to find some code for the while loop to check if the application is still running.
Creating a behavior that must execute for a given time period is a frequent problem; you can tackle it with a dedicated Waiter class; this avoids the use of threads that are poorly supported by tkinter.
The following example opens a window with a label and a button. When the button is pressed, the label will update every seconds for 10 seconds, then stop.
Pressing the button again within 10 seconds has no effect; after that, it restarts the process for 10 more seconds.
import Tkinter as tk # tkinter if python >= 3
import time
import random
class Waiter(object):
def __init__(self, waiting_time):
"""
:param waiting_time: int, in seconds
"""
self.waiting_time = waiting_time
self.expiring_time = time.time() + self.waiting_time
self.waiting = True
# print('waiter started')
def stop(self):
# print('waiter stopping')
self.expiring_time = None
self.waiting = False
def is_waiting(self):
"""returns True while waiting, false otherwise"""
if time.time() > self.expiring_time:
self.stop()
return self.waiting
def atest():
global waiter
if waiter is None:
waiter = Waiter(10)
_atest()
def _atest():
""" equivalent to:
while the waiter is waiting,
change the label every second),
then destroy the waiter
"""
global waiter
if waiter.is_waiting():
a.set(random.random())
# print(time.time())
t.after(1000, _atest)
else:
waiter = None
if __name__ == '__main__':
waiter = None
t = tk.Tk()
a = tk.StringVar()
a.set('0')
tk.Label(t, textvariable=a).pack()
tk.Button(t, text='test', command=atest).pack()
t.mainloop()
Note:
You could make _atest an inner function of atest, but maybe it is easier to understand as it is?
using import Tkinter as tk instead of from Tkinter import * prevents cluttering the namespace, and arguably makes the code clearer.
You should probably consider using python 3.

How to pause a thread (python)

The context:
I'm building a Graphical Interface with Qt creator and the "behaviour" file in python. A test version of my GUI is:
The expected behaviour:
I am running 2 different threads which are referred to the same function with different input arguments. With the SELECTOR button I can assign the value of 1 or 2 to a variable (and display it)
The button Start thread enables the correct thread to start (the first time).
The loop should be turned off by the stop button by modifying the global running variable.
This is my code
# -*- coding: utf-8 -*-
from PyQt4 import QtCore, QtGui, uic
import sys
import threading
import time
import Queue
running = False
first_thread = None
second_thread = None
form_class = uic.loadUiType("simple2.ui")[0]
q = Queue.Queue()
select = 0
def action(string, queue): #function called by threads
global running
while(running):
phrase = string
if queue.qsize() < 10:
queue.put(phrase)
#else:
# print queue.qsize()
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
#buttons
self.startButton.clicked.connect(self.start_clicked)
self.stopButton.clicked.connect(self.stop_clicked)
self.selector.clicked.connect(self.sel_click)
#variables
self.first = False
self.second = False
#queue
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.update_phrase)
self.timer.start(1)
def start_clicked(self): #start button callback
global select
if select > 0:
global running
running = True
print "started"
if (not self.first) & (select == 1):
first_thread.start()
self.first = True
if (not self.second) & (select == 2):
second_thread.start()
self.second = True
self.startButton.setEnabled(False)
self.startButton.setText('Starting...')
def stop_clicked(self): #stop button callback
global running
running = False
print "stopped"
self.startButton.setEnabled(True)
self.startButton.setText('Start Thread')
def sel_click(self): #selector button callback
global select
if select < 2:
select = select + 1
else:
select = 1
self.thread_counter.setText(str(select))
def update_phrase(self): #looping function
global running
if (not q.empty()) & running:
self.startButton.setText('Thread on')
abc = q.get()
print abc
def closeEvent(self, event):
global running
running = False
if __name__ == "__main__":
first_thread = threading.Thread(target=action, args = ("first", q))
second_thread = threading.Thread(target=action, args = ("second", q))
app = QtGui.QApplication(sys.argv)
w = MyWindowClass(None)
w.setWindowTitle('Multiple threads test in python')
w.show()
app.exec_()
For now, each thread should simple print on terminal their arguments ("First" or "Second").
If threads are started for the first time, my code works. But I would like to switch between threads infinite times.
Since threads cannot be stopped, is there a way to "pause" them?
I cannot find a solution, I hope someone will help me also with a piece of code. Thank you in advance
You can use Lock class to do that, a simple example would be:
import threading
lock = threading.Lock()
//here it will be lock
lock.acquire() # will block if lock is already held
...
then in other side do
//this will wake up
lock.release()
you can read more here http://effbot.org/zone/thread-synchronization.htm

End looping function on function return - Python

My question is a bit confusing so I will explain it by saying exactly what I am trying to do.
I just got a Raspberry Pi and am writing a Python project with it. I have a function that makes a light blink on and off infinitely. I want to use the blinking light to show a status of a job (one that could take awhile).
Here is the pseudo-code for what I am trying to do:
def blink():
while 1:
##light on##
time.sleep(.5)
##light off##
time.sleep(.5)
longRunningJob() #stop blinking when job returns
Any ideas?
You may use a class to pass a stop variable and finish thread like this:
import time
from threading import Thread
class Blink(Thread):
def __init__(self,starting_variable):
Thread.__init__(self)
print("starting variable: %s"%(starting_variable))
self.stop=False
def Stop(self):
self.stop = True
def blink(self):
print("light on")
##light on##
time.sleep(.5)
print("light off")
##light off##
time.sleep(.5)
def run(self):
while not self.stop:
self.blink()
print("exiting loop ...")
def longRunningJob():
for sleep_delay in range(5):
print("in longRunningJob with sleep: %s"%(sleep_delay))
time.sleep(sleep_delay)
blink=Blink("something")
blink.start()
longRunningJob()
blink.Stop()
print("END")
Here is the solution
import threading
import time
RUNNING = False
def blink():
while RUNNING:
##light on##
time.sleep(.5)
##light off##
time.sleep(.5)
t = threading.Thread(target=blink)
RUNNING = True
t.start()
longRunningJob() #stop blinking when job returns
RUNNING = False

python add time to a countdown already running

I want to have an app where if I click a button I add X amount of time to my running countdown timer.
I'm guessing I have to use threads for this but am not sure how to implement it..
Here is the code I have so far:
def countdown_controller(add_time):
end_it = False
def timer(time_this):
start = time.time()
lastprinted = 0
finish = start + time_this
while time.time() < finish:
now = int(time.time())
if now != lastprinted:
time_left = int(finish - now)
print time_left
lastprinted = now
if end_it == True:
now = finish
time.sleep(0.1)
# Check if the counter is running otherwise just add time.
try:
time_left
except NameError:
timer(add_time)
else:
if time_left == 0:
timer(add_time)
else:
add_this = time_left
end_it = True
while now != finish:
time.sleep(0.1)
timer(add_time + add_this)
Obviously this will not work, because every time I call countdown_controller(15) fx, it will start counting down for 15 seconds and if I click my button nothing happens until the timer is ended.
Help would be greatly appreciated.
I would say that there is a flaw in the design of the code, because your screen output blocks down the entire program doing nothing (time.sleep(0.1)).
Typically what you want to to do in these cases is having a main loop in your program that cycles through the various operations that make your program run. This guarantees a sensible distribution of system resources between the various tasks.
In your specific case, what you would like to have in your main loop is:
Check user input (has extra time been added?)
Update output of the countdown
Example implementation:
import time
import curses
# The timer class
class Timer():
def __init__(self):
self.target = time.time() + 5
def add_five(self):
self.target += 5
def get_left(self):
return int(self.target-time.time())
# The main program
t = Timer()
stdscr = curses.initscr()
stdscr.nodelay(True)
curses.noecho()
# This is the main loop done in curses, but you can implement it with
# a GUI toolkit or any other method you wish.
while True:
left = t.get_left()
if left <= 0:
break
stdscr.addstr(0, 0, 'Seconds left: %s ' % str(left).zfill(3))
c = stdscr.getch()
if c == ord('x') :
t.add_five()
# Final operations start here
stdscr.keypad(0)
curses.echo()
curses.endwin()
print '\nTime is up!\n'
The above program will increase the counter of 5 seconds if you press the x key (lowercase). Most of the code is boilerplate to use the curses module, but of course if you use PyGTK, PySide or any other graphical toolkit, it will be different.
EDIT: As a rule of thumb, in python you want to avoid threading as much as you can, both because it often (but not always) slows down programs (see "Global Interpreter Lock") and because it makes software harder to debug/maintain.
HTH!
I would probably have a Timer object with a finish attribute that I could simply add an int to. Have that timer running in another thread that you can then query for the current time remaining from your GUI.
class Timer(object):
def __init__(self, length):
self.finish = time.time() + length
def get_time(self):
return time.time() >= self.finish

Categories

Resources