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
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)
I need to modify a script in psychopy. The thing I need is the subject to press and hold a buttom in the keyboard, but I'd like to have two responses: one when he/her presses it (and the windows with text has to change), and the second when he/her releases it (and the windows with text has to change again).
I am using event.getKeys function of psychopy, and then I'm using keyboard library.
after inizializing the window screen and the text and importing psychopy and the functions I need and keyboard library as key.
intro.draw()
win.flip() # reset the clock to record a response time and clear events
event.clearEvents('all')
event.globalKeys.clear() #to avoid problems with the keys you pressed before
intro_loop = True
while intro_loop:
intro_keys = kb.getKeys(["space"], waitRelease=True)
if key.is_pressed('space'):
intro.text = 'Hi, please follow these instructions :) '
intro.draw()
win.flip() # reset the clock to record a response time and clear events
if len(intro_keys):
win.close()
intro_loop = False
I did the same in another script (just to try) but instead of having a window text screen I printed two different things, and it worked. in this script, instead, it completely ignores the function "key.is_pressed".
do you have any ideas?
I'm trying to build a countup clock with a raspberry pi. Now I'm thinking about, how I can show the counter (yeras:months:days) on a screen. So how can I generate a window, which shows the counter and updates itself (e. g. every hour). My idea is to use it as a decoration, so it shouln't be nessessary to interact with the shown.
That's my code so far:
import time
now = time.localtime()
hour=now.tm_hour
minute=now.tm_min
summer=now.tm_isdst
#print("Tag:", day)
#print("Monat:", month)
#print("Jahr:", year)
while True:
time.sleep(3600.0)
day=7
month=4
year=now.tm_year
if month<4:
j=year-2020
elif month==4:
if day<4:
j=year-2020
else:
j=year-2019
else:
j=year-2019
if month<4:
m=month+7
elif month==4:
if day<7:
m=month+7
elif day>=7:
m=0
else:
m=month-4
else:
m=month-4
if day<7:
t=day+24
elif day==7:
t=0
else:
t=day-7
print(j , ":" , m , ":" , t)
look into the python curses module or one of the wrappers around it (i.e. blessings) which is a little easier to use. This will allow you to create a screen in the terminal and refresh the screen every so often, this would be every second in your case. This would allow you to open a terminal in the pi run it and just leave it.
However a much easier solution would be to just keep calling time.ctime() and clearing the screen and refreshing it, but if you want to get some practice with curses so you can do stuff with it in the future go for it.
I'm playing around trying to emulate some Ctrl-P-like behaviour in Vim. What I have so far:
User calls my function.
A window is opened with a list of items in it.
A prompt is displayed under the list of items.
I want the user to be able to use the up and down arrows to select from the items but without leaving the prompt (in which they can also type).
I have got 1-3 mostly under control but am struggling with #4. I have got as far as this:
python << EOF
import vim
def MyWindow():
# Create a new window at the bottom
vim.command("below new")
vim.current.buffer.append("123")
vim.current.buffer.append("234")
vim.current.buffer.append("345")
vim.command("redraw")
vim.command("setl cursorline")
vim.command("echon '>> ' | echoh None")
while True:
vim.command("let x = getchar()")
character_code = vim.eval("x")
if character_code == "13":
# Exit by pressing Enter
break
elif character_code == "\x80kd": # Down
vim.command("keepj norm! j")
vim.command("redraw")
EOF
command! MyWindow python MyWindow()
Now the first down arrow keypress works perfectly, but subsequent down arrow kepresses don't, even though the code in the "if down key pressed" case is executed. Any ideas?
I just realised I can make it work by doing this:
elif character_code == "\x80kd": # Down
vim.command("keepj norm! j")
vim.command("setl cursorline") # <-- This fixed it
vim.command("redraw")
But I have no idea why or if this is a sensible thing to do.
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....