Forum Replies Created
-
AuthorPosts
-
Jeremy
KeymasterThe code in my second message actually already does what you describe: it presents the sentence “Today is a very nice day!” as a whole chunk, and the second sentence word-by-word
This is actually how the DashedSentence controller works by default in Ibex: if you take a close look at the s parameter, the first time it is an array of one element (that element being the string) whereas the second time it is a string, directly (no square brackets). When the parameter is an array, every element is presented as a chunk, but when the parameter is directly a string, it is split using the space character and each word (separated by a space) is a chunk.
Jeremy
Jeremy
KeymasterSorry, posting a new message because after re-reading your message I think I just realized that you want something slightly different. Would you be OK with having two DashedSentence back-to-back, like this?
newTrial( newController( "DashedSentence" , {s: ["Today is a very nice day!"]} ) .print() .wait() , newController( "DashedSentence" , {s: "So I went outside for a walk."} ) .print() .wait() )
Jeremy
KeymasterHi,
I’m not sure I understand your design, but wouldn’t a table like the one below work the way you want?
AddTable("myTable", `ItemNumber,ButtonText,Group,Block 1,Lorem,A,1 2,ipsum,A,1 3,dolor,A,1 4,sit,A,2 5,consectetur,A,2 6,adipiscing,A,2 7,elit,B,1 8,sed,B,1 9,do,B,1 10,eiusmod,B,2 11,tempor,B,2 12,incididunt,B,2`) Sequence( randomize("block-1") , "break" , randomize("block-2") ) newTrial( "break" , newButton("Start second half").print().wait() ) Template( "myTable" , row => newTrial( "block-"+row.Block , newButton(row.ButtonText).print().wait() ) .log( "OrderGroup" , row.Group ) )
This is just illustrating two order groups, my understanding is that you’d want to have 18 of them so using letters as group labels you would add rows until you have an R group. I used different items on every single row (different item numbers and different button text).
Is this the kind of thing you want to do?
Jeremy
Jeremy
KeymasterHello Jun,
Yes, one option would be to inject the native-Ibex DashedSentence controller in a PCIbex trial, like this:
newTrial( newText("context", "Today is a very nice day!") .css('font-family', 'monospace') .center() .print() , newText("instructions", "
Press Space to start reading the next sentence
").italic().print() , newKey(" ").wait() , getText("instructions").remove() , getText("context").text("_____ __ _ ____ ____ ____") , newController( "DashedSentence" , {s: "So, I went outside for a walk.", display: "in place"} ) .print() .wait() )More info on the DashedSentence controller in the original Ibex documentation
Jeremy
-
This reply was modified 5 years, 3 months ago by
Jeremy. Reason: Tried to make the example closer to what was described
April 3, 2020 at 1:33 pm in reply to: VoiceRecorder: block incomplete recording / authorization page #4988Jeremy
KeymasterHi,
1. If you look at the documentation page for the VoiceRecorder element, you’ll see that there are two relevant commands: a wait command and a test.recorded command. I would favor the first one, using it like this:
newTrial( newVoiceRecorder("sample") .print() .wait() , newButton("Next") .print() .wait() )
Alternatively, you could use getVoiceRecorder("sample").test.recorded() in your button’s wait command to validate the click only if a recording is present.
2. You can partially customize the text by passing a string as the second parameter of InitiateRecorder. I should add an option to customize the consent text too, but in the meantime you can add this to your script:
let replaceConsentMic = ()=>{ let consentLink = $(".PennController-PennController a.Message-continue-link"); if (consentLink.length > 0 && consentLink[0].innerHTML.match(/^By clicking this link I understand that I grant this experiment's script access to my recording device/)) consentLink.html("En cliquant sur ce lien je comprends que j'autorise le script de cette expérience à accéder à mon microphone..."); else window.requestAnimationFrame( replaceConsentMic ); }; window.requestAnimationFrame( replaceConsentMic );
Of course you want to replace the French text with your own text.
Let me know if you have questions
Jeremy
Jeremy
KeymasterHi,
There are a few things that cause your script to crash:
- Elements should not be created by nesting them, ie. this is not valid syntax: newText((newScale. Either you create a Text element (if you just want to show a sentence or a paragraph on the screen) or you create a Scale element (if you want to provide participants with one). If you want to show both elements on the same line, one before the other, that’s what the before and after commands are for (you can use new* or get* commands inside the parentheses of non-new/get* commands).
- All new* commands follow the same syntactic rules, in particular, they all require to be immediately followed by a pair of parentheses in which you (optionally) pass parameters. The first occurrence of newScale in your script comes with no such pair of parentheses, but the second occurrence does.
- Besides a first optional name parameter, the parameter(s) of newScale can have two possible formats (as described on the documentation): either you use a single number that specifies the number of points on your scale, or you give a series of strings (separated by commas) that correspond to the labels of your scale’s buttons. So either something like newScale("yesno", "Yes", "No") or something like newScale("correctness", 5).
- The script will execute the lines in newTrial from the top down, and will only halt on wait commands. Since your script has no wait command, it will execute all the lines in an instant and immediately reach the closing parenthesis of newTrial, effectively ending your trial milliseconds after it started. You want to insert a wait command somewhere.
I am not totally sure what exactly you want your script to do, but below is a working variant of it. Note that I took the liberty to get rid of the .settings prefixes that became deprecated with PennController 1.7, and that the Text element named DashedSentence is just that: a Text element, so the content of row.DS will be printed at once. If you want to use the native-Ibex DashedSentence controller, you can use newController("DashedSentence", {s: row.DS}).print().wait() (you can take a look at the Controller element documentation page and this topic in the FAQ/Tips section).
Template(row => newTrial( newText(row.sentence) .print() , newText("DashedSentence", row.DS) .print() , newScale("yesnocorrect", "Yes","No" ) .labelsPosition("right") .keys() .before( newText("Is the sentence correct?") ) .print() , newText("How correct would you say it is?") .print() , newScale("correct", 5) .keys() .before(newText("completely incorrect")) .after(newText("completely correct")) .print() .wait() ) )
If you haven’t done so yet (or if you want a refresher) I strongly recommend that you read the tutorial. You can also watch this video of a webinar on this tutorial that we recorded just yesterday.
Best,
JeremyJeremy
KeymasterHello,
Ah, right, I noticed this problem before. I doubt anyone would even want to center the cursor, so I should probably change the effect of the center command on TextInput elements. In the meantime, you can do this:
newTextInput().cssContainer("text-align","center").print()
Jeremy
Jeremy
KeymasterHI Alan,
You can record audio any time you want, just make sure that the trial that you create using InitiateRecorder comes before any trial in which you use the VoiceRecorder element in your sequence of trials. For example:
PennController.ResetPrefix(null) Sequence( "intro" , "init-audio" , "first-half" , "record-audio" , "second-half" ) AddTable( "first-table" , "ButtonText\nClick on me\nAlmost there\nNext you'll be halfway through" ) AddTable( "second-table" , "ButtonText\nClick again\nAlmost done\nThis is the last trial" ) newTrial("intro" , newText("Welcome!").print() , newButton("Start").print().wait() ) InitiateRecorder( "https://myserver/uploadVoices/saveRecordings.php" , "You will first go over a few trials, then record a sample, and then go over some more trial. Please authorize this experiment to access you microphone." ) .label( "init-audio" ) Template( "first-table" , row => newTrial( "first-half" , newButton( row.ButtonText ).print().wait() ) ) newTrial( "record-audio" , newVoiceRecorder().print().wait() , newButton("Continue").print().wait() ) Template( "second-table" , row => newTrial( "second-half" , newButton( row.ButtonText ).print().wait() ) )
Of course this example script won’t succeed in sending the audio sample since I provided a fake URL to InitiateRecorder
Jeremy
Jeremy
KeymasterThen your best option is probably to create another table in parallel for the fillers specifically, in which you don’t need to have a Group column at all (this way you can have only one row per filler item). When you have multiple tables in your project, just make sure to specify which one you want to use in the respective Template commands.
Jeremy
KeymasterIf you have 20 items and 5 conditions, with the latin-square design that you illustrated in your table above, each of your participants would experience 4 repetitions of each condition (20 / 5 = 4 items per condition).
For the different context and question sentences, simply add columns to your table
Jeremy
Jeremy
KeymasterHi,
Yes, with the table you describe, participants in the A group would see two items: the first one in the definite Singular condition, the second one in the bare plural condition. Participants in the B group would see the same two items, but now the first one would be bare plural, and the second one definite Singular. The same logic extends to groups C, D and E.
Is that what you want?
Your table should also include columns for your other variable contents, so you probably want to add a Context column, a Question column and as many columns as there are different answer choices to that question on each trial (something like Answer1, Answer2, …)
Jeremy
Jeremy
KeymasterGood catch about the mismatch between the keys and the images’ positions
Moving the counterbalancing to the table is actually simpler than what you currently have: just add a pair of columns that you name leftPicture and rightPicture (for example) and simply alternate between “CoveredPicture” and “VisualPicture.” Then you can get rid of the whole “toggle” Var chunk and replace it with something like getCanvas("left").add( 0 , 0 , getImage(row.leftPicture) ) (same thing for “right”)
And apply the same logic when adding your images to your selector: .add( getImage(row.leftPicture) , getImage(row.rightPicture) ) (no need to change anything about the keys command then)
Does that make sense?
Jeremy
Jeremy
KeymasterHi Elise,
Yes, the logic is exactly the same as with the Key element, you just test for the selected item using .test.selected:
Template( "Filler_orig.csv" , row => ["filler_orig", "DashedSentence", {s: row.Sentence}, "PennController", newTrial( defaultImage .size(400,400) , newImage("CoveredPicture", "covered.jpg") , newImage("VisualPicture", row.Picture) , newCanvas(800,400) .add( -25 , 0 , newCanvas("left" , 400, 400) ) .add( 425 , 0 , newCanvas("right", 400, 400) ) .print() .settings.log() , newCanvas(50,50) .settings.add(-75,-200, newText("(F)")) .settings.add(850,-200, newText("(J)")) .print() , newVar("toggle", 1) // Initialize with value 1 .global().set( v=>1-v ).test.is(1) // This should be 1-v, I think .success( getImage("CoveredPicture").print(0,0,getCanvas("left")), getImage( "VisualPicture").print(0,0,getCanvas("right")) ) .failure( getImage("CoveredPicture").print(0,0,getCanvas("right")), getImage( "VisualPicture").print( 0,0,getCanvas("left") ) ) , newSelector() .settings.disableClicks() .settings.add( getImage("CoveredPicture") , getImage("VisualPicture") ) .settings.keys( "F" , "J" ) .settings.log() .wait() .test.selected( getImage(row.Target) ) .success( newText("Good job!").print() ) .failure( newText("Nope!").print() ) , newButton("Next").print().wait() ) .log('Condition', row.Condition) .log('Item', row.Item) ])
NOTE: I replaced the v-1 bit with 1-v as it makes more sense to me (you want the toggle Var to alternate between 1 and 0, not to tend towards -inifinity)—sorry if that was my mistake from a previous code
Jeremy
KeymasterHi,
PennController provides a Template command that can generate trials from a CSV table, and CSV tables can optionally contain a Group (alternatively called List) column that further distinguishes subsets of rows, so that each time the experiment is run only rows from one subset are used.
You can find an example and discussion of it on this page of the documentation tutorial, which illustrates 4 items each associated with two conditions (Ending: -s vs Ending: No-s) but presented in only one condition to each participation (latin-square design). Take a look at the Table section of the page more particularly.
Let me know if you have more questions
Jeremy
Jeremy
KeymasterYes, what you see is a good sign: it means that the script works as intended and does not crash when your request for the image fails
My guess is that the moving object is missing because your request to your bucket is processed very quickly but, instead of returning the zip file, it returns an error-like object (hence the non-display of your image upon running the trial)
In the meantime, if you don’t have too many images and the files are not too big, feel free to upload them to the PCIbex Farm (the Potsdam-experiments account currently only uses about 4.5MB out of the new 64MB limit)
Jeremy
-
This reply was modified 5 years, 3 months ago by
-
AuthorPosts