Single mouse click within While loop (PsychoPy) - python

I am using PsychoPy and I would like to print the position of my mouse when it has been clicked.
The actual printing of the position needs to be placed inside a while loop. Using the code below, when I click I get more than one output lines, which print the same positions. I would like to have only one output printing for each click.
This is the code I am using:
#!/usr/bin/env python2
from psychopy import visual, core, event
from pyglet.gl import *
width = 600
height = 600
myWin = visual.Window([width,height], color='white',units='pix',monitor='testMonitor')
#This will set the windows units (pixels) to GL units
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, 0, height, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_BLEND)
glBlendFunc(GL_ZERO, GL_SRC_COLOR)
myMouse = event.Mouse() # will use myWin by default
while True:
#Triangle left
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_TRIANGLES)
glVertex3f(150, 550, 1)
glVertex3f(50, 350, 1)
glVertex3f(250, 350, -1)
glEnd()
if myMouse.getPressed()[0]:
myMouse.clickReset()
print myMouse.getPos()
myWin.flip()
core.quit()
Is there something wrong I am doing? Should the 'frame rate' of the while loop be changed?

I've not used the module, but it seems like mouse events are thrown for mouse raises as well.
You'll need to
Store the mouse state for next time
Each iteration, test whether the mouse state for button 0 has gone up → down
The mouse state is returned by myMouse.getPressed.
So something like:
oldMouseIsDown = False
while True:
...
mouseIsDown = myMouse.getPressed()[0]
myMouse.clickReset()
if mouseIsDown and not oldMouseIsDown:
print myMouse.getPos()
oldMouseIsDown = mouseIsDown

Veedrac's answer is correct. Your code is using the typical PsychoPy pattern of checking for events once every time the window is redrawn. This will typically be happening at least at 60 Hz. So unless you manage to press the mouse button for less than 16.7 ms (or less for a faster screen), you will detect it multiple times as being pressed, as each time you check on successive window redraws, the mouse button remains down. Even though it was pushed only once, the duration of the push is not instantaneous.
As Veedrac suggests, you therefore need to maintain the previous state of the mouse button in a variable so that you can choose to only print the position once. Mouseup events are not relevant here: you are purely testing for whether the button is currently pressed.

I had a very similar problem and fixed it in a slightly different way from the accepted answer. The advantage of this one is that you can set how long you want to 'desensitize' the mouse after a click (i.e. to prevent longer clicks from triggering your if loop multiple times). Depending on your mouse or the user's click release speed you can change minFramesAfterClick:
minFramesAfterClick = 10 # to prevent re-entering the if loop too early
myMouse.clickReset()
timeAfterClick = 0
while True:
timeAfterClick += 1
if myMouse.getPressed()[0] and timeAfterClick >= minFramesAfterClick:
print myMouse.getPos()
myMouse.clickReset()
timeAfterClick = 0
myWin.flip()
By the way, the reason why the OP couldn't get Veedrac's answer work is because line oldMouseIsDown = mouseIsDown should be placed it inside the if loop rather than after it (not enough reputation to comment there).

Related

Variable adds itself for unknown reason

(EDIT #1)
I am new to coding and tried to create an infinite grid and a way to drag/move the grid using the mouse when any mouse button is pressed.
What I am trying to do is take the coordinates of the mouse right before any mouse button is pressed.
Then calculate the difference in coordinates while mouse is being moved in real time from that starting position (without releasing the button).
Then I want to save that difference and use it to move the grid (after mouse button is released).
After that, when I release any mouse button and the grid was successfully moved, I want to then be able to drag it again without resetting the already existing coordinates of the grid.
The problem with all of this is: when any mouse button is held down and is not being released, if moved from its starting point and then stopped at another location than the starting, the variable "CPD" representing the difference between starting and current mouse position adds itself, yet I do not see why that is supposed to be happening. It is an "=" sign and not "+=" nor is itself specified when measuring the difference and the "if" is not triggered at all, only the "else" is!
SP - Starting position of the mouse (right before left mouse button is pressed). CP - Current position of the mouse. PPD - The previous difference in starting and current mouse position. CPD - Current difference in starting and current mouse position.
This is the continuation of the previous picture (couldn't fit everything in one)
import time
import mouse
CPD = [0, 0] # Current difference in the starting and curruent mouse position
PPD = [0, 0] # Previous difference in the starting and curruent mouse position
CP = [0, 0] # Current position of the mouse
SP = [0, 0] # Position of the mouse right before any mouse button is pressed
# the loop (obviously) so it can check again and again so that the difference would be seen in real time.
while True:
# simply a way to slow down the code to see the difference step by step
time.sleep(1)
# Saves the position of the mouse [x, y] and the previous difference in starting and current mouse position until any mouse button is pressed
if not mouse.is_pressed():
PPD = CPD
SP = list(mouse.get_position())
# Then, if any mouse button is pressed: it finds current mouse's position
# Then it calculates the difference in starting and current mouse positions for x first and then y after which it stores the result
else:
CP = list(mouse.get_position())
for position in range(2):
CPD[position] = PPD[position] + CP[position] - SP[position]
# all the variable results after math
print(f"Mouse is pressed: {mouse.is_pressed()}\nSP: {SP}\nCP: {CP}\nPPD: {PPD}\nCPD: {CPD}\n")
I have tried to figure it out for a very long time and neither could my friend who is decent at coding (I believe). Any ideas?
The confusion here for you is - I think - coming from the interpretation of mouse.is_pressed(). This function returns you the current state, not a change of state. So throughout the whole time you have the button pressed, it returns True. It is not that, when you first time access this method, it is somehow reset to False again. Only when you release the mouse button again.
That also explains, why it is being added again and again: You have a loop and check for it every 0.1 seconds.
is_pressed() gives True all time when you keep pressed button.
You would have to remeber previous value and execute code only when previous state is False and current state is True
Somethink like this
import time
import mouse
Current_Position_Difference = [0, 0]
Previous_Position_Difference = [0, 0]
Start_Position = [0, 0]
previous_state = False
while True:
time.sleep(0.1)
current_state = mouse.is_pressed()
if current_state is False:
Previous_Position_Difference = Current_Position_Difference
Start_Position = list(mouse.get_position())
else:
if previous_state is False:
Current_Position = list(mouse.get_position())
for position in range(2):
Current_Position_Difference[position] = Previous_Position_Difference[position] + Current_Position[position] - Start_Position[position]
previous_state = current_state
print(Current_Position_Difference)
But mouse has also mouse.on_click(callback) to run function callback() only once when you press button. And on_click doesn't need to run in while-loop.
I don't know what you try to do in your code so I don't know if this code works the same
import time
import mouse
# --- function ---
def my_function():
print('clicked', mouse.get_position())
Current_Position = list(mouse.get_position())
for position in range(2):
Current_Position_Difference[position] = Previous_Position_Difference[position] + Current_Position[position] - Start_Position[position]
# --- main ---
Current_Position_Difference = [0, 0]
Previous_Position_Difference = [0, 0]
Start_Position = [0, 0]
mouse.on_click(my_function)
while True:
time.sleep(0.01)
Previous_Position_Difference = Current_Position_Difference
Start_Position = mouse.get_position()
print(Current_Position_Difference)
Other modules may have also on_move(callback) to run function only when mouse change position.

pyautogui.moveTo() failing sometimes and I have no idea why

I included the whole loop, but the part I'm talking about is the third paragraph. I have this loop, where it clicks on something, and then it moves the mouse back to wherever it was, to not interrupt the user while this thing is running. However, the pyautogui.MoveTo() call seems to be getting skipped sometimes?
while unlim or intFTC > 0:
# switch to program, do thing, and switch back to current window
current_win = gw.getActiveWindow().title
win = gw.getWindowsWithTitle('World of Warcraft')[0]
win.activate()
pyautogui.press(input_key)
win = gw.getWindowsWithTitle(current_win)[0]
win.activate()
# find and click thing
bx, by = thing_finder(x, y, x1, x2, y1, y2)
time.sleep(random.uniform(0.2, 0.6))
current_x, current_y = pyautogui.position()
current_win = gw.getActiveWindow().title
pyautogui.moveTo(bx, by)
pyautogui.rightClick()
# send mouse back to original position
while pyautogui.position() != (current_x, current_y):
pyautogui.moveTo(current_x, current_y)
print('::', pyautogui.position())
# return to og window
win = gw.getWindowsWithTitle(current_win)[0]
win.activate()
window.refresh()
stop()
time.sleep(random.uniform(1.5, 2))
runs += 1
So in the third chunk commented "send mouse back to original position," it doesn't always work. The current_x, and current_y are always correct, but it won't move the mouse to those coordinates. I thought the while loop would fix it, but it still doesn't always work which boggles my mind, because how does it get out of the while loop without moving the mouse? Thank you for reading.
Is it simply a case of not enough pause time? I have the time between pyautogui calls set to 0.001, but I figured it would slow down if it had to, not skip commands.
The loop does not make sense, you should send the command pyautogui.moveTo(current_x, current_y) and it should work. That condition can lead to some errors.

PysimpleGUI - simple animation

Thank you for reading this.
I'm working on a simple animation that is based on one of two examples from the PysimpleGUI cookbook. The attached code, of course, it is not doing anything. I've looked through many examples trying to figure out how to update the canvas but without success.
My first attempt was based on the sine wave plot example. I have an endless while loop and a display function. The display on the graph area shows the first iteration through the loop but is never updated after that.
The display function contains:
graph.DrawCircle((i,j), 5, line_color='black', etc
A second related question, should I be using the canvas or the graph method (as in the sine wave plot example), or doesn't it matter?
I don't want to overwhelm the reader with too much code. If I can get the following to work then I may have a good chance with the real code.
import PySimpleGUI as sg
import time
layout = [
[sg.Canvas(size=(100, 100), background_color='red', key= 'canvas')],
[sg.T('Change circle color to:'), sg.Button('Red'), sg.Button('Blue')]
]
window = sg.Window('Canvas test')
window.Layout(layout)
window.Finalize()
canvas = window.FindElement('canvas')
cir = canvas.TKCanvas.create_oval(50, 50, 100, 100)
while True:
event, values = window.Read()
'''
if event is None:
break
if event == 'Blue':
canvas.TKCanvas.itemconfig(cir, fill="Blue")
elif event == 'Red':
canvas.TKCanvas.itemconfig(cir, fill="Red")
'''
# this is the part that I need to sort out
for i in range(10):
if i % 2 == 0:
canvas.TKCanvas.itemconfig(cir, fill="Blue")
else:
canvas.TKCanvas.itemconfig(cir, fill="Red")
time.sleep(1)
I discovered the answer and that is, window.Read(timeout = 0).
In order for changes to show up in a window after making changes, you much either call Read or Refresh. I think all you need to do is down in your bottom loop, add the line:
window.Refresh()
From the docs at http://www.PySimpleGUI.org:
Refresh()
Cause changes to the window to be displayed on the screen.
Normally not needed unless the changes are immediately required or if
it's going to be a while before another call to Read.

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

how to set the orientation of the stimulus for each trial in psychopy

I am fairly new to the python language and psychopy. I am practicing it by creating dummy experiments. Here, I am trying to create an experiment about bayesian brain. Non-vertical lines will be presented to the participant while no respond is expected from the participants, just exposure. Then for the last trial (it stays on the monitor for longer period of time to be responded), it is expected from the participant to judge whether the last line trial is vertical or not? (after exposing to non-vertical lines, I am expecting to see a change in perception of verticality).
However, there are so many things that I couldn't learn from the web. I am pretty sure you guys can help me easily.
My primary problem is; how to set up the orientation of the line? I found out the stim.ori but not sure how to use it on 'line' stimuli. Below I've attached the codes that I made so far. Also, I have added some extra questions with #.
I tried to be clear as much as I can. Sorry for my bad english.
Thank you!
from psychopy import visual, core, event #import some libraries from PsychoPy
import random
#create a window
mywin = visual.Window([800,600],monitor="testMonitor", units="deg")
#stimuli
lineo = visual.Line(mywin, start=(-5, -1), end=(-5, 1))
fixation = visual.GratingStim(mywin, size=0.2, pos=[0,0], color = 'black')
#draw the stimuli and update the window
n = 5 # trial number
i = 0
while i < n:
#fixation
fixation.draw()
mywin.flip()
presses = event.waitKeys(1)
# stimulus
orientationlist = [20,30,40,50,60] # I want to draw the orientation info from this list
x = random.choice(orientationlist)
lineo.ori((x)) #
lineo.draw()
mywin.flip()
presses= event.waitKeys(2)
i +=1
if i == 5: # how do I change the number into the length of the trial; len(int(n) didnt work.
lineo.draw()
mywin.flip()
presses = event.waitKeys(4)
#quiting
# I dont know how to command psychopy for quiting the
# experiment when 'escape' is pressed.
#cleanup
mywin.close()
core.quit()
There's a few things that you would want to do differently. I've updated your code and marked changes with the comment "CHANGE". Changing the orientation of a stimulus is pretty consistent in psychopy, so it's no different for Line than any other visual stimulus type.
from psychopy import visual, core, event #import some libraries from PsychoPy
import random
#create a window
mywin = visual.Window([800,600],monitor="testMonitor", units="deg")
#stimuli
lineo = visual.Line(mywin, start=(-5, -1), end=(-5, 1))
fixation = visual.GratingStim(mywin, size=0.2, pos=[0,0], color = 'black')
orientationlist = [20,30,40,50,60] # CHANGED. No need to redefine on every iteration of the while-loop.
#draw the stimuli and update the window
n = 5 # trial number
for i in range(n): # CHANGED. This is much neater in your case than a while loop. No need to "manually" increment i.
#fixation
fixation.draw()
mywin.flip()
event.waitKeys(1) # CHANGED. No need to assign output to anything if it isn't used.
# stimulus
lineo.ori = random.choice(orientationlist) # CHANGED. Alternative: lineo.setOri(random.choice(orientationlist)).
lineo.draw()
mywin.flip()
event.waitKeys(2)
# At this point, all the stimuli have been shown. So no need to do an if-statement within the loop. The following code will run at the appropriate time
lineo.draw()
mywin.flip()
event.waitKeys(keyList=['escape']) # CHANGED. Listen for escape, do not assign to variable
# CHANGED. No need to call core.quit() or myWin.close() here since python automatically cleans everything up on script end.

Categories

Resources