1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
|
"""
A touchscreen button box.
This module contains a class implementing a touchscreen button box.
"""
__author__ = 'Florian Krause <florian@expyriment.org>, \
Oliver Lindemann <oliver@expyriment.org>'
__version__ = '0.7.0'
__revision__ = '55a4e7e'
__date__ = 'Wed Mar 26 14:33:37 2014 +0100'
import expyriment
from expyriment.misc._timer import get_time
from _input_output import Input
class TouchScreenButtonBox(Input):
"""A class implementing a TouchScreenButtonBox."""
def __init__(self, button_fields, stimuli=[], background_stimulus=None):
"""Initialize a touchscreen button box.
Parameters
----------
button_fields : visual Expyriment stimulus or list of stimuli
The button fields defines the area on which a click action will be
registered.
stimuli : visual Expyriment stimulus or list of stimuli, optional
Additional visual stimuli that will be presented together with the
button fields. Stimuli are plotted on top of the button_fields.
background_stimulus : visual Expyriment stimulus, optional
The background stimulus on which the touschscreen button fields
are presented. Importantly, background_stimulus has to have
size of the screen.
Notes
-----
Every visual Expyriment stimulus can serve as a touchscreen button
field.
If the TouchScreenButtonBox is presented, it can be checked for events
using the methods 'check' and 'wait'.
"""
Input.__init__(self)
if type(button_fields) is not list:
button_fields = [button_fields]
if type(stimuli) is not list:
stimuli = [stimuli]
self._mouse = expyriment._active_exp.mouse
self._last_touch_position = None
self._canvas = None
self._button_fields = []
self._stimuli = []
self.background_stimulus = background_stimulus
map(self.add_button_field, button_fields)
map(self.add_stimulus, stimuli)
def add_button_field(self, button_field):
"""Add a touchscreen button fields.
Parameters
----------
button_field : visual Expyriment stimulus
"""
if not isinstance(button_field, expyriment.stimuli._visual.Visual):
raise TypeError("Button field has to a visual Expyriment stimulus")
self._button_fields.append(button_field)
self._canvas = None
def add_stimulus(self, stimulus):
"""Add additional stimulus
The added stimulus will be presented together with the button fields.
Parameters
----------
stimulus : visual Expyriment stimulus
"""
if not isinstance(stimulus, expyriment.stimuli._visual.Visual):
raise TypeError("Additional stimuli has to a visual Expyriment stimulus")
self._stimuli.append(stimulus)
self._canvas = None
@property
def last_touch_position(self):
"""getter for the last touch position (x,y)"""
return self._last_touch_position
@property
def button_field(self):
"""getter of button fields (list of visual Expyriment stimuli)"""
return self._button_fields
@property
def stimuli(self):
"""getter of additional stimuli (list of visual Expyriment stimuli)"""
return self._stimuli
@property
def background_stimulus(self):
"""Getter of background stimulus.
Background stimulus, on which the button fields and the additional
stimuli will be presented. (visual Expyriment stimuli)
"""
return self._background_stimulus
@background_stimulus.setter
def background_stimulus(self, stimulus):
"""Setter background stimulus"""
if stimulus is None:
self._background_stimulus = expyriment.stimuli.BlankScreen()
elif not isinstance(stimulus, expyriment.stimuli._visual.Visual):
raise TypeError("Background stimulus has to be be a " +
"visual Expyriment stimulus")
else:
self._background_stimulus = stimulus
self._canvas = None
def clear_event_buffer(self):
"""Clear the event buffer of the touchscreen/mouse input device."""
if self._mouse is not None:
self._mouse.clear()
def create(self):
"""Create the touchscreen buttonbox.
Prepares the button fields and additional stimuli for fast
presentation.
"""
self._canvas = self._background_stimulus.copy()
if len(self._button_fields) < 1:
raise RuntimeError("No button field defined!")
map(lambda x:x.plot(self._canvas), self._button_fields)
map(lambda x:x.plot(self._canvas), self._stimuli)
self._canvas.preload()
def destroy(self):
if self._canvas is not None:
self._canvas.unload()
self._canvas = None
def show(self):
"""Present touchscreen buttons."""
if self._canvas is None:
self.create()
self._canvas.present()
def check(self, button_fields=None, check_for_control_keys=True):
"""Check if a button field is clicked.
Parameters
----------
button_fields : Expyriment stimulus or list of stimuli, optional
The button fields that will be checked for.
check_for_control_keys : bool, optional
checks if control key has been pressed (default=True)
Returns
-------
pressed_button_field : Expyriment stimulus or list of stimuli, optional
The button fields that will be checked for.
touch_time : integer
The time when the button was touched. Function might return delayed,
because button field comparison (after touch) takes time. The
return time is most accurate.
Notes
-----
Don't forget to show the TouchScreenButtonBox.
"""
if button_fields is not None and type(button_fields) is not list:
button_fields = [button_fields]
if check_for_control_keys:
expyriment.io.Keyboard.process_control_keys()
pressed_button_field = None
touch_time = None
if self._mouse.get_last_button_down_event() is not None:
touch_time = get_time()
self._last_touch_position = self._mouse.position
pressed_button_field = self._get_button_field(self._last_touch_position,
button_fields)
if self._logging and pressed_button_field is not None:
expyriment._active_exp._event_file_log(
"{0},received, button press,check".format(
self.__class__.__name__))
return pressed_button_field, touch_time
def _get_button_field(self, position, button_fields):
""" helper function return the button field of the position"""
if button_fields is None:
button_fields = self._button_fields
for bf in button_fields:
if bf.overlapping_with_position(position):
return bf
return None
def wait(self, duration=None, button_fields=None,
check_for_control_keys=True):
"""Wait for a touchscreen button box click.
Parameters
----------
button_fields : Expyriment stimulus or list of stimuli, optional
The button fields that will be checked for.
duration : int, optional
maximal time to wait in ms
Returns
-------
pressed_button_field : Expyriment stimulus or None
the button field defined by a stimulus that has been pressed
rt : int
reaction time
Notes
-----
Don't forget to show the TouchScreenButtonBox.
"""
start = get_time()
self.clear_event_buffer()
while True:
expyriment._active_exp._execute_wait_callback()
pressed_button_field, touch_time = self.check(button_fields,
check_for_control_keys)
if pressed_button_field is not None:
rt = int((touch_time - start) * 1000)
break
elif (duration is not None and rt>= duration):
pressed_button_field, rt = None, None
break
return pressed_button_field, rt
|