Unable to translate code component in Pavlovia

Hi all!

URL of experiment: https://run.pavlovia.org/MarinaIosifian/reverse-correlation-child-face/html

Description of the problem: My experiment runs properly in Builder. It crashes because the translation of the following code component is not correct, as I get “win is not defined” error. The code component is used to create a Continue button which will be enabled when my Form component is completed. How can I correct this code?

Begine routine:
continueButton = visual.ButtonStim(win, labelText= “Continue”, pos=(.35, -.4))
demo.aperture.disable()
in JavaScript:
continueButton = new visual.ButtonStim(win, {“labelText”: “Continue”, “pos”: [0.35, (- 0.4)]});
demo.aperture.disable();
Each frame:
continueButton.draw()

if demo.formComplete():
continueButton.buttonEnabled = True

if continueButton.buttonSelected:
continueRoutine = False
In Java:
continueButton.draw();
if (demo.formComplete()) {
continueButton.buttonEnabled = true;
}
if (continueButton.buttonSelected) {
continueRoutine = false;
}

replace “win” with “psychoJS.window”.

Thank you very much for such a super quick response, Jonathan!
There is no “win” error now, but I get an error related to another component of my code.
visual.ButtonStim is not a constructor

Is there a manual with the advises to translate py to JV? I guess that auto translation makes a lot of errors…

You probably want to just create this button element in the builder and make its appearance conditional. In a pinch you could make the thing in builder, have it never appear by default, and then use a code component to make it appear manually, but I think you should be able to make something like this just in builder.

Thanks! I created a text component ‘Continue’ and I want it only to appear on the screen when the participant completed the Form component.

I have the following Code component to make that happen

if demo.formComplete():
cue_visibility = 1
else:
cue_visibility = 0

For some reason my text component is always visible. But at least I don’t have the error with JS anymore

I’m not sure what cue_visibility is in this context. Your better bet would be to set the condition for the text component to demo.formComplete() in the builder, I think. Failing that, you would want to look into changing the “setAutoDraw” property for it, so in Python, on each frame:

if demo.formComplete():
   cue.setAutoDraw(True)
else:
   cue.setAutoDraw(False)

and if you set your code component type to “auto -> JS” it should make the equivalent JS code for you.

1 Like

Works perfectly! Thank you, Jonathan!

I feel really dumb asking that question, but do you have any idea how can I translate that into JS?

if demo.formComplete():
cue.setAutoDraw(True)
else:
cue.setAutoDraw(False)

Pavlovia tells me that demo is not defined, while Builder runs perfectly well. I could not find any demo test to have an example of how this code should look in JS…

I don’t know much about how form components work in Pavlovia, unfortunately, because I’ve never used them.

In general, for converting code components, in your code component there should be a “code type” dropdown. Make sure it is set to auto->JS.

However, that won’t help if the way that a component works is changed in a big way from Python to JS. If you link your repository and make it public (under general settings->permissions), I can have a look and see how forms work in JS.

Yes, auto-> JS is set. I guess the code should be changed manually. Although sometimes auto transcription helps, quite often it does not, unfortunately.

My project is public on git hub

Thank you very much!

Ah, it seems form elements just don’t work in JavaScript at all at the moment. It just doesn’t acknowledge them at all. So code component or not, it just won’t display the form online at all as far as I can tell! You may need to look at other ways of putting that together.

@dvbridges is that documented anywhere?

@jonathan.kominsky, does not look like it is on the docs page for form - could be added though. There is a GitHub issue thoughn and its in progress:

I came to this page to try and fix the same error:
visual.ButtonStim is not a constructor

But I’m not clear on that the bottom line of the discussion between dvbridges and jonathan.kominsky was. Do forms not work on Pavlovia at all? Is there a recommended alternative?

Thanks

@JKugel, Forms and ButtonStim have not yet been implemented online, but they are in progress. In the meantime, you can create something similar using text components and sliders inside a loop, where your conditions file contains your questions to be presented.

1 Like

I am trying to do something similar, and I managed to make a continue button using the “both” option instead of Auto->JS.

In begin experiment (Py side):

continueButton = visual.ShapeStim(win,  fillColor= [0.3, 0.3, 0.3], fillColorSpace='rgb', vertices=((-0.08, -0.05), (-0.08, 0.05), (0.08, 0.05), (0.08, -0.05)), closeShape=True, pos=(.70, -.40), name='continueButton')
continueButtonText = visual.TextStim(win, text='Continue', height = 0.03, pos=(.70, -.40))
continueButtonSelected = False

In begin experiment (JS side):

continueButton = new visual.ShapeStim({"win":psychoJS.window,"fillColor": [0.3, 0.3, 0.3], "fillColorSpace": "rgb", "vertices": [[(- 0.08), (- 0.05)], [(- 0.08), 0.05], [0.08, 0.05], [0.08, (- 0.05)]], "closeShape": true, "pos": [0.7, (- 0.4)], "name": "continueButton"});
continueButtonText = new visual.TextStim({"win":psychoJS.window, "text": "Continue", "height": 0.03, "pos": [0.7, (- 0.4)]});
continueButtonSelected = false;

So far, I got no errors from that. Then I want to add

In begin routine (Py side):

effortRate_resp = event.Mouse(win=win)
continueButtonSelected = False

In begin routine (JS side):

mouse = new core.Mouse({win: psychoJS.window});
continueButtonSelected = false;

In each frame (Py side):

if effortRate_resp.isPressedIn(continueButton):
        continueButtonSelected = True
    if continueButtonSelected:
        continueRoutine = False

In each frame (JS side):

if ((continueButton.contains(mouse)) & (mouse.getPressed()[0] === 1)) {
    continueButtonSelected = true;
}
if (continueButtonSelected) {
    continueRoutine = false;
}
1 Like

Hi @aisa2 and psychopy commmunity,
thanks for this code! I’m trying to include this into my own script and I am not sure I understand the code completely (I am new to programming and pyhthon…). What does this line mean?

effortRate_resp = event.Mouse(win=win)

Is this something specific to your experiment and do I need to adapt it for my own or is it a line needed in general?

@Rika Hi!
This line is just to create a mouse object called effortRate_resp. You will need a mouse object (to check if the participant has clicked on something), but you can substitute any name for effortRate_resp. Just make sure to use the new name wherever effortRate_resp appears in this code.

1 Like

Hi @aisa2, thanks for sharing this code! I’ve been trying to do this based off of other older posts, but none seem to be successful on v2020.2.2, so this is fantastic.

I’m having a weird problem where, while the continue button “works” on Builder, in that if I finish filling out the form and then press the mouse in the location of where it’s being drawn, the routine completes successfully (and doesn’t complete if I press in another location, as expected).
However, I can’t actually see the button. I’ve tried changing the background color, changing the color of the button, changing the position of the button code component within the routine to check if maybe the form & background is being drawn over it (which I’d think could result in the functionality maintaining, but the visual not being there), but I’m still not seeing it.

This makes me think it either has to do with the code in Begin Experiment tab (since the functionality works from beg. routine & each frame), or another factor in how you’ve set up the routine.

Would you mind sharing how your routine is set up/any other factors that might affect seeing the button drawn?

Thanks!

@sawal glad it could help!

I think you need to “draw” the button during each frame, something I didn’t include in the above portion as it is documented elsewhere. Here’s a more detailed version of the code I used. Note that I have created 3 logical variables that are set to false in the “begin routine” section: formAnswered, continueButtonOn, continueButtonSelected. The formAnswered became true when the participant had responded to all of the slider-scale questions on the page.

Python, begin routine:

effortRate_resp = event.Mouse(win=win)
continueButton.setAutoDraw(False)
continueButtonText.setAutoDraw(False)
continueButtonOn = False
continueButtonSelected = False
formAnswered = False

Python, each frame:

# enable button once the form is complete
if formAnswered and not continueButtonOn:
    # draw continue button <-- I think these are the lines you are looking for
    continueButton.setAutoDraw(True) 
    continueButtonText.setAutoDraw(True)
    continueButtonOn = True

# check for click in button when it is on
if continueButtonOn and effortRate_resp.isPressedIn(continueButton) and not continueButtonSelected:
     continueButtonSelected = True

# if activated button is pressed, store info & proceed
if continueButtonSelected:
    # store results and reaction times
      ## some code eliminated here

    # get out of this routine
    continueRoutine = False

JS, begin routine:

mouse = new core.Mouse({win: psychoJS.window});
continueButton.setAutoDraw(false);
continueButtonText.setAutoDraw(false);
continueButtonOn = false;
continueButtonSelected = false;
formAnswered = false;

JS, each frame:

// enable button once the form is complete
if ((formAnswered) & (!continueButtonOn)) {
    console.log("formAnswered:"); // <-- you don't need all the console.log commands, that was just for me to be able to troubleshoot when I was going online
    console.log(formAnswered);
    
    // draw continue button <-- I think these are the lines you are looking for
    continueButton.setAutoDraw(true);
    continueButtonText.setAutoDraw(true);
    continueButtonOn = true;
    console.log("continue button on");
}

//check for click in button when it is on
if ((continueButtonOn) & (!continueButtonSelected)) {
    if ((continueButton.contains(mouse)) & (mouse.getPressed()[0] === 1)) {
        continueButtonSelected = true;
        console.log("continue button selected");
    }
}

//if activated button is pressed, store info & proceed
if (continueButtonSelected) {
    //store results and reaction times
      // some code removed here
    
    //get out of this routine
    continueRoutine = false;
}

p.s. If you are curious, I tested the slider questions with the following
Py:

sliderName.getRating() is not None

JS:

(sliderName.getRating() !== undefined);
1 Like

@aisa2, thank you so much for your very quick response, and your comments for what’s doing what in the code! :slightly_smiling_face:

This mostly works in builder (haven’t tried pavlovia yet)-- if I remove “formAnswered” in line 2 of Each Frame, I can see the button, it proceeds to the next routine (however, it also remains visible for the following routines that don’t have this code, but I’ll check if that’s fixed by fixing the current issue). Keeping the “formAnswered” check in line 2 never creates the button & clicking where the button should be drawn does nothing.

Where/how does formAnswered get updated? I tried changing my form name to “form” in case that’s an internal check similar to slider.getRating(), still doesn’t work… looking around for a similar psychopy-created check for forms, it doesn’t seem to be listed as an available attribute.

Did you create/update the “formAnswered” variable elsewhere? If so, would you mind sharing how you check that a form is complete?

UPDATE:
Everything works!

For other folks, if using forms, the equivalent to check if a form is complete is:
yourForm.formComplete()

Where “yourForm” is your unique form name. And the button works as expected in that it only appears once the whole form is filled out, and clicking it proceeds to the next routine. However, it still maintains in the next routine without additional code.
To fix that, I just added:

continueButton.setAutoDraw(False) 
continueButtonText.setAutoDraw(False)

In “End Routine” to stop drawing the button & button text.

Thank you so much for your help @aisa2! You saved me a ton of time : )