Skip to content

Commit

Permalink
Merge branch 'release' of https://github.com/psychopy/psychopy into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
peircej committed May 24, 2024
2 parents e187820 + 10d4dcf commit 5157d38
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 40 deletions.
1 change: 1 addition & 0 deletions docs/source/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
Coder - writing experiments with scripts <coder/index>
Online - running experiments on the web <online/index>
Hardware - interacting with external hardware <hardware/index>
`Plugins<https://plugins.psychopy.org/>`_
api/index

.. only:: html
Expand Down
23 changes: 20 additions & 3 deletions docs/source/hardware/eyeTracking.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,24 @@ Communicating with an Eyetracker

PsychoPy has components that allow you to connect and communicate with eyetrackers directly from Builder - without any code! These steps will guide you through how to set up, calibrate, and record from your eyetracker.

Step one: Know Your Eyetracker
Step one: Select your plugin
-------------------------------------------------------------
`If you are using a version of PsychoPy from 2022 or earlier, skip this step and go straight to step two`

To use your eye tracker, you will need to install a plugin. PsychoPy supports many of the commonly used eye trackers. To find out whether yours is supported, follow these steps:

* Under the `Tools` tab, select `Plugin/packages Manager`
* Find your eye tracker in the plugins list and select `install`

.. figure:: /images/PluginsAndPackagesManager.png

If you would like to install a plugin from a file (e.g. a .whl file):

* In the Plugin and Packages Manager, select the `Packages` tab
* At the bottom left of the window, select whether you would like to install from a file or via the PIP terminal
* Select the relevant file or run a PIP install

Step two: Know Your Eyetracker
-------------------------------------------------------------

PsychoPy supports many of the commonly used eyetrackers, you can find out if yours is supported by following these steps:
Expand All @@ -19,7 +36,7 @@ PsychoPy supports many of the commonly used eyetrackers, you can find out if you
* When you've found your eyetracker, just select it and click `OK`.
* If you want to test out your eyetracking experiment but don't have an eyetracker with you, you can select `MouseGaze`. This will allow your mouse cursor to act as a gaze point on your screen, and so allow you to simulate eye movements without using an eyetracker. Then, when you're ready to use your eyetracker, you can just select it from the Experiment Settings and run your experiment in the same way.

Step two: Set up your Eyetracker
Step three: Set up your Eyetracker
-------------------------------------------------------------
When you've selected your eyetracker from the drop-down menu, a set of options that are specific to that device will appear, such as the model and serial number of your device. Here we will follow through with the MouseGaze options:

Expand Down Expand Up @@ -175,4 +192,4 @@ If there is a problem - We want to know!
-------------------------------------------------------------
If you have followed the steps above and are having an issue, please post details of this on the `PsychoPy Forum <https://discourse.psychopy.org/>`_.

We are constantly looking to update our documentation so that it's easy for you to use PsychoPy in the way that you want to. Posting in our forum allows us to see what issues users are having, offer solutions, and to update our documentation to hopefully prevent those issues from occurring again!
We are constantly looking to update our documentation so that it's easy for you to use PsychoPy in the way that you want to. Posting in our forum allows us to see what issues users are having, offer solutions, and to update our documentation to hopefully prevent those issues from occurring again!
Binary file added docs/source/images/PluginsAndPackagesManager.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 14 additions & 5 deletions psychopy/app/locale/ar_001/LC_MESSAGE/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PsychoPy v2023.2.3\n"
"POT-Creation-Date: 2024-02-03 15:47+0000\n"
"POT-Creation-Date: 2024-05-19 12:59+0100\n"
"PO-Revision-Date: \n"
"Last-Translator: Jeremy R. Gray <jrgray@gmail.com>\n"
"Language-Team: \n"
Expand All @@ -15,7 +15,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && "
"n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
"X-Generator: Poedit 3.4.2\n"
"X-Generator: Poedit 3.4.4\n"
"X-Poedit-Basepath: ../../../../..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: _translate\n"
Expand Down Expand Up @@ -264,6 +264,7 @@ msgstr ""
"يتطلب الناسخ الذي تم اختياره '{engine}' مفتاح API، يرجى توفير واحد في "
"التفضيلات."

# the translation of the word "parameter" might need revision
#: psychopy/alerts/alertsCatalogue/alertmsg.py:132
#, fuzzy, python-brace-format
msgid ""
Expand All @@ -272,35 +273,43 @@ msgstr ""
"لن يتم استخدام معامل الجهاز (device parameter) الخاص بمكوّن الميكروفون "
"\"{name}\" عبر الإنترنت."

# runner translation
#: psychopy/alerts/alertsCatalogue/alertmsg.py:136
#, python-brace-format
#, fuzzy, python-brace-format
msgid ""
"The file you are attempting to run does not seem to exist, the full path "
"supplied to Runner was {path}"
msgstr ""
"يبدو أن الملف الذي تحاول تشغيله غير موجود، يبدو أن المسار الكامل الذي تم "
"توفيره إلى \"العداء\" كان {path}"

#: psychopy/alerts/alertsCatalogue/alertmsg.py:140
#, python-brace-format
msgid ""
"Color space attribute `.{colorSpaceAttrib}` is no longer in use, as colors are "
"no longer tied to one space."
msgstr ""
"لم تعد سمة مساحة اللون \". {colorSpaceAttrib}\" قيد الاستخدام، حيث لم تعد "
"الألوان مرتبطة بمساحة واحدة."

#: psychopy/alerts/alertsCatalogue/alertmsg.py:144
#, python-brace-format
msgid ""
"RGB attribute `.{rgbAttrib}` is no longer in use, as non-RGB colors now handle "
"their own conversion."
msgstr ""
"لم تعد سمة RGB \". {rgbAttrib}\" قيد الاستخدام، حيث أن الألوان غير RGB تتعامل "
"الآن مع التحويل الخاص بها."

#: psychopy/app/Resources/psychopy.desktop:5
#: psychopy/app/Resources/psychopy.desktop:6
#, fuzzy
msgid "PsychoPy"
msgstr ""
msgstr "سايكوباي"

#: psychopy/app/Resources/psychopy.desktop:7
msgid "Psychology software in Python"
msgstr ""
msgstr "برنامج سايكوباي في بايثون"

#: psychopy/app/_psychopyApp.py:98 psychopy/app/builder/builder.py:381
#: psychopy/app/coder/coder.py:1570 psychopy/app/runner/runner.py:207
Expand Down
1 change: 0 additions & 1 deletion psychopy/experiment/components/keyboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ def writeRoutineStartCode(self, buff):
code = (
"# create starting attributes for %(name)s\n"
"%(name)s.keys = []\n"
"%(name)s.getKeys(clear=True)\n"
"%(name)s.rt = []\n"
"_%(name)s_allKeys = []\n"
)
Expand Down
19 changes: 12 additions & 7 deletions psychopy/hardware/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ def getKeys(self, keyList=None, ignoreKeys=None, waitRelease=True, clear=True):
if resp.value in ignoreKeys:
wanted = False
# if we got this far and the key is still wanted and not present, add it to output
if wanted and resp not in keys:
if wanted and not any(k is resp for k in keys):
keys.append(resp)
# if clear=True, mark wanted responses as toClear
if wanted and clear:
Expand Down Expand Up @@ -575,7 +575,8 @@ def parseMessage(self, message):
if message.type == "KEYBOARD_PRESS":
# if message is from a key down event, make a new response
response = KeyPress(code=message.char, tDown=message.time, name=message.key)
response.rt = response.tDown - (self.clock.getLastResetTime() - self._iohubKeyboard.clock.getLastResetTime())
response.rt = response.tDown - (
self.clock.getLastResetTime() - self._iohubKeyboard.clock.getLastResetTime())
self._keysStillDown.append(response)
else:
# if message is from a key up event, alter existing response
Expand All @@ -602,9 +603,9 @@ def parseMessage(self, message):

def waitKeys(self, maxWait=float('inf'), keyList=None, waitRelease=True,
clear=True):
"""Same as `~psychopy.hardware.keyboard.Keyboard.getKeys`,
"""Same as `~psychopy.hardware.keyboard.Keyboard.getKeys`,
but halts everything (including drawing) while awaiting keyboard input.
:Parameters:
maxWait : any numeric value.
Maximum number of seconds period and which keys to wait for.
Expand All @@ -624,9 +625,9 @@ def waitKeys(self, maxWait=float('inf'), keyList=None, waitRelease=True,
clear : **True** or False
Whether to clear the keyboard event buffer (and discard preceding
keypresses) before starting to monitor for new keypresses.
Returns None if times out.
"""
timer = psychopy.clock.Clock()

Expand All @@ -645,6 +646,7 @@ def waitKeys(self, maxWait=float('inf'), keyList=None, waitRelease=True,

def clearEvents(self, eventType=None):
"""Clear the events from the Keyboard such as previous key presses"""
# clear backend buffers
if KeyboardDevice._backend == 'ptb':
for buffer in self._buffers.values():
buffer.flush() # flush the device events to the soft buffer
Expand All @@ -656,6 +658,9 @@ def clearEvents(self, eventType=None):
else:
global event
event.clearEvents(eventType)
# clear dispatched responses
self.responses = []

logging.info("Keyboard events cleared", obj=self)


Expand Down Expand Up @@ -747,7 +752,7 @@ def getKeys(self, keyList=[], ignoreKeys=[], waitRelease=True, clear=True):
if not keyList and not waitRelease:
keyPresses = list(self._keysStillDown)
for k in list(self._keys):
if not any(x.name == k.name and x.tDown == k.tDown for x in keyPresses):
if not any(x.name == k.name and x.tDown == k.tDown for x in keyPresses):
keyPresses.append(k)
if clear:
self._keys = deque()
Expand Down
48 changes: 24 additions & 24 deletions psychopy/iohub/client/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
from ..devices.keyboard import KeyboardInputEvent
from ..constants import EventConstants, KeyboardConstants

#pylint: disable=protected-access
# pylint: disable=protected-access

getTime = Computer.getTime
kb_cls_attr_names = KeyboardInputEvent.CLASS_ATTRIBUTE_NAMES
kb_mod_codes2labels = KeyboardConstants._modifierCodes2Labels


class KeyboardEvent(ioEvent):
"""
Base class for KeyboardPress and KeyboardRelease events.
Expand All @@ -42,13 +43,13 @@ class KeyboardEvent(ioEvent):
def __init__(self, ioe_array):
super(KeyboardEvent, self).__init__(ioe_array)
for aname, aindex, in list(self._attrib_index.items()):
setattr(self, '_%s'%aname, ioe_array[aindex])
setattr(self, '_%s' % aname, ioe_array[aindex])
self._modifiers = kb_mod_codes2labels(self._modifiers)

@property
def key(self):
return self._key

@property
def char(self):
"""The unicode value of the keyboard event, if available. This field is
Expand Down Expand Up @@ -87,7 +88,7 @@ def modifiers(self):
def __str__(self):
pstr = ioEvent.__str__(self)
return '{}, key: {} char: {}, modifiers: {}'.format(pstr, self.key,
self.char, self.modifiers)
self.char, self.modifiers)

def __eq__(self, v):
if isinstance(v, KeyboardEvent):
Expand All @@ -113,8 +114,8 @@ class KeyboardRelease(KeyboardEvent):

def __init__(self, ioe_array):
super(KeyboardRelease, self).__init__(ioe_array)
#self._duration = ioe_array[self._attrib_index['duration']]
#self._press_event_id = ioe_array[self._attrib_index['press_event_id']]
# self._duration = ioe_array[self._attrib_index['duration']]
# self._press_event_id = ioe_array[self._attrib_index['press_event_id']]

@property
def duration(self):
Expand Down Expand Up @@ -153,47 +154,47 @@ def __str__(self):
class Keyboard(ioHubDeviceView):
"""The Keyboard device provides access to KeyboardPress and KeyboardRelease
events as well as the current keyboard state.
Examples:
A. Print all keyboard events received for 5 seconds::
from psychopy.iohub import launchHubServer
from psychopy.core import getTime
# Start the ioHub process. 'io' can now be used during the
# experiment to access iohub devices and read iohub device events.
io = launchHubServer()
keyboard = io.devices.keyboard
# Check for and print any Keyboard events received for 5 seconds.
stime = getTime()
while getTime()-stime < 5.0:
for e in keyboard.getEvents():
print(e)
# Stop the ioHub Server
io.quit()
B. Wait for a keyboard press event (max of 5 seconds)::
from psychopy.iohub import launchHubServer
from psychopy.core import getTime
# Start the ioHub process. 'io' can now be used during the
# experiment to access iohub devices and read iohub device events.
io = launchHubServer()
keyboard = io.devices.keyboard
# Wait for a key keypress event ( max wait of 5 seconds )
presses = keyboard.waitForPresses(maxWait=5.0)
print(presses)
# Stop the ioHub Server
io.quit()
io.quit()
"""
KEY_PRESS = EventConstants.KEYBOARD_PRESS
KEY_RELEASE = EventConstants.KEYBOARD_RELEASE
Expand Down Expand Up @@ -228,8 +229,8 @@ def _syncDeviceState(self):
"""
kb_state = self.getCurrentDeviceState()

events = {int(k):v for k,v in list(kb_state.get('events').items())}
pressed_keys = {int(k):v for k,v in list(kb_state.get('pressed_keys',{}).items())}
events = {int(k): v for k, v in list(kb_state.get('events').items())}
pressed_keys = {int(k): v for k, v in list(kb_state.get('pressed_keys', {}).items())}

self._reporting = kb_state.get('reporting_events')
self._pressed_keys.clear()
Expand Down Expand Up @@ -289,11 +290,10 @@ def reporting(self, r):
self._reporting = self.enableEventReporting(r)
return self._reporting


def clearEvents(self, event_type=None, filter_id=None):
self._clearLocalEvents(event_type)
return self._clearEventsRPC(event_type=event_type,
filter_id=filter_id)
filter_id=filter_id)

def getKeys(self, keys=None, chars=None, ignoreKeys=None, mods=None, duration=None,
etype=None, clear=True):
Expand Down
Loading

0 comments on commit 5157d38

Please sign in to comment.