Repository: psychopy/psychopy

Issue:
Textbox component receiving input from Keyboard component in separate routine in prior trial
I put this in discussion but so far no comments, this seems like a bug to me.

**OS (e.g. Win10):** Win10
**PsychoPy version (e.g. 1.84.x):** 2022.1.3 and 2022.1.4
**Standard Standalone? (y/n) If not then what?:** Standard Standalone
**What are you trying to achieve?:**
I have 2 routines in the trial, the first has a textbox component to ask for text input from the participant. I’m detecting when they hit the Enter key to continue onto the next routine in the trial which has a keyboard response component to ask for a specific key response. However, when the experiment iterates onto the next trial, the response for the keyboard object in the previous trial appears in the textbox.

**What did you try to make it work?:**
I’ve tried explicitly setting the textbox text to blank, and calling textbox.reset in the Begin Routine section of a code component. My textbox component has its Text set to “set every repeat”.  I've tried explicitly clearing keyboard buffers.

**What specifically went wrong when you tried that?:**
Neither of the above worked. I seem unable to prevent the response made to the keyboard object from appearing in the textbox on the next iteration.

**Test file here:**
[testTextbox.zip](https://github.com/psychopy/psychopy/files/8794890/testTextbox.zip)



Discussion:
I think I've found the problem - it's that the Keyboard component in the y/n routine uses `waitRelease=False`, but the Textbox listens for key releases, not key presses. So the routine ends when the key is pressed, but the character is sent to the textbox when the key is released. It may be as simple as just having Keyboard use `waitRelease=True` but I'll need to investigate further what the effects of that would be (I'm wary of introducing a new bug by fixing this one!)

Resolving patch:
diff --git a/psychopy/experiment/components/keyboard/__init__.py b/psychopy/experiment/components/keyboard/__init__.py
--- a/psychopy/experiment/components/keyboard/__init__.py
+++ b/psychopy/experiment/components/keyboard/__init__.py
@@ -35,7 +35,7 @@ class KeyboardComponent(BaseComponent):
     tooltip = _translate('Keyboard: check and record keypresses')
 
     def __init__(self, exp, parentName, name='key_resp',
-                 allowedKeys="'y','n','left','right','space'",
+                 allowedKeys="'y','n','left','right','space'", registerOn="press",
                  store='last key', forceEndRoutine=True, storeCorrect=False,
                  correctAns="", discardPrev=True,
                  startType='time (s)', startVal=0.0,
@@ -55,8 +55,8 @@ def __init__(self, exp, parentName, name='key_resp',
         # params
 
         # NB name and timing params always come 1st
-        self.order += ['forceEndRoutine',  # Basic tab
-                       'allowedKeys', 'store', 'storeCorrect', 'correctAns'  # Data tab
+        self.order += ['forceEndRoutine', 'registerOn', 'allowedKeys',  # Basic tab
+                       'store', 'storeCorrect', 'correctAns'  # Data tab
                        ]
 
         msg = _translate(
@@ -70,6 +70,16 @@ def __init__(self, exp, parentName, name='key_resp',
             hint=(msg),
             label=_localized['allowedKeys'])
 
+        msg = _translate(
+            "When should the keypress be registered? As soon as pressed, or when released?")
+        self.params['registerOn'] = Param(
+            registerOn, valType='str', inputType='choice',
+            categ='Basic', updates='constant',
+            allowedVals=["press", "release"],
+            hint=msg,
+            label=_translate("Register keypress on...")
+        )
+
         # hints say 'responses' not 'key presses' because the same hint is
         # also used with button boxes
         msg = _translate("Do you want to discard all responses occurring "
@@ -235,12 +245,13 @@ def writeFrameCode(self, buff):
             keyListStr = self.params['allowedKeys']
 
         # check for keypresses
-        code = ("theseKeys = {name}.getKeys(keyList={keyStr}, waitRelease=False)\n"
+        code = ("theseKeys = {name}.getKeys(keyList={keyStr}, waitRelease={waitRelease})\n"
                 "_{name}_allKeys.extend(theseKeys)\n"
                 "if len(_{name}_allKeys):\n")
         buff.writeIndentedLines(
             code.format(
                 name=self.params['name'],
+                waitRelease=self.params['registerOn'] == "release",
                 keyStr=(keyListStr or None)
             )
         )
@@ -373,12 +384,16 @@ def writeFrameCodeJS(self, buff):
             keyListStr = "%s" % repr(keyList)
 
         # check for keypresses
-        code = ("let theseKeys = {name}.getKeys({{keyList: {keyStr}, waitRelease: false}});\n"
+        waitRelease = "false"
+        if self.params['registerOn'] == "release":
+            waitRelease = "true"
+        code = ("let theseKeys = {name}.getKeys({{keyList: {keyStr}, waitRelease: {waitRelease}}});\n"
                 "_{name}_allKeys = _{name}_allKeys.concat(theseKeys);\n"
                 "if (_{name}_allKeys.length > 0) {{\n")
         buff.writeIndentedLines(
             code.format(
                 name=self.params['name'],
+                waitRelease=waitRelease,
                 keyStr=keyListStr
             )
         )


Test patch: