scanpath-studio 0.40.0__tar.gz → 0.41.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scanpath_studio-0.40.0/scanpath_studio.egg-info → scanpath_studio-0.41.0}/PKG-INFO +13 -13
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/README.md +12 -12
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/__init__.py +1 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/analysis_recipe.py +2 -2
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/api.py +69 -64
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/app.py +1 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/build_info.py +19 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/cli.py +339 -328
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/code_snippet.py +1 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/controls.py +12 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/datasets.py +1 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/plot_config.py +1 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/plots.py +166 -4
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/session_keys.py +4 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/tabs.py +3 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/updates.py +1 -1
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/url_state.py +6 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0/scanpath_studio.egg-info}/PKG-INFO +13 -13
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/LICENSE +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/MANIFEST.in +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/NOTICE +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/pyproject.toml +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/__main__.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/aggregation.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/alignment.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/animation_export.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/annotations.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/assets/scanpath_studio_title_logo.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/assets/scanpath_studio_title_logo_dark.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/authoring.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/authoring_component.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/color_picker_commit.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/column_names.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/compare_source.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/computations.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/constants.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/crash_report.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/data.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/data_health.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/dataset_table.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/debug_log.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/desktop_update.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/easter_egg.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/experimental_setup.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/export.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/export_status.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/feedback.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/fields.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/html_embed.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/illustration.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/loading.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/measures.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/menu.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/metadata.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/model_scanpaths.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/multipart.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/onestop_shard.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/persistence.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/preprocessing.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/progress.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/fixations.csv +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/fixations.parquet +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/ia.csv +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/ia.parquet +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_a.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_b.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_c.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_d.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__paragraph.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__question.png +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/manifest.csv +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/raw_gaze.csv +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/raw_gaze.parquet +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/save_dialog.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/similarity.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/streamlit_entry.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/styles.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/synthetic.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/tour.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/truncation_tooltip.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/update_sample_data.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/utils.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/wizard.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/wizard_shell.py +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/SOURCES.txt +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/dependency_links.txt +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/entry_points.txt +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/requires.txt +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/top_level.txt +0 -0
- {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scanpath-studio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.41.0
|
|
4
4
|
Summary: Interactive visualization of eye-tracking data from reading studies: draw, replay and compare scanpaths, fixations, saccades and gaze heatmaps over the text, and export figures and tables.
|
|
5
5
|
Author: Keren Gruteke Klein, Maya Grossman, Ella Lion, Deborah N. Jakobi, David R. Reich, Lena Jäger, Yevgeni Berzak
|
|
6
6
|
Author-email: Omer Shubi <lacclab.technion@gmail.com>
|
|
@@ -60,11 +60,11 @@ Dynamic: license-file
|
|
|
60
60
|
|
|
61
61
|
**See how people read.**
|
|
62
62
|
|
|
63
|
-
A free, open-source app for exploring eye movements in reading. Check data quality, compare
|
|
63
|
+
A free, open-source app for exploring eye movements in reading. Check data quality, compare participants, and make figures and replay videos. No coding required.
|
|
64
64
|
|
|
65
65
|
**[Try it online](https://scanpath-studio.streamlit.app)** · **[Download the app](https://lacclab.github.io/scanpath-studio/install/)** · **[Browse the gallery](https://lacclab.github.io/scanpath-studio/gallery/)**
|
|
66
66
|
|
|
67
|
-

|
|
68
68
|
|
|
69
69
|
[](https://pypi.org/project/scanpath-studio/)
|
|
70
70
|
[](https://github.com/lacclab/scanpath-studio/blob/main/LICENSE)
|
|
@@ -72,21 +72,21 @@ A free, open-source app for exploring eye movements in reading. Check data quali
|
|
|
72
72
|
|
|
73
73
|
## From your data to a figure
|
|
74
74
|
|
|
75
|
-
- **Bring your own data.** Load
|
|
76
|
-
- **Explore and replay.** View fixations, saccades and heatmaps
|
|
77
|
-
- **Compare readings.** Overlay two scanpaths or place them side by side,
|
|
78
|
-
- **Analyze a corpus.**
|
|
79
|
-
- **Export and share.** Save publication-ready figures, GIF
|
|
75
|
+
- **Bring your own data.** Load your Fixations, Words or Raw gaze tables, and add participant, trial and text metadata. [Data guide →](https://lacclab.github.io/scanpath-studio/guides/loading-data/)
|
|
76
|
+
- **Explore and replay.** View fixations, saccades and heatmaps, and replay a reading fixation by fixation. Filter trials, then star or tag the ones to revisit.
|
|
77
|
+
- **Compare readings.** Overlay two scanpaths or place them side by side, from two participants, conditions or datasets.
|
|
78
|
+
- **Analyze a corpus.** Summarize the reading measures in your data by text, participant or group.
|
|
79
|
+
- **Export and share.** Save publication-ready figures, GIF or MP4 replays, or a batch of trials. Share a view as a link.
|
|
80
80
|
|
|
81
81
|
[Follow a tutorial](https://lacclab.github.io/scanpath-studio/tutorials/) · [See it in action](https://lacclab.github.io/scanpath-studio/#from-recording-to-publication)
|
|
82
82
|
|
|
83
83
|
## Run it on your computer
|
|
84
84
|
|
|
85
|
-
Download the desktop app
|
|
85
|
+
Download the desktop app. You don't need Python.
|
|
86
86
|
|
|
87
87
|
[Windows](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-windows-x86_64-setup.exe) · [macOS (Apple silicon)](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-macos-arm64.dmg) · [Linux](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-linux-x86_64.tar.gz)
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
A local install keeps your data on your computer. To try the app first, open the bundled demo [online](https://scanpath-studio.streamlit.app).
|
|
90
90
|
|
|
91
91
|
<details>
|
|
92
92
|
<summary>Prefer pip? Install with Python 3.11–3.14</summary>
|
|
@@ -102,14 +102,14 @@ scanpath-studio
|
|
|
102
102
|
|
|
103
103
|
## Python & command line
|
|
104
104
|
|
|
105
|
-
Build a figure in the app, then copy its Python or CLI code from **Share**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) include
|
|
105
|
+
Build a figure in the app, then copy its Python or CLI code from **Share** → **Code**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) also include Python and CLI examples.
|
|
106
106
|
|
|
107
107
|
[Python API](https://lacclab.github.io/scanpath-studio/api/) · [CLI reference](https://lacclab.github.io/scanpath-studio/cli/)
|
|
108
108
|
|
|
109
109
|
## Research & community
|
|
110
110
|
|
|
111
|
-
Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/cite
|
|
111
|
+
Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/about/#cite-scanpath-studio) (Shubi et al., 2026). The bundled demo is a subset of [OneStop Eye Movements](https://doi.org/10.1038/s41597-025-06272-2).
|
|
112
112
|
|
|
113
113
|
[Report a bug or request a feature](https://github.com/lacclab/scanpath-studio/issues) · [Contribute](https://github.com/lacclab/scanpath-studio/blob/main/CONTRIBUTING.md) · [Code of conduct](https://github.com/lacclab/scanpath-studio/blob/main/CODE_OF_CONDUCT.md)
|
|
114
114
|
|
|
115
|
-
<sub>
|
|
115
|
+
<sub>Scanpath Studio was developed with AI assistance. As with any research software, cross-check its results before you rely on them in published work.</sub>
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
**See how people read.**
|
|
4
4
|
|
|
5
|
-
A free, open-source app for exploring eye movements in reading. Check data quality, compare
|
|
5
|
+
A free, open-source app for exploring eye movements in reading. Check data quality, compare participants, and make figures and replay videos. No coding required.
|
|
6
6
|
|
|
7
7
|
**[Try it online](https://scanpath-studio.streamlit.app)** · **[Download the app](https://lacclab.github.io/scanpath-studio/install/)** · **[Browse the gallery](https://lacclab.github.io/scanpath-studio/gallery/)**
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
[](https://pypi.org/project/scanpath-studio/)
|
|
12
12
|
[](https://github.com/lacclab/scanpath-studio/blob/main/LICENSE)
|
|
@@ -14,21 +14,21 @@ A free, open-source app for exploring eye movements in reading. Check data quali
|
|
|
14
14
|
|
|
15
15
|
## From your data to a figure
|
|
16
16
|
|
|
17
|
-
- **Bring your own data.** Load
|
|
18
|
-
- **Explore and replay.** View fixations, saccades and heatmaps
|
|
19
|
-
- **Compare readings.** Overlay two scanpaths or place them side by side,
|
|
20
|
-
- **Analyze a corpus.**
|
|
21
|
-
- **Export and share.** Save publication-ready figures, GIF
|
|
17
|
+
- **Bring your own data.** Load your Fixations, Words or Raw gaze tables, and add participant, trial and text metadata. [Data guide →](https://lacclab.github.io/scanpath-studio/guides/loading-data/)
|
|
18
|
+
- **Explore and replay.** View fixations, saccades and heatmaps, and replay a reading fixation by fixation. Filter trials, then star or tag the ones to revisit.
|
|
19
|
+
- **Compare readings.** Overlay two scanpaths or place them side by side, from two participants, conditions or datasets.
|
|
20
|
+
- **Analyze a corpus.** Summarize the reading measures in your data by text, participant or group.
|
|
21
|
+
- **Export and share.** Save publication-ready figures, GIF or MP4 replays, or a batch of trials. Share a view as a link.
|
|
22
22
|
|
|
23
23
|
[Follow a tutorial](https://lacclab.github.io/scanpath-studio/tutorials/) · [See it in action](https://lacclab.github.io/scanpath-studio/#from-recording-to-publication)
|
|
24
24
|
|
|
25
25
|
## Run it on your computer
|
|
26
26
|
|
|
27
|
-
Download the desktop app
|
|
27
|
+
Download the desktop app. You don't need Python.
|
|
28
28
|
|
|
29
29
|
[Windows](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-windows-x86_64-setup.exe) · [macOS (Apple silicon)](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-macos-arm64.dmg) · [Linux](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-linux-x86_64.tar.gz)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
A local install keeps your data on your computer. To try the app first, open the bundled demo [online](https://scanpath-studio.streamlit.app).
|
|
32
32
|
|
|
33
33
|
<details>
|
|
34
34
|
<summary>Prefer pip? Install with Python 3.11–3.14</summary>
|
|
@@ -44,14 +44,14 @@ scanpath-studio
|
|
|
44
44
|
|
|
45
45
|
## Python & command line
|
|
46
46
|
|
|
47
|
-
Build a figure in the app, then copy its Python or CLI code from **Share**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) include
|
|
47
|
+
Build a figure in the app, then copy its Python or CLI code from **Share** → **Code**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) also include Python and CLI examples.
|
|
48
48
|
|
|
49
49
|
[Python API](https://lacclab.github.io/scanpath-studio/api/) · [CLI reference](https://lacclab.github.io/scanpath-studio/cli/)
|
|
50
50
|
|
|
51
51
|
## Research & community
|
|
52
52
|
|
|
53
|
-
Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/cite
|
|
53
|
+
Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/about/#cite-scanpath-studio) (Shubi et al., 2026). The bundled demo is a subset of [OneStop Eye Movements](https://doi.org/10.1038/s41597-025-06272-2).
|
|
54
54
|
|
|
55
55
|
[Report a bug or request a feature](https://github.com/lacclab/scanpath-studio/issues) · [Contribute](https://github.com/lacclab/scanpath-studio/blob/main/CONTRIBUTING.md) · [Code of conduct](https://github.com/lacclab/scanpath-studio/blob/main/CODE_OF_CONDUCT.md)
|
|
56
56
|
|
|
57
|
-
<sub>
|
|
57
|
+
<sub>Scanpath Studio was developed with AI assistance. As with any research software, cross-check its results before you rely on them in published work.</sub>
|
|
@@ -41,7 +41,7 @@ __all__ = [
|
|
|
41
41
|
# against. `__version__` is the exact build, worked out from it lazily
|
|
42
42
|
# (build_info.py) — "0.35.0" at the release itself,
|
|
43
43
|
# "0.35.0.post3+g8f18219" three commits after it.
|
|
44
|
-
__release__ = "0.
|
|
44
|
+
__release__ = "0.41.0"
|
|
45
45
|
|
|
46
46
|
# Public headless API (see api.py / datasets.py). Resolved lazily so
|
|
47
47
|
# `import scanpath_studio` stays cheap and doesn't pull in pandas/plotly/
|
|
@@ -10,7 +10,7 @@ It **references** the dataset by name and never embeds it: no table rows, and
|
|
|
10
10
|
no annotation notes (a *Favorites only* or tag filter is recorded as the filter
|
|
11
11
|
it is, not as the annotations behind it). Filters and figure settings are kept
|
|
12
12
|
apart on purpose — this file holds the filters and no styling; the figure
|
|
13
|
-
settings file (🔗 Share →
|
|
13
|
+
settings file (🔗 Share → Settings file) holds styling and no filters — so the recipe
|
|
14
14
|
says so in ``excludes`` rather than leaving a reader to guess.
|
|
15
15
|
|
|
16
16
|
It describes the current analysis only. There is no runner and no workspace
|
|
@@ -36,7 +36,7 @@ RECIPE_VERSION = 1
|
|
|
36
36
|
EXCLUDES = {
|
|
37
37
|
"figure_settings": (
|
|
38
38
|
"Palette, canvas and fonts are in the settings file (Scanpath → "
|
|
39
|
-
"Share →
|
|
39
|
+
"Share → Settings file). That file has no trial filters."
|
|
40
40
|
),
|
|
41
41
|
"data": (
|
|
42
42
|
"The recipe has no table rows or annotation notes. It names the "
|
|
@@ -107,7 +107,7 @@ if TYPE_CHECKING:
|
|
|
107
107
|
def build_authored_scanpath(
|
|
108
108
|
text: str, events: pd.DataFrame | None = None, **layout_options: Any
|
|
109
109
|
) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
110
|
-
"""Build normalized
|
|
110
|
+
"""Build normalized Words and Fixations frames from a hand-drawn scanpath.
|
|
111
111
|
|
|
112
112
|
Args:
|
|
113
113
|
text: The text to lay out as word boxes. Explicit line breaks are kept.
|
|
@@ -132,7 +132,7 @@ def build_authored_scanpath(
|
|
|
132
132
|
def load_authored_scanpath(
|
|
133
133
|
source: str | Path,
|
|
134
134
|
) -> tuple[pd.DataFrame, pd.DataFrame]:
|
|
135
|
-
"""Load a draft file as normalized
|
|
135
|
+
"""Load a draft file as normalized Words and Fixations frames.
|
|
136
136
|
|
|
137
137
|
Args:
|
|
138
138
|
source: The JSON the app's **Download draft (JSON)** saves on *Draw a
|
|
@@ -816,7 +816,7 @@ def load_scanpath_data(
|
|
|
816
816
|
keep_columns: Iterable[str] | None = None,
|
|
817
817
|
names: str = NAMES_SOURCE,
|
|
818
818
|
) -> ScanpathData:
|
|
819
|
-
"""Load and normalize a
|
|
819
|
+
"""Load and normalize a Words table, a Fixations table, or both.
|
|
820
820
|
|
|
821
821
|
Columns keep the names your files give them: ``CURRENT_FIX_DURATION``, not
|
|
822
822
|
``duration_ms``. A column Scanpath Studio built, converted, computed or changed
|
|
@@ -831,27 +831,27 @@ def load_scanpath_data(
|
|
|
831
831
|
|
|
832
832
|
- **One table is enough.** Leave out the table a dataset doesn't have: it comes back
|
|
833
833
|
as an empty frame, and the plots skip that layer.
|
|
834
|
-
- **Stimulus-level words.** A
|
|
834
|
+
- **Stimulus-level words.** A Words table without a participant column holds each
|
|
835
835
|
text's word boxes once, and they are copied onto every trial in the fixations.
|
|
836
|
-
Each trial is matched by its trial
|
|
837
|
-
``_r2`` suffix, else its ``text_id`` (for trial
|
|
836
|
+
Each trial is matched by its trial ID, else the trial ID it had before a repeat's
|
|
837
|
+
``_r2`` suffix, else its ``text_id`` (for trial IDs that embed the participant).
|
|
838
838
|
Trials that match none raise a ``data.StimulusJoinWarning`` (a ``UserWarning``).
|
|
839
|
-
- **Fixations without x/y** but with a word
|
|
840
|
-
|
|
839
|
+
- **Fixations without x/y** but with a word ID are placed at the centers of their
|
|
840
|
+
word boxes.
|
|
841
841
|
- **Bookkeeping columns.** Columns named in ``data.INTERNAL_COLUMNS`` are the
|
|
842
842
|
pipeline's own; ``data.drop_internal_columns`` removes them.
|
|
843
843
|
|
|
844
844
|
Args:
|
|
845
|
-
words: The
|
|
845
|
+
words: The Words table, one word box per row: a DataFrame; a ``.csv``, ``.tsv``, ``.txt``,
|
|
846
846
|
``.tab``, ``.parquet``, ``.feather``, ``.xlsx`` or ``.xls`` file, or a
|
|
847
847
|
``.zip`` of them; a glob pattern; or a list of paths. Several files (one per
|
|
848
848
|
participant and/or text) are concatenated, with each file's stem in a
|
|
849
849
|
``source_file`` column.
|
|
850
|
-
fixations: The
|
|
851
|
-
word_schema: A mapping from field to column name for the
|
|
850
|
+
fixations: The Fixations table, in any form ``words`` takes.
|
|
851
|
+
word_schema: A mapping from field to column name for the Words table, replacing
|
|
852
852
|
auto-detection. Start from
|
|
853
853
|
[`propose_schema`][scanpath_studio.api.propose_schema].
|
|
854
|
-
fix_schema: The same for the
|
|
854
|
+
fix_schema: The same for the Fixations table.
|
|
855
855
|
trial_parts_manifest: A nested parent-trial/parts definition, for datasets whose
|
|
856
856
|
tables identify screens through arbitrary selector columns. Explicit
|
|
857
857
|
``screen_id`` / ``screen_index`` columns can instead be mapped in each
|
|
@@ -882,11 +882,11 @@ def load_scanpath_data(
|
|
|
882
882
|
column names auto-detection looked for, and the columns the table has.
|
|
883
883
|
ValueError: ``image_root`` does not exist, or ``image_pattern`` is absolute or
|
|
884
884
|
climbs out of it.
|
|
885
|
-
data.StimulusJoinError: A stimulus-level
|
|
885
|
+
data.StimulusJoinError: A stimulus-level Words table shares neither a trial ID
|
|
886
886
|
nor a ``text_id`` with any trial (or, multipart, with every screen a trial
|
|
887
887
|
has fixations on). A ``ValueError``.
|
|
888
|
-
data.UnplacedFixationsError: The fixations map no x/y and their word
|
|
889
|
-
|
|
888
|
+
data.UnplacedFixationsError: The fixations map no x/y and their word ID column
|
|
889
|
+
holds no numbers, so none could be placed. A ``ValueError``.
|
|
890
890
|
"""
|
|
891
891
|
_check_names_choice(names)
|
|
892
892
|
if words is None and fixations is None:
|
|
@@ -1087,12 +1087,12 @@ def load_participant_metadata(
|
|
|
1087
1087
|
|
|
1088
1088
|
Args:
|
|
1089
1089
|
table: A DataFrame, or a path or glob to a CSV, TSV, Parquet or Excel file, or a
|
|
1090
|
-
list of them, stacked into one table. It holds an
|
|
1090
|
+
list of them, stacked into one table. It holds an ID column plus anything
|
|
1091
1091
|
known about each participant (``native_language``, ``age``, a comprehension
|
|
1092
1092
|
score).
|
|
1093
|
-
id_column: The participant
|
|
1093
|
+
id_column: The participant ID column. Defaults to the first recognized spelling
|
|
1094
1094
|
(``participant_id``, ``subject``, ``RECORDING_SESSION_LABEL``, …).
|
|
1095
|
-
participants: A normalized frame or a list of
|
|
1095
|
+
participants: A normalized frame or a list of IDs to validate the join against.
|
|
1096
1096
|
The result's ``.report`` then names the participants missing from either
|
|
1097
1097
|
side.
|
|
1098
1098
|
|
|
@@ -1103,7 +1103,7 @@ def load_participant_metadata(
|
|
|
1103
1103
|
Examples:
|
|
1104
1104
|
```python
|
|
1105
1105
|
words, fixations = sps.load_sample_data()
|
|
1106
|
-
meta = sps.load_participant_metadata("
|
|
1106
|
+
meta = sps.load_participant_metadata("participants.csv", participants=fixations)
|
|
1107
1107
|
meta.names # ('native_language', 'age')
|
|
1108
1108
|
```
|
|
1109
1109
|
"""
|
|
@@ -1144,18 +1144,18 @@ def load_trial_metadata(
|
|
|
1144
1144
|
grain down. Nothing is broadcast onto the words or fixations frames.
|
|
1145
1145
|
|
|
1146
1146
|
**The key is yours to state, and it changes what the table means.** Keyed by trial
|
|
1147
|
-
|
|
1147
|
+
ID alone, a row describes a *text*, and every trial of it inherits that row. Keyed
|
|
1148
1148
|
by participant **and** trial (``participant_column``), a row describes one *trial*.
|
|
1149
1149
|
Nothing in a file says which a corpus is, so this is never inferred.
|
|
1150
1150
|
|
|
1151
1151
|
Args:
|
|
1152
1152
|
table: A DataFrame or file(s), as `load_participant_metadata` takes, stacked
|
|
1153
|
-
into one table. It holds a trial
|
|
1153
|
+
into one table. It holds a trial ID column plus anything known about each
|
|
1154
1154
|
trial (a list name, a condition, a per-trial comprehension score).
|
|
1155
|
-
id_column: The trial
|
|
1155
|
+
id_column: The trial ID column. Defaults to the first recognized spelling
|
|
1156
1156
|
(``trial_id``, ``item_id``, ``TRIAL_INDEX``, …).
|
|
1157
|
-
participant_column: The participant
|
|
1158
|
-
and trial. Left out, rows are keyed by trial
|
|
1157
|
+
participant_column: The participant ID column, to key each row by participant
|
|
1158
|
+
and trial. Left out, rows are keyed by trial ID alone.
|
|
1159
1159
|
trials: A normalized fixations or words frame, or any frame with
|
|
1160
1160
|
``participant_id`` and ``trial_id``, to validate the join against. The
|
|
1161
1161
|
result's ``.report`` then names the trials missing from either side.
|
|
@@ -1217,12 +1217,12 @@ def load_text_metadata(
|
|
|
1217
1217
|
|
|
1218
1218
|
Args:
|
|
1219
1219
|
table: A DataFrame or file(s), as `load_participant_metadata` takes, stacked
|
|
1220
|
-
into one table. It holds a text
|
|
1220
|
+
into one table. It holds a text ID column plus anything known about each
|
|
1221
1221
|
text (genre, difficulty, a stimulus-level comprehension score).
|
|
1222
|
-
id_column: The text
|
|
1223
|
-
the
|
|
1222
|
+
id_column: The text ID column, or several columns that build a composite ID, as
|
|
1223
|
+
the Text ID mapping can. Defaults to the first recognized
|
|
1224
1224
|
spelling (``text_id``, ``paragraph_id``, ``stimulus_id``, …).
|
|
1225
|
-
texts: A normalized fixations or words frame, or any iterable of text
|
|
1225
|
+
texts: A normalized fixations or words frame, or any iterable of text IDs, to
|
|
1226
1226
|
validate the join against. The result's ``.report`` then names the texts
|
|
1227
1227
|
missing from either side.
|
|
1228
1228
|
|
|
@@ -1262,9 +1262,10 @@ def load_text_metadata(
|
|
|
1262
1262
|
|
|
1263
1263
|
|
|
1264
1264
|
def load_sample_data(*, names: str = NAMES_SOURCE) -> ScanpathData:
|
|
1265
|
-
"""Load the bundled
|
|
1265
|
+
"""Load the bundled demo, normalized and ready to plot.
|
|
1266
1266
|
|
|
1267
|
-
|
|
1267
|
+
The bundled demo is a subset of OneStop: two participants, twelve trials each, every
|
|
1268
|
+
one with fixations. The frames carry the
|
|
1268
1269
|
demo's recorded screen (OneStop's 2560×1440), so ``plot_scanpath`` draws them on it
|
|
1269
1270
|
without a ``canvas_size``, as ``scanpath-studio render --sample`` does.
|
|
1270
1271
|
|
|
@@ -1821,7 +1822,7 @@ def list_trials(
|
|
|
1821
1822
|
raw_gaze: A frame from [`load_raw_gaze`][scanpath_studio.api.load_raw_gaze].
|
|
1822
1823
|
|
|
1823
1824
|
Returns:
|
|
1824
|
-
The participant and trial
|
|
1825
|
+
The participant and trial IDs, sorted by participant, then trial, under the
|
|
1825
1826
|
names the frames carry.
|
|
1826
1827
|
"""
|
|
1827
1828
|
words, word_names = _named_in(words, "words", optional=True)
|
|
@@ -2369,7 +2370,8 @@ def figure_options(kind: str = "static", *, choices: bool = False) -> dict:
|
|
|
2369
2370
|
|
|
2370
2371
|
The defaults are what a call actually renders with, the app's Scanpath design, so a
|
|
2371
2372
|
script can diff its intended settings against what it would get. The *Figure
|
|
2372
|
-
options*
|
|
2373
|
+
options* section of the API reference lists them by section, with their ``render``
|
|
2374
|
+
flags.
|
|
2373
2375
|
|
|
2374
2376
|
Args:
|
|
2375
2377
|
kind: ``"static"`` for [`plot_scanpath`][scanpath_studio.api.plot_scanpath],
|
|
@@ -2541,9 +2543,9 @@ def plot_scanpath(
|
|
|
2541
2543
|
[`load_scanpath_data`][scanpath_studio.api.load_scanpath_data], or ``None``
|
|
2542
2544
|
for a dataset recorded as raw gaze alone.
|
|
2543
2545
|
fixations: The normalized fixations frame, or ``None`` likewise.
|
|
2544
|
-
participant: The participant
|
|
2546
|
+
participant: The participant ID. It may be left out when the frames hold one
|
|
2545
2547
|
trial.
|
|
2546
|
-
trial: The trial
|
|
2548
|
+
trial: The trial ID. It may be left out when the frames hold one trial.
|
|
2547
2549
|
screen: The screen of a multipart trial to draw. Left out, the first recorded
|
|
2548
2550
|
screen; coordinate spaces are never concatenated.
|
|
2549
2551
|
canvas_size: The monitor size in px, ``(width, height)``. Estimated from the
|
|
@@ -2561,9 +2563,11 @@ def plot_scanpath(
|
|
|
2561
2563
|
but ``None`` or ``"off"`` raises ``ValueError``.
|
|
2562
2564
|
drift_connectors: Experimental, with ``drift_correction``.
|
|
2563
2565
|
fix_index_range: ``(start, end)``: draw only fixations ``start`` to ``end`` of
|
|
2564
|
-
the trial, 1-based and both inclusive. The app's
|
|
2565
|
-
|
|
2566
|
-
|
|
2566
|
+
the trial, 1-based and both inclusive. The app's **Filters & highlights** →
|
|
2567
|
+
**Fixations** → *Index range*.
|
|
2568
|
+
illustration: Apply the Illustration design: fixations snapped to their lines,
|
|
2569
|
+
arced saccades, one color for the fixations and one for the saccades, no
|
|
2570
|
+
heatmap or word boxes. Keywords you pass
|
|
2567
2571
|
still win.
|
|
2568
2572
|
illustration_label: ``"auto"`` labels the figure an illustration when it no
|
|
2569
2573
|
longer shows the data as recorded; ``"show"`` and ``"hide"`` decide it.
|
|
@@ -2581,10 +2585,10 @@ def plot_scanpath(
|
|
|
2581
2585
|
app's names) sets a group of colors at once. A color you pass explicitly
|
|
2582
2586
|
wins.
|
|
2583
2587
|
- ``show_stimulus_image=True`` draws the trial's own stimulus page under the
|
|
2584
|
-
scanpath, as the app's
|
|
2588
|
+
scanpath, as the app's **Stimulus** → **Image** does: the ``image_path`` its
|
|
2585
2589
|
rows carry (from ``load_scanpath_data(image_root=…)`` or
|
|
2586
2590
|
[`attach_stimulus_images`][scanpath_studio.api.attach_stimulus_images];
|
|
2587
|
-
the demo ships its pages), at its ``image_x`` / ``image_y``. A
|
|
2591
|
+
the bundled demo ships its pages), at its ``image_x`` / ``image_y``. A
|
|
2588
2592
|
``background_image=`` you pass wins.
|
|
2589
2593
|
- ``fixation_snap_to_line=True`` moves each fixation up or down onto its
|
|
2590
2594
|
text line, keeping its x: its word's line, else the nearest.
|
|
@@ -2750,9 +2754,9 @@ def animate_scanpath(
|
|
|
2750
2754
|
"""Build the animated replay of one trial's scanpath, or of two on one clock.
|
|
2751
2755
|
|
|
2752
2756
|
Save it as interactive HTML with [`save_figure`][scanpath_studio.api.save_figure],
|
|
2753
|
-
whose page keeps the replay's
|
|
2754
|
-
`animation_export.export_animation`, which
|
|
2755
|
-
Plotly's
|
|
2757
|
+
whose page keeps the replay's timing, or render it to GIF or MP4 with
|
|
2758
|
+
`animation_export.export_animation`, which keeps the same timing. ``fig.show()``
|
|
2759
|
+
plays it on Plotly's frame queue, which runs slower than the reading did.
|
|
2756
2760
|
|
|
2757
2761
|
The replay takes a subset of [`plot_scanpath`][scanpath_studio.api.plot_scanpath]'s
|
|
2758
2762
|
options (``figure_options("animation")``), and the ones they share default alike, so
|
|
@@ -2800,8 +2804,8 @@ def animate_scanpath(
|
|
|
2800
2804
|
``fixations_b`` when they are given, else in ``words`` / ``fixations``, as
|
|
2801
2805
|
[`compare_scanpaths`][scanpath_studio.api.compare_scanpaths] takes it.
|
|
2802
2806
|
dataset_b: The name of B's dataset, when it is another one. The pair is then
|
|
2803
|
-
checked as an overlay, and B's participant
|
|
2804
|
-
hover says
|
|
2807
|
+
checked as an overlay, and B's participant IDs are prefixed with it, so a
|
|
2808
|
+
hover says which dataset the participant is from.
|
|
2805
2809
|
setup: A's `experimental_setup.SetupSnapshot`, the screen the overlay check
|
|
2806
2810
|
reads. A side without one is read off its data: the extent of that one
|
|
2807
2811
|
trial, which rarely spans the whole screen, so state both when you know
|
|
@@ -3230,8 +3234,8 @@ def render_parent_trial(
|
|
|
3230
3234
|
Args:
|
|
3231
3235
|
words: The normalized words frame.
|
|
3232
3236
|
fixations: The normalized fixations frame.
|
|
3233
|
-
participant: The participant
|
|
3234
|
-
trial: The parent trial's
|
|
3237
|
+
participant: The participant ID.
|
|
3238
|
+
trial: The parent trial's ID.
|
|
3235
3239
|
animate: Build replays with
|
|
3236
3240
|
[`animate_scanpath`][scanpath_studio.api.animate_scanpath] instead of
|
|
3237
3241
|
[`plot_scanpath`][scanpath_studio.api.plot_scanpath]'s figures.
|
|
@@ -3239,8 +3243,8 @@ def render_parent_trial(
|
|
|
3239
3243
|
``layout.meta['transition_after_ms']`` records the delay before the next
|
|
3240
3244
|
screen: zero for ``"instant"``, the observed parent-clock gap for
|
|
3241
3245
|
``"recorded"``.
|
|
3242
|
-
screens: Only these screen
|
|
3243
|
-
|
|
3246
|
+
screens: Only these screen IDs (one may be given as a string), in the trial's
|
|
3247
|
+
order, as the app's **Export** → *Screens* does. ``None`` renders them all.
|
|
3244
3248
|
Each figure's ``screen_index`` meta stays the screen's place in the trial.
|
|
3245
3249
|
**options: Passed to [`plot_scanpath`][scanpath_studio.api.plot_scanpath] or
|
|
3246
3250
|
[`animate_scanpath`][scanpath_studio.api.animate_scanpath].
|
|
@@ -3250,7 +3254,7 @@ def render_parent_trial(
|
|
|
3250
3254
|
A single-screen trial gives ``{"screen-1": figure}``.
|
|
3251
3255
|
|
|
3252
3256
|
Raises:
|
|
3253
|
-
ValueError: A screen
|
|
3257
|
+
ValueError: A screen ID the trial does not have, or ``screens`` for a trial with
|
|
3254
3258
|
no screens to choose from.
|
|
3255
3259
|
"""
|
|
3256
3260
|
if transition_mode not in {"instant", "recorded"}:
|
|
@@ -3413,9 +3417,9 @@ def compare_scanpaths(
|
|
|
3413
3417
|
|
|
3414
3418
|
**Two datasets.** ``words_b`` / ``fixations_b`` draw B from a *different* dataset.
|
|
3415
3419
|
Two datasets can hold the same ``(participant_id, trial_id)``, so B's participant
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
only the trace labels show the
|
|
3420
|
+
IDs are prefixed with ``dataset_b`` inside the merged frames. Without that, the
|
|
3421
|
+
figure could draw the wrong reading, or rows of both mixed into one. The frames you
|
|
3422
|
+
pass in are not modified, and only the trace labels show the prefix.
|
|
3419
3423
|
|
|
3420
3424
|
**The overlay gate.** An overlay needs both readings on screens of the same size, or
|
|
3421
3425
|
it raises ``ValueError`` (the app falls back to side by side). That holds across
|
|
@@ -3453,7 +3457,7 @@ def compare_scanpaths(
|
|
|
3453
3457
|
another dataset's.
|
|
3454
3458
|
words_b: B's words frame, to draw B from another dataset.
|
|
3455
3459
|
fixations_b: B's fixations frame, likewise.
|
|
3456
|
-
dataset_b: The name B's participant
|
|
3460
|
+
dataset_b: The name B's participant IDs are prefixed with.
|
|
3457
3461
|
raw_gaze: Samples from [`load_raw_gaze`][scanpath_studio.api.load_raw_gaze],
|
|
3458
3462
|
drawn under each reading's scanpath in its color. It serves both readings of
|
|
3459
3463
|
one dataset; across datasets it is A's. Passing it turns the layer on;
|
|
@@ -3868,10 +3872,11 @@ def save_figure_layers(
|
|
|
3868
3872
|
) -> dict:
|
|
3869
3873
|
"""Save a scanpath figure as one file per layer, for editing.
|
|
3870
3874
|
|
|
3871
|
-
Each *visible* layer (
|
|
3872
|
-
|
|
3873
|
-
background, at the same size and axis ranges. The files line up
|
|
3874
|
-
in Illustrator or Inkscape.
|
|
3875
|
+
Each *visible* layer (stimulus image, heatmap, word boxes, saccades, fixations, raw
|
|
3876
|
+
gaze, labels, frame) is the full figure with only that layer's elements, on a
|
|
3877
|
+
transparent background, at the same size and axis ranges. The files line up when
|
|
3878
|
+
stacked in Illustrator or Inkscape. The app's Export bundle writes the same files
|
|
3879
|
+
under *Layers as separate files*.
|
|
3875
3880
|
|
|
3876
3881
|
Args:
|
|
3877
3882
|
fig: A scanpath figure.
|
|
@@ -3940,7 +3945,7 @@ def figure_code(
|
|
|
3940
3945
|
) -> str:
|
|
3941
3946
|
"""Write the API or CLI code that reproduces a figure.
|
|
3942
3947
|
|
|
3943
|
-
The headless
|
|
3948
|
+
The headless form of the app's **Share** → **Code**. Give
|
|
3944
3949
|
it the arguments you would give [`plot_scanpath`][scanpath_studio.api.plot_scanpath]
|
|
3945
3950
|
(``kind="static"``), [`animate_scanpath`][scanpath_studio.api.animate_scanpath]
|
|
3946
3951
|
(``"animation"``) or [`compare_scanpaths`][scanpath_studio.api.compare_scanpaths]
|
|
@@ -3960,9 +3965,9 @@ def figure_code(
|
|
|
3960
3965
|
"fixations": [...]}`` and so on. With ``show_raw_gaze=True``, ``"raw_gaze"``
|
|
3961
3966
|
(and an optional ``"raw_gaze_schema"``) names the raw-gaze files; the demo
|
|
3962
3967
|
uses its own.
|
|
3963
|
-
participant: The participant
|
|
3968
|
+
participant: The participant ID. Left empty, the snippet renders the first
|
|
3964
3969
|
available trial, as ``render`` does.
|
|
3965
|
-
trial: The trial
|
|
3970
|
+
trial: The trial ID, likewise.
|
|
3966
3971
|
screen: A's screen of a multipart trial (``screen=``, ``--screen``).
|
|
3967
3972
|
compare: B's ``(participant, trial)``, for a comparison or a co-animation.
|
|
3968
3973
|
compare_screen: B's screen (``screen_b=``, ``--compare-screen``).
|
|
@@ -3980,7 +3985,7 @@ def figure_code(
|
|
|
3980
3985
|
compare_labels: The two trace labels, when they are not the composed defaults:
|
|
3981
3986
|
``labels=`` and ``--label-a`` / ``--label-b``.
|
|
3982
3987
|
canvas_size: The monitor size. Defaults to the screen ``render`` assumes for the
|
|
3983
|
-
source (the demo's 2560×1440, PoTeC's 1680×1050, …), so both forms draw the
|
|
3988
|
+
source (the bundled demo's 2560×1440, PoTeC's 1680×1050, …), so both forms draw the
|
|
3984
3989
|
same figure.
|
|
3985
3990
|
base_font_size: As the builders take it.
|
|
3986
3991
|
font_family: As the builders take it.
|
|
@@ -4106,9 +4111,9 @@ def cache_status() -> dict:
|
|
|
4106
4111
|
|
|
4107
4112
|
A local or desktop app stores completed uploaded datasets, column mappings, view
|
|
4108
4113
|
settings, saved designs, metadata tables and annotations under your cache folder, so
|
|
4109
|
-
a refresh or a restart resumes where it left off. A hosted deployment
|
|
4110
|
-
The same information is
|
|
4111
|
-
and in ``scanpath-studio cache``.
|
|
4114
|
+
a refresh or a restart resumes where it left off. A hosted deployment, such as the
|
|
4115
|
+
online demo, keeps none. The same information is on the app's **Data Management**
|
|
4116
|
+
page under *Saved on this computer*, and in ``scanpath-studio cache``.
|
|
4112
4117
|
|
|
4113
4118
|
| Key | What it holds |
|
|
4114
4119
|
| --- | --- |
|
|
@@ -1461,7 +1461,7 @@ def _about_dialog() -> None:
|
|
|
1461
1461
|
f"""
|
|
1462
1462
|
Developed by [Omer Shubi](https://omershubi.github.io/),
|
|
1463
1463
|
[Keren Gruteke Klein](https://kerengruteke.github.io/),
|
|
1464
|
-
[Maya Grossman](https://www.linkedin.com/in/maya-
|
|
1464
|
+
[Maya Grossman](https://www.linkedin.com/in/maya-grossman-32b547292/),
|
|
1465
1465
|
[Ella Lion](https://ella-lion.github.io/),
|
|
1466
1466
|
[Deborah N. Jakobi]({_DILI}/lab-members/jakobi.html),
|
|
1467
1467
|
[David R. Reich]({_DILI}/lab-members/reich.html),
|
|
@@ -173,6 +173,25 @@ def read_checkout(
|
|
|
173
173
|
return None
|
|
174
174
|
if done.returncode != 0:
|
|
175
175
|
return None
|
|
176
|
+
line = done.stdout.strip()
|
|
177
|
+
match = _DESCRIBE.match(line)
|
|
178
|
+
if not match:
|
|
179
|
+
return None
|
|
180
|
+
# describe counts every commit the tag lacks, merges included. The demo's
|
|
181
|
+
# `stable` branch takes each release as a merge commit, so there that
|
|
182
|
+
# count grows by one per release ever merged; count only the commits that
|
|
183
|
+
# brought changes of their own.
|
|
184
|
+
try:
|
|
185
|
+
counted = run(
|
|
186
|
+
["git", "-C", str(root), "rev-list", "--count", "--no-merges"]
|
|
187
|
+
+ [f"v{match['release']}..HEAD"],
|
|
188
|
+
**options,
|
|
189
|
+
)
|
|
190
|
+
distance = int(counted.stdout) if counted.returncode == 0 else None
|
|
191
|
+
except (OSError, subprocess.SubprocessError, ValueError):
|
|
192
|
+
distance = None
|
|
193
|
+
if distance is not None:
|
|
194
|
+
line = f"v{match['release']}-{distance}-g{match['commit']}"
|
|
176
195
|
# `describe --dirty` would refresh and rewrite .git/index, which can make
|
|
177
196
|
# a concurrent `git commit` fail; `status` with optional locks off does
|
|
178
197
|
# not.
|
|
@@ -184,7 +203,6 @@ def read_checkout(
|
|
|
184
203
|
dirty = status.returncode == 0 and bool(status.stdout.strip())
|
|
185
204
|
except (OSError, subprocess.SubprocessError):
|
|
186
205
|
dirty = False
|
|
187
|
-
line = done.stdout.strip()
|
|
188
206
|
return from_describe(line + "-dirty" if dirty else line)
|
|
189
207
|
|
|
190
208
|
|