event.waitPressed

81 views
Skip to first unread message

Jonas Lindeløv

unread,
Jan 24, 2016, 7:33:46 AM1/24/16
to psychopy-dev
Hi all,

The event.waitKeys() method is really useful. I just wrote the analogue for event.mouse. First of all, is it worth adding to the event module or are we shifting to iohub entirely? If it's good, then I've got a few API decisions that I'd like your thoughts on. First the method in functional programming:

from psychopy import visual, event
win = visual.Window()
mouse = event.Mouse()

def waitPressed(maxWait=float('inf'), keyList=(0,1,2), timeStamped=False):
    """
    Equivalent to event.waitKeys but for the mouse's left-scroll-right buttons.

    Returns the button_index if timeStamped=False (e.g. 2 for left key)
    Returns (button_index, duration) if timeStamped=True (e.g. (0, 4.343 for slow right key).
    Returns None if maxWait is exceeded. maxWait is infinite by default.
    """
    mouse.clickReset()
    while True:
        if not mouse.mouseClock.getTime() >= maxWait:  # if we're still within the maxWait duration
            buttons, times = mouse.getPressed(getTime=True)  # get mouse presses
            for key in keyList:  # match presses with keyList
                if buttons[key]:
                    # Return in the desired format
                    if timeStamped:
                        return buttons.index(1), times[buttons.index(1)]
                    else:
                        return buttons.index(1)
        # Return None when maxWait is exceeded
        else:
            return None

# Try it out
print waitPressed(3, keyList=[1], timeStamped=True)


  • I've deviated from event.waitKeys() which returns a always-one-element list of integers (for timeStamped=False) or tuples (for timeStampled=True). I think it's more intuitive to just return that one element.
  • I've renamed the mouse's "getTime" to "timeStamped" which is the familiar keyword for event.waitKeys users.
  • It returns the button index (e.g. "2") instead of a logical tuple (0,0,1) since always exactly one button will be pressed unless the subject is pressing down more buttons within a few nanoseconds :-)
Do you have any thoughts on the API here? 

Best,
Jonas

Jeremy Gray

unread,
Jan 24, 2016, 11:14:01 AM1/24/16
to psycho...@googlegroups.com
Hi Jonas,

I do not have a comment on the API at this point. But lets coordinate a bit. I plan to refactor event.py starting in just under 2 weeks to adjust whitespace and line-lengths, and it would be nice to avoid merge conflicts.

If you are adding a new function and not touching existing lines, I think any merge conflicts will be quite minimal (if any). But if you are also changing existing lines inside event.py (or anywhere, actually), it could become quite annoying for someone to resolve all of those.

Ways to avoid worst-case conflicts: 1) your new code is merged into upstream master before I start messing with things, 2) you wait for my dust to settle, then add your new stuff on top of it. Should be very easy for just adding a new function into event.py. 

Make sense? 

--Jeremy

--
You received this message because you are subscribed to the Google Groups "psychopy-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to psychopy-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jonas Lindeløv

unread,
Jan 24, 2016, 3:15:52 PM1/24/16
to psychopy-dev
It's a new function that doesn't touch existing lines so no problem. But I'll avoid adding it during the refactor.

Jonas

Michael MacAskill

unread,
Jan 24, 2016, 3:18:31 PM1/24/16
to <psychopy-dev@googlegroups.com>
Hi Jonas,

Great to have you contributing again.

On reading this code (and the very closely-related event.waitKeys), it strikes me that both suffer from not having any allowance for sleep periods to avoid hogging the CPU. e.g. compare to core.wait():

> psychopy.core.wait(secs, hogCPUperiod=0.2)
> Wait for a given time period.
>
> If secs=10 and hogCPU=0.2 then for 9.8s python’s time.sleep function will be used, which is not especially precise, but allows the cpu to perform housekeeping. In the final hogCPUperiod the more precise method of constantly polling the clock is used for greater precision.

Equivalently, for both waitKeys() and waitPressed(), it would probably be a good idea to insert a call to time.sleep(0.001) on every iteration. Without such brief sleep periods, there could be quite a stutter in timing and responsiveness if the OS takes over control of the CPU to do some urgent housekeeping if it has been starved of access for quite some time.

Lastly, on a more trivial naming point of view, to be consistent with waitKeys(), rather than using a past tense, should this new function actually be called "waitPress()" or "waitPresses()" (although that is a bit ugly)? Or to be even more explicit, "waitMousePress()"?

Best wishes,

Michael

Jon Peirce

unread,
Jan 25, 2016, 12:47:07 PM1/25/16
to psycho...@googlegroups.com
Mike's point is a good one, that I hadn't been aware of until recently. I agree this would probably be good elsewhere too, although we might need to check how quickly the OS gives resources back after a sleep (i.e. whether we get control back in 1ms after requesting a 1ms sleep). This is probably OS dependent.

Jonas you idea is good. One thing to note (maybe you have done) is that Mouse. getPressed() is not the same as event.getKeys() in that it returns the current state of the buttons, whereas getKeys() returns the buttons pressed and/or released since last.

In a way it would be useful to have the equiv of getKeys() as well. This can crop up as a problem in code like this:

    for thisTrial in trials:
        stim.draw()
        win.flip()
        while
        buttons = myMouse.getPressed()
        if button[0]:
            break #end trial

The surprising thing about the code above is that it skips across trials. Assuming the mouse stays down for more than 1 video frame you'll get it being accepted as a response for multiple occurrences, because the code is going around the loop faster than the finger is releasing the button. That isn't true for getKeys() which requires a release and a new press to consider it a new response.

So, possibly we' like
    Mouse.getPressed() #buttons currently pressed
    Mouse.getPresses() #report the distinct press/releases
    Mouse.waitPresses() #wait for a press to occur

OK, now the problem with identifying/reporting distinct press/release events is that you need to poll frequently enough to know that the button couldn't have been released and pressed again between polling points.

iohub might have (probably has?) this already solved.

Apologies if any of this was obvious, but it's a common error to think getPressed is more similar to getKeys than it really is.

best wishes
Jon

Jeremy Gray

unread,
Jan 25, 2016, 1:05:08 PM1/25/16
to psycho...@googlegroups.com
Another thing we might do at the same time, either via documentation or the API, is clarify the difference between an actual mouse click (= was pressed + is now released) and getPressed() (= is the mouse it currently pressed down). So maybe we also want to at least tell people how to do the equivalent of:

  Mouse.waitClick()  # == waitPresses() and then wait for a release event on the button that was pressed


--Jeremy

Michael MacAskill

unread,
Jan 25, 2016, 3:47:10 PM1/25/16
to psycho...@googlegroups.com
Yes, it now strikes me that I've had to help a few people with this issue before: manually tracking when the mouse button has been pressed and then when it has subsequently not been pressed in order to indirectly infer a mouse release. As Jon notes, individual mouse presses span multiple screen refreshes and this has tripped people up a number of times.

ioHub on the other hand I think is better at directly providing mouseDown and mouseUp events. I think these are also time stamped, rather than relying on whatever time we happen to be polling the sate of the mouse. Given the goal to further integrate ioHub into PsychoPy, it might be timely to discuss going that route under-the-hood to provide the basis for these new functions?

Cheers,

Mike

Jeremy Gray

unread,
Jan 25, 2016, 4:02:50 PM1/25/16
to psycho...@googlegroups.com
Definitely a timely time for discussion of a full integration of iohub. 

--Jeremy

Richard Höchenberger

unread,
Jan 25, 2016, 5:27:10 PM1/25/16
to psychopy-dev
On Mon, Jan 25, 2016 at 10:02 PM, Jeremy Gray <jrg...@gmail.com> wrote:
> Definitely a timely time for discussion of a full integration of iohub.

I would like to second that. :)

Jonas Lindeløv

unread,
Jan 26, 2016, 6:00:09 PM1/26/16
to psychopy-dev
Wow, this was more complicated than I anticipated ;-) But rightly so. 

I'm asking exactly because of the iohub plans. If the iohub-as-primary move is 5 years into the future, I think something like what I proposed (and Jon refined) would be helpful for users. But if it's just 1 year from now, we shouldn't give users "psychopy.event-habits" which my function would result in.

There's currently no documentation of the iohub mouse device/events. The keyboard device has the very handy waitForKeys, waitForPress, and waitForRelease (the latter two are special cases of the first which checks it with a default interval of 2 ms: https://github.com/psychopy/versions/blob/master/psychopy/iohub/client/keyboard.py) but the mouse doesn't seem to have these methods. The same behavior could be mimicked.

Jonas

Jonathan Peirce

unread,
Jan 27, 2016, 6:00:25 AM1/27/16
to psycho...@googlegroups.com
Greater integration of iohub (particularly the documentation), working towards a deprecation of the current event.py has been high on the list of my aims for some time (recall that's normally what I refer to when complaining about code refactoring! ;-) ).

Although it's high on my list, something always comes along and pips it in my own time. Currently my time is spent writing a sync tool for Open Science Framework with a view to being able to run experiments online (yes, for real!!). They gave us some money and set a date for completion, so that has become the #1 priority. Hopefully next month I'll do the iohub keyboard/mouse Builder integration as previously planned... if no new emergencies pop up. :-)

best wishes
Jon
--
You received this message because you are subscribed to the Google Groups "psychopy-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to psychopy-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

-- 
Jonathan Peirce
University of Nottingham

http://www.peirce.org.uk


This message and any attachment are intended solely for the addressee
and may contain confidential information. If you have received this
message in error, please send it back to me, and immediately delete it. 

Please do not use, copy or disclose the information contained in this
message or in any attachment.  Any views or opinions expressed by the
author of this email do not necessarily reflect the views of the
University of Nottingham.

This message has been checked for viruses but the contents of an
attachment may still contain software viruses which could damage your
computer system, you are advised to perform your own checks. Email
communications with the University of Nottingham may be monitored as
permitted by UK legislation.

Sol Simpson

unread,
Mar 30, 2016, 6:02:05 PM3/30/16
to psychopy-dev
Adding a psychopy process side wrapper of the iohub mouse device makes sense, similar to what what was already done for the keyboard device.

I've been working on cleaning up the iohub code in general. See the isolver/psychopy lazyimport branch (https://github.com/isolver/psychopy/tree/lazyimport)  if interested in what has been going so far. This is definitely a work in progress. The to do list for April includes a) adding more iohub test coverage, b) making the iohub source python 3.4+ compatible, c) improving the psychopy api docs for iohub, and more. Not sure how far into this list I will get in 3 - 4 weeks, we'll see.

Starting beginning of May I will have a few months where I can ( and need to ) spend some time working on psychopy - iohub integration. This mainly comes down to integrating the most used iohub devices into the Builder app (keyboard, mouse, eye tracker, ...), but better api level integration should come out of this as well I would think.  I've briefly talked with Jon and Jeremy about this already and would be happy and thankful for input from anyone else who has the interest and time.

So I think over the next 3 - 4 months, with a little help from some friends ;), iohub will become more of a first class citizen in the psychopy universe. Time will tell....

Take care.

Jonathan Peirce

unread,
Apr 12, 2016, 12:41:07 PM4/12/16
to psycho...@googlegroups.com
I can help get the first bits going with you. I'd suggest that
    - Experiment Settings in builder experiemnts have a panel for iohub (where we set config files and output filenames for hdf5 files)
    - then some components for
        - iohub keyboard
        - iohub mouse
        - eyetracker
This way, the components can use whatever iohub config variables were set in the Experiment Settings.

cheers
Jon
--
You received this message because you are subscribed to the Google Groups "psychopy-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to psychopy-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

-- 
Jonathan Peirce
Nottingham Visual Neuroscience

http://www.peirce.org.uk
Reply all
Reply to author
Forward
0 new messages