Forum Replies Created
-
AuthorPosts
-
JeremyKeymasterHi,
The two
newTrialcommands in your code are embedded inside aPennControllercommand —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
JeremyKeymasterHi,
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
JeremyKeymasterHi,
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
varForImgvariable, it creates thesetvarForImgandgetvarForImgfunctions, it sets theSequence, it creates the trial labeled"whoareyou", and runs theTemplatefunction to create as manynewTrials as there are lines in fullList.csv. Because you callsetvarForImg9 times in thatnewTrialfunction, it is called 9 times with eachnewTrialcreation, and when the script gets tonewImage("comicstrip", varForImg)when creating a trial, the value ofvarForImgis always set to the value ofvariable.P3C, because that’s the value passed to the most recently executedsetvarForImgfunctionThen, 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 theP3Ccell for the row from which the trial was created. The first PennController commands to be run as you start a new trial are thetests on the Var element, whosesuccesscommands have no actual effect (becausesetvarForImgreturnsundefined, whichsuccesswill simply ignore:undefinedis not a PennController command). When the trial’s execution reaches the Image element, it will simply display the the file referenced in theP3Ccell, regardless of the earliertestsWhat you can do instead is delete all references to
varForImg(including the javascript functions) and include all 9 possiblenewImagecommands in the trial (then PennController will effectively create, and preload, all 9 Image elements per trial) but only execute one of those in thetests 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
JeremyKeymasterHi 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
JeremyKeymasterHi,
1. The syntax you are using in
Templateautomatically assigns a label to the items that are generated. Indeed, when I look up your code, every item generated by yourTemplatecommands consists of two PennController trials, and the twonewTrialcommands 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
newTrials of a pair in a row, and so that those twonewTrials 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 currentSequencecommand 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
JeremyKeymasterThanks, I’ll fix it for the next release!
Jeremy
JeremyKeymasterHi 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.comURLs withhttps://raw.githubusercontent.comURLs 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.mp3withhttps://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
JeremyKeymasterHi Henrik,
You could use add a
Footerto every trial that checks how long it’s been since the experiment started andjumps 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
JeremyKeymasterHi,
EDIT: actually you do need
callbackif 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_resis 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, 3 months ago by
Jeremy.
JeremyKeymasterHi,
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.callbackalso 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
JeremyKeymasterHi,
newXcommands (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
newTriallike I suggest)Jeremy
-
This reply was modified 3 years, 3 months ago by
Jeremy.
July 20, 2022 at 1:06 pm in reply to: Placing elements of unknown width in sequence in a canvas #8286
JeremyKeymasterHi,
Create a container element (eg. a Text element) and set its
displaystyle attribute toflex. Then print your elements inside that container element. Also make sure to set thepositionstyle attribute of your boxes torelativein 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
JeremyKeymasterHi,
The PennController MediaRecorder element (which relies on, but is distinct from, the MediaRecorder API) only checks
webmandoggfor audio, but Safari 14+ only referencesmp4as supported, which is why you get that errorI will need to add
audio/mp4to 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 #8280
JeremyKeymasterHi,
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
removeon 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
JeremyKeymasterHi,
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, 3 months ago by
-
AuthorPosts