Jeremy

Forum Replies Created

Viewing 15 posts - 1,471 through 1,485 (of 1,522 total)
  • Author
    Posts
  • in reply to: Variable Updating #4138
    Jeremy
    Keymaster

    Hi Leo,

    It’s hard to tell without seeing your code, but I suspect that the replay makes it so that ultimately, in the sequential flow of commands in your experiment, the command .set( v => Date.now() ) gets executed more than once, and the last time it is executed happens after the last time .set( v => v - Date.now() ) is executed.

    Are you trying to record the RT both for the first pass and for the replay? Or only for the replay? In any case, it would probably be cleanest to just use different variables on the two different occasions, e.g. (adapting the code from this topic):

    newText("contextText", "Look at the square below").print(),
    newCanvas("square", 200,200).settings.css("background","red").print(),
    // Var element for the first RT
    newVar("RTfirst", 0).settings.log().set(v => Date.now()),
    newButton("ok", "OK").print().wait().remove(),
    newText("instructions", "How red is it?").print(),
    newScale("firstScale", 7).settings.log().print().wait(),
    getVar("RTfirst").set(v => Date.now() - v),
    newButton("Replay").print().wait().remove(),
    // Removing everything still on screen
    getText("contextText").remove(),
    getCanvas("square").remove(),
    getText("instructions").remove(),
    getScale("firstScale").remove(),
    // Wait 500ms before reprinting
    newTimer(500).start().wait(),
    getText("contextText").print(),
    // Var element for the second RT
    newVar("RTsecond", 0).settings.log().set(v => Date.now()),
    getCanvas("square").print(),
    getButton("ok").print().wait().remove(),
    getText("instructions").print(),
    // Print a new scale
    newScale("secondScale", 7).settings.log().print().wait(),
    getVar("RTsecond").set(v => Date.now() - v)

    Unfortunately you cannot use getVar after => in the set command the way you’ve tried, because it actually is in a non-PennController environment (which incidentally also makes it possible to use Date.now() upon runtime). If you really need to access the value of the Var element there, you need to use getVar("sentenceDisplayTime")._element.value.

    Let me know if you have more questions!

    Jeremy

    in reply to: Group Columns – distribution #4135
    Jeremy
    Keymaster

    Ah, sorry, I didn’t realize fillers were not grouped. Then no, you don’t have to duplicate the rows, just make sure you don’t have a Group column in you fillers table so Template doesn’t exclude any row.

    in reply to: Group Columns – distribution #4132
    Jeremy
    Keymaster

    Hi,

    It sounds like you can use a single CSV file within a single project indeed.

    1. Yes, see this page from the tutorial for a detailed discussion.

    2. All should be fine as long as you specify the right table for each Template command, and as long as your tables contain the same set of values in their Group columns (since PennController 1.4). That is, if trials are generated from rows where Group is A is table 1, trials will be generated from rows where Group is A in table 2.

    3. Yes, I think it would be more reliable and this is what I usually do: I collect one batch of participants in one group, then update the URL and collect the next batch of participants in another group.

    Let me know if you have more questions

    Jeremy

    in reply to: Un-select and storing mid-trial variables #4127
    Jeremy
    Keymaster

    Hi Leo,

    Thanks for the feedback. What do you think should be a consistent behavior? Should there always be two lines in the results file when you pass both “first” and “last”? If so, should there be a value somewhere in the line reporting “first” / “last” / “first-last” (in case only one selection happens) / “NA” (in case no selection happens)?

    In your case, as I was suggesting earlier, I would advise using a different scale on replay if it’s an option: this way, the two lines in the results file will have different names (the code from my previous message will generate two lines: one named ‘firstScale’ and one named ‘secondScale’). It will also ensure you always have two lines (Choice will read NA if no selection ever happened on a logged scale).

    Oh and also, just a reminder: “first” means the very first time the participant clicks the scale. This means that if your participant clicks on the scale more than once before validating the first presentation of the scale, and then goes on to replaying everything and clicks again on the (now unselected) scale, passing “first” to log will not log the value selected on the scale when the participant validated the first presentation (instead, it will log the first value that was selected, before they changed their mind). One more reason to use two different Scale elements.

    Thanks for the note on the button.wait page, I fixed the code.

    Let me know if you have any questions,

    Jeremy

    • This reply was modified 4 years, 8 months ago by Jeremy.
    in reply to: Group Columns – distribution #4122
    Jeremy
    Keymaster

    Hi Mogli,

    Ibex cannot know in advance how many participants you will end up running, so it cannot automatically assign groups based on your total number of participants.

    Each participant is associated with one value from the Group column and only sees trials generated from rows with that value in the Group column. How that value is picked depends on how you set your experiment, as described in the original Ibex documentation. By default, PennController uses Ibex’s internal counter: in your case, your first participants would be assigned group A (the first value of the Group column in your spreadsheet) and after the first participant has completed your experiment, the next participant who takes the experiment would be assigned group B. After the second participant completes your experiment, the next participant taking the experiment would be assigned group C, etc.
    If you like the internal counter method but would rather have the counter incremented as soon as a participant starts the experiment, you can use PennController.SetCounter.

    You can replace experiment.html with server.py?withsquare=N in your experiment’s URL to force running the experiment in a specific group, where N is a number, from 0 to 15 in your case (see Ibex’s documentation).

    Let me know if you have any questions

    Jeremy

    • This reply was modified 4 years, 8 months ago by Jeremy.
    in reply to: choosing values in the result sheet #4121
    Jeremy
    Keymaster

    Hi!

    Options to customize the results file are limited at the moment—I’ll look into improving that

    The _Trial_,Start and _Trial_,End lines are automatically included by PennController: they help you get a sense of the experiment’s timeline. This way you could tell whether, say, a participant’s computer was being very slow.

    Text elements, like any PennController element, only add a line to the results file if you use the command .setting.log on them.

    What you can do, in absence of an option to prevent the _Trial_ lines from logging, is filter your results file with your spreadsheet (you can use a filter like “Column Parameter does not contain _Trial_”) or you can parse it using a script to output a new file containing only the lines you are interested in.

    Let me know if you have any questions

    Jeremy

    in reply to: Unselect Scale #4116
    Jeremy
    Keymaster

    EDIT: the unselect command was introduced in PennController 1.6.

    Thanks for posting a request here Leo,

    As a workaround, you can add this at the top of your script to add the command unselect (will only work with getScalenot with newScale):

    var oldGetScale = getScale;
    getScale = name => {
        let t = oldGetScale(name);
        t.unselect = ()=>{
            let e = t._element;
            t._promises.push(()=>new Promise(resolve=>{
                if (e.scaleType == "buttons")
                  e.table.find("td.PennController-"+e.type+"-scaleButton").css("outline","");
                else if (e.scaleType == "slider"){
                  let r = e.table.find("input[type=range]")[0];
                  r.value = (r.max - r.min) / 2;
                }
                else
                  e.table.find(".PennController-"+e.type+"-scaleButton input[type=radio]").removeAttr("checked");
                resolve();
            }));
            return t;
        };
        return t;
    }; 

    Jeremy

    • This reply was modified 4 years, 5 months ago by Jeremy. Reason: Command added to PennController 1.6
    in reply to: Un-select and storing mid-trial variables #4114
    Jeremy
    Keymaster

    Hi Leo,

    (1) It’s definitely an option that’s missing for the Scale element, all there is at the moment is select

    (2) You can log all the selections that happen on a scale element, see scale.settings.log

    In your case, since the unselect command is missing, I would probably just create a new Scale element on replay, e.g.:

      newText("contextText", "Look at the square below").print(),
      newCanvas("square", 200,200).settings.css("background","red").print(),
      newButton("ok", "OK").print().wait().remove(),
      newText("instructions", "How red is it?").print(),
      newScale("firstScale", 7).settings.log().print().wait(),
      newButton("Replay").print().wait().remove(),
      // Removing everything still on screen
      getText("contextText").remove(),
      getCanvas("square").remove(),
      getText("instructions").remove(),
      getScale("firstScale").remove(),
      // Wait 500ms before reprinting
      newTimer(500).start().wait(),
      getText("contextText").print(),
      getCanvas("square").print(),
      getButton("ok").print().wait().remove(),
      getText("instructions").print(),
      // Print a new scale
      newScale("secondScale", 7).settings.log().print().wait()

    Let me know if you have any questions

    Jeremy

    in reply to: Conditional Randomization #4111
    Jeremy
    Keymaster

    Hi Leo,

    Since your example uses the exact same template for all your trials, I would simply call Template once, like this:

    PennController.Template( "items.csv" ,
        item => PennController(
            newButton("thisTrialButton", item.text)
              .print()
              .wait()
        )
    )

    No need to explicitly label your trials, since your table already contains a Label column.

    in reply to: Conditional Randomization #4108
    Jeremy
    Keymaster

    My “use it like this” code is just a standard PennController example, with dummy one-button trials. I just use ‘i’ and ‘ii’ so you can identify the trial from the buttons’ text when you take the experiment.

    Using a template shouldn’t make a difference: just label your template-generated trials consistently with your definition of the sequence of trials (see the other post on how to label template-generated trials—the simplest solution is to have a Label column in your table).

    The command PennController.Sequence is just the standard PennController way of defining the sequence of trials, it replaces and standardizes the more obscure var shuffleSequence = native-Ibex method. You cannot use .label on it since it’s not used to create trials, but just order the ones defined elsewhere in your script, based on their labels.

    Jeremy

    in reply to: Conditional Randomization #4106
    Jeremy
    Keymaster

    Hi Leo,

    Playing with the order of trials is probably Ibex’s least handy aspect. If you look up Ibex’s documentation manual, you’ll see that the function rshuffle in part does what you want: it makes sure that specifically labeled trials are evenly spaced. Say you have 6 trials in your experiment, two labeled typeA, two labeled typeB and two labeled typeB. Using PennController.Sequence( rshuffle("typeA", "typeB", "typeC") ) will give you one of these six possible patterns:

    • typeA, typeB, typeC, typeA, typeB, typeC
    • typeA, typeC, typeB, typeA, typeC, typeB
    • typeB, typeA, typeC, typeB, typeA, typeC
    • typeB, typeC, typeA, typeB, typeC, typeA
    • typeC, typeA, typeB, typeC, typeA, typeB
    • typeC, typeB, typeA, typeC, typeB, typeA

    I understand your request to be slightly different though. You don’t necessarily want to define a specific pattern that should repeat itself, you would be okay with having C, B, A in the first block and A, B, C in the second block as long as, say, no C follows an A directly. Am I right3

    One of the problems with implementing a general solution to such a request is that, depending on how many trials you have per label, what you ask may or may not be feasible. So you need to make sure, as the designer, that your set of trials are labeled in a way that makes it possible to create a sequence with the desired properties. Then you could define this function at the top of your script:

    function RandomizeExcludeSequence(ar, type1, type2) {
      this.args = ar;
    
      this.run = function(arrays) {
          let sequence = arrays[0];
          let shuffle = true;
          while (shuffle){
            shuffle = false;
            fisherYates(sequence);
            let prev = "", next = "";
            for (let i = 0; i < sequence.length; i++){
              if (i>0) prev = sequence[i-1][0].type;
              next = sequence[i][0].type;
              if (prev==type1&&next==type2){
                shuffle = true;
                break;
              }
            }
          }
          return sequence;
      }
    }
    function randomizeExcludeSequence(ar, type1, type2) { return new RandomizeExcludeSequence([ar], type1, type2); }

    and use it like this:

    PennController.Sequence( randomizeExcludeSequence( anyOf("typeA", "typeB", "typeC") , "typeA", "typeC" ) )
    
    PennController("typeA", newButton("typeA i").print().wait() )
    PennController("typeA", newButton("typeA ii").print().wait() )
    PennController("typeB", newButton("typeB i").print().wait() )
    PennController("typeB", newButton("typeB ii").print().wait() )
    PennController("typeC", newButton("typeC i").print().wait() )
    PennController("typeC", newButton("typeC ii").print().wait() )

    This generates any random sequence using all the trials labeled typeA, typeB or typeC that does not contain any pair of trials successively labeled typeA then typeC.

    Jeremy

    in reply to: Aesthetics in the middle of text #4101
    Jeremy
    Keymaster

    Sorry, missed a ' between italic; and >

    Jeremy

    in reply to: Trial templates & tables #4086
    Jeremy
    Keymaster

    Hi Mogli,

    Did you add the command getScale("akzeptabilität").remove() after the wait command on the button?

    Jeremy

    in reply to: Aesthetics in the middle of text #4085
    Jeremy
    Keymaster

    Hi Leo,

    You could use HTML tags to apply a style to a portion of your text. Usually browsers render <em> as italics, and the tag <i> is sort of deprecated. The most reliable, universal solution would probably be <span style='font-style: italic;'> like this:

    newText("practiceinstructions", "We would not typically describe this scene using this sentence, because \"at least two\" leads us to expect more than two apples. You should <span style='font-style: italic;'>not</span> rate it as 6 or 7.")

    More generally though, when you want a trailing white space, you can add a non-breaking space using &nbsp;

    Jeremy

    • This reply was modified 4 years, 9 months ago by Jeremy. Reason: Missed a '
    in reply to: obligatory TextInput #4081
    Jeremy
    Keymaster

    Hi!

    The code you report mixes Html and TextInput elements, when I presume you’d rather use only one type of element. Html elements are used exclusively to embed the content of an Html file that you have uploaded to your project, and I doubt that you have an Html file named “Background Information.” The complete test checks all the fields with the class obligatory in the corresponding file have been completed. This is not the test to use in the code you report since there is no “Background Information” Html file to start with.

    What you want is a test on your TextInput element: you want to make sure it is not empty. To do so, you need to use the text test on your TextInput element (or, rather, its negation) like this:

    newText("Date of Birth: (MM/DD/YYYY) After entering your date of birth in the box below, press the Return/Enter key.")
      .print()
    ,
    newTextInput("background")
      .print()
      .wait( getTextInput("background").testNot.text("") )

    NB: in your case, because you want a date in a specific format, you could even test the text using a regular expression: getTextInput("background").test.text(/^\d{2}\/\d{2}\/\d{4}$/)

    Let me know if you have more questions

    Jeremy

Viewing 15 posts - 1,471 through 1,485 (of 1,522 total)