How to loop with button input, cycle between different options in python? - python

I have a loop that takes a three way switch input and selects an option on power up of a camera:
# Set GPIO input
switchColorOne = pyb.Pin("P9", pyb.Pin.IN, pyb.Pin.PULL_UP)
switchColorTwo = pyb.Pin("P7", pyb.Pin.IN, pyb.Pin.PULL_UP)
#Set color pallete by switch
if switchColorOne.value() == 0:
sensor.set_pixformat(sensor.RGB565)
elif switchColorTwo.value() == 0:
sensor.set_pixformat(sensor.GRAYSCALE)
else:
sensor.set_color_palette(sensor.PALETTE_IRONBOW)
sensor.set_pixformat(sensor.RGB565)
I would like to take the input from a single push button to cycle through the three options during the switch, preferably with a while loop so it can happen continually. I can't figure out how to make this happen, do I need a debouncer, can I use a for loop to iterate through different lines of code?

You said above was button is in off state,
You can try like this.
switchColorOne = pyb.Pin("P9", pyb.Pin.IN, pyb.Pin.PULL_DOWN)
switchColorTwo = pyb.Pin("P7", pyb.Pin.IN, pyb.Pin.PULL_DOWN)
#Set color pallete by switch
if switchColorOne.value() == 1:
sensor.set_pixformat(sensor.RGB565)
elif switchColorTwo.value() == 1:
sensor.set_pixformat(sensor.GRAYSCALE)
else:
sensor.set_color_palette(sensor.PALETTE_IRONBOW)
sensor.set_pixformat(sensor.RGB565)````

The problem is you are just creating arbitrary conditions and your program has no way to know to go back to them when you press a button. If you want to make this work in a non-blocking manner you should use interrupts
C1 = pyb.Pin("P9", pyb.Pin.IN, pyb.Pin.PULL_UP)
C2 = pyb.Pin("P7", pyb.Pin.IN, pyb.Pin.PULL_UP)
sensor.set_color_palette(sensor.PALETTE_IRONBOW)
sensor.set_pixformat(sensor.RGB565)
def update(pin):
globals C1, C2, sensor #you might not even need this line
if pin is C1: sensor.set_pixformat(sensor.RGB565)
elif pin is C2: sensor.set_pixformat(sensor.GRAYSCALE)
C1.irq(update, pyb.Pin.IRQ_FALLING)
C2.irq(update, pyb.Pin.IRQ_FALLING)
You may have to switch to IRQ_RISING or pull your pins down. This gets you in the ballpark, though.

Related

How to properly update tkinter label element

I'm working on creating a stopwatch that is given two values. It starts with value A and counts down to 0, then changes to value B, counts down to 0, then goes back to value A, counts down to 0 etc until I close the program (I'll probably add a pause button at some point) and overall it's working really well. However, when it updates the label with the new text, It seems to just be making a new text item and putting it as a layer on top of the previous. I can see then when I go from a single double-digit number to a single digit, and the sentence is shortened, the part of the old sentence that isn't covered can still be seen. So I'm hoping I'm just missing something incredibly simple. The newWindow.update() is what I thought would update the window but it does not appear to be doing that. Below is my snippet of code that handles the logic.
def countdown(timer_count,count_type):
counter = timer_count
count_type = count_type
while counter >= 0:
timer = tk.Label(newWindow, text=f"{count_type} for: {counter}")
timer.config(font=("TkDefaultFont",30))
timer.grid(row=0,column=2)
newWindow.update()
time.sleep(1)
counter -= 1
print(counter)
if count_type == "work":
count_type = "rest"
elif count_type == "rest":
count_type = "work"
return count_type
def interval():
counter_type = "work"
while True:
if counter_type == "work":
counter_type = countdown(int(exer_var.get()),counter_type)
elif counter_type == "rest":
counter_type = countdown(int(rest_var.get()),counter_type)
You are creating a new Label widget each time through your while loop instead of changing the text inside the while loop. That is why it is layering one widget on top of the other, so you need to create your widget, then run the while loop, and set the text to change in the timer.config inside the loop. You should also declare the font in the original tk.Label, no need to change that each trip through the loop. For "some_starting value" it would probably be text = counter
timer = tk.Label(newWindow, font=("TkDefaultFont",30), text="some_starting_value")
while counter >= 0:
timer.config(text=f"{count_type} for: {counter}")
timer.grid(row=0,column=2)
Its hard to say from your code where this is taking place, but here is how its usually done:
Make the label outside all functions in the main block.
timer = tk.Label(newWindow,font=("TkDefaultFont",30)) # Adding the font here itself
Then now inside the function just change its value using config:
def countdown(timer_count,count_type):
counter = timer_count
count_type = count_type
while counter >= 0:
timer.config(text=f"{count_type} for: {counter}") # Update the options for the created label.
timer.grid(row=0,column=2)
So now each time function/loop is running, new labels wont be created and overwritten, instead existing label will be configured.
On a side note, using time.sleep() and while loop is not the very best practice, even with update() it will still cause some kind of disturbance to GUI. Instead, re-arrange your code to use after(ms,func) method, which will not freeze the GUI. You can ask a new question on that if you face any trouble, later.

How To Print Grid In Python Without "Flashing" Screen

In the program I've been working on in Python, I need to be able to print a list of elements one by one, going to a new line after n elements to form a grid. However, every time the program reprints the grid, you can see it progressing element by element, which looks rather ugly and distracting to the user. I was wondering if there was a way to "pause" the console output for a brief amount of time to allow the grid to be printed, then show the grid afterwards, erasing the previous printout, as to not show it printing element by element. The reason I need to do this is because the program uses Colorama for colored outputs, but different elements in the list will need to have different colors, meaning each element has to be printed one by one.
EDIT (Current code):
import time as t
from os import system as c
h = 50
w = 50
loop1 = 0
ostype = "Windows"
def cl():
if(ostype == "Linux"):
c('clear')
if(ostype == "Windows"):
c('cls')
def do():
grid = []
for x in range(0,h):
temp = []
for z in range(0,w):
temp.append("#")
grid.append(temp)
for a in range(0,h):
for b in range(0,w):
print(grid[a][b], flush=False, end="")
print()
while(loop1 == 0):
do()
t.sleep(1)
cl()
You can probably tell print to not to flush the standard out buffer, and have the last print to flush everything. Depends on what version of python you're using, for python 3 print function takes a flush argument, set that to true/false accordingly.

How to update polygon size with cumulative key press in Psychopy

I am using Psychopy to create a psychological task. For a given routine, I would like the height of a polygon (rectangle) to increase with every key press (same key every time), until it reaches the maximum number of key presses (e.g. 10). I cannot figure out how to create a loop to count number of key presses within the same routine, nor how to use this to generate a variable that will constantly update the size of the polygon.
Here is what I have tried as code in the routine which gets stuck in while loop... and I am not sure if the loop should go in the code "Before Routine" or for "Each Frame"
total_key_count = 0
while True:
resp_key = event.waitKeys(keyList=['1'])
if resp_key == '1':
total_key_count = total_key_count + 1
# .. or break out of the loop if reach 10
elif total_key_count == 10:
break
Thanks!
Never use event.waitKeys() in a Builder code component. Builder is structured around a drawing loop that requires updating the screen and responding to events on every screen refresh. If you call waitKeys(), you pause execution completely until a key is pressed, which will completely break Builder's temporal structure.
In the Begin routine tab, put this:
key_count = 0
max_keys = 10
In the Each frame tab, put this:
key_press = event.getKeys('1')
if key_press: # i.e. if list not empty
key_count = key_count + 1
if key_count <= max_keys:
# increment the height of the stimulus by some value
# (use what is appropriate to its units):
your_stimulus.size[1] = your_stimulus.size[1] + 0.1
else:
# terminate the routine (if required)
continueRoutine = False
Note that getKeys(), unlike waitKeys() just checks instantaneously for keypresses. i.e. it doesn't pause, waiting for a key. This is fine though, as this code will run on every screen refresh, until the required number of keys have been pushed.
Presumably you also need to save some data about the response. This would best be done in the End routine tab, e.g.
thisExp.addData('completion_time', t) # or whatever needs recording

Why isn't my if statement working? (graphics.py related)

I'm creating a mini program & I'm trying to check whether the user inputs what I want them to -- They're only supposed to type one of the following: happy, sad, angry,nervous,excited but for some reason its ignoring that entire if statement and even the table (rectangle) I drew doesn't appear as well?
from graphics import *
win = GraphWin("Moods", 800, 500)
#Creating the input Box + A Go Button.
inputBox=Entry(Point(400,250),12)
inputBox.draw(win)
colour=inputBox.getText().lower()
message=Text(Point(400,50),"Click to go next!")
message.setFace('courier')
message.setSize(20)
message.draw(Win)
submsg1=Text(Point(400,100),"")
submsg1.setText("(Allowed moods are: Happy, Sad, Angry, Nervous,
Excited)")
submsg1.setFace('courier')
submsg1.setSize(15)
submsg1.setStyle('italic')
submsg1.draw(win)
clickPoint = win.getMouse()
#Checking user inputs the right way.
if not colour.isalpha():
error=Text(Point(400,300),"Please type either: happy, sad, angry")
error.draw(win)
elif (colour !="happy" or colour !="sad" or colour !="angry"):
error=Text(Point(400,300),"Please type either: happy, sad, angry")
error.draw(win)
else:
#Clearing Second Frame, for next screen.
inputBox.undraw()
goButton.undraw()
error.undraw()
message.undraw()
submsg1.undraw()
#Moving to next frame.
table=Rectangle(Point(50,400),Point(750,400))
table.setFill("blue")
table.draw(win)
Basically what is happening with your code is that this line here;
elif (colour !="happy" or colour !="sad" or colour !="angry"):
this will execute if one of the conditions is true because of how the or works. because one of them will always be true (because the user cannot enter happy and sad at the same time).
so for your example you will want the and function as then all of the conditions will have to be true for it to run.
elif (colour !="happy" and colour !="sad" and colour !="angry"):
now to finish off you need to move this line colour=inputBox.getText().lower() to below this line clickPoint = win.getMouse() but before the if because getText is an event which executes when you call it, so at the moment when you call it at beginning it gets nothing because the user hasnt entered anything yet.
so it should look like this;
clickPoint = win.getMouse()
colour=inputBox.getText().lower()
#Checking user inputs the right way.
if not colour.isalpha():
Instead of
elif (colour !="happy" or colour !="sad" or colour !="angry"):
use
elif (colour !="happy" and colour !="sad" and colour !="angry"):
(and instead of or) as your original condition is always satisfied.

Make text appear based on key press (give user real time feedback) in psychopy

I have a stimulus loop in psychopy that displays images for 4 seconds that subjects make binary decisions about. I would like to give them feedback as to which choice they have made.
I.e.: a image gets displayed for 4 seconds with white 'YES' and 'NO' displayed on either side of it. When the user presses a key, the corresponding word turns red. If they then press a different key, it switches. After 4 seconds, the next image appear with white words.
Does anyone know how to go about doing this? Many thanks for any suggestions.
You can do this with a custom code component. Add the code component to your routine.
Under the "Each Frame" tab add the following code:
if (t >=4) and (t < 8):
if clear_keys:
event.getKeys()
clear_keys = False
else:
theseKeys = event.getKeys(keyList=['y', 'n'])
if 'y' in theseKeys:
Yes.color = 'red'
No.color = 'white'
elif 'n' in theseKeys:
Yes.color = 'white'
No.color = 'red'
Under the "Begin Experiment" tab add the following code:
clear_keys = True
You will need to change the Yes and No objects in the script to the names of your text components. You will also need to change the number 4 to the start time of the picture and number 8 to the end time.
Here is a picture of my trial as an example.

Categories

Resources