i'm currently developping a program that runs on a robot. The program needs to add positions to a list but you have to push a button to add a new position. This has to be inside a while loop. But when i press the button the function repeats itself multiple times instead of just 1. Here is the part of code:
while not self.done():
if self._io_left_lower.state:
self._gripper_left.open()
elif self._io_left_upper.state:
self._gripper_left.close()
if self._io_right_lower.state:
self._gripper_right.open()
elif self._io_right_upper.state:
self._gripper_right.close()
if self._io_left_nav.button0:
print("repeats")
#self._mapping.append([self._limb_left.joint_angles(), self._gripper_left.position()])
if self._io_right_nav.button0:
self._mapping.append([self._limb_right.joint_angles(), self._gripper_right.position()])
if self._io_left_nav.button1:
self.stop()
As you can see when the self._io_left_nav.button0 is pressed it will now just print 'repeats' several times but it has to to be printed just onces. same for the self._io_right_nav.button0.
If button0 is a physical button and your loop is polling the button state then it is normal that many iterations run in the time your button is being pressed. You have several options to solve this situation, two of them are:
Do not use a loop check the button but hardware interruptions used to keep an updated representation of your state.
If you still want to use your loop, you should detect button state changes instead checking its current state at each iteration. That may require your program to implement a noise filter.
A very simple way to implement the second option is to have a button state variable with a timestamp associated: At each loop iteration, if the difference between the current time and the timestamp is big enought then you should check if the current state is the same than the one stored in the variable. If not then you have detected a change. In any case, at this point you should update your state-timestamp variable.
This is an example:
from time import time
prevState = (False,time())
while not self.done():
current = time()
if self._io_left_nav.button0 and (current-prevState[1])*1000>2: #For example, 2 ms
if self._io_left_nav.button0 != prevState[0]:
print("repeats")
prevState = (self._io_left_nav.button0,time())
I presume that's some kind of flag, that the button was pressed.
If you don't clear the state, the condition in the if statement will evaluate true. Again, and again....
Related
What I'm trying to do:
Add a fourth mode, let's call it SendsMode, to the Session page of my Launchpad—AKA when hitting the "Stop-Solo-Mute" button a fourth time, it would cycle into SendsMode.
In this mode, the bottom row(s) of buttons would turn the sends on/off for each track within the Session Ring.
Where I am now:
I have functioning code that adds SendsMode, and works for one row of sends perfectly. It changes with movements of the Session Ring.
What I need help with:
I can't get it to work for more than one send at a time (the buttons go blank, but don't do anything when pressed, not reflect changes in the values done in Ableton).
Full code is here: https://github.com/jonniepeller/launchpad-mini-mk3-augmented
The relevant bit is adding the following to _create_stop_solo_mute_modes:
self._stop_solo_mute_modes.add_mode(
u"send_controls",
AddLayerMode(self._mixer, Layer(send_controls=bottom_x_rows)),
cycle_mode_button_color=u"Mixer.SendControls",
)
self._stop_solo_mute_modes.selected_mode = u"send_controls"
self._stop_solo_mute_modes.set_enabled(True)
_set_send_controls in Novation's mixer component wasn't appropriate for multiple sends—it was built and used for one thing per track, like mute, solo, stop.
I implemented my own MixerComponent and NovationBase with an adapted version of what they have for the Launch Control XL.
This is the key bit that fixed my issue:
def set_send_controls(self, controls):
num_sends = len(self._song.return_tracks)
for channel_idx, channel_strip in enumerate(self._channel_strips):
send_controls = []
for send_idx in range(num_sends):
if controls:
try:
button = controls.get_button(send_idx, channel_idx)
except:
logger.info("Tried getting a button out of range")
send_controls.append(button)
channel_strip.set_send_controls(send_controls)
In my experiment, I have 10 trials, and at each trial, the participant has to press a key ("space") asap after seeing a change of colour in the animation. As feedback that confirms the key pressing, I'd like to move to the next trial (move to the next loop iteration) after the key was pressed. I tried to implement the idea in my code with break and continue, but it doesn't work:
for i in range(nTrials):
# Start with fixation cross
fixation.draw()
win.flip()
core.wait(2)
# Play the video for 200 frames
for Nframes in range(200):
optic_flow_movie.draw()
fixation.draw()
win.flip()
# Get Participants responses
keys = psychopy.event.getKeys(keyList=["space"],timeStamped=clock)
if (keys[0][0] == 'space') is True:
break
else:
continue
# Take only the timestamps from the variable key and store it in the variable RTs
RTs = [sublist[1:2] for sublist in keys] # This stores only the timestamps
RTs = [item for sublist in RTs for item in sublist] # This converts from list of lists to a flat list
Many thanks for your help !
It's not entirely clear what the structure of your trial is, but if you want to monitor responses during the animation, then the call to event.getKeys() needs to be embedded within the drawing loop (i.e. within for Nframes in range(200):). That way, you are checking for a keypress on every screen refresh, so the animation can be halted in realtime.
At the moment, the code shows the entire animation and only then checks the keyboard (but just once per trial). Regardless of what happens there, the next trial will begin, as that is the last code in the main trial loop (i.e. for i in range(nTrials):.
Lastly, the event module is deprecated for keyboard responses. You should really shift to the newer Keyboard class for much better performance:
https://www.psychopy.org/api/hardware/keyboard.html
https://discourse.psychopy.org/t/3-ways-to-get-keyboard-input-which-is-best/11184/3
I am creating an experiment using Psychopy builder.
The participant is presented with an image containing numbers, e.g. 10 and 20.
They enter what they think is the mean of the numbers, 15 in this case, and then press the spacebar to move on to the next image.
I am trying to have it so there is a display/box on screen that shows them their entry, as with larger numbers in the hundreds of thousands and millions I think they might lose track of what they have pressed.
The ability to change their entry would be excellent also, but really I am most interested in them being able to see it on screen.
In builder I can't find a way to do this, and the ratings scale is not appropriate for huge numbers.
I found these solutions in code to do something that sounds like it:
http://www.psychopy.org/wiki/home.php/Snippets/LiveUpdatingText
However when I try to add them using the code insert function , or just adding them to the compiled script the screen locks up when I try to run the experiment. I am a novice at python, and am not sure where to start fixing this. Is what I'm trying to do possible?
I'm happy to provide some example code from the compiled builder experiment.
Thanks in advance!
Those code snippets are designed for Coder, where you control everything that is happening and when. The same thing can be done in Builder, but you will have to amend the code to fit in with Builder's event loop cycle. i.e. Builder does certain things at the start of an experiment, on every trial, on every screen refresh and so on. So you can't just insert this sort of code without modification, because, for example, it attempts to wait indefinitely for a keypress. Builder meanwhile, is checking the keyboard every screen refresh (typically at 60 Hz), so if you try to wait indefinitely for a keypress in code, you'll be halting Builder from doing everything else it needs to do.
In essence, you just need to break up the code into snippets that go in the appropriate tab in a Builder Code Component (for code to be executed at experiment start, on each frame, and so on), and avoid indefinite functions like event.waitKeys() in favour of instantaneous checking via event.getKeys()
e.g. to adapt the second example from Jonas Lindeløv, in the "Begin Routine" tab, put:
chars = list('0123456789.') # the valid characters
meanText = '' # start with an empty answer on each trial
In the "Each Frame" tab, put something like:
response = event.getKeys() # get a list of keys pressed at this instant
if len(response) > 0: # if there was one,
key = response[0] # just convenient shorthand
if key in chars:
meanText = meanText + response[0]
elif key == 'space':
meanText = meanText + ' '
elif key == 'backspace' and len(meanText) > 0:
meanText = meanText[:-1]
elif key == 'return':
thisExp.addData('Answer', meanText) # save the response
continueRoutine = False # finish this trial
# update the appropriate text stimulus with the current response value:
insertNameOfYourTextStimulusComponent.text = meanText
I wrote a modified program of the 'mines' game, and I hope it shows every step/click graphically. I use time.sleep(0.5) to make a pause. So, in general the main program is like:
check_block():
if mine == 0:
buttons[current].config(image = tile_clicked)
elif mine == 1:
buttons[current].config(image = tile[1])
...
while(1):
time.sleep(0.5)
check_block()
get_next()
if check_fail():
break
However, the buttons don't update every 0.5 second: they are all updated together when the game(loop) finishes.
I guess it's just like 'cout' in C++: if you don't flush they will get stacked. So, is there a method to get them updated step by step, or say, instantly?
Thanks!
In all GUI systems you have to allow the message loop to run so that Windowing events occur promptly. So do not use a while loop like this. Instead, create a method that calls check_block() and get_next() and use after to call that function after a delay. At the end of that function, you use after again to call the same function again so that this function is called every 0.5 second forever. The after function queues a timer event and then lets the message queue be processed. Once your timer event fires, the callback function is run which allows you to do things and keep the UI responsive.
You should never call sleep in a GUI program. This is because the GUI must be "awake" at all times so that it can service events (including internal events that cause the screen to update). Instead, leverage the already-running eventloop by using the after method to put events on the queue at regular intervals.
In your case, you would replace the while loop with something like:
def do_check():
check_block()
if not check_fail():
root.after(500, do_check)
# in your initialization code, start the loop by calling it directly:
do_check()
I don't know what your get_next function does, so I don't know if you need to call it periodically too. Probably not. I'm guessing it waits for the next button press, which you don't need to do with tkinter or most other GUI toolkits. Instead, you configure the button to call a function when clicked.
Regardless, the way to do the type of looping you want is to place events on the event queue at a regular interval.
I need to write a code that runs similar to normal calculators in such a way that it displays the first number I type in, when i press the operand, the entry widget still displays the first number, but when i press the numbers for my second number, the first one gets replaced. I'm not to the point in writing the whole code yet, but I'm stuck at the point where when I press the 2nd number(s), the first set gets replaced. I was thinking about if key == one of the operands, than I set the num on the entry as variable first, then I do ent.delete(0,end) to clear the screen and ent.insert(0,first) to display the first num in the entry widget. Now I don't know what to do to clear the entry widget when the 2nd number(s) is pressed.
What you need here is a concept of state. Each time a key is pressed, you check the state and determine what action to take.
In the initial state, you take input of numbers.
When an operand button is pressed, you store the operand, and change the state.
When another number is pressed, you store the number, clear the numeric input, and start the number input again.
Then when the equals button is pressed, you perform the operation, using your stored number and operand with the current number in the numeric input.
Note that with a dynamic language like Python, instead of using a variable and if statements to check the state, you can just change the function that handles key/button pressed depending on what the state is.