From 59bdb3da81564e02ac32e33fae8425225bbf7c93 Mon Sep 17 00:00:00 2001 From: smathot Date: Wed, 15 Mar 2023 21:25:29 +0100 Subject: [PATCH 1/5] Refactor plugins for OpenSesame 4.0 api --- .../artwork => artwork}/icons.svg | 0 opensesame_plugins/pygaze/__init__.py | 2 + .../pygaze/pygaze_drift_correct/__init__.py | 40 +++ .../pygaze_drift_correct.py | 89 +++++ .../pygaze/pygaze_init/__init__.py | 107 ++++++ .../pygaze/pygaze_init/pygaze_init.py | 253 +++++++++++++ .../pygaze/pygaze_log/__init__.py | 23 ++ .../pygaze/pygaze_log/pygaze_log.py | 40 +++ .../pygaze/pygaze_start_recording/__init__.py | 12 + .../pygaze_start_recording.py | 33 ++ .../pygaze/pygaze_stop_recording/__init__.py | 10 + .../pygaze_stop_recording.py | 33 ++ .../pygaze/pygaze_wait/__init__.py | 20 ++ .../pygaze/pygaze_wait/pygaze_wait.py | 55 +++ .../pygaze_drift_correct/info.yaml | 39 -- .../pygaze_drift_correct.py | 138 -------- opensesame_plugins/pygaze_init/info.yaml | 111 ------ opensesame_plugins/pygaze_init/pygaze_init.py | 334 ------------------ opensesame_plugins/pygaze_log/info.yaml | 21 -- opensesame_plugins/pygaze_log/pygaze_log.py | 60 ---- .../pygaze_start_recording/info.yaml | 9 - .../pygaze_start_recording.py | 53 --- .../pygaze_stop_recording/info.yaml | 9 - .../pygaze_stop_recording.py | 59 ---- opensesame_plugins/pygaze_wait/info.yaml | 16 - opensesame_plugins/pygaze_wait/pygaze_wait.py | 78 ---- pygaze/_mouse/osmouse.py | 6 +- pygaze/_screen/osscreen.py | 7 +- pygaze/_time/ostime.py | 6 +- 29 files changed, 724 insertions(+), 939 deletions(-) rename {opensesame_plugins/artwork => artwork}/icons.svg (100%) create mode 100644 opensesame_plugins/pygaze/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_drift_correct/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_drift_correct/pygaze_drift_correct.py create mode 100644 opensesame_plugins/pygaze/pygaze_init/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_init/pygaze_init.py create mode 100644 opensesame_plugins/pygaze/pygaze_log/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_log/pygaze_log.py create mode 100644 opensesame_plugins/pygaze/pygaze_start_recording/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_start_recording/pygaze_start_recording.py create mode 100644 opensesame_plugins/pygaze/pygaze_stop_recording/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_stop_recording/pygaze_stop_recording.py create mode 100644 opensesame_plugins/pygaze/pygaze_wait/__init__.py create mode 100644 opensesame_plugins/pygaze/pygaze_wait/pygaze_wait.py delete mode 100644 opensesame_plugins/pygaze_drift_correct/info.yaml delete mode 100644 opensesame_plugins/pygaze_drift_correct/pygaze_drift_correct.py delete mode 100644 opensesame_plugins/pygaze_init/info.yaml delete mode 100644 opensesame_plugins/pygaze_init/pygaze_init.py delete mode 100644 opensesame_plugins/pygaze_log/info.yaml delete mode 100644 opensesame_plugins/pygaze_log/pygaze_log.py delete mode 100644 opensesame_plugins/pygaze_start_recording/info.yaml delete mode 100644 opensesame_plugins/pygaze_start_recording/pygaze_start_recording.py delete mode 100644 opensesame_plugins/pygaze_stop_recording/info.yaml delete mode 100644 opensesame_plugins/pygaze_stop_recording/pygaze_stop_recording.py delete mode 100644 opensesame_plugins/pygaze_wait/info.yaml delete mode 100644 opensesame_plugins/pygaze_wait/pygaze_wait.py diff --git a/opensesame_plugins/artwork/icons.svg b/artwork/icons.svg similarity index 100% rename from opensesame_plugins/artwork/icons.svg rename to artwork/icons.svg diff --git a/opensesame_plugins/pygaze/__init__.py b/opensesame_plugins/pygaze/__init__.py new file mode 100644 index 0000000..65ed55f --- /dev/null +++ b/opensesame_plugins/pygaze/__init__.py @@ -0,0 +1,2 @@ +# The name of the packages to check for updates on conda and pip +packages = ['pygaze'] diff --git a/opensesame_plugins/pygaze/pygaze_drift_correct/__init__.py b/opensesame_plugins/pygaze/pygaze_drift_correct/__init__.py new file mode 100644 index 0000000..fd0bdea --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_drift_correct/__init__.py @@ -0,0 +1,40 @@ +"""Performs eye-tracker drift correction""" + +category = 'PyGaze' +priority = 10 +help = 'manual/eyetracking/pygaze' +icon = 'os-pygaze_drift_correct' +controls = [ + {'type': 'line_edit', + 'var': 'xpos', + 'label': 'X position', + 'tooltip': 'X coordinate for drift correction'}, + {'type': 'line_edit', + 'var': 'ypos', + 'label': 'Y position', + 'tooltip': 'Y position for drift correction'}, + {'type': 'line_edit', + 'var': 'target_color', + 'label': 'Target color', + 'tooltip': 'Color for the drift-correction target', + 'name': 'line_edit_target_color'}, + {'type': 'combobox', + 'var': 'target_style', + 'label': 'Target style (OpenSesame >= 2.8.0)', + 'options': ['default', + 'large-filled', + 'small-filled', + 'large-open', + 'small-open', + 'large-cross', + 'small-cross'], + 'tooltip': 'Style for the drift-correction target', + 'name': 'combobox_target_style'}, + {'type': 'checkbox', + 'var': 'draw_target', + 'label': 'Show display with drift-correction target', + 'tooltip': 'Indicates whether a drift-correction display should be shown'}, + {'type': 'checkbox', + 'var': 'fixation_triggered', + 'label': 'Fixation triggered (no spacebar press required)', + 'tooltip': 'Indicates whether drift correction should be performed as soon as a stable fixation is detected'}] diff --git a/opensesame_plugins/pygaze/pygaze_drift_correct/pygaze_drift_correct.py b/opensesame_plugins/pygaze/pygaze_drift_correct/pygaze_drift_correct.py new file mode 100644 index 0000000..cac0580 --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_drift_correct/pygaze_drift_correct.py @@ -0,0 +1,89 @@ +"""This file is part of PyGaze. + +PyGaze is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PyGaze is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with PyGaze. If not, see . +""" + +import inspect +from openexp.canvas import Canvas +from libopensesame.item import Item +from libqtopensesame.items.qtautoplugin import QtAutoPlugin +from pygaze.display import Display + + +class PygazeDriftCorrect(Item): + + def reset(self): + self.var.xpos = 0 + self.var.ypos = 0 + self.var.fixation_triggered = u'no' + self.var.target_color = u'[foreground]' + self.var.target_style = u'default' + self.var.draw_target = u'yes' + + def prepare_drift_correction_canvas(self): + """A hook to prepare the canvas with the drift-correction target.""" + if self.var.draw_target == u'yes': + self.dc_canvas = Canvas(self.experiment) + self.dc_canvas.fixdot( + self.var.xpos, self.var.ypos, color=self.var.target_color, + style=self.var.target_style) + else: + self.dc_canvas = None + + def draw_drift_correction_canvas(self, x, y): + """A hook to show the canvas with the drift-correction target.""" + if self.dc_canvas is not None: + self.dc_canvas.show() + + def prepare(self): + super().prepare() + self.prepare_drift_correction_canvas() + self.experiment.pygaze_eyetracker. \ + set_draw_drift_correction_target_func( + self.draw_drift_correction_canvas) + + def run(self): + self.set_item_onset() + xpos = self.var.width / 2 + self.var.xpos + ypos = self.var.height / 2 + self.var.ypos + while True: + success = self.experiment.pygaze_eyetracker.drift_correction( + pos=(xpos, ypos), + fix_triggered=self.var.fixation_triggered == 'yes') + if success: + break + + +class QtPygazeDriftCorrect(PygazeDriftCorrect, QtAutoPlugin): + + def __init__(self, name, experiment, script=None): + PygazeDriftCorrect.__init__(self, name, experiment, script) + QtAutoPlugin.__init__(self, __file__) + + def init_edit_widget(self): + super().init_edit_widget() + self.custom_interactions() + + def apply_edit_changes(self): + if not super().apply_edit_changes() or self.lock: + return False + self.custom_interactions() + + def custom_interactions(self): + """Disables the target-style combobox if no target display should be + drawn + """ + draw_target = self.var.draw_target == u'yes' + self.combobox_target_style.setEnabled(draw_target) + self.line_edit_target_color.setEnabled(draw_target) diff --git a/opensesame_plugins/pygaze/pygaze_init/__init__.py b/opensesame_plugins/pygaze/pygaze_init/__init__.py new file mode 100644 index 0000000..dfe7d7e --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_init/__init__.py @@ -0,0 +1,107 @@ +"""Initializes and calibrates the eye tracker""" + +category = 'PyGaze' +priority = 10 +help = 'manual/eyetracking/pygaze' +icon = 'os-pygaze_init' +controls = [ + {'type': 'combobox', + 'var': 'tracker_type', + 'label': 'Select tracker type', + 'options': ['Simple dummy', + 'Advanced dummy (mouse simulation)', + 'EyeLink', + 'EyeLogic', + 'SMI', + 'EyeTribe', + 'OpenGaze', + 'Alea', + 'Tobii', + 'Tobii-legacy', + 'Tobii Pro Glasses 2'], + 'tooltip': 'A list of supported eye trackers'}, + {'type': 'checkbox', + 'var': 'calibrate', + 'label': 'Calibrate tracker', + 'tooltip': 'Indicates whether calibration should be started'}, + {'type': 'checkbox', + 'var': 'calbeep', + 'label': 'Calibration beep', + 'tooltip': 'Play a beep when the calibration target jumps', + 'name': 'checkbox_calbeep'}, + {'type': 'line_edit', + 'label': 'Log file', + 'var': '_logfile', + 'tooltip': 'The name for the log file'}, + {'type': 'spinbox', + 'var': 'sacc_vel_thr', + 'label': 'Saccade velocity threshold', + 'min_val': 0, + 'max_val': 100000, + 'suffix': ' °/s', + 'tooltip': 'Velocity threshold for saccade detection algorithm', + 'name': 'spinbox_sacc_vel_thr'}, + {'type': 'spinbox', + 'var': 'sacc_acc_thr', + 'label': 'Saccade acceleration threshold', + 'min_val': 0, + 'max_val': 100000, + 'suffix': ' °/s/s', + 'tooltip': 'Acceleration threshold for saccade detection algorithm', + 'name': 'spinbox_sacc_acc_thr'}, + {'type': 'text', + 'label': 'Warning: PyLink is required for EyeLink functionality and is not installed. Visit http://osdoc.cogsci.nl/manual/eyetracking/eyelink/ for more information.', + 'name': 'text_eyelink_pylink_check'}, + {'type': 'text', 'label': 'DUMMY', 'name': 'text_pygaze_version'}, + {'type': 'line_edit', + 'label': 'Alea API key', + 'var': 'alea_api_key', + 'name': 'line_edit_alea_api_key', + 'tooltip': 'The API key provided to you by Alea'}, + {'type': 'checkbox', + 'var': 'alea_animated_calibration', + 'label': 'Animated calibration target', + 'tooltip': 'Turn on child-friendly animated calibration targets', + 'name': 'checkbox_alea_animated_calibration'}, + {'type': 'checkbox', + 'var': 'eyelink_force_drift_correct', + 'label': 'Force drift correction (for EyeLink 1000)', + 'tooltip': 'Turn on active drift correction', + 'name': 'checkbox_eyelink_force_drift_correct'}, + {'type': 'combobox', + 'var': 'eyelink_pupil_size_mode', + 'label': 'Pupil-size mode', + 'tooltip': 'Determines whether pupil size is recorded as area or diameter', + 'name': 'combobox_eyelink_pupil_size_mode', + 'options': ['area', 'diameter']}, + {'type': 'line_edit', + 'label': 'SMI IP address', + 'var': 'smi_ip', + 'name': 'line_edit_smi_ip', + 'tooltip': "The tracker's IP address"}, + {'type': 'spinbox', + 'var': 'smi_send_port', + 'label': 'SMI send-port number', + 'min_val': 0, + 'max_val': 100000, + 'name': 'spinbox_smi_send_port', + 'tooltip': 'Port number for sending messages to the SMI tracker'}, + {'type': 'spinbox', + 'var': 'smi_recv_port', + 'label': 'SMI receive-port number', + 'min_val': 0, + 'max_val': 100000, + 'name': 'spinbox_smi_recv_port', + 'tooltip': 'Port number for receiving messages from the SMI tracker'}, + {'type': 'line_edit', + 'label': 'Tobii Glasses IPV4/IPv6 address', + 'var': 'tobiiglasses_address', + 'name': 'line_edit_tobiiglasses_address', + 'tooltip': 'The Tobii Pro Glasses IPv4/IPv6 address'}, + {'type': 'spinbox', + 'label': 'Tobii Glasses UDP port number', + 'var': 'tobiiglasses_udpport', + 'min_val': 0, + 'max_val': 100000, + 'name': 'spinbox_tobiiglasses_udpport', + 'tooltip': 'Port number for the Tobii Pro Glasses 2 eye-tracker'}] diff --git a/opensesame_plugins/pygaze/pygaze_init/pygaze_init.py b/opensesame_plugins/pygaze/pygaze_init/pygaze_init.py new file mode 100644 index 0000000..eb3892f --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_init/pygaze_init.py @@ -0,0 +1,253 @@ +#-*- coding:utf-8 -*- + +""" +This file is part of PyGaze. + +PyGaze is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PyGaze is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with PyGaze. If not, see . +""" + +import os +import inspect +from openexp.canvas import Canvas +from libopensesame.exceptions import OSException, InvalidValue +from libopensesame.item import Item +from libopensesame.oslogging import oslogger +from libqtopensesame.items.qtautoplugin import QtAutoPlugin +from pygaze.eyetracker import EyeTracker +from pygaze.display import Display +import pygaze + + +class PygazeInit(Item): + + def __init__(self, name, experiment, string=None): + super().__init__(name, experiment, string) + self.reload_pygaze() + + def reset(self): + # Generic settings + self.var.tracker_type = u'Simple dummy' + self.var.calibrate = u'yes' + self.var.calbeep = u'yes' + self.var.sacc_vel_thr = 35 + self.var.sacc_acc_thr = 9500 + self.var._logfile = u'automatic' + # Alea-specific settings + self.var.alea_api_key = u'Contact Alea for an API key' + self.var.alea_animated_calibration = u'no' + # EyeLink-specific settings + self.var.eyelink_force_drift_correct = u'yes' + self.var.eyelink_pupil_size_mode = u'area' + # SMI-specific settings + self.var.smi_ip = u'127.0.0.1' + self.var.smi_send_port = 4444 + self.var.smi_recv_port = 5555 + # Tobii Pro Glasses 2 settings + self.var.tobiiglasses_address = u'192.168.71.50' + self.var.tobiiglasses_udpport = 49152 + + def close(self): + """Closes the connection with the eye tracker when the experiment is + finished. + """ + oslogger.debug('Starting PyGaze deinitialisation') + self.clock.sleep(1000) + self.experiment.pygaze_eyetracker.close() + self.experiment.pygaze_eyetracker = None + oslogger.debug('Finished PyGaze deinitialisation') + self.clock.sleep(1000) + + def draw_calibration_canvas(self, x, y): + """A hook to prepare the canvas with the clibration target.""" + dc_canvas = Canvas(self.experiment) + x -= dc_canvas._xcenter + y -= dc_canvas._ycenter + dc_canvas.fixdot(x, y, style='large-open') + if self.var.calbeep == 'yes': + self.beep.play() + dc_canvas.show() + + def reload_pygaze(self): + """Reloads pygaze modules to get a clean start. This is necessary, + because otherwise PyGaze will try to use the old experiment instance + when the experiment is executed twice. Explicitly reloading all + OpenSesame-related modules will fix this. + """ + from pygaze import settings + settings.osexperiment = self.experiment + settings.DISPTYPE = u'opensesame' + settings.DISPSIZE = self.resolution() + settings.BGC = self.var.background + settings.FGC = self.var.foreground + settings.ALEAKEY = self.var.alea_api_key + if self.var.calbeep == u'yes': + settings.EYELINKCALBEEP = True + else: + settings.EYELINKCALBEEP = False + if self.var.alea_animated_calibration == u'yes': + settings.ALEAANIMATEDCALIBRATION = True + else: + settings.ALEAANIMATEDCALIBRATION = False + + def run(self): + if hasattr(self.experiment, u'pygaze_eyetracker'): + raise OSException( + 'You should have only one instance of `pygaze_init` in your experiment') + self.set_item_onset() + # Determine the tracker type and perform certain tracker-specific + # operations. + kwdict = {} + if self.var.tracker_type == u'Simple dummy': + tracker_type = u'dumbdummy' + elif self.var.tracker_type == u'Advanced dummy (mouse simulation)': + tracker_type = u'dummy' + elif self.var.tracker_type == u'EyeLink': + tracker_type = u'eyelink' + kwdict[u'eyelink_force_drift_correct'] = \ + self.var.eyelink_force_drift_correct == u'yes' + kwdict[u'pupil_size_mode'] = self.var.eyelink_pupil_size_mode + elif self.var.tracker_type == u'EyeLogic': + tracker_type = u'eyelogic' + elif self.var.tracker_type == u'SMI': + tracker_type = u'smi' + kwdict[u'ip'] = self.var.smi_ip + kwdict[u'sendport'] = self.var.smi_send_port + kwdict[u'receiveport'] = self.var.smi_recv_port + elif self.var.tracker_type == u'EyeTribe': + tracker_type = u'eyetribe' + elif self.var.tracker_type == u'OpenGaze': + tracker_type = u'opengaze' + elif self.var.tracker_type == u'Alea': + tracker_type = u'alea' + kwdict[u'alea_key'] = self.var.alea_api_key + kwdict[u'animated_calibration'] = \ + self.var.alea_animated_calibration == u'yes' + elif self.var.tracker_type == u'Tobii': + tracker_type = u'tobii' + elif self.var.tracker_type == u'Tobii-legacy': + tracker_type = u'tobii-legacy' + elif self.var.tracker_type == u'Tobii Pro Glasses 2': + tracker_type = u'tobiiglasses' + kwdict[u'address'] = self.var.tobiiglasses_address + kwdict[u'udpport'] = self.var.tobiiglasses_udpport + else: + raise InvalidValue( + f'Unknown tracker type: {self.var.tracker_type}') + # Determine logfile + if self.var._logfile == u'automatic': + logfile = os.path.splitext(self.var.logfile)[0] + if tracker_type == u'eyelink': + # Automatically shorten filenames like 'subject-0', because + # these are too long. This avoids having to rename logfiles + # all the time. + basename = os.path.basename(logfile) + dirname = os.path.dirname(logfile) + if len(basename) > 8 and basename.startswith(u'subject-'): + basename = u'sub_' + basename[8:] + logfile = os.path.join(dirname, basename) + print(u'Attention: EyeLink logfile renamed to %s.edf' \ + % logfile) + elif basename == u'defaultlog': + logfile = u'default' + print(u'Attention: EyeLink logfile renamed to %s.edf' \ + % logfile) + logfile = logfile + u'.edf' + kwdict[u'data_file'] = logfile + else: + logfile = self.var._logfile + # Register the logfile with OpenSesame + self.experiment.data_files.append(logfile) + # Determine event detection. Currently, only the EyeLink has native + # event detection. + if tracker_type == u'eyelink': + event_detection = u'native' + else: + event_detection = u'pygaze' + # Initialize pygaze and the eye-tracker object + self.experiment.pygaze_display = Display(u'opensesame') + self.experiment.pygaze_eyetracker = EyeTracker( + self.experiment.pygaze_display, + trackertype=tracker_type, + eventdetection=event_detection, + saccade_velocity_threshold=self.var.sacc_vel_thr, + saccade_acceleration_threshold=self.var.sacc_acc_thr, + logfile=logfile, + **kwdict) + if self.var.calbeep == u'yes': + from openexp.synth import synth + self.beep = synth(self.experiment) + self.experiment.pygaze_eyetracker.set_draw_calibration_target_func( + self.draw_calibration_canvas) + self.experiment.pygaze_eyetracker.set_draw_drift_correction_target_func( + self.draw_calibration_canvas) + self.experiment.cleanup_functions.append(self.close) + if self.var.calibrate == u'yes': + self.experiment.pygaze_eyetracker.calibrate() + self.python_workspace[u'eyetracker'] = self.experiment.pygaze_eyetracker + + +class QtPygazeInit(PygazeInit, QtAutoPlugin): + + def __init__(self, name, experiment, script=None): + PygazeInit.__init__(self, name, experiment, script) + QtAutoPlugin.__init__(self, __file__) + + def init_edit_widget(self): + super().init_edit_widget() + self.custom_interactions() + self.text_pygaze_version.setText( + f'PyGaze version {pygaze.version}') + + def apply_edit_changes(self): + if not super().apply_edit_changes() or self.lock: + return False + self.custom_interactions() + + def edit_widget(self): + if self.lock: + return + self.lock = True + w = super().edit_widget() + self.custom_interactions() + self.lock = False + return w + + def custom_interactions(self): + """Activates the relevant controls for each tracker.""" + alea = self.var.tracker_type == u'Alea' + self.line_edit_alea_api_key.setEnabled(alea) + self.checkbox_alea_animated_calibration.setEnabled(alea) + smi = self.var.tracker_type == u'SMI' + self.line_edit_smi_ip.setEnabled(smi) + self.spinbox_smi_send_port.setEnabled(smi) + self.spinbox_smi_recv_port.setEnabled(smi) + eyelink = self.var.tracker_type == u'EyeLink' + self.checkbox_eyelink_force_drift_correct.setEnabled(eyelink) + self.combobox_eyelink_pupil_size_mode.setEnabled(eyelink) + self.spinbox_sacc_acc_thr.setDisabled(eyelink) + self.spinbox_sacc_vel_thr.setDisabled(eyelink) + tobiiglasses = self.var.tracker_type == u'Tobii Pro Glasses 2' + self.line_edit_tobiiglasses_address.setEnabled(tobiiglasses) + self.spinbox_tobiiglasses_udpport.setEnabled(tobiiglasses) + if eyelink: + try: + import pylink + except Exception as e: + pylink = None + if pylink is None: + self.text_eyelink_pylink_check.show() + else: + self.text_eyelink_pylink_check.hide() + else: + self.text_eyelink_pylink_check.hide() diff --git a/opensesame_plugins/pygaze/pygaze_log/__init__.py b/opensesame_plugins/pygaze/pygaze_log/__init__.py new file mode 100644 index 0000000..83be897 --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_log/__init__.py @@ -0,0 +1,23 @@ +"""Writes information to the eye-tracker logfile""" + +category = 'PyGaze' +priority = 10 +icon = 'os-pygaze_log' +help = 'manual/eyetracking/pygaze' +controls = [ + {'type': 'spinbox', + 'var': 'throttle', + 'label': 'Pause between messages', + 'min_val': 0, + 'max_val': 1000, + 'suffix': ' ms', + 'tooltip': 'A pause between messages to avoid overloading the eye tracker'}, + {'type': 'checkbox', + 'var': 'auto_log', + 'label': 'Automatically log all variables', + 'tooltip': 'Automatically send all experimental variables to the eye tracker'}, + {'type': 'editor', + 'var': 'msg', + 'label': 'Log message', + 'syntax': False, + 'tooltip': 'The message to write to the eye tracker'}] diff --git a/opensesame_plugins/pygaze/pygaze_log/pygaze_log.py b/opensesame_plugins/pygaze/pygaze_log/pygaze_log.py new file mode 100644 index 0000000..4257c3f --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_log/pygaze_log.py @@ -0,0 +1,40 @@ +#-*- coding:utf-8 -*- + +""" +This file is part of PyGaze. + +PyGaze is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PyGaze is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with PyGaze. If not, see . +""" + +from libopensesame.item import Item +from pygaze.display import Display + + +class PygazeLog(Item): + + def reset(self): + self.var.msg = '' + self.var.auto_log = 'no' + self.var.throttle = 2 + + def run(self): + self.set_item_onset() + for msg in self.var.msg.split(u'\n'): + self.experiment.pygaze_eyetracker.log(self.syntax.eval_text(msg)) + self.clock.sleep(self.var.throttle) + if self.var.auto_log == u'yes': + for logvar, info in self.experiment.var.inspect().items(): + self.experiment.pygaze_eyetracker.log_var( + logvar, info[u'value']) + self.clock.sleep(self.var.throttle) diff --git a/opensesame_plugins/pygaze/pygaze_start_recording/__init__.py b/opensesame_plugins/pygaze/pygaze_start_recording/__init__.py new file mode 100644 index 0000000..1805ad0 --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_start_recording/__init__.py @@ -0,0 +1,12 @@ +"""Puts the eye tracker out of recording mode""" + +category = 'PyGaze' +priority = 10 +help = 'manual/eyetracking/pygaze' +icon = 'os-pygaze_start_recording' +controls = [ + {'type': 'line_edit', + 'var': 'status_msg', + 'label': 'Status message', + 'tooltip': 'A text to use as status message' + }] diff --git a/opensesame_plugins/pygaze/pygaze_start_recording/pygaze_start_recording.py b/opensesame_plugins/pygaze/pygaze_start_recording/pygaze_start_recording.py new file mode 100644 index 0000000..f454664 --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_start_recording/pygaze_start_recording.py @@ -0,0 +1,33 @@ +#-*- coding:utf-8 -*- + +""" +This file is part of PyGaze. + +PyGaze is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PyGaze is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with PyGaze. If not, see . +""" + +from libopensesame.item import Item +from pygaze.display import Display + + +class PygazeStartRecording(Item): + + def reset(self): + self.var.status_msg = 'start_trial' + + def run(self): + self.set_item_onset() + self.experiment.pygaze_eyetracker.start_recording() + self.experiment.pygaze_eyetracker.status_msg(self.var.status_msg) + self.experiment.pygaze_eyetracker.log(self.var.status_msg) diff --git a/opensesame_plugins/pygaze/pygaze_stop_recording/__init__.py b/opensesame_plugins/pygaze/pygaze_stop_recording/__init__.py new file mode 100644 index 0000000..a1a0183 --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_stop_recording/__init__.py @@ -0,0 +1,10 @@ +category = 'PyGaze' +priority = 10 +help = 'manual/eyetracking/pygaze' +icon = 'os-pygaze_stop_recording' +controls = [ + {'type': 'line_edit', + 'var': 'status_msg', + 'label': 'Status message', + 'tooltip': 'A text to use as status message' + }] diff --git a/opensesame_plugins/pygaze/pygaze_stop_recording/pygaze_stop_recording.py b/opensesame_plugins/pygaze/pygaze_stop_recording/pygaze_stop_recording.py new file mode 100644 index 0000000..c41dac4 --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_stop_recording/pygaze_stop_recording.py @@ -0,0 +1,33 @@ +#-*- coding:utf-8 -*- + +""" +This file is part of PyGaze. + +PyGaze is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PyGaze is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with PyGaze. If not, see . +""" + +from libopensesame.item import Item +from pygaze.display import Display + + +class PygazeStopRecording(Item): + + def reset(self): + self.var.status_msg = 'stop_trial' + + def run(self): + self.set_item_onset() + self.experiment.pygaze_eyetracker.status_msg(self.var.status_msg) + self.experiment.pygaze_eyetracker.log(self.var.status_msg) + self.experiment.pygaze_eyetracker.stop_recording() diff --git a/opensesame_plugins/pygaze/pygaze_wait/__init__.py b/opensesame_plugins/pygaze/pygaze_wait/__init__.py new file mode 100644 index 0000000..baca00b --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_wait/__init__.py @@ -0,0 +1,20 @@ +category = 'PyGaze' +priority = 10 +help = 'manual/eyetracking/pygaze' +icon = 'os-pygaze_wait' +controls = [ + { + 'type': 'combobox', + 'var': 'event', + 'label': 'Event', + 'options': [ + 'Saccade start', + 'Saccade end', + 'Fixation start', + 'Fixation end', + 'Blink start', + 'Blink end' + ], + 'tooltip': 'An eye-tracker event to wait for' + } +] diff --git a/opensesame_plugins/pygaze/pygaze_wait/pygaze_wait.py b/opensesame_plugins/pygaze/pygaze_wait/pygaze_wait.py new file mode 100644 index 0000000..adf27cd --- /dev/null +++ b/opensesame_plugins/pygaze/pygaze_wait/pygaze_wait.py @@ -0,0 +1,55 @@ +#-*- coding:utf-8 -*- + +""" +This file is part of PyGaze. + +PyGaze is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PyGaze is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with PyGaze. If not, see . +""" + +from libopensesame.item import Item +from libopensesame.exceptions import InvalidValue +from pygaze.display import Display + + +class PygazeWait(Item): + + def reset(self): + self.var.event = 'Saccade start' + + def prepare(self): + super().prepare() + if self.var.event == u'Saccade start': + self.wait_func = self.experiment.pygaze_eyetracker. \ + wait_for_saccade_start + elif self.var.event == u'Saccade end': + self.wait_func = self.experiment.pygaze_eyetracker. \ + wait_for_saccade_end + elif self.var.event == u'Fixation start': + self.wait_func = self.experiment.pygaze_eyetracker. \ + wait_for_fixation_start + elif self.var.event == u'Fixation end': + self.wait_func = self.experiment.pygaze_eyetracker. \ + wait_for_fixation_end + elif self.var.event == u'Blink start': + self.wait_func = self.experiment.pygaze_eyetracker. \ + wait_for_blink_start + elif self.var.event == u'Blink end': + self.wait_func = self.experiment.pygaze_eyetracker. \ + wait_for_blink_start + else: + raise InvalidValue(f'Unknown event: {self.var.event}') + + def run(self): + self.wait_func() + self.set_item_onset() diff --git a/opensesame_plugins/pygaze_drift_correct/info.yaml b/opensesame_plugins/pygaze_drift_correct/info.yaml deleted file mode 100644 index 237eb7d..0000000 --- a/opensesame_plugins/pygaze_drift_correct/info.yaml +++ /dev/null @@ -1,39 +0,0 @@ -category: PyGaze -priority: 10 -help: manual/eyetracking/pygaze -icon: os-pygaze_drift_correct -controls: - - type: line_edit - var: xpos - label: X position - tooltip: X coordinate for drift correction - - type: line_edit - var: ypos - label: Y position - tooltip: Y position for drift correction - - type: line_edit - var: target_color - label: Target color - tooltip: Color for the drift-correction target - name: line_edit_target_color - - type: combobox - var: target_style - label: 'Target style (OpenSesame >= 2.8.0)' - options: - - default - - large-filled - - small-filled - - large-open - - small-open - - large-cross - - small-cross - tooltip: Style for the drift-correction target - name: combobox_target_style - - type: checkbox - var: draw_target - label: Show display with drift-correction target - tooltip: Indicates whether a drift-correction display should be shown - - type: checkbox - var: fixation_triggered - label: Fixation triggered (no spacebar press required) - tooltip: Indicates whether drift correction should be performed as soon as a stable fixation is detected diff --git a/opensesame_plugins/pygaze_drift_correct/pygaze_drift_correct.py b/opensesame_plugins/pygaze_drift_correct/pygaze_drift_correct.py deleted file mode 100644 index e14e039..0000000 --- a/opensesame_plugins/pygaze_drift_correct/pygaze_drift_correct.py +++ /dev/null @@ -1,138 +0,0 @@ -#-*- coding:utf-8 -*- - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with PyGaze. If not, see . -""" - -import inspect -from openexp.canvas import canvas -from libopensesame.item import item -from libqtopensesame.items.qtautoplugin import qtautoplugin -from pygaze.display import Display - -class pygaze_drift_correct(item): - - """Plug-in runtime definition.""" - - description = u'Perform eye-tracker drift correction' - - def reset(self): - - """ - desc: - Resets plug-in settings. - """ - - self.var.xpos = 0 - self.var.ypos = 0 - self.var.fixation_triggered = u'no' - self.var.target_color = u'[foreground]' - self.var.target_style = u'default' - self.var.draw_target = u'yes' - - def prepare_drift_correction_canvas(self): - - """A hook to prepare the canvas with the drift-correction target.""" - - if self.var.draw_target == u'yes': - self.dc_canvas = canvas(self.experiment) - self.dc_canvas.fixdot(self.var.xpos, self.var.ypos, - color=self.var.target_color, style=self.var.target_style) - else: - self.dc_canvas = None - - def draw_drift_correction_canvas(self, x, y): - - """ - A hook to show the canvas with the drift-correction target. - - Arguments: - x -- The X coordinate (unused). - y -- The Y coordinate (unused). - """ - - if self.dc_canvas is not None: - self.dc_canvas.show() - - def prepare(self): - - """The preparation phase of the plug-in goes here.""" - - item.prepare(self) - self.prepare_drift_correction_canvas() - self.experiment.pygaze_eyetracker.set_draw_drift_correction_target_func( - self.draw_drift_correction_canvas) - - def run(self): - - """The run phase of the plug-in goes here.""" - - self.set_item_onset() - if self.var.uniform_coordinates == u'yes': - xpos = self.var.width / 2 + self.var.xpos - ypos = self.var.height / 2 + self.var.ypos - else: - xpos = self.var.xpos - ypos = self.var.ypos - while True: - success = self.experiment.pygaze_eyetracker.drift_correction( - pos=(xpos, ypos), - fix_triggered=self.var.fixation_triggered==u'yes') - if success: - break - -class qtpygaze_drift_correct(pygaze_drift_correct, qtautoplugin): - - """Plug-in GUI definition.""" - - def __init__(self, name, experiment, script=None): - - """ - Constructor. - - Arguments: - name -- The name of the plug-in. - experiment -- The experiment object. - - Keyword arguments: - script -- A definition script. (default=None) - """ - - pygaze_drift_correct.__init__(self, name, experiment, script) - qtautoplugin.__init__(self, __file__) - - def init_edit_widget(self): - - qtautoplugin.init_edit_widget(self) - self.custom_interactions() - - def apply_edit_changes(self): - - """Apply the controls""" - - if not qtautoplugin.apply_edit_changes(self) or self.lock: - return False - self.custom_interactions() - - def custom_interactions(self): - - """ - Disables the target-style combobox if no target display should be drawn. - """ - - draw_target = self.var.draw_target == u'yes' - self.combobox_target_style.setEnabled(draw_target) - self.line_edit_target_color.setEnabled(draw_target) diff --git a/opensesame_plugins/pygaze_init/info.yaml b/opensesame_plugins/pygaze_init/info.yaml deleted file mode 100644 index e16f963..0000000 --- a/opensesame_plugins/pygaze_init/info.yaml +++ /dev/null @@ -1,111 +0,0 @@ -category: PyGaze -priority: 10 -help: manual/eyetracking/pygaze -icon: os-pygaze_init -controls: - - type: combobox - var: tracker_type - label: Select tracker type - options: - - Simple dummy - - Advanced dummy (mouse simulation) - - EyeLink - - EyeLogic - - SMI - - EyeTribe - - OpenGaze - - Alea - - Tobii - - Tobii-legacy - - Tobii Pro Glasses 2 - tooltip: A list of supported eye trackers - - type: checkbox - var: calibrate - label: Calibrate tracker - tooltip: Indicates whether calibration should be started - - type: checkbox - var: calbeep - label: Calibration beep - tooltip: Play a beep when the calibration target jumps - name: checkbox_calbeep - - type: line_edit - label: Log file - var: _logfile - tooltip: The name for the log file - - type: spinbox - var: sacc_vel_thr - label: Saccade velocity threshold - min_val: 0 - max_val: 100000 - suffix: ' °/s' - tooltip: Velocity threshold for saccade detection algorithm - name: spinbox_sacc_vel_thr - - type: spinbox - var: sacc_acc_thr - label: Saccade acceleration threshold - min_val: 0 - max_val: 100000 - suffix: ' °/s/s' - tooltip: Acceleration threshold for saccade detection algorithm - name: spinbox_sacc_acc_thr - - type: text - label: 'Warning: PyLink is required for EyeLink functionality and is not installed. Visit http://osdoc.cogsci.nl/manual/eyetracking/eyelink/ for more information.' - name: text_eyelink_pylink_check - - type: text - label: DUMMY - name: text_pygaze_version - - type: line_edit - label: Alea API key - var: alea_api_key - name: line_edit_alea_api_key - tooltip: "The API key provided to you by Alea" - - type: checkbox - var: alea_animated_calibration - label: Animated calibration target - tooltip: Turn on child-friendly animated calibration targets - name: checkbox_alea_animated_calibration - - type: checkbox - var: eyelink_force_drift_correct - label: Force drift correction (for EyeLink 1000) - tooltip: Turn on active drift correction - name: checkbox_eyelink_force_drift_correct - - type: combobox - var: eyelink_pupil_size_mode - label: Pupil-size mode - tooltip: Determines whether pupil size is recorded as area or diameter - name: combobox_eyelink_pupil_size_mode - options: - - area - - diameter - - type: line_edit - label: SMI IP address - var: smi_ip - name: line_edit_smi_ip - tooltip: "The tracker's IP address" - - type: spinbox - var: smi_send_port - label: SMI send-port number - min_val: 0 - max_val: 100000 - name: spinbox_smi_send_port - tooltip: Port number for sending messages to the SMI tracker - - type: spinbox - var: smi_recv_port - label: SMI receive-port number - min_val: 0 - max_val: 100000 - name: spinbox_smi_recv_port - tooltip: Port number for receiving messages from the SMI tracker - - type: line_edit - label: Tobii Glasses IPV4/IPv6 address - var: tobiiglasses_address - name: line_edit_tobiiglasses_address - tooltip: "The Tobii Pro Glasses IPv4/IPv6 address" - - type: spinbox - label: Tobii Glasses UDP port number - var: tobiiglasses_udpport - min_val: 0 - max_val: 100000 - name: spinbox_tobiiglasses_udpport - tooltip: "Port number for the Tobii Pro Glasses 2 eye-tracker" - diff --git a/opensesame_plugins/pygaze_init/pygaze_init.py b/opensesame_plugins/pygaze_init/pygaze_init.py deleted file mode 100644 index a5620c8..0000000 --- a/opensesame_plugins/pygaze_init/pygaze_init.py +++ /dev/null @@ -1,334 +0,0 @@ -#-*- coding:utf-8 -*- - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with PyGaze. If not, see . -""" - -import os -import inspect -from openexp.canvas import canvas -from libopensesame import debug -from libopensesame.exceptions import osexception -from libopensesame.item import item -from libqtopensesame.items.qtautoplugin import qtautoplugin -from pygaze.eyetracker import EyeTracker -from pygaze.display import Display -import pygaze - -class pygaze_init(item): - - """ - desc: - Plug-in runtime definition. - """ - - description = u'Initialize and calibrate eye tracker' - - def __init__(self, name, experiment, string=None): - - item.__init__(self, name, experiment, string) - self.reload_pygaze() - - def reset(self): - - """ - desc: - Resets plug-in settings. - """ - - # Generic settings - self.var.tracker_type = u'Simple dummy' - self.var.calibrate = u'yes' - self.var.calbeep = u'yes' - self.var.sacc_vel_thr = 35 - self.var.sacc_acc_thr = 9500 - self.var._logfile = u'automatic' - # Alea-specific settings - self.var.alea_api_key = u'Contact Alea for an API key' - self.var.alea_animated_calibration = u'no' - # EyeLink-specific settings - self.var.eyelink_force_drift_correct = u'yes' - self.var.eyelink_pupil_size_mode = u'area' - # SMI-specific settings - self.var.smi_ip = u'127.0.0.1' - self.var.smi_send_port = 4444 - self.var.smi_recv_port = 5555 - # Tobii Pro Glasses 2 settings - self.var.tobiiglasses_address = u'192.168.71.50' - self.var.tobiiglasses_udpport = 49152 - - def close(self): - - """ - desc: - Closes the connection with the eye tracker when the experiment is - finished. - """ - - debug.msg(u'Starting PyGaze deinitialisation') - self.clock.sleep(1000) - self.experiment.pygaze_eyetracker.close() - self.experiment.pygaze_eyetracker = None - debug.msg(u'Finished PyGaze deinitialisation') - self.clock.sleep(1000) - - def draw_calibration_canvas(self, x, y): - - """ - desc: - A hook to prepare the canvas with the clibration target. - - arguments: - x: - desc: The X coordinate. - type: int - y: - desc: The Y coordinate. - type: int - """ - - dc_canvas = canvas(self.experiment) - # Coordinates are always sent in 0,0=top-left mode, so we need to - # correct for this if we're using uniform coordinates. - if self.var.uniform_coordinates == u'yes': - x -= dc_canvas._xcenter - y -= dc_canvas._ycenter - dc_canvas.fixdot(x, y, style=u'large-open') - if self.var.calbeep == 'yes': - self.beep.play() - dc_canvas.show() - - def reload_pygaze(self): - - """ - desc: - Reloads pygaze modules to get a clean start. This is necessary, - because otherwise PyGaze will try to use the old experiment instance - when the experiment is executed twice. Explicitly reloading all - OpenSesame-related modules will fix this. - """ - - from pygaze import settings - settings.osexperiment = self.experiment - settings.DISPTYPE = u'opensesame' - settings.DISPSIZE = self.resolution() - settings.BGC = self.var.background - settings.FGC = self.var.foreground - settings.ALEAKEY = self.var.alea_api_key - - if self.var.calbeep == u'yes': - settings.EYELINKCALBEEP = True - else: - settings.EYELINKCALBEEP = False - - if self.var.alea_animated_calibration == u'yes': - settings.ALEAANIMATEDCALIBRATION = True - else: - settings.ALEAANIMATEDCALIBRATION = False - - def run(self): - - """ - desc: - The run phase of the plug-in goes here. - """ - - if hasattr(self.experiment, u'pygaze_eyetracker'): - raise osexception( - u'You should have only one instance of `pygaze_init` in your experiment') - self.set_item_onset() - # Determine the tracker type and perform certain tracker-specific - # operations. - kwdict = {} - if self.var.tracker_type == u'Simple dummy': - tracker_type = u'dumbdummy' - elif self.var.tracker_type == u'Advanced dummy (mouse simulation)': - tracker_type = u'dummy' - elif self.var.tracker_type == u'EyeLink': - tracker_type = u'eyelink' - kwdict[u'eyelink_force_drift_correct'] = \ - self.var.eyelink_force_drift_correct == u'yes' - kwdict[u'pupil_size_mode'] = self.var.eyelink_pupil_size_mode - elif self.var.tracker_type == u'EyeLogic': - tracker_type = u'eyelogic' - elif self.var.tracker_type == u'SMI': - tracker_type = u'smi' - kwdict[u'ip'] = self.var.smi_ip - kwdict[u'sendport'] = self.var.smi_send_port - kwdict[u'receiveport'] = self.var.smi_recv_port - elif self.var.tracker_type == u'EyeTribe': - tracker_type = u'eyetribe' - elif self.var.tracker_type == u'OpenGaze': - tracker_type = u'opengaze' - elif self.var.tracker_type == u'Alea': - tracker_type = u'alea' - kwdict[u'alea_key'] = self.var.alea_api_key - kwdict[u'animated_calibration'] = \ - self.var.alea_animated_calibration == u'yes' - elif self.var.tracker_type == u'Tobii': - tracker_type = u'tobii' - elif self.var.tracker_type == u'Tobii-legacy': - tracker_type = u'tobii-legacy' - elif self.var.tracker_type == u'Tobii Pro Glasses 2': - tracker_type = u'tobiiglasses' - kwdict[u'address'] = self.var.tobiiglasses_address - kwdict[u'udpport'] = self.var.tobiiglasses_udpport - else: - raise osexception( - u'Unknown tracker type: %s' % self.var.tracker_type) - # Determine logfile - if self.var._logfile == u'automatic': - logfile = os.path.splitext(self.var.logfile)[0] - if tracker_type == u'eyelink': - # Automatically shorten filenames like 'subject-0', because - # these are too long. This avoids having to rename logfiles - # all the time. - basename = os.path.basename(logfile) - dirname = os.path.dirname(logfile) - if len(basename) > 8 and basename.startswith(u'subject-'): - basename = u'sub_' + basename[8:] - logfile = os.path.join(dirname, basename) - print(u'Attention: EyeLink logfile renamed to %s.edf' \ - % logfile) - elif basename == u'defaultlog': - logfile = u'default' - print(u'Attention: EyeLink logfile renamed to %s.edf' \ - % logfile) - logfile = logfile + u'.edf' - kwdict[u'data_file'] = logfile - else: - logfile = self.var._logfile - # Register the logfile with OpenSesame - self.experiment.data_files.append(logfile) - # Determine event detection. Currently, only the EyeLink has native - # event detection. - if tracker_type == u'eyelink': - event_detection = u'native' - else: - event_detection = u'pygaze' - # Initialize pygaze and the eye-tracker object - self.experiment.pygaze_display = Display(u'opensesame') - self.experiment.pygaze_eyetracker = EyeTracker( - self.experiment.pygaze_display, - trackertype=tracker_type, - eventdetection=event_detection, - saccade_velocity_threshold=self.var.sacc_vel_thr, - saccade_acceleration_threshold=self.var.sacc_acc_thr, - logfile=logfile, - **kwdict) - if self.var.calbeep == u'yes': - from openexp.synth import synth - self.beep = synth(self.experiment) - self.experiment.pygaze_eyetracker.set_draw_calibration_target_func( - self.draw_calibration_canvas) - self.experiment.pygaze_eyetracker.set_draw_drift_correction_target_func( - self.draw_calibration_canvas) - self.experiment.cleanup_functions.append(self.close) - if self.var.calibrate == u'yes': - self.experiment.pygaze_eyetracker.calibrate() - self.python_workspace[u'eyetracker'] = self.experiment.pygaze_eyetracker - -class qtpygaze_init(pygaze_init, qtautoplugin): - - """ - desc: - Plug-in GUI definition. - """ - - def __init__(self, name, experiment, script=None): - - """ - Constructor. - - Arguments: - name -- The name of the plug-in. - experiment -- The experiment object. - - Keyword arguments: - script -- A definition script. (default=None) - """ - - pygaze_init.__init__(self, name, experiment, script) - qtautoplugin.__init__(self, __file__) - - def init_edit_widget(self): - - qtautoplugin.init_edit_widget(self) - self.custom_interactions() - self.text_pygaze_version.setText( - u'PyGaze version %s' % pygaze.version) - - def apply_edit_changes(self): - - """ - desc: - Applies the controls. - """ - - if not qtautoplugin.apply_edit_changes(self) or self.lock: - return False - self.custom_interactions() - - def edit_widget(self): - - """ - Refreshes the controls. - - Returns: - The QWidget containing the controls - """ - - if self.lock: - return - self.lock = True - w = qtautoplugin.edit_widget(self) - self.custom_interactions() - self.lock = False - return w - - def custom_interactions(self): - - """ - desc: - Activates the relevant controls for each tracker. - """ - - alea = self.var.tracker_type == u'Alea' - self.line_edit_alea_api_key.setEnabled(alea) - self.checkbox_alea_animated_calibration.setEnabled(alea) - smi = self.var.tracker_type == u'SMI' - self.line_edit_smi_ip.setEnabled(smi) - self.spinbox_smi_send_port.setEnabled(smi) - self.spinbox_smi_recv_port.setEnabled(smi) - eyelink = self.var.tracker_type == u'EyeLink' - self.checkbox_eyelink_force_drift_correct.setEnabled(eyelink) - self.combobox_eyelink_pupil_size_mode.setEnabled(eyelink) - self.spinbox_sacc_acc_thr.setDisabled(eyelink) - self.spinbox_sacc_vel_thr.setDisabled(eyelink) - tobiiglasses = self.var.tracker_type == u'Tobii Pro Glasses 2' - self.line_edit_tobiiglasses_address.setEnabled(tobiiglasses) - self.spinbox_tobiiglasses_udpport.setEnabled(tobiiglasses) - if eyelink: - try: - import pylink - except: - pylink = None - if pylink == None: - self.text_eyelink_pylink_check.show() - else: - self.text_eyelink_pylink_check.hide() - else: - self.text_eyelink_pylink_check.hide() diff --git a/opensesame_plugins/pygaze_log/info.yaml b/opensesame_plugins/pygaze_log/info.yaml deleted file mode 100644 index a1ae487..0000000 --- a/opensesame_plugins/pygaze_log/info.yaml +++ /dev/null @@ -1,21 +0,0 @@ -category: PyGaze -priority: 10 -icon: os-pygaze_log -help: manual/eyetracking/pygaze -controls: - - type: spinbox - var: throttle - label: Pause between messages - min_val: 0 - max_val: 1000 - suffix: ' ms' - tooltip: A pause between messages to avoid overloading the eye tracker - - type: checkbox - var: auto_log - label: Automatically log all variables - tooltip: Automatically send all experimental variables to the eye tracker - - type: editor - var: msg - label: Log message - syntax: false - tooltip: The message to write to the eye tracker diff --git a/opensesame_plugins/pygaze_log/pygaze_log.py b/opensesame_plugins/pygaze_log/pygaze_log.py deleted file mode 100644 index ee02c6f..0000000 --- a/opensesame_plugins/pygaze_log/pygaze_log.py +++ /dev/null @@ -1,60 +0,0 @@ -#-*- coding:utf-8 -*- - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with PyGaze. If not, see . -""" - -from libopensesame.item import item -from libqtopensesame.items.qtautoplugin import qtautoplugin -from pygaze.display import Display - -class pygaze_log(item): - - """Plug-in runtime definition.""" - - description = u'Writes information to the eye-tracker logfile' - - def reset(self): - - """ - desc: - Resets plug-in settings. - """ - - self.var.msg = u'' - self.var.auto_log = u'no' - self.var.throttle = 2 - - def run(self): - - """The run phase of the plug-in goes here.""" - - self.set_item_onset() - for msg in self.var.msg.split(u'\n'): - self.experiment.pygaze_eyetracker.log(self.syntax.eval_text(msg)) - self.clock.sleep(self.var.throttle) - if self.var.auto_log == u'yes': - for logvar, info in self.experiment.var.inspect().items(): - self.experiment.pygaze_eyetracker.log_var(logvar, - info[u'value']) - self.clock.sleep(self.var.throttle) - -class qtpygaze_log(pygaze_log, qtautoplugin): - - def __init__(self, name, experiment, script=None): - - pygaze_log.__init__(self, name, experiment, script) - qtautoplugin.__init__(self, __file__) diff --git a/opensesame_plugins/pygaze_start_recording/info.yaml b/opensesame_plugins/pygaze_start_recording/info.yaml deleted file mode 100644 index 92073b3..0000000 --- a/opensesame_plugins/pygaze_start_recording/info.yaml +++ /dev/null @@ -1,9 +0,0 @@ -category: PyGaze -priority: 10 -help: manual/eyetracking/pygaze -icon: os-pygaze_start_recording -controls: - - type: line_edit - var: status_msg - label: Status message - tooltip: A text to use as status message diff --git a/opensesame_plugins/pygaze_start_recording/pygaze_start_recording.py b/opensesame_plugins/pygaze_start_recording/pygaze_start_recording.py deleted file mode 100644 index 5b23992..0000000 --- a/opensesame_plugins/pygaze_start_recording/pygaze_start_recording.py +++ /dev/null @@ -1,53 +0,0 @@ -#-*- coding:utf-8 -*- - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with PyGaze. If not, see . -""" - -from libopensesame.item import item -from libqtopensesame.items.qtautoplugin import qtautoplugin -from pygaze.display import Display - -class pygaze_start_recording(item): - - """Plug-in runtime definition.""" - - description = u'Puts the eye tracker into recording mode' - - def reset(self): - - """ - desc: - Resets plug-in settings. - """ - - self.var.status_msg = u'start_trial' - - def run(self): - - """The run phase of the plug-in goes here.""" - - self.set_item_onset() - self.experiment.pygaze_eyetracker.start_recording() - self.experiment.pygaze_eyetracker.status_msg(self.var.status_msg) - self.experiment.pygaze_eyetracker.log(self.var.status_msg) - -class qtpygaze_start_recording(pygaze_start_recording, qtautoplugin): - - def __init__(self, name, experiment, script=None): - - pygaze_start_recording.__init__(self, name, experiment, script) - qtautoplugin.__init__(self, __file__) diff --git a/opensesame_plugins/pygaze_stop_recording/info.yaml b/opensesame_plugins/pygaze_stop_recording/info.yaml deleted file mode 100644 index f19eb27..0000000 --- a/opensesame_plugins/pygaze_stop_recording/info.yaml +++ /dev/null @@ -1,9 +0,0 @@ -category: PyGaze -priority: 10 -help: manual/eyetracking/pygaze -icon: os-pygaze_stop_recording -controls: - - type: line_edit - var: status_msg - label: Status message - tooltip: A text to use as status message diff --git a/opensesame_plugins/pygaze_stop_recording/pygaze_stop_recording.py b/opensesame_plugins/pygaze_stop_recording/pygaze_stop_recording.py deleted file mode 100644 index 32d2ec7..0000000 --- a/opensesame_plugins/pygaze_stop_recording/pygaze_stop_recording.py +++ /dev/null @@ -1,59 +0,0 @@ -#-*- coding:utf-8 -*- - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with PyGaze. If not, see . -""" - -from libopensesame.item import item -from libqtopensesame.items.qtautoplugin import qtautoplugin -from pygaze.display import Display - -class pygaze_stop_recording(item): - - """Plug-in runtime definition.""" - - description = u'Stops recording of eye tracking data' - - def reset(self): - - """ - desc: - Resets plug-in settings. - """ - - self.var.status_msg = u'stop_trial' - - def prepare(self): - - """The preparation phase of the plug-in goes here.""" - - item.prepare(self) - - def run(self): - - """The run phase of the plug-in goes here.""" - - self.set_item_onset() - self.experiment.pygaze_eyetracker.status_msg(self.var.status_msg) - self.experiment.pygaze_eyetracker.log(self.var.status_msg) - self.experiment.pygaze_eyetracker.stop_recording() - -class qtpygaze_stop_recording(pygaze_stop_recording, qtautoplugin): - - def __init__(self, name, experiment, script=None): - - pygaze_stop_recording.__init__(self, name, experiment, script) - qtautoplugin.__init__(self, __file__) diff --git a/opensesame_plugins/pygaze_wait/info.yaml b/opensesame_plugins/pygaze_wait/info.yaml deleted file mode 100644 index bc8db80..0000000 --- a/opensesame_plugins/pygaze_wait/info.yaml +++ /dev/null @@ -1,16 +0,0 @@ -category: PyGaze -priority: 10 -help: manual/eyetracking/pygaze -icon: os-pygaze_wait -controls: - - type: combobox - var: event - label: Event - options: - - Saccade start - - Saccade end - - Fixation start - - Fixation end - - Blink start - - Blink end - tooltip: An eye-tracker event to wait for diff --git a/opensesame_plugins/pygaze_wait/pygaze_wait.py b/opensesame_plugins/pygaze_wait/pygaze_wait.py deleted file mode 100644 index b31602d..0000000 --- a/opensesame_plugins/pygaze_wait/pygaze_wait.py +++ /dev/null @@ -1,78 +0,0 @@ -#-*- coding:utf-8 -*- - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with PyGaze. If not, see . -""" - -from libopensesame.item import item -from libopensesame.exceptions import osexception -from libqtopensesame.items.qtautoplugin import qtautoplugin -from pygaze.display import Display - -class pygaze_wait(item): - - """Plug-in runtime definition.""" - - description = u'Waits for an eye-tracker event' - - def reset(self): - - """ - desc: - Resets plug-in settings. - """ - - self.var.event = u'Saccade start' - - def prepare(self): - - """The preparation phase of the plug-in goes here.""" - - item.prepare(self) - if self.var.event == u'Saccade start': - self.wait_func = self.experiment.pygaze_eyetracker. \ - wait_for_saccade_start - elif self.var.event == u'Saccade end': - self.wait_func = self.experiment.pygaze_eyetracker. \ - wait_for_saccade_end - elif self.var.event == u'Fixation start': - self.wait_func = self.experiment.pygaze_eyetracker. \ - wait_for_fixation_start - elif self.var.event == u'Fixation end': - self.wait_func = self.experiment.pygaze_eyetracker. \ - wait_for_fixation_end - elif self.var.event == u'Blink start': - self.wait_func = self.experiment.pygaze_eyetracker. \ - wait_for_blink_start - elif self.var.event == u'Blink end': - self.wait_func = self.experiment.pygaze_eyetracker. \ - wait_for_blink_start - else: - raise osexception(u'Unknown event: %s' % self.var.event) - - def run(self): - - """The run phase of the plug-in goes here.""" - - self.wait_func() - self.set_item_onset() - -class qtpygaze_wait(pygaze_wait, qtautoplugin): - - def __init__(self, name, experiment, script=None): - - pygaze_wait.__init__(self, name, experiment, script) - qtautoplugin.__init__(self, __file__) diff --git a/pygaze/_mouse/osmouse.py b/pygaze/_mouse/osmouse.py index 182d7dc..71457c7 100644 --- a/pygaze/_mouse/osmouse.py +++ b/pygaze/_mouse/osmouse.py @@ -53,8 +53,6 @@ def __init__(self, mousebuttonlist=settings.MOUSEBUTTONLIST, pass self.experiment = settings.osexperiment - self.uniform_coordinates = \ - self.experiment.var.uniform_coordinates == "yes" self.mouse = mouse(self.experiment, buttonlist=mousebuttonlist, timeout=timeout) @@ -62,7 +60,7 @@ def _from_pos(self, pos): """Convert OpenSesame coordinates to PyGaze coordinates.""" - if pos is None or not self.uniform_coordinates: + if pos is None: return pos return pos[0]+self.mouse._xcenter, pos[1]+self.mouse._ycenter @@ -70,7 +68,7 @@ def _to_pos(self, pos): """Convert PyGaze coordinates to OpenSesame coordinates.""" - if pos is None or not self.uniform_coordinates: + if pos is None: return pos return pos[0]-self.mouse._xcenter, pos[1]-self.mouse._ycenter diff --git a/pygaze/_screen/osscreen.py b/pygaze/_screen/osscreen.py index d71269b..fa6a247 100644 --- a/pygaze/_screen/osscreen.py +++ b/pygaze/_screen/osscreen.py @@ -52,8 +52,6 @@ def __init__(self, screen=None, **args): pass self.experiment = settings.osexperiment - self.uniform_coordinates = \ - self.experiment.var.uniform_coordinates == "yes" self.create(screen=screen) def _pos(self, pos): @@ -63,9 +61,8 @@ def _pos(self, pos): if pos in (None, (None, None)): return None, None x, y = pos - if self.uniform_coordinates: - x -= self.canvas._xcenter - y -= self.canvas._ycenter + x -= self.canvas._xcenter + y -= self.canvas._ycenter return x, y def create(self, screen=None): diff --git a/pygaze/_time/ostime.py b/pygaze/_time/ostime.py index 76945f4..b19acbd 100644 --- a/pygaze/_time/ostime.py +++ b/pygaze/_time/ostime.py @@ -63,18 +63,18 @@ def get_time(self): # see pygaze._time.basetime.BaseTime - return settings.osexperiment.time() + return settings.osexperiment.clock.time() def pause(self, pausetime): # see pygaze._time.basetime.BaseTime - return settings.osexperiment.sleep(pausetime) + return settings.osexperiment.clock.sleep(pausetime) def expend(self): # see pygaze._time.basetime.BaseTime - return settings.osexperiment.time() + return settings.osexperiment.clock.time() From 33d3f0af0c99df75615e54f698ed567fd0d591ab Mon Sep 17 00:00:00 2001 From: smathot Date: Wed, 15 Mar 2023 21:30:06 +0100 Subject: [PATCH 2/5] Refactor build scripts to use pyproject.toml and poetry --- COPYING.txt => COPYING | 0 MANIFEST.in | 5 -- pyproject.toml | 23 ++++++++ README.md => readme.md | 2 +- setup.cfg | 2 - setup.py | 120 ----------------------------------------- stdeb.cfg | 6 --- 7 files changed, 24 insertions(+), 134 deletions(-) rename COPYING.txt => COPYING (100%) delete mode 100644 MANIFEST.in create mode 100644 pyproject.toml rename README.md => readme.md (94%) delete mode 100644 setup.cfg delete mode 100755 setup.py delete mode 100644 stdeb.cfg diff --git a/COPYING.txt b/COPYING similarity index 100% rename from COPYING.txt rename to COPYING diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 54cbe98..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -recursive-include opensesame_plugins * -graft pygaze/_eyetracker/alea -global-exclude *.pyc -global-exclude *.pyo - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fbaf85e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[tool.poetry] +name = "pygaze" +version = "0.8.0a1" +description = "A Python library for eye tracking" +authors = [ + "Edwin Dalmaijer ", + "Sebastiaan Mathôt " +] +readme = "readme.md" +license = "COPYING" +packages = [ + {include = "pygaze"}, + {include = "opensesame_plugins"} +] +homepage = "https://pygaze.org" +repository = "https://github.com/esdalmaijer/PyGaze/" + +[tool.poetry.dependencies] +python = ">= 3.7" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/README.md b/readme.md similarity index 94% rename from README.md rename to readme.md index 26b46cd..6c65e07 100644 --- a/README.md +++ b/readme.md @@ -1,6 +1,6 @@ # PyGaze - the open-source toolbox for eye tracking -*Copyright 2013 - 2022, Edwin Dalmaijer, Sebastiaan Mathôt, and contributors* +*Copyright 2013 - 2023, Edwin Dalmaijer, Sebastiaan Mathôt, and contributors* For more information, see: diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3c6e79c..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal=1 diff --git a/setup.py b/setup.py deleted file mode 100755 index ea7848a..0000000 --- a/setup.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python - -""" -This file is part of PyGaze. - -PyGaze is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PyGaze is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with qnotero. If not, see . -""" - -import os -import sys -import glob -import pygaze -from setuptools import setup - -print("Running setup for PyGaze version {}".format(pygaze.__version__)) - - -def files(path): - - return [ - fname - for fname in glob.glob(path) if os.path.isfile(fname) - and not fname.endswith('.pyc') - ] - - -def data_files(): - - """ - desc: - The OpenSesame plug-ins are installed as additional data. Under Windows, - there is no special folder to put these plug-ins in, so we skip this - step. - - returns: - desc: A list of data files to include. - type: list - """ - - return [ - ("share/opensesame_plugins/pygaze_init/resources/locale", - files("opensesame_plugins/pygaze_init/resources/locale/*")), - ("share/opensesame_plugins/pygaze_init", - files("opensesame_plugins/pygaze_init/*")), - ("share/opensesame_plugins/pygaze_drift_correct", - files("opensesame_plugins/pygaze_drift_correct/*")), - ("share/opensesame_plugins/pygaze_log", - files("opensesame_plugins/pygaze_log/*")), - ("share/opensesame_plugins/pygaze_start_recording", - files("opensesame_plugins/pygaze_start_recording/*")), - ("share/opensesame_plugins/pygaze_stop_recording", - files("opensesame_plugins/pygaze_stop_recording/*")), - ("share/opensesame_plugins/pygaze_wait", - files("opensesame_plugins/pygaze_wait/*")) - ] - - -def get_readme(): - - if os.path.exists('README.md'): - with open('README.md') as fd: - return fd.read() - return 'No readme information' - - -setup( - name='pygaze' if 'bdist_deb' in sys.argv else u'python-pygaze', - python_requires=">=3", - version=pygaze.__version__, - description="A Python library for eye tracking", - long_description=get_readme(), - long_description_content_type='text/markdown', - author="Edwin Dalmaijer", - author_email="edwin.dalmaijer@gmail.com", - url="http://www.pygaze.org/", - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Science/Research', - 'Topic :: Scientific/Engineering', - 'Environment :: MacOS X', - 'Environment :: Win32 (MS Windows)', - 'Environment :: X11 Applications', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - 'Programming Language :: Python :: 3', - ], - include_package_data=True, - package_data={ - "pygaze._eyetracker.alea": ["*.dll"], - "pygaze._eyetracker.eyelogic": ["*.dll"] - }, - packages=[ - "pygaze", - "pygaze._display", - "pygaze._eyetracker", - "pygaze._eyetracker.alea", - "pygaze._eyetracker.eyelogic", - "pygaze._eyetracker.tobiiglasses", - "pygaze._joystick", - "pygaze._keyboard", - "pygaze._logfile", - "pygaze._misc", - "pygaze._mouse", - "pygaze._screen", - "pygaze._sound", - "pygaze._time", - "pygaze.plugins", - ], - data_files=data_files() -) diff --git a/stdeb.cfg b/stdeb.cfg deleted file mode 100644 index 6ca90a8..0000000 --- a/stdeb.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[DEFAULT] -Source=pygaze -Package=pygaze -Debian-version=1 -Suite=focal -Copyright-File=COPYING.txt From 27354aa1f341bcbd0a4a711fb4b9191186726bd0 Mon Sep 17 00:00:00 2001 From: smathot Date: Tue, 10 Oct 2023 15:56:16 +0200 Subject: [PATCH 3/5] Bump to 0.7.7 --- pygaze/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygaze/__init__.py b/pygaze/__init__.py index 6691408..625c602 100644 --- a/pygaze/__init__.py +++ b/pygaze/__init__.py @@ -25,7 +25,7 @@ import sys import os -__version__ = version = "0.7.6" +__version__ = version = "0.7.7" strict_version = StrictVersion(__version__) # The version without the prerelease (if any): e.g. 3.0.0 main_version = ".".join([str(i) for i in strict_version.version]) From f1a964d2e3f1133ed6d5f9f56b849be5e7a9f874 Mon Sep 17 00:00:00 2001 From: smathot Date: Tue, 10 Oct 2023 16:00:20 +0200 Subject: [PATCH 4/5] Bump to 0.8.7 --- pygaze/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygaze/__init__.py b/pygaze/__init__.py index 625c602..84f5ff1 100644 --- a/pygaze/__init__.py +++ b/pygaze/__init__.py @@ -25,7 +25,7 @@ import sys import os -__version__ = version = "0.7.7" +__version__ = version = "0.8.7" strict_version = StrictVersion(__version__) # The version without the prerelease (if any): e.g. 3.0.0 main_version = ".".join([str(i) for i in strict_version.version]) From 978d4d87934390a0f67c1e608643fcabf640e2c4 Mon Sep 17 00:00:00 2001 From: smathot Date: Tue, 10 Oct 2023 18:01:08 +0200 Subject: [PATCH 5/5] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fbaf85e..2c8e56f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pygaze" -version = "0.8.0a1" +version = "0.8.7" description = "A Python library for eye tracking" authors = [ "Edwin Dalmaijer ",