"
]
},
{
"cell_type": "markdown",
"id": "970e5e4a-987a-4d47-a040-77ee02fb7ae0",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Important prerequisites \n",
"\n",
"In order to get the best and most helpful experience out of this session, there are two important prerequisites everyone should have. If you have any problems concerning this, please just let one of the instructors know. \n",
"\n",
"### I - solid understanding of python\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "a57f9b85-7876-4095-a907-a7cc6a69b28d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### II - working PsychoPy installation\n",
"\n",
"You should have downloaded the standalone version specific for your OS from [https://www.psychopy.org/download.html](https://www.psychopy.org/download.html) (it should automatically suggest the fitting version via the big blue button) and subsequently installed it. \n",
"\n",
"You should be able to see the `PsychoPy` application in your `Applications`/`Programs`, click on it to open it!\n",
"\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "768d4dff-956c-4eff-bce1-2651cb388eab",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## Computational environments\n",
"\n",
"While we could set up and use a `computational environment` as discussed in the [school prerequisites](https://julia-pfarr.gitlab.io/nowaschool/prerequisites.html#introduction-to-computational-environments) and [RDM session](https://julia-pfarr.gitlab.io/nowaschool/materials/RDM/sections/organization.html), we will use the [standalone version of PsychoPy](https://www.psychopy.org/download.html#download) for this session. \n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "4136ee9f-7f8e-4f5e-8770-9363ffa476d0",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"The reasons are outlined below, but in short, the [manual installation](https://www.psychopy.org/download.html#manual-installations) tends to result in some differences between operating systems concerning drivers, etc. and we unfortunately don't have time to address these potential problems during the session. However, please note and remember that the manual installation usually works perfectly fine and you can utilize either in your local setup. \n",
"\n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "8efa9e54-df84-4b37-95ee-2c6d0611ec4b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"One argument that is routinely brought up goes something like \"Clearly open source can’t match the performance of proprietary software.\". While performance is definitely an important aspect, generalizing that open source software is generally worse, isn't very accurate and also not very scientific. How about having a precise look at it and compare different software and settings?"
]
},
{
"cell_type": "markdown",
"id": "7fab107e-d44b-4e7c-b5b7-690f5a95d986",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"That's exactly what the [The timing mega-study: comparing a range of experiment generators, both lab-based and online](https://peerj.com/articles/9414/) did:\n",
"\n",
"\n",
"- compared prominently used software across `OS`, local & online\n",
"- `PsychoPy` showed performance comparable to proprietary software\n",
"- prominent differences between `OS`:\n",
" - `ubuntu` > `windows` > `macOS` \n",
"- also prominent differences between browsers \n",
"- online versions less precise, independent of software\n",
" but `PsychoPy` overall great performance\n",
"\n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "cbadf08f-4f38-4ce3-981b-84169a955f94",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### PsychoPy resources\n",
"\n",
"\n",
"| PsychoPy website | PsychoPy documentation | PsychoPy discourse forum |\n",
"|----------------------|-----------------------|-----------------------|\n",
"| | | |\n",
"| [https://www.psychopy.org/](https://www.psychopy.org/ ) | [https://psychopy.org/documentation.html](https://psychopy.org/documentation.html)|[https://discourse.psychopy.org/ ](https://discourse.psychopy.org/ ) \n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "9e5a9fd1-6e0f-42d2-8140-d0e8a92664f9",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### RDM - Experiments\n",
"\n",
"Starting new `experiments` follows the same guidelines as starting new `projects` in general:\n",
"\n",
"- create and store everything in a dedicated place on your machine \n",
"\n",
"- use the (standalone version of) `PsychoPy` specific for your `OS`\n",
"\n",
"- document everything or at least as much as possible\n",
"\n",
"- test and save things in very short intervals, basically after every change\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "2a3a0bc7-8ed4-4c51-8336-aa158107a091",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### PsychoPy components\n",
"\n",
"The standalone version of `PsychoPy` comes with three distinct `windows` and respective functionalities.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "8989395a-1b36-4c49-93be-b34c66ff255c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### PsychoPy Experiment files\n",
"\n",
"As mentioned before, we will save and store everything in a dedicated place on our machines. In `PsychoPy` experiments that are created/managed via the `Builder` are saved as `.psyexp` files, so let's check this. After you opened `PsychoPy`, ie the `Builder`, you can do either save it via `File` -> `Save as` or the little 💾 icons. In both cases you should select the `code/experiment` directory we created.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "bf510afe-33e2-451a-9555-e57f5a9b080a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} What’s one of the first things we always have to do when utilizing python?\n",
":class: tip, dropdown\n",
"That’s right: thinking about `modules`/`functions` we need.\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "869527f9-9066-4908-8d83-f957640fc857",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### PsychoPy modules\n",
"\n",
"::::{grid}\n",
":gutter: 3\n",
"\n",
":::{grid-item-card} [psychopy.core](https://psychopy.org/api/core.html)\n",
"various basic functions, including `timing` & `experiment` termination\n",
":::\n",
"\n",
":::{grid-item-card} [psychopy.gui](https://psychopy.org/api/gui.html)\n",
"creation/management of `dialog boxes` allowing user input\n",
":::\n",
"\n",
":::{grid-item-card} [psychopy.event](https://psychopy.org/api/event.html)\n",
"handling of `keyboard`/`mouse`/other input from user\n",
":::\n",
"::::\n",
"\n",
"::::{grid}\n",
":gutter: 3\n",
"\n",
":::{grid-item-card} [psychopy.visual](https://psychopy.org/api/visual/index.html)/[sound](https://psychopy.org/api/sound/index.html)\n",
"presentation of `stimuli` of various types (e.g. `images`, `sounds`, etc.)\n",
":::\n",
"\n",
":::{grid-item-card} [psychopy.data](https://psychopy.org/api/data.html)\n",
"handling of `condition parameters`, `response registration`, `trial order`, etc.\n",
":::\n",
"\n",
":::{grid-item-card} many more …\n",
"we unfortunately can’t check out due to time constraints\n",
":::\n",
"::::"
]
},
{
"cell_type": "markdown",
"id": "2090cca5-b594-41f9-a221-8549d0c5e9f5",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"```{admonition} Question for y'all\n",
"Which one do we most likely need?\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "f443c2aa-fe66-4655-b616-f96ffa09990d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### A general experiment workflow\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "2cbba051-6a1a-4087-b109-e2a8022b6d08",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"- Running experiments (using python)\n",
"- **Introduction to PsychoPy**\n",
" - basic overview\n",
" - **basic working principles**\n",
"- PsychoPy\n",
" - stimuli & trials\n",
" - data output\n",
" - a very simple experiment\n",
"- PsychoPy advanced\n",
" - Eye-Tracking and neuroimaging\n",
" - online experiments\n",
"- Outro/Q&A\n",
"\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "2986ed18-6c59-4d25-903c-62a4ce037d82",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"### basic working principles\n",
"\n",
"After this brief overview of `PsychoPy` and its parts, we will explore some of it basic working principles, situated along the above outlined `experiment workflow`.\n",
"\n",
"#### Dialog box(es) to get user input\n",
"\n",
"Many experiments start with a `GUI dialog box` that allow users/participant to input certain information, for example `participant id`, `session`, `group`, `data storage path`, etc. .\n",
"\n",
"We can implement this crucial aspect via the [psychopy.gui module](https://psychopy.org/api/gui.html) with the respective being accessible via the `⚙️` icon in the top menu bar."
]
},
{
"cell_type": "markdown",
"id": "d585ed6b-ed1b-462e-8912-c2cbdbf1042c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"It will open the `Experiment Properties window` through which we can set a variety of important experiment characteristics, including `Experiment info` which will become our `dialog box`.\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"id": "fab58bfc-21d5-4ce8-a14d-2f1cb7266e03",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"That’s actually all we need to test our `GUI dialog box`!\n",
"\n",
"In order to do that, we need to `run/execute` our `experiment`. This is achieved via clicking the ▶️ icon in the top menu bar. This will `run/execute` the `experiment` via the `python` version installed in the `standalone application`. "
]
},
{
"cell_type": "markdown",
"id": "dd3cda8c-8494-44cc-83b6-a22255270ff6",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"To bring in some form of `computing environment management`, we have to set the `Use PsychoPy version` field in the `Experiment Properties` window -> please select `2023.2.3`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "000da8ce-4db1-4d01-b35e-46ff675e6f9e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Testing the GUI dialog box\n",
"\n",
"If everything works/is set correctly, you should see a `GUI dialog box` appearing on your screen asking for the information we indicated in \n",
"our `crtt_exp.py` `python script` (chances are the layout on your end looks a bit different than mine, that’s no biggie).\n",
"\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "f0c2d66f-1272-47a3-bbd6-fa823c588564",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"After entering all requested information and clicking `OK` the `GUI dialog box` should close, a `frame rate measurement` should start and no errors should appear. \n",
"\n",
"If you click `Cancel` the same thing should happen."
]
},
{
"cell_type": "markdown",
"id": "be52bb54-2123-498f-b62f-da0c20ed90a1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"You can also check the `logs` via `The runner`\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "9ca0ec43-ccaa-447c-aecc-b59e3aad9257",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Wow, this actually works for everyone (hopefully)!\n",
"\n",
"While this is kinda cool, it’s super detached from what we really wanted to do: exploring how we can run `experiments` using `Python` by building on top of all the things you already learned…"
]
},
{
"cell_type": "markdown",
"id": "4d1f3e73-3e44-4ede-bc78-e8263f7f8aa9",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"One way to still get something of that is to use `PsychoPy`’s `builder` magic ...\n",
"\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "bd59009e-b0be-4b17-9041-8dd415ce4a9c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### Converting experiments to python code & scripts\n",
"\n",
"`PsychoPy`’s `builder` allows you to **automatically convert** `experiments` you build via the `GUI` to `python` code and scripts. \n",
"\n",
"This is done via clicking the `python` icon in the top menu bar which should result in a new `python script` called `crtt_exp.py` \n",
"that opens in the `editor window` of `The coder` and is saved to your `code/experiment` folder.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "a6f7e42a-80d3-4650-8395-48256e630396",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"But what's in this `python code`? Let's check it out in more detail and look for things we defined/set, as well as classic `python` aspects.\n",
"\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "146c2aa9-fb1a-4aa7-b1e1-2969b0d0abf7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### The builder components and routines - adding instructions\n",
"\n",
"After having set this crucial aspect of our `experiment`, it’s time to actually start it.\n",
"\n",
"Quite often, `experiments` starts with several `instruction messages` that explain the `experiment` to the participant. Thus, we will add a few here as well, starting with a common `“welcome” text message`."
]
},
{
"cell_type": "markdown",
"id": "3561ca5c-0826-4b5a-a9fe-b8ce67400418",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"To `display` things in general but also `text`, the `psychopy.visual` module is the way to go. Regarding this we, however, need to talk about the general outline/functional principles of the `builder` first…\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "a2c9f9e0-4087-4ce7-b3c5-e9b7f87555f5",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"For example, if we want to add some `instructions`, we need to create a respective `Routine`.\n",
"\n",
"To add one, click on \"`Insert Routine`\" and select \"`new`\" (should you already have an empty `Routine`, please make sure to delete it via right-clicking on it and selecting \"`remove`\").\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "21fc465e-308b-4d0d-94cc-84792b630c9a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Finally, we're going to give this new routine an informative name, ie \"`welcome`\".\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "96730e21-b772-4f58-ba2a-eee90b850f66",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Within this `routine`, click on `Text` in the `components window` which should open a `text properties window`.\n",
"\n",
"This allows to set basically everything we want/need regarding the message we want to display and once everything is set, click on `OK` and the presentation of the `instruction` is added to the `Routine`.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "6bb70ab9-a214-4a26-bf09-0a169925b422",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"But what does all of this mean? Let's have a closer look at the inputs and settings!\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "df5e2ed0-d1df-4e0d-9be6-88eba6220d22",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Now we need to add a `Component` to the `Routine` that will allow us to continue with the `experiment` once the `space bar` was pressed.\n",
"\n",
"This is achieved via the `Keyboard Component` which will bring up the `Keyboard Response Properties window`. As with any other `Components Properties window`, we can set certain characteristics to attain a certain behavior."
]
},
{
"cell_type": "markdown",
"id": "c41a67e7-a471-43b5-9516-b87c940f958b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Here, we set the `Name` of the `Component`, `Start`, `Stop` and that the `Keyboard Response` should `end` the `Routine`, i.e. allow us to continue to the next part of the `experiment` and specifically only via the allowed key “`space`”.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c3c43264-2c36-4cfc-9914-3c927165c5cd",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"With that, our first ever routine is done and looks like this:\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "c7116f45-e297-4c28-836a-36734461eb5b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Testing the welcome routine\n",
"\n",
"Let’s give it a try via ▶️!\n",
"\n",
"If everything works/is set correctly, you should see the `GUI dialog box` and after clicking `OK`, the text we defined as a `welcome message` should appear next and after pressing the `space bar`, the `Experiment` should end without any errors.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "abdae42f-0561-4fa4-9f80-297b41e72d6e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"We are also going to update our `python script` again to check what changed via the `python` icon.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "84f6e383-e533-4deb-ab08-a8692614bc6e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### windows\n",
"\n",
"We came across one of `PsychoPy`’s core working principles: we need a `general experiment window`, i.e. a place we can display/present something on. \n",
"\n",
"You can define a variety of different `windows` based on different `screens`/`monitors` which should, however, be adapted to the `setup` and `experiment` at hand (e.g., `size`, `background color`, etc.). You will get to the respective `property window` via the little `screen` icon next to the `⚙️` icon.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "67032e2d-e96a-4e95-a639-5572c59ed07c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Basically, all `experiments` you will set up will require to define a `general experiment window` as without it, no `visual stimuli` (e.g. `images`, `text`, `movies`, etc.) can be `displayed`/`presented` or how `PsychoPy` would say it: `drawn`.\n",
"\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "1dd81c8f-1f64-4bd3-9c23-75554cfde1d1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"Speaking of which: this is the next core working principle we are going to see and explore is the difference between drawing something and showing it."
]
},
{
"cell_type": "markdown",
"id": "9d743d12-eb97-406a-a740-eae21038db23",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### draw & flip\n",
"\n",
"In `PsychoPy` (and many other comparable software), there’s a big difference between `drawing` and `showing` something.\n",
"\n",
"While we need to `draw` something on/in a `window` that alone **won’t actually `show` it**.\n",
"\n",
"This is because `PsychoPy` internally uses “two `screens`” one `background` or `buffer screen` which is not seen (yet) and one `front screen` which is (currently) seen.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "8500a042-3260-4b64-8c0a-e6c0fe01411c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"When you `draw` something it’s always going to be `drawn` on the `background`/`buffer screen`, thus “invisible” and you need to `flip` it to the `front screen` to be “visible”.\n",
"\n",
"Let’s see how that looks in `python` code:\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "00221679-6ca7-4ec6-98d6-c21e2e04aa13",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Why does `PsychoPy` (and other comparable software) work like that?\n",
"\n",
"- the idea/aim is always the same: increase performance and minimize delays \n",
" (as addressed in the `introduction`) \n",
"\n",
"- `draw`ing something might take a long time, depending on the `stimulus` at \n",
" hand, but `flip`ping something already `drawn` from the `buffer` to the \n",
" `front screen` is fast(er)\n",
"\n",
"- can ensure better and more precise timing \n",
"\n",
"- can work comparably for `images`, `sounds`, `movies`, etc. where things are \n",
" `set`/`drawn`/`pre-loaded` and presented exactly when needed\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "ded676ae-ac8e-412e-a690-f03ec9089fb1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### adding more instructions\n",
"\n",
"With these things set up, let’s add a few more messages to our `experiment`.\n",
"\n",
"One will be presented right after the `welcome message` and explain very generally what will happen in the `experiment`.\n",
"\n",
"Another one will be presented at the end of the `experiment` and display a general “that’s it, thanks for taking part” message.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "72ed77dc-cad0-4b7d-8fd6-08b070b656c4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"The outline for creating, `draw`ing and presenting these messages is identical to the one we just explored: we need to create respective `Routines`.\n",
"\n",
"We're going to start with some general instructions and information and add a new `routine` called `instructions_general` via `Insert Routine` -> `(new)`.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "cdc26f8e-7876-4681-aafb-b05f9623a695",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"After clicking `OK`, a white dot will appear which indicates where the new routine should be added.\n",
"\n",
"Please make sure to add it after the `welcome screen` by moving the dot there and then clicking on it.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "8770d540-be77-4d34-ac7c-3a5777008174",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"After that, the new routine should appear in our `experiment` and a respective new `routine window`\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c1d091d6-455c-4443-ad97-0b31872a0a29",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} Task for y'all!\n",
"\n",
"Now it's time to add the actual general instructions. Please use above-outlined approach and `components` to do the following:\n",
"\n",
"- add this `text`: \"In this task, you will make decisions as to which stimulus you have seen. There are three versions of the task. In the first, you have to decide which shape you have seen, in the second which image and the third which sound you've heard. Before each task, you will get set of specific instructions and short practice period. Please press the space bar to continue.\"\n",
"- add a component that advances the experiment, ie continues with the next screen, once participants pressed the `space bar`\n",
"\n",
"If you get stuck, make sure to check the \"`welcome`\" routine again. You have 5 min and please let us know if you have questions/run into problems!\n",
"\n",
"`````{admonition} Answer\n",
":class: tip, dropdown\n",
"\n",
"Step 1: Add a `text` `component` displaying the instructions\n",
"\n",
"- click on `Text` in the `components` window, set the stop to `condition` and remove any number in the respective value field\n",
"- add the instructions in the `Text` window, adding some line breaks for readability\n",
"- click `OK`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 2: Add a `Keyboard response` `component` that ends the `routine`\n",
"\n",
"- click on `Keyboard` in the `components` window, name it and remove any all but the 'space' key from the list of `Allowed keys`\n",
"- click `OK`\n",
"\n",
"
\n",
" \n",
"
\n",
"`````\n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "ade60e82-3cb2-40cc-b7c3-3ce185aadd7a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Your new general instruction `routine` should now look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "fc93d58f-6db0-4889-8e80-c562087a232e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} Task for y'all!\n",
"\n",
"We also have to add the general end screen. Please use above-outlined approach and `components` to do the following:\n",
"\n",
"- add a new `routine` called `end_screen`\n",
"- add this `text`: \"You have reached the end of the experiment, thank you very much for participating. Please press the `space bar` to finish.\"\n",
"- add a component that ends the experiment once participants pressed the `space bar`\n",
"\n",
"If you get stuck, make sure to check the \"`welcome`\" or prior routine again. You have 5 min and please let us know if you have questions/run into problems!\n",
"\n",
"`````{admonition} Answer\n",
":class: tip, dropdown\n",
"\n",
"Step 1: Add a new `routine`\n",
"\n",
"- click on \"`Insert Routine`\" -> `(new)` and name it \"`end_screen`\", placing it behind the `instructions_general` `routine`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 2: Add a `text` `component` displaying the message\n",
"\n",
"- click on `Text` in the `components` window, set the stop to `condition` and remove any number in the respective value field\n",
"- add the instructions in the `Text` window, adding some line breaks for readability\n",
"- click `OK`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 3: Add a `Keyboard response` `component` that ends the `routine`\n",
"\n",
"- click on `Keyboard` in the `components` window, name it and remove any all but the 'space' key from the list of `Allowed keys`\n",
"- click `OK`\n",
"\n",
"
\n",
" \n",
"
\n",
"`````\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "349dbf34-e57e-4b24-8dc0-c0b049e23909",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Your new end screen `routine` should now look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "cfa3964c-c2af-4ea8-b401-949a8c597130",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} Task for y'all!\n",
"\n",
"We still need to update our respective `python` script. Please use the `convert to python` functionality to convert our `experiment` again and thus update the corresponding `python` script. After that, please scroll through the `python` script to find and briefly evaluate the newly added `routines` and `components`.\n",
"\n",
"If you get stuck, make sure to check the prior sections again. You have 5 min and please let us know if you have questions/run into problems!\n",
"\n",
"`````{admonition} Answer\n",
":class: tip, dropdown\n",
"\n",
"Step 1: Convert the experiment to python\n",
"\n",
"- click on \"`Compile to Python script`\" and open/look the coder window \n",
"\n",
"Step 2: Check the newly added routines and components\n",
"\n",
"
\n",
" \n",
"
\n",
"`````\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "b25035ba-0e82-4122-a909-a03f457343f4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Testing the updated experiment\n",
"\n",
"Let’s give it a try via ▶️!\n",
"\n",
"If everything works/is set correctly, you should see the `GUI dialog box` and after clicking `OK`, the `text` we defined as a `welcome message` should appear next, followed by the `general instruction message` and finally the `end message`.\n",
"\n",
"Having this rough frame of our `experiment` it’s actually time to add the `experiment` itself: the **\"Choice Reaction Time Task\"**\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "910d0102-74e0-4918-9788-4ff3021a8ea4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"- Running experiments (using python)\n",
"- Introduction to PsychoPy\n",
" - basic overview\n",
" - basic working principles\n",
"- **PsychoPy**\n",
" - **stimuli & trials**\n",
" - data output\n",
" - a very simple experiment\n",
"- PsychoPy advanced\n",
" - Eye-Tracking and neuroimaging\n",
" - online experiments\n",
"- Outro/Q&A"
]
},
{
"cell_type": "markdown",
"id": "bbe199ee-366d-4465-8734-8c4803d028e2",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## PsychoPy\n",
"\n",
"### stimuli & trials\n",
"\n",
"Quick reminder: our `experiment` should collect `responses` from `participants` regarding their perception of various stimuli: `shapes`, `images` and `sounds`.\n",
"\n",
"Specifically, they should indicate which `shape`/`image`/`sound` was presented in a given `trial` via a dedicated `button press`.\n",
"\n",
"Thus we need to add/implement three new aspects in our `experiment`: the presentation of `stimuli`, `trials` and `responses`.\n",
"\n",
"We're going to explain these concepts and their implementation based on the `shape` `task`."
]
},
{
"cell_type": "markdown",
"id": "a913f7c7-3cf3-4118-bb9b-bcfc5b930443",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### adding further instructions - `shape task`\n",
"\n",
"At first, we need to add further instructions that are specific to the subsequent `task` as mentioned above, ie before each `task`\n",
"(`shape`, `image`, `sound`), a short explanation should provide further information.\n",
"\n",
"Adding a respective new `routine` and `components` is simply done as before."
]
},
{
"cell_type": "markdown",
"id": "783a1751-07bf-4197-baf3-00c97ad0d130",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} Here are the instructions for copy-paste\n",
"\n",
"In this task you will make a decision as to which shape you have seen.\n",
"\n",
"Press C or click cross for a cross\n",
"Press V or click square for a square\n",
"Press B or click plus for a plus\n",
"\n",
"First, we will have a quick practice.\n",
"\n",
"Push space bar or click / touch one of the buttons to begin.\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "1f5332bd-bee0-4325-9d15-32a494529a3c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Your instructions for the `shape task` `routine` should now look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "0e9564eb-d471-4a11-abef-1a0b39b8a642",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"To support the familiarization with the `task`, we will also include some `visual reminders` concerning which `button` should be pressed when.\n",
"\n",
"#### stimuli - the `Image` `component`\n",
"\n",
"To implement this, we need a new `component`: `Image`. Once you clicked on it, a window like the following should appear.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "5826568a-d5cd-44e0-9567-32a64838db33",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Comparable to the ones we've seen before, it allows us to specify important aspects of the `Component` and here specifically `Image`, ie `visual stimulus`.\n",
"\n",
"There's however something new: the `Image` field which allows us to set a `path` to the `image` we want to `display`.\n",
"\n",
"The `images` and `stimuli` we want to use are actually part of the `materials` you downloaded, they can be found under `school/materials/psychopy/stimuli`.\n",
"\n",
"At this point, we have to go back to `RDM`..."
]
},
{
"cell_type": "markdown",
"id": "ea8039f1-8390-4f5c-9c00-e593e8606e13",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} Following best practice RDM, where should the stimuli be stored?\n",
":class: tip, dropdown\n",
"\n",
"They should be placed within the `stimuli` directory of our `project`/`dataset` `directory`.\n",
"\n",
"```console\n",
"choice_rtt/\n",
" code/\n",
" stimuli/\n",
"```\n",
"\n",
"Please go ahead and move the `shape stimuli` to this `directory`.\n",
"\n",
"```console\n",
"choice_rtt/\n",
" code/\n",
" stimuli/\n",
" shapes/\n",
" black_square.jpg\n",
" blank_square.jpg\n",
" ...\n",
"```\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "fd815e9f-5ad6-44a0-9fa3-2ec0ba93a449",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Now that we have the stimuli in place, we can set them in the `Image Component`. Importantly, we will create one `image component` per visual aid we want to `display`, ie three in total.\n",
"\n",
"Starting with the first, we will select \"`response_square.jpg`\"\n",
"\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "79648fa6-383c-40bd-beb8-e29960fc4b31",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Given that we want to display multiple `visual reminders` and thus `stimuli`, we have to arrange and place them respectively in our `window` on the `screen`.\n",
"\n",
"To achieve this, we can use the \"`Layout`\" tab of the `Image component` window, which let's us set the `Size` and `Position` of the `image` at hand (among other things)\n",
"\n",
"We are going to set `(0.2, 0.1)` for `Size` and `(0, -0.25)` for `Position` and leave the rest as is.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "eb99abb4-0ae3-4456-9e1e-7945b9612741",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} What python data type was used to set the size and position?\n",
":class: tip, dropdown\n",
"A `tuple`, denoting `width` and `height` concerning `Size` and `x` and `y` coordinates concerning `position`.\n",
"`````\n",
"\n",
"`````{admonition} What do the other tabs entail?\n",
":class: tip\n",
"\n",
"Please have a quick look at the other tabs and briefly summarize what you can do with them.\n",
"\n",
"While you're at it, go back to the instruction message and change the `Position` under `Layout` to `(0, 0.1)`\n",
"and the `Letter Height` under `Formatting` to `0.035`.\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "ed9189f6-ce9a-4f8a-a236-fcf513013dd8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Your updated `shape task instruction` `routine` should now look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "430727fc-7646-4d89-b4b0-9c4b0f109a8c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Let’s give it a try via ▶️!\n",
"\n",
"If everything works/is set correctly, you should see the `visual reminder`, ie `image` on the bottom of the `window` and the `text` should be smaller.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "dc3b0f84-de27-4251-bd0c-47582af04d55",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Fantastic! We will now add two more `visual reminders` for the other `stimuli` and set the following `component properties`:\n",
"\n",
"- `Image component`, `stimulus`: `response_plus.jpg`, `Size`: `(0.2, 0.1)`, `Position`: `(0.25, -0.25)`\n",
"- `Image component`, `stimulus`: `response_cross.jpg`, `Size`: `(0.2, 0.1)`, `Position`: `(-0.25, -0.25)`\n",
"\n",
"The remaining settings (e.g. `duration`, etc.) should be identical to the first `Image component`.\n",
"\n",
"Your updated `shape task instruction` `routine` should now look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "4386d4c7-8da0-48f1-b4a6-6a8ce7efb95b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"When running the `experiment` via ▶️ now, the `instructions` for the `shape task` should look something like this: \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c65cfb63-eb5f-4d4d-b744-3c38cc6b17a5",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"One thing we haven't done in a while is to update our `python script` via the `Compile Python script` option and check the respective changes. Let's do that!\n",
"\n",
"If you scroll through the `python script`, you should be able to see the newly added `images`, ie `visual stimuli`:\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "f64e03ce-f653-48c6-8e6d-726cd829168e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### implementing trials - `shape task`\n",
"\n",
"A lot of experiments, ie most of them, include a few `task`-specific practice `trials` and the `experiment` at hand is no exception to that. Especially considering that we have multiple `sub-tasks`. Thus, let's add `practice trials` and while doing so, we are going to explore how to implement one core aspect of `experiments` in `PsychoPy`, `trials`."
]
},
{
"cell_type": "markdown",
"id": "d4a3ade9-ec52-4f90-86a5-15f93550920b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### (practice) trials\n",
"\n",
"`Trials` start like any other aspect of the `experiment` we've explored so far: by creating a new `Routine`. This one is going to be a bit special, though, as we want to utilize the same basic structure for `practice` and actual `experiment trials`, ie we will be using this `routine` at multiple points in our `experiment`. \n",
"\n",
"The first point to do so, is right after the `instructions_shape` `routine`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "7d2ebaf8-bd24-46a8-8563-e337b3ba0a80",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Starting easy, we will add the `visual ques` as we did before. However, instead of creating them from scratch, we will make use of the `copy-paste` functionality of `components`. In more detail, we will go back to the `instructions_shape` `component`, right-click on the `component` we want to copy, e.g. `instruct_shape_square`, select `copy`, go to the `trial` `component`, right-click, select `paste` and give the `component` a new name, e.g. `visual_reminder_square`.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "46b84e76-2724-4f97-83da-9c26e50af0f7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Our newly created `trial` `routine` should now look something like this:\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "e6cf6b95-d216-4490-b041-5a20c60de484",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} Task for y'all\n",
":class: tip\n",
"\n",
"Please apply the same approach to the other `visual reminders`, renaming them to `visual_reminder_plus/cross` respectively.\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "1e4d285f-5db7-48a7-aa80-e7949a70d441",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"This is our `trial` `routine` after having added all `visual reminders`: \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "602511e6-6582-49fd-a6b5-e09f94df1cc0",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Great, that worked like a charm. Within the `Choice Reaction Time Task`, the to-be-evaluated/recognized `stimulus` can appear at different positions on the `screen`. Here, we are going with `4` different positions and thus will add `4 tiles` at the respective position the `stimuli` can appear at/in. Concerning this, we will keep it simple and utilize a respective graphic called `white_square.png` and insert it as an `Image` at the desired positions. Thus, we need to add `4` new `Image components` to our `trial` `routine`.\n",
"\n",
"The first one is going to be called \"`left_far_tile`\", utilze the aforementiond `white_square.png` and be positioned at `(-0.375, 0)` with a size of `(0.22, 0.22)`. Importantly, we will set its start to `0.5` so that it's going to be presented shortly after the `trial` started.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "235c4099-bf71-43d6-abc6-1ef1a2431541",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} Task for y'all\n",
":class: tip\n",
"\n",
"Please apply the same approach to the other `tiles` as follows:\n",
"\n",
"- `name`: `left_mid_tile`, `position`: `(-0.125, 0)` \n",
"- `name`: `right_mid_tile`, `position`: `(0.125, 0)` \n",
"- `name`: `right_far_tile`, `position`: `(0.375, 0)` \n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "3d50942f-bed7-467f-be6d-29e9d6214c9d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Your updated `trial instruction` `routine` should now look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "2d467ffd-b894-4a4b-a7ca-3609de07ab92",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Having set up the `cues` and `tiles`, we should start thinking about presenting the actual `target`, ie the `stimulus` participants have to evaluate and make decisions on. Obviously, we need another `Image` `component` and if you have a closer look at the `stimuli` directory, you'll see a couple of files named `target_`. Given that `1+1=2`, we know what to do: create a new `Image` `component` and set one of the `target_` files as the `image` that should be presented. As it should appear slightly after the `cues` and `tiles` are already there, we will set its `Start` `time` to e.g. `0.7`, present it for `0.2s` (`Stop duration (s)`) and choose one of the `tile` `position` `coordinates` to present it at:\n",
"\n",
"- `name`: `target_image`\n",
"- `Start time(s)`: `0.7`\n",
"- `Image`: `target_square.jpg`\n",
"- `Size`: `(0.2, 0.2)`\n",
"- `Position`: `(-0.125, 0)`\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "5b8625eb-4036-4e6b-aa60-7df267ebccbf",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Great job! Now, we only have to add a `Keyboard` `Component` to record the `response` of the `participants`. Comparable to the ones before, we set the `Start time (s)` to `0` and leave `Stop duration (s)` empty. In contrast to the `Keyboard` `Components` we set prior, we will change the `Allowed keys` to `b`,`c`,`v` with regard to the `task` and `visual cues`: `b` if a `plus` was presented, `c` if a `cross` and `v` if a `square`. This will also allow us to compute `task` performance, ie `accuracies`, later on.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "8388172c-0913-4610-bd18-c48397fe0132",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"That should do the trick. Let's give it a try via ▶️. \n",
"\n",
"You should now see the `welcome` message, followed by the `general instructions`, the `task-specific instructions` and then the first `practice trial` that remains on screen until you've responded via one of the set keys. Finally, you should see the `end screen` and the `experiment` should end without `errors`.\n",
"\n",
"Looking back at our `experiment flow`, we can see, that we basically already got the core aspects implemented. Nice!\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "dac90608-ded2-441d-bb75-10682ce79843",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Wait: there's a slight problem/inconvenience with this approach...but what? \n",
"\n",
"While the approach worked nice for a single `stimulus`, we usually want to show lots of them and most likely different ones. So, do we have to set a new component for every single `stimulus`? "
]
},
{
"cell_type": "markdown",
"id": "2fd177b8-8f31-4083-87cb-69df0d2f2d2c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"`````{admonition} What would be a great way of utilizing a python control flow to adress this?\n",
":class: tip, dropdown\n",
"That's right, we could use a `for-loop` to iterate over a `list` of `stimuli`, ie setting a new `Image` in the `component` with every `trial` iteration.\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "5c16bf17-db7c-4172-8587-fdd30deac609",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### for-loops\n",
"\n",
"Implementing `for-loops` in `PsychoPy` is a three-stage endeavor (at least within the `builder`):\n",
"\n",
"- preparing a file that entails the `list` of `stimuli` we want to iterate over\n",
"- creating/adapting a `component` to work iteratively\n",
"- insert the `for loop` in the experiment flow"
]
},
{
"cell_type": "markdown",
"id": "cf73dfcc-d45f-490b-8454-5a42a0384e23",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### Stimuli lists\n",
"\n",
"While there are other ways to create `stimuli lists` (e.g. via `coding`, etc.), we will keep it brief and simple for this session and use a `spreadsheet application` to do so. \n",
"\n",
"We only need to provide an informative `column name`, e.g. `TargetImage` and then provide the paths to the `images` below, one per row.\n",
"An example is shown below: we will call the file `shapes_targets.csv`, define one `column` called `TargetImage` and then a list of `stimuli`, one per row. NB: you can use `relative` or `absolute` `paths`, just make sure that the path(s) is/are correct. Here, we used `relative paths`, pointing to the `choice_rtt/stimuli/shapes` directory relative to where the `stimulus list` is/will be saved, ie `choice_rtt/code/experiment`.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"NB: If you don't want to create this file, we also included it in the `materials` you downloaded, ie `psychopy/choice_rtt/code/experiment`. Just make sure to move it to the respective directory you saved the `PsychoPy` `experiment` in.\n"
]
},
{
"cell_type": "markdown",
"id": "1df6c365-7489-4ef9-afd0-a1f6bdbf5d04",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### Using components iteratively\n",
"\n",
"With the `stimuli list` in place, we can continue with adapting our `trial` `component` to work iteratively. \n",
"\n",
"Luckily, this is relatively straight-forward, as we \"only\" need to change the `Image` parameter of the component concerning two aspects:\n",
"\n",
"- instead of providing a path to one single `stimulus`, we will enter the name of the `column` that denotes the `list` of `stimuli` in our `shapes_targets.csv`, ie `TargetImage`, and add a `$` in the beginning to indicate that the paths and thus `stimuli` provided in a given `row` should be used when iterating\n",
"- instead of setting the presentation to `constant`, we will change it to `set every repeat`, ie indicating that we want to update the `image` every iteration of the `for-loop`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"This approach is generally how creating/adapting `components` that work `iteratively` is done, e.g. also for other types of `stimuli`, etc. ."
]
},
{
"cell_type": "markdown",
"id": "b27df26c-62a1-4e39-990b-07eb4fb92e7c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### Inserting a for loop\n",
"\n",
"The last step entails adding the actual `for loop`. However, instead of adding it directly to the/a `component`, `for loop`s are added \"around\" whatever is supposed to be \"`loop`ed\", ie `iteratively updated`. In our example, the `for loop` should be added around the `trial` `component`.\n",
"\n",
"Here's how this is done:\n",
"\n",
"- click on \"`Insert Loop`\" in the `Flow` window\n",
"- select the start position of the `for loop` by placing the `dot` before the `routine` that should be looped over\n",
"- select the end position of the `for loop` by placing the `dot` after the `routine` that should be looped over\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c26dbacd-1a5c-405a-bda1-f0236e15f88c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"- the `for loop` properties window should appear within which we can set the \"behavior\" of the `for loop`\n",
"- we will set `Name` to `PracticeLoop`\n",
"- we will set `loopType` to `random`, indicating that the `stimuli list` should be iterated over in a `random` fashion\n",
"- we will set `nReps` to `1`, indicating that the `stimuli list` should be iterated over once\n",
"- we will set the `Conditions` file to be our `shapes_targets.csv`\n",
"\n",
"If we did everything right, it should already state how many `conditions` and `parameters` are in our `stimuli list`. Here, it should be `6 conditions` as we have `6 rows`, ie `stimuli` and `1 parameter` as we have one `column`, ie `TargetImage`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "b35bff66-7b94-41d1-bc05-57a1b10385e4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"After clicking `OK`, the `for loop` should be added to our `experiment flow`, enclosing the `trial` `routine`. This should look something like this.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "d8e8d084-1780-4df3-8f76-b5e77ef99fe8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Before we are going to try it...\n",
"\n",
"`````{admonition} What would be the expected behavior of our experiment?\n",
":class: tip, dropdown\n",
"\n",
"1. We see the `welcome screen`.\n",
"2. We see the `general instructions`.\n",
"3. We see the `task-specific instructions`.\n",
"4. We get `6` `practice trials` across which the `target` is changed each iteration. \n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "d0fcf73e-0a52-4f1f-a288-d2dc5cee3c31",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"This seems that have worked out great. However ...\n",
"\n",
"`````{admonition} What are the two other problems with the way we are presenting stimuli?\n",
":class: tip, dropdown\n",
"\n",
"1. We set a fixed `onset`, i.e., the `target` will always appear at the same time within the `trial.`\n",
"2. We set a fixed `position`, i.e., the `target` will always appear at the same location within the `window`/on `screen`.\n",
"\n",
"Both aspects are not ideal concerning fundamentals of `experimentation` (e.g. regarding fatigue and expectation, etc.) and are usually addressed via introducing a `jitter` so that the `onset` and `position` (in our example) are not constant but change between `trials`.\n",
"\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "cb4bba12-374c-42b2-a0f1-b05366cbff0a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### adding python code via `Costum` `components`\n",
"\n",
"While we could theorectically add more columns to our `stimuli list` file, ie `jitter_onset` and `jitter_position`, we will use this opportunity to explore another great feature of `PsychoPy`: adding `python code` via `Costum components`. \n",
"\n",
"We already know that we can use the `Builder` to `Compile` a corresponding `python script` but we can also go the other way around and address some of the `Builder`'s limits (or just to be faster). The `Costum` `component` allows us to add `python code` that directly interacts with our `routines` and `components`. Here, we will use it to introduce `jitters` to the `onset` and `position` of the `targets`. \n",
"\n",
"As with other `components`, we start by selecting it from the `Components` `window`, specifically `Code`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "79c95181-b6e6-47fc-a4a4-ac8d84c55e52",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"This should open up the `Code properties window`, within which we set some `properties` and also enter our `python code`. \n",
"\n",
"Initially, we are going to name it \"`jitterOnsetPosition`\" and select `Begin Routine` to indicate that the code should be run at the beginning of our `routine`, ie with each iteration of the `for loop`. \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "229a1d38-e822-480e-94d4-b8758964c0ef",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Now to the actual `python code`. What we need is:\n",
"\n",
"- a `list` of possible `onsets` called `list_onsets`\n",
"- a `list` of possible `positions` called `list_positions`\n",
"- picking one `random`ly for a given `trial` and assigning them to variables called `onset_trial` and `position_trial`"
]
},
{
"cell_type": "markdown",
"id": "ec01691e-0ed4-45a1-9e67-4bfada1b5bb2",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} How would you implement this in python?\n",
":class: tip, dropdown\n",
"\n",
"There are of course many different ways to do it, but here's one.\n",
"\n",
"```python\n",
"#list of possible onsets for target\n",
"list_onsets = [1, 1.2, 1.4, 1.6, 1.8]\n",
"\n",
"# randomize these onsets\n",
"shuffle(list_onsets)\n",
"\n",
"#pick the first value from the list \n",
"onset_trial = list_onsets[0]\n",
"\n",
"#list of possible positions for target\n",
"list_positions = [-0.375, -0.125, 0.125, 0.375]\n",
"\n",
"# randomize these onsets\n",
"shuffle(list_positions)\n",
"\n",
"#pick the first value from the list \n",
"position_trial = list_positions[0]\n",
"```\n",
"\n",
"What other options can you think of?\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "bffdc34e-26d3-4cfe-9bcf-4ddcf974f0ee",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"With our `python code` ready, we simply copy-paste/write it in the left column of the `Custom` `Code` `window`.\n",
"\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"id": "ea837492-1855-4728-8e9e-e4e11ce204d0",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"So far, so good. However, to actally change the `onset` and `position` of the `target` at each `iteration`, we have to set this in the respective `Image` `component` `properties`, ie `Start time (s)` and `Position [x,y]`. \n",
"\n",
"In more detail, we have to replace the fixed values with the variables we're assigning in our `python code`:\n",
"\n",
"- `Start time (s)` : `onset_trial`\n",
"- `Position [x,y]`: `(position_trial,0)`, `set every repeat`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"\n",
"As mentioned before, the `python code` and `Builder` directly interact with one another and thus are aware of the respective variables. In other words, at the beginning of every `trial` (`routine`) iteration, the `python code` will be run and generating the variables `onset_trial` and `position_trial` which are then used by the `Image` `component` to set the `onset` and `position` of the `target`, now in a `jittered` manner (based on the `shuffle()` in our `python code`."
]
},
{
"cell_type": "markdown",
"id": "3d001999-03c1-4f24-b9b8-18f7931bb142",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"One thing we need to do however, is to `move` the `Custom` `Code` `component` to the top of the `components` in order to ensure that it's `run` first, ie generating the `onset` and `position` so that they can be used in the `Image component`. \n",
"\n",
"We can do that via right-clicking on the `Custom` `Code` `component` and then select `move to top`.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "78fc9e09-c32d-43b3-9db7-c96077d90960",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"When running the `experiment` via ▶️ now, the `onset` and `position` of the `targets` should change every `trial`."
]
},
{
"cell_type": "markdown",
"id": "0b6f678e-1901-42e9-98d0-486cb41cb9c0",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### providing participant feedback \n",
"\n",
"Another important aspect of `practice trials` is to provide `participants` with `feedback` concerning their `performance` to ensure that they understood the `task` and perform respectively. Here, we are going to provide `feedback` regarding their response `accuracy` and `reaction time`.\n",
"\n",
"In order to do this in `PsychoPy` (at least our `experiment`), we need to do the following:\n",
"\n",
"1) add functionality to compute `accuracy` and `reaction times` on the fly\n",
"2) add a new `routine` that presents the computed `feedback`"
]
},
{
"cell_type": "markdown",
"id": "776e5c0b-6d62-43a6-bac1-f45029d428ce",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Starting with 1., we intially need adapt our `Costum` `Code` `component` `jitterOnsetPosition` and also indicate which `response`, ie `button press` is `correct` for a given `image`, so that we can utilize this information later on.\n",
"\n",
"Here's the `python code` we need to add:\n",
"\n",
"```python\n",
"#TargetImage is the variable in the Image field of the target_image component\n",
"\n",
"#path of where to find the target image\n",
"if TargetImage == '../../stimuli/shapes/target_square.jpg': #path of where to find the target image\n",
" \n",
" #setting the key press that will be the correct answer\n",
" corrAns = 'v' \n",
" \n",
"#path of where to find the target image\n",
"elif TargetImage == '../../stimuli/shapes/target_cross.jpg':\n",
"\n",
" #setting the key press that will be the correct answer\n",
" corrAns = 'c'\n",
"\n",
"#path of where to find the target image\n",
"elif TargetImage == '../../stimuli/shapes/target_plus.jpg':\n",
"\n",
" #setting the key press that will be the correct answer\n",
" corrAns = 'b'\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "85a2656f-ee8c-487b-adf7-3ff78b959630",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Thus, our `Custom` `Code` `Component` should now look like this:\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "dd61a2a5-f4a4-4cbc-a6da-89f696be6d32",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Given that we now have indicated what the `correct response` for a given `stimulus` would be, we can utilize this information within the `keyboard_response` `component`. \n",
"\n",
"Under the `Data` tab we can actually set what the `correct response` or here `Correct answer` would be and if it should be stored. \n",
"Here, we will set `$corrAns` and thus forward the information set in the `Custom` `Code` `Component`.\n",
"\n",
"Additionally, we have to deselect the `Save onset/offset times` and `Sync timing with screen` fields in order to compute the respective `reaction time`.\n",
"\n",
"Last but not least, we should change the `Allowed keys` field to `set every repeat` as we're iterating over different `stimuli`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "60429b00-0827-4703-b110-4a9d2a009e25",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"This basically already assess if a `response` was correct by comparing the `response` to the set `correct response` of the respective `stimulus`. However, we still need to utilize this information and will do so by adding another `Custom` `Code` `Component`. \n",
"\n",
"Within it, we will `programmatically` access the `keyboard_response` `Component` and its content. Let's call it `compRTandAcc` and copy-paste/write the following `python code` in the tab `End Routine` to indicate that the `code` should be run at the end of the `routine`, ie each `trial` `iteration`. "
]
},
{
"cell_type": "markdown",
"id": "8ac42773-a3bc-4e79-967c-52ce948c8249",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```python\n",
"#this code is to record the reaction times and accuracy of the trial\n",
"\n",
"thisRoutineDuration = t # how long did this trial last\n",
"\n",
"# keyboard_response.rt is the time with which a key was pressed\n",
"# thisRecRT - how long it took for participants to respond after onset of target_image\n",
"# thisAcc - whether or not the response was correct\n",
"\n",
"# compute RT based on onset of target\n",
"thisRecRT = keyboard_response.rt - onset_trial \n",
"\n",
"# check of the response was correct\n",
"if keyboard_response.corr == 1: \n",
"\n",
" # if it was correct, assign 'correct' value\n",
" thisAcc = 'correct' \n",
"\n",
"# if not, assign 'incorrect' value\n",
"else:\n",
" thisAcc = 'incorrect'\n",
"\n",
"# record the actual response times of each trial \n",
"thisExp.addData('trialRespTimes', thisRecRT) \n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "13521beb-146c-464e-841a-1a01329dec67",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "b71b66fd-fd9d-4fc0-8655-e33a2233c5eb",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"As you can see, we directly access the `keyboard_response` `Component`, specifically `keyboard_response.rt` and `keyboard_response.corr`. While the latter entails the comparison between the `response` provided by the `participant` and the `response` we set as `correct` (ie if the `correct` `key` was pressed a `1` is assigned and if not a `0`), the former entails the the `reaction time` in terms of the first `key press` within a given `trial`. Thus, we have to substract the `onset_trial` value from it to get the actual `reaction time` from `target` presentation to `key press` (Reminder: `onset_trial` is the variable assigned by the `jitterOnsetPosition` `Custom` `Code` `Component`.)."
]
},
{
"cell_type": "markdown",
"id": "c7c3be8f-6fc1-472f-9036-a8e6ff805de8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"After clicking \"`OK`\", the new `Component` should appear in the `Routines` window:\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "1bfc8f7b-a830-464b-a316-f33c5ac5e235",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"That concludes 1., let's continue with 2.: add a new `routine` that presents the computed `feedback`.\n",
"\n",
"At first, you've guessed it, we need to add a new `routine` via \"`Insert Routine`\" and we're going to name it \"`practice_feedback`\" and place it behind the `trial` `routine` but still within the `for loop` as we want to present `feedback` after every practice `trial`.\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"id": "4c2bfab5-a9e9-478a-a8d8-ef1d34110490",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"As we want to present the `feedback` visually, ie via `text`, we will add a new `Text` `Component` and call it `feedback_text`. As with the other `Text` `Components`, we will set `Start time (s)` to `0.0` and leave `Stop duration (s)` empty. \n",
"\n",
"The new functionality comes now: we have to once more interact with the `Custom` `Code` `Component`. This time, we need information from `compRTandAcc`, specifically the `reaction time`, ie `thisRecRT`, and `accuracy`, ie `thisAcc`.\n",
"\n",
"We are going to use these `variables` within the `Text` field of the `Component` like so:\n",
"\n",
"```\n",
"$\"The last recorded RT was: \" + str(round(thisRecRT, 3)) + \" \\nThe response was: \" + thisAcc + \" \\n\\nPress the space bar to continue.\" \n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "a5ef17b6-09d1-47bc-9b31-128aa0461ca8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"In more detail, we're making use of `python strings` and `string formatting`, via directly adding the `variables` to the `string`, ie `text`, that should be presented. \n",
"\n",
"The leading `$` in combination with setting the `Text` to `set every repeat` indicates that the `variables` and thus the `string` should be updated at each `iteration`, ie `practice trial`."
]
},
{
"cell_type": "markdown",
"id": "b7bf11be-8607-47ac-8c60-676ceeca763f",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} What are the \\n denoting?\n",
":class: tip, dropdown\n",
"They are used to denote a `line break`.\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "fda32f74-03bc-474d-a3aa-94e14614d141",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"The complete `Text` `Component` should look like this:\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "004bcad9-f248-400e-ba38-645a03da837e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"We will also add a `Keyboard` `Component`, calling it `keyboard_response_feedback` and setting the `Allowed keys` to `'space'` in order to allow participant to advance to the next (practice) `trial`.\n",
"\n",
"And that's our `routine` to present the `feedback` computed by the `compRTandAcc` `Custom` `Code` `Component`.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "1ac93409-d9f6-4ba7-aa26-1ce770f18153",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"If you now run the `experiment` again, you should be presented with a `feedback screen` after each `practice` `trial`, displaying your `reaction time` and if your `response` was `correct`.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "a271f0fd-2ab2-4db5-922e-090c990a4818",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"To get the full experience, please make sure to go through the entire `experiment` once (we will also need the `data` in a few minutes)!"
]
},
{
"cell_type": "markdown",
"id": "c7858da8-6073-445e-ae24-c82ca7912356",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**Great job everyone!**\n",
"\n",
"
\n",
" \n",
"
\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "9daff12c-2521-4684-8b82-0a0704a82517",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"Now that we have collected our first set of `responses`, we can have a look at another core aspect of `PsychoPy` (and comparable software): `storing` and `handling` acquired `data`.\n",
"\n",
"- Running experiments (using python)\n",
"- Introduction to PsychoPy\n",
" - basic overview\n",
" - basic working principles\n",
"- **PsychoPy**\n",
" - stimuli & trials\n",
" - **data output**\n",
" - a very simple experiment\n",
"- PsychoPy advanced\n",
" - Eye-Tracking and neuroimaging\n",
" - online experiments\n",
"- Outro/Q&A\n"
]
},
{
"cell_type": "markdown",
"id": "c12546a6-f73f-4595-93bf-08721d774691",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### data output\n",
"\n",
"One thing we haven't checked out yet is what kind of `output`, ie `data` is actually generated by `PsychoPy` and specifically, our `experiment`. Let's start this topic with two quick questions."
]
},
{
"cell_type": "markdown",
"id": "f19ac8b6-8019-4b14-8d33-7476282b07dd",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"`````{admonition} What did other software you have used in the past provide as output/data?\n",
":class: tip\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "4aedd0b3-dead-4a63-90e6-0139042ddc9d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"`````{admonition} What kind of output/data would you like to have?\n",
":class: tip\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "d72989c4-60a5-465b-94a6-3c8a0ae5517d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Here's the good news: `PsychoPy` is once again highly customizable and can provide many different types of output/data across many levels. As we didn't set anything specific yet, we should start with having a look at the default output we get by running our (or any other) `experiment`.\n"
]
},
{
"cell_type": "markdown",
"id": "386a2382-4470-4ae5-b07a-0c93979dd07c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### default outputs\n",
"\n",
"\n",
"By default, you should have a directory called `data` within the `experiment` direcotry within which all `data` acquired by running the `experiment` is stored.\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" code/\n",
" experiment/\n",
" data/\n",
" 01_crtt_exp_2024-02-02.csv\n",
" 01_crtt_exp_2024-02-02.log\n",
" 01_crtt_exp_2024-02-02.psydat\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "9af98820-e709-465a-80f2-3b2092ad6858",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"As you can see, the `file name`s should be a combination of the `participant ID` and the `date` (+ the `time`). Furthermore, there should be `3` different kinds of `output`/`data` files: `.csv`, `.log` and `.psydat`, each containing information about the `experiment` `run` at different level of detail. We will have a quick look at them now."
]
},
{
"cell_type": "markdown",
"id": "e36b3805-5607-40af-b2d3-d657cfcabf15",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**01_crtt_exp_2024-02-02.csv**\n",
"\n",
"The `.csv` file contains information, i.e. `data`, in a **summarized way** for each `routine` and `trials` therein, which is (kinda) intelligible and easy to grasp. Usually, this is the file you want to use/work with wrt to `analyses`, etc. .\n",
"\n",
"\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "00f8b057-30b1-4b1c-bd58-470e30e28bbd",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"While we definitely get a lot of `data`, its provided in way we can make sense of and easily utilize in subsequent steps: we get `data` concerning the presented `routine` (e.g. `start`/`stop`), `trials` (e.g. `start`/`stop`), `stimuli` (e.g. `file`, `position` in `loop`) and `components` (e.g. `text`, `stimuli`, `responses`) and we get the `data` entered in the `GUI dialog box` within the last `columns`. We also get the `reaction time` and `accuracy` we computed on the fly. Basically, everything we need."
]
},
{
"cell_type": "markdown",
"id": "b557f490-c1f2-4954-97c9-d533cc9184af",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**01_crtt_exp_2024-02-02.log**\n",
"\n",
"The `.log` file contains information, i.e. `data`, in a **detailed way** for each and every `frame`, which is (kinda) intelligible but rather hard to grasp. Usually, these are the files you want to use/work with wrt to precise/detailed `quality control` and `testing`, etc. but not wrt `analyses`, etc. .\n"
]
},
{
"cell_type": "markdown",
"id": "4420446e-b51f-4057-9276-1f454a8e31c8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "3b63e4fe-b725-4779-8f39-c2f69d39cb0e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"Here, we get each `frame` (left `column`), what \"event type\" happened during it (middle `column`) and what happend within the \"event type\" (right `column`)."
]
},
{
"cell_type": "markdown",
"id": "4856487e-92df-4e25-a0ce-1796186ab940",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"**01_crtt_exp_2024-02-02.psydat**\n",
"\n",
"The `.psydat` file as comparable to the `.log` as it contains information, i.e. `data`, in a detailed way for each and every frame. In order to view and interact with the `data`, you need to use [PsychoPy's python module](https://www.psychopy.org/download.html#pip-install).\n",
"\n",
"At first, you need to `load` the `data`:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "4f0d3720-5355-43f9-bc01-379af6046f1f",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [],
"source": [
"from psychopy.misc import fromFile\n",
"\n",
"# set the psydat file\n",
"psydatFile = \"/Users/peerherholz/Desktop/choice_rtt/code/experiment/data/01_crtt_exp_2024-02-01_09h33.45.473.psydat\"\n",
"\n",
"# load the psydat file\n",
"psydatFile_load = fromFile(psydatFile)"
]
},
{
"cell_type": "markdown",
"id": "0f037e9b-c6d0-4e5f-8992-bc5db25eab78",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Which you can then view:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "dce0026b-3de0-4886-8f41-50906cfbdd76",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_getAllParamNames', '_getExtraInfo', '_getLoopInfo', '_guessPriority', '_paramNamesSoFar', '_status', 'abort', 'addAnnotation', 'addData', 'addLoop', 'appendFiles', 'autoLog', 'close', 'columnPriority', 'currentLoop', 'dataFileName', 'dataNames', 'entries', 'extraInfo', 'getAllEntries', 'getJSON', 'getPriority', 'loopEnded', 'loops', 'loopsUnfinished', 'name', 'nextEntry', 'originPath', 'pause', 'resume', 'runtimeInfo', 'saveAsPickle', 'saveAsWideText', 'savePickle', 'saveWideText', 'setPriority', 'sortColumns', 'status', 'stop', 'thisEntry', 'timestampOnFlip', 'version']\n"
]
}
],
"source": [
"print(dir(psydatFile_load))"
]
},
{
"cell_type": "markdown",
"id": "abab0be5-7989-4fdd-869b-3ae021828c0e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"or access/interact with:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "175b9dc6-b8e4-40ad-9ea9-cafe2d646a5a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_createOutputArray', '_createOutputArrayData', '_createSequence', '_exp', '_makeIndices', '_terminate', 'addData', 'autoLog', 'data', 'extraInfo', 'finished', 'getCurrentTrial', 'getEarlierTrial', 'getExp', 'getFutureTrial', 'getOriginPathAndFile', 'method', 'nRemaining', 'nReps', 'nTotal', 'name', 'next', 'origin', 'originPath', 'printAsText', 'saveAsExcel', 'saveAsJson', 'saveAsPickle', 'saveAsText', 'saveAsWideText', 'seed', 'sequenceIndices', 'setExp', 'thisIndex', 'thisN', 'thisRepN', 'thisTrial', 'thisTrialN', 'trialList']\n",
"{'ran': masked_array(\n",
" data=[[1.0],\n",
" [1.0],\n",
" [1.0],\n",
" [1.0],\n",
" [1.0],\n",
" [1.0]],\n",
" mask=[[False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False]],\n",
" fill_value=1e+20,\n",
" dtype=float32), 'order': masked_array(\n",
" data=[[1.0],\n",
" [5.0],\n",
" [4.0],\n",
" [3.0],\n",
" [0.0],\n",
" [2.0]],\n",
" mask=[[False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False]],\n",
" fill_value=1e+20,\n",
" dtype=float32), 'keyboard_response.keys': array([['b'],\n",
" ['c'],\n",
" ['v'],\n",
" ['c'],\n",
" ['b'],\n",
" ['c']], dtype=object), 'keyboard_response.corr': masked_array(\n",
" data=[[1.0],\n",
" [1.0],\n",
" [1.0],\n",
" [1.0],\n",
" [1.0],\n",
" [0.0]],\n",
" mask=[[False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False]],\n",
" fill_value=1e+20,\n",
" dtype=float32), 'keyboard_response.rt': masked_array(\n",
" data=[[2.702897787094116],\n",
" [2.077255964279175],\n",
" [2.052070140838623],\n",
" [2.1845827102661133],\n",
" [2.8191428184509277],\n",
" [2.8643736839294434]],\n",
" mask=[[False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False]],\n",
" fill_value=1e+20,\n",
" dtype=float32), 'keyboard_response.duration': array([[None],\n",
" [None],\n",
" [None],\n",
" [None],\n",
" [None],\n",
" [None]], dtype=object), 'keyboard_response_feedback.keys': array([['space'],\n",
" ['space'],\n",
" ['space'],\n",
" ['space'],\n",
" ['space'],\n",
" ['space']], dtype=object), 'keyboard_response_feedback.rt': masked_array(\n",
" data=[[1.059588074684143],\n",
" [0.6677191853523254],\n",
" [0.7012380361557007],\n",
" [0.6207000017166138],\n",
" [2.571624517440796],\n",
" [1.0015552043914795]],\n",
" mask=[[False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False],\n",
" [False]],\n",
" fill_value=1e+20,\n",
" dtype=float32), 'keyboard_response_feedback.duration': array([[None],\n",
" [None],\n",
" [None],\n",
" [None],\n",
" [None],\n",
" [None]], dtype=object)}\n",
"[[1.0]\n",
" [1.0]\n",
" [1.0]\n",
" [1.0]\n",
" [1.0]\n",
" [1.0]]\n",
"[[1.0]\n",
" [5.0]\n",
" [4.0]\n",
" [3.0]\n",
" [0.0]\n",
" [2.0]]\n",
"[[1.059588074684143]\n",
" [0.6677191853523254]\n",
" [0.7012380361557007]\n",
" [0.6207000017166138]\n",
" [2.571624517440796]\n",
" [1.0015552043914795]]\n"
]
}
],
"source": [
"trialHandler = psydatFile_load.loops[0]\n",
"\n",
"trialHandlerParams = dir(psydatFile_load.loops[0])\n",
"print(trialHandlerParams)\n",
"\n",
"print(trialHandler.data)\n",
"print(trialHandler.data['ran'])\n",
"print(trialHandler.data['order'])\n",
"print(trialHandler.data['keyboard_response_feedback.rt'])"
]
},
{
"cell_type": "markdown",
"id": "201732aa-f91e-48e2-adea-c6927b5b98a8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Which one you use further is definitely up to you, but starting with `.csv` is definitely a good default. While these three files are already pretty cool and useful, `PsychoPy` actually has quite a bunch more options concerning `data output` which we will explore next."
]
},
{
"cell_type": "markdown",
"id": "9fee84b5-8e99-41c7-8340-7e1cbee30237",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Output data properties\n",
"\n",
"One way to change the `output data` provided by `PsychoPy`, in terms of formatting, level of detail, etc., is to adjust the settings in the `Data` tab of the `experiment` `Properties` window which is accessible via the ⚙️ icon in the top menu bar.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "20d1b668-2946-4a1c-b2e5-9898a0ac65a3",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Based on the settings we can see here, the generation of the `output data` files we checked before makes more sense: \n",
"\n",
"- the `Data filename` is created by using `experiment` `variables` (`participant ID`, `experiment name` and `experiment date/time`) and `string formatting`\n",
"- the different `output files` are generated by `Save log file`, `Save csv file (summaries)` and `Save psydat file`\n",
"\n",
"As you can see, we could actually set more properties, e.g. the `Data file delimiter` changing `column` arrangement, change the `Logging level` and save further `output` files, ie `Save Excel file`, `Save csv file (trial-by-trial)` and `Save hdf5 file`. \n",
"\n",
"We can also add basically any `data` obtained via `Costum` `Code` `Components` as seen before:\n",
"\n",
"```python\n",
"# record the actual response times of each trial \n",
"thisExp.addData('trialRespTimes', thisRecRT) \n",
"```"
]
},
{
"cell_type": "markdown",
"id": "e7ab5f1f-2fb5-42a2-9659-0a84953f6b54",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"For now, we will keep things as they are with one exception: the `Data filename`. "
]
},
{
"cell_type": "markdown",
"id": "20439878-b565-4f6b-a31a-ec43bd1f47db",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Changing data output files \n",
"\n",
"Going back to the [RDM session](https://julia-pfarr.gitlab.io/nowaschool/materials/RDM/overview_RDM.html) and specifically [Project and Data Organization](https://julia-pfarr.gitlab.io/nowaschool/materials/RDM/overview_RDM.html#project-and-data-organization), we learned utilizing a dedicated `directory structure` and `file identifier` will go a long way concerning `FAIR`-ness. \n",
"\n",
"Obviously, we want to apply these principles to our `experiment` as well and therefore, we are going to change the `Data filename` in two aspects: \n",
"\n",
"- we will add the `session identifier` to the `file name string`\n",
"- we will add a `path` `variable` to the `file name` so that we can choose a dedicated directory to save the `data output files` in"
]
},
{
"cell_type": "markdown",
"id": "45b03567-a777-42e3-af98-35773d5c45b3",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"The first aspect is comparably easy and straightforward, as we already have the `session identifier` through our `GUI dialog box` and only need to add it to the `file name string`.\n",
"\n",
"`````{admonition} How would we do this?\n",
":class: tip, dropdown\n",
"\n",
"We simply need to do the following:\n",
"\n",
"```python\n",
"u'data/%s_%s_%s_%s' % (expInfo['participant'], expInfo['session'], expName, expInfo['date'])\n",
"```\n",
"\n",
"where `expInfo['session']` is the value of the `session` field in our `GUI dialog box`.\n",
"\n",
"`````\n"
]
},
{
"cell_type": "markdown",
"id": "3dd4ead8-67e6-4d45-bfbe-4fff87963f99",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"The second aspect requires slightly more work, as we so far didn't include an option to specify an `output path`. However, this is easily added to our `GUI dialog box`, via clicking the last `+` icon to add a field, which we will name \"`output-path`\".\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "32708335-654e-43aa-8972-b9fe5e1ef698",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Following the same approach as above...\n",
"\n",
"`````{admonition} How would you add this to the file name?\n",
":class: tip, dropdown\n",
"\n",
"We need to exchange the `data` `string` with the value of the `output-path` `variable` obtained from the `GUI dialog box` via `string formatting:\n",
"\n",
"```python\n",
"u'%s/%s_%s_%s_%s' % (expInfo['output-path'], expInfo['participant'], expInfo['session'], expName, expInfo['date'])\n",
"```\n",
"`````\n"
]
},
{
"cell_type": "markdown",
"id": "d4311c4d-8513-46f2-9e1a-b0deacf4a21e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Fantastic, we're almost there. One last thing we should think about and incorporate is the `directory structure`. As learned in the [Project and Data Organization](https://julia-pfarr.gitlab.io/nowaschool/materials/RDM/overview_RDM.html#project-and-data-organization) part of the [RDM session](https://julia-pfarr.gitlab.io/nowaschool/materials/RDM/overview_RDM.html), we should keep different versions of our `data` strictly separate to avoid confusion, data loss and provenance problems. Specifically, this refers to an example `directory structure` with the following aspects (applied to our `experiment`):\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" code/\n",
" experiment/\n",
" crtt_exp.py\n",
" stimuli/\n",
" shapes/\n",
" sourcedata/\n",
" sub-ID\n",
" sub-ID/\n",
" derivatives/\n",
" pipeline/\n",
" sub-ID\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "0c38a4ab-3b90-43c5-9fd7-857ae0d2abb0",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} Do you know what each aspect should entail?\n",
":class: tip, dropdown\n",
"\n",
"Let's have a look at the `directories`!\n",
"\n",
"```bash\n",
"choice_rtt/\n",
"```\n",
"\n",
"`choice_rtt` is our `root directory` within which we will store everything else, ie the highest level.\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" code/\n",
" expertiment/\n",
" crtt_exp.py\n",
"```\n",
"\n",
"Within `code`, we are going to store every piece of `code` related to the `project`/`dataset`. For example, `code` used to acquire `data` (as in our example the `experiment` `directory`, `data` `conversion` or `data` `analyses`.\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" stimuli/\n",
" shapes/\n",
"```\n",
"\n",
"The `stimuli` `directory` will store all `stimuli` used during `data` `acquisition`, ie within the `experiment`.\n",
"\n",
"\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" sourcedata/\n",
" sub-ID\n",
"```\n",
"All `acquired` `data` in its \"`source`\" form, ie before `conversion` and/or `standardization` will be stored under `sourcedata`. Importantly, each participant will get their own directory in there. \n",
"\n",
"\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" sub-ID/\n",
"```\n",
"\n",
"`raw` `data`, ie `data` after `conversion` and `standardization` should be placed within a participant-specific directory, called `sub-ID` where `ID` is the participant identifier. All respective directories are stored directly at `root`.\n",
"\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" derivatives/\n",
" pipelineID/\n",
" sub-ID\n",
"```\n",
"\n",
"The `derivatives` `directory` will store all `derivatives` obtained through applying some form of `data processing` to the `raw` `data`, ie in `sub-ID`. Within it, there should be one directory for each processing `pipeline`, e.g. `preprocessing`, `statistics`, etc. and within it usually one `directory` per `participant` in the form of `sub-ID` and/or `group` in case `results` are aggregated across participants.\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "0778298b-d811-4dae-be9b-02f020bf7d13",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Importantely, if you don't have these directories yet, please make sure to create them via:\n",
"\n",
"```bash\n",
"mkdir path/to/choice_rtt/sourcedata\n",
"mkdir path/to/choice_rtt/derivatives\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "f53af45b-dbc9-4450-9b41-7f98e896727d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Only adapting the `string` of the `Data filename` to \n",
"\n",
"\n",
"```python\n",
"u'%s/sub-%s/ses-%s/%s_%s_%s_%s' % (expInfo['output-path'], expInfo['participant'], expInfo['session'], expInfo['participant'], expInfo['session'], expName, expInfo['date'])\n",
"```\n",
"\n",
"unfortunately won't be enough, as we have to make that the respective `directories` for a given participant and `session` are created before we attempt to save `files` there."
]
},
{
"cell_type": "markdown",
"id": "c9118efa-dc32-47a5-a4d2-c02f76e50ee6",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"But no worries: we can just add `Custom` `Code` `Component` that does this for us. Let's place at the beginning of the `welcome` `routine` so that it immediately creates the `directory structure` we need. Here's the respective `python code`:\n",
"\n",
"```python\n",
"# Construct the path with placeholders replaced by actual values\n",
"dir_path = expInfo['output-path'] + \"/sub-\" + expInfo['participant'] + \"/ses-\" + expInfo['session']\n",
"\n",
"# Check if the path exists\n",
"if not os.path.exists(dir_path):\n",
" # Create the directory, including all intermediate directories\n",
" os.makedirs(path)\n",
" print(f\"Directory '{dir_path}' was created.\")\n",
"else:\n",
" print(f\"Directory '{dir_path}' already exists.\")\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "6281bccf-d3f7-4659-9a49-54924e60a973",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"`````{admonition} How would you add this component to the welcome routine?\n",
":class: tip, dropdown\n",
"\n",
"- open the `welcome` `routine`\n",
"- select the `Custom` `Code` `Component`\n",
"- copy-paste/write the `python code` from above within the `Begin Routine` tab\n",
"\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"- click `OK`\n",
"\n",
"`````"
]
},
{
"cell_type": "markdown",
"id": "0ef243db-740d-40b8-a4c4-4bdaad2d20e1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"With that in place, we adapt the `Data filename` `string` as indicated above:\n",
"\n",
"```python\n",
"u'%s/sub-%s/ses-%s/%s_%s_%s_%s' % (expInfo['output-path'], expInfo['participant'], expInfo['session'], expInfo['participant'], expInfo['session'], expName, expInfo['date'])\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "7f4ca96b-5368-4d16-b113-ff32329b3445",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"When running the `experiment` now, you should be able to specify a `path` in the `GUI dialog box`, which should be `/path/to/choice_rtt/sourcedata`, and the `data output files` should be stored under this `directory` in the aforementioned structure: `/path/to/choice_rtt/sourcedata/sub-ID/ses-ID/`. \n",
"\n",
"Give it a try!"
]
},
{
"cell_type": "markdown",
"id": "afa8637a-e99c-4e80-b9e6-8a3afbacab85",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"That seemed to have worked out great, awesome! This concludes our `data output` adventure and bring us to the next chapter.\n",
"\n",
"- Running experiments (using python)\n",
"- Introduction to PsychoPy\n",
" - basic overview\n",
" - basic working principles\n",
"- **PsychoPy**\n",
" - stimuli & trials\n",
" - data output\n",
" - **a very simple experiment**\n",
"- PsychoPy advanced\n",
" - Eye-Tracking and neuroimaging\n",
" - online experiments\n",
"- Outro/Q&A"
]
},
{
"cell_type": "markdown",
"id": "d3ed4cc5-bb88-439c-a15b-d7ae4a661770",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"### A very simple experiment\n",
"\n",
"By now, we have already explored quite a lot of `PsychoPy`'s functionality, even though our `experiment` is quite simple. Speaking of which: we actually didn't implement the `experiment trials` yet, only the practice ones.\n",
"\n",
"In order to evaluate if we did a good job of teaching you the respective `PsychoPy` aspects and find potential gaps/problems, we kindly ask you to add the `experiment trials` to the `experiment`. Below you find a bit more information on how to do it, as well as the answer. However, it would be cool if you could give it an honest try first. After 30 min., we will go through the corresponding steps together."
]
},
{
"cell_type": "markdown",
"id": "8a7cded8-e566-44cf-a2e1-31c63a44f9f3",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} Task 1 - Adding instructions for the actual trials\n",
"\n",
"Similar to the `instructions` we provided for the `practice trials,` we should provide some for the actual `trials.` Thus, please add/do something to achieve the following:\n",
"\n",
"- the `instructions` should appear after the `practice trials` and before the actual `trials`\n",
"- the following `text` should be displayed: \"Well done, you have finished the practice! Now for the main experiment. Press space to continue!\"\n",
" - there should be `line breaks` between the sentences\n",
"- participants should be able to end the `instructions` and start the `experiment` by pressing the `space bar` and only the `space bar`\n",
"\n",
"`````{admonition} Answer\n",
":class: tip, dropdown\n",
"\n",
"\n",
"Step 1: Copy-paste the `instructions_shape` `Routine`\n",
"\n",
"- the `instructions_shape` `Component` almost has all the things we need, except for the `text` which we can simply replace\n",
"- select the `instructions_shape` `Component` in the `Routine` `window`\n",
"- click on \"`Experiment`\" in the top menu bar and select `Copy Routine`\n",
"- click on \"`Experiment`\" in the top menu bar and select `Paste Routine`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"- rename it, e.g. to \"`instructions_shape_experiment`\"\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 2: Remove unnecessary `Components`\n",
"\n",
"- remove all `Image` `Components` via right-clicking on them and selecting \"`remove `\"\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 3: Adapt the `Text`\n",
"\n",
"- open the `Text` `Component` and exchange the `Text` to the one provided above, adding `line breaks` between sentences\n",
"- optional: assign the `Component` a new name\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 4: Insert the `Component`\n",
"\n",
"- select \"`Insert Routine`\" and choose `instructions_shape_experiment`\n",
"- place it after the `practice trial` `for loop` and click on the dot\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Done!\n",
"\n",
"NB: While you also could have simply created a new `Routine` and add the respective `Components`, we wanted to show you the `Copy-Paste Routine` functionality. Here, it's actually more work and takes longer, but for more complex and heavy `Routines`, this functionality comes in handy!"
]
},
{
"cell_type": "markdown",
"id": "ddbe2496-7c25-4358-b3f3-8917174a6fc7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Great job, y'all! Let's step it up with the next task."
]
},
{
"cell_type": "markdown",
"id": "4511cefe-9dfc-4ede-9545-953ca87b2197",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"```{admonition} Task 2 - Adding the actual trials\n",
"\n",
"Now it's time to add the actual `experiment trials.` Thus, please add/do something to achieve the following:\n",
"\n",
"- the `experiment trials` should appear after the `experiment instructions` and before the `end screen`\n",
"- they should be identical to the `practice trials` in terms of `cues`, `targets`, `keyboard responses`, etc.\n",
"- every `stimulus` in the `stimuli list` should appear `2` times\n",
"\n",
"`````{admonition} Answer\n",
":class: tip, dropdown\n",
"\n",
"Given that the actual `trials` should be identical to the `practice trials` (in terms of their structure, etc.), we can simply make use of the `trial` `Routine` again and then add a new `for loop`.\n",
"\n",
"Step 1: Re-use the `trial` `Component`\n",
"\n",
"- the `trial` `Routine` has all the things we need\n",
"- click on `Insert Routine` and select `trial`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"- place it between the `instructions_shape_experiment` and `end_screen` `Routine`\n",
"- click on the dot\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Step 2: Add a `for loop`\n",
"\n",
"- click on `Insert Loop`\n",
"- place the first dot before the just added `trial` `Routine` and the second on behind it\n",
"- name it, e.g. to \"`exp_loop`\"\n",
"- set `nReps` to `2`\n",
"- for `Conditions` select the `stimuli list` file, `shapes_targets.csv`\n",
"- click `OK`\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Done!\n",
"\n",
"NB: As in Task 1, we could have created a new `Routine` from scratch but in this case re-using an already existing one was definitely faster."
]
},
{
"cell_type": "markdown",
"id": "28fc23ed-ee9e-4d2f-8b1d-357f4d938562",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Fantastic work everyone. However, there's one last thing you need to do."
]
},
{
"cell_type": "markdown",
"id": "1f56c316-74b5-4922-9ffe-4544d212b58b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"```{admonition} Task 3 - Run the experiment\n",
"\n",
"After all this work, you should check what you have created and of course also test things. Thus, please go through the entire experiment at least once and afterwards have a look at the data!\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "abfa01d6-e034-40a7-ae9c-9e640f859e57",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"That concludes our adventure into the basics of `PsychoPy`. While this was a lot already, we still wanted to talk about some advanced topics. \n",
"\n",
"- Running experiments (using python)\n",
"- Introduction to PsychoPy\n",
" - basic overview\n",
" - basic working principles\n",
"- PsychoPy\n",
" - stimuli & trials\n",
" - data output\n",
" - a very simple experiment\n",
"- **PsychoPy advanced**\n",
" - Eye-Tracking and neuroimaging\n",
" - online experiments\n",
"- Outro/Q&A"
]
},
{
"cell_type": "markdown",
"id": "02dfb25b-7ffd-4003-b9ac-7139f0183fd1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## PsychoPy advanced\n",
"\n",
"After exploring a lot of `PsychoPy`'s basic functionalities, we will also have a look at more advanced topics that are, however, common and important for everyday research work. Specifically, we will briefly talk about how to integrate `Eye-Tracking`, `EEG` and `fMRI` into `PsychoPy`, as well as have a look at running online studies based on `PsychoPy`."
]
},
{
"cell_type": "markdown",
"id": "63a83417-3338-465d-9f21-7a7ce437ff90",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"## ET, EEG and fMRI\n",
"\n",
"It should come to no surprise that `PsychoPy` supports the integartion of several external hardward very well. This includes:\n",
"\n",
"- `Eye-Tracking`\n",
"- `EEG`\n",
"- `fMRI`\n",
"- `microcontrollers`, e.g. `Arduino`s\n",
"- `fNIRS`\n",
"\n",
"Within this brief exploration, we will focus on `Eye-Tracking`, `EEG` and `fMRI`."
]
},
{
"cell_type": "markdown",
"id": "539077b7-320c-499f-9edb-7bf0ff9e21cb",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### Eye-Tracking\n",
"\n",
"[`PsychoPy` has several options and functionalities](https://psychopy.org/hardware/eyeTracking.html#eyetracking) to connect to and communicate with `Eye-Tracking` systems, directly via the `Builder` or the `Coder`.\n",
"\n",
"Overall, adding `Eye-Tracking` to your `PsychoPy` `experiment` is a two-stage approach: `Eye-Tracker` setup/configuration, followed by integrating `Eye-Tracking` `Components`."
]
},
{
"cell_type": "markdown",
"id": "4e09694b-3c3c-4c83-9bda-4cc59bbff83a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Setup/Configuration\n",
"\n",
"The `setup` and `configuration` of an `Eye-Tracker` is comparably easy and straightforward (We know, we know...we say this all the time.), as `PsychoPy` supports multiple common systems, including `SR Research` and `Tobii Technology`.\n",
"\n",
"Initially, you need to access the `Eye-Tracking` tab of the `experiment` settings which you find via the `⚙️` icon in the top menu bar and select the device you're using.\n",
"\n",
"Depending on the device, you can then set a variety options and information such as the model and serial number of your device.\n",
"\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "b263da6e-52b1-4b6e-9a83-1cf765780a8b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### SR Research\n",
"\n",
"\n",
"Please make sure to also have a look at the specific requirements outlined [here](https://psychopy.org/hardware/eyeTracking.html#eyelink)."
]
},
{
"cell_type": "markdown",
"id": "63b0b88d-9c40-4283-b949-8f8eaa4341d6",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"##### Tobii Technology\n",
"\n",
"Please make sure to also have a look at the specific requirements outlined [here](https://www.psychopy.org/hardware/eyeTracking.html#tobii)."
]
},
{
"cell_type": "markdown",
"id": "1ccb7562-c80e-4658-97f0-ccbd7a2fafa5",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### MouseGaze\n",
"\n",
"Don't have an `Eye-Tracker` ready to test wherever you're working on your `PsychoPy` `experiment` and/or just want to simulate respective `Eye-Tracking` `data`? No worries at all, `PsychoPy` has your back. \n",
"\n",
"You can select `MouseGaze`. This will allow your `mouse cursor` to act as a `gaze point` on your `screen`, and so allow you to `simulate eye movements` without using an `Eye-Tracker`. Then, when you’re ready to use your `Eye-Tracker`, you can just select it from the `Experiment Settings` and run your `experiment` in the same way.\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "e7cd5825-ede0-41bb-9b59-0bd97961670d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Eye-Tracking Components\n",
"\n",
"`PsychoPy` comes with a core set of `Component` specifically for `Eye-Tracking`. You can find them in the `Component window`. \n",
"In general, there are two types of `Components`: `calibration`/`validation` and `recording`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c4f7f20e-b8f7-4683-b345-474372a7699c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### `Calibration`/`Validation`\n",
"\n",
"When adding `Eye-Tracking` to your `experiment`, you usually want to start with adding the `Calibration` and `Validation` `Component` to the beginning of your `experiment`, ie the very first things that should happen.\n",
"\n",
"Even though you select them from the `Component window`, they're added as `standalone Routines` to your `experiment`."
]
},
{
"cell_type": "markdown",
"id": "7ff0f48d-ff28-4af8-9e96-fc74f6cff654",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"You can then select and set different options for these `Routines`, e.g. the number of `calibration points` and their `properties` (e.g. `color`, `size`, etc.). \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c10474e1-11f2-4597-a27e-c02f14ccba6c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### Recording - General\n",
"\n",
"To actually start `recording` `Eye-Tracking` data, you need to add the `Eyetracker Record` `Component` as this `starts` and `stops` the `Eye-Tracker` recording. Usually, you would add this `Component` to your `instructions` `Routine` or something similar, so that your `Eye-Tracker` is set off `recording` before your `trials` start, but you can add them in wherever makes sense for your `experiment`. As you can see below, you simply add it to the `Routine` during which you want to `start`/`stop` the `Eye-Tracker`.\n",
"\n",
"Importantly, this `Component` does it all: `start` only, `stop` only and `start` and `stop` after a certain time.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "a061379f-ef55-4978-9b0f-a5ba8975bf7a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### Recording - ROIs\n",
"\n",
"If you want to record information on `gaze position`, or you want something to happen when the `participant` looks at or away from a certain part of the `screen`, the `ROI` `Component` is your best friend. The `ROI` `Component` has lots of options - you can choose what should happen given a certain `gaze position`/`pattern`, what shape the `ROI` has etc. All of which can also be defined in a `conditions file` (like we used for `for loop`), just like any other `Component`. Simply, choose the options that fit the needs of your `experiment`. \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "2df2228d-d640-4723-b9e3-3701890599da",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"For our `experiment`, we could for example use the information, ie `variables`, generated in the `Custom Code` `Component` that jitters the `onset` and `position` of the `target` to indicate the `ROI` position and another `Custom Code` `Component` that interacts with the `data` generated by the `ROI Component`. In that way, the `ROI` `Component` behaves similarily to a `visual Component`, e.g. `Image`. "
]
},
{
"cell_type": "markdown",
"id": "0a4c1de5-4586-4120-ad3a-35674e15b0ea",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### EEG\n",
"\n",
"Integrating `EEG` `data acquisition` in your `PsychoPy` `experiment` is no biggie either, although no dedicated `Components` exist. However, there are two important prerequisites and caveats you have address before you start.\n",
"\n",
"##### Prerequisites and caveats\n",
"\n",
"At first, you have to think about problems inherent to `EEG data acquisition` in general, specifically `timing and synchronization issues` as [outlined here](https://psychopy.org/general/timing/index.html). "
]
},
{
"cell_type": "markdown",
"id": "04a86705-2ee3-4456-9fdf-9f03cc3a3ad2",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"###### Caveats\n",
"\n",
"We briefly talked about that at the beginning of this session, when we had a look at the [Timing Mega Study by Bridges et al. 2020](https://peerj.com/articles/9414/): there can be issues concerning `dropped frames`, `lag` and `variability`, `monitor refresh rate` ([Poth at al., 2018](https://link.springer.com/article/10.3758/s13428-017-1003-6)) and so on. \n",
"\n",
"In order to address these aspects and mitigate potential problems, it's recommended to:\n",
"\n",
"- use the `Builder` as it automatically optimizes the `code` (e.g. `draw` and `flip`) concerning these problems\n",
"- repeatedly `test` your `experiment` and evaluate `timings`\n",
"\n",
"This is especially important for `EEG` where you have to work with `millisecond precision`."
]
},
{
"cell_type": "markdown",
"id": "8d45c43c-1eaa-4eca-a375-d3a00f99a0c2",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### Parallel & Serial Ports\n",
"\n",
"When connecting the machine you run your `PsychoPy` `experiment` on and the `EEG system` you're using, you need to find out two aspects and set things in your `experiment` respectively:\n",
"\n",
"1. The `port address` your `EEG acquisition system` is connected to.\n",
"\n",
"2. How your `EEG acquisition system` wants to `receive` the `triggers` you send.\n"
]
},
{
"cell_type": "markdown",
"id": "01eb32f5-539f-4e6b-bbc6-27097182b409",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Why is this important? When acquiring `EEG` `data` via any `software`, including `PsychoPy`, you usually want to send `triggers` to the `EEG system` do denote `what` (e.g. `conditions`, `stimuli`) happened `when` (e.g. `onset` and `duration`), so that this information is added to the recorded `data` and available in the acquired `EEG` `data`."
]
},
{
"cell_type": "markdown",
"id": "37d3c5ab-c1e1-4951-9ed8-a4d719129d6b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Concerning 1., you have to find out if your setup is using a [Parallel Port](https://www.psychopy.org/hardware/parallelPortInstr.html#parallel) or a [Serial Port](https://www.psychopy.org/hardware/serialPortInstr.html#serial). Then you can select the respective `Component` from the `Component window`, either within `EEG` or `I/O` and place it in the `Routine` you would like to add triggers to.\n",
"\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "65f396d6-66f8-474a-bcd6-6b562bc0db84",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Each comes with a range of settings and options, such as\n",
"\n",
"Insert a Serial Out component to the routine that you'd like to add triggers to\n",
"\n",
"- `Start`: when you want the `trigger` to be sent; this can be at a certain `time` or `frame`, or we can set the `trigger` to be sent when a certain `condition` is met (more on this later)\n",
"\n",
"- `Stop`: for how long the `trigger` should be sent\n",
" \n",
"- `Port` refers to the `address` of the `port` your `device` is connected to\n",
"\n",
"- `Start data` refers to the value that you want to actually send to your `acquisition system` - exactly what you'd like to send will depend on what your `system` wants to receive. This can take a `variable` just like most other `fields` so that you can send `different triggers` for `different stimuli` etc.\n"
]
},
{
"cell_type": "markdown",
"id": "cd33d812-3786-4f90-83e9-26aae1a2140d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c8965729-e298-4900-be62-564dc02efc8e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Both `Components` are essentially set up in the same manner, except that the `Parallel port` has the `properties` spread across tabs."
]
},
{
"cell_type": "markdown",
"id": "d0219aca-d98a-424e-adde-e46c40046ed4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"In case you have to add a `port address`, you can do so via `Preferences` -> `Hardware`: \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "4b54020a-afb7-4165-8f67-d9062d86b382",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Regarding 2., you have to carefully think about how you want to send `triggers`, ie as addressed before `when` and for `how` long, the `trigger` itself and so on. \n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "8567b3b1-0991-46a5-a9fc-680c709762ba",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"\n",
"\n",
"As mentioned before, you usually want to set the `Start` to `condition` so that the `trigger` is send when something happens.\n",
"\n",
"To send on the `onset` of something (e.g., a `visual stimulus`) set that `condition` to be (where `stimulus` is the name of the `Component` you want to yoke your `trigger` to, e.g. in our example `TargetImage`):\n",
"\n",
"```\n",
"stimulus.status == STARTED\n",
"```\n",
"\n",
"To send on a `keypress`, keep the `Start` as `Condition` and set the `condition` to be (where `key_response` is the name of your `Keyboard` `Component`):\n",
"\n",
"```\n",
"key_response.keys\n",
"```\n",
"\n",
"To send when a `stimulus` is clicked by a `mouse` (`mouse` here refers to the name of your `mouse` `Component`, and `stimulus_mouse` refers to the `stimulus` you want to be clicked):\n",
"\n",
"```\n",
"mouse.isPressedIn(stimulus_mouse)\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "965d82ae-8e8b-4223-83ba-147389413f3a",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### fMRI\n",
"\n",
"Generally, rather than programming your `PsychoPy` `experiment` to send `triggers` to some `hardware` in the same way as, e.g. `EEG`, with `fMRI` you would want to set up your `experiment` so that it waits until it has detected when the `scanner` has sent out a `trigger` before moving on to present `trials`.\n",
"\n",
"Comparable to the other external hardware we talked about before, `PsychoPy` also allows you to run `fMRI` `experiment` and the respective implementation is done in a two-step-approach: the `MRI scanner setup` and `trigger handling`. "
]
},
{
"cell_type": "markdown",
"id": "abacb060-20e4-4ff9-960a-c240ccfee5e5",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### MRI scanner setup\n",
"\n",
"Before doing anything else, it’s important that you know how the `scanner` you’ll be using will emit these `triggers`, and whether these are converted to some other signal such as `characters` on a `serial port` or a `simulated keypress`. In general, there are at least 3 ways a `scanner` might send a `trigger` to your `experiment`:\n",
"\n",
"- emmulate a `keypress`\n",
"\n",
"- via `parallel port`\n",
"\n",
"- via `serial port`\n"
]
},
{
"cell_type": "markdown",
"id": "203e66c5-2cf9-42b1-bbbd-5e60415511e7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Trigger handling\n",
"\n",
"A `Routine` to detect `fMRI` `triggers` is really simple to set up. Regardless of the method your `scanner` uses to send the `triggers`, you’ll just need a `Routine` that `waits` until it has detected the `trigger` before moving on. \n",
"\n",
"A common approach is to create a new `Routine` and insert a `Text` `Component` that says \"`Waiting for Scanner`\".\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "07e359d3-b949-4b97-a265-d1c119929c2e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### The scanner simulates a key press\n",
"\n",
"Insert a `Keyboard` `Component` to your `\"Waiting for Scanner\"` `Routine`. In `allowed keys` use the `key` that the `scanner` will send e.g. if the `scanner` sends a `5`, the `allowed keys` will be `5`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "6c151b97-5777-4128-a371-97b9706aa768",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Now, when the `keypress` is detected, the `\"Waiting for Scanner\"` `screen` will end and the next `Routine`, e.g. `trial`s start. Although, be careful! `PsychoPy` doesn’t know the difference between the `emulated key presses` sent from the `scanner` and `key presses` made by a `participant`! So take care not to type on the `keyboard` connected to the `PsychoPy` computer whilst your `experiment` runs to avoid your `key presses` being mistaken for `triggers`."
]
},
{
"cell_type": "markdown",
"id": "63d99020-ca0b-4052-83b9-19e130e056dc",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"##### The scanner communicates via a port\n",
"\n",
"Regardless if `serial` or `parallel port`, you want to add a `Custom Code` `Component` to set up the respective `port` and check for `triggers`."
]
},
{
"cell_type": "markdown",
"id": "1d293252-64c2-4f34-a841-b2e02658d34c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"In the `Begin Experiment` tab of the `Code` `Component`, add the following `code` to set up a `Parallel Port`:\n",
"\n",
"```python\n",
"from psychopy.hardware.parallel import ParallelPort\n",
"triggers = ParallelPort(address = 0x0378) #Change this address to match the address of the Parallel Port that the device is connected to\n",
"pinNumber = 4 #Change to match the pin that is receiving the pulse value sent by your scanner. Set this to None to scan all pins\n",
"```\n",
"\n",
"and for a `Serial Port`:\n",
"\n",
"```python\n",
"from psychopy.hardware.serial import SerialPort\n",
"triggers = SerialPort('COM3', baudrate = 9600) #Change to match the address of your Serial Port\n",
"trigger = '1' #Change to match the expected character sent from your scanner, or set to None for any character\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "815ee585-3591-415b-9801-1846f425fe1d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"In the `Each Frame` tab of the same `Code` `Component`, add the following `code` to check for `triggers`.\n",
"\n",
"For a `Parallel Port`:\n",
"\n",
"```python\n",
"if frameN > 1: #To allow the 'Waiting for Scanner' screen to display\n",
" trig = triggers.waitTriggers(triggers = [pinNumber], direction = 1, maxWait = 30)\n",
" if trig is not None:\n",
" continueRoutine = False #A trigger was detected, so move on\n",
"```\n",
"\n",
"and for a `Serial Port`:\n",
"\n",
"```python\n",
"if thisTrigger in self.read(self.inWaiting()):\n",
" continueRoutine = False #Our trigger was detected, so move on\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "571aec28-66f9-447c-b9ad-c4a7403d6f63",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Lastly, you have to make sure that your `experiment` keeps in synch with the `MRI scanner` to avoid timing issues. In more detail, if you only synch the `experiment` and the `MRI scanner` in the beginning of the `experiment`, their timescales might get out of synch as the `experiment` progresses. Thus, it's a good idea to add a synchronization to certain parts of the `experiment`, e.g. when a `block` of `trials` is over, etc. . This can basically be implemented via the approach described above."
]
},
{
"cell_type": "markdown",
"id": "0bce27fa-56af-4bb0-b388-e6bd8920406f",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "slide"
},
"tags": []
},
"source": [
"## Running experiments online - PsychoPy & Pavlovia\n",
"\n",
"After our brief tour of communicating with and integrating `external hardware`, we will also have a brief look at running `PsychoPy` `experiment`s online via its connection to [Pavlovia](https://pavlovia.org/docs/home/about)."
]
},
{
"cell_type": "markdown",
"id": "a7938191-9a27-4b28-8e8a-f5d206d31d34",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"### Pavlovia\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"\n",
"[Pavlovia](https://pavlovia.org/docs/home/about) is a secure server for running `experiments` and storing `data`. It's built upon a `git-based version control` system and has a huge open-access library of `experiments` (that you can add to!). Additionally, it's place for creating and running Surveys (using [Pavlovia Surveys](https://pavlovia.org/docs/surveys/overview))."
]
},
{
"cell_type": "markdown",
"id": "122dca01-588c-4284-841e-5ba595cf1e08",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Account & costs\n",
"\n",
"In order to get started, you only need to create a **free** account using your institutional (or other) Email address. Afterwards, you can immediately start using `Pavlovia`, e.g. uploading your `PsychoPy` `experiment` or creating a `survey`.\n",
"\n",
"While the account itself is free, acquiring data is not. However, with only ~ `0.28 Cents` to store one `participant`'s `data`, it's comparably cheap (e.g. acquiring `data` from `1000` `participants` would be less than `300 Euro`).\n",
"\n",
"Regarding this, you need to buy [Pavlovia Credits](https://pavlovia.org/store/credits) through the [Pavlovia Store](https://pavlovia.org/store).\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "a166ad18-c378-4bbb-a390-14264762dc99",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Experiments library and dashboard\n",
"\n",
"As mentioned before, `Pavlovia` has a huge open-access library of `experiments` that you can browse, re-use and adapt. Furthermore, it allows you to query the library concerning certain properties.\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"Your `experiments` are stored in the `dashboard` which has the same interface as the open-access library. You can of course choose if your `experiment` should be `public` or `private`.\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "3fd3df77-8782-4fef-b3b9-62d4ff4a4ac9",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Launching an experiment\n",
"\n",
"`Pavlovia` evolved from the `PsychoPy` realm and those the two have a direct connection that you can leverage to launch the `experiment`, you created locally via `PsychoPy`, online via `Pavlovia`.\n",
"\n",
"When you make an `experiment` in `PsychoPy`'s `Builder`, your `experiment` can compile to `python` (for running offline) or `JavaScript` (which allows it to run in a browser). `Pavlovia` acts as the way of hosting the `JavaScript` file online (it handles creating a URL for you to share with your `participant`, saving the `data` etc.). \n"
]
},
{
"cell_type": "markdown",
"id": "5d535a1c-b3bc-4e05-a5a7-f86c100100d4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Initially, you have to make sure your files are set up such that you have one directory that only contains one `.psyexp` file and the files needed to run the `experiment`. For example, in our `experiment` we have:\n",
"\n",
"```bash\n",
"choice_rtt/\n",
" code/\n",
" experiment/\n",
" crtt_exp.psyexp\n",
" crtt_exp.py\n",
" shapes_targets.csv\n",
"```\n",
"\n",
"Which means, that we almost have everything ready to go. We would only need to add the `stimuli files`so that they're included as well. "
]
},
{
"cell_type": "markdown",
"id": "775ca4de-0e65-47f4-ab96-86dc664467cf",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"After that's done, we have to open the `experiment` in the `Builder` and log into `Pavlovia`.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "a7d108da-b03f-4127-ad07-1a9c16ba2d20",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"You can now sync your `experiment` to `Pavlovia`:\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "c755fd2d-4996-4422-8bf2-5c911acaa9dd",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"Select `project info` then select the `hyperlink`, that will take you to your `Pavlovia` project (if you are not logged into `Pavlovia.org` already in the browser you may need to log in to view).\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"\n",
"
\n",
" \n",
"
\n",
"\n",
"\n",
"
\n",
" \n",
"
"
]
},
{
"cell_type": "markdown",
"id": "8e6a847b-9a12-4d56-9178-a263813a47ad",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"You can then set your `experiment` to `running` and given you have `Pavlovia credits`, you start acquriring `data` by sharing the `link` to the `experiment` with your `participants`.\n",
"\n",
"
\n",
" \n",
"
\n"
]
},
{
"cell_type": "markdown",
"id": "3afaf02e-94be-4308-933a-1525b2d07add",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"Once your `participant` completes the `experiment`, you can view `data` in one of two ways. Either select `\"Download Results\"` or `\"View Code\"` to see all the `data` in the `\"data\"` subfolder of your `project`."
]
},
{
"cell_type": "markdown",
"id": "359a252a-7c1e-4cd5-aaa6-449efa2c9c12",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Integration with recruitment platforms\n",
"\n",
"Sometimes you might want to connect your `experiment` with other platforms. For example, you might use a third party service to help with recruitment (such as [prolific](https://www.prolific.com/) or [SONA](https://www.sona-systems.com/)). In these cases you want your `experiment` to go through something like the following chain: \n",
"\n",
"- `recruitment website`: `participant`s discover your `experiment` and are issued a `participant ID`\n",
"- `experiment` and/or `survey`: `participant` completes the `experiment` and/or `survey`\n",
"- `recruitment website`: mark as complete so `participants` can be reimbursed if needed\n",
"\n",
"and you want information like \"participant ID\" as well as other info to be passed forward through the chain. "
]
},
{
"cell_type": "markdown",
"id": "859ba2d3-0d61-446f-bafb-7f4749d52e4b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"How does this work?\n",
"\n",
"`Information` is passed to a website using something known as a `query string`. This is essentially any information that follows a \"`?`\" in the `URL` in your browser. For example, your `experiment` could have the `URL`: \n",
"\n",
"`run.pavlovia.org/your_user_name/your_experiment_name/`\n",
"\n",
"This would likely start up your `experiment` with a dialogue window where you can type `participant` and `session`. You could however provide the respective values already within the `URL` using a `query string`, like this:\n",
"\n",
"`run.pavlovia.org/your_user_name/your_experiment_name/?participant=123&session=1`\n",
"\n",
"Here the last part of the `URL` is a `query string`, and you will notice that this autocompletes the fields \"`participant`\" and \"`session`\" in the `startup dialogue` of your `experiment`. This is what happens when you daisy chain with other platforms, platform 1 will send `information` via a `query string` to platform 2, then platform 2 sends it to platform 3 and so on…"
]
},
{
"cell_type": "markdown",
"id": "717149c8-6d29-4f20-bc1e-6f3f7ae55a0d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"source": [
"The exact implementation, however, depends on the `recruitment platform` you are using."
]
},
{
"cell_type": "markdown",
"id": "7e0b9d9e-1427-4284-8759-150e01e2ef4d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": "subslide"
},
"tags": []
},
"source": [
"#### Git-based features\n",
"\n",
"By now, you might have already noticed that `Pavlovia` exists on `Gitlab`. As we have learned in the [Git & Gitlab session](https://julia-pfarr.gitlab.io/nowaschool/materials/Git/overview_Git.html), it's pretty feature rich and allows many neat features and control over your projects:\n",
"\n",
"`Version control`: view the version history of your `experiment` and restore old versions \n",
"\n",
"`Collaboration`: Add team members or `fork` projects to groups\n",
"\n",
"`Privacy settings`: Make your project public or private\n",
"\n",
"You can access the `Gitlab repository` of your project by selecting \"`View code`\" on your `project dashboard`. "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "nowaschool",
"language": "python",
"name": "nowaschool"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}