Forum Replies Created
-
AuthorPosts
-
JeremyKeymasterHi Yev,
Use the Selector element’s
shufflecommand, as illustrated above and in the tutorial:newSelector("images") , defaultImage.size(100,100).selector("images") , newCanvas("3x3", 300, 300) .add( 0, 0,newImage("one.png")) .add(100, 0,newImage("two.png")) .add(200, 0,newImage("three.png")) .add( 0,100,newImage("four.png")) .add(100,100,newImage("five.png")) .add(200,100,newImage("six.png")) .add( 0,200,newImage("seven.png")) .add(100,200,newImage("eight.png")) .add(200,200,newImage("nine.png")) .print() , getSelector("images").shuffle().disable()Jeremy
JeremyKeymasterHi Laia,
You could inject some plain javascript into your code to conditionally reference the first vs the second sentence:
newTrial("practice", oneFirst = (Math.random()>=0.5) , newAudio("audio1", "1fishSquareTank.mp3"), newAudio("audio2", "2fishRoundTank.mp3"), newText("sentence1", "The fish swims in a tank which is perfectly square."), newText("sentence2", "The fish swim in a tank which is perfectly round.") , newCanvas("frases", 800, 400).print() , getText(oneFirst?"sentence1":"sentence2").print( 0,0,getCanvas("frases")), getAudio(oneFirst?"audio1":"audio2").play().wait() , getText(oneFirst?"sentence2":"sentence1").print(400,0,getCanvas("frases")), getAudio(oneFirst?"audio2":"audio1").play().wait() , newSelector("choice") .add( getText("sentence1") , getText("sentence2") ) .log() .wait() ) .log("startWith", oneFirst?1:2) // log which sentence came firstJeremy
JeremyKeymasterHi,
What you describe is the
placeholderattribute of the HTMLtextareaelement, and PennController does not provide a direct command to set it, but it actually isn’t that hard to implement:newTextInput('text').print() , // The bit after the dash ('text') needs to match the name of the TextInput element newFunction( ()=>document.querySelector("textarea.PennController-text").placeholder = 'This is the default text.' ).call() ,Jeremy
JeremyKeymasterHi,
The columns are not randomized: the
featuresarray is randomized, which contains predicates over labels of trials; in effect, it is the order in which the trials are inserted in the sequence that is randomized, not the table or its columns, and not the way the trials are created more generallyIf you want to detect a change in feature upon runtime, you can use a global Var element to remember the value of the
Featurecolumn from the trial that was run before the current one, and compare the value of that Var element with the current value ofrow.Feature: if they are different, it means the current trial is starting a new feature block:newTrial( row.Feature + "-" + row.Item , newText("instruct_exerc", "Bitte bewerten Sie, ob beide Tiere gleich gut Verursacher oder Empfänger der Handlung sein können.") .css({'font-family': "helvetica"}) .center() .print() , newVar("feature") .global().test.is( row.Feature ).failure( newText("feature_change", "Attention: New Feature ") .cssContainer({"margin-top":"2em", "margin-bottom":"2em", 'font-family': "helvetica"}) .css("font-size", "1.2em").color("Red").bold().center() .print() ) .set( row.Feature ) ,Jeremy
JeremyKeymasterHi Karen,
When the Question controller’s
asparameter is not an array of strings, but an array of pairs of strings (whose first member represents a key and the second member is the printed answer) you cannot pass a string ashasCorrect: you need to either passtrue, in which case it means the first answer is the correct one, or a integer between0and the length of the array minus one, so in your case where you have two answers, either0(first answer in the array, selectable by pressing F) or1(second answer in the array, selectable by pressing J)Now, there is a subtlety, which is that values fetched from the table in
Templateare interpreted as strings by default, so even if you list0s and1s in your “Corr” column, they’ll still be interpreted as strings when you referencerow.Corr. The solution is to force a coercion into an integer, usingparseInt:newController("Question", {q: "Is it reasonable?", as: [["F","reasonable"], ["J","unreasonable"]], hasCorrect: parseInt(row.Corr), randomOrder: false})Jeremy
JeremyKeymasterNote that I found another bug with the suggestion above: creating a second DragDrop element messes with detecting dropping events on it, hence never validating its
waitcommand. So instead of creating two DragDrop elements, one should create and reuse a single one:newDragDrop("dragdrop") .addDrag(getImage("bear")) .addDrop(getCanvas("flowerCV")) .wait() .removeDrag(getImage("bear")) .removeDrop(getCanvas("flowerCV")) , // ... getDragDrop("dragdrop") .addDrag(getCanvas("flowerCV")) .addDrop(getCanvas("boxCV")) .wait()Jeremy
JeremyKeymasterSimply print the “bear” Image element and the Canvas elements containing the other images on a bigger Canvas element, and then shuffle those:
newCanvas("mainContainer", 900,300).print() , newImage("bear","bear.png").size(100,100).print(0,0,getCanvas("mainContainer")), newImage("bearCopy","bear.png").size(100,100).cssContainer("pointer-events","none").css("pointer-events","none") , newCanvas("flowerCV", 200,200).add(0,0,newImage("flower","flower.png").size(200,200)).print(300,0,getCanvas("mainContainer")) , newCanvas("boxCV", 300,300).add(0,0,newImage("box","box.png").size(300,300)).print(600,0,getCanvas("mainContainer")) , newSelector("shuffle").add(getImage("bear"),getCanvas("flowerCV"),getCanvas("boxCV")).shuffle().disable()Jeremy
JeremyKeymasterHi,
I just realized I never addressed the previous post, so I will do it now. Unfortunately there is no built-in way to log coordinates in a DragDrop element, but it can be achieved using some javascript. The
setcommand of Var elements accepts javascript functions, so we can create one Var element for each pair of coordinates we want to log:newTimer(5000).start() .wait() , newVar("manXY").set(v=>{ const bcr = getImage("man")._element.jQueryElement[0].getBoundingClientRect(); return bcr.x+';'+bcr.y; }).log(), newVar("boyXY").set(v=>{ const bcr = getImage("boy")._element.jQueryElement[0].getBoundingClientRect(); return bcr.x+';'+bcr.y; }).log(), newVar("figlistXY").set(v=>{ const bcr = getCanvas("figlist")._element.jQueryElement[0].getBoundingClientRect(); return bcr.x+';'+bcr.y; }).log() )Now, regarding the linking of two pictures, it should be possible in theory, but I found out there are a few bugs with such chaining in the DragDrop element. We will need a few tricks to work around those bugs, namely we will remove the image of the bear once it’s placed on the flower, and replace it with a copy of it; we will need to retrieve the coordinates of the original image to do that though, which will involve some javascript
We will also need to have the bear and the flower placed on a Canvas element and make them “transparent” so that the Canvas element can be dragged onto the box:ERRATUM: see this post on how to use a single DragDrop element instead
newText("instructions", "Put the bear on the flower").print() , newImage("bear","bear.png").size(100,100).print(), newImage("bearCopy","bear.png").size(100,100).cssContainer("pointer-events","none").css("pointer-events","none") , newCanvas("flowerCV", 200,200).add(0,0,newImage("flower","flower.png").size(200,200)).print() , newCanvas("boxCV", 300,300).add(0,0,newImage("box","box.png").size(300,300)).print() , newDragDrop("bearToFlower") .addDrag(getImage("bear")) .addDrop(getCanvas("flowerCV")) .wait() , newFunction(()=>new Promise(async r=>{ const bearBCR = getImage("bear")._element.jQueryElement[0].getBoundingClientRect(); const flowerBCR = getCanvas("flowerCV")._element.jQueryElement[0].getBoundingClientRect(); await getCanvas("flowerCV").add(bearBCR.x-flowerBCR.x,bearBCR.y-flowerBCR.y,getImage("bearCopy"))._runPromises(); r(); })).call() , getImage("bear").hidden().remove(), getImage("flower").cssContainer("pointer-events","none") , getText("instructions").text("Now put them in the box") , newDragDrop("flowerToBox") .addDrag(getCanvas("flowerCV")) .addDrop(getCanvas("boxCV")) .wait()Let me know if you have questions
Jeremy
-
This reply was modified 3 years ago by
Jeremy. Reason: reference to correction below
JeremyKeymasterHi,
If you used the Ibexfarm interface to delete those two files, they were deleted using Perl’s
unlinkfunction, so the OS no longer maintains a copy of the files. In that case, you might want to consider using some recovery tools to try and retrieve your files (eg. PhotoRect is a free, cross-platform software that I’ve used with success in the past) — do it as soon as you can though, since when the OS reallocates memory used by the deleted files those can no longer be recoveredIf you deleted them manually using a file browser, maybe the OS sent copies in a recycle bin of some sort
Jeremy
JeremyKeymasterHi,
You cannot use PennController’s
testcommands to conditionally include an Image element: you are effectively creating an Image element for your filler items too, which don’t have any value forflan_file, resulting in invalid Image elementsIn the script you currently have in your project, your sentence and flanker filler trials actually have nothing in common (as things stand, they do not define pic-spr pairs: there is no pic-spr sequence of commands, unlike in my code above for example; your code currently either includes
piccommands orsprcommands in a trial) so if that’s the kind of design you are implementing, I would just split the CSV file in two tables and correspondingly use twoTemplatecommands to generate the filler trials (and also oneTemplatefor your target trials, since they all seem to be SPR’s):Template("flanker-fillers.csv", newTrial("fillers", ...pic(row) ) Template("sentence-fillers.csv", newTrial("fillers", ...spr(row) ) Template("items-target.csv", newTrial("targets", ...spr(row) )Jeremy
JeremyKeymasterHi Karen,
Add
randomOrder: falseto the Question controller:newController("Question", {q: "Is it reasonable?", as: [["F","reasonable"], ["J","unreasonable"]], hasCorrect: row.Corr, randomOrder: false})Jeremy
JeremyKeymasterHi,
One option is to report the features in your table (as you are currently doing in Features_SemVerMixed.csv) and label your items accordingly:
newTrial( row.Feature + "-" + row.Item ,Then you can reference the labels in your
Sequencecommand. This would show items 1-to-5 in a fixed order for each feature, with the feature groups of items randomized:features = [...new Array(10)].map((v,n)=>startsWith("Feature "+Number(n+1))) fisherYates(features) Sequence("WelcomeConsent", "counter", "demographics", "instructions1", "exercise", "startofexp", ...features, SendResults(), "Bye");If you would like to additionally randomize the order of the items within each feature group, replace the first line with:
features = [...new Array(10)].map((v,n)=>randomize(startsWith("Feature "+Number(n+1))))Jeremy
JeremyKeymasterHi,
The script that’s actually running has
PennController.ResetPrefix(null)too far down (it should be the first line of the script) and it misses a comma between"demographic"and"instructions"in theSequencecommandThis is most likely due to the Save function bug on the farm: try copying the content of your script file, deleting it from your project and recreating it and pasting the content back in, and it should work
Jeremy
JeremyKeymasterHi,
Since your code associates
Fwith the image placed where “File1IDb1” originates, andJwith the image placed where “File2IDb1” originates, and sincelogreports the result ofshufflein the Comments column, you can technically retrieve the key that was pressed from the results file. For example, from this line I can determine that I pressedJ, since I selected the option that is listed second in the last column (File1IDb1):1666024185 REDACTED PennController 0 0 unlabeled NULL Selector selection1 Selection File1IDb1 1666024185448 File2IDb1;File1IDb1
That being said, you can still directly log which key was pressed using a Key element in parallel to the Selector element:
newKey("FJ").log("last") , newSelector("selection1") .disableClicks() .add( getImage("File1IDb1") , getImage("File2IDb1") ) .shuffle() .keys("F","J") .wait() .log() , getKey("FJ").disable()Jeremy
-
This reply was modified 3 years ago by
-
AuthorPosts