I am trying to use the DirectIN Rotary Controller on Mac OS X (yosemite) with PsychoPy2 (v. 1.82.01). I would like to conduct a two-alternative forced choice experiment and use the buttons on the button box in order to respond; however, I cannot get psychopy to recognize the device.
Somebody with a similar problem was able to make the button box work in coder (see here), and there is a similar question using a different game controller here. So far I have gathered the following information:
Psychopy will recognize the button box as a joystick.
I need to use a code component in the trial routine.
The post from the emprisoft forum provides the following code:
import pyglet
joysticks = pyglet.input.get_joysticks()
for joy in joysticks:
if joy.device.name == 'Analog Scale Device':
joy.open()
break
def present_pair_joystick(trial,isi,curdata): #trial is a sound object, isis is the time to wait after response/end of sound, and curdata is a dictionary used to store response data
event.clearEvents()
while joy.buttons[0] or joy.buttons[1]:
continue
curdata['trial_start']=time.time()
trial.play()
dur = trial.getDuration()
while True:
if not (joy.buttons[0] and joy.buttons[1]):
if joy.buttons[0]:
curdata['rt'] = time.time() - curdata['trial_start']
curdata['resp'] = 'Word'
break
elif joy.buttons[1]:
curdata['rt'] = time.time() - curdata['trial_start']
curdata['resp'] = 'Nonword'
break
if 'escape' in event.getKeys():
core.quit()
if time.time() - curdata['trial_start'] > dur:
core.wait(isi)
else:
core.wait((dur - (time.time() - curdata['trial_start'])) + isi)
curdata['dur'] = dur
return
So I believe I can get the button box to work if I incorporate this code into a code component in builder, but I have not had any success with this (I am able to run an experiment without error, but the key responses are not recorded). Any help would be appreciated.
You say that "I cannot get PsychoPy to recognize the device" but don't actually say what you did, so it is hard to diagnose the problem.
Simply pasting in the code above wouldn't do anything visible for you, as it creates a function (present_pair_joystick()) which you aren't otherwise explicitly calling.
The first bit of code accesses the joystick but doesn't give you any visible feedback as to whether it succeeded, so let's address that. I'm not familiar with joysticks, so will just modify the code you found above and assume that it is appropriate. e.g. Put this in the "Begin Experiment" tab of a code component:
import pyglet
joysticks = pyglet.input.get_joysticks()
joystickFound = False
for joy in joysticks:
if joy.device.name == 'Analog Scale Device':
joy.open()
joystickFound = True
break
if joystickFound:
print('Joystick found!')
else:
print('Joystick NOT found.')
core.quit() # no point continuing
Assuming the connection is successful, then we can start dealing with the responses, at the beginning of each trial and on every screen refresh.
It seems that joystick button presses can keep registering even when they haven't been released, and hence could carry over from one trial to the next. hence, we need to ensure that at least for a moment, the button is NOT pressed at the beginning of a trial before we test for the next actual button press.
So put something like this in the "Begin Routine" tab of a code component:
buttonReleased = False
And something like this in the "Every frame" tab:
# check that the buttons go through a period of NOT being pressed
# before we check if they HAVE been pressed:
if not (joy.buttons[0] or joy.buttons[1]):
buttonReleased = True # the previous trial's press has been released
if buttonReleased:
response = ''
if joy.buttons[0]:
response = '0'
elif joy.buttons[1]:
response = response + '1' # allows you to capture if both buttons pressed simultaneously
if response != '':
thisExp.addData('buttonResponse', response) # save the response in the data
thisExp.addData('buttonRT', t) # store current time since trial start
continueRoutine = False # end trial once response received
event.clearEvents(eventType='joystick')
Caveat: I'm not entirely sure about the placement of the clearEvents call, and have no joystick to test this button press/release handling.
Related
I was hoping someone could help me with this issue. I'm hoping it's fairly simple to fix, but I have been trying for a while to figure this out. I have trimmed my larger code to this, as I believe the issue here is the crux of the problem.
I have a raspberry pi and an external button. This is on python3 on Linux. I am using GPIOZero for the button. The code below I believe is simple to understand, but basically, I want a function to loop at all times. When I press a button I want another function to run, but only if a variable is a certain number. I describe what I ultimately want to happen in a comment below, but my code is unfinished and simplified just for this problem.
I only want button.when_pressed to work when timer = 0. The problem is, once the code naturally gets into the button.when_pressed function, it never "lets go" of the function again. When I successfully redefine the variable to timer = 1, it still prints button is pressed when I press the button. I don't know why. To me, it seems like it should only work once timer = 0.
Any suggestions? I think I have a misunderstanding of global variables I will plan on researching. I'm not sure if that's the issue. I have also tried using break and continue to try to get it "back on its loop" but that hasn't worked either. Also I want to use button.when_pressed instead of btn.is_pressed, because I want the program to only do something when I'm holding the button down once, and not loop when I'm holding it down. In this case, I want button is pressed to print a single time. If I did btn.is_pressed it would print button is pressed every two seconds, which I dont want.
Thanks for any help. I'm happy to learn.
#!/usr/bin/python3
from gpiozero import Button
from time import sleep
import time
button = Button(4)
timer = 0
def press():
print("Button is pressed")
global timer
timer = 1
def loop():
global timer
timer = 1
while True:
if timer == 0:
button.when_pressed = press
else:
loop()
sleep(2)
If you want to disable the callback you had set to button.when_pressed, you need to do another assignment with button.when_pressed = None. This is listed in the documentation:
when_released:
[...] Set this property to None (the default) to disable the event.
It's not exactly clear what behavior you want from your current code. If you want the button to be active for 2 seconds, then be deactivated indefinitely, you can use:
button.when_pressed = press
sleep(2)
button.when_pressed = None
There's no need for a loop, since you don't want to repeat anything.
If you only want the button to be active for a single button press, that needs to happen within 2 seconds, you could instead call button.wait_for_press(2). I hesitate to write a full block of code for that though, as the docs don't specify how a timeout is signaled (it might be by return value, or via an exception). I don't have a Raspberry Pi so I can't test myself, but you could try it out and see what happens.
Treat your whole piece of code as one "black box", ask yourself, what is the input/output? button press or timer mode? (because I don't quite understand what does timer variable mean in your code)
Your code implies timer mode is the top level input to control the flow,
while True:
if timer == 0:
button.when_pressed = press
else:
loop()
sleep(2)
Is it expected?
If you allow user to press the button at any time, suggest you make button press to be your top level input, change the logic to keep when_pressed callback always on, set flag once triggered, and then check if the button has been pressed and still is_pressed in your while loop.
pressed = False
def play_video_1():
pass
def play_video_2():
pass
def press():
print("Button is pressed")
global pressed
pressed = True
button.when_pressed = press
while True:
if pressed and not_playing_video2:
if is_pressed:
play_video_1()
else:
pressed = False
play_video_2()
else:
play_video_2()
I was hoping someone might have some insight on how to stop a script from continuing to repeat if a button is held (or in my case pressed longer than a second)?
Basically i've a button setup on the breadboard, and I have it coded to play an audio file when the button is pressed. This works, however if the button isn't very quickly tapped, then the audio will repeat itself until button is fully released. Also if the button is pressed and held, the audio file will just repeat indefinitely.
I've recorded a quick recording to demonstrate the issue if its helpful, here: https://streamable.com/esvoy6
I should also note that I am very new to python (coding in general actually), so its most likely something simple that I just haven't been able to find yet. I am using gpiozero for my library.
Any help or insight is greatly appreciated!
Here is what my code looks like right now:
from gpiozero import LED, Button
import vlc
import time
import sys
def sleep_minute(minutes):
sleep(minutes * 60)
# GPIO Pins of Green LED
greenLight = LED(17)
greenButton = Button(27)
# Green Button Pressed Definition
def green_btn_pressed():
print("Green Button Pressed")
greenButton.when_pressed = greenLight.on
greenButton.when_released = greenLight.on
# Executed Script
while True:
if greenButton.is_pressed:
green_btn_pressed()
time.sleep(.1)
print("Game Audio Start")
p = vlc.MediaPlayer("/home/pi/Desktop/10 Second Countdown.mp3")
p.play()
So from a brief look at it, it seems that 'time.sleep(.1)' is not doing what you are expecting. Ie. it is obviously interrupted by button presses. This is not abnormal behaviour as button presses on Ardiuno and raspPi (guessing here) would be processed as interrupts.
The script itself does not contain any prevention from double pressing or press and hold etc.
Have you put in any debug lines to see what is executing when you press the button?
I would start there and make adjustments based on what you are seeing.
I am not familiar with this gpiozero, so I can't give any insight about what it may be doing, but looking at the code and given the issue you are having, I would start with some debug lines in both functions to confirm what is happening.
Thinking about it for a minute though, could you not just change the check to 'if greenButton.is_released:'? As then you know the button has already been pressed, and the amount of time it is held in for becomes irrelevant. May also want to put in a check for if the file is already playing to stop it and start it again, or ignore and continue playing (if that is the desired behaviour).
Further suggestions:
For this section of code:
# Executed Script
while True:
if greenButton.is_pressed:
green_btn_pressed()
time.sleep(.1)
print("Game Audio Start")
p = vlc.MediaPlayer("/home/pi/Desktop/10 Second Countdown.mp3")
p.play()
You want to change this to something along these lines:
alreadyPlaying = 0
# Executed Script
while True:
if greenButton.is_pressed:
green_btn_pressed()
#Check if already playing file.
if alreadyPlaying == 1:
# Do check to see if file is still playing (google this, not sure off the top of head how to do this easiest).
# If file still playing do nothing,
#else set 'alreadyPlaying' back to '0'
break
#Check if already playing file.
if alreadyPlaying == 0:
time.sleep(.1)
print("Game Audio Start")
p = vlc.MediaPlayer("/home/pi/Desktop/10 Second Countdown.mp3")
p.play()
alreadyPlaying = 1
Hopefully you get the idea of what I am saying. Best of luck!
i think you have to write something like this in your loop:
import time, vlc
def Sound(sound):
vlc_instance = vlc.Instance()
player = vlc_instance.media_player_new()
media = vlc_instance.media_new(sound)
player.set_media(media)
player.play()
time.sleep(1.5)
duration = player.get_length() / 1000
time.sleep(duration)
I have been facing this problem for the last week,I thought it would be trivial but after trying many different approaches I don't know what else to try.
I have an application where I need to have key detection (to move a robot arm with the keyboard) but when I press enter I need to add some inputs, which should be as long as I want, just some normal input("insert here").
I know about the python libraries to get key detection, I got pynput to work successfully but it crashes my raspberry pi when I start and stop the threads a few times,I tried the Keyboard library but the whole root requirement is a let down, I also got curses to work and this seems to be solid and is (almost) not causing any issues, so detecting 1 key is not a problem.
I of course know how to name my files and get all the information that I need by doing input(), so if I had to use one of those options the job would be rather simple, the challenge comes when I try to apply both approaches together, basically detect the keys to do everything I need, and use python Input to get all the inputs from the user as soon as enter is pressed, all the libraries to detect key seems to take full control and they don't want to release it without a fight. They seem to expect the user to always require single key detection but in my case I would need to constantly turn it on and off, I couldn't figure out any efficient (or not) way to get it to work properly.
My question is:
What is the best approach to have key detection + full user input when needed with curses (or any alternative) in a non blocky way (as my code need to do some other things while listening for keys), is creating and destroying the whole thing the only alternative?
This is my current test code that I created for simplicity (which works but blocks everything while listening for keys):
import curses
import time
import os
stdscr = None
addInput = False
def SetupCurses():
global stdscr
stdscr = curses.initscr()
curses.cbreak()
stdscr.keypad(1)
def StartCurse():
global addInput
key = ''
while key != ord('q'):
key = stdscr.getch()
stdscr.addstr(str(key))
if key == ord('a'):
print("\nyou pressed a\n")
if key == 10:
print("\nyou pressed enter!\n")
addInput = True
break
def EndCurse():
curses.endwin()
while(True):
SetupCurses()
StartCurse()
EndCurse()
if addInput:
theinput = input("add your input\n")
print(theinput)
time.sleep(4)
addInput = False
#if there isn't any input to add I want the code to continue because there is non-related keys stuff to do, but of course it stopped at "StartCurse"
#if there is something to add the code can stop at addInput
The reason for the loop is because the user can save as many positions as he want, so after adding some inputs the possibility of adding more is there.
I saw people making this non-blocking by closing the curses loop after a few seconds (which stops everything anyway...) kind of getting the input by luck...something like:
def ExecuteCurses():
global AddInput
#open it and close it very quickly to grab a key if it is pressed
c = stdscr.getch()
if c == ord('a'):
print("you pressed a")
AddInput = True
time.sleep(1)
curses.endwin()
If you want a full and long user input you will need to use the curses.echo() and then use the stdscr.getstr(). That will wait for the user to press enter().
And to not block the program while getting input you need threading which you will have to import at the top of your program
And for the threading here is a link so you can find out more about threading.
I hope it answers your question
I have a question about Pyautogui. I want to click on a button/link/image, but in my country the Internet speed is very slow. So I'm trying to delay the program. But sometimes it doesn't work. The program clicks on the link/image/button before it appears on the website. That's why I cannot continue or the whole program fails.
Is there any way the program can locate if the button/link/image is displaying or not? If it is not displaying, it should wait for 30 more seconds or 1 minute. That's how I want my program to work every time. I really want to solve this issue. I hope you will reply.
This may be tricky if you are looking at a bunch of different webpages all with different content. But if you know a specific image or link text that must be there, you should be okay.
max_wait below is so the program doesn't loop endlessly. Make it as big as you want.
For a specific image you know has to show up in the page, you can try
import time
import pyautogui
found_image = False
max_wait = 120
x = time.time()
while (found_image == False) and time.time() - x < max_wait:
if (pyautogui.locateOnScreen('my_png.png') is not None):
found_image = True
if found_image: do_main_test()
For text, you can use pyautogui to select all...
import time
import pyautogui
import pyperclip
x = time.time()
found_text = False
max_wait = 120
while (found_text == False) and time.time() - x < max_wait:
pyautogui.click(150, 150) # or wherever is in the window
pyautogui.hotkey('ctrl', 'a')
pyautogui.hotkey('ctrl', 'c')
pyautogui.click(x=150, y=150)
lines = pyperclip.paste()
found_text = needed_text in lines
if not found_text: sleep(5) # this is so pyautogui isn't continually highlighting etc.
if found_text: do_main_test()
I don't know about tracking buttons, but I hope the text and image examples are useful. And of course you can specify running do_main_test() only if found_text and found_image are true.
I am programming an experiment with Pygame 1.9.2 on Python 2.7. In the experiment, I display an image and ask the user to click either the left mouse button or right mouse button based on a feature of the image (I instruct them in advance when to click what). The image is displayed until the user clicks or if the time of image display exceeds a fixed duration.
Here's the code snippet.(Hope this bit is enough to understand what is being done.)
pygame.display.update()
resp = None
while 1:
dispEnd = time.time()
pygame.mouse.set_visible(True)
pygame.event.get()
ms = pygame.mouse.get_pressed()
if ms[0] or ms[2]:
rt = dispEnd - dispSt
if ms[0]:
resp = 'Yes'
else:
resp = 'No'
break
if dispEnd - dispSt >= changeDuration:
break
This piece of code is part of a bigger loop where an image is selected and displayed, so this runs several times.
What happens at unpredictable times is that the program does not wait for user input. Right after displaying the image, it enters the while loop and proceeds as if the mouse were pressed.
It's a random error and happens anytime; sometimes right at the start of the program, from the very first run of the loop; so probably it is not because of the event queue possibly not being cleared (which it is while calling pygame.event.get()) and it also cannot be defaulting to the previous mouse click; sometimes it happens after a few iterations of the loop. Either way, it is disastrous for an experiment.
Try this out:
...
while 1:
dispEnd = time.time()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
#do something
...