pyForceDAQ 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. pyforcedaq/__init__.py +43 -0
  2. pyforcedaq/__main__.py +42 -0
  3. pyforcedaq/_lib/__init__.py +1 -0
  4. pyforcedaq/_lib/lsl.py +56 -0
  5. pyforcedaq/_lib/misc.py +126 -0
  6. pyforcedaq/_lib/polling_time_profile.py +52 -0
  7. pyforcedaq/_lib/process_priority_manager.py +148 -0
  8. pyforcedaq/_lib/timer.py +45 -0
  9. pyforcedaq/_lib/types.py +400 -0
  10. pyforcedaq/_lib/udp_connection.py +326 -0
  11. pyforcedaq/daq/__init__.py +19 -0
  12. pyforcedaq/daq/_daq_read_Analog_pydaqmx.py +114 -0
  13. pyforcedaq/daq/_daq_read_analog_nidaqmx.py +84 -0
  14. pyforcedaq/daq/_mock_sensor.py +80 -0
  15. pyforcedaq/daq/_pyATIDAQ.py +306 -0
  16. pyforcedaq/daq/config.py +13 -0
  17. pyforcedaq/extras/__init__.py +0 -0
  18. pyforcedaq/extras/convert.py +275 -0
  19. pyforcedaq/extras/expyriment_daq_control.py +246 -0
  20. pyforcedaq/extras/opensesame_daq_control.py +280 -0
  21. pyforcedaq/extras/read_force_data.py +89 -0
  22. pyforcedaq/extras/remote_control.py +93 -0
  23. pyforcedaq/force/__init__.py +13 -0
  24. pyforcedaq/force/_log.py +18 -0
  25. pyforcedaq/force/data_recorder.py +400 -0
  26. pyforcedaq/force/sensor.py +200 -0
  27. pyforcedaq/force/sensor_process.py +251 -0
  28. pyforcedaq/gui/__init__.py +6 -0
  29. pyforcedaq/gui/_gui_status.py +306 -0
  30. pyforcedaq/gui/_layout.py +104 -0
  31. pyforcedaq/gui/_level_indicator.py +59 -0
  32. pyforcedaq/gui/_pg_surface.py +100 -0
  33. pyforcedaq/gui/_plotter.py +234 -0
  34. pyforcedaq/gui/_run.py +522 -0
  35. pyforcedaq/gui/_scaling.py +71 -0
  36. pyforcedaq/gui/_settings.py +98 -0
  37. pyforcedaq/gui/forceDAQ_logo.png +0 -0
  38. pyforcedaq/gui/launcher.py +249 -0
  39. pyforcedaq-2.0.0.dist-info/METADATA +15 -0
  40. pyforcedaq-2.0.0.dist-info/RECORD +42 -0
  41. pyforcedaq-2.0.0.dist-info/WHEEL +4 -0
  42. pyforcedaq-2.0.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,59 @@
1
+ import numpy as np
2
+ from expyriment.stimuli import Canvas, Rectangle, TextLine
3
+ from expyriment.misc import constants
4
+
5
+ def level_indicator(value, text, scaling, width=20,
6
+ text_size=14, text_gap=20, position=(0,0), thresholds = None,
7
+ colour=constants.C_EXPYRIMENT_ORANGE):
8
+ """make an level indicator in for of an Expyriment stimulus
9
+
10
+ text_gap: gap between indicator and text
11
+ scaling: Scaling object
12
+
13
+ Returns
14
+ --------
15
+ expyriment.Canvas
16
+
17
+ """
18
+
19
+ value = scaling.trim(value)
20
+
21
+ # indicator
22
+ height = scaling.pixel_max - scaling.pixel_min
23
+ indicator = Canvas(size=(width + 2, height + 2),
24
+ colour=(30, 30, 30))
25
+
26
+ zero = scaling.data2pixel(0)
27
+ px_bar_height = scaling.data2pixel(value) - zero
28
+ bar = Rectangle(size=(width, abs(px_bar_height)),
29
+ position=(0, zero + int((px_bar_height + 1) / 2)),
30
+ colour=colour)
31
+ bar.plot(indicator)
32
+
33
+ # levels & horizontal lines
34
+ try:
35
+ px_horizontal_lines = scaling.data2pixel(values=np.array(thresholds.thresholds))
36
+ except:
37
+ px_horizontal_lines = None
38
+ if px_horizontal_lines is not None:
39
+ for px in px_horizontal_lines:
40
+ level = Rectangle(size=(width+6, 2),
41
+ position=(0, px),
42
+ colour=constants.C_WHITE)
43
+ level.plot(indicator)
44
+
45
+
46
+
47
+
48
+ # text labels
49
+ txt = TextLine(text=text, text_size=text_size,
50
+ position=(0, -1 * (int(height / 2.0) + text_gap)),
51
+ text_colour=constants.C_YELLOW)
52
+
53
+ # make return canvas
54
+ w = max(txt.surface_size[0], indicator.size[0])
55
+ h = height + 2 * (txt.surface_size[1]) + text_gap
56
+ rtn = Canvas(size=(w, h), colour=(0, 0, 0), position=position)
57
+ indicator.plot(rtn)
58
+ txt.plot(rtn)
59
+ return rtn
@@ -0,0 +1,100 @@
1
+ import pygame
2
+ from expyriment.stimuli import Canvas
3
+
4
+ class PGSurface(Canvas):
5
+ """PyGame Surface: Expyriment Stimulus for direct Pygame operations and
6
+ PixelArrays
7
+
8
+ In contrast to other Expyriment stimuli the class does not generate temporary
9
+ surfaces.
10
+ """
11
+
12
+ def __init__(self, size, position=None, colour=None):
13
+ Canvas.__init__(self, size, position, colour)
14
+ self._px_array = None
15
+
16
+ @property
17
+ def surface(self):
18
+ """DOC"""
19
+ if not self.has_surface:
20
+ ok = self._set_surface(self._get_surface()) # create surface
21
+ if not ok:
22
+ raise RuntimeError("Cannot call surface on compressed stimuli!")
23
+ return self._surface
24
+
25
+ @property
26
+ def pixel_array(self):
27
+ """DOC"""
28
+ if self._px_array is None:
29
+ self._px_array = pygame.PixelArray(self.surface)
30
+ return self._px_array
31
+
32
+ @pixel_array.setter
33
+ def pixel_array(self, value):
34
+ if self._px_array is None:
35
+ self._px_array = pygame.PixelArray(self.surface)
36
+ self._px_array = value
37
+
38
+ def unlock_pixel_array(self):
39
+ """DOC"""
40
+ self._px_array = None
41
+
42
+ def preload(self, inhibit_ogl_compress=False):
43
+ self.unlock_pixel_array()
44
+ return Canvas.preload(self, inhibit_ogl_compress)
45
+
46
+ def compress(self):
47
+ self.unlock_pixel_array()
48
+ return Canvas.compress(self)
49
+
50
+ def decompress(self):
51
+ self.unlock_pixel_array()
52
+ return Canvas.decompress(self)
53
+
54
+ def plot(self, stimulus):
55
+ self.unlock_pixel_array()
56
+ return Canvas.plot(self, stimulus)
57
+
58
+ def clear_surface(self):
59
+ self.unlock_pixel_array()
60
+ return Canvas.clear_surface(self)
61
+
62
+ def copy(self):
63
+ self.unlock_pixel_array()
64
+ return Canvas.copy(self)
65
+
66
+ def unload(self, keep_surface=False):
67
+ if not keep_surface:
68
+ self.unlock_pixel_array()
69
+ return Canvas.unload(self, keep_surface)
70
+
71
+ def rotate(self, degree, filter=True): # Exypriment >=.9
72
+ self.unlock_pixel_array()
73
+ return Canvas.rotate(self, degree, filter)
74
+
75
+ def scale(self, factors):
76
+ self.unlock_pixel_array()
77
+ return Canvas.scale(self, factors)
78
+
79
+ # expyriment 0.8.0
80
+ # def scale_to_fullscreen(self, keep_aspect_ratio=True):
81
+ # self.unlock_pixel_array()
82
+ # return Canvas.scale_to_fullscreen(self, keep_aspect_ratio)
83
+
84
+ def flip(self, booleans):
85
+ self.unlock_pixel_array()
86
+ return Canvas.flip(self, booleans)
87
+
88
+ def blur(self, level):
89
+ self.unlock_pixel_array()
90
+ return Canvas.blur(self, level)
91
+
92
+ def scramble(self, grain_size):
93
+ self.unlock_pixel_array()
94
+ return Canvas.scramble(self, grain_size)
95
+
96
+ def add_noise(self, grain_size, percentage, colour):
97
+ self.unlock_pixel_array()
98
+ return Canvas.add_noise(self, grain_size, percentage, colour)
99
+
100
+
@@ -0,0 +1,234 @@
1
+ __version__ = "0.3"
2
+
3
+ import threading
4
+ import numpy as np
5
+ import pygame
6
+ try:
7
+ # expyriment >= 0.10
8
+ from expyriment.misc import Colour as ExpyColor
9
+ except:
10
+ # old expyriment
11
+ ExpyColor = tuple
12
+
13
+ from ._pg_surface import PGSurface
14
+
15
+ lock_expyriment = threading.Lock()
16
+ Numpy_array_type = type(np.array([]))
17
+
18
+ class Plotter(PGSurface):
19
+ """Pygame Plotter"""
20
+
21
+ def __init__(self, n_data_rows, data_row_colours,
22
+ width=600, y_range=(-100, 100),
23
+ background_colour=(180, 180, 180),
24
+ marker_colour=(200, 200, 200),
25
+ position=None,
26
+ axis_colour=None):
27
+ self._n_data_rows = n_data_rows
28
+ self._data_row_colours = check_colour_array(values=data_row_colours,
29
+ required_n_colours=n_data_rows)
30
+ self._y_range = y_range
31
+ height = self._y_range[1] - self._y_range[0]
32
+
33
+ self._background_colour = tuple(background_colour)
34
+ self._marker_colour = tuple(marker_colour)
35
+ self._horizontal_lines = None
36
+
37
+ if axis_colour is None:
38
+ self.axis_colour = background_colour
39
+ else:
40
+ self.axis_colour = axis_colour
41
+ self._previous = [None] * n_data_rows
42
+ PGSurface.__init__(self, size=(width, height),
43
+ position=position)
44
+ self.clear_area()
45
+
46
+ @property
47
+ def width(self):
48
+ return self.size[0]
49
+
50
+ @property
51
+ def height(self):
52
+ return self.size[1]
53
+
54
+ def clear_area(self):
55
+ self.pixel_array[:, :] = self._background_colour
56
+
57
+
58
+ def set_horizontal_line(self, y_values):
59
+ """y_values: array"""
60
+ try:
61
+ self._horizontal_lines = np.array(y_values, dtype=int)
62
+ except:
63
+ self._horizontal_lines = None
64
+
65
+ def write_values(self, position, values, set_marker=False,
66
+ set_point_marker=False):
67
+ """
68
+ additional points: np.array
69
+ """
70
+
71
+ if set_marker:
72
+ self.pixel_array[position, :] = self._marker_colour
73
+ else:
74
+ self.pixel_array[position, :] = self._background_colour
75
+
76
+ if set_point_marker:
77
+ self.pixel_array[position, 0:2] = self._marker_colour
78
+
79
+
80
+ if self._horizontal_lines is not None:
81
+ for c in (self._y_range[1] - self._horizontal_lines):
82
+ self.pixel_array[:, c:c+1] = self._marker_colour
83
+
84
+ for c, plot_value in enumerate(self._y_range[1] - \
85
+ np.array(values, dtype=int)):
86
+
87
+ if self._previous[c] is not None and \
88
+ plot_value >= 0 and \
89
+ self._previous[c] >= 0 and \
90
+ plot_value <= self.height and \
91
+ self._previous[c] <= self.height:
92
+ if self._previous[c] > plot_value:
93
+ self.pixel_array[position,
94
+ plot_value:self._previous[c] + 1] = \
95
+ self._data_row_colours[c]
96
+ else:
97
+ self.pixel_array[position,
98
+ self._previous[c]:plot_value + 1] = \
99
+ self._data_row_colours[c]
100
+ self._previous[c] = plot_value
101
+
102
+ def add_values(self, values, set_marker=False):
103
+ """ high level function of write values with type check and shifting to left
104
+ not used by plotter thread
105
+ """
106
+ if type(values) is not Numpy_array_type and \
107
+ not isinstance(values, tuple) and \
108
+ not isinstance(values, list):
109
+ values = [values]
110
+ if len(values) != self._n_data_rows:
111
+ raise RuntimeError('Number of data values does not match the ' +
112
+ 'defined number of data rows!')
113
+
114
+ # move plot one pixel to the left
115
+ self.pixel_array[:-1, :] = self.pixel_array[1:, :]
116
+ self.write_values(position=-1, values=values, set_marker=set_marker)
117
+
118
+
119
+ class PlotterThread(threading.Thread):
120
+ def __init__(self, n_data_rows, data_row_colours,
121
+ width=600, y_range=(-100, 100),
122
+ background_colour=(80, 80, 80),
123
+ marker_colour=(200, 200, 200),
124
+ position=None,
125
+ axis_colour=None):
126
+ super(PlotterThread, self).__init__()
127
+ self._plotter = Plotter(n_data_rows=n_data_rows,
128
+ data_row_colours=data_row_colours,
129
+ width=width, y_range=y_range,
130
+ background_colour=background_colour,
131
+ marker_colour=marker_colour,
132
+ position=position,
133
+ axis_colour=axis_colour)
134
+ self._new_values = []
135
+ self._lock_new_values = threading.Lock()
136
+ self._running = threading.Event()
137
+ self._stop_request = threading.Event()
138
+ self._clear_area_event = threading.Event()
139
+ self.unpause()
140
+
141
+ def get_plotter_rect(self, screen_size):
142
+ half_screen_size = (screen_size[0] / 2, screen_size[1] / 2)
143
+ pos = self._plotter.absolute_position
144
+ stim_size = self._plotter.surface_size
145
+ rect_pos = (pos[0] + half_screen_size[0] - stim_size[0] / 2,
146
+ - pos[1] + half_screen_size[1] - stim_size[1] / 2)
147
+ return pygame.Rect(rect_pos, stim_size)
148
+
149
+ def clear_area(self):
150
+ self._clear_area_event.set()
151
+
152
+ def pause(self):
153
+ self._running.clear()
154
+
155
+ def unpause(self):
156
+ self._running.set()
157
+
158
+ def join(self, timeout=None):
159
+ self._stop_request.set()
160
+ super(PlotterThread, self).join(timeout)
161
+
162
+ def run(self):
163
+ """the plotter thread is constantly updating the the
164
+ pixel_area"""
165
+
166
+ while not self._stop_request.is_set():
167
+
168
+ if not self._running.is_set():
169
+ self._running.wait(timeout=1)
170
+ continue
171
+
172
+ if self._clear_area_event.is_set():
173
+ self._plotter.clear_area()
174
+ self._clear_area_event.clear()
175
+
176
+ # get data
177
+ if self._lock_new_values.acquire(False):
178
+ values = self._new_values
179
+ self._new_values = []
180
+ self._lock_new_values.release() # release to receive new values
181
+ else:
182
+ values = []
183
+
184
+ n = len(values)
185
+ if n > 0:
186
+ if n > self._plotter.width:
187
+ values = values[-1 * self._plotter.width:] # only the last
188
+ n = len(values)
189
+ self._plotter.pixel_array[:-1 * n, :] = \
190
+ self._plotter.pixel_array[n:, :]
191
+ for x in range(-1 * n, 0):
192
+ self._plotter.write_values(position=x,
193
+ values=values[x][0],
194
+ set_marker=values[x][1],
195
+ set_point_marker=values[x][2])
196
+ # Expyriment present
197
+ lock_expyriment.acquire()
198
+ self._plotter.present(update=False, clear=False)
199
+ lock_expyriment.release()
200
+
201
+ def set_horizontal_lines(self, y_values):
202
+ """adds new values to the plotter
203
+ y_values has to be an array
204
+ """
205
+ self._lock_new_values.acquire()
206
+ self._plotter.set_horizontal_line(y_values=y_values)
207
+ self._lock_new_values.release()
208
+
209
+ def add_values(self, values, set_marker=False, set_point_marker=False):
210
+ """adds new values to the plotter"""
211
+ self._lock_new_values.acquire()
212
+ self._new_values.append((values, set_marker, set_point_marker))
213
+ self._lock_new_values.release()
214
+
215
+
216
+ ## helper
217
+ def check_colour_array(values, required_n_colours):
218
+ """ check if it is a list of colours list of colours,
219
+ otherwise raise an error
220
+ """
221
+ try:
222
+ if not isinstance(values[0], (list, tuple, ExpyColor) ):
223
+ # it is a list, but the elements are not colours, let's assume
224
+ # it is one dimensional array
225
+ values = [values]
226
+ except:
227
+ values = [()] # values is not list pixel_array
228
+
229
+ values = list(map(lambda x:tuple(x), values)) # force list of tuples
230
+
231
+ if len(values) != required_n_colours:
232
+ raise RuntimeError('Number of data row colour does not match the ' +
233
+ 'defined number of data rows!')
234
+ return values