Forum Replies Created
-
AuthorPosts
-
Jeremy
KeymasterHi 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 theset
command the way you’ve tried, because it actually is in a non-PennController environment (which incidentally also makes it possible to useDate.now()
upon runtime). If you really need to access the value of the Var element there, you need to usegetVar("sentenceDisplayTime")._element.value
.Let me know if you have more questions!
Jeremy
Jeremy
KeymasterAh, 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.
Jeremy
KeymasterHi,
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
Jeremy
KeymasterHi 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 5 years, 8 months ago by
Jeremy.
Jeremy
KeymasterHi 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 5 years, 8 months ago by
Jeremy.
Jeremy
KeymasterHi!
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
Jeremy
KeymasterEDIT: 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
getScale
—not withnewScale
):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 5 years, 5 months ago by
Jeremy. Reason: Command added to PennController 1.6
Jeremy
KeymasterHi 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
Jeremy
KeymasterHi 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.
Jeremy
KeymasterMy “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
Jeremy
KeymasterHi 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 labeledtypeA
, two labeledtypeB
and two labeledtypeB
. UsingPennController.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
ortypeC
that does not contain any pair of trials successively labeledtypeA
thentypeC
.Jeremy
Jeremy
KeymasterSorry, missed a
'
betweenitalic;
and>
Jeremy
Jeremy
KeymasterHi Mogli,
Did you add the command
getScale("akzeptabilität").remove()
after thewait
command on the button?Jeremy
Jeremy
KeymasterHi 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
Jeremy
-
This reply was modified 5 years, 8 months ago by
Jeremy. Reason: Missed a '
Jeremy
KeymasterHi!
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
-
This reply was modified 5 years, 8 months ago by
-
AuthorPosts