{ "metadata": { "name": "", "signature": "sha256:40fadd107c66685cda9c1684327a5839cb381f4620e8454c7ca829caf652bd1b" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Hands-on: PsychoPy -- Response/events collection" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Largely based on materials from [PsychoPy Course](http://github.com/lindeloev/psychopy-course) by\n", " Jonas Lindel\u00f8v, licensed under [GPL v.2](https://github.com/lindeloev/psychopy-course/blob/master/LICENSE)" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "2 ways to collect keyboard/etc events" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- `psychopy.event` module is the original PsychoPy way. **Cons**: runs in the same process, so might be blocking execution etc\n", "- `psychopy.iohub` runs in a separate process\n", "\n", "http://www.psychopy.org/api/iohub/device/keyboard.html" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# PsychoPy event way\n", "\n", "from psychopy import visual\n", "from psychopy import event\n", "\n", "win = visual.Window()\n", "stim = visual.TextStim(win, text=\"Press a bloody button\")\n", "\n", "# Keep main process busy\n", "flip_times = []\n", "event.clearEvents()\n", "for frame in range(60):\n", " stim.draw()\n", " flip_times += [win.flip()]\n", "\n", "# Get responses and their timing\n", "responses = event.getKeys(timeStamped=True)\n", "if responses:\n", " print responses[0][1] - flip_times[0] # RT\n", "else:\n", " print \"no responses\"\n", "win.close()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Excercise**\n", "\n", "- Familiarize yourself with the content of `response`\n", "- Plot histograms of \n", " - durations between flips (flip_times)\n", " - \"offset\" from target time when any given frame should have been presented (count from flip_times[0])" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# PsychoPy iohub way\n", "# Unfortunately there is an \"issue\" with using iohub under IPython notebooks:\n", "# https://github.com/psychopy/psychopy/issues/907\n", "# which overrides sys.stdout so lets workaround\n", "import sys\n", "stdout_orig = sys.stdout\n", "\n", "from psychopy import iohub\n", "sys.stdout = stdout_orig # workaround \"patch\"\n", "io = iohub.launchHubServer() # Start the server listening to events\n", "keyboard = io.devices.keyboard\n", "\n", "win = visual.Window()\n", "stim = visual.TextStim(win, text=\"Press a bloody button\")\n", "\n", "# Keep main process busy\n", "flip_times = []\n", "io.clearEvents('all')\n", "for frame in range(60):\n", " stim.draw()\n", " flip_times += [win.flip()]\n", "\n", "# Get response and RT\n", "responses = keyboard.getPresses()\n", "\n", "# Or use one of these:\n", "releases = keyboard.getReleases()\n", "keys = keyboard.getKeys()\n", "\n", "io.quit() # or .shutdown() - Stops and disconnect from iohub server\n", "win.close()\n", "\n", "if responses:\n", " print responses[0].time - flip_times[0]\n", "else:\n", " print \"no responses\"" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Excercise**\n", "\n", "- Inspect content of `responses`, `releases`, `keys`\n", "- Why `keys` is ... Make it non-...\n", "- Plot a histogram of key-press durations" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "More of iohub events" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once again -- iohub runs in the background and collects events until it is shutdown" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from psychopy.iohub import launchHubServer\n", "\n", "# Start the ioHub process. The return variable is what is used\n", "# during the experiment to control the iohub process itself,\n", "# as well as any running iohub devices.\n", "io = launchHubServer()\n", "\n", "# By default, ioHub will create Keyboard and Mouse devices and\n", "# start monitoring for any events from these devices only.\n", "keyboard = io.devices.keyboard\n", "mouse = io.devices.mouse\n", "\n", "# As a simple example, use the keyboard to have the experiment\n", "# wait until a key is pressed.\n", "print \"Press any Key to Exit Example.... .\"\n", "\n", "keys = keyboard.waitForKeys()\n", "\n", "print \"Key press detected, exiting experiment.\"" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# See what keys were pressed\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# uncomment, gather and retrospect all the events which iohub collected by NOW\n", "# all_events = io.getEvents()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Shutdown the iohub\n", "io.quit()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Questions***\n", "- What have you observed while looking at all_events? What button presses did you see?" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Compare event to iohub" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from psychopy import iohub, visual, event\n", "win = visual.Window()\n", "textStim = visual.TextStim(win, text='press now', height=0.1, wrapWidth=100)\n", "io = iohub.launchHubServer()\n", "keyboard = io.devices.keyboard\n", "\n", "while True:\n", " # Animate and show results\n", " textStim.ori += 0.1\n", " textStim.draw()\n", " win.flip()\n", " \n", " # Get responses\n", " event_getkeys = event.getKeys(timeStamped=True)\n", " io_getpresses = keyboard.getPresses()\n", " \n", " # Update text if a matching response was found\n", " if len(event_getkeys) and len(io_getpresses):\n", " textStim.text = \"\"\"\n", " event.getKeys time: %.3f s\n", " iohub.getPresses time: %.3f s\n", " io time is %i ms before event\n", " \"\"\" %(event_getkeys[0][1], io_getpresses[0].time,\n", " 1000*(event_getkeys[0][1] - io_getpresses[0].time))\n", " \n", " if io_getpresses[0].char == 'q': \n", " break\n", "\n", "win.close()\n", "io.quit()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Excercise**\n", "\n", "- Enhance example to report also release timings" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Waiting for events" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from psychopy import iohub\n", "sys.stdout = stdout_orig # workaround \"patch\"\n", "\n", "io = iohub.launchHubServer()\n", "keyboard = io.devices.keyboard\n", "\n", "io.wait(1)\n", "io.clearEvents('all')\n", "print 'listening now!'\n", "sys.stdout.flush()\n", "\n", "# iohub wait* methods above are identical to doing this, although they do \n", "# it in a different process than the main python session:\n", "while True:\n", " events = keyboard.getEvents(iohub.EventConstants.KEYBOARD_RELEASE) # listening for releases\n", " if events: # if non-empty\n", " break\n", "\n", "print 'finish'\n", "io.quit()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Excercise**\n", "- just inspect iohub.EventConstants for other interesting constants" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Waiting for specific key presses/releses\n", "Let's now wait for specific keypresses (could well be the trigger from Lumina emulated as a keyboard)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Initiate keyboard\n", "from psychopy import iohub\n", "sys.stdout = stdout_orig # workaround \"patch\"\n", "\n", "io = iohub.launchHubServer()\n", "keyboard = io.devices.keyboard\n", "\n", "# WAITING FOR KEYBOARD\n", "print 'Listening now...'\n", "keyboard.waitForKeys(chars=['@', '!'])\n", "print 'key event!'\n", "print keyboard.state # print keyboard state\n", "sys.stdout.flush() # so we could see right away\n", "\n", "keyboard.waitForReleases(keys=['f'], mods=['lalt', 'ralt'])\n", "print 'key release!'\n", "print keyboard.state\n", "sys.stdout.flush() # so we could see right away\n", "\n", "keyboard.waitForPresses(chars=['F', '@', '!'])\n", "print 'key press!'\n", "print keyboard.state\n", "sys.stdout.flush() # so we could see right away\n", "\n", "io.quit()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- - -\n", "**Excercise**\n", "\n", "* Write code that waits until the subject has written 'Dawg!' \n", "\n", " *Hint*: (D, a, w, g, !).\n", " \n", "* Start a psychopy Window with a TextStim that updates to show\n", " this in text, as if one was typing in a text field.\n", " (*Extra:* Maybe the text should turn green upon success?)\n", " \n", "* *Extra*: use keyboard.state to determine if the subject is currently\n", " holding down a, s, d, f, and g and exit the loop if then\n", " \n", " *Hint*: dict.keys() and set(lista) == set(listb)\n" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }