How to communicate variable between two functions using add_event_detect? - python

To simplify a complicated project, I have one switch and 1 pushbutton. What I want is to have code do separate functions when the button is pressed for both positions of the switch.
So I have two files. The main file sets up some gpio and event detects. Another file has a callback function defined for the pushbutton. Here is the main file:
import Adafruit_GPIO as GPIO
gpio = GPIO.get_platform_gpio()
global staff_mode
staff_mode = 0
Bg1 = 24
global audio_latch
audio_latch = 13
global image_latch
image_latch = 26
def staff_mode_audio():
staff_mode_write(0)
global staff_mode
if not gpio.input(audio_latch) and gpio.input(image_latch):
staff_mode=0;
print('You are in audio setup')
print(staff_mode)
def staff_mode_image():
staff_mode_write(1)
global staff_mode
if not gpio.input(image_latch) and gpio.input(audio_latch):
staff_mode=1;
print('You are in image setup')
gpio.add_event_detect(Bg1, GPIO.FALLING, callback = lambda x:grid_input(1,page_select,current_mode,staff_mode), bouncetime=debounce_time)
gpio.add_event_detect(image_latch, GPIO.FALLING, callback = lambda x: staff_mode_image(), bouncetime=300)
gpio.add_event_detect(audio_latch, GPIO.FALLING, callback = lambda x: staff_mode_audio(), bouncetime=300)
try:
while True:
signal.pause()
except KeyboardInterrupt:
gpio.cleanup()
The key being that the the lines image_select_write('num') is defined in another file but basically just writes a 0 or 1 to a text file. The next file is:
def staff_mode_write(num):
file=open('image_select.txt', 'w')
file.write(str(num))
file.close()
def staff_mode_read():
file=open('image_select.txt', 'rt')
image_select=int(file.read())
file.close()
return image_select
def grid_input(grid_select,page_select,current_mode,staff_mode):
staff_mode = staff_mode_read()
if (staff_mode == 0):
#Do stuff
staff_mode_write(1)
else:
#Do other stuff
staff_mode_write(0)
So the callback function grid_input reads the text file to determine what function the pushbutton should perform. I tried unsucessfully to communicate the value of staff_mode using global variables.
This solution worked but it was clunky. How can I communicate the status of staff_mode without using a text file? Thank you!

You should indeed be able to use a global for this. However you have to declare the global wherever it is used. You can't just declare it at the top of your module. In other words you have to change grid_input to this to access the global.
def grid_input(grid_select,page_select,current_mode):
global staff_mode
if (staff_mode == 0):
#Do stuff
else:
#Do other stuff
However the goal of your code is not that clear to me so I think you could probably come up with a better approach.

Related

ROS How to pass a callback variable into another file?

I have a callback function in a file "color.py" that constantly recieves incoming data but updates a global variable every 3 seconds:
last_color = ""
last_calltime = datetime.strptime("00:00:00","%H:%M:%S")
def callback(data):
global last_calltime, last_color
cube = data.data
t = time.localtime()
tf = time.strftime("%H:%M:%S",t)
current_time = datetime.strptime(tf, "%H:%M:%S")
delta_time = current_time - last_calltime
if abs(delta_time.total_seconds()) >= 3:
if "Red" in cube:
last_color = "Red"
elif "Green" in cube:
last_color = "Green"
else:
last_color = "Blue"
t = time.localtime()
tf = time.strftime("%H:%M:%S",t)
last_calltime = datetime.strptime(tf, "%H:%M:%S")
return last_color
if __name__ == '__main__':
try:
rospy.init_node('color')
rospy.Subscriber ('/topic',String, callback)
rospy.spin()
In another file, I need to reference this "last_color" variable that updates. So far the function I have in this file:
from color import last_color
a = 0
def observe_color(a)
if a == 0:
return last_color
else:
return "None"
I need this to return the updated variable from the "color.py" file but it only returns the initialized value for last_color which is "". How can I do this?
When you import something in Python, you're effectively asking it to execute everything in that module in the same runtime as your current program.
Suppose I have some module color.py with variable last_color='green'
And in a different program observer.py I have from color import last_color
The last_color that exists in color.py and the last_color that exists in observer.py are different places in computer memory - they do not reference the same string value.
A quick (and maybe dirty) solution to this is just to re-import the module every time you want to observe one of its member variables.
I don't think it's gonna work that way. How about save the value of last_color to a file and monitor it ? Because to me you run 2 separated programs, 1 with the ROS and another just to monitor last_color
while True:
with open(filename, 'r') as f:
color = f.readline()
time.sleep(timesteps)
When write to file in the main, you could also append the read value and also the time, to get timeline data.
Since this is all done in ROS it would be easiest to simply have the separate file initialize as a ros node with a subscriber. If for some reason that's not the case you should make color.py a class and have the other file initialize an object. Take the following simplified example:
color.py
class colorPy:
def __init__(self):
self.last_color = None
rospy.init_node('some_node')
tmp = rospy.Subscriber('/some_topic', self.callback)
def callback(self, msg):
self.last_color = msg.data
other file
from color import colorPy
from time import sleep
local_var = colorPy()
sleep(5)
print(local_var.last_color)

How handle unexpected Key press

I have designed an application which have two buttons i.e CAL and SAV.
Accordingly that I have two functions but the issue is sometimes production line Operator by mistake presses SAV button. So that attribute error arises and program stuck.
How to overcome this issue? Please guide me.
Here is my code:
class ADS1x15:
"""Base functionality for ADS1x15 analog to digital converters."""
class ADS1115(ADS1x15):
"""Class for the ADS1115 16 bit ADC."""
class AnalogIn:
"""AnalogIn Mock Implementation for ADC Reads."""
import RPi.GPIO as GPIO
import tkinter as tk
GPIO.setmode(GPIO.BCM)
GPIO.setup(12,GPIO.IN) #Save Button
GPIO.setup(5,GPIO.IN) #Cal Button
root=tk.Tk()
root.geometry("1000x600")
file = open("/home/pi/data_log.txt", "r")
f = file.read().split(',')
rangeh = int(f[3])
offset = int(f[4])
fullScale = int(f[5])
chan=AnalogIn(ads,P0,P1)
def cal(channel):
global Dsel,cal_c,rangeh,offset,fullScale,chan
cal_c = cal_c + 1
if cal_c == 1:
root.L1 = tk.Label(root,text="Put Zero Weight and Press CAL btn",fg="brown",font="bold")
root.L1.pack()
root.L1.place(x=1,y=1)
elif cal_c == 2:
root.L1.destroy()
offset = chan.value
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh)+","+str(offset)+","+str(fullScale))
file.flush()
root.L2 = tk.Label(root,text="Put Full Weight and Press SAV btn",fg="brown",font="bold")
root.L2.pack()
root.L2.place(x=1,y=1)
def sav(channel):
global rangeh,offset,fullScale
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh)+","+str(offset)+","+str(fullScale))
file.flush()
root.L2.destroy()
def update():
""" function for continuous show value in every 500ms in tkinter window"""
GPIO.add_event_detect(5,GPIO.RISING,callback=cal,bouncetime=1000)
GPIO.add_event_detect(12,GPIO.RISING,callback=sav,bouncetime=1000)
root.after(500,update)
root.mainloop()
This error generated due to root.L2.destroy() this line.
Can I block or disable this sav function, so that without call of cal function, it shouldn't execute?
A brute force solution would be to check whether root has an L2 attribute or not
from tkinter import messagebox
def sav(channel):
if hasattr(root, 'L2'):
global rangeh, offset, fullScale
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh) + "," + str(offset) + "," + str(fullScale))
file.flush()
root.L2.destroy()
else:
messagebox.showinfo('Unable to save', 'No data was generated yet')
A more elegant approach would be to disable the save button on startup and only enable it after the cal function has been executed.
I am not very familiar with Raspberry Pi implementations, so this is only a rough sketch on how to achieve the button disabling:
By the looks of it, the buttons are "wired in" via the GPIO.add_event_detect functions.
So i would remove the sav-callback from the main script and dynamically add it after the cal script, something like that:
# [...] beginning of your script [...]
def cal(channel):
# [...] original body of cal function [...]
activate_save_button()
def activate_save_button():
GPIO.add_event_detect(12, GPIO.RISING, callback=sav, bouncetime=1000)
def deactivate_save_button():
GPIO.remove_event_detect(12)
def sav(channel):
# [...] original body of sav function [...]
# remove save button functionality after saving
deactivate_save_button()
def update():
""" function for continuous show value in every 500ms in tkinter window"""
GPIO.add_event_detect(5, GPIO.RISING, callback=cal, bouncetime=1000)
# line with callback=sav is deleted here
root.after(500, update)
root.mainloop()

Best way to run code once within while loops?

I'm writing a text-based RPG for a class, and am stuck on a code predicament...
from tkinter import *
...
runOnce = False
nextRun = False
"""Main Loop"""
while True:
#New Game initialize
if you.rName == None and runOnce == False:
log("What is your name, adventurer?", eventLog)
runOnce = True
if you.rName != None and nextRun == False:
log(f'What is your profession, {you.rName}?', eventLog)
nextRun = True
#keypresses
playerInput.bind("<Return>", keyPress)
playerInput.bind("<FocusIn>", focusIn)
top.update()
top.update_idletasks()
What I have currently works, but there are a ton more if-statement type situations that need responses before continuing to the next statement. The loop is to continuously update the GUI as the game is run.
How can I code the something that needs a response once within a while loop efficiently?
Seeing the clarification, I agree with the comments these type of actions shouldn't belong in the loop. These should be all data that are collected prior to the main game loop.
If you're looping through these to validate for inputs, you can have separate loops instead:
while not you.name:
you.name = input('Enter name: ')
# some additional validation text if necessary...
while not you.job:
you.job = input('Enter job: ')
# some additional validation text if necessary...
while True:
# main game loop requiring you.name, you.job
Another approach is a bit contrived. You can pre-define these functions before your mainloop and create a RunOnce class to only execute these functions once:
class RunOnce(object):
def __init__(self, func):
self.func = func
self.ran = False
def __call__(self):
if not self.ran:
self.func()
self.ran = True
# once the function has been called,
# flip self.ran so it won't fire again.
# Decorate your functions with this special class
#RunOnce
def get_name():
you.name = input('Enter Name: ')
#RunOnce
def get_job():
you.job = input('Enter Job: ')
And then when you are in the main game loop:
while True:
get_name() # this will run once
get_job() # this too will run once
get_name() # this won't run anymore
get_job() # this neither.
The benefit of this approach is it gives you the flexibility to re-run the function if necessary:
get_name() # get_name.ran becomes True
get_name.ran = False # reset the flag
get_name() # this will run again.
I'd still say it's much better to just restructure your code so that things that only need to be captured once stay outside the main loop.
Try checking your parameters for null before using them. If you're looking for user input you can do this:
userInput = None
while True:
userInput = input("Do the thing only once") if userInput is None else userInput
...

Python 3 Save Buttons

I have a button that can create another buttons, but when I close the program, the buttons I just created disappear.
I know how to save things in a file using pickle, but how should I write the code so that it saves the buttons and creates them again when I open the program.
code:
def CreateButton():
global name,clicked,started
started=True
name = str(input("Write a Student Name..."))
print('variable changed...')
clicked=True
def update():
global MainWindow,changed,clicked,name
#global clicked
if clicked and started:
#name = input("Write a Student Name: ")
button_new=needed_but(MainWindow=MainWindow,color="#E6E6E6",text=name)
clicked=False
buttonred=False
MainWindow.after(1*1000,update)
class needed_but:
def __init__(self,MainWindow,color,text):
console = Button(MainWindow, text=text,bg=color, command=self.changecolor)
console.pack()
self.instance=console
def changecolor(self):
buttonred,buttongreen,buttonblue=get_color()
global clicked,misses_dict
#clicked=True
#global buttoncolor
if buttonred == True:
self.instance.configure(bg = "#ff0000")
dun = self.instance.cget('text')
print(dun)
if dun in misses_dict:
misses_dict[('%s' %(dun))] += 1
else:
misses_dict[('%s' %(dun))] = 1
pickle.dump(dictionary, open("%s\SI\SIA.dat" %(path), 'wb'))
print(misses_dict)
buttonred = False
elif buttongreen == True:
self.instance.configure(bg = "#00ff00")
elif buttonblue == True:
self.instance.configure(bg = "#2e9afe")
how should I write the code so that it saves the buttons and creates them again when I open the program.
You can't. The objects are actually objects within an embedded tcl interpreter. There's no way to save the state of the interpreter.
You'll have to write code to save all of the information about the widgets in some sort of format where you can read it back and recreate the widgets.

How do I write my generated information from python into a file?

I was wondering whether I could write the information of the attributes of each character into a text file and how would I be able to do this? The attributes on the notepad file need to be updated every time someone plays the game.
def strength():
dice12 = random.randint(1,12)
dice04 = random.randint(1,4)
strfinalstats = dice12/dice04+10
print ">",strfinalstats,"strength"
def skill():
dice12 = random.randint(1,12)
dice04 = random.randint(1,4)
skfinalstats = dice12/dice04+10
print ">",skfinalstats,"skill!"
def player1():
name1=raw_input("what would you like to call the first player?")
print name1,"has:"
time.sleep(1)
strength()
time.sleep(1)
skill()
def player2():
name2=raw_input("what would you like to call the first player?")
print name2,"has:"
time.sleep(1)
strength()
time.sleep(1)
skill()
def player3():
name3=raw_input("what would you like to call the first player?")
print name3,"has:"
time.sleep(1)
strength()
time.sleep(1)
skill()
First you need:``
f = open('filename', 'r+')
You can put right below each attribute:
f.write(attributes)
The attributes on the notepad file will update every time someone plays the game.
P/s: Why you do not declear a class name "player" then create three instance?

Categories

Resources