psychopy 2024.1.2__py3-none-any.whl → 2024.1.4__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.
Potentially problematic release.
This version of psychopy might be problematic. Click here for more details.
- psychopy/__init__.py +2 -2
- psychopy/app/builder/localizedStrings.py +11 -9
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.po +359 -346
- psychopy/app/plugin_manager/plugins.py +7 -0
- psychopy/demos/builder/Feature Demos/progress/progressBar.psyexp +4 -4
- psychopy/experiment/components/buttonBox/__init__.py +21 -12
- psychopy/experiment/components/mouse/__init__.py +12 -0
- psychopy/experiment/components/progress/__init__.py +1 -1
- psychopy/experiment/routines/counterbalance/__init__.py +1 -1
- psychopy/experiment/routines/photodiodeValidator/__init__.py +1 -1
- psychopy/hardware/keyboard.py +1 -2
- psychopy/tests/test_experiment/test_components/__init__.py +1 -1
- psychopy/tests/test_experiment/test_components/{test_ButtonBox.py → test_ButtonBoxComponent.py} +9 -27
- psychopy/tests/test_experiment/test_components/{test_Code.py → test_CodeComponent.py} +8 -20
- psychopy/tests/test_experiment/test_components/test_GratingComponent.py +7 -0
- psychopy/tests/test_experiment/test_components/test_ImageComponent.py +8 -0
- psychopy/tests/test_experiment/test_components/{test_Mouse.py → test_MouseComponent.py} +44 -57
- psychopy/tests/test_experiment/test_components/{test_Polygon.py → test_PolygonComponent.py} +9 -25
- psychopy/tests/test_experiment/test_components/{test_ResourceManager.py → test_ResourceManagerComponent.py} +3 -13
- psychopy/tests/test_experiment/test_components/{test_Settings.py → test_SettingsComponent.py} +1 -3
- psychopy/tests/test_experiment/test_components/{test_Static.py → test_StaticComponent.py} +3 -12
- psychopy/tests/test_experiment/test_components/test_all_components.py +8 -66
- psychopy/tests/test_experiment/test_components/test_base_components.py +212 -125
- psychopy/tests/test_hardware/test_keyboard.py +153 -16
- psychopy/visual/progress.py +1 -1
- psychopy/web.py +5 -2
- {psychopy-2024.1.2.dist-info → psychopy-2024.1.4.dist-info}/METADATA +2 -2
- {psychopy-2024.1.2.dist-info → psychopy-2024.1.4.dist-info}/RECORD +33 -32
- {psychopy-2024.1.2.dist-info → psychopy-2024.1.4.dist-info}/WHEEL +1 -1
- psychopy/tests/test_experiment/test_components/test_Image.py +0 -24
- {psychopy-2024.1.2.dist-info → psychopy-2024.1.4.dist-info}/entry_points.txt +0 -0
- {psychopy-2024.1.2.dist-info → psychopy-2024.1.4.dist-info}/licenses/AUTHORS.md +0 -0
- {psychopy-2024.1.2.dist-info → psychopy-2024.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1222,6 +1222,13 @@ def getAllPluginDetails():
|
|
|
1222
1222
|
return None
|
|
1223
1223
|
# otherwise get as a string
|
|
1224
1224
|
value = resp.text
|
|
1225
|
+
|
|
1226
|
+
if value is None or value == "":
|
|
1227
|
+
return None
|
|
1228
|
+
|
|
1229
|
+
# make sure we are using UTF-8 encoding
|
|
1230
|
+
value = value.encode('utf-8', 'ignore').decode('utf-8')
|
|
1231
|
+
|
|
1225
1232
|
# attempt to parse JSON
|
|
1226
1233
|
try:
|
|
1227
1234
|
database = json.loads(value)
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
<Param val="from exp settings" valType="str" updates="None" name="units" />
|
|
126
126
|
</TextboxComponent>
|
|
127
127
|
<ProgressComponent name="timer10" plugin="None">
|
|
128
|
-
<Param val="center
|
|
128
|
+
<Param val="center-left" valType="str" updates="constant" name="anchor" />
|
|
129
129
|
<Param val="grey" valType="color" updates="constant" name="borderColor" />
|
|
130
130
|
<Param val="grey" valType="color" updates="constant" name="color" />
|
|
131
131
|
<Param val="rgb" valType="str" updates="constant" name="colorSpace" />
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
<Param val="from exp settings" valType="str" updates="None" name="units" />
|
|
191
191
|
</TextboxComponent>
|
|
192
192
|
<ProgressComponent name="timer5" plugin="None">
|
|
193
|
-
<Param val="center
|
|
193
|
+
<Param val="center-left" valType="str" updates="constant" name="anchor" />
|
|
194
194
|
<Param val="darkgrey" valType="color" updates="constant" name="borderColor" />
|
|
195
195
|
<Param val="darkgrey" valType="color" updates="constant" name="color" />
|
|
196
196
|
<Param val="rgb" valType="str" updates="constant" name="colorSpace" />
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
<Param val="from exp settings" valType="str" updates="None" name="units" />
|
|
256
256
|
</TextboxComponent>
|
|
257
257
|
<ProgressComponent name="timer1" plugin="None">
|
|
258
|
-
<Param val="center
|
|
258
|
+
<Param val="center-left" valType="str" updates="constant" name="anchor" />
|
|
259
259
|
<Param val="lightgrey" valType="color" updates="constant" name="borderColor" />
|
|
260
260
|
<Param val="lightgrey" valType="color" updates="constant" name="color" />
|
|
261
261
|
<Param val="rgb" valType="str" updates="constant" name="colorSpace" />
|
|
@@ -320,7 +320,7 @@
|
|
|
320
320
|
<Param val="from exp settings" valType="str" updates="None" name="units" />
|
|
321
321
|
</TextboxComponent>
|
|
322
322
|
<ProgressComponent name="trialCtr" plugin="None">
|
|
323
|
-
<Param val="center
|
|
323
|
+
<Param val="center-left" valType="str" updates="constant" name="anchor" />
|
|
324
324
|
<Param val="dodgerblue" valType="color" updates="constant" name="borderColor" />
|
|
325
325
|
<Param val="dodgerblue" valType="color" updates="constant" name="color" />
|
|
326
326
|
<Param val="rgb" valType="str" updates="constant" name="colorSpace" />
|
|
@@ -161,6 +161,14 @@ class ButtonBoxComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
161
161
|
f"%(name)s.resetTimer({clockStr})\n"
|
|
162
162
|
)
|
|
163
163
|
buff.writeIndentedLines(code % self.params)
|
|
164
|
+
# clear keys
|
|
165
|
+
code = (
|
|
166
|
+
"# clear %(name)s button presses\n"
|
|
167
|
+
"%(name)s.buttons = []\n"
|
|
168
|
+
"%(name)s.times = []\n"
|
|
169
|
+
"%(name)s.corr = []\n"
|
|
170
|
+
)
|
|
171
|
+
buff.writeIndentedLines(code % self.params)
|
|
164
172
|
|
|
165
173
|
def writeFrameCode(self, buff):
|
|
166
174
|
params = self.params
|
|
@@ -201,7 +209,10 @@ class ButtonBoxComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
201
209
|
# include code to get correct
|
|
202
210
|
if self.params['storeCorrect']:
|
|
203
211
|
code += (
|
|
204
|
-
" %(
|
|
212
|
+
" if _thisResp.channel in %(correctAns)s or _thisResp.channel == %(correctAns)s:\n"
|
|
213
|
+
" %(name)s.corr.append(1)\n"
|
|
214
|
+
" else:\n"
|
|
215
|
+
" %(name)s.corr.append(0)\n"
|
|
205
216
|
)
|
|
206
217
|
elif self.params['store'] == "last":
|
|
207
218
|
# if storing last, replace
|
|
@@ -212,7 +223,10 @@ class ButtonBoxComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
212
223
|
# include code to get correct
|
|
213
224
|
if self.params['storeCorrect']:
|
|
214
225
|
code += (
|
|
215
|
-
" %(
|
|
226
|
+
" if _thisResp.channel in %(correctAns)s or _thisResp.channel == %(correctAns)s:\n"
|
|
227
|
+
" %(name)s.corr = 1\n"
|
|
228
|
+
" else:\n"
|
|
229
|
+
" %(name)s.corr = 0\n"
|
|
216
230
|
)
|
|
217
231
|
elif self.params['store'] == "first":
|
|
218
232
|
# if storing first, replace but only if empty
|
|
@@ -224,7 +238,10 @@ class ButtonBoxComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
224
238
|
# include code to get correct
|
|
225
239
|
if self.params['storeCorrect']:
|
|
226
240
|
code += (
|
|
227
|
-
" %(
|
|
241
|
+
" if _thisResp.channel in %(correctAns)s or _thisResp.channel == %(correctAns)s:\n"
|
|
242
|
+
" %(name)s.corr = 1\n"
|
|
243
|
+
" else:\n"
|
|
244
|
+
" %(name)s.corr = 0\n"
|
|
228
245
|
)
|
|
229
246
|
else:
|
|
230
247
|
code = "pass\n"
|
|
@@ -260,15 +277,7 @@ class ButtonBoxComponent(BaseDeviceComponent, PluginDevicesMixin):
|
|
|
260
277
|
"thisExp.addData('%(name)s.corr', %(name)s.corr)\n"
|
|
261
278
|
)
|
|
262
279
|
buff.writeIndentedLines(code % params)
|
|
263
|
-
|
|
264
|
-
code = (
|
|
265
|
-
"# clear %(name)s button presses\n"
|
|
266
|
-
"%(name)s.buttons = []\n"
|
|
267
|
-
"%(name)s.times = []\n"
|
|
268
|
-
"%(name)s.corr = []\n"
|
|
269
|
-
)
|
|
270
|
-
buff.writeIndentedLines(code % params)
|
|
271
|
-
|
|
280
|
+
|
|
272
281
|
|
|
273
282
|
class KeyboardButtonBoxBackend(DeviceBackend):
|
|
274
283
|
"""
|
|
@@ -364,10 +364,22 @@ class MouseComponent(BaseComponent):
|
|
|
364
364
|
buff.writeIndentedLines(code % self.params)
|
|
365
365
|
buff.setIndentLevel(1, relative=True)
|
|
366
366
|
dedent += 1
|
|
367
|
+
# keep track of whether something's been written
|
|
368
|
+
hasContent = False
|
|
369
|
+
# write code to check clickable stim, if there are any
|
|
367
370
|
if self.params['clickable'].val:
|
|
368
371
|
self._writeClickableObjectsCode(buff)
|
|
372
|
+
hasContent = True
|
|
373
|
+
# write code to check correct stim, if there are any
|
|
369
374
|
if self.params['storeCorrect']:
|
|
370
375
|
self._writeCorrectAnsCode(buff)
|
|
376
|
+
hasContent = True
|
|
377
|
+
# if current if statement has no content, add a pass
|
|
378
|
+
if not hasContent:
|
|
379
|
+
buff.writeIndentedLines(
|
|
380
|
+
"pass"
|
|
381
|
+
)
|
|
382
|
+
|
|
371
383
|
return buff, dedent
|
|
372
384
|
|
|
373
385
|
# No mouse tracking, end routine on any or valid click
|
|
@@ -24,7 +24,7 @@ class ProgressComponent(BaseVisualComponent):
|
|
|
24
24
|
progress=0,
|
|
25
25
|
color="white", fillColor="None", borderColor="white", colorSpace="rgb",
|
|
26
26
|
opacity=1, lineWidth=4,
|
|
27
|
-
pos=(0, 0), size=(0.5, 0.5), anchor="center
|
|
27
|
+
pos=(0, 0), size=(0.5, 0.5), anchor="center-left", ori=0, units="height",
|
|
28
28
|
disabled=False):
|
|
29
29
|
|
|
30
30
|
self.exp = exp # so we can access the experiment if necess
|
|
@@ -43,7 +43,7 @@ class CounterbalanceRoutine(BaseStandaloneRoutine):
|
|
|
43
43
|
self.params['specMode'] = Param(
|
|
44
44
|
specMode, valType="str", inputType="choice", categ="Basic",
|
|
45
45
|
allowedVals=["uniform", "file"],
|
|
46
|
-
allowedLabels=[_translate("Num. groups"), _translate("Conditions file")],
|
|
46
|
+
allowedLabels=[_translate("Num. groups"), _translate("Conditions file (local only)")],
|
|
47
47
|
label=_translate("Groups from..."),
|
|
48
48
|
hint=_translate(
|
|
49
49
|
"Specify groups using an Excel file (for fine tuned control), specify as a variable name, or specify a "
|
|
@@ -17,7 +17,7 @@ class PhotodiodeValidatorRoutine(BaseValidatorRoutine, PluginDevicesMixin):
|
|
|
17
17
|
|
|
18
18
|
categories = ['Validation']
|
|
19
19
|
iconFile = Path(__file__).parent / 'photodiode_validator.png'
|
|
20
|
-
tooltip = _translate('')
|
|
20
|
+
tooltip = _translate('Photodiode validator')
|
|
21
21
|
deviceClasses = []
|
|
22
22
|
version = "2024.2.0"
|
|
23
23
|
|
psychopy/hardware/keyboard.py
CHANGED
|
@@ -521,7 +521,6 @@ class KeyboardDevice(BaseResponseDevice, aliases=["keyboard"]):
|
|
|
521
521
|
elif KeyboardDevice._backend == 'iohub':
|
|
522
522
|
# get events from backend (need to reverse order)
|
|
523
523
|
key_events = KeyboardDevice._iohubKeyboard.getKeys(clear=True)
|
|
524
|
-
key_events.reverse()
|
|
525
524
|
# parse and receive each event
|
|
526
525
|
for k in key_events:
|
|
527
526
|
kpress = self.parseMessage(k)
|
|
@@ -573,7 +572,7 @@ class KeyboardDevice(BaseResponseDevice, aliases=["keyboard"]):
|
|
|
573
572
|
if message.type == "KEYBOARD_PRESS":
|
|
574
573
|
# if message is from a key down event, make a new response
|
|
575
574
|
response = KeyPress(code=message.char, tDown=message.time, name=message.key)
|
|
576
|
-
response.rt = response.tDown
|
|
575
|
+
response.rt = response.tDown - (self.clock.getLastResetTime() - self._iohubKeyboard.clock.getLastResetTime())
|
|
577
576
|
self._keysStillDown.append(response)
|
|
578
577
|
else:
|
|
579
578
|
# if message is from a key up event, alter existing response
|
|
@@ -1 +1 @@
|
|
|
1
|
-
from .test_base_components import
|
|
1
|
+
from .test_base_components import BaseComponentTests
|
psychopy/tests/test_experiment/test_components/{test_ButtonBox.py → test_ButtonBoxComponent.py}
RENAMED
|
@@ -11,21 +11,14 @@ from psychopy.hardware.button import ButtonResponse
|
|
|
11
11
|
from psychopy.experiment.routines import Routine
|
|
12
12
|
from psychopy.experiment.components.buttonBox import ButtonBoxComponent
|
|
13
13
|
from psychopy.experiment.components.code import CodeComponent
|
|
14
|
-
from .test_base_components import
|
|
14
|
+
from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests
|
|
15
|
+
from psychopy.hardware.button import ButtonBox
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class TestButtonBoxComponent(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
self.routine = Routine(name="testRoutine", exp=self.exp)
|
|
22
|
-
self.exp.addRoutine("testRoutine", self.routine)
|
|
23
|
-
self.exp.flow.addRoutine(self.routine, 0)
|
|
24
|
-
# make component
|
|
25
|
-
self.comp = ButtonBoxComponent(
|
|
26
|
-
exp=self.exp, name="testPhotodiodeValidatorRoutine", parentName="testRoutine"
|
|
27
|
-
)
|
|
28
|
-
|
|
18
|
+
class TestButtonBoxComponent(BaseComponentTests):
|
|
19
|
+
comp = ButtonBoxComponent
|
|
20
|
+
libraryClass = ButtonBox
|
|
21
|
+
|
|
29
22
|
def test_values(self):
|
|
30
23
|
"""
|
|
31
24
|
Test that a variety of different values work when run from Builder.
|
|
@@ -65,9 +58,8 @@ class TestButtonBoxComponent(_TestBaseComponentsMixin):
|
|
|
65
58
|
thisCase[keys[i]] = val
|
|
66
59
|
# add case
|
|
67
60
|
cases.append(thisCase)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
exp = Experiment()
|
|
61
|
+
# make minimal experiment just for this test
|
|
62
|
+
comp, rt, exp = self.make_minimal_experiment()
|
|
71
63
|
# configure experiment
|
|
72
64
|
exp.requireImport("ButtonResponse", importFrom="psychopy.hardware.button")
|
|
73
65
|
exp.settings.params['Full-screen window'].val = False
|
|
@@ -79,18 +71,8 @@ class TestButtonBoxComponent(_TestBaseComponentsMixin):
|
|
|
79
71
|
resps = case.pop("resps")
|
|
80
72
|
if not isinstance(resps, (list, tuple)):
|
|
81
73
|
resps = [resps]
|
|
82
|
-
# make a name
|
|
83
|
-
name = f"rt{i}"
|
|
84
|
-
# make a Routine
|
|
85
|
-
rt = exp.addRoutine(name, Routine(name=name, exp=exp))
|
|
86
|
-
exp.flow.addRoutine(rt, 0)
|
|
87
74
|
# add timeout
|
|
88
75
|
rt.settings.params['stopVal'].val = 0.2
|
|
89
|
-
# make a Component
|
|
90
|
-
comp = ButtonBoxComponent(
|
|
91
|
-
exp, parentName=name, name=name + "_comp", **case
|
|
92
|
-
)
|
|
93
|
-
rt.addComponent(comp)
|
|
94
76
|
# make a Code Component to send responses
|
|
95
77
|
code = (
|
|
96
78
|
"if frameN > 1:\n"
|
|
@@ -104,7 +86,7 @@ class TestButtonBoxComponent(_TestBaseComponentsMixin):
|
|
|
104
86
|
name=comp.name, value=resp.value, channel=resp.channel
|
|
105
87
|
)
|
|
106
88
|
codeComp = CodeComponent(
|
|
107
|
-
exp, parentName=name + "_code", eachFrame=code
|
|
89
|
+
exp, parentName=comp.name + "_code", eachFrame=code
|
|
108
90
|
)
|
|
109
91
|
rt.addComponent(codeComp)
|
|
110
92
|
# save exp in temp directory
|
|
@@ -2,42 +2,30 @@ from pathlib import Path
|
|
|
2
2
|
from tempfile import mkdtemp
|
|
3
3
|
|
|
4
4
|
from psychopy import experiment
|
|
5
|
-
from . import
|
|
5
|
+
from . import BaseComponentTests
|
|
6
6
|
from psychopy.experiment.loops import TrialHandler
|
|
7
7
|
from psychopy.experiment.routines import Routine
|
|
8
8
|
from psychopy.experiment.components.code import CodeComponent
|
|
9
9
|
from psychopy.tests.utils import TESTS_DATA_PATH
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class TestCodeComponent(
|
|
12
|
+
class TestCodeComponent(BaseComponentTests):
|
|
13
13
|
"""
|
|
14
14
|
Test that Code coponents have the correct params and write as expected.
|
|
15
15
|
"""
|
|
16
|
+
comp = CodeComponent
|
|
16
17
|
|
|
17
18
|
@classmethod
|
|
18
19
|
def setup_class(cls):
|
|
19
|
-
cls.exp = experiment.Experiment() # create once, not every test
|
|
20
20
|
try:
|
|
21
21
|
cls.tempDir = mkdtemp(dir=Path(__file__).root, prefix='psychopy-tests-app')
|
|
22
22
|
except (PermissionError, OSError):
|
|
23
23
|
# can't write to root on Linux
|
|
24
24
|
cls.tempDir = mkdtemp(prefix='psychopy-tests-app')
|
|
25
25
|
|
|
26
|
-
def setup_method(self):
|
|
27
|
-
# Make blank experiment
|
|
28
|
-
self.exp = experiment.Experiment()
|
|
29
|
-
# Make blank routine
|
|
30
|
-
self.routine = Routine(name="testRoutine", exp=self.exp)
|
|
31
|
-
self.exp.addRoutine("testRoutine", self.routine)
|
|
32
|
-
self.exp.flow.addRoutine(self.routine, 0)
|
|
33
|
-
# Add loop around routine
|
|
34
|
-
self.loop = TrialHandler(exp=self.exp, name="testLoop")
|
|
35
|
-
self.exp.flow.addLoop(self.loop, 0, -1)
|
|
36
|
-
# Make Mouse component
|
|
37
|
-
self.comp = CodeComponent(exp=self.exp, parentName="testRoutine", name="testCode")
|
|
38
|
-
self.routine.addComponent(self.comp)
|
|
39
|
-
|
|
40
26
|
def test_all_code_component_tabs(self):
|
|
27
|
+
# make minimal experiment just for this test
|
|
28
|
+
comp, rt, exp = self.make_minimal_experiment()
|
|
41
29
|
# Names of each tab in a Code component
|
|
42
30
|
tabs = {
|
|
43
31
|
'Before Experiment': '___before_experiment___',
|
|
@@ -50,11 +38,11 @@ class TestCodeComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
|
|
|
50
38
|
# Add markers to component
|
|
51
39
|
for paramName, marker in tabs.items():
|
|
52
40
|
jsParamName = paramName.replace(" ", " JS ")
|
|
53
|
-
|
|
41
|
+
comp.params[paramName].val = comp.params[jsParamName].val = " = ".join([self.comp.__name__, comp.name, marker])
|
|
54
42
|
|
|
55
43
|
# Write script
|
|
56
|
-
pyScript =
|
|
57
|
-
jsScript =
|
|
44
|
+
pyScript = exp.writeScript(target="PsychoPy")
|
|
45
|
+
jsScript = exp.writeScript(target="PsychoJS")
|
|
58
46
|
|
|
59
47
|
# Check that code from each tab exists in compiled script
|
|
60
48
|
for lang, script in {"Python": pyScript, "JS": jsScript}.items():
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _TestLibraryClassMixin
|
|
2
|
+
from psychopy.experiment.components.grating import GratingComponent
|
|
3
|
+
from psychopy.visual import GratingStim
|
|
4
|
+
|
|
5
|
+
class TestGratingComponent(BaseComponentTests, _TestLibraryClassMixin):
|
|
6
|
+
comp = GratingComponent
|
|
7
|
+
libraryClass = GratingStim
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from psychopy.experiment.components.image import ImageComponent
|
|
2
|
+
from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _TestDepthMixin, _TestLibraryClassMixin
|
|
3
|
+
from psychopy.visual.image import ImageStim
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestImage(BaseComponentTests, _TestLibraryClassMixin):
|
|
7
|
+
comp = ImageComponent
|
|
8
|
+
libraryClass = ImageStim
|
|
@@ -1,65 +1,52 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from . import
|
|
4
|
-
from psychopy.experiment import Experiment
|
|
3
|
+
from . import BaseComponentTests
|
|
5
4
|
from psychopy.experiment.loops import TrialHandler
|
|
6
|
-
from psychopy.experiment.routines import Routine
|
|
7
5
|
from psychopy.experiment.components.mouse import MouseComponent
|
|
8
6
|
from psychopy.experiment.components.polygon import PolygonComponent
|
|
9
7
|
from psychopy.tests.utils import TESTS_DATA_PATH
|
|
10
8
|
from psychopy.hardware.mouse import Mouse
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
class TestMouseComponent(
|
|
11
|
+
class TestMouseComponent(BaseComponentTests):
|
|
14
12
|
"""
|
|
15
13
|
Test that Mouse coponents have the correct params and write as expected.
|
|
16
14
|
"""
|
|
15
|
+
comp = MouseComponent
|
|
17
16
|
libraryClass = Mouse
|
|
18
17
|
|
|
19
|
-
def setup_method(self):
|
|
20
|
-
# Make blank experiment
|
|
21
|
-
self.exp = Experiment()
|
|
22
|
-
# Make blank routine
|
|
23
|
-
self.routine = Routine(name="testRoutine", exp=self.exp)
|
|
24
|
-
self.exp.addRoutine("testRoutine", self.routine)
|
|
25
|
-
self.exp.flow.addRoutine(self.routine, 0)
|
|
26
|
-
# Add loop around routine
|
|
27
|
-
self.loop = TrialHandler(exp=self.exp, name="testLoop")
|
|
28
|
-
self.exp.flow.addLoop(self.loop, 0, -1)
|
|
29
|
-
# Make Mouse component
|
|
30
|
-
self.comp = MouseComponent(exp=self.exp, parentName="testRoutine", name="testMouse")
|
|
31
|
-
self.routine.addComponent(self.comp)
|
|
32
|
-
# Make a rect for when we need something to click on
|
|
33
|
-
self.target = PolygonComponent(exp=self.exp, parentName="testRoutine", name="testPolygon")
|
|
34
|
-
self.routine.addComponent(self.target)
|
|
35
|
-
|
|
36
18
|
def test_click_save_end_clickable_cases(self):
|
|
37
19
|
"""
|
|
38
20
|
Test all combinations of options for what to save, what can be clicked on & what kind of clicks to end the
|
|
39
21
|
routine on.
|
|
40
22
|
"""
|
|
23
|
+
# make minimal experiment just for this test
|
|
24
|
+
comp, rt, exp = self.make_minimal_experiment()
|
|
25
|
+
# make a rect for when we need something to click on
|
|
26
|
+
target = PolygonComponent(exp=exp, parentName=rt.name, name="testPolygon")
|
|
27
|
+
|
|
41
28
|
saveMouseStateCases = [
|
|
42
29
|
{'val': "final",
|
|
43
|
-
'want': ["thisExp.addData('
|
|
44
|
-
'avoid': ["
|
|
30
|
+
'want': [f"thisExp.addData('{comp.name}.x', x)"], # should contain code for adding final value of x
|
|
31
|
+
'avoid': [f"{comp.name}.x.append(x)"]}, # should not contain code to update testMouse.x in frame loop
|
|
45
32
|
{'val': "on click",
|
|
46
|
-
'want': ["thisExp.addData('
|
|
47
|
-
"
|
|
48
|
-
'avoid': ["thisExp.addData('
|
|
33
|
+
'want': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should add testMouse.x at the end
|
|
34
|
+
f"{comp.name}.x.append(x)"], # should contain code to update testMouse.x in frame loop
|
|
35
|
+
'avoid': [f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x
|
|
49
36
|
{'val': "on valid click",
|
|
50
|
-
'want': ["thisExp.addData('
|
|
51
|
-
"
|
|
37
|
+
'want': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should add testMouse.x at the end
|
|
38
|
+
f"{comp.name}.x.append(x)", # should contain code to update testMouse.x in frame loop
|
|
52
39
|
"if gotValidClick:"], # should check for valid clicks
|
|
53
|
-
'avoid': ["thisExp.addData('
|
|
40
|
+
'avoid': [f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x
|
|
54
41
|
{'val': "every frame",
|
|
55
|
-
'want': ["thisExp.addData('
|
|
56
|
-
"
|
|
57
|
-
'avoid': ["thisExp.addData('
|
|
42
|
+
'want': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should add testMouse.x at the end
|
|
43
|
+
f"{comp.name}.x.append(x)"], # should contain code to update testMouse.x in frame loop
|
|
44
|
+
'avoid': [f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x
|
|
58
45
|
{'val': "never",
|
|
59
46
|
'want': [],
|
|
60
|
-
'avoid': ["thisExp.addData('
|
|
61
|
-
"
|
|
62
|
-
"thisExp.addData('
|
|
47
|
+
'avoid': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should not add testMouse.x at the end
|
|
48
|
+
f"{comp.name}.x.append(x)", # should not contain code to update testMouse.x in frame loop
|
|
49
|
+
f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x]},
|
|
63
50
|
]
|
|
64
51
|
forceEndRoutineOnPressCases = [
|
|
65
52
|
{'val': "never",
|
|
@@ -85,54 +72,54 @@ class TestMouseComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
|
|
|
85
72
|
# Iterate through saveMouseState cases
|
|
86
73
|
for SMScase in saveMouseStateCases:
|
|
87
74
|
# Set saveMouseState
|
|
88
|
-
|
|
75
|
+
comp.params['saveMouseState'].val = SMScase['val']
|
|
89
76
|
for FEROPcase in forceEndRoutineOnPressCases:
|
|
90
77
|
# Set forceEndRoutineOnPress
|
|
91
|
-
|
|
78
|
+
comp.params['forceEndRoutineOnPress'].val = FEROPcase['val']
|
|
92
79
|
for Ccase in clickableCases:
|
|
93
80
|
# Set clickable
|
|
94
|
-
|
|
81
|
+
comp.params['clickable'].val = Ccase['val']
|
|
95
82
|
|
|
96
83
|
# Compile script
|
|
97
|
-
script =
|
|
84
|
+
script = exp.writeScript(target="PsychoPy")
|
|
98
85
|
try:
|
|
99
86
|
# Look for wanted phrases
|
|
100
87
|
for phrase in SMScase['want']:
|
|
101
88
|
assert phrase in script, (
|
|
102
|
-
f"{phrase} not found in script when saveMouseState={
|
|
103
|
-
f"forceEndRoutineOnPress={
|
|
104
|
-
f"clickable={
|
|
89
|
+
f"{phrase} not found in script when saveMouseState={comp.params['saveMouseState']}, "
|
|
90
|
+
f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
|
|
91
|
+
f"clickable={comp.params['clickable']}"
|
|
105
92
|
)
|
|
106
93
|
for phrase in FEROPcase['want']:
|
|
107
94
|
assert phrase in script, (
|
|
108
|
-
f"{phrase} not found in script when saveMouseState={
|
|
109
|
-
f"forceEndRoutineOnPress={
|
|
110
|
-
f"clickable={
|
|
95
|
+
f"{phrase} not found in script when saveMouseState={comp.params['saveMouseState']}, "
|
|
96
|
+
f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
|
|
97
|
+
f"clickable={comp.params['clickable']}"
|
|
111
98
|
)
|
|
112
99
|
for phrase in Ccase['want']:
|
|
113
100
|
assert phrase in script, (
|
|
114
|
-
f"{phrase} not found in script when saveMouseState={
|
|
115
|
-
f"forceEndRoutineOnPress={
|
|
116
|
-
f"clickable={
|
|
101
|
+
f"{phrase} not found in script when saveMouseState={comp.params['saveMouseState']}, "
|
|
102
|
+
f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
|
|
103
|
+
f"clickable={comp.params['clickable']}"
|
|
117
104
|
)
|
|
118
105
|
# Check there's no avoid phrases
|
|
119
106
|
for phrase in SMScase['avoid']:
|
|
120
107
|
assert phrase not in script, (
|
|
121
|
-
f"{phrase} found in script when saveMouseState={
|
|
122
|
-
f"forceEndRoutineOnPress={
|
|
123
|
-
f"clickable={
|
|
108
|
+
f"{phrase} found in script when saveMouseState={comp.params['saveMouseState']}, "
|
|
109
|
+
f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
|
|
110
|
+
f"clickable={comp.params['clickable']}"
|
|
124
111
|
)
|
|
125
112
|
for phrase in FEROPcase['avoid']:
|
|
126
113
|
assert phrase not in script, (
|
|
127
|
-
f"{phrase} found in script when saveMouseState={
|
|
128
|
-
f"forceEndRoutineOnPress={
|
|
129
|
-
f"clickable={
|
|
114
|
+
f"{phrase} found in script when saveMouseState={comp.params['saveMouseState']}, "
|
|
115
|
+
f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
|
|
116
|
+
f"clickable={comp.params['clickable']}"
|
|
130
117
|
)
|
|
131
118
|
for phrase in Ccase['avoid']:
|
|
132
119
|
assert phrase not in script, (
|
|
133
|
-
f"{phrase} found in script when saveMouseState={
|
|
134
|
-
f"forceEndRoutineOnPress={
|
|
135
|
-
f"clickable={
|
|
120
|
+
f"{phrase} found in script when saveMouseState={comp.params['saveMouseState']}, "
|
|
121
|
+
f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
|
|
122
|
+
f"clickable={comp.params['clickable']}"
|
|
136
123
|
)
|
|
137
124
|
except AssertionError as err:
|
|
138
125
|
# If any assertion fails, save script to view
|
|
@@ -1,37 +1,21 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from . import _TestDisabledMixin, _TestBaseComponentsMixin
|
|
4
|
-
from psychopy.experiment import Experiment
|
|
5
|
-
from psychopy.experiment.loops import TrialHandler
|
|
6
|
-
from psychopy.experiment.routines import Routine
|
|
1
|
+
from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _TestLibraryClassMixin
|
|
7
2
|
from psychopy.experiment.components.polygon import PolygonComponent
|
|
8
3
|
from psychopy.visual.polygon import Polygon
|
|
9
4
|
|
|
10
5
|
|
|
11
|
-
class TestPolygonComponent(
|
|
6
|
+
class TestPolygonComponent(BaseComponentTests, _TestLibraryClassMixin):
|
|
12
7
|
"""
|
|
13
8
|
Test that Polygon coponents have the correct params and write as expected.
|
|
14
9
|
"""
|
|
10
|
+
comp = PolygonComponent
|
|
15
11
|
libraryClass = Polygon
|
|
16
12
|
|
|
17
|
-
def setup_method(self):
|
|
18
|
-
# Make blank experiment
|
|
19
|
-
self.exp = Experiment()
|
|
20
|
-
# Make blank routine
|
|
21
|
-
self.routine = Routine(name="testRoutine", exp=self.exp)
|
|
22
|
-
self.exp.addRoutine("testRoutine", self.routine)
|
|
23
|
-
self.exp.flow.addRoutine(self.routine, 0)
|
|
24
|
-
# Add loop around routine
|
|
25
|
-
self.loop = TrialHandler(exp=self.exp, name="testLoop")
|
|
26
|
-
self.exp.flow.addLoop(self.loop, 0, -1)
|
|
27
|
-
# Make a rect for when we need something to click on
|
|
28
|
-
self.comp = PolygonComponent(exp=self.exp, parentName="testRoutine", name="testPolygon")
|
|
29
|
-
self.routine.addComponent(self.comp)
|
|
30
|
-
|
|
31
13
|
def test_vertices_usage(self):
|
|
32
14
|
"""
|
|
33
15
|
Test that vertices values are used only under the correct conditions
|
|
34
16
|
"""
|
|
17
|
+
# make minimal experiment just for this test
|
|
18
|
+
comp, rt, exp = self.make_minimal_experiment()
|
|
35
19
|
# Define values to look for and avoid in code according to value of shape
|
|
36
20
|
cases = [
|
|
37
21
|
# Shape is a line
|
|
@@ -61,14 +45,14 @@ class TestPolygonComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
|
|
|
61
45
|
'avoid': ["___nVertices___"]},
|
|
62
46
|
]
|
|
63
47
|
# Setup component with markers for nVertices and vertices
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
comp.params['nVertices'].val = "___nVertices___"
|
|
49
|
+
comp.params['vertices'].val = "___vertices___"
|
|
66
50
|
# Test each case
|
|
67
51
|
for case in cases:
|
|
68
52
|
# Set shape
|
|
69
|
-
|
|
53
|
+
comp.params['shape'].val = case['val']
|
|
70
54
|
# Write experiment
|
|
71
|
-
pyScript =
|
|
55
|
+
pyScript = exp.writeScript(target="PsychoPy")
|
|
72
56
|
# Look for sought values in experiment script
|
|
73
57
|
for seekVal in case['seek']:
|
|
74
58
|
assert seekVal in pyScript, (
|
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from . import
|
|
4
|
-
from .test_base_components import _find_global_resource_in_js_experiment
|
|
3
|
+
from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _find_global_resource_in_js_experiment
|
|
5
4
|
from psychopy.experiment.components.resourceManager import ResourceManagerComponent
|
|
6
5
|
from psychopy import experiment
|
|
7
6
|
from ...utils import TESTS_DATA_PATH
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
class TestResourceManagerComponent(
|
|
11
|
-
|
|
12
|
-
# Make blank experiment
|
|
13
|
-
self.exp = experiment.Experiment()
|
|
14
|
-
# Make blank routine
|
|
15
|
-
self.routine = experiment.routines.Routine(name="testRoutine", exp=self.exp)
|
|
16
|
-
self.exp.addRoutine("testRoutine", self.routine)
|
|
17
|
-
self.exp.flow.addRoutine(self.routine, 0)
|
|
18
|
-
# Make Resource Manager component
|
|
19
|
-
self.comp = ResourceManagerComponent(exp=self.exp, parentName="testRoutine", name="testResourceManager")
|
|
20
|
-
self.routine.addComponent(self.comp)
|
|
9
|
+
class TestResourceManagerComponent(BaseComponentTests):
|
|
10
|
+
comp = ResourceManagerComponent
|
|
21
11
|
|
|
22
12
|
def test_handled_resources_removed(self):
|
|
23
13
|
"""
|
psychopy/tests/test_experiment/test_components/{test_Settings.py → test_SettingsComponent.py}
RENAMED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from . import _TestBaseComponentsMixin, _TestDisabledMixin
|
|
4
3
|
from .test_base_components import _find_global_resource_in_js_experiment
|
|
5
|
-
from psychopy.experiment.components.settings import SettingsComponent
|
|
6
4
|
from psychopy import experiment
|
|
7
5
|
from ...utils import TESTS_DATA_PATH
|
|
8
6
|
|
|
9
7
|
|
|
10
|
-
class TestSettingsComponent
|
|
8
|
+
class TestSettingsComponent:
|
|
11
9
|
def test_unhandled_resources_js(self):
|
|
12
10
|
"""
|
|
13
11
|
Check that resources not otherwise handled are present at the start of the experiment
|