Jeremy

Forum Replies Created

Viewing 15 posts - 1,171 through 1,185 (of 1,522 total)
  • Author
    Posts
  • in reply to: How to set mediaRecorder FileName #5857
    Jeremy
    Keymaster

    Hi Xavi,

    You cannot name the ZIP files yourself, and I don’t think I’ll give the option to do so in the next releases of PennController. The filenames need to be unique so the automatic uploads don’t overwrite preexisting files on the server. The current method generates a random filename which is extremely likely to be unique. Also, you have the option to asynchronously upload recordings, which can result in the creation of an indefinite number of zip files whose sample repartition is also non-deterministic.

    The names of the ZIP files are reported in the results file, so you can associate them with their corresponding submission.

    You can name the samples within the ZIP files though by giving your MediaRecorder element a name, e.g. newMediaRecorder("sample", "audio") will generate a file named sample.ogg (or sample.webm depending on the participant’s browser).

    Jeremy

    in reply to: No reading times on results file #5856
    Jeremy
    Keymaster

    Hi Noe,

    You can get the reading times by subtracting the timestamps of the two Key elements, named " " and " 2" in the results file. For example, you have a reading time of 350ms for item 24 and a reading time of 396ms for item 33.

    If you know R, you can take a look at this R script from the tutorial to get a sense of how to automatize RT calculation.

    One suggestion for your script: you could .disable your first Key element immediately after its .wait command, this way your results file wouldn’t report two keypress events on the wait of the second Key element (at that point, your first Key element is still active, which is why the single keypress gets detected twice).

    Jeremy

    in reply to: Choosing subet of items to present #5853
    Jeremy
    Keymaster

    Hi Sam,

    There probably are more optimal implementations, but how about this:

    const NBLOCKS = 24;
    const NITEMSPERBLOCK = 12;
    
    const blocks = {};
    const addItem = type => {
        if (!blocks.hasOwnProperty(type))
            blocks[type] = [...new Array(NBLOCKS)].map((v,i)=>Object({id:i, n:0}));
        let freeblocks = blocks[type].filter(v=>v.n<NITEMSPERBLOCK);
        if (!freeblocks.length) return console.error("# of items in table does not match config");
        let r = Math.floor(Math.random()*freeblocks.length);
        freeblocks[r].n++;
        return type+"-"+freeblocks[r].id;
    }
    
    Sequence(
        rshuffle("Test-0","Train-0"),"pause",
        rshuffle("Test-1","Train-1"),"pause",
        rshuffle("Test-2","Train-2"),"pause",
        rshuffle("Test-3","Train-3"),"pause",
        rshuffle("Test-4","Train-4"),"pause",
        rshuffle("Test-5","Train-5"),"pause",
        rshuffle("Test-6","Train-6"),"pause",
        rshuffle("Test-7","Train-7"),"pause",
        rshuffle("Test-8","Train-8"),"pause",
        rshuffle("Test-9","Train-9"),"pause",
        rshuffle("Test-10","Train-10"),"pause",
        rshuffle("Test-11","Train-11"),"pause",
        rshuffle("Test-12","Train-12"),"pause",
        rshuffle("Test-13","Train-13"),"pause",
        rshuffle("Test-14","Train-14"),"pause",
        rshuffle("Test-15","Train-15"),"pause",
        rshuffle("Test-16","Train-16"),"pause",
        rshuffle("Test-17","Train-17"),"pause",
        rshuffle("Test-18","Train-18"),"pause",
        rshuffle("Test-19","Train-19"),"pause",
        rshuffle("Test-20","Train-20"),"pause",
        rshuffle("Test-21","Train-21"),"pause",
        rshuffle("Test-22","Train-22"),"pause",
        rshuffle("Test-23","Train-23")
    )
    
    newTrial("pause", newButton("Take a break").print().wait() )
    
    Template( "myTable" , row =>
        newTrial( addItem(row.Type) ,
            newButton(row.Word).print().wait()
        )
    )

    This will randomly assign each of the items from your table to one of 24 (NBLOCKS) type-specific blocks of 12 (NITEMSPERBLOCK) items each. You can then write a Sequence where you randomly shuffle the items in those randomly-generated blocks, and separate each block by a pause trial.

    Jeremy

    in reply to: Exit button #5848
    Jeremy
    Keymaster

    Hi Nickolas,

    When used as an in-trial command, SendResults will save the results generated by the trials that have been completed by the time the command is executed. If your TextInput element is (created and) logged immediately before an in-trial SendResults command you shouldn’t expect it to appear in your results file.

    As an alternative solution, you could try to implement the method described in this message on a separate thread. The idea is to insert a trial that can save the results after every other trial in your experiment, but which will only actually do so if you flag a global Var element. That way you can technically complete the current trial (even if doing so by quitting) before SendResults is executed.

    Jeremy

    in reply to: multiple choice buttons (Scale element) #5843
    Jeremy
    Keymaster

    Hi,

    First let me reiterate that this method is more of a hack, and is meant to allow for unselection. Multiple parallel selections is another matter. I’ll see about adding a checkbox feature in the next release of PennController.

    In your case you’ll need to use a different method. I suggest you use a single Scale element and use a Var element at the very end of your trial to list which options are checked then. Here is how it would look like:

    newTrial("survey",
        newText("<p><b>What are your preferred gender pronouns?</b></p>")
            .print()
            .css("font-size", "16px")
            .css("font-family", "Helvetica Neue")
        ,
        newTextInput("other","")
            .log()
            .before( newText("Other: ") )
            .size(150,20)
        ,
        newScale("pronouns", "He/Him/His", "She/Her/Hers", "They/Them/Theirs", "Ze/Hir", "Other:")
            .vertical()
            .print()
            .css({"font-size": "16px", "font-family": "Helvetica Neue", "line-height": "2em"})
            .label(4, getTextInput("other") )
        ,
        newFunction( ()=> $(".PennController-pronouns input").each(function(i,e){
            $(e).attr("name","pronouns-"+i);
            $(e).click( ()=>{
                if ($(e).attr("toggle")==1) $(e).removeAttr("checked");
                $(e).attr("toggle", 1-($(e).attr("toggle")==1));
            });
        }) ).call()
        ,
        newButton("next")
            .print("center at 50%", "center at 50%")
            .css("font-size","15px")
            .wait()
        ,
        newVar("selections")
            .set( v => new Array(...document.querySelectorAll(".PennController-pronouns input"))
                .filter(e=>e.checked)
                .map(e=>e.value)
                .join("+") 
            )
            .log()
    )

    Note that I used the .label command on the Scale element, which lets you use another PennController element as a label rather than a plain string. Note also that I used the line-height CSS property to increase the spacing between the options.

    This Function element basically does the same job as the one you copied from the other topic, except that it additionally renames each of the scale’s options so they are now independent from one another, thus allowing for parallel selection (they share a single name by default, which means only one option can be selected at a time).

    The Var element does some javascript magic to see which options on the scale are checked by the end of the trial, and lists all the checked ones in a string, separated by the + character (which is purely arbitrary, just don’t use a comma because it would mess with the CSV format of the results file)

    Jeremy

    in reply to: Special character not showing #5807
    Jeremy
    Keymaster

    Hello,

    See this post for a solution

    Jeremy

    in reply to: Stuck in one trial. #5806
    Jeremy
    Keymaster

    Hi Grace,

    Your script works as intended: it shows the two pictures side by side, waits for a keypress on F or J, then reaches the end of the first trial so it moves to the next one, namely the SendResults trial, and after that it moves to the next (and final trial) where it prints two Text elements and immediately reaches the end of the trial, thus moving to a final empty screen. Just add something like newButton().wait() after printing your second Text element to fool the script into waiting on that trial forever.

    Re. your third image, I am not sure what’s happening, but people have identified a bug with the farm where, if you name a file with an uppercase extension (eg. flanker.PNG) the experiment won’t run (you get a blank screen). Also, there is no wait command for the Canvas element (see the list of commands here).

    Here is a script that should print all the images as you intend, as long as you have correspondingly named files (case-sensitive) under Resources (I don’t insert a linebreak before single commands, but you can keep them there if you prefer):

    newTrial(
        newText("the cat jumped over the moon in pajamas").print()
        ,
        newImage("one", "thecatinpajamas.png").size(200,200)
        ,
        newImage("two", "themooninpajamas.png").size(200.200)
        ,
        newCanvas("onetwo", 450,400)
            .settings.add( 0, 0 , getImage("two") )
            .settings.add(250, 0, getImage("one") )
            .print()
        ,
        newKey("FJ").wait()
        ,
        getCanvas("onetwo").remove()
        ,
        newImage("flanker.png")
            .size(200.200)
            .print()
        ,
        newButton("Next")
            .print()
            .wait()
    )
        
    SendResults()
    
    newTrial(
        newText("Thank you for your participation!").print()
        ,
        newText("Click here to validate your participation").print()
        ,
        newButton().wait()
    )

    Jeremy

    in reply to: Required fields in form #5799
    Jeremy
    Keymaster

    Hi,

    [ I created a new topic for this question, as it does not relate to self-paced reading tasks ]

    How did you add those boxes: as part of an Html document, or as TextInput elements?

    If you are using an Html document, then you need to give the obligatory class to your required elements, as described in the original Ibex documentation. Then you can use the .test.complete command on your Html element.

    If you are using a TextInput element, then you’ll want to use the .test.text command on it to check it’s not empty, e.g.: .test.text(/\w+/)

    Checkboxes have not yet been implemented in PennController, so the simplest solution at this point would be to use an Html document

    Jeremy

    in reply to: Maze task #5795
    Jeremy
    Keymaster

    Hi Susanne,

    The Maze controller, being designed for native Ibex, handles feedback the native-Ibex way: it flags a value for the next controller to check. The command failure is a PennController command that is only defined in the context of a .test command, the only test command on a Controller element being the standard printed.

    The good news is that you can easily add a custom PennController test command. Add this to your script:

    _AddStandardCommands(function(PennEngine){
        this.test = {
            passed: function(){
                return !PennEngine.controllers.running.utils.valuesForNextElement ||
                        !PennEngine.controllers.running.utils.valuesForNextElement.failed
            }
        }
    });

    Then you can do this:

    Template("training.csv", row =>
        newTrial("training",
    
            newController("Maze", {s: row.Sentence, a: row.Distractor})
                .print()
                .log()
                .wait()
                .remove()
                .test.passed()
                .failure( newText("oops!").print() )
            ,
            newTimer(500).start().wait()
        )
    )

    Jeremy

    in reply to: Restricting to desktop #5793
    Jeremy
    Keymaster

    Alex Drummond wrote this in utils.js: var isIPhone = navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i);

    And the code of DashedSentence contains a conditional on isIPhone to print the “Next” button. I guess the longer term idea was to expand the check to any mobile browser (remember that Alex started writing ibex in the early 2010s)

    Jeremy

    in reply to: Maze task #5790
    Jeremy
    Keymaster

    Hello Susanne,

    Unfortunately you cannot use a Selector element from PennController to modify the behavior of a controller like Maze that was designed for Ibex. The code you posted will execute the maze and, once the whole sentence has been read or a wrong word has been selected (ie once your first wait command has been released), the script will then reach the Selector element and wait for its completion. So as you can see they are two distinct elements. (you also shouldn’t use a Selector without adding any element to select, but that’s a different issue)

    I looked at the code for Maze (in Maze.js) and the e/i keys are hard-coded there, so if you want to use this controller you’ll need to edit the code. It’s not very complicated: replace the two occurrences of 69 with 37 and the two occurrences of 73 with 39. You’ll also want to replace

    this.larrow.html("e");
    this.rarrow.html("i");

    with

    this.larrow.html("←");
    this.rarrow.html("→");

    Oh and simply delete the file sample.js that comes with the repo, or overwrite its content with your own code.

    Let me know if you have questions

    Jeremy

    in reply to: Not sending results #5787
    Jeremy
    Keymaster

    Hi Irene,

    Thank you for the feedback. These 500 and 502 errors mean that there were problems with the server, which is a little concerning in itself, but the good news is that there is no problem on your side. Hopefully these errors won’t occur often, and clicking “Retry” will take care of it.

    Jeremy

    in reply to: Removing Canvas after completion of Dashed Sentence #5783
    Jeremy
    Keymaster

    Hi Zach,

    You are waiting for the Canvas element to complete, which won’t happen because Canvas elements define no events. You want to wait for the Controller element to complete instead:

    newCanvas("sprcanv", 700, 700)
        .center()
        .add( 0, 0, getImage("sb1"))
        .print()
    ,
    newController("DashedSentence", {s: row.SentenceText, display: "in place", blankText: "+"})
        .print( 20, 40 , getCanvas("sprcanv") ) 
        .log()
        .wait()
        .remove()
    ,
    getCanvas("sprcanv").remove()

    Jeremy

    in reply to: Not sending results #5781
    Jeremy
    Keymaster

    Hi Irene,

    It’s a problem with the MouseTracker element then. I think it happens when you exit the experiment before the trial has reached the line newMouseTracker("mouse"). Try moving this line at the top of the trial (i.e. after newTrial("test_trial",) and of course use getMouseTracker("mouse") instead where the line currently is.

    Let me know if this fixes the problem

    Jeremy

    in reply to: Output Text based on Selection in Dropdown Menu #5777
    Jeremy
    Keymaster

    Hi Andreas,

    You’re mixing up plain javascript code and PennController code. The PennController test commands are meant to be used autonomously (ie. as a block of commands separated by commas, or inside a callback command for example, just like a newText command for example) not inside a javascript triconditional like this. You need to use success and/or failure so you can execute PennController commands conditionally. So this is what you second trial should look like:

    newTrial("check",
        newText("optionA").print(),
        newText("optionB").print()
        ,
        getVar("handedness").test.is("left")
            .success( getText("optionA").text("Option1"),getText("optionB").text("Option3") )
            .failure( getText("optionA").text("Option2"),getText("optionB").text("Option4") )
        ,
        newButton("Next")
            .print()
            .wait()
    )

    Jeremy

Viewing 15 posts - 1,171 through 1,185 (of 1,522 total)