Forum Replies Created
-
AuthorPosts
-
Jeremy
KeymasterHi,
The two
newTrial
commands in your code are embedded inside aPennController
command —PennController()
is the older name ofnewTrial()
, so you’re effectively passing PennController trials as arguments to another PennController trial, which won’t workHere is what you can do instead:
newTrial("age" , newVar("over18", false).global() , newText("age", "<p>Are you 18 years of age or older?</p>").print() , newButton("yes", "Yes, I am 18 years old or older.").print() , newButton("no", "No, I am under 18 years old.").print() , newSelector("yesno") .add( getButton("yes") , getButton("no") ) .wait() .test.selected( getButton("yes") ) .success( getVar("over18").set(true) ) ) newTrial( "*sent" , getVar("over18").test.is(true).success( newHtml("consent", "Consent-Online.html").css("margin-bottom", "2em").print() , newButton("By clicking this button I indicate my consent") .print() .wait() ).failure( newHtml("assent", "Assent-Online.html").css("margin-bottom", "2em").print() , newButton("By clicking this button I indicate my assent") .print() .wait() ) )
Jeremy
Jeremy
KeymasterHi,
You could print the image onto a Canvas element, and manipulate that one Canvas element instead—I don’t think there’s anything you can do on an Image element you cannot do on a Canvas element:
Template("fullList.csv", variable => newTrial("demo", newText("head", " ") .text(getVar("asdf")) .before(newText("b","").text(getVar("namelist")).after(newText(": "))) .center() .print() , newCanvas("image").center().print() , getVar("namelist") .test.is("P1A").success( newImage(variable.P1A).print(getCanvas("image")) ) .test.is("P1B").success( newImage(variable.P1B).print(getCanvas("image")) ) .test.is("P1C").success( newImage(variable.P1C).print(getCanvas("image")) ) .test.is("P2A").success( newImage(variable.P2A).print(getCanvas("image")) ) .test.is("P2B").success( newImage(variable.P2B).print(getCanvas("image")) ) .test.is("P2C").success( newImage(variable.P2C).print(getCanvas("image")) ) .test.is("P3A").success( newImage(variable.P3A).print(getCanvas("image")) ) .test.is("P3B").success( newImage(variable.P3B).print(getCanvas("image")) ) .test.is("P3C").success( newImage(variable.P3C).print(getCanvas("image")) ) , newButton("Flicker") .center() .print() .wait() .remove() , getCanvas("image").remove() , newTimer(2000).start().wait() , getCanvas("image").print() , newButton("Finish").center().print().wait() ) )
Jeremy
Jeremy
KeymasterHi,
Javascript is immediate, PennController is delayed. More specifically, you cannot dynamically pass values to
newImage
(also, how would PennController be able to preload the image file if it had to wait until the trial starts to know which file to use?)What happens in your case is, as soon as the experiment page is open, all the javascript code is executed immediately (top down). First it creates the
varForImg
variable, it creates thesetvarForImg
andgetvarForImg
functions, it sets theSequence
, it creates the trial labeled"whoareyou"
, and runs theTemplate
function to create as manynewTrial
s as there are lines in fullList.csv. Because you callsetvarForImg
9 times in thatnewTrial
function, it is called 9 times with eachnewTrial
creation, and when the script gets tonewImage("comicstrip", varForImg)
when creating a trial, the value ofvarForImg
is always set to the value ofvariable.P3C
, because that’s the value passed to the most recently executedsetvarForImg
functionThen, when you progress through the experiment, as you get to a trial labeled
"demo"
, that trial already contains an Image element named"comicstrip"
that points to a file whose name is the value in theP3C
cell for the row from which the trial was created. The first PennController commands to be run as you start a new trial are thetest
s on the Var element, whosesuccess
commands have no actual effect (becausesetvarForImg
returnsundefined
, whichsuccess
will simply ignore:undefined
is not a PennController command). When the trial’s execution reaches the Image element, it will simply display the the file referenced in theP3C
cell, regardless of the earliertest
sWhat you can do instead is delete all references to
varForImg
(including the javascript functions) and include all 9 possiblenewImage
commands in the trial (then PennController will effectively create, and preload, all 9 Image elements per trial) but only execute one of those in thetest
s on the Var element:Template("fullList.csv", variable => newTrial("demo", newText("head", " ") .text(getVar("asdf")) .before(newText("b","").text(getVar("namelist")).after(newText(": "))) .center() .print() , defaultImage.center().print() , getVar("namelist") .test.is("P1A").success( newImage(variable.P1A) ) .test.is("P1B").success( newImage(variable.P1B) ) .test.is("P1C").success( newImage(variable.P1C) ) .test.is("P2A").success( newImage(variable.P2A) ) .test.is("P2B").success( newImage(variable.P2B) ) .test.is("P2C").success( newImage(variable.P2C) ) .test.is("P3A").success( newImage(variable.P3A) ) .test.is("P3B").success( newImage(variable.P3B) ) .test.is("P3C").success( newImage(variable.P3C) ) , newButton("moveOn", "Continue") .center() .print() .wait() ) )
Jeremy
Jeremy
KeymasterHi Judith,
The critical Audio element is played twice because you tell it twice to play: once when you create it, just after the previous Audio element has stopped playing, and once inside the callback function, when the mouse moves upward. Simply delete the
.play()
that you don’t wantJeremy
Jeremy
KeymasterHi,
1. The syntax you are using in
Template
automatically assigns a label to the items that are generated. Indeed, when I look up your code, every item generated by yourTemplate
commands consists of two PennController trials, and the twonewTrial
commands you use in there have different strings as their first arguments ("example"
and"grad"
, or"experiment"
and"grad"
)If you want to give labels to items consisting of pairs of elements, so that participants will always see the two
newTrial
s of a pair in a row, and so that those twonewTrial
s will remain associated with each other throughrandomize
, you can pass the label before"PennController"
:["experiment", "PennController",newTrial(
,["example", "PennController",newTrial(
(also remember to change"example.csv"
in the currentSequence
command in project back to"example"
like you have in your message above)2. Yes, I think it’s fine, but always take test-runs of your experiment and check the results file to see if you do get all the information you want. In this case, if you press F or J early, I expect that you should have a line in your results file for that Key element (
"stop early"
) but no line for the other Key element ("keypress"
) or a line that reports NA. But you should really just take a test-run and check your results fileJeremy
Jeremy
KeymasterThanks, I’ll fix it for the next release!
Jeremy
Jeremy
KeymasterHi Kelly,
My guess is that it’s just too many resources. You’re requesting close to 700 files: it’s just very likely that at least one of those requests will fail for any number of reasons, and still quite likely that more than one will fail. Distributing the requests over two different servers (github and the farm) probably helped, but to be honest, I’m surprised you were still able to run 2 pilots with no issues
As I mentioned in my previous post, one minor thing you could do is replace the
https://github.com
URLs withhttps://raw.githubusercontent.com
URLs so the browser doesn’t have to send as many requests, i.e you can replacehttps://github.com/USERNAME/REPO/raw/main/FOLDER/SUBFOLDER/file.mp3
withhttps://raw.githubusercontent.com/USERNAME/REPO/main/FOLDER/SUBFOLDER/file.mp3
. It won’t solve everything, since as you said you’ve also experienced issues preloading files from the farm, but it’s still a step in the right directionAmazon offers 5GB of S3 storage with AWS Free Tier (valid 12 months). You could upload a zip file there (see this post for tips on how to set things up so your experiment can download the zip file from S3)
Jeremy
Jeremy
KeymasterHi Henrik,
You could use add a
Footer
to every trial that checks how long it’s been since the experiment started andjump
s to a trial that sends the results if it’s been too long:const start_time = Date.now() Footer( newFunction( ()=>Date.now()-start_time>(20 * 60 * 1000) ) // 20min .test.is( true ) .success( jump("timeout") ) ) Sequence("intro", randomize("trials"), SendResults(), "end", "timeout") newTrial("intro", newButton("Start the experiment").print().wait() ) newTrial("end", newText("This is the end").print(),newButton().wait() ) newTrial("timeout", newText("You have timed out, we are sending your results to the server").print() , SendResults() , newText("Your results have been sent, thank you for your participation").print() , newButton().wait() )
Jeremy
Jeremy
KeymasterHi,
EDIT: actually you do need
callback
if you want to only move on when dropping in the right box:newText("yes"),newText("no", "drop in the other box") , newDragDrop("dd", "bungee") .log("all") .addDrop( getText(first_arg), getText(second_arg) ) .addDrag(getText("word")) .callback( getText("yes").remove(),getText("no").remove() , self.test.dropped( getText(target_res) ) .success( getText("yes").print() ) .failure( getText("no").print() ) ) .offset('0.5em', '0.1em', getText(first_arg), getText(second_arg) ) .wait( self.test.dropped(getText(target_res)) ) .removeDrag(getText("word")) .removeDrop( getText(first_arg), getText(second_arg) ) , getMouseTracker("mouse").stop() ,
End of EDIT
You don’t need to use
callback
, just test whether the Text element whose name corresponds to the value oftarget_res
is the one that received the draggable element afterwait
:newDragDrop("dd", "bungee") .log("all") .addDrop( getText(first_arg), getText(second_arg) ) .addDrag(getText("word")) .offset('0.5em', '0.1em', getText(first_arg), getText(second_arg) ) .wait() .test.dropped( getText(target_res) ) .success( newText("yes").print() ) .failure( newText("no").print() ) .removeDrag( getText("word") ) .removeDrop( getText(first_arg), getText(second_arg) ) , getMouseTracker("mouse").stop() ,
Jeremy
-
This reply was modified 3 years, 1 month ago by
Jeremy.
Jeremy
KeymasterHi,
My initial suggestion was to use a Selector element to detect a click on the word, and start the MouseTracker element then, but it turns out that
selector.callback
also fires only after the click has been released, which defeats the purposeAt this point, you’re better off injecting a javascript function using the Function element:
newMouseTracker("mouse").log() , newFunction( async ()=> { await new Promise(r=>getText("word")._element.jQueryContainer.mousedown(r)); getMouseTracker("mouse").start()._runPromises(); }) .call() ,
Jeremy
Jeremy
KeymasterHi,
newX
commands (and their arguments) are evaluated immediately so even if you were able to directly refer to the value of the Var element (which you can:getVar("word_num").value
) you wouldn’t necessarily get what you want, because the execution of a PennController trial (and of the commands on its elements) happens, well, whenever the trial is executedYou could do this instead:
newVar("random_word", variable["word_" + Math.floor(Math.random() * 12)] ).log(), newText("word", "").text( getVar("random_word") ).print()
or
newTrial("trial_prac", // ... word_num = Math.floor(Math.random() * 12), newText("word", variable["word_" + word_num]).print() //... ) .log("word_num", word_num)
EDIT: sorry, I just saw your second message. That’s a great solution (it’s actually cleaner than creating a variable within
newTrial
like I suggest)Jeremy
-
This reply was modified 3 years, 1 month ago by
Jeremy.
July 20, 2022 at 1:06 pm in reply to: Placing elements of unknown width in sequence in a canvas #8286Jeremy
KeymasterHi,
Create a container element (eg. a Text element) and set its
display
style attribute toflex
. Then print your elements inside that container element. Also make sure to set theposition
style attribute of your boxes torelative
in order for “word” to be positioned relative to the boxes themselves and not their container (the container Text element):Template("practice.csv", variable => newTrial("trial_prac", newText("container", "").css('display', 'flex').print() , newText("Should the word go here ").print(getText("container")) , newText("firstbox", " ") .css({border:'1px solid #000', width: '4em', position: 'relative'}) .print(getText("container")) , newText(" or here ").print(getText("container")) , newText("secondbox", " ") .css({border:'1px solid #000', width: '4em', position: 'relative'}) .print(getText("container")) , newText("?").print(getText("container")) , newText("word", "word").center().print() , newDragDrop("dd", "bungee") .log() .addDrop( getText("firstbox"), getText("secondbox"), ) .addDrag(getText("word")) .offset('0.5em', '0em', getText("firstbox"), getText("secondbox")) .wait() , newButton("Ready") .css("margin-top", "2em") .center() .print() .wait() .remove() ) )
Jeremy
Jeremy
KeymasterHi,
The PennController MediaRecorder element (which relies on, but is distinct from, the MediaRecorder API) only checks
webm
andogg
for audio, but Safari 14+ only referencesmp4
as supported, which is why you get that errorI will need to add
audio/mp4
to the next release, and raise an explicit error message when no supported type is found. In the meantime, feel free to upload a copy of PennElement_mediarecorder.js to your project’s Modules folder, replacing lineaudio: {'audio/webm': 'webm', 'audio/ogg': 'ogg'},
withaudio: {'audio/webm': 'webm', 'audio/ogg': 'ogg', 'audio/mp4': 'mp4'},
. You’ll get an error about MediaRecorder being defined multiple times, but I think the experiment should still run okJeremy
July 19, 2022 at 10:59 am in reply to: Side-by-side buttons in a selector that is removed after selection #8280Jeremy
KeymasterHi,
If you haven’t done so yet, I invite you to read the tutorial, more specifically section 5.1.2 which explains how to display elements side by side
A Selector element has no visible content: it just makes some elements that are independently printed onto the page, selectable (by a click, by a keypress, or either). You remove an element by calling
remove
on that elementApplying these points to your code (and updating it to modern PennController syntax and commands):
Template("practice.csv", variable => newTrial("trial_prac", newText("sentence", "<p>testing</p>") .center() .print() , newSelector("position") , newCanvas("buttons", 200, 50) .add( 0, 0, newButton("XXXX").selector("position") ) .add("right at 100%", 0, newButton("YYYY").selector("position") ) .center() .print() , getSelector("position") .shuffle() .once() .wait() .log() , getCanvas("buttons").remove() , newButton("Next") .center() .print() .wait() ) )
Jeremy
Jeremy
KeymasterHi,
Since your experiment starts with a PennController trial and ends with one too, and since PennController trials automatically add a line to the results file for the start of the trial and a line for the end of the trial too, simply subtract the EventTime of the first “_Start_” event of the run from the EventTime of the last “_End_” event of the run. No need to use any Var element
Jeremy
-
This reply was modified 3 years, 1 month ago by
-
AuthorPosts