File: _touchscreenbuttonbox.py

package info (click to toggle)
python-expyriment 0.7.0%2Bgit34-g55a4e7e-3.2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,504 kB
  • ctags: 2,094
  • sloc: python: 12,766; makefile: 150
file content (254 lines) | stat: -rw-r--r-- 8,519 bytes parent folder | download | duplicates (2)
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