Text responses in JavaScript: Dialogue boxes

Description of the problem:
I have a Pychopy experiment in which participants need to click on a figure to make a Dialogue Box appear on screen. I defined a function that is called when a person clicks on a figure. It runs well locally in Python. However, when running it online in Pavlovia, the translated Javascript code shows an error saying “psychoJS.gui.Dlg is not a constructor”.

The code in Python (at the beginning of th eexperiment)

def showTextBox(textAnimal, textOrder):
    textBox = gui.Dlg(title='Type your response here')
    textBox.addField('Animal: ', textAnimal)
    textBox.addField('Order: ', textOrder)
    textBox.show()
    if textBox.OK:
        textAnimal = textBox.data[0].lower()
        textOrder = textBox.data[1].lower()
        return textAnimal, textOrder
    else:
        return textAnimal, textOrder

The code in JavaScript (at the beginning of th eexperiment)

showTextBox = function(textAnimal, textOrder) {
    var textBox;
    textBox = new psychoJS.gui.Dlg({title: "Type your response here"});
    textBox.addField("Animal: ", textAnimal);
    textBox.addField("Order: ", textOrder);
    textBox.show();
    if (textBox.OK) {
        textAnimal = textBox.data[0].toLowerCase();
        textOrder = textBox.data[1].toLowerCase();
        return [textAnimal, textOrder];
    } else {
        return [textAnimal, textOrder];
    }
}

The error I get in Pavlovia:

Which is the proper way to create a new Dialogue Box using JavaScript??

I haven’t find a Psychopy reference guide for translating different functions and methods form Python to JavaScript. Does it exist? Having one would be of great help and would save people a lot of trouble.

@miguel_santin, try following to create and present dialog boxes

// Create dialog entries
myFields = {"Field": "Enter text here", "Selection": [1,2]};
myDlgTitle = "The Dlg Title";

// Build dialog
myDialog = psychoJS.gui.DlgFromDict({
  dictionary: myFields,
  title: myDlgTitle
});

// Present dialog
myDialog();

@dvbridges, Thank you for the help. I managed to use your suggestion to adapt the function I had in Python to also make a Dialogue Box appear correctly when a person clicks on a shape when testing online, however, I am stuck in two things:

  1. The box accepts text input but it does not close when clicking on “Ok” or “Cancel”. It only closes when clicking on the “x” on the top right corner.

  2. How to update the values of the variables in its argument when people click on the “OK” button.

I have read the documentation, the source code, the compiled script of the expeirment and googled about it, but I cant figure out why the box is not closing and how to use the input values of the Dialogue Box’s dictionary to update the variables used as arguments in the function to return them in case “OK” was clicked. This is the JavaScript code of the function I have right now:

showTextBox = function(textAnimal, textOrder) {
    var textBox;
    let boxTitle = 'Type your response here';
    let boxFields = {"Animal": textAnimal, "Order": textOrder};
    textBox = psychoJS.gui.DlgFromDict({
        dictionary: boxFields,
        title: boxTitle
        });
    
    // Present dialog
    textBox();
    
    if (psychoJS.gui.dialogComponent.button === 'OK') {
        textAnimal = textBox.data[0].toLowerCase();
        textOrder = textBox.data[1].toLowerCase();
        return [textAnimal, textOrder];
    } else {
        return [textAnimal, textOrder];
    }
}

I tried deleting the conditional statement to see if it was somehow preventing that the Dialogue Box could not be closed with the “Ok” and “Cancel” buttons but I had the same result: It is not possible to close it once it is opened.

Do you know what I am doing wrong so to solve bulletpoint 1 and 2?

After a whole day of trying to figure out what is wrong and how to solve it… I failed miserably.

The only things I was able to notice by using the console.log() for debugging are two things:

  1. Once the Dialogue Box appears the code set for every frame keeps running repeatedly instead of pausing the execution of the code. Conversely, when the experiment is ran locally in Python, the execution of the code is paused while the Dialogue Box is open.

  2. If the code crashes just after the Dialogue Box opened (before starting the loop execution of the code in “every frame”, the “OK” and “Cancel” buttons do work and close the box adequately. The “X” button works well all the time.

  3. The value of “psychoJS.gui.dialogComponent.button” right before creating and showing the Dialogue Box is OK, and its value right after the box is created and shown changes to cancel.

No matter what I tried, I was not able get the default values assigned to the “dictionary” argument for create the new Dialogue Box with “textBox()”.

@dvbridges, Any ideas?

Oh, I forgot to paste the link to the experiment in Pavlovia: https://pavlovia.org/miguel_santin/working-memory-task

@dvbridges After a second day of going deep in the source code and trying debug my code, I figued out that in fact the core problem is that the "OK and “Cancel” buttons do not work, only the “X” button.

@dvbridges NEW UPDATE

Today I was able to understand better how the source code of the Dialog Box works. It seems that when a new Dialogue Box is created the previous one needs to be manually destroyed in order to make the buttons work again. There is a function in GUI source code which is in charge of doing this, however, this function is only called automatically when a new Message Box is created, but not when a Dialogue Box is created. So calling this function manually solved the problem with the buttons.

Nevertheless, this led to another problem. When the brand new Dialogue Box was called to show on screen, although the buttons worked, the “OK” button was disabled. To solved this I had to manually activate the button with code from the GUI source code as well. This seems to have solved the problem.

Additionally, the progress bar of the Dialogue Box was visible so I hid it manually.

Bellow is the JavaScript code that I used. There is only one last mystery to solve. How do I adapt the code to update the value of the arguments of my when a person clicks on “OK”?

  showTextBox = function(textAnimal, textOrder) {

      //Destroy the Old Dialog Box
      psychoJS.gui.destroyDialog();

      let boxTitle = 'Type your response here';
      let boxFields = {"Animal": textAnimal, "Order": textOrder};
     
      var myBox = psychoJS.gui.DlgFromDict({
          dictionary: boxFields,
          title: boxTitle
          });
      
      //Run the new Dialogue Box
      myBox();

      //Enable the "OK" button
      $("#buttonOk").button("option", "disabled", false);
      $("#buttonOk").hide(0, () => { $("#buttonOk").show(); });

      //Hide the progress bar
      $("#progressbar").hide();
      
      return [textAnimal, textOrder];
  }

Fianaly, after a lot of work I managed to solve the issues. The full approach is to use a JQueries to access and customize a Dialogue Box. The code works well while a new version of Psychopy arrives with new features that make life easier for everyone.

For anyone interested in the solution, the function in JavaScript below creates and displays a Dialogue Box. The function takes two arguments as default text for the input fields. When people click on the “OK” button, the text they typed in the input fields is used to update the value of those variables.

Code at the beginning of the experiment:

  showTextBox = function(text1="", text2="") {

      //Destroy the Old Dialog Box
      psychoJS.gui.destroyDialog();


      //Create new Dialoge Box
      let boxTitle = 'Title of the dialogue box';
      let boxFields = {'Label1': text1, 'Label2': text2};
     
      var myBox = psychoJS.gui.DlgFromDict({
          dictionary: boxFields,
          title: boxTitle
          });
      
      //Run the new Dialogue Box
      myBox();

      //Enable the "OK" button
      $("#buttonOk").button("option", "disabled", false);
      $("#buttonOk").hide(0, () => { $("#buttonOk").show(); });

      //Hide the progress bar
      $("#progressbar").hide();
      
     //When "OK" is pressed... 
      $("#buttonOk").click( function() {
          var Label1_Input = document.getElementById("Label1" + "_id");
          var Label2_Input = document.getElementById("Label2" + "_id");
          
          // Variables that will be updated
          myText1 = Label1_Input.value;
          myText2 = Label2_Input.value;            
       }

     //When "CANCEL" is pressed
      $("#buttonCancel").click( function() {
       }

How to call the function during the experiment:

var myText1 = "Default Text";
var myText2 = "Default Text";

showTextBox(myText1, myText2);

** Notice that this function cannot return the updated values of the input fields as it finishes running way before the people are able to type something and click on “OK”. The function in charge of updating the value of the variables is a JQuery called when the “OK” button is pressed.

Hi @miguel_santin this is very helpful. I’m trying to do something similar in the hope that a dialogue box will potentially allow participants to complete tasks using an ipad or tablet (currently, the keyboard doesn’t ‘automatically’ appear on tablets when text responses are required, not sure if this makes sense). But, anyway, I wondered if you would be willing to provide a link to the code via pavlovia?
Many thanks!

@KtS Sure, no problem. Here is the link to the Pavlovia GitLab of the project. I hope it comes handy. You should start a new thread with the name “Dialogue Boxes on Tablets”. I am sure it will later be interesting and helpful for some other people as well :slight_smile:

1 Like

There seems that there have been some change sin the PsychoJS source code since I found the workaround. This made the fix I found to stop working.

After some time trying to figure out what changed and how to solve the problem once more, I found out that the PsychoPy team had solved the problem with the destruction of the Dialogue Boxes. Now it happens automatically when one of the “Ok”, “Cancel” or “X” buttons is pressed.

This made the function … document.getElementById(); … to unusable to get the values typed in the input values. As the Dialogue Box is now destroyed automatically, the function can no longer find the IDs of the input fields.

However, this helped me figure out an other way, which I think is the proper way, of getting the values typed in the input fields. Here is the new working code:

showTextBox = function(text1="", text2="") {
      
      //Create new Dialoge Box
      let boxTitle = 'Title of the dialogue box';
      let boxFields = {'Label1': text1, 'Label2': text2};
     
      var myBox = psychoJS.gui.DlgFromDict({
          dictionary: boxFields,
          title: boxTitle
          });
     
      //Run the new Dialogue Box
      myBox();

      //Enable the "OK" button
      $("#buttonOk").button("option", "disabled", false);
      $("#buttonOk").hide(0, () => { $("#buttonOk").show(); });

     //Hide the progress bar
      $("#progressbar").hide();
      
     //When "OK" is pressed... 
      $("#buttonOk").click(function() {

            // Variables that will be updated
            var Label1_Input = boxFields["Label1"];
            var Label2_Input = boxFields["Label2"];
      });

     //When "CANCEL" is pressed
      $("#buttonCancel").click(function() {
      });

     //When the "X" is pressed to close the box
      $(".ui-dialog-titlebar-close").click(function() {
      });
}

** Notice that this function cannot return the updated values of the input fields as it finishes running way before the people are able to type something and click on “OK”. The function in charge of updating the value of the variables is a JQuery called when the “OK” button is pressed.

1 Like

For how to create dialogue boxes in psychoPy using Python, please visit this other post:

This is stange… the moment I embed my own dialogbox in a function the text input fields become uneditable? I’m using you exact code. Any ideas on what might be causing that?

@Tamas_Andrei_Foldes

PsychoPy regularly experience several important updates across the year. It’s possible that something in the newer versions is incompatible with the code I used to create the dialogue boxes. About a month ago or two I used PsychoPy 2020.2.9 to collect data with that same code through the dialogue boxes. Nevertheless, it’s important to tell that some participants experienced problems. I was not able to track down the problem, although I recommend the use of the latest versions of Firefox to collect data. I received a few complains regarding all browsers in Windows and Mac but not for Firefox.

Try using the version 2020.2.9 of PsychoPy. You can change the version of the PsychoPy script to be used in the experiment settings.

Otherwise, you can take a look at the source code and try to figure out how to update the code I used so that it works with the new version of PsychoPy. People in the future would be glad if you find a solution and post it here.

Miguel

Hi @miguel_santin

Thank you that is useful tip, I am currently using the newest one. I have a hypothesis of what is going on… when the initial Psychopy “built-in” dialogbox pops up at the beginning everything works fine, people can type in their answer - however I altered your code such that I check whether their participant code is “valid” - in an array of valid codes.

If it is not I give people a JS alert and re-present the dialogbox. However something interesting happens; when people make an error in typing their participant code and the “re-presentation” is triggered the experiment seems to have started in the background and my hypothesis that some sort of high-frequency sampling is taking place on the screen. The reason I think this is because if press the button for long you can see the characters appearing, but single-button presses don’t appear.

This is the code I am using in the “Begin Experiment” section of the Code component of the first Routine.

let validParticipantCodes = ['QTCMDY','3WCM98','NUW7V3','ZGCQ0E','K0111K']

window.validParticipantCodes = validParticipantCodes
        
var showTextBox = function(text1=""){
      
      //Create new Dialoge Box
      let boxTitle = 'Details';
      let boxFields = {'participant': text1, 'session': '001'};
     
      var myBox = psychoJS.gui.DlgFromDict({
          dictionary: boxFields,
          title: boxTitle
          });
     
      //Run the new Dialogue Box
      myBox();

      //Enable the "OK" button
      $("#buttonOk").button("option", "disabled", false);
      $("#buttonOk").hide(0, () => { $("#buttonOk").show(); });

     //Hide the progress bar
      $("#progressbar").hide();
      
     //When "OK" is pressed... 
      $("#buttonOk").click(function() {

            // Variables that will be updated
            var Label1_Input = boxFields["participant"];
            var Label2_Input = boxFields["Session"];
            window.Label1_Input = Label1_Input;
            window.Label2_Input = Label2_Input;
            
            if(!validParticipantCodes.includes(boxFields["participant"])){
                showTextBox(Label1_Input, Label2_Input);
                alert('Warning! Participant code is not valid');

                }else{expInfoManual['participant']=Label1_Input}
      });

     //When "CANCEL" is pressed
      $("#buttonCancel").click(function() {
        core.quit()
      });

     //When the "X" is pressed to close the box
      $(".ui-dialog-titlebar-close").click(function() {
        core.quit()
      });
}

showTextBox(expInfo['participant']);