From 18e338437d32b4019d0c283ef131be3108822aca Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Thu, 5 Mar 2026 09:28:27 +0000 Subject: [PATCH 01/67] Remove unecessary conditional in build action --- .github/workflows/build_docs.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index 774b8d8..e8493f3 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -49,7 +49,6 @@ jobs: - name: Checkout psychopy (manual choose branch) uses: actions/checkout@v4 - if : ${{ github.event_name == 'workflow_dispatch' }} with: repository: psychopy/psychopy path: psychopy @@ -65,7 +64,6 @@ jobs: - name: Checkout psychopy-docs (manual choose branch) uses: actions/checkout@v4 - if : ${{ github.event_name == 'workflow_dispatch' }} with: repository: psychopy/psychopy-docs path: docs From 3589ba1ed3491fef23f6fee029945fb2f2b43b69 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Thu, 5 Mar 2026 17:58:58 +0000 Subject: [PATCH 02/67] Added alt text for Text Component dialog --- source/gettingStarted.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/gettingStarted.rst b/source/gettingStarted.rst index 10cada9..c2daf36 100644 --- a/source/gettingStarted.rst +++ b/source/gettingStarted.rst @@ -39,6 +39,7 @@ Start |PsychoPy|, and be sure to be in the Builder view. * Click on a Text component and a Text Properties dialog will pop up. .. image:: /images/textComponentApril24.png + :alt: The dialog box of the Text Component from 2024 :width: 80% :align: center From 15bc126156b456fba54776a66764a36862df5b65 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:50:35 +0000 Subject: [PATCH 03/67] DOC: Add alt text for Stroop task image Added an alt text description for the Stroop task image. --- source/tutorials/experiments/stroop.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/tutorials/experiments/stroop.rst b/source/tutorials/experiments/stroop.rst index 499ed73..0403784 100644 --- a/source/tutorials/experiments/stroop.rst +++ b/source/tutorials/experiments/stroop.rst @@ -21,6 +21,7 @@ OK step 1 of any experiment is to consider "what makes the main stimuli in my tr .. image:: ./stroop_basic_flow.png :width: 100 % + :alt: A screenshot of the routine tab in builder showing the components used to make a Stroop task. From top to bottom the components read as "fixation" (A textbox component), "word" (another textbox component) and "key_resp" (a keyboard component). Setting up our conditions file From 8822f872f3a4d460e42eb0d0f60752a5efb3f18e Mon Sep 17 00:00:00 2001 From: Cecilia Baldoni <111133332+cecibaldoni@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:50:41 +0100 Subject: [PATCH 04/67] DOC: added alt-text to coloursPicker.png --- source/general/colours.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/general/colours.rst b/source/general/colours.rst index b23eda0..6bfb74c 100644 --- a/source/general/colours.rst +++ b/source/general/colours.rst @@ -5,6 +5,7 @@ Color spaces .. image:: /images/colorPicker.png + :alt: "A screenshot of the PsychoPy software interface, featuring two overlapping configuration windows used to define the visual properties of a polygon" *You can explore colors in PsychoPy Builder through accessing the color picker from any parameter that takes a color value.* From a86dada1746985dd8dde1f806e2e02d72a67932b Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:50:41 +0000 Subject: [PATCH 05/67] DOC: Added alt text to device manager icon --- source/general/deviceManager.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/general/deviceManager.rst b/source/general/deviceManager.rst index 4688950..31e3b00 100644 --- a/source/general/deviceManager.rst +++ b/source/general/deviceManager.rst @@ -10,6 +10,7 @@ Device Manager .. image:: /images/deviceManagerIcon.png :width: 5% :align: left + :alt: "The device manager icon" |PsychoPy| includes a Device Manager to help you set up and manage your hardware devices, such as monitors, keyboards, mice, and other input/output devices. @@ -31,4 +32,4 @@ Devices you might configure include: - Light sensors (including a light sensor emulator for emulating a photodiode, for validating visual timing) - Cameras - Button boxes -- Serial devices \ No newline at end of file +- Serial devices From ad6f3051884c037e59028eb21694176e3b38f041 Mon Sep 17 00:00:00 2001 From: ebobrowska Date: Mon, 9 Mar 2026 13:50:43 +0100 Subject: [PATCH 06/67] DOC: Added alt text to monitor center icon --- source/general/monitors.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/general/monitors.rst b/source/general/monitors.rst index fede163..2868506 100644 --- a/source/general/monitors.rst +++ b/source/general/monitors.rst @@ -4,6 +4,7 @@ Monitor Center ==================================== .. image:: /images/monitor_center.png :width: 5% + :alt: "Monitor center icon" |PsychoPy| provides a simple and intuitive way for you to calibrate your monitor and provide other information about it and then import that information into your experiment. From 9cbb10b268b8085839dc6d3bced0558f70cda4f2 Mon Sep 17 00:00:00 2001 From: TMH237 Date: Mon, 9 Mar 2026 12:50:43 +0000 Subject: [PATCH 07/67] DOC: Added alt text to builderMonitors gif --- source/builder/builderMonitors.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/builder/builderMonitors.rst b/source/builder/builderMonitors.rst index 33473f2..5c70ef4 100644 --- a/source/builder/builderMonitors.rst +++ b/source/builder/builderMonitors.rst @@ -11,4 +11,5 @@ Having set up your monitor settings you should then tell |PsychoPy| which of you .. only:: html - .. image:: /images/monitorcenter.gif \ No newline at end of file + .. image:: /images/monitorcenter.gif + :alt: "Animated gif showing how to set up your monitor with PsychoPy" From 966dc548cac35af9479ac4a31db21e23d775ffb0 Mon Sep 17 00:00:00 2001 From: TMH237 Date: Mon, 9 Mar 2026 18:02:05 +0000 Subject: [PATCH 08/67] DOC: Added alt-text to Pavlovia Survey Component --- source/builder/components/PavloviaSurveyRoutine.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/builder/components/PavloviaSurveyRoutine.rst b/source/builder/components/PavloviaSurveyRoutine.rst index 3abafdc..8738dcd 100644 --- a/source/builder/components/PavloviaSurveyRoutine.rst +++ b/source/builder/components/PavloviaSurveyRoutine.rst @@ -23,6 +23,7 @@ The Pavlovia Survey Routine is a "Standalone Routine", which means rather than a .. image:: /images/PavloviaSurveyComponent.png :width: 60% + :alt: "Image showing snapshot of Pavlovia Survey Routine window" To specify a survey you can either use "Survey ID" or "Survey Model File". @@ -94,4 +95,4 @@ Tools for testing, debugging and checking the performance of this Component. Disable Routine Disable this Routine -.. redirect-from:: advanced_survey.rst \ No newline at end of file +.. redirect-from:: advanced_survey.rst From 818b1d584cda3b4c3e6dab739f2a1eb8b2bf7c69 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:49:14 +0000 Subject: [PATCH 09/67] Update googleSpeech.rst with API setup instructions Added instructions for enabling Speech API and creating Service Account credentials. --- source/builder/googleSpeech.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/builder/googleSpeech.rst b/source/builder/googleSpeech.rst index db9d59f..48f616c 100644 --- a/source/builder/googleSpeech.rst +++ b/source/builder/googleSpeech.rst @@ -23,6 +23,7 @@ Steps - Then click on Credentials and create Service Account credentials. .. figure:: /images/google_speech_service_acc.png + :alt: "Screenshot of google cloud platform credential settings. There is a red arrow pointing to the option Service Account" Add credentials to your Google cloud project and select "Service Account". @@ -52,4 +53,4 @@ Steps .. warning:: Remember to check that your accounts billing information stays up to date. Even if you haven't done enough recordings to warrant a large payment, if a card on your billing account expires this will invalidate the JSON key and raise a "billing" error in |PsychoPy|. -.. redirect-from:: ./components/googleSpeech.rst \ No newline at end of file +.. redirect-from:: ./components/googleSpeech.rst From 4faa7c88f08849b24b6449d376dff0e170c54cb5 Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Sat, 14 Mar 2026 10:38:00 +0000 Subject: [PATCH 10/67] DOC: Add link to creating fork from dev index --- source/developers/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/developers/index.rst b/source/developers/index.rst index 37e96df..1013159 100644 --- a/source/developers/index.rst +++ b/source/developers/index.rst @@ -65,6 +65,7 @@ For a simple step-by-step guide to correcting typos, or adding an extra paragrap addingdocumentation.rst testsuitecontributing + setupfork localization menuitem buildercomponent From 4ca26b97e4ac6cd3254736e8c68ad915a4c8e321 Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Sat, 14 Mar 2026 10:39:42 +0000 Subject: [PATCH 11/67] DOC: Fixed broken link to forking --- source/developers/addingdocumentation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/developers/addingdocumentation.rst b/source/developers/addingdocumentation.rst index 11b699b..c85fb64 100644 --- a/source/developers/addingdocumentation.rst +++ b/source/developers/addingdocumentation.rst @@ -4,7 +4,7 @@ Making edits to PsychoPy's documentation ========================================= First you'll need to have your own "fork" of |PsychoPy| - this is a copy of the code which is yours -to edit, without affecting the main code. :ref:`Click here for a how-to for forking |PsychoPy| ` +to edit, without affecting the main code. :ref:`Click here for a how-to for forking PsychoPy ` If you only want to change a single file: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 7fb4444e9124dfc0330b2bd5fe40610c1275a7d5 Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Sat, 14 Mar 2026 10:41:14 +0000 Subject: [PATCH 12/67] DOC: Fixed broken ref on forking page --- source/developers/setupfork.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/developers/setupfork.rst b/source/developers/setupfork.rst index 20382d9..78d2870 100644 --- a/source/developers/setupfork.rst +++ b/source/developers/setupfork.rst @@ -1,4 +1,4 @@ -.. _forkPsychoPy +.. _forkPsychoPy: Create a fork of |PsychoPy| ================================================== @@ -35,4 +35,4 @@ It's good practice to create a new branch of the repository for each piece of wo Now that you have your own fork of the PsychoPy repository, and a branch to make your changes in, you can make changes to the code! You'll then make a pull request so that your changes can be merged into the PsychoPy repository itself. There are several ways to do this, depending on the extent of the changes you wish to make. -In this walkthrough we're imagining that you just want to make a change to a single page on the documentation, but if you want to see how to make bigger changes take a look at the other links on the :ref:`developers` page! \ No newline at end of file +In this walkthrough we're imagining that you just want to make a change to a single page on the documentation, but if you want to see how to make bigger changes take a look at the other links on the :ref:`developers` page! From d25e1e8d2ccbb7802c0d2f90cc400a087c090668 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Wed, 18 Mar 2026 18:10:28 +0000 Subject: [PATCH 13/67] Update download buttons for 2026.1.2 --- source/download.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source/download.rst b/source/download.rst index ad5cabd..495c0c4 100644 --- a/source/download.rst +++ b/source/download.rst @@ -48,10 +48,10 @@ Download We will continue to release new versions until PsychoPy Studio is stable enough to replace it. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.1/StandalonePsychoPy-2026.1.1-win64-3.10.exe + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/StandalonePsychoPy-2026.1.2-win64-3.10.exe :color: primary - PsychoPy Standalone 2026.1.1 + PsychoPy Standalone 2026.1.2 .. grid-item-card:: PsychoPy Studio (Beta) @@ -59,10 +59,10 @@ Download The underlying experiment engine is still Python, but Studio provides a smoother, more user-friendly interface and better version independence with useVersion. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.1/PsychoPy_Studio_2026.1.1.exe + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/PsychoPy_Studio_2026.1.2.exe :color: primary - PsychoPy Studio 2026.1.1 + PsychoPy Studio 2026.1.2 .. tab-item:: MacOS :class-label: mac-tab @@ -76,10 +76,10 @@ Download We will continue to release new versions until PsychoPy Studio is stable enough to replace it. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.1/StandalonePsychoPy-2026.1.1-macOS-3.10.dmg + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/StandalonePsychoPy-2026.1.2-macos-3.10.dmg :color: primary - PsychoPy Standalone 2026.1.1 + PsychoPy Standalone 2026.1.2 .. grid-item-card:: PsychoPy Studio (Beta) @@ -87,10 +87,10 @@ Download The underlying experiment engine is still Python, but Studio provides a smoother, more user-friendly interface and better version independence with useVersion. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.1/PsychoPy_Studio_2026.1.1.dmg + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/PsychoPy_Studio_2026.1.2.dmg :color: primary - PsychoPy Studio 2026.1.1 + PsychoPy Studio 2026.1.2 .. tab-item:: Linux :class-label: linux-tab @@ -136,11 +136,11 @@ Download Plus, it's much easier to install on Linux! - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.1/PsychoPy_Studio_2026.1.1.AppImage - :tooltip: PsychoPy_Studio_2026.1.1.AppImage + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/PsychoPy_Studio_2026.1.2.AppImage + :tooltip: PsychoPy_Studio_2026.1.2.AppImage :color: primary - PsychoPy Studio 2026.1.1 + PsychoPy Studio 2026.1.2 **For all versions** see the `PsychoPy releases on github `_ From 4f243edfd4b97e63b69174488615f507c8732c96 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:53:26 +0000 Subject: [PATCH 14/67] Update gettingStarted.rst with alt text for images missing alt text Clarified instructions for running and compiling programs in PsychoPy Builder. --- source/gettingStarted.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/gettingStarted.rst b/source/gettingStarted.rst index c2daf36..f93f9cd 100644 --- a/source/gettingStarted.rst +++ b/source/gettingStarted.rst @@ -49,10 +49,12 @@ Start |PsychoPy|, and be sure to be in the Builder view. * Back in the main Builder, type `Ctrl-R` (Windows, Linux) or `Cmd-R` (Mac), or use the mouse to click the `Run` icon. .. image:: /images/run32.png + :alt: A green circle with a white triangle indicating a play/run button in psychopy Assuming you typed in "Hello world!", your screen should have looked like this (briefly): .. image:: /images/helloworld.png + :alt: A gray screen with white text that reads Hello World :width: 80% :align: center @@ -88,6 +90,7 @@ To get a better feel for what was happening "behind the scenes" in the Builder p * Instead of running the program, explicitly convert it into python: Type `F5`, or click the `Compile` icon: .. image:: /images/compile_py.png + :alt: The compile button in PsychoPy builder, indicating a blue and yellow snake python icon. The view will automatically switch to the Coder, and display the python code. If you then save and run this code, it would look the same as running it directly from the Builder. From c49e7b6477a7fb39286a0f350270ff21142e6edd Mon Sep 17 00:00:00 2001 From: ashishvijayan <73926048+ashishvijayan@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:24:59 +0530 Subject: [PATCH 15/67] Enhance image accessibility with alt text Added alt text to image for accessibility. --- source/tutorials/experiments/lexical_decision.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/tutorials/experiments/lexical_decision.rst b/source/tutorials/experiments/lexical_decision.rst index 192ae27..51c40e1 100644 --- a/source/tutorials/experiments/lexical_decision.rst +++ b/source/tutorials/experiments/lexical_decision.rst @@ -58,6 +58,7 @@ Including a random Inter-Stimulus-Interval (ISI) To add a random ISI to our trials, we can add a routine before our word presentation and add a simple text component. Type '+' in the text field. Now, in the *duration* field type :code:`$random()`. This will produce a random number between 0 and 1, so if we want a random ISI between 0.5 and 1 seconds we can simple add this to 0.5 i.e. :code:`0.5 +random()`. Your text component should look like this. .. image:: ./lexical_decision_isi.png + :alt: " A psychopy routine showing a fixation cross, a letter and key response" :width: 100 % From f74c7d69209827b9fd2959e565cc66483b4d30cf Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:57:49 +0000 Subject: [PATCH 16/67] DOC: Added alt text to n-back tutorial --- source/tutorials/experiments/n_back.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/tutorials/experiments/n_back.rst b/source/tutorials/experiments/n_back.rst index 501c9af..f8a40d6 100644 --- a/source/tutorials/experiments/n_back.rst +++ b/source/tutorials/experiments/n_back.rst @@ -57,6 +57,7 @@ Feeding trial info into PsychoPy once we have our conditions file set up and **saved in the same location as our experiment** we need to give this info to our experiment. Add a loop around your trial routine and give the path to your conditions file in the Conditions field. We want to make sure our letters are presented in a preset order, so make sure to set loop type to **sequential**. .. image:: ./n_back_loop_sequential.png + :alt: "A PsychoPy Routine showing a fixation cross, a letter and a key response" :width: 100 % Finally, because our letter is changing trial-by-trial add :code:`$thisLetter` to the text field of your letter component and make sure to **set every repeat** @@ -67,6 +68,7 @@ Collecting responses There are a few tweaks we need to make to our keyboard component to make sure things are just right here. First, by default a keyboard response will force the end of the current routine. That means that if any stimuli were going to be presented later in the routine they would not be presented, in our case it would alter the inter-stimulus-interval by shortening this trial. So, make sure to uncheck the `Force end of Routine` box. .. image:: ./n_back_force_end_none.png + :alt: "A PsychoPy sequential loop" :width: 100 % The final thing we might want to do is make sure that we store whether a keypress was correct or not. We can do this by adding a column to our conditions file to indicate what the correct response would be on that trial: @@ -174,4 +176,4 @@ We covered a few code concepts in this tutorial so let's play with what we learn 1. Make a basic string variable that represents your name. 2. Convert that string to a list. 3. Use a while loop to keep selecting letters from your name *until* you find one letter you want. -4. Present that letter in a text component. \ No newline at end of file +4. Present that letter in a text component. From 7447b289d769fa9b7f10180868a113db8e7c4782 Mon Sep 17 00:00:00 2001 From: mhtessier <58186829+mhtessier@users.noreply.github.com> Date: Thu, 19 Mar 2026 08:57:50 -0400 Subject: [PATCH 17/67] DOC: Added alt text to pluginDevGuide Added alt text for the image in the plugin development guide. --- source/developers/pluginDevGuide.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/developers/pluginDevGuide.rst b/source/developers/pluginDevGuide.rst index 55e1b32..b8137b7 100644 --- a/source/developers/pluginDevGuide.rst +++ b/source/developers/pluginDevGuide.rst @@ -101,6 +101,7 @@ Now that your GitHub repo is all set up, you need to setup PyPI to look for your Scroll down to the "Pending publishers" section and choose "GitHub" from the tabs on the control there. It should look like this: .. image:: pypi-setup-plugin.png + :alt: "GitHub page to describe the plugin" but with the fields flanked by `<>` replaced by the relevant information for your plugin. Once you click "Add", you should be good to go! @@ -114,4 +115,4 @@ Listing a plugin in Builder Once your plugin is published on PyPI, it can be installed by anyone (via ``pip install `` - but it won't appear in the list of plugins from |PsychoPy| Builder as at this point it's no different than any of the thousands of other Python packages on PyPI. Builder gets its list of plugins from a file in the `PsychoPy Plugins repo `_ called ``plugins.json``. This file contains a list of plugins alongwith information about them, links to documentation, an icon for the plugin and author, etc. -To add your plugin to this list, simply fork this repo, edit the file to include information about your plugin, and submit it as a pull request. We'll give your plugin a quick check over for malicious code and, assuming it's all fine, will accept your pull request and your plugin will be immediately available from Builder! \ No newline at end of file +To add your plugin to this list, simply fork this repo, edit the file to include information about your plugin, and submit it as a pull request. We'll give your plugin a quick check over for malicious code and, assuming it's all fine, will accept your pull request and your plugin will be immediately available from Builder! From 3a59bf2f3ba623486190ce1cb65a5c76da6b98ad Mon Sep 17 00:00:00 2001 From: lizziewatson <141944822+lizziewatson@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:57:52 +0000 Subject: [PATCH 18/67] DOC: Added alt text to digit span tutorial --- source/tutorials/experiments/digit_span_task.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/tutorials/experiments/digit_span_task.rst b/source/tutorials/experiments/digit_span_task.rst index 037e81b..fa28e54 100644 --- a/source/tutorials/experiments/digit_span_task.rst +++ b/source/tutorials/experiments/digit_span_task.rst @@ -67,6 +67,7 @@ In this experiment, we are going to allow participants to type responses. Add a Your entire experiment should now look like this: .. image:: ./digit_span_full_flow.png + :alt: "Psychopy routine with a textbox object called 'recall'" :width: 100 % From 127939b2d004fbbe2be25441311a4ec8e56cb895 Mon Sep 17 00:00:00 2001 From: nhasshim <37693272+nhasshim@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:57:52 +0000 Subject: [PATCH 19/67] DOC: Added alt text to arduino.rst image --- source/hardware/arduino.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/hardware/arduino.rst b/source/hardware/arduino.rst index ffd195f..caa9020 100644 --- a/source/hardware/arduino.rst +++ b/source/hardware/arduino.rst @@ -13,6 +13,7 @@ Step one: Find out the address of your serial port You can quickly find out the address of the serial port that your Arduino is connected to by opening the Arduino IDE and clicking on `Tools` at the top of the window, then down to `Port`. Here, the port that your Arduino is connected to will show the model of your Arduino next to it. .. figure:: /images/arduinoPort.png + :alt: "An screenshot of a window with the following option selected Tools>Port:>Com4(Arduino UNO) " :scale: 20% @@ -61,4 +62,4 @@ If there is a problem - We want to know! ------------------------------------------------------------- If you have followed the steps above and are having an issue, please post details of this on the `PsychoPy Forum `_. -We are constantly looking to update our documentation so that it's easy for you to use PsychoPy in the way that you want to. Posting in our forum allows us to see what issues users are having, offer solutions, and to update our documentation to hopefully prevent those issues from occurring again! \ No newline at end of file +We are constantly looking to update our documentation so that it's easy for you to use PsychoPy in the way that you want to. Posting in our forum allows us to see what issues users are having, offer solutions, and to update our documentation to hopefully prevent those issues from occurring again! From 2a700416dd1d961fb9f1b9da380d67f0cb6f213b Mon Sep 17 00:00:00 2001 From: marzenas-hub Date: Thu, 19 Mar 2026 13:57:52 +0100 Subject: [PATCH 20/67] DOC: Added alt text to pluginDevGuide --- source/developers/pluginDevGuide.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/developers/pluginDevGuide.rst b/source/developers/pluginDevGuide.rst index 55e1b32..1b62f1b 100644 --- a/source/developers/pluginDevGuide.rst +++ b/source/developers/pluginDevGuide.rst @@ -101,6 +101,7 @@ Now that your GitHub repo is all set up, you need to setup PyPI to look for your Scroll down to the "Pending publishers" section and choose "GitHub" from the tabs on the control there. It should look like this: .. image:: pypi-setup-plugin.png + :alt:"Wtyczka" but with the fields flanked by `<>` replaced by the relevant information for your plugin. Once you click "Add", you should be good to go! @@ -114,4 +115,4 @@ Listing a plugin in Builder Once your plugin is published on PyPI, it can be installed by anyone (via ``pip install `` - but it won't appear in the list of plugins from |PsychoPy| Builder as at this point it's no different than any of the thousands of other Python packages on PyPI. Builder gets its list of plugins from a file in the `PsychoPy Plugins repo `_ called ``plugins.json``. This file contains a list of plugins alongwith information about them, links to documentation, an icon for the plugin and author, etc. -To add your plugin to this list, simply fork this repo, edit the file to include information about your plugin, and submit it as a pull request. We'll give your plugin a quick check over for malicious code and, assuming it's all fine, will accept your pull request and your plugin will be immediately available from Builder! \ No newline at end of file +To add your plugin to this list, simply fork this repo, edit the file to include information about your plugin, and submit it as a pull request. We'll give your plugin a quick check over for malicious code and, assuming it's all fine, will accept your pull request and your plugin will be immediately available from Builder! From ddf2d4c7ffda021fd0d25175650e6fbce5fe2503 Mon Sep 17 00:00:00 2001 From: PLRohrer <49818244+PLRohrer@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:58:10 +0100 Subject: [PATCH 21/67] DOC: Added alt text to source/online/psychojsCode.rst --- source/online/psychojsCode.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/online/psychojsCode.rst b/source/online/psychojsCode.rst index 1fdbb46..a103688 100644 --- a/source/online/psychojsCode.rst +++ b/source/online/psychojsCode.rst @@ -20,6 +20,7 @@ Adding JS functions If you have a function you want to use, and you find the equivalent on the crib sheet or stack overflow, add an 'initialization' code component to the start of your experiment. Set code type to be 'JS' and copy and paste the function(s) you want there in the 'Begin experiment' tab. These functions will then be available to be called throughout the rest of the task. .. image:: initializeJScode.png + :alt: "Code component indicating code type as JS and containing JS functions" Don't change the generated JS file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -27,4 +28,4 @@ When you export an experiment to HTML from the PsychoPy builder, it generates a - Changes you make in your .js file will not be reflected back in your builder file; it is a one way street. - It becomes more difficult to sync your experiment with |Pavlovia| from the |PsychoPy| builder -- Researchers that would like to replicate your experiment but aren't very JavaScript-savvy might be better off using the PsychoPy Builder \ No newline at end of file +- Researchers that would like to replicate your experiment but aren't very JavaScript-savvy might be better off using the PsychoPy Builder From 7e8d88d4a47c480be55e68c90a18b53e78e69e64 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Wed, 25 Mar 2026 15:08:46 +0000 Subject: [PATCH 22/67] Reinstate API docs to psychopy.org --- {api => source/api}/app.rst | 0 {api => source/api}/clock.rst | 0 {api => source/api}/color.rst | 0 {api => source/api}/core.rst | 0 {api => source/api}/data.rst | 0 {api => source/api}/encryption.rst | 0 {api => source/api}/event.rst | 0 {api => source/api}/filters.rst | 0 {api => source/api}/gui.rst | 0 {api => source/api}/hardware/brainproducts.rst | 0 {api => source/api}/hardware/camera.rst | 0 {api => source/api}/hardware/cedrus.rst | 0 {api => source/api}/hardware/egi.rst | 0 {api => source/api}/hardware/emulator.rst | 0 {api => source/api}/hardware/forp.rst | 0 {api => source/api}/hardware/index.rst | 0 {api => source/api}/hardware/iolab.rst | 0 {api => source/api}/hardware/joystick.rst | 0 {api => source/api}/hardware/keyboard.rst | 0 {api => source/api}/hardware/labjack.rst | 0 {api => source/api}/hardware/minolta.rst | 0 {api => source/api}/hardware/pr.rst | 0 {api => source/api}/hardware/pylink.rst | 0 {api => source/api}/hardware/qmix.rst | 0 {api => source/api}/hardware/speaker.rst | 0 {api => source/api}/hardware/triggerbox.rst | 0 {api => source/api}/index.rst | 0 {api => source/api}/info.rst | 0 {api => source/api}/iohub/device/computer.rst | 0 .../api}/iohub/device/default_yaml_configs/default_display.yaml | 0 .../iohub/device/default_yaml_configs/default_experiment.yaml | 0 .../device/default_yaml_configs/default_eyelink_eyetracker.yaml | 0 .../iohub/device/default_yaml_configs/default_gp3_eyetracker.yaml | 0 .../api}/iohub/device/default_yaml_configs/default_keyboard.yaml | 0 .../api}/iohub/device/default_yaml_configs/default_mouse.yaml | 0 .../device/default_yaml_configs/default_mousegaze_eyetracker.yaml | 0 .../default_yaml_configs/default_pupil_core_eyetracker.yaml | 0 .../default_yaml_configs/default_pupil_neon_eyetracker.yaml | 0 .../device/default_yaml_configs/default_tobii_eyetracker.yaml | 0 {api => source/api}/iohub/device/eyetracker.rst | 0 .../eyetracker_interface/MouseGaze_Implementation_Notes.rst | 0 {api => source/api}/iohub/device/keyboard.rst | 0 {api => source/api}/iohub/device/mouse.rst | 0 {api => source/api}/iohub/device/serial.rst | 0 {api => source/api}/iohub/devices.rst | 0 {api => source/api}/iohub/index.rst | 0 {api => source/api}/iohub/requirements.rst | 0 {api => source/api}/iohub/starting.rst | 0 {api => source/api}/layout.rst | 0 {api => source/api}/logging.rst | 0 {api => source/api}/microphone.rst | 0 {api => source/api}/misc.rst | 0 {api => source/api}/monitors.rst | 0 {api => source/api}/parallel.rst | 0 {api => source/api}/plugins.rst | 0 {api => source/api}/preferences.rst | 0 {api => source/api}/serial.rst | 0 {api => source/api}/session.rst | 0 {api => source/api}/sound/audioclip.rst | 0 {api => source/api}/sound/audiodevice.rst | 0 {api => source/api}/sound/index.rst | 0 {api => source/api}/sound/microphone.rst | 0 {api => source/api}/sound/playback.rst | 0 {api => source/api}/sound/transcribe.rst | 0 {api => source/api}/tools/colorspacetools.rst | 0 {api => source/api}/tools/coordinatetools.rst | 0 {api => source/api}/tools/filetools.rst | 0 {api => source/api}/tools/gltools.rst | 0 {api => source/api}/tools/imagetools.rst | 0 {api => source/api}/tools/index.rst | 0 {api => source/api}/tools/mathtools.rst | 0 {api => source/api}/tools/monitorunittools.rst | 0 {api => source/api}/tools/movietools.rst | 0 {api => source/api}/tools/pkgtools.rst | 0 {api => source/api}/tools/plottools.rst | 0 {api => source/api}/tools/rifttools.rst | 0 {api => source/api}/tools/systemtools.rst | 0 {api => source/api}/tools/typetools.rst | 0 {api => source/api}/tools/unittools.rst | 0 {api => source/api}/tools/viewtools.rst | 0 {api => source/api}/visual/aperture.rst | 0 {api => source/api}/visual/boundingbox.rst | 0 {api => source/api}/visual/boxstim.rst | 0 {api => source/api}/visual/bufferimagestim.rst | 0 {api => source/api}/visual/circle.rst | 0 {api => source/api}/visual/custommouse.rst | 0 {api => source/api}/visual/dotstim.rst | 0 {api => source/api}/visual/elementarraystim.rst | 0 {api => source/api}/visual/form.rst | 0 {api => source/api}/visual/gratingstim.rst | 0 {api => source/api}/visual/helperfunctions.rst | 0 {api => source/api}/visual/imagestim.rst | 0 {api => source/api}/visual/index.rst | 0 {api => source/api}/visual/lightsource.rst | 0 {api => source/api}/visual/line.rst | 0 {api => source/api}/visual/moviestim.rst | 0 {api => source/api}/visual/noisestim.rst | 0 {api => source/api}/visual/objmeshstim.rst | 0 {api => source/api}/visual/patchstim.rst | 0 {api => source/api}/visual/phongmaterial.rst | 0 {api => source/api}/visual/pie.rst | 0 {api => source/api}/visual/planestim.rst | 0 {api => source/api}/visual/polygon.rst | 0 {api => source/api}/visual/progressbar.rst | 0 {api => source/api}/visual/radialstim.rst | 0 {api => source/api}/visual/ratingscale.rst | 0 {api => source/api}/visual/rect.rst | 0 {api => source/api}/visual/rift.rst | 0 {api => source/api}/visual/rigidbodypose.rst | 0 {api => source/api}/visual/sceneskybox.rst | 0 {api => source/api}/visual/secondorder.rst | 0 {api => source/api}/visual/shapestim.rst | 0 {api => source/api}/visual/simpleimagestim.rst | 0 {api => source/api}/visual/slider.rst | 0 {api => source/api}/visual/spherestim.rst | 0 {api => source/api}/visual/textbox.rst | 0 {api => source/api}/visual/textbox2.rst | 0 {api => source/api}/visual/textstim.rst | 0 {api => source/api}/visual/vshd.rst | 0 {api => source/api}/visual/window.rst | 0 {api => source/api}/visual/windowframepack.rst | 0 {api => source/api}/visual/windowwarp.rst | 0 {api => source/api}/web.rst | 0 123 files changed, 0 insertions(+), 0 deletions(-) rename {api => source/api}/app.rst (100%) rename {api => source/api}/clock.rst (100%) rename {api => source/api}/color.rst (100%) rename {api => source/api}/core.rst (100%) rename {api => source/api}/data.rst (100%) rename {api => source/api}/encryption.rst (100%) rename {api => source/api}/event.rst (100%) rename {api => source/api}/filters.rst (100%) rename {api => source/api}/gui.rst (100%) rename {api => source/api}/hardware/brainproducts.rst (100%) rename {api => source/api}/hardware/camera.rst (100%) rename {api => source/api}/hardware/cedrus.rst (100%) rename {api => source/api}/hardware/egi.rst (100%) rename {api => source/api}/hardware/emulator.rst (100%) rename {api => source/api}/hardware/forp.rst (100%) rename {api => source/api}/hardware/index.rst (100%) rename {api => source/api}/hardware/iolab.rst (100%) rename {api => source/api}/hardware/joystick.rst (100%) rename {api => source/api}/hardware/keyboard.rst (100%) rename {api => source/api}/hardware/labjack.rst (100%) rename {api => source/api}/hardware/minolta.rst (100%) rename {api => source/api}/hardware/pr.rst (100%) rename {api => source/api}/hardware/pylink.rst (100%) rename {api => source/api}/hardware/qmix.rst (100%) rename {api => source/api}/hardware/speaker.rst (100%) rename {api => source/api}/hardware/triggerbox.rst (100%) rename {api => source/api}/index.rst (100%) rename {api => source/api}/info.rst (100%) rename {api => source/api}/iohub/device/computer.rst (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_display.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_experiment.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_eyelink_eyetracker.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_gp3_eyetracker.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_keyboard.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_mouse.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_mousegaze_eyetracker.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_pupil_core_eyetracker.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_pupil_neon_eyetracker.yaml (100%) rename {api => source/api}/iohub/device/default_yaml_configs/default_tobii_eyetracker.yaml (100%) rename {api => source/api}/iohub/device/eyetracker.rst (100%) rename {api => source/api}/iohub/device/eyetracker_interface/MouseGaze_Implementation_Notes.rst (100%) rename {api => source/api}/iohub/device/keyboard.rst (100%) rename {api => source/api}/iohub/device/mouse.rst (100%) rename {api => source/api}/iohub/device/serial.rst (100%) rename {api => source/api}/iohub/devices.rst (100%) rename {api => source/api}/iohub/index.rst (100%) rename {api => source/api}/iohub/requirements.rst (100%) rename {api => source/api}/iohub/starting.rst (100%) rename {api => source/api}/layout.rst (100%) rename {api => source/api}/logging.rst (100%) rename {api => source/api}/microphone.rst (100%) rename {api => source/api}/misc.rst (100%) rename {api => source/api}/monitors.rst (100%) rename {api => source/api}/parallel.rst (100%) rename {api => source/api}/plugins.rst (100%) rename {api => source/api}/preferences.rst (100%) rename {api => source/api}/serial.rst (100%) rename {api => source/api}/session.rst (100%) rename {api => source/api}/sound/audioclip.rst (100%) rename {api => source/api}/sound/audiodevice.rst (100%) rename {api => source/api}/sound/index.rst (100%) rename {api => source/api}/sound/microphone.rst (100%) rename {api => source/api}/sound/playback.rst (100%) rename {api => source/api}/sound/transcribe.rst (100%) rename {api => source/api}/tools/colorspacetools.rst (100%) rename {api => source/api}/tools/coordinatetools.rst (100%) rename {api => source/api}/tools/filetools.rst (100%) rename {api => source/api}/tools/gltools.rst (100%) rename {api => source/api}/tools/imagetools.rst (100%) rename {api => source/api}/tools/index.rst (100%) rename {api => source/api}/tools/mathtools.rst (100%) rename {api => source/api}/tools/monitorunittools.rst (100%) rename {api => source/api}/tools/movietools.rst (100%) rename {api => source/api}/tools/pkgtools.rst (100%) rename {api => source/api}/tools/plottools.rst (100%) rename {api => source/api}/tools/rifttools.rst (100%) rename {api => source/api}/tools/systemtools.rst (100%) rename {api => source/api}/tools/typetools.rst (100%) rename {api => source/api}/tools/unittools.rst (100%) rename {api => source/api}/tools/viewtools.rst (100%) rename {api => source/api}/visual/aperture.rst (100%) rename {api => source/api}/visual/boundingbox.rst (100%) rename {api => source/api}/visual/boxstim.rst (100%) rename {api => source/api}/visual/bufferimagestim.rst (100%) rename {api => source/api}/visual/circle.rst (100%) rename {api => source/api}/visual/custommouse.rst (100%) rename {api => source/api}/visual/dotstim.rst (100%) rename {api => source/api}/visual/elementarraystim.rst (100%) rename {api => source/api}/visual/form.rst (100%) rename {api => source/api}/visual/gratingstim.rst (100%) rename {api => source/api}/visual/helperfunctions.rst (100%) rename {api => source/api}/visual/imagestim.rst (100%) rename {api => source/api}/visual/index.rst (100%) rename {api => source/api}/visual/lightsource.rst (100%) rename {api => source/api}/visual/line.rst (100%) rename {api => source/api}/visual/moviestim.rst (100%) rename {api => source/api}/visual/noisestim.rst (100%) rename {api => source/api}/visual/objmeshstim.rst (100%) rename {api => source/api}/visual/patchstim.rst (100%) rename {api => source/api}/visual/phongmaterial.rst (100%) rename {api => source/api}/visual/pie.rst (100%) rename {api => source/api}/visual/planestim.rst (100%) rename {api => source/api}/visual/polygon.rst (100%) rename {api => source/api}/visual/progressbar.rst (100%) rename {api => source/api}/visual/radialstim.rst (100%) rename {api => source/api}/visual/ratingscale.rst (100%) rename {api => source/api}/visual/rect.rst (100%) rename {api => source/api}/visual/rift.rst (100%) rename {api => source/api}/visual/rigidbodypose.rst (100%) rename {api => source/api}/visual/sceneskybox.rst (100%) rename {api => source/api}/visual/secondorder.rst (100%) rename {api => source/api}/visual/shapestim.rst (100%) rename {api => source/api}/visual/simpleimagestim.rst (100%) rename {api => source/api}/visual/slider.rst (100%) rename {api => source/api}/visual/spherestim.rst (100%) rename {api => source/api}/visual/textbox.rst (100%) rename {api => source/api}/visual/textbox2.rst (100%) rename {api => source/api}/visual/textstim.rst (100%) rename {api => source/api}/visual/vshd.rst (100%) rename {api => source/api}/visual/window.rst (100%) rename {api => source/api}/visual/windowframepack.rst (100%) rename {api => source/api}/visual/windowwarp.rst (100%) rename {api => source/api}/web.rst (100%) diff --git a/api/app.rst b/source/api/app.rst similarity index 100% rename from api/app.rst rename to source/api/app.rst diff --git a/api/clock.rst b/source/api/clock.rst similarity index 100% rename from api/clock.rst rename to source/api/clock.rst diff --git a/api/color.rst b/source/api/color.rst similarity index 100% rename from api/color.rst rename to source/api/color.rst diff --git a/api/core.rst b/source/api/core.rst similarity index 100% rename from api/core.rst rename to source/api/core.rst diff --git a/api/data.rst b/source/api/data.rst similarity index 100% rename from api/data.rst rename to source/api/data.rst diff --git a/api/encryption.rst b/source/api/encryption.rst similarity index 100% rename from api/encryption.rst rename to source/api/encryption.rst diff --git a/api/event.rst b/source/api/event.rst similarity index 100% rename from api/event.rst rename to source/api/event.rst diff --git a/api/filters.rst b/source/api/filters.rst similarity index 100% rename from api/filters.rst rename to source/api/filters.rst diff --git a/api/gui.rst b/source/api/gui.rst similarity index 100% rename from api/gui.rst rename to source/api/gui.rst diff --git a/api/hardware/brainproducts.rst b/source/api/hardware/brainproducts.rst similarity index 100% rename from api/hardware/brainproducts.rst rename to source/api/hardware/brainproducts.rst diff --git a/api/hardware/camera.rst b/source/api/hardware/camera.rst similarity index 100% rename from api/hardware/camera.rst rename to source/api/hardware/camera.rst diff --git a/api/hardware/cedrus.rst b/source/api/hardware/cedrus.rst similarity index 100% rename from api/hardware/cedrus.rst rename to source/api/hardware/cedrus.rst diff --git a/api/hardware/egi.rst b/source/api/hardware/egi.rst similarity index 100% rename from api/hardware/egi.rst rename to source/api/hardware/egi.rst diff --git a/api/hardware/emulator.rst b/source/api/hardware/emulator.rst similarity index 100% rename from api/hardware/emulator.rst rename to source/api/hardware/emulator.rst diff --git a/api/hardware/forp.rst b/source/api/hardware/forp.rst similarity index 100% rename from api/hardware/forp.rst rename to source/api/hardware/forp.rst diff --git a/api/hardware/index.rst b/source/api/hardware/index.rst similarity index 100% rename from api/hardware/index.rst rename to source/api/hardware/index.rst diff --git a/api/hardware/iolab.rst b/source/api/hardware/iolab.rst similarity index 100% rename from api/hardware/iolab.rst rename to source/api/hardware/iolab.rst diff --git a/api/hardware/joystick.rst b/source/api/hardware/joystick.rst similarity index 100% rename from api/hardware/joystick.rst rename to source/api/hardware/joystick.rst diff --git a/api/hardware/keyboard.rst b/source/api/hardware/keyboard.rst similarity index 100% rename from api/hardware/keyboard.rst rename to source/api/hardware/keyboard.rst diff --git a/api/hardware/labjack.rst b/source/api/hardware/labjack.rst similarity index 100% rename from api/hardware/labjack.rst rename to source/api/hardware/labjack.rst diff --git a/api/hardware/minolta.rst b/source/api/hardware/minolta.rst similarity index 100% rename from api/hardware/minolta.rst rename to source/api/hardware/minolta.rst diff --git a/api/hardware/pr.rst b/source/api/hardware/pr.rst similarity index 100% rename from api/hardware/pr.rst rename to source/api/hardware/pr.rst diff --git a/api/hardware/pylink.rst b/source/api/hardware/pylink.rst similarity index 100% rename from api/hardware/pylink.rst rename to source/api/hardware/pylink.rst diff --git a/api/hardware/qmix.rst b/source/api/hardware/qmix.rst similarity index 100% rename from api/hardware/qmix.rst rename to source/api/hardware/qmix.rst diff --git a/api/hardware/speaker.rst b/source/api/hardware/speaker.rst similarity index 100% rename from api/hardware/speaker.rst rename to source/api/hardware/speaker.rst diff --git a/api/hardware/triggerbox.rst b/source/api/hardware/triggerbox.rst similarity index 100% rename from api/hardware/triggerbox.rst rename to source/api/hardware/triggerbox.rst diff --git a/api/index.rst b/source/api/index.rst similarity index 100% rename from api/index.rst rename to source/api/index.rst diff --git a/api/info.rst b/source/api/info.rst similarity index 100% rename from api/info.rst rename to source/api/info.rst diff --git a/api/iohub/device/computer.rst b/source/api/iohub/device/computer.rst similarity index 100% rename from api/iohub/device/computer.rst rename to source/api/iohub/device/computer.rst diff --git a/api/iohub/device/default_yaml_configs/default_display.yaml b/source/api/iohub/device/default_yaml_configs/default_display.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_display.yaml rename to source/api/iohub/device/default_yaml_configs/default_display.yaml diff --git a/api/iohub/device/default_yaml_configs/default_experiment.yaml b/source/api/iohub/device/default_yaml_configs/default_experiment.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_experiment.yaml rename to source/api/iohub/device/default_yaml_configs/default_experiment.yaml diff --git a/api/iohub/device/default_yaml_configs/default_eyelink_eyetracker.yaml b/source/api/iohub/device/default_yaml_configs/default_eyelink_eyetracker.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_eyelink_eyetracker.yaml rename to source/api/iohub/device/default_yaml_configs/default_eyelink_eyetracker.yaml diff --git a/api/iohub/device/default_yaml_configs/default_gp3_eyetracker.yaml b/source/api/iohub/device/default_yaml_configs/default_gp3_eyetracker.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_gp3_eyetracker.yaml rename to source/api/iohub/device/default_yaml_configs/default_gp3_eyetracker.yaml diff --git a/api/iohub/device/default_yaml_configs/default_keyboard.yaml b/source/api/iohub/device/default_yaml_configs/default_keyboard.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_keyboard.yaml rename to source/api/iohub/device/default_yaml_configs/default_keyboard.yaml diff --git a/api/iohub/device/default_yaml_configs/default_mouse.yaml b/source/api/iohub/device/default_yaml_configs/default_mouse.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_mouse.yaml rename to source/api/iohub/device/default_yaml_configs/default_mouse.yaml diff --git a/api/iohub/device/default_yaml_configs/default_mousegaze_eyetracker.yaml b/source/api/iohub/device/default_yaml_configs/default_mousegaze_eyetracker.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_mousegaze_eyetracker.yaml rename to source/api/iohub/device/default_yaml_configs/default_mousegaze_eyetracker.yaml diff --git a/api/iohub/device/default_yaml_configs/default_pupil_core_eyetracker.yaml b/source/api/iohub/device/default_yaml_configs/default_pupil_core_eyetracker.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_pupil_core_eyetracker.yaml rename to source/api/iohub/device/default_yaml_configs/default_pupil_core_eyetracker.yaml diff --git a/api/iohub/device/default_yaml_configs/default_pupil_neon_eyetracker.yaml b/source/api/iohub/device/default_yaml_configs/default_pupil_neon_eyetracker.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_pupil_neon_eyetracker.yaml rename to source/api/iohub/device/default_yaml_configs/default_pupil_neon_eyetracker.yaml diff --git a/api/iohub/device/default_yaml_configs/default_tobii_eyetracker.yaml b/source/api/iohub/device/default_yaml_configs/default_tobii_eyetracker.yaml similarity index 100% rename from api/iohub/device/default_yaml_configs/default_tobii_eyetracker.yaml rename to source/api/iohub/device/default_yaml_configs/default_tobii_eyetracker.yaml diff --git a/api/iohub/device/eyetracker.rst b/source/api/iohub/device/eyetracker.rst similarity index 100% rename from api/iohub/device/eyetracker.rst rename to source/api/iohub/device/eyetracker.rst diff --git a/api/iohub/device/eyetracker_interface/MouseGaze_Implementation_Notes.rst b/source/api/iohub/device/eyetracker_interface/MouseGaze_Implementation_Notes.rst similarity index 100% rename from api/iohub/device/eyetracker_interface/MouseGaze_Implementation_Notes.rst rename to source/api/iohub/device/eyetracker_interface/MouseGaze_Implementation_Notes.rst diff --git a/api/iohub/device/keyboard.rst b/source/api/iohub/device/keyboard.rst similarity index 100% rename from api/iohub/device/keyboard.rst rename to source/api/iohub/device/keyboard.rst diff --git a/api/iohub/device/mouse.rst b/source/api/iohub/device/mouse.rst similarity index 100% rename from api/iohub/device/mouse.rst rename to source/api/iohub/device/mouse.rst diff --git a/api/iohub/device/serial.rst b/source/api/iohub/device/serial.rst similarity index 100% rename from api/iohub/device/serial.rst rename to source/api/iohub/device/serial.rst diff --git a/api/iohub/devices.rst b/source/api/iohub/devices.rst similarity index 100% rename from api/iohub/devices.rst rename to source/api/iohub/devices.rst diff --git a/api/iohub/index.rst b/source/api/iohub/index.rst similarity index 100% rename from api/iohub/index.rst rename to source/api/iohub/index.rst diff --git a/api/iohub/requirements.rst b/source/api/iohub/requirements.rst similarity index 100% rename from api/iohub/requirements.rst rename to source/api/iohub/requirements.rst diff --git a/api/iohub/starting.rst b/source/api/iohub/starting.rst similarity index 100% rename from api/iohub/starting.rst rename to source/api/iohub/starting.rst diff --git a/api/layout.rst b/source/api/layout.rst similarity index 100% rename from api/layout.rst rename to source/api/layout.rst diff --git a/api/logging.rst b/source/api/logging.rst similarity index 100% rename from api/logging.rst rename to source/api/logging.rst diff --git a/api/microphone.rst b/source/api/microphone.rst similarity index 100% rename from api/microphone.rst rename to source/api/microphone.rst diff --git a/api/misc.rst b/source/api/misc.rst similarity index 100% rename from api/misc.rst rename to source/api/misc.rst diff --git a/api/monitors.rst b/source/api/monitors.rst similarity index 100% rename from api/monitors.rst rename to source/api/monitors.rst diff --git a/api/parallel.rst b/source/api/parallel.rst similarity index 100% rename from api/parallel.rst rename to source/api/parallel.rst diff --git a/api/plugins.rst b/source/api/plugins.rst similarity index 100% rename from api/plugins.rst rename to source/api/plugins.rst diff --git a/api/preferences.rst b/source/api/preferences.rst similarity index 100% rename from api/preferences.rst rename to source/api/preferences.rst diff --git a/api/serial.rst b/source/api/serial.rst similarity index 100% rename from api/serial.rst rename to source/api/serial.rst diff --git a/api/session.rst b/source/api/session.rst similarity index 100% rename from api/session.rst rename to source/api/session.rst diff --git a/api/sound/audioclip.rst b/source/api/sound/audioclip.rst similarity index 100% rename from api/sound/audioclip.rst rename to source/api/sound/audioclip.rst diff --git a/api/sound/audiodevice.rst b/source/api/sound/audiodevice.rst similarity index 100% rename from api/sound/audiodevice.rst rename to source/api/sound/audiodevice.rst diff --git a/api/sound/index.rst b/source/api/sound/index.rst similarity index 100% rename from api/sound/index.rst rename to source/api/sound/index.rst diff --git a/api/sound/microphone.rst b/source/api/sound/microphone.rst similarity index 100% rename from api/sound/microphone.rst rename to source/api/sound/microphone.rst diff --git a/api/sound/playback.rst b/source/api/sound/playback.rst similarity index 100% rename from api/sound/playback.rst rename to source/api/sound/playback.rst diff --git a/api/sound/transcribe.rst b/source/api/sound/transcribe.rst similarity index 100% rename from api/sound/transcribe.rst rename to source/api/sound/transcribe.rst diff --git a/api/tools/colorspacetools.rst b/source/api/tools/colorspacetools.rst similarity index 100% rename from api/tools/colorspacetools.rst rename to source/api/tools/colorspacetools.rst diff --git a/api/tools/coordinatetools.rst b/source/api/tools/coordinatetools.rst similarity index 100% rename from api/tools/coordinatetools.rst rename to source/api/tools/coordinatetools.rst diff --git a/api/tools/filetools.rst b/source/api/tools/filetools.rst similarity index 100% rename from api/tools/filetools.rst rename to source/api/tools/filetools.rst diff --git a/api/tools/gltools.rst b/source/api/tools/gltools.rst similarity index 100% rename from api/tools/gltools.rst rename to source/api/tools/gltools.rst diff --git a/api/tools/imagetools.rst b/source/api/tools/imagetools.rst similarity index 100% rename from api/tools/imagetools.rst rename to source/api/tools/imagetools.rst diff --git a/api/tools/index.rst b/source/api/tools/index.rst similarity index 100% rename from api/tools/index.rst rename to source/api/tools/index.rst diff --git a/api/tools/mathtools.rst b/source/api/tools/mathtools.rst similarity index 100% rename from api/tools/mathtools.rst rename to source/api/tools/mathtools.rst diff --git a/api/tools/monitorunittools.rst b/source/api/tools/monitorunittools.rst similarity index 100% rename from api/tools/monitorunittools.rst rename to source/api/tools/monitorunittools.rst diff --git a/api/tools/movietools.rst b/source/api/tools/movietools.rst similarity index 100% rename from api/tools/movietools.rst rename to source/api/tools/movietools.rst diff --git a/api/tools/pkgtools.rst b/source/api/tools/pkgtools.rst similarity index 100% rename from api/tools/pkgtools.rst rename to source/api/tools/pkgtools.rst diff --git a/api/tools/plottools.rst b/source/api/tools/plottools.rst similarity index 100% rename from api/tools/plottools.rst rename to source/api/tools/plottools.rst diff --git a/api/tools/rifttools.rst b/source/api/tools/rifttools.rst similarity index 100% rename from api/tools/rifttools.rst rename to source/api/tools/rifttools.rst diff --git a/api/tools/systemtools.rst b/source/api/tools/systemtools.rst similarity index 100% rename from api/tools/systemtools.rst rename to source/api/tools/systemtools.rst diff --git a/api/tools/typetools.rst b/source/api/tools/typetools.rst similarity index 100% rename from api/tools/typetools.rst rename to source/api/tools/typetools.rst diff --git a/api/tools/unittools.rst b/source/api/tools/unittools.rst similarity index 100% rename from api/tools/unittools.rst rename to source/api/tools/unittools.rst diff --git a/api/tools/viewtools.rst b/source/api/tools/viewtools.rst similarity index 100% rename from api/tools/viewtools.rst rename to source/api/tools/viewtools.rst diff --git a/api/visual/aperture.rst b/source/api/visual/aperture.rst similarity index 100% rename from api/visual/aperture.rst rename to source/api/visual/aperture.rst diff --git a/api/visual/boundingbox.rst b/source/api/visual/boundingbox.rst similarity index 100% rename from api/visual/boundingbox.rst rename to source/api/visual/boundingbox.rst diff --git a/api/visual/boxstim.rst b/source/api/visual/boxstim.rst similarity index 100% rename from api/visual/boxstim.rst rename to source/api/visual/boxstim.rst diff --git a/api/visual/bufferimagestim.rst b/source/api/visual/bufferimagestim.rst similarity index 100% rename from api/visual/bufferimagestim.rst rename to source/api/visual/bufferimagestim.rst diff --git a/api/visual/circle.rst b/source/api/visual/circle.rst similarity index 100% rename from api/visual/circle.rst rename to source/api/visual/circle.rst diff --git a/api/visual/custommouse.rst b/source/api/visual/custommouse.rst similarity index 100% rename from api/visual/custommouse.rst rename to source/api/visual/custommouse.rst diff --git a/api/visual/dotstim.rst b/source/api/visual/dotstim.rst similarity index 100% rename from api/visual/dotstim.rst rename to source/api/visual/dotstim.rst diff --git a/api/visual/elementarraystim.rst b/source/api/visual/elementarraystim.rst similarity index 100% rename from api/visual/elementarraystim.rst rename to source/api/visual/elementarraystim.rst diff --git a/api/visual/form.rst b/source/api/visual/form.rst similarity index 100% rename from api/visual/form.rst rename to source/api/visual/form.rst diff --git a/api/visual/gratingstim.rst b/source/api/visual/gratingstim.rst similarity index 100% rename from api/visual/gratingstim.rst rename to source/api/visual/gratingstim.rst diff --git a/api/visual/helperfunctions.rst b/source/api/visual/helperfunctions.rst similarity index 100% rename from api/visual/helperfunctions.rst rename to source/api/visual/helperfunctions.rst diff --git a/api/visual/imagestim.rst b/source/api/visual/imagestim.rst similarity index 100% rename from api/visual/imagestim.rst rename to source/api/visual/imagestim.rst diff --git a/api/visual/index.rst b/source/api/visual/index.rst similarity index 100% rename from api/visual/index.rst rename to source/api/visual/index.rst diff --git a/api/visual/lightsource.rst b/source/api/visual/lightsource.rst similarity index 100% rename from api/visual/lightsource.rst rename to source/api/visual/lightsource.rst diff --git a/api/visual/line.rst b/source/api/visual/line.rst similarity index 100% rename from api/visual/line.rst rename to source/api/visual/line.rst diff --git a/api/visual/moviestim.rst b/source/api/visual/moviestim.rst similarity index 100% rename from api/visual/moviestim.rst rename to source/api/visual/moviestim.rst diff --git a/api/visual/noisestim.rst b/source/api/visual/noisestim.rst similarity index 100% rename from api/visual/noisestim.rst rename to source/api/visual/noisestim.rst diff --git a/api/visual/objmeshstim.rst b/source/api/visual/objmeshstim.rst similarity index 100% rename from api/visual/objmeshstim.rst rename to source/api/visual/objmeshstim.rst diff --git a/api/visual/patchstim.rst b/source/api/visual/patchstim.rst similarity index 100% rename from api/visual/patchstim.rst rename to source/api/visual/patchstim.rst diff --git a/api/visual/phongmaterial.rst b/source/api/visual/phongmaterial.rst similarity index 100% rename from api/visual/phongmaterial.rst rename to source/api/visual/phongmaterial.rst diff --git a/api/visual/pie.rst b/source/api/visual/pie.rst similarity index 100% rename from api/visual/pie.rst rename to source/api/visual/pie.rst diff --git a/api/visual/planestim.rst b/source/api/visual/planestim.rst similarity index 100% rename from api/visual/planestim.rst rename to source/api/visual/planestim.rst diff --git a/api/visual/polygon.rst b/source/api/visual/polygon.rst similarity index 100% rename from api/visual/polygon.rst rename to source/api/visual/polygon.rst diff --git a/api/visual/progressbar.rst b/source/api/visual/progressbar.rst similarity index 100% rename from api/visual/progressbar.rst rename to source/api/visual/progressbar.rst diff --git a/api/visual/radialstim.rst b/source/api/visual/radialstim.rst similarity index 100% rename from api/visual/radialstim.rst rename to source/api/visual/radialstim.rst diff --git a/api/visual/ratingscale.rst b/source/api/visual/ratingscale.rst similarity index 100% rename from api/visual/ratingscale.rst rename to source/api/visual/ratingscale.rst diff --git a/api/visual/rect.rst b/source/api/visual/rect.rst similarity index 100% rename from api/visual/rect.rst rename to source/api/visual/rect.rst diff --git a/api/visual/rift.rst b/source/api/visual/rift.rst similarity index 100% rename from api/visual/rift.rst rename to source/api/visual/rift.rst diff --git a/api/visual/rigidbodypose.rst b/source/api/visual/rigidbodypose.rst similarity index 100% rename from api/visual/rigidbodypose.rst rename to source/api/visual/rigidbodypose.rst diff --git a/api/visual/sceneskybox.rst b/source/api/visual/sceneskybox.rst similarity index 100% rename from api/visual/sceneskybox.rst rename to source/api/visual/sceneskybox.rst diff --git a/api/visual/secondorder.rst b/source/api/visual/secondorder.rst similarity index 100% rename from api/visual/secondorder.rst rename to source/api/visual/secondorder.rst diff --git a/api/visual/shapestim.rst b/source/api/visual/shapestim.rst similarity index 100% rename from api/visual/shapestim.rst rename to source/api/visual/shapestim.rst diff --git a/api/visual/simpleimagestim.rst b/source/api/visual/simpleimagestim.rst similarity index 100% rename from api/visual/simpleimagestim.rst rename to source/api/visual/simpleimagestim.rst diff --git a/api/visual/slider.rst b/source/api/visual/slider.rst similarity index 100% rename from api/visual/slider.rst rename to source/api/visual/slider.rst diff --git a/api/visual/spherestim.rst b/source/api/visual/spherestim.rst similarity index 100% rename from api/visual/spherestim.rst rename to source/api/visual/spherestim.rst diff --git a/api/visual/textbox.rst b/source/api/visual/textbox.rst similarity index 100% rename from api/visual/textbox.rst rename to source/api/visual/textbox.rst diff --git a/api/visual/textbox2.rst b/source/api/visual/textbox2.rst similarity index 100% rename from api/visual/textbox2.rst rename to source/api/visual/textbox2.rst diff --git a/api/visual/textstim.rst b/source/api/visual/textstim.rst similarity index 100% rename from api/visual/textstim.rst rename to source/api/visual/textstim.rst diff --git a/api/visual/vshd.rst b/source/api/visual/vshd.rst similarity index 100% rename from api/visual/vshd.rst rename to source/api/visual/vshd.rst diff --git a/api/visual/window.rst b/source/api/visual/window.rst similarity index 100% rename from api/visual/window.rst rename to source/api/visual/window.rst diff --git a/api/visual/windowframepack.rst b/source/api/visual/windowframepack.rst similarity index 100% rename from api/visual/windowframepack.rst rename to source/api/visual/windowframepack.rst diff --git a/api/visual/windowwarp.rst b/source/api/visual/windowwarp.rst similarity index 100% rename from api/visual/windowwarp.rst rename to source/api/visual/windowwarp.rst diff --git a/api/web.rst b/source/api/web.rst similarity index 100% rename from api/web.rst rename to source/api/web.rst From c29a2ef0aa7dbaca60b410348ce38c845f8f1d54 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Wed, 25 Mar 2026 15:35:51 +0000 Subject: [PATCH 23/67] SYS: make sure we don't accidentally publish from dev branch changes The upload/deploy steps should only occur from the release branch --- .github/workflows/build_docs.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index 1127417..ccea477 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -12,7 +12,7 @@ on: pp_branch: description: 'PsychoPy branch to build docs from' required: true - default: 'dev' + default: 'release' type: choice options: - dev @@ -117,7 +117,7 @@ jobs: path: build/latex/PsychoPyManual.tex # upload as artefact for deployment job - name: Upload static files as artifact - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.ref_name == 'release' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') id: deployment uses: actions/upload-pages-artifact@v3 # or specific "vX.X.X" version tag for this action with: @@ -125,7 +125,7 @@ jobs: # Deployment job converts the artifact into a GitHub Pages deployment deploy: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.ref_name == 'release' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') permissions: contents: write pages: write # Added for GitHub Pages deployment From 193ba1757357b2d6219e646626e67a1afce23753 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Wed, 25 Mar 2026 15:36:21 +0000 Subject: [PATCH 24/67] Incorrect title highlight for psychopy.app --- source/api/app.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/api/app.rst b/source/api/app.rst index f59b593..6ffc079 100644 --- a/source/api/app.rst +++ b/source/api/app.rst @@ -11,7 +11,7 @@ intended for unit testing the GUI and for developers wishing to extend it. .. currentmodule:: psychopy.app Overview -======== +-------- .. autosummary:: startApp @@ -21,7 +21,7 @@ Overview getAppFrame Details -======= +------- .. autofunction:: startApp .. autofunction:: quitApp From 980e51ff01e63515af4c7ece385d298cde014572 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Wed, 25 Mar 2026 15:37:16 +0000 Subject: [PATCH 25/67] Remove reference to deprecated Microphone class --- source/api/encryption.rst | 13 ------------- source/api/microphone.rst | 8 -------- 2 files changed, 21 deletions(-) delete mode 100644 source/api/encryption.rst delete mode 100644 source/api/microphone.rst diff --git a/source/api/encryption.rst b/source/api/encryption.rst deleted file mode 100644 index f6e90ea..0000000 --- a/source/api/encryption.rst +++ /dev/null @@ -1,13 +0,0 @@ - -Encryption -============ - -Some labs may wish to better protect their data from casual inspection -or accidental disclosure. This is possible within |PsychoPy| using a separate -python package, pyFileSec, which grew out of |PsychoPy|. pyFileSec is distributed -with the StandAlone versions of |PsychoPy|, or can be installed using pip or easy_install -via https://pypi.python.org/pypi/PyFileSec/ - -Some elaboration of pyFileSec usage and security strategy can be found here: https://pythonhosted.org/PyFileSec - -Basic usage is illustrated in the Coder demo > misc > encrypt_data.py diff --git a/source/api/microphone.rst b/source/api/microphone.rst deleted file mode 100644 index 03f2951..0000000 --- a/source/api/microphone.rst +++ /dev/null @@ -1,8 +0,0 @@ -:orphan: - -.. _legacy_microphone: - -Deprecated Microphone -====================================================== - -**Deprecated** Use :class:`~psychopy.sound.microphone.Microphone` for new projects. \ No newline at end of file From c2f892e84727b84d828ea8c5d44833fbcd9604cc Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Sat, 28 Mar 2026 11:09:55 +0000 Subject: [PATCH 26/67] Update blue button to 2026.1.3 --- source/download.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source/download.rst b/source/download.rst index 495c0c4..15eb8a5 100644 --- a/source/download.rst +++ b/source/download.rst @@ -48,10 +48,10 @@ Download We will continue to release new versions until PsychoPy Studio is stable enough to replace it. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/StandalonePsychoPy-2026.1.2-win64-3.10.exe + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.3/StandalonePsychoPy-2026.1.3-win64-3.10.exe :color: primary - PsychoPy Standalone 2026.1.2 + PsychoPy Standalone 2026.1.3 .. grid-item-card:: PsychoPy Studio (Beta) @@ -59,10 +59,10 @@ Download The underlying experiment engine is still Python, but Studio provides a smoother, more user-friendly interface and better version independence with useVersion. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/PsychoPy_Studio_2026.1.2.exe + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.3/PsychoPy_Studio_2026.1.3.exe :color: primary - PsychoPy Studio 2026.1.2 + PsychoPy Studio 2026.1.3 .. tab-item:: MacOS :class-label: mac-tab @@ -76,10 +76,10 @@ Download We will continue to release new versions until PsychoPy Studio is stable enough to replace it. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/StandalonePsychoPy-2026.1.2-macos-3.10.dmg + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.3/StandalonePsychoPy-2026.1.3-macos-3.10.dmg :color: primary - PsychoPy Standalone 2026.1.2 + PsychoPy Standalone 2026.1.3 .. grid-item-card:: PsychoPy Studio (Beta) @@ -87,10 +87,10 @@ Download The underlying experiment engine is still Python, but Studio provides a smoother, more user-friendly interface and better version independence with useVersion. - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/PsychoPy_Studio_2026.1.2.dmg + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.3/PsychoPy_Studio_2026.1.3.dmg :color: primary - PsychoPy Studio 2026.1.2 + PsychoPy Studio 2026.1.3 .. tab-item:: Linux :class-label: linux-tab @@ -136,11 +136,11 @@ Download Plus, it's much easier to install on Linux! - .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.2/PsychoPy_Studio_2026.1.2.AppImage - :tooltip: PsychoPy_Studio_2026.1.2.AppImage + .. button-link:: https://github.com/psychopy/psychopy/releases/download/2026.1.3/PsychoPy_Studio_2026.1.3.AppImage + :tooltip: PsychoPy_Studio_2026.1.3.AppImage :color: primary - PsychoPy Studio 2026.1.2 + PsychoPy Studio 2026.1.3 **For all versions** see the `PsychoPy releases on github `_ From 7d004051c1087f34e73fb2247bb0f649d2d5afad Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Tue, 28 Apr 2026 16:05:55 +0100 Subject: [PATCH 27/67] Fix missing plugins.json file --- .github/workflows/build_docs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index ccea477..f40279f 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -89,6 +89,9 @@ jobs: - name: "HTML: Sphinx build html" run: | sphinx-build -b html source build/html + # then copy over the plugins.json from psychopy/plugins repo + curl -LO https://github.com/psychopy/plugins/raw/refs/heads/main/plugins.json + mv plugins.json build/html - name: "PDF: Sphinx build latex" run: | From 895cc70d474088e6354c10497f2c62d7869fa5be Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Tue, 28 Apr 2026 16:08:37 +0100 Subject: [PATCH 28/67] Allow the plugins.json to come from dev or release branches --- .github/workflows/build_docs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index f40279f..e1f275d 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -90,7 +90,7 @@ jobs: run: | sphinx-build -b html source build/html # then copy over the plugins.json from psychopy/plugins repo - curl -LO https://github.com/psychopy/plugins/raw/refs/heads/main/plugins.json + curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json mv plugins.json build/html - name: "PDF: Sphinx build latex" From f2da6ea2d42dadcc0ec822a2cfeb1827595dbc04 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Wed, 29 Apr 2026 09:46:20 +0100 Subject: [PATCH 29/67] Testing why Tectonic stopped building --- .github/workflows/build_docs.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index e1f275d..085d9ce 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -89,10 +89,6 @@ jobs: - name: "HTML: Sphinx build html" run: | sphinx-build -b html source build/html - # then copy over the plugins.json from psychopy/plugins repo - curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json - mv plugins.json build/html - - name: "PDF: Sphinx build latex" run: | sphinx-build -b latex source build/latex @@ -108,6 +104,10 @@ jobs: - name: "PDF: Build PDF with Tectonic" run: | tectonic build/latex/PsychoPyManual.tex --outdir build/html + - name: fetch plugins.json + run: | + curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json + mv plugins.json build/html - name: "DEBUG: Upload docs as artifact" uses: actions/upload-artifact@v6 with: From e76c6f5f84247049c2a240fa3ad1e8c3f407f138 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Wed, 29 Apr 2026 10:15:31 +0100 Subject: [PATCH 30/67] Workaround current tectonic failure to build --- .github/workflows/build_docs.yaml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index 085d9ce..e0bedf3 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -92,18 +92,22 @@ jobs: - name: "PDF: Sphinx build latex" run: | sphinx-build -b latex source build/latex - - name: "PDF: Cache Tectonic files" - uses: actions/cache@v5 - with: - path: ~/.cache/Tectonic - key: ${{ runner.os }}-tectonic-${{ hashFiles('**/*.tex') }} - restore-keys: | - ${{ runner.os }}-tectonic- + # - name: "PDF: Cache Tectonic files" + # uses: actions/cache@v5 + # with: + # path: ~/.cache/Tectonic + # key: ${{ runner.os }}-tectonic-${{ hashFiles('**/*.tex') }} + # restore-keys: | + # ${{ runner.os }}-tectonic- - name: "PDF: Set up Tectonic" uses: wtfjoke/setup-tectonic@v4 - name: "PDF: Build PDF with Tectonic" run: | - tectonic build/latex/PsychoPyManual.tex --outdir build/html + # try building with tectonic or fetch from psychopy.org if that fails (e.g. due to missing latex packages) + if ! tectonic build/latex/PsychoPyManual.tex --outdir build/html; then + echo "Tectonic build failed, fetching pre-built PDF from psychopy.org" + curl -L -o build/html/PsychoPyManual.pdf https://www.psychopy.org/PsychoPyManual.pdf + fi - name: fetch plugins.json run: | curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json @@ -115,6 +119,7 @@ jobs: path: build/html - name: "DEBUG: Upload PsychoPyManual.tex as artifact" uses: actions/upload-artifact@v6 + if: always() # upload even if build fails, to allow debugging of build issues with: name: PsychoPyManual.tex path: build/latex/PsychoPyManual.tex From 0b68f8b1a2f0c2c2404afb99cb9d576fe91487d3 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Thu, 30 Apr 2026 12:10:09 +0100 Subject: [PATCH 31/67] plugins.json was being sabotaged by jekyll --- .github/workflows/build_docs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index e0bedf3..2d0bb1d 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -112,6 +112,9 @@ jobs: run: | curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json mv plugins.json build/html + - name: add nojekyll to docs folder + run: | + touch .nojekyll - name: "DEBUG: Upload docs as artifact" uses: actions/upload-artifact@v6 with: From c98a73d6f79cafb0bb253a55dfa02f14389705f7 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:46:54 +0100 Subject: [PATCH 32/67] Enhance figure alt text in Pavlovia project guide Updated figure descriptions and alt text for clarity. --- source/online/create-pavlovia-project.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/online/create-pavlovia-project.rst b/source/online/create-pavlovia-project.rst index 41dc4f7..53aef4a 100644 --- a/source/online/create-pavlovia-project.rst +++ b/source/online/create-pavlovia-project.rst @@ -43,6 +43,7 @@ Create a new Pavlovia project 6. You should now be able to find your experiment when you log into Pavlovia.org, under **Dashboard → Experiments**. .. figure:: /images/newProjectUpload.png + :alt: “New Project” dialog in PsychoPy Builder for uploading a Pavlovia project, with fields for the project name and the local root path of the experiment files. :name: newProjectUpload :align: center :figclass: align-center @@ -56,6 +57,7 @@ Interacting with |Pavlovia| from the Builder App When running your study online, the relevant controls are in the Browser and Pavlovia sections of the ribbon: .. figure:: /images/pavlovia_icons_2025.png + :alt: PsychoPy Builder showing the “Browser” and “Pavlovia” tabs. The Browser section includes icons for compiling to JavaScript, running an online pilot, and syncing to Pavlovia. The Pavlovia tab displays the user’s username and the current project path. :name: pavlovia_icons_2025 :align: center :figclass: align-center @@ -85,6 +87,7 @@ Every time you make a change locally, click **Sync** to update the online versio To view the version history of your experiment, log into Pavlovia.org, go to your experiment page and click on the **"View Code"** button. Here you can select **"Repository" → "Commits"** to see a list of all changes made to your experiment, and download previous versions if you wish. To reinstate a previous version, you will need to download the files, replace those in your existing local folder (keeping the same names) and re-upload them to your local experiment folder. .. figure:: /images/navigateVersionHistory.png + :alt: GitLab interface showing navigation to “Repository” to “Commits,” with a commit list where a file-open icon next to each entry allows viewing the Pavlovia project at that specific revision. :name: navigateVersionHistory :align: center :figclass: align-center From af1c52282b7aefaa7acb80cd8976968b727ceffb Mon Sep 17 00:00:00 2001 From: michaelwoodc Date: Thu, 30 Apr 2026 07:48:03 -0400 Subject: [PATCH 33/67] added alt text for device manager image --- source/general/deviceManager.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/general/deviceManager.rst b/source/general/deviceManager.rst index 31e3b00..af3dd7c 100644 --- a/source/general/deviceManager.rst +++ b/source/general/deviceManager.rst @@ -20,6 +20,7 @@ To use a specific device in a component you can select the "Device" tab. For exa .. figure:: /images/deviceManagerSetUp.png :scale: 50% + :alt: "Add device modal on the left with sound Properties modal on the right" Screenshot of the device manager window showing a list of configured devices (left) and how to use a configured device in a component (right). From a404f346b83a5a4b0a7d5a023debdb4388725f60 Mon Sep 17 00:00:00 2001 From: Charlotte Potter <161601250+Charlotte-Potter@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:49:41 +0100 Subject: [PATCH 34/67] Update ApertureComponent.rst --- source/builder/components/ApertureComponent.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/builder/components/ApertureComponent.rst b/source/builder/components/ApertureComponent.rst index cdfb03a..2cfa184 100644 --- a/source/builder/components/ApertureComponent.rst +++ b/source/builder/components/ApertureComponent.rst @@ -11,6 +11,7 @@ Aperture Component .. image:: /images/aperture.gif :width: 60% + :alt: "A grey screen with a moving cirle, which is linked to a mouse. As the mouse is moved a photo of a landscape underneath is shown as if revealing a hidden image underneath." This component can be used to filter the visual display, as if the subject is looking at it through an opening (i.e. add an image component, as the background image, then add an aperture to show part of the image). Currently, in builder, only circular apertures are supported (you can change the shape by specifying your aperture in a code component- we are hoping to make it easier to do this through builder soon!). Moreover, only one aperture is enabled at a time. You can't "double up": a second aperture takes precedence. Currently this component **does not run online** (`see the status of online options `_, but you can achieve something similar online using an image with a mask: see an `example demo here `_ with corresponding `PsychoPy experiment files here `_ or by using the `MouseView plugin `_. @@ -236,4 +237,4 @@ Validate with... API reference for :class:`~psychopy.visual.Aperture` -.. redirect-from:: aperture.rst \ No newline at end of file +.. redirect-from:: aperture.rst From 5615282a74bb607300611e3ba2d08c05997e96ef Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:55:17 +0100 Subject: [PATCH 35/67] DOC: Added alt-text to Pavlovia dashboard image --- source/online/launch-and-collect-data.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/online/launch-and-collect-data.rst b/source/online/launch-and-collect-data.rst index fbe6b82..bb4d78f 100644 --- a/source/online/launch-and-collect-data.rst +++ b/source/online/launch-and-collect-data.rst @@ -28,6 +28,7 @@ Once your experiment is ready for participants: 2. Under **Project Status**, select **Running**. This makes the experiment accessible to participants who you share the link with. .. figure:: /images/runningModePavlovia.png + :alt: "Screenshot of a Pavlovia experiment dashboard." :name: runningModePavlovia :align: center :figclass: align-center From cf834fb07d509782b20ced980ee0fb07f05d0a17 Mon Sep 17 00:00:00 2001 From: Turyesh Date: Thu, 30 Apr 2026 17:25:19 +0530 Subject: [PATCH 36/67] DOC: Added alt text to Psychopy Builder image --- source/online/known-limitations.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/online/known-limitations.rst b/source/online/known-limitations.rst index 7620ede..d7c2281 100644 --- a/source/online/known-limitations.rst +++ b/source/online/known-limitations.rst @@ -85,6 +85,7 @@ files that are not available online. This can happen if you have files (images, .. figure:: /images/addResources.png + :alt: "Screenshot of the Psychopy Builder" :name: filterComponents :align: center :figclass: align-center From 8b0052fa434d50cb287571fbe83bdc0f6b6ffda7 Mon Sep 17 00:00:00 2001 From: raj-labcode Date: Thu, 30 Apr 2026 12:55:22 +0100 Subject: [PATCH 37/67] DOC: added alt-text to DKL graph --- source/general/colours.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/general/colours.rst b/source/general/colours.rst index 6bfb74c..1138379 100644 --- a/source/general/colours.rst +++ b/source/general/colours.rst @@ -83,6 +83,7 @@ To use DKL color space the monitor should be calibrated with an appropriate spec In the Derrington, Krauskopf and Lennie [#dkl1984]_ color space (based on the Macleod and Boynton [#mb1979]_ chromaticity diagram) colors are represented in a 3-dimensional space using spherical coordinates that specify the `elevation` from the isoluminant plane, the `azimuth` (the hue) and the contrast (as a fraction of the maximal modulations along the cardinal axes of the space). .. image:: /images/dklSpace.png + :alt:"3 dimensional graph of colours" In |PsychoPy| these values are specified in units of degrees for elevation and azimuth and as a float (ranging -1:1) for the contrast. From b1bfe99580012cefcb08cb3612dc592ebf01dc8d Mon Sep 17 00:00:00 2001 From: Julian Bas <49486428+juju-bowow@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:55:33 +0100 Subject: [PATCH 38/67] DOC: Added alt text to frame timing image --- source/general/timing/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/general/timing/index.rst b/source/general/timing/index.rst index 5eb66c7..0aa23d9 100644 --- a/source/general/timing/index.rst +++ b/source/general/timing/index.rst @@ -27,6 +27,7 @@ There are certain steps that we strongly advise you to take before running an ex * Check that your stimulus presentation monitor is not dropping frames. You can do this by running the timeByFrames.py demo. Find this demo in the `Coder` window > demos > timing. The timeByFrames demo examines the precision of your frame flips, and shows the results in a plot similar to the one below: .. figure:: /images/timeByFrameRes.png + :alt: "A figure showing two performance graphs that analyse frame timing data. The first graph is a caterpillar plot. The vertical axis shows time in milliseconds and ranges from 16.45 to 16.85. The horizontal axis shows the sequence of frames and goes from 0 to 500. The caterpillar line shows fluctuations in frame time around a central value of approximately 16.65ms. The second graph is a histogram titled. The vertical axis showc a count of frames and ranges from 0 to 140. The horizontal axis is shows a range of millisecond values. The histogram bars show a high concentration of frames occurring between 16.6ms and 16.7ms, forming a narrow peak that indicates a consistent frame rate." The results here are for a 60Hz monitor, and you can see that there are no dropped frames from the left hand side of the screen, and also the timing of each frame is 16.7ms (shown on the right-hand side of the screen) which is what we would expect from a 60Hz monitor (1000ms/60 = 16.66ms). From 059a3e372a3b8591cfc9b1b5cc782d57e84c3e24 Mon Sep 17 00:00:00 2001 From: Julian Bas <49486428+juju-bowow@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:05:31 +0100 Subject: [PATCH 39/67] DOC: Added alt text to frame timing image Fixed typos and tab indent error --- source/general/timing/index.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/general/timing/index.rst b/source/general/timing/index.rst index 0aa23d9..e48ea66 100644 --- a/source/general/timing/index.rst +++ b/source/general/timing/index.rst @@ -26,8 +26,10 @@ There are certain steps that we strongly advise you to take before running an ex * Read `this timing megastudy `_ by Bridges et al (2020) which compares several pieces of behavioural software in terms of their temporal precision. You can find a summary of the results here: :ref:`timing2020` * Check that your stimulus presentation monitor is not dropping frames. You can do this by running the timeByFrames.py demo. Find this demo in the `Coder` window > demos > timing. The timeByFrames demo examines the precision of your frame flips, and shows the results in a plot similar to the one below: + .. figure:: /images/timeByFrameRes.png - :alt: "A figure showing two performance graphs that analyse frame timing data. The first graph is a caterpillar plot. The vertical axis shows time in milliseconds and ranges from 16.45 to 16.85. The horizontal axis shows the sequence of frames and goes from 0 to 500. The caterpillar line shows fluctuations in frame time around a central value of approximately 16.65ms. The second graph is a histogram titled. The vertical axis showc a count of frames and ranges from 0 to 140. The horizontal axis is shows a range of millisecond values. The histogram bars show a high concentration of frames occurring between 16.6ms and 16.7ms, forming a narrow peak that indicates a consistent frame rate." + :alt: "A figure showing two performance graphs that analyse frame timing data. The first graph is a caterpillar plot. The vertical axis shows time in milliseconds and ranges from 16.45 to 16.85. The horizontal axis shows the sequence of frames and goes from 0 to 500. The caterpillar line shows fluctuations in frame time around a central value of approximately 16.65ms. The second graph is a histogram. The vertical axis shows a count of frames and ranges from 0 to 140. The horizontal axis is shows a range of millisecond values. The histogram bars show a high concentration of frames occurring between 16.6ms and 16.7ms, forming a narrow peak that indicates a consistent frame rate." + The results here are for a 60Hz monitor, and you can see that there are no dropped frames from the left hand side of the screen, and also the timing of each frame is 16.7ms (shown on the right-hand side of the screen) which is what we would expect from a 60Hz monitor (1000ms/60 = 16.66ms). From 510edb8f46826bce1e15bea0f1d7de79c4c58809 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Thu, 30 Apr 2026 14:45:46 +0100 Subject: [PATCH 40/67] put .nojekyll straight into build/html ? Putting it in the root of the repo didn't seem to work --- .github/workflows/build_docs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index 2d0bb1d..835007a 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -113,6 +113,7 @@ jobs: curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json mv plugins.json build/html - name: add nojekyll to docs folder + working-directory: build/html run: | touch .nojekyll - name: "DEBUG: Upload docs as artifact" From 7897860af82664afe596fe1bf5635126c5d19b98 Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Thu, 30 Apr 2026 16:13:58 +0100 Subject: [PATCH 41/67] Fix to location of the plugins.json download plugins repo doesn't have release/dev only main --- .github/workflows/build_docs.yaml | 4 +- plugins.json | 1483 +++++++++++++++++++++++++++++ 2 files changed, 1485 insertions(+), 2 deletions(-) create mode 100644 plugins.json diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index 835007a..58eb3dd 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -110,9 +110,9 @@ jobs: fi - name: fetch plugins.json run: | - curl -LO https://github.com/psychopy/plugins/raw/refs/heads/${{ github.event.inputs.pp_branch }}/plugins.json + curl -LO https://github.com/psychopy/plugins/raw/refs/heads/main/plugins.json mv plugins.json build/html - - name: add nojekyll to docs folder + - name: add .nojekyll to docs folder so Actions doesn't rebuild with jekyll working-directory: build/html run: | touch .nojekyll diff --git a/plugins.json b/plugins.json new file mode 100644 index 0000000..db25843 --- /dev/null +++ b/plugins.json @@ -0,0 +1,1483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Page not found · GitHub · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+ Skip to content + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + +
+ + + + + + + + + +
+
+ + + +
+
+ +
+
+ 404 “This is not the web page you are looking for” + + + + + + + + + + + + +
+
+ +
+
+ +
+ + +
+
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + From 8ec61c8c7a7a57fc01adb61c2ad09f20764dd96f Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Fri, 1 May 2026 10:43:28 +0100 Subject: [PATCH 42/67] Create documentation for PsychoPy Studio Added documentation for PsychoPy Studio, including features, webinar Q&A, and installation instructions. --- source/about/psychopystudio.rst | 135 ++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 source/about/psychopystudio.rst diff --git a/source/about/psychopystudio.rst b/source/about/psychopystudio.rst new file mode 100644 index 0000000..629f9c7 --- /dev/null +++ b/source/about/psychopystudio.rst @@ -0,0 +1,135 @@ +.. _psychopystudio: + +PsychoPy Studio +===================================== + +In early 2026 we released a new version of the|PsychoPy| app, PsychoPy studio. You can read more about why PsychoPy studio was created and what we hope it will offer for the future `in this blog post `_. + +In April, we hosted a webinar to introduce PsychoPy studio to the world, you can watch the webinar below and read the Q & A from attendees. We really hope that you will like PsychoPy studio - please download it and try it (it can be installed alongside your standalone release) please share your feedback via `the PsychoPy forum `_. + + +.. youtube:: kwkiWzwJNDs + +Q&A from Webinar Attendees +-------------------------- + +**"Does Psychopy have newer articles or releases to cite than those listed here, or should we still go with these? +https://psychopy.readthedocs.io/en/stable/about/index.html"** + + No not yet - but hopefully soon! + +**Many here rely on Psychopy’s old photometer support and automated gamma measurement/correction tool in Monitor Center. Just a minor request to please keep that functionality if possible!** + + This is absolutely our intention, yes! There's a few bugs in the current release that we're hoping to fix soon, but the intention is very much to keep that functionality. + +**Nice to see the presentation is on Linux :-). Two Qs: 1) Any official documentation about installing to Linux? 2) A typical error on Linux installations was the (in)ability to upload/sync to Pavlovia. Is this still the case?** + + 1) On the download page (psychopy.org/download), there's tabs for each OS. For Standalone there's instructions for the old method, for Studio it's just a downloadable .AppImage file (installing will depend on your distro, I used Gear Lever for my Bazzite GNOME setup) + 2) I've been syncing to Pavlovia from Studio on Linux and generally found it much the same as on Windows/Mac :) + +**Will the QoL updates shown also be added to the regular version?** + + If you mean things like drag and drop of components and loops, then no, since it is the way Studio has been written that makes these easy to implement. + +**Does PsychoPy Studio support adding Additional resources (e.g., Webgazer) like before?** + + Yes! For the webgazer example specifically - this is currently implemented with javascript code components in a Builder experiment. Since you can still use code components (including JavaScript code components) in Studio - then this means you should be able to interact with external packages including webgazer.js in the same way. + +**For teaching in the Autumn 26 would you recommend sticking to Standalone or making a jump? ** + + I personally am hoping to be able to switch to Studio for teaching from version 2026.2.0, which should be released in time for the Autumn. + +**I have two question: 1, I am currently collecting data using PsychoPy Standalone, if I switch to psychopy studio now, will it affects the data and creat mismatch between my old data file and new data file? 2. I just installed it (super fast to open!) I notice that there is no separate app/icon for psychopy runner? Sometime, we let the student run the study with only the the Runner so they don't change anything in the experiment, since some of them don't have experience with the software.** + + In preferences you can choose which view opens when you open the app (to select Runner) - but yes we do want to retain runner view for the purpose you describe. + + There shouldn’t be mismatch between your data files - however we do recommend testing in the same way as you would when moving between standalone releases. Do remember to turn on use version. + +**Any way to test run experiments without having user input? As you can in E-Prime at a silly test speed generating test data?** + + There is a plugin already available called "monkeys" which does this. Try installing it from the Plugins menu. + +**Does the studio (browser) version has any implications for controlling local devices (eyetracking, EEG, biopac, etc)** + + The packaged electron app can still communicate with hardware in the same way (it still uses the same python libraries). In future when this is in a webpage this could be difficult because browsers could place limitations on python based functionalities. + +**Are there any plans to add some sort of interface to rearrange components within a routine by using drag and drop ** + + This is already possible in Studio (but not Standalone) using drag-and-drop! + +**We have users that write their entire experiment in a conda environment. They don’t use any Psychopy GUIs at all. They just pip install psychopy and write their code. Should I warn them that this may go away?** + + No. If you don’t use the app GUI at all then `pip install psychopy` will achieve exactly the same in the future as it does now. Actually it should be easier because, without the app being included, there are fewer dependencies, like no need for wxpython. + It should also be easier to use in a combination - install the Studio app and it will get a venv ready for you to use from other places (in a way that the old standalone python was not) + +**Are there any obvious bugs in the current PsychoPy Studio build?** + + If you go to github and look up psychopy-studio repo any pull request that starts with BF reflects a bugfix pull request. Those most important to look out for are: Loading of plugins, monitor center and interacting with pavlovia. but these will be fixed soon. + +**I wrote a PsychoPy app that makes it easier to find coordinates of objects on the screen. It helped me develop a page with lots of objects on the page. I can describe in more detail if interested.** + + That sounds interesting. Is it something you could share on the PsychoPy forum https://discourse.psychopy.org/ ? + +**Could you please do an introduction to the plugin system and how it may have changed? Are there new plugin requirements? Any plugin development resources?** + + We don’t anticipate any real changes around plugins, although the icons can now support vector (svg) files (and that has the advantage we can adapt them more easily at runtime according to the theme) + +**Is there a way of converting old experiments into studio version (beyond just running old versions in studio)** + + The file format is exactly the same (.psyexp) so you should be able to simply open your old experiments in Studio. The Studio itself is just a 'wrapper' to interact with the PsychoPy Python library, which hasn't changed. + +**Hello I have 2 silly questions: "Do I need to download PsychoPy to use Pavlovia in the browser? When will the Pavlovia app be available for download?"** + + Currently you need to download PsychoPy Studio (or PsychoPy standalone) to write an experiment suitable for running the browser. The plan is that eventually you will be able to write experiments directly in a web version of PsychoPy Studio and won't need to download anything unless you want to run locally. PsychoPy Studio is available to download now. There is also a different "PsychoPy App" which is in development. + +**Studio is smooth as you mentioned and standalone takes a lot of time to open and its slow, I have already developed an experiment in standalone, would it be more easy to replicate the same in the studio by following the same procedure with some minimal changes. And if there is any guide avilable to use Psychopy studio more conveniently** + + You should be able to open the experiment you've already created in Standalone in Studio and run/edit it from there (although of course if not, let us know via the forum - discourse.psychopy.org!). The interface itself is very similar to the Standalone version so our current docs should hopefully help! + +**Is it possible to use the app in a browser on a tablet?** + + Not yet, but this will be coming soon -- hopefully next year. + +**I'd appreciate it if you could tell me whether you think there'd be any problem running an experiment programmed using the 2023.2.3. on the latest PsychoPy Studio. I upgraded it once to a 2025 version but my experiment crashed. Though, I don't remember what the error was now. So, I downgraded back to the 2023 version. Everything works great with the 2023.2.3 version for me, I am concerned once I switch to PsychoPy studio, again my experiment will crash.** + + We would suggest giving it a go but duplicate the folder (don’t save over the original folder) - and let us know in the forum! In theory Studio has better use version functionality - so the experiance should be better - but test carefully and please let us know. + +**Will future workshops be using PsychoPy Studio for all the demos?** + + Yes! Sometime soon, at least when it doesn’t carry the “Beta” tag anymore (that’s the key to us thinking this is production ready) + +**Any changes related to the rendering of fonts in the Studio, especially non-English fonts? I had experienced issues while showing two different fonts in the same routine.** + + There is no changes to this based on Studio - any issues will be due to the python library rather than the studio interface. There have been some recent fixes (which might fix the issue you describe) - but keep us posted on the forum! + +**Are you planning to add a kind of layout view to Studio that makes it easier to place components on the screen?** + + YESSS we really would like this - and wx was holding us back - so fingers crossed this should happen at some point. + +**Apple warns me that “this version” of Standalone PsychoPy will not open in future versions of MacOS. 1. Is that going to be a problem with Standalone for Mac users “soon” and 2. Will Studio avoid this problem?** + + This should be fine. So far we have been supporting newer chips using an emulator mode in rosetta for mac OS but that is being dropped in the near future. However from this summer we should support native mac sillocon arm chips (2026.2.0 onwards). + +**I’m not sure if this is already default behavior during installation in Windows, but if not, it would important to allow Studio installs on a per user account basis (Standalone does this). We have shared machines where each user has a login.** + + To summarise the live anser: Currently, Studio installs to Program Files (so system-wide and requires admin), but creates its Python environments and saves preferences in a user space. So installing the software itself requires admin, but once installed users will retain their own preferences and shouldn't need admin access to make new Python environments (e.g. when running an experiment set to use an older version). I believe it's possible in Electron to add a choice in the installer whether to install in Program Files or some user space, so I'll look into it for the next major release :) + +**Hi! I am testing the app on Mac. When trying to add a loop, the app displays ‘Loading loops…’ and goes nowhere from here. Is this a bug or am I doing something wrong? Thanks a lot!** + + You might need to wait a bit and reload the app! on first install you will see “Loading Components” and there might be something similar for loops - so give it a little time and re-open and I suspect this should work as expected. + +**I just tried to install it on my Uni PC and I need admin to sign in** + + Thanks for the feedback. + +**Where are the studio stickers????😬😂** + + VERY important question. We are awaiting our new merch to arrive… very soon… + +**Translation to other languages sometimes gets funny but it's a bit difficult to go back to English in the Standalone. Is this something that could be made easier in Studio?** + + Yes! Unfortunately translation didn't quite make it into the release but we've got a new system for label translations ready to go in 2026.2.0 and have been coordinating our volunteer translators getting ready to translate all the new labels Studio adds + +**Would like to report that for our uni where we used a cloud computing system for student learning, we just had admin install the standalone version on their end. Students had their own settings and preferences on their own terminals. Not sure about studio but will see** + + Yes, this will be similar for Studio but we’ll need to provide this new option to allowyour admins to configure where the Python environments are stored (to prevent a machine with 50 users having 50+ python installations on it) From 9d71e3526fade0677faa1c7e37093df11ebaa163 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Fri, 1 May 2026 10:44:19 +0100 Subject: [PATCH 43/67] Add psychopystudio to the table of contents --- source/about/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/about/index.rst b/source/about/index.rst index b5c303b..1db4838 100755 --- a/source/about/index.rst +++ b/source/about/index.rst @@ -7,6 +7,7 @@ About |PsychoPy| :maxdepth: 1 overview + psychopystudio testimonials ../screenshots credits From 0f4e1d65423640ce53e48f9b08f1704e2e02c2d7 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Fri, 1 May 2026 13:53:52 +0100 Subject: [PATCH 44/67] Revise YouTube link for PsychoPy studio webinar Updated the YouTube link format for the launch webinar. --- source/about/psychopystudio.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/about/psychopystudio.rst b/source/about/psychopystudio.rst index 629f9c7..ba7a53c 100644 --- a/source/about/psychopystudio.rst +++ b/source/about/psychopystudio.rst @@ -8,7 +8,8 @@ In early 2026 we released a new version of the|PsychoPy| app, PsychoPy studio. Y In April, we hosted a webinar to introduce PsychoPy studio to the world, you can watch the webinar below and read the Q & A from attendees. We really hope that you will like PsychoPy studio - please download it and try it (it can be installed alongside your standalone release) please share your feedback via `the PsychoPy forum `_. -.. youtube:: kwkiWzwJNDs +`**Watch our launch webinar on YouTube** `_. + Q&A from Webinar Attendees -------------------------- From 6407b2d4127575d6e9988152c3a814224cfbfc53 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Fri, 1 May 2026 13:54:17 +0100 Subject: [PATCH 45/67] Bold the YouTube webinar link in documentation --- source/about/psychopystudio.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/about/psychopystudio.rst b/source/about/psychopystudio.rst index ba7a53c..e6d5f49 100644 --- a/source/about/psychopystudio.rst +++ b/source/about/psychopystudio.rst @@ -8,7 +8,7 @@ In early 2026 we released a new version of the|PsychoPy| app, PsychoPy studio. Y In April, we hosted a webinar to introduce PsychoPy studio to the world, you can watch the webinar below and read the Q & A from attendees. We really hope that you will like PsychoPy studio - please download it and try it (it can be installed alongside your standalone release) please share your feedback via `the PsychoPy forum `_. -`**Watch our launch webinar on YouTube** `_. +**`Watch our launch webinar on YouTube `_.** Q&A from Webinar Attendees From 610bceb7922b94f7e0ca1973576b97653a0b1b81 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Fri, 1 May 2026 13:54:29 +0100 Subject: [PATCH 46/67] Update psychopystudio.rst --- source/about/psychopystudio.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/about/psychopystudio.rst b/source/about/psychopystudio.rst index e6d5f49..79bc5a7 100644 --- a/source/about/psychopystudio.rst +++ b/source/about/psychopystudio.rst @@ -8,7 +8,7 @@ In early 2026 we released a new version of the|PsychoPy| app, PsychoPy studio. Y In April, we hosted a webinar to introduce PsychoPy studio to the world, you can watch the webinar below and read the Q & A from attendees. We really hope that you will like PsychoPy studio - please download it and try it (it can be installed alongside your standalone release) please share your feedback via `the PsychoPy forum `_. -**`Watch our launch webinar on YouTube `_.** +`Watch our launch webinar on YouTube `_. Q&A from Webinar Attendees From 3948cdf3305d7e501248913bb8d59e2cc744efe8 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Mon, 11 May 2026 11:28:46 +0100 Subject: [PATCH 47/67] Update "new Component" docs to include info about Studio --- source/developers/buildercomponent.rst | 316 +++++++++++++++----- source/developers/slider-params-example.png | Bin 0 -> 34573 bytes 2 files changed, 242 insertions(+), 74 deletions(-) create mode 100644 source/developers/slider-params-example.png diff --git a/source/developers/buildercomponent.rst b/source/developers/buildercomponent.rst index 160837b..a89d96a 100644 --- a/source/developers/buildercomponent.rst +++ b/source/developers/buildercomponent.rst @@ -3,113 +3,281 @@ Adding a new Builder Component ===================================== -Builder Components are auto-detected and displayed to the experimenter as icons (in the right-most panel of the Builder interface panel). This makes it straightforward to add new ones. +Builder Components are auto-detected and displayed to the experimenter as icons (in the right-most panel of the Builder interface panel), so adding new ones is fairly straightforward. You can add Components directly to PsychoPy (see :ref:`addFeature`, if this doesn't mean anything to you then see :ref:`usingRepos`) or create a plugin which adds them via an entry point. -All you need to do is create a list of parameters that the Component needs to know about (that will automatically appear in the Component's dialog) and a few pieces of code specifying what code should be called at different points in the script (e.g. beginning of the Routine, every frame, end of the study etc...). Many of these will come simply from subclassing the _base or _visual Components. - -To get started, :ref:`addFeature` for the development of this component. (If this doesn't mean anything to you then see :ref:`usingRepos` ) - -You'll mainly be working in the directory `.../psychopy/experiment/components/`. -Take a look at several existing Components (such as `image.py`), and key files including `_base.py` and `_visual.py`. - -There are three main steps, the first being by far the most involved. - -1. Create the file defining the component: newcomp.py +Creating a Component class --------------------------------------------------------- -It's most straightforward to model a new Component on one of the existing ones. Be prepared to specify what your Component needs to do at several different points in time: the first trial, every frame, at the end of each routine, and at the end of the experiment. In addition, you may need to sacrifice some complexity in order to keep things streamlined enough for a Builder (see e.g., `ratingscale.py`). - -Your new Component class (in your file `newcomp.py`) should inherit from `BaseComponent` (in `_base.py`), `VisualComponent` (in `_visual.py`), or `KeyboardComponent` (in `keyboard.py`). You may need to rewrite some or all some of these methods, to override default behavior:: +Components in PsychoPy are Python classes, so creating a Component is just a case of creating the right kind of class. In order to be recognised as a Component, your class needs to be a subclass of BaseComponent. You can do this via BaseComponent directly:: - class NewcompComponent(BaseComponent): # or (VisualComponent) - def __init__(...): - super(NewcompComponent, self).__init__(...) - ... - def writeInitCode(self, buff): - def writeRoutineStartCode(self, buff): - def writeFrameCode(self, buff): - def writeRoutineEndCode(self, buff): + from psychopy.experiment.components import BaseComponent -Calling `super()` will create the basic default set of `params` that almost every component will need: `name`, `startVal`, `startType`, etc. Some of these fields may need to be overridden (e.g., `durationEstim` in `sound.py`). Inheriting from `VisualComponent` (which in turn inherits from `BaseComponent`) adds default visual params, plus arranges for Builder scripts to import `psychopy.visual`. If your component will need other libs, call `self.exp.requirePsychopyLib(['neededLib'])` (see e.g., `parallelPort.py`). + class MyNewComponent(BaseComponent): + pass -At the top of a component file is a dict named `_localized`. It contains mappings that allow a strict separation of internal string values (= used in logic, never displayed) from values used for display in the Builder interface (= for display only, possibly translated, never used in logic). The `.hint` and `.label` fields of `params['someParam']` should always be set to a localized value, either by using a dict entry such as `_localized['message']`, or via the globally available translation function, `_('message')`. Localized values must **not** be used elsewhere in a component definition. +Or via another class which itself is a subclass of BaseComponent (such as BaseVisualComponent or BaseDeviceComponent):: -Very occasionally, you may also need to edit `settings.py`, which writes out the set-up code for the whole experiment (e.g., to define the window). For example, this was necessary for the ApertureComponent, to pass `allowStencil=True` to the window creation. + from psychopy.experiment.components import BaseVisualComponent, BaseDeviceComponent -Your new Component writes code into a buffer that becomes an executable python file, `xxx_lastrun.py` (where `xxx` is whatever the experimenter specifies when saving from the Builder, `xxx.psyexp`). You will do a bunch of this kind of call in your `newcomp.py` file:: + class MyNewDeviceComponent(BaseDeviceComponent): + pass + + class MyNewVisualComponent(BaseVisualComponent): + pass - buff.writeIndented(your_python_syntax_string_here) +In order to be detected by the |PsychoPy| Studio/Standalone app, your Component class needs to either: -You have to manage the indentation level of the output code, see `experiment.IndentingBuffer()`. +* Be defined in a .py file in its own folder in the the folder `psychopy/experiment/components` within the PsychoPy library (if you're contributing directly to PsychoPy) +* Be connected to the module ``psychopy.experiment.components`` by an `entry point <_entryPoints>`_ (if you're writing a plugin) -`xxx_lastrun.py` is the file that gets built when you run `xxx.psyexp` from the Builder. So you will want to look at `xxx_lastrun.py` frequently when developing your component. +Both apps use the same function in the |PsychoPy| library to get a list of all Components, so you can test whether your Component will be detected without having to actually start either app by running the following:: -**Name-space** + from psychopy.experiment import getAllComponents -There are several internal variables (i.e. names of Python objects) that have a specific, hardcoded meaning within `xxx_lastrun.py`. You can expect the -following to be there, and they should only be used in the original way (or something will break for the end-user, likely in a mysterious way):: + print( + getAllComponents() + ) - win # the window - t # time within the trial loop, referenced to `trialClock` - x, y # mouse coordinates, but only if the experimenter uses a mouse component +If your Component is configured correctly, you should see its name in the output. -Handling of variable names is under active development, so this list may well be out of date. (If so, you might consider updating it or posting a note to the |PsychoPy| Discourse developer forum.) -Preliminary testing suggests that there are 600-ish names from numpy or numpy.random, plus the following:: - - ['KeyResponse', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'buttons', 'core', 'data', 'dlg', 'event', 'expInfo', 'expName', 'filename', 'gui', 'logFile', 'os', 'psychopy', 'sound', 't', 'visual', 'win', 'x', 'y'] +Adding parameters +--------------------------------------------------------- -Yet other names get derived from user-entered names, like `trials` --> `thisTrial`. +The controls that you see in |PsychoPy| Studio/Standalone are created based on the value of the ``.params`` attribute of your Component. + +.. image:: slider-params-example.png + +This attribute should be a ``dict``, with each key being a ``Param`` object. These objects can be created with the following values: + +* *val*: A default value for this param +* *valType*: This tells |PsychoPy| how to write the value of this param when compiling the experiment code. Note that, if the param's value begins with a $, it will always be treated as code regardless of valType. Options are: + + * ``str``: A string, will be compiled with " around it + * ``extendedStr``: A long string, will be compiled with " around it and linebreaks will be preserved + * ``code``: Some code, will be compiled verbatim or translated to JS (no ") + * ``extendedCode``: A block of code, will be compiled verbatim or translated to JS and linebreaks will be preserved + * ``file``: A file path, will be compiled like str but will replace unescaped \\ with / + * ``list``: A list of values, will be compiled like code but if there's no [] or () then these are added + +* *inputType*: This tells |PsychoPy| how to represent the parameter in the Builder; what kind of control to show for it. Options are: + + * ``single``: A single-line text control + * ``multi``: A multi-line text control + * ``color``: A single-line text control with a button to open the color picker + * ``survey``: A single-line text control with a button to open Pavlovia surveys list + * ``file``: A single-line text control with a button to open a file browser + * ``fileList``: Several file controls with buttons to add/remove + * ``table``: A file control with an additional button to open in Excel + * ``choice``: A single-choice control (dropdown) whose choices are listed in ``allowedVals`` and ``allowedLabels`` + * ``device``: A single-choice control (dropdown) whose choices are the devices from DeviceManager which match the classes listed in ``allowedVals`` + * ``multiChoice``: A multi-choice control (tickboxes) + * ``richChoice``: A single-choice control (dropdown) with rich text for each option + * ``bool``: A single checkbox control + * ``dict``: Several key:value pair controls with buttons to add/remove fields + +* *allowedVals*: For params with a fixed set of options, this tells |PsychoPy| what the values of those options are. These *should not* be translated, as they are the actual values used in code. +* *allowedLabels*: A list of labels corresponding to the values in ``allowedVals`` which tells |PsychoPy| what to display each option as. These can (and should) be translated, as they are not used in code. +* *categ*: Which tab this param should appear in, leave as ``None`` to have the param appear at the top of the Component dialog (use this sparingly; it is best saved for core functionality like the Component name and disabled control) +* *label*: What to label this param as. Keep in mind, the param will still be referred to in code by its key, this is just for the graphical interface of Builder. +* *hint*: The hint shown when this param's label is hovered over. +* *updates*: When the value of this param should be set, should be one of: + + * *constant*: Value is set just the once + * *set every repeat*: Value is set at the start of each Routine + * *set every frame*: Value is set each frame + +* *allowedUpdates*: Which values for ``updates`` the user is allowed to choose (leave as None to hide the control) + +So, for example, the parameter in the Keyboard Component which determines whether keypresses should be registered on press or on release looks like this:: + + self.params['registerOn'] = Param( + # starts off as "press" + "press", + # written to code as a string + valType="str", + # shown in Builder as a dropdown menu + inputType="choice", + # has two options... + allowedVals=["press", "release"], + # ...which should be labelled as follows + allowedLabels=[_translate("Press"), _translate("Release")], + # shown in the "Basic" tab + categ="Basic", + # the label shown in Builder + label=_translate("Register keypress on..."), + # the tooltip when hovered over + hint=_translate( + "When should the keypress be registered? As soon as pressed, or when released?" + ), + # set just the once, when the Keyboard is created + updates="constant", + # there is no option to set each repeat/frame + allowedUpdates=None + ) + +Many params will already exist by virtue of subclassing. For example, the ``name`` param is always present as it's added by ``BaseComponent``, and the ``pos`` and ``size`` params are always present on subclasses of ``BaseVisualComponent`` for the same reason. + +Translating labels +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PsychoPy has a team of volunteer translators who provide translations for all labels in the PsychoPy apps. To use an existing translation (or make sure your label is picked up by the code which tells the team what to translate), you need to import the ``_translate`` function from ``psychopy.localization`` and wrap any string in the following:: + + _translate("My label") + +This should be done on labels and tooltips which are *presented to the user*, never on values which are *used in code*, as translating a string could break code further down the line (e.g. if you had code which said ``if fruit == "banana":`` and ``fruit`` was translated into another language, this statement would always be ``False``). Also, as strings are submitted for translation before interpretation, any string formatting needs to be done after ``_translate``, so for example:: + + # CORRECT + _translate("The value is {}").format(someValue) + # INCORRECT + _translate("The value is {}".format(someValue)) + +As in the former example, translaters can translate ``"The value is "`` and leave ``{}`` as is. Whereas in the latter, the string to translate would depend on the value of ``someValue``, so the translation would only cover the default value. + +Writing code +--------------------------------------------------------- -**Params** +What a Component is, at its core, is something which *takes parameter values* and returns *Python or JavaScript code*. It does this by defining a set of functions which write code at certain points in an experiment, using the values of its parameters to construct `Python formatted strings `_. Some of this code is already defined by the base class you're subclassing (e.g. if you're creating a subclass of BaseVisualComponent, then it will already know how to draw itself each frame according to start/stop parameters), but generally you will want to overwrite some or all of these to get the desired behaviour. + +The functions which write a Component's code are roughly equivalent to the tabs of a :ref:`_code`: + +* ``writePreCode/writePreCodeJS``: Written before the experiment starts (Before Experiment) +* ``writeInitCode/writeInitCodeJS``: Written at the start of the experiment (Begin Experiment) +* ``writeRoutineStartCode/writeRoutineStartCodeJS``: Written at the start of this Component's Routine (Begin Routine) +* ``writeFrameCode/writeFrameCodeJS``: Written each frame of this Component's Routine (Each Frame) +* ``writeRoutineEndCode/writeRoutineEndCodeJS``: Written at the end of this Component's Routine (End Routine) +* ``writeExperimentEndCode/writeExperimentEndCodeJS``: Written at the end of the experiment (End Experiment) + +All of these functions take an input argument ``buff``, which is the text buffer to which the current experiment is being written. You can therefore write your code to this buffer by calling:: + + buff.writeIndentedLines(code) + +You will also need to use this buffer to manage indenting:: + + buff.setIndentLevel(, relative=) + +The ``BaseComponent`` class also offers some helper functions which you can use to make your code writing a bit easier: + +* ``writeStartTestCode(self, buff, extra="")/writeStartTestCodeJS(self, buff, extra="")``: Writes code to check whether the Component needs to start, based on its start params, leaving the relevant ``if`` statements open. The returned value is how many ``if`` statements were left open, so after writing whatever you would like to happen on the starting frame, you will need to call ``buff.setIndentLevel(-)`` (and write some ``}``'s in JS) to return to normal after. Use the optional argument ``extra`` to add additional conditions to check for (e.g. ``and someLimitingFactor == True``). +* ``writeStopTestCode(self, buff, extra="")/writeStopTestCodeJS(self, buff, extra="")``: Same as ``writeStartTestCode`` but rather than checking whether the Component is ready to start, checks whether it is ready to stop. +* ``writeActiveTestCode(self, buff, extra="")/writeActiveTestCodeJS(self, buff, extra="")``: Same as ``writeStartTestCode`` and ``writeStopTestCode`` but rather than checking whether the Component is ready to start or stop, checks whether it has started and is yet to stop. +* ``writeParamUpdates(self, buff, updateType)/writeParamUpdatesJS(self, buff, updateType)``: Writes the necessary updates to params if their update type matches what is given. This is already called by ``writeActiveTestCode`` and in the default behaviour of ``writeRoutineStartCode``. +* ``getPosInRoutine(self)``: Gets the position of this Component in its Routine; useful for choosing what value to give for ``depth`` with visual Components. + +Using parameters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Param`` class has some clever functions for handling turning itself into a string (``__str__``), which means you don't have to worry about getting and processing the value of the parameter yourself. Simply pass it to a Python ``format`` function, and it will write itself correctly according to its ``valType`` attribute. Examples of how you might do this:: + + # using %s formatting + code = ( + "%(name)s = visual.SomeStim(\n" + " name='%(name)s',\n" + " pos=%(pos)s,\n" + ");" + ) % self.params + # using .format + code = ( + "{name} = visual.SomeStim(\n" + " name='{name}',\n" + " pos={pos},\n" + ");" + ).format(**self.params) + # using f strings + code = ( + f"{self.params['name']} = visual.SomeStim(\n" + f" name='{self.params['name']}',\n" + f" pos={self.params['pos']},\n" + f");" + ) + +It is worth noting that in ``writeInitCode``, parameters may need to have different values, as if a parameter's value is ``set each frame`` or ``set each repeat`` then it will need to be initialised as a safe default. To get these defaults, you can use the function ``psychopy.experiment.components:getInitVals``, which will return a dict which you can use in place of ``self.params``. + +Name space +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are several internal variables (i.e. names of Python objects) that have a specific, hardcoded meaning within `xxx_lastrun.py`. You can expect the following to be there, and they should only be used in the original way (or something will break for the end-user, likely in a mysterious way) + +=================== ============== +Name Use +=================== ============== +win The window +t Time (s) since the Routine began +continueRoutine Boolean which will end the Routine on next frame flip if set to False +logging PsychoPy's ``logging`` module +thisExperiment ``psychopy.data.experiment:ExperimentHandler`` object representing the current experiment +currentLoop Trial handler object representing the current loop, if any +=================== ============== + +Other names will also get derived from user-entered names, like `trials` (name of a loop) --> `thisTrial`.Handling of variable names is under active development, so this list may well be out of date. (If so, you might consider updating it or posting a note to the |PsychoPy| Discourse developer forum.) + +Creating an icon +--------------------------------------------------------- -`self.params` is a key construct that you build up in `__init__`. You need name, startTime, duration, and several other params to be defined or you get errors. `'name'` should be of type `'code'`. +The best way to make an icon which will work in both PsychoPy Studio and PsychoPy Standalone is to use a vector editor like `Affinity `_, `Inkscape `_ or `Adobe Illustrator `_ which allows you to export your icon to whatever file format is required. While the appearance of your Component icon is totally up to you, we recommend using thick grey lines with curved corners and limiting colors to just red and blue. -The `Param()` class is defined in `psychopy.app.builder.experiment.Param()`. A very useful thing that Params know is how to create a string suitable for writing into the .py script. In particular, the `__str__` representation of a Param will format its value (`.val`) based on its type (`.valType`) appropriately. This means that you don't need to check or handle whether the user entered a plain string, a string with a code trigger character (`$`), or the field was of type `code` in the first place. If you simply request the `str()` representation of the param, it is formatted correctly. +For PsychoPy Studio +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To indicate that a param (eg, `thisParam`) should be considered as an advanced feature, set its category to advanced: `self.params['thisParam'].categ = 'Advanced'`. Then the GUI shown to the experimenter will automatically place it on the 'Advanced' tab. Other categories work similarly (`Custom`, etc). +As PsychoPy Studio uses modern web-based technologies, icons for PsychoPy Studio are .svg files. This makes them small, fast to load, infinitely scalable and theme responsive. The app interface of PsychoPy Studio is Chromium-based, so if you can open your icon in Google Chrome (or other Chromium-based browsers) then it should look the same in PsychoPy Studio. -During development, it can sometimes be helpful to save the params into the `xxx_lastrun.py` file as comments, so you can see what is happening:: +Because .svg is a text-based format, you can make your icon theme-responsive by opening the exported .svg file in a text editor and replacing the color values (e.g. ``#F2545B``) with named CSS variables (e.g. ``var(--red)``). See below for the color names used by PsychoPy Studio and what color they correspond to in PsychoPy Light and PsychoPy Dark themes. - def writeInitCode(self,buff): - # for debugging during Component development: - buff.writeIndented("# self.params for aperture:\n") - for p in self.params: - try: buff.writeIndented("# %s: %s \n" % (p, self.params[p].val, self.params[p].valType)) - except: pass +=============== =============== =============== +Name PsychoPy Light PsychoPy Dark +=============== =============== =============== +--red #F2545B #F2545B +--purple #C3BEF7 #C3BEF7 +--blue #02A9EA #02A9EA +--green #6CCC74 #6CCC74 +--yellow #F1D302 #F1D302 +--orange #EC9703 #EC9703 +--base #FFFFFF #75757d +--mantle #F2F2F2 #75757d +--crust #E4E4E4 #57575f +--overlay #D6D6D6 #484850 +--outline #66666E #ACACB0 +--text #242427 #FFFFFF +--hltext #FFFFFF #242427 +=============== =============== =============== -A lot more detail can be inferred from existing components. +In general, PsychoPy icons stick to just ``--red``, ``--blue`` and ``--outline``, so your icon will best fit in with existing Components if you limit your palette to these three colors. -Making things loop-compatible looks interesting -- see `keyboard.py` for an example, especially code for saving data at the end. +Once you have a .svg file ready to add, you can assign it to your Component by setting the class attribute ``iconSVG`` of your Component class to be the file's path. The easiest way to do this is to put it in the same folder as your Component and set the attribute to:: -Notes & gotchas ----------------- + pathlib.Path(__file__).parent / '.svg' - *syntax errors in new_comp.py:* - The |PsychoPy| app will fail to start if there are syntax error in any of the components that are auto-detected. Just correct them and start the app again. +For PsychoPy Standalone +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - *param[].val:* - If you have a boolean variable (e.g., `my_flag`) as one of your params, note that `self.param["my_flag"]` is always True (the param exists --> True). So in a boolean context you almost always want the `.val` part, e.g., `if self.param["my_flag"].val:`. +As PsychoPy Standalone was written in wxPython, adding icons for this app means exporting a .png file for each of the three app themes: Light, dark and classic. These should all be 48x48px, though in order for your Component to look correct on an Apple Retina screen you should also export it as 96x96px with ``@2x`` at the end of the file stem (just before ``.png``). Many image editors will include this as an export preset. - However, you do not always want `.val`. Specifically, in a string/unicode context (= to trigger the self-formatting features of Param()s), you almost always want `"%s" % self.param['my_flag']`, without `.val`. Note that it's better to do this via `"%s"` than `str()` because `str(self.param["my_flag"])` coerces things to type str (squashing unicode) whereas `%s` works for both str and unicode. +Light and dark icons should be broadly the same, only with a different shade of grey for outlines so as to be more visible on light/dark background. PsychoPy icons generally stick to the following colors for each theme: - *Travis testing* - Before submitting a pull request with the new component, you should regenerate the `componsTemplate.txt` file. This is a text file that lists the attributes of all of the user interface settings and options in the various components. It is used during the Travis automated testing process when a pull request is submitted to GitHub, allowing the detection of errors that may have been caused in refactoring. Your new component needs to have entries added to this file if the Travis testing is going to pass successfully. - - To re-generate the file, cd to this directory `.../psychopy/tests/test_app/test_builder/` and run:: +=============== =============== +PsychoPy Light PsychoPy Dark +=============== =============== +#F2545B #F2545B +#02A9EA #02A9EA +#66666E #ACACB0 +=============== =============== - `python genComponsTemplate.py --out` +The classic theme exists for compatability with PsychoPy's old (pre-2020) look, so icons for that theme should be drawn from the `Crystal icon pack `_. If none meet your needs, you can create a copy of your dark mode icon, as this will be visible on either background. - This will over-write the existing file so you might want to make a copy in case the process fails. *Compatibility issues:* As at May 2018, that script is not yet Python 3 compatible, and on a Mac you might need to use `pythonw`. +Icons should be sorted into folders (called ``light``, ``dark`` and ``classic``) by theme. To connect them to your Component, set the ``iconFile`` attribute of the Component class to be the file path of the icon *without the theme subfolder or @2x*. For example, if your file structure looked like this:: -2. Icon: newcomp.png ------------------------- -Using your favorite image software, make an icon for your Component with a descriptive name, e.g., `newcomp.png`. Dimensions = 48 × 48. Put it in the components directory. + -> myComponent + -> light + -> myComponentIcon.png + -> myComponentIcon@2x.png + -> dark + -> myComponentIcon.png + -> myComponentIcon@2x.png + -> classic + -> myComponentIcon.png + -> myComponentIcon@2x.png -In `newcomp.py`, have a line near the top:: +Then you would set ``icon`` as:: - iconFile = path.join(thisFolder, 'newcomp.png') + Path(__file__).parent / 'myComponentIcon.png' -3. Documentation: newcomp.rst ---------------------------------- -Just make a descriptively-named text file that ends in `.rst` ("restructured text"), and put it in `psychopy/docs/source/builder/components/` . It will get auto-formatted and end up at `https://www.psychopy.org/builder/components/newcomp.html` diff --git a/source/developers/slider-params-example.png b/source/developers/slider-params-example.png new file mode 100644 index 0000000000000000000000000000000000000000..35c147d59ef1139ab7983c47ed5a5a19f074ade1 GIT binary patch literal 34573 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 L>|mVKiEIA@FD#ND literal 0 HcmV?d00001 From 8ce01f848fb6e08bef8ba90be6d50432e9a66aa4 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Tue, 19 May 2026 14:22:05 +0100 Subject: [PATCH 48/67] Update pricing in consultancy.rst --- source/consultancy.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/consultancy.rst b/source/consultancy.rst index 8d98f4d..c9fd2d2 100644 --- a/source/consultancy.rst +++ b/source/consultancy.rst @@ -237,9 +237,9 @@ If you would like to purchase a package for support from our team for your depar Free - £1800 £2000 - £5000 + £2200 + £5200
From 17affa553f0b181fd930c5886494f94feed00c0e Mon Sep 17 00:00:00 2001 From: Jon Peirce Date: Fri, 22 May 2026 15:30:30 +0100 Subject: [PATCH 49/67] Attempting to add sitemap.xml --- pyproject.toml | 1 + source/conf.py | 4 +++- source/robots.txt | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 source/robots.txt diff --git a/pyproject.toml b/pyproject.toml index 46f969f..9c77280 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ "myst-parser", "sphinxcontrib.svg2pdfconverter", # requires inkscape "psychopy-sphinx-theme", + "sphinx-sitemap", ] [project.optional-dependencies] # This is optional dependencies diff --git a/source/conf.py b/source/conf.py index d9c0a2b..fc4ac81 100755 --- a/source/conf.py +++ b/source/conf.py @@ -45,6 +45,7 @@ 'sphinx_copybutton', 'myst_parser', 'redirect', + 'sphinx_sitemap', 'sphinxcontrib.inkscapeconverter' # part of sphinxcontrib-svg2pdfconverter ] @@ -174,6 +175,7 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +html_extra_path = ['robots.txt'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. @@ -212,7 +214,7 @@ # Output file base name for HTML help builder. htmlhelp_basename = 'PsychoPydoc' - +html_baseurl = 'https://psychopy.org/' # -- Options for LaTeX output -------------------------------------------------- latex_engine = 'pdflatex' # xelatex or pdflatex diff --git a/source/robots.txt b/source/robots.txt new file mode 100644 index 0000000..b2d65ba --- /dev/null +++ b/source/robots.txt @@ -0,0 +1,3 @@ +User-agent: * + +Sitemap: https://psychopy.org/sitemap.xml \ No newline at end of file From 472c9207b87e506b1c7bed78c5e6a2f3d2ff344d Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Fri, 22 May 2026 15:55:12 +0100 Subject: [PATCH 50/67] Change homepage title for better searchability --- source/conf.py | 4 ++-- source/index.rst | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/conf.py b/source/conf.py index fc4ac81..7c20e21 100755 --- a/source/conf.py +++ b/source/conf.py @@ -157,10 +157,10 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = "%s v%s" %(project, release) +html_title = "PsychoPy®" # A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = "PsychoPy" +html_short_title = "PsychoPy®" # The name of an image file (relative to this directory) to place at the top # of the sidebar. diff --git a/source/index.rst b/source/index.rst index 1ee79b5..ee2501a 100644 --- a/source/index.rst +++ b/source/index.rst @@ -1,6 +1,5 @@ :orphan: :header_template: homepage -:title: Home .. grid:: 1 1 1 1 @@ -41,7 +40,7 @@ Lots of helpful tutorials on youtube! Click here to see -Home +Build and run experiments for free ========================================= What users say From 6d63496c7b1e05e327e57204da23f60042c21abd Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Fri, 22 May 2026 18:23:16 +0100 Subject: [PATCH 51/67] Add a pricing page (content TBD) --- source/pricing.rst | 158 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 source/pricing.rst diff --git a/source/pricing.rst b/source/pricing.rst new file mode 100644 index 0000000..1a869ce --- /dev/null +++ b/source/pricing.rst @@ -0,0 +1,158 @@ + +Pricing +==================================================================== + +|PsychoPy| *is and will always be free*, but we do offer paid-for services which allow us to keep doing what we're doing. + +Pavlovia +------------------------------------------------ + +Pavlovia is a secure server for hosting, sharing and running experiments online. It includes a `feature rich survey creator `_, an open access `library of experiment demos `_, and there are easy tools built into the |PsychoPy| app for syncing experiments. + +Unsure? You can `book a free one-hour demo `_. + +.. grid:: 1 1 3 3 + + .. grid-item-card:: Licence-only + :text-align: center + + **£2000 annually** + + :octicon:`check` Open access `library of experiment demos `_ + + :octicon:`check` Build and upload experiments with |PsychoPy| + + :octicon:`check` Community support via `our forum `_ + + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + + .. grid-item-card:: Licence + workshops + :text-align: center + :class-footer: recommended + + **£2200 annually** + + :octicon:`check` Open access `library of experiment demos `_ + + :octicon:`check` Build and upload experiments with |PsychoPy| + + :octicon:`check` Community support via `our forum `_ + + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + + :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` + + :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` + + +++ + **Recommended** + + + .. grid-item-card:: Licence + workshops + clinic + :text-align: center + + **£5200 annually** + + :octicon:`check` Open access `library of experiment demos `_ + + :octicon:`check` Build and upload experiments with |PsychoPy| + + :octicon:`check` Community support via `our forum `_ + + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + + :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` + + :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` + + :octicon:`check` 40 weeks of 1-hour 'PsychoPy Clinic' sessions :abbr:`i (Clinic hours will be bookable with our team on a first come first served basis. We set aside hours agreed with your Licence Manager \(e.g. every Wednesday 9 - 10am from dd/mm to dd/mm\). Hourly usage per account user will be monitored and reported to the licence manager. We reserve the right to not guarantee a solution to all issues raised within clinic sessions \(but we will always try our best to resolve most issues!\))` + +.. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center + + Request a licence + +`How does this compare to other similar services? `_ + +Consultancy +------------------------------------------------ + +Short on time but have spare funds? We can help! + +We've helped many researchers to create and debug experiments, as well as delivered bespoke tutorials. + +.. tab-set:: + + .. tab-item:: For universities/charities... + + .. grid:: 1 1 2 2 + + .. grid-item-card:: Support + + **£70 per hour** + + Our team of Scientists can help to: + + * Create a task given a brief. + + * Debug existing tasks. + + * Create one-to-one tutorials and training. + + * Organise departmental workshops and training. + + .. grid-item-card:: Senior Staff Support + + **£140 per hour** + + Sometimes your project may need time from a developer or senior staff member, these rates would apply in cases where we are: + + * Adding a new feature to PsychoPy + + * Working with unsupported or bespoke hardware + + * Creating new software + + .. tab-item:: For commercial/businesses... + + .. grid:: 1 1 2 2 + + .. grid-item-card:: Support + + **£105 per hour** + + Our team of Scientists can help to: + + * Create a task given a brief. + + * Debug existing tasks. + + * Create one-to-one tutorials and training. + + * Organise departmental workshops and training. + + .. grid-item-card:: Senior Staff Support + + **£210 per hour** + + Sometimes your project may need time from a developer or senior staff member, these rates would apply in cases where we are: + + * Adding a new feature to PsychoPy + + * Working with unsupported or bespoke hardware + + * Creating new software + + +.. button-link:: https://forms.clickup.com/4570406/f/4bf96-7552/ZN8URSTDTWDENY6RP9 + :color: primary + :align: center + + Click here to make a request for support! \ No newline at end of file From 803b02f67b51b9f36b3ac86e3f2e7a504cff4664 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Mon, 1 Jun 2026 17:06:03 +0100 Subject: [PATCH 52/67] Add "Request a license" buttons to each plan card --- source/pricing.rst | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/source/pricing.rst b/source/pricing.rst index 1a869ce..a9c4380 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -27,6 +27,12 @@ Unsure? You can `book a free one-hour demo `_ From e1b45e8cb163be763fa2fd8634eb5187a76d7422 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Mon, 1 Jun 2026 17:49:27 +0100 Subject: [PATCH 53/67] Add section about PsychoPy being free --- source/pricing.rst | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/pricing.rst b/source/pricing.rst index a9c4380..c3bff46 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -2,7 +2,22 @@ Pricing ==================================================================== -|PsychoPy| *is and will always be free*, but we do offer paid-for services which allow us to keep doing what we're doing. +PsychoPy +------------------------------------------------ +.. grid:: 1 1 2 2 + + .. grid-item-card:: |PsychoPy| is and will always be free! + :text-align: center + + This means you can: + + :octicon:`check` Build and run experiments on your local machine via `PsychoPy Studio `_ + + :octicon:`check` Program experiments in Python using the `PsychoPy library `_ + + :octicon:`check` Access community support via `our forum `_ + +`Thousands of researchers per month `_ are already using |PsychoPy| to build and run local experiments for free! Pavlovia ------------------------------------------------ From da43fec0c63744846fdbfdaa4154f1febae9870b Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Mon, 1 Jun 2026 17:59:25 +0100 Subject: [PATCH 54/67] Add consultancy client quotes --- source/pricing.rst | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/source/pricing.rst b/source/pricing.rst index c3bff46..8071e5e 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -182,4 +182,30 @@ We've helped many researchers to create and debug experiments, as well as delive :color: primary :align: center - Click here to make a request for support! \ No newline at end of file + Click here to make a request for support! + +What our consultancy clients say +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. card-carousel:: 2 + + .. card:: + + “Becca was absolutely fantastic. She explained concepts very clearly, and was really patient when I had questions. I'll definitely be working with her again in the future!” + + +++ + **Anonymous** + + .. card:: + + “It has been very useful to have the creators of the product assist with task development. Dr. Hirst has been very knowledgeable, thorough, and a delight to interact with. Dr. Peirce has been very helpful when he needed to step in with advice.” + + +++ + **Anonymous** + + .. card:: + + “The team at PsychoPy are incredibly helpful and are very skilled in what they do. No matter the type of experiment you are trying to programme, their consultancy team can handle it (whilst teaching you along the way!) - I am particularly grateful to Becca Hirst for her amazing support with my experiment.” + + +++ + **Client from Liverpool John Moores University, UK** \ No newline at end of file From eef54ed3663b7c2511d7490a5eb206c7dc2b0815 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Mon, 1 Jun 2026 17:59:34 +0100 Subject: [PATCH 55/67] Restyle front page quotes --- source/index.rst | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/source/index.rst b/source/index.rst index 1ee79b5..0c296d3 100644 --- a/source/index.rst +++ b/source/index.rst @@ -49,26 +49,41 @@ What users say .. card-carousel:: 2 - .. card:: **Davide Massida** via twitter + .. card:: "PsychoPy is one of those things that improve the life of an experimental psychologist. Really. #python #neuroscience" - .. card:: **Harriet Allen** Professor, University of Nottingham + +++ + **Davide Massida** via twitter + + .. card:: "What made me switch was the combination of Builder and Coder options. Students are not scared of the Builder, but I can still write code when needed (and even hide it in student experiments)." - .. card:: **Nate Vack** Research Programmer, UW-Madison + +++ + **Harriet Allen** Professor, University of Nottingham + + .. card:: "It's wonderful to have a product that makes stimulus presentation easy (and is free!)..." - .. card:: **Fenja Ziegler** Senior Lecturer, University of Lincoln + +++ + **Nate Vack** Research Programmer, UW-Madison + + .. card:: "...easy to customise with a bit of scripting, a helpful user community, can be explained to a student in an hour or so..." - .. card:: **Micah Allen** Post-doc, UCL + +++ + **Fenja Ziegler** Senior Lecturer, University of Lincoln + + .. card:: "I love psychopy! I used it for one previous fMRI experiment and found the language extremely easy to use..." + +++ + **Micah Allen** Post-doc, UCL + How is PsychoPy Free? ========================================= From 68658696c9c6bb82e82593d1eed5892558de4d81 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Fri, 5 Jun 2026 13:34:48 +0100 Subject: [PATCH 56/67] SYS: Allow editable install (installs utils) --- pyproject.toml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9c77280..db44c5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Topic :: Scientific/Engineering", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", - ] +] dependencies = [ "psychopy", # to inspect the API @@ -100,4 +100,12 @@ getter = "psychopy.__init__:getVersion" universal = 1 [tool.psychopy-docs] -src = ["psychopy-docs"] \ No newline at end of file +src = ["psychopy-docs"] + +[tool.setuptools] +packages = [ + "psychopy_docs" +] + +[tool.setuptools.package-dir] +psychopy_docs = "utils" \ No newline at end of file From ed38c4b436d705f46f2faf6e34ad9d555ed6c2a0 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Fri, 5 Jun 2026 13:35:02 +0100 Subject: [PATCH 57/67] FF: Fix Component docs generator --- utils/components.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/utils/components.py b/utils/components.py index 6c9b9e2..10911f0 100644 --- a/utils/components.py +++ b/utils/components.py @@ -79,14 +79,15 @@ def createFromTemplate(cls): categs = OrderedDict() for ref, param in comp.params.items(): # make sure categ exists - if param.categ not in params: - params[param.categ] = [] - # give categ a description if available - categs[param.categ] = "" - for srcFile in (__folder__ / "categHints").glob(param.categ + ".rst"): - categs[param.categ] = srcFile.read_text(encoding="utf-8") - # sort by category - params[param.categ].append(param) + if param.categ: + if param.categ not in params: + params[param.categ] = [] + # give categ a description if available + categs[param.categ] = "" + for srcFile in (__folder__ / "categHints").glob(param.categ + ".rst"): + categs[param.categ] = srcFile.read_text(encoding="utf-8") + # sort by category + params[param.categ].append(param) # store ref param.ref = ref # make sure allowedLabels/Vals is a list of strings From 17a2a77d24290cbe9fb6b6338017dbdb67a86012 Mon Sep 17 00:00:00 2001 From: Todd Parsons Date: Fri, 5 Jun 2026 13:35:15 +0100 Subject: [PATCH 58/67] Add Eyetracker Marker Component docs --- .../components/EyetrackerMarkerComponent.rst | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 source/builder/components/EyetrackerMarkerComponent.rst diff --git a/source/builder/components/EyetrackerMarkerComponent.rst b/source/builder/components/EyetrackerMarkerComponent.rst new file mode 100644 index 0000000..abfd269 --- /dev/null +++ b/source/builder/components/EyetrackerMarkerComponent.rst @@ -0,0 +1,70 @@ +.. _eyetrackermarkercomponent: + +------------------------------- +Eyetracker Marker Component +------------------------------- + +Add a text marker in the hdf5 file + +Categories: + Eyetracking +Works in: + PsychoPy + +**Note: Since this is still in beta, keep an eye out for bug fixes.** + +Parameters +------------------------------- + +Basic +=============================== + +The required attributes of the stimulus, controlling its basic function and behaviour + + +.. _eyetrackermarkercomponent-startVal: +Send when... + When the Eyetracker Marker Component should start, see :ref:`startStop`. + +.. _eyetrackermarkercomponent-startEstim: +Expected start (s) + If you are using frames to control timing of your stimuli, you can add an expected start time to display the component timeline in the routine. + +.. _eyetrackermarkercomponent-startType: +Start type + How do you want to define your start point? + + Options: + + * time (s) + + * frame N + + * condition + +.. _eyetrackermarkercomponent-durationEstim: +Expected duration (s) + If you are using frames to control timing of your stimuli, you can add an expected duration to display the component timeline in the routine. + +.. _eyetrackermarkercomponent-message: +Text + Text to send to the eyetracker (128 characters max) + +.. _eyetrackermarkercomponent-category: +Category + Optional grouping text for the message (32 characters max) + +Data +=============================== + +What information about this Component should be saved? + + +.. _eyetrackermarkercomponent-saveStartStop: +Save onset/offset times + Store the onset/offset times in the data file (as well as in the log file). + +.. _eyetrackermarkercomponent-syncScreenRefresh: +Sync timing with screen refresh + Synchronize times with screen refresh (good for visual stimuli and responses based on them) + \ No newline at end of file From f6b947cd85d556dea472c17db8d50de86a6b93a7 Mon Sep 17 00:00:00 2001 From: TEParsons Date: Wed, 10 Jun 2026 16:13:48 +0100 Subject: [PATCH 59/67] Add tab groups for packages as well as consultancy --- source/pricing.rst | 187 +++++++++++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 50 deletions(-) diff --git a/source/pricing.rst b/source/pricing.rst index 8071e5e..8b5af87 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -26,85 +26,169 @@ Pavlovia is a secure server for hosting, sharing and running experiments online. Unsure? You can `book a free one-hour demo `_. -.. grid:: 1 1 3 3 - - .. grid-item-card:: Licence-only - :text-align: center +.. tab-set:: + :sync-group: customer - **£2000 annually** + .. tab-item:: For universities/charities... + :sync: university - :octicon:`check` Open access `library of experiment demos `_ + .. grid:: 1 1 3 3 + + .. grid-item-card:: Licence-only + :text-align: center - :octicon:`check` Build and upload experiments with |PsychoPy| + **£2,000 annually** :abbr:`i (We offer reduced rates for emerging economies)` - :octicon:`check` Community support via `our forum `_ + :octicon:`check` Open access `library of experiment demos `_ - :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + :octicon:`check` Build and upload experiments with |PsychoPy| - :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + :octicon:`check` Community support via `our forum `_ - .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI - :color: primary - :align: center + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` - Request a licence - - .. grid-item-card:: Licence + workshops - :text-align: center - :class-footer: recommended - - **£2200 annually** + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` - :octicon:`check` Open access `library of experiment demos `_ + .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center - :octicon:`check` Build and upload experiments with |PsychoPy| + Request a licence + + .. grid-item-card:: Licence + workshops + :text-align: center + :class-footer: recommended + + **£2,200 annually** :abbr:`i (We offer reduced rates for emerging economies)` - :octicon:`check` Community support via `our forum `_ + :octicon:`check` Open access `library of experiment demos `_ - :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + :octicon:`check` Build and upload experiments with |PsychoPy| - :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + :octicon:`check` Community support via `our forum `_ - :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` - :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` - .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI - :color: primary - :align: center + :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` - Request a licence + :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` - +++ - **Recommended** - - - .. grid-item-card:: Licence + workshops + clinic - :text-align: center + .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center + + Request a licence + + +++ + **Recommended** + + + .. grid-item-card:: Licence + workshops + clinic + :text-align: center + + **£5,200 annually** :abbr:`i (We offer reduced rates for emerging economies)` + + :octicon:`check` Open access `library of experiment demos `_ + + :octicon:`check` Build and upload experiments with |PsychoPy| + + :octicon:`check` Community support via `our forum `_ + + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + + :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` + + :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` + + :octicon:`check` 40 weeks of 1-hour 'PsychoPy Clinic' sessions :abbr:`i (Clinic hours will be bookable with our team on a first come first served basis. We set aside hours agreed with your Licence Manager \(e.g. every Wednesday 9 - 10am from dd/mm to dd/mm\). Hourly usage per account user will be monitored and reported to the licence manager. We reserve the right to not guarantee a solution to all issues raised within clinic sessions \(but we will always try our best to resolve most issues!\))` + + .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center + + Request a licence + + .. tab-item:: For commercial/businesses... + :sync: business + + .. grid:: 1 1 3 3 + + .. grid-item-card:: Licence-only + :text-align: center + + **£4,000 annually** :abbr:`i (We offer reduced rates for emerging economies)` + + :octicon:`check` Open access `library of experiment demos `_ + + :octicon:`check` Build and upload experiments with |PsychoPy| + + :octicon:`check` Community support via `our forum `_ + + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + + .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center + + Request a licence + + .. grid-item-card:: Licence + workshops + :text-align: center + + **£4,400 annually** :abbr:`i (We offer reduced rates for emerging economies)` + + :octicon:`check` Open access `library of experiment demos `_ + + :octicon:`check` Build and upload experiments with |PsychoPy| + + :octicon:`check` Community support via `our forum `_ + + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + + :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` + + :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` + + .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center + + Request a licence + + .. grid-item-card:: Licence + workshops + clinic + :text-align: center - **£5200 annually** + **£10,400 annually** :abbr:`i (We offer reduced rates for emerging economies)` - :octicon:`check` Open access `library of experiment demos `_ + :octicon:`check` Open access `library of experiment demos `_ - :octicon:`check` Build and upload experiments with |PsychoPy| + :octicon:`check` Build and upload experiments with |PsychoPy| - :octicon:`check` Community support via `our forum `_ + :octicon:`check` Community support via `our forum `_ - :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` + :octicon:`check` Unlimited accounts on pavlovia.org for your institute :abbr:`i (A pavlovia.org licence allows you to have an unlimited number of researchers)` - :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` + :octicon:`check` Unlimited number of experiments hosted on Pavlovia.org :abbr:`i (A pavlovia.org licence allows every researcher to host as many experiments as they like!)` - :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` + :octicon:`check` 3 x 1-hour virtual workshops :abbr:`i (Your Licence manager can schedule 3 x 1 hour virtual workshops, that will be hosted on Zoom. The workshops will introduce you to the basics of how to make an experiment in PsychoPy and how to launch your study on Pavlovia.org)` - :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` + :octicon:`check` Recording of workshops for departmental use :abbr:`i (A recording of the 3 x 1 hour workshops will be shared with your department to use as a teaching resource)` - :octicon:`check` 40 weeks of 1-hour 'PsychoPy Clinic' sessions :abbr:`i (Clinic hours will be bookable with our team on a first come first served basis. We set aside hours agreed with your Licence Manager \(e.g. every Wednesday 9 - 10am from dd/mm to dd/mm\). Hourly usage per account user will be monitored and reported to the licence manager. We reserve the right to not guarantee a solution to all issues raised within clinic sessions \(but we will always try our best to resolve most issues!\))` + :octicon:`check` 40 weeks of 1-hour 'PsychoPy Clinic' sessions :abbr:`i (Clinic hours will be bookable with our team on a first come first served basis. We set aside hours agreed with your Licence Manager \(e.g. every Wednesday 9 - 10am from dd/mm to dd/mm\). Hourly usage per account user will be monitored and reported to the licence manager. We reserve the right to not guarantee a solution to all issues raised within clinic sessions \(but we will always try our best to resolve most issues!\))` - .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI - :color: primary - :align: center + .. button-link:: https://forms.clickup.com/4570406/f/4bf96-7632/5QVSTYUIXN6Y9BY2OI + :color: primary + :align: center - Request a licence + Request a licence `How does this compare to other similar services? `_ @@ -116,8 +200,10 @@ Short on time but have spare funds? We can help! We've helped many researchers to create and debug experiments, as well as delivered bespoke tutorials. .. tab-set:: + :sync-group: customer .. tab-item:: For universities/charities... + :sync: university .. grid:: 1 1 2 2 @@ -148,6 +234,7 @@ We've helped many researchers to create and debug experiments, as well as delive * Creating new software .. tab-item:: For commercial/businesses... + :sync: business .. grid:: 1 1 2 2 From 866570a3125b1659c9008925c212607bd4bb53f0 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Thu, 11 Jun 2026 10:38:48 +0100 Subject: [PATCH 60/67] Update pricing for Support and Senior Staff services --- source/pricing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/pricing.rst b/source/pricing.rst index 8b5af87..404b259 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -240,7 +240,7 @@ We've helped many researchers to create and debug experiments, as well as delive .. grid-item-card:: Support - **£105 per hour** + **£140 per hour** Our team of Scientists can help to: @@ -254,7 +254,7 @@ We've helped many researchers to create and debug experiments, as well as delive .. grid-item-card:: Senior Staff Support - **£210 per hour** + **£280 per hour** Sometimes your project may need time from a developer or senior staff member, these rates would apply in cases where we are: @@ -295,4 +295,4 @@ What our consultancy clients say “The team at PsychoPy are incredibly helpful and are very skilled in what they do. No matter the type of experiment you are trying to programme, their consultancy team can handle it (whilst teaching you along the way!) - I am particularly grateful to Becca Hirst for her amazing support with my experiment.” +++ - **Client from Liverpool John Moores University, UK** \ No newline at end of file + **Client from Liverpool John Moores University, UK** From d64ae6f591d99b59198eb439c5897549a27ca99a Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:04:47 +0100 Subject: [PATCH 61/67] Update navbar links to include Pricing section --- source/navbar_links.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/navbar_links.json b/source/navbar_links.json index 13dddcf..f6c63d9 100644 --- a/source/navbar_links.json +++ b/source/navbar_links.json @@ -4,11 +4,10 @@ "Install": "/download", "Changelog": "/changelog" }, - "Support": { - "Forum": "https://discourse.psychopy.org/", - "Consultancy": "/consultancy" + "Pricing": {"https://psychopy.org/pricing" }, "Learn": { + "Forum": "https://discourse.psychopy.org/", "Documentation": "/documentation", "Workshops": "https://workshops.psychopy.org/", "Teaching": "/teaching/index", @@ -21,4 +20,4 @@ "Blog": "https://blog.psychopy.org/", "Ambassadors": "/ambassadors/index" } -} \ No newline at end of file +} From 0baae1b94fb8d7e0092130a6c5d42adbf528765a Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:06:44 +0100 Subject: [PATCH 62/67] Update Pricing link in navbar_links.json --- source/navbar_links.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/navbar_links.json b/source/navbar_links.json index f6c63d9..28cc7af 100644 --- a/source/navbar_links.json +++ b/source/navbar_links.json @@ -4,8 +4,7 @@ "Install": "/download", "Changelog": "/changelog" }, - "Pricing": {"https://psychopy.org/pricing" - }, + "Pricing": {"/pricing"}, "Learn": { "Forum": "https://discourse.psychopy.org/", "Documentation": "/documentation", From f31e7666ce4e58372dd772ddb083cb4cabe8d5a8 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Fri, 19 Jun 2026 09:58:34 +0100 Subject: [PATCH 63/67] Fix JSON structure for Pricing link --- source/navbar_links.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/navbar_links.json b/source/navbar_links.json index 28cc7af..dc2e592 100644 --- a/source/navbar_links.json +++ b/source/navbar_links.json @@ -4,7 +4,7 @@ "Install": "/download", "Changelog": "/changelog" }, - "Pricing": {"/pricing"}, + "Pricing": "/pricing", "Learn": { "Forum": "https://discourse.psychopy.org/", "Documentation": "/documentation", From 772939fd9b12f3193637e8a3cbe91fbc8909a108 Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Mon, 22 Jun 2026 10:07:11 +0100 Subject: [PATCH 64/67] Rename pricing tiers in pricing.rst --- source/pricing.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/pricing.rst b/source/pricing.rst index 404b259..d2fcaa1 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -55,7 +55,7 @@ Unsure? You can `book a free one-hour demo Date: Tue, 23 Jun 2026 12:04:17 +0100 Subject: [PATCH 65/67] Remove comparison link from pricing documentation Removed comparison link to similar services. --- source/pricing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/pricing.rst b/source/pricing.rst index d2fcaa1..87a01a3 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -190,7 +190,7 @@ Unsure? You can `book a free one-hour demo `_ + Consultancy ------------------------------------------------ From 839957ef8b195449593583d12b846c6aa25f8d3f Mon Sep 17 00:00:00 2001 From: Rebecca Hirst <30597180+RebeccaHirst@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:44:17 +0100 Subject: [PATCH 66/67] Include pay-per-participant pricing details Added pricing information for individual researchers using Pavlovia. --- source/pricing.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/pricing.rst b/source/pricing.rst index 87a01a3..cce84d9 100644 --- a/source/pricing.rst +++ b/source/pricing.rst @@ -26,6 +26,8 @@ Pavlovia is a secure server for hosting, sharing and running experiments online. Unsure? You can `book a free one-hour demo `_. +If you are an individual researcher and want to use Pavlovia without a licence, you can pay per participant. Credits cost £0.26 per participant and can be purchased on `the pavlovia store `_. + .. tab-set:: :sync-group: customer From 6d0e6a37874254d90a89633674c3c44fc6ae67d7 Mon Sep 17 00:00:00 2001 From: kimDundas <98759376+kimDundas@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:36:40 +0100 Subject: [PATCH 67/67] DOC: Added alt-text to gif on Resource Manager page --- source/builder/components/ResourceManagerComponent.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/builder/components/ResourceManagerComponent.rst b/source/builder/components/ResourceManagerComponent.rst index 157da1b..33ff178 100644 --- a/source/builder/components/ResourceManagerComponent.rst +++ b/source/builder/components/ResourceManagerComponent.rst @@ -17,6 +17,7 @@ Most experiments need "resources" in order to run. Be it images, sounds, spreads .. image:: /images/load_gif.gif :width: 60% + :alt: "Animation showing a progress bar loading an experiment" However, sometimes this loading can take a pretty long time. This happens either because you have a very large number of resources or because individual files are large (e.g. long movies) . In cases like this, it may be preferred to load these within your experiment, for example whilst your participants are reading through the instructions, in an inter-trial interval or during a break between blocks. This is where the *Resource Manager* component and/or the :ref:`staticcomponent` come in. @@ -161,4 +162,4 @@ Tools for testing, debugging and checking the performance of this Component. Disable Component Disable this Component -.. redirect-from:: resource_manager.rst \ No newline at end of file +.. redirect-from:: resource_manager.rst