scanpath-studio 0.43.0__tar.gz → 0.44.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.43.0/scanpath_studio.egg-info → scanpath_studio-0.44.0}/PKG-INFO +1 -1
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/__init__.py +1 -1
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/api.py +10 -2
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/app.py +70 -18
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/column_names.py +25 -2
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/computations.py +40 -5
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/constants.py +12 -3
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/controls.py +202 -77
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/data.py +502 -91
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/plots.py +55 -11
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/styles.py +38 -4
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/tabs.py +24 -3
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/wizard.py +26 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0/scanpath_studio.egg-info}/PKG-INFO +1 -1
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/LICENSE +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/MANIFEST.in +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/NOTICE +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/README.md +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/pyproject.toml +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/__main__.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/aggregation.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/alignment.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/analysis_recipe.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/animation_export.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/annotations.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/assets/scanpath_studio_title_logo.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/assets/scanpath_studio_title_logo_dark.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/authoring.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/authoring_component.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/build_info.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/citation.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/cite_button.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/cli.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/code_snippet.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/color_picker_commit.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/compare_source.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/crash_report.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/data_health.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/dataset_table.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/datasets.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/debug_log.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/desktop_update.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/easter_egg.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/experimental_setup.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/export.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/export_status.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/feedback.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/fields.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/file_citation.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/html_embed.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/illustration.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/loading.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/measures.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/menu.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/metadata.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/model_scanpaths.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/multipart.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/onestop_shard.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/persistence.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/plot_config.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/preprocessing.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/progress.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/fixations.csv +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/fixations.parquet +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/ia.csv +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/ia.parquet +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_1_Ele__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_1_Ele__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_2_Ele__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_2_Ele__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_3_Adv__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_3_Adv__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_4_Adv__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_4_Adv__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_5_Adv__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_5_Adv__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_6_Ele__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_1_6_Ele__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_1_Adv__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_1_Adv__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_2_Adv__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_2_Adv__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_3_Adv__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_3_Adv__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_4_Ele__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_4_Ele__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_5_Ele__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_5_Ele__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_a.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_b.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_c.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_d.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_6_Ele__paragraph.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/2_2_6_Ele__question.png +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/images/manifest.csv +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/raw_gaze.csv +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/sample_data/raw_gaze.parquet +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/save_dialog.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/session_keys.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/similarity.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/streamlit_entry.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/synthetic.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/tour.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/truncation_tooltip.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/ui_mode.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/update_sample_data.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/updates.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/url_state.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/utils.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/wizard_shell.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio/wizard_watch.py +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio.egg-info/SOURCES.txt +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio.egg-info/dependency_links.txt +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio.egg-info/entry_points.txt +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio.egg-info/requires.txt +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.0}/scanpath_studio.egg-info/top_level.txt +0 -0
- {scanpath_studio-0.43.0 → scanpath_studio-0.44.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.44.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>
|
|
@@ -42,7 +42,7 @@ __all__ = [
|
|
|
42
42
|
# against. `__version__` is the exact build, worked out from it lazily
|
|
43
43
|
# (build_info.py) — "0.35.0" at the release itself,
|
|
44
44
|
# "0.35.0.post3+g8f18219" three commits after it.
|
|
45
|
-
__release__ = "0.
|
|
45
|
+
__release__ = "0.44.0"
|
|
46
46
|
|
|
47
47
|
# Public headless API (see api.py / datasets.py). Resolved lazily so
|
|
48
48
|
# `import scanpath_studio` stays cheap and doesn't pull in pandas/plotly/
|
|
@@ -840,6 +840,13 @@ def load_scanpath_data(
|
|
|
840
840
|
Trials that match none raise a ``data.StimulusJoinWarning`` (a ``UserWarning``).
|
|
841
841
|
- **Fixations without x/y** but with a word ID are placed at the centers of their
|
|
842
842
|
word boxes.
|
|
843
|
+
- **Text word IDs.** A word ID column that holds text and no numbers (``w1``,
|
|
844
|
+
``Hello_3``) is read as keys, kept in a ``word_key`` column (under the column's
|
|
845
|
+
own name with ``names="source"``). Each word's ``word_id`` is then its position
|
|
846
|
+
in its text, 1, 2, … in the Words table's row order (the order of the reading
|
|
847
|
+
that lists the most of the text), one number for every reader, and a
|
|
848
|
+
fixation's is the number of the word with the same text ID in its trial —
|
|
849
|
+
exactly the same text, else none. A text ID never matches a number.
|
|
843
850
|
- **Bookkeeping columns.** Columns named in ``data.INTERNAL_COLUMNS`` are the
|
|
844
851
|
pipeline's own; ``data.drop_internal_columns`` removes them.
|
|
845
852
|
|
|
@@ -887,8 +894,9 @@ def load_scanpath_data(
|
|
|
887
894
|
data.StimulusJoinError: A stimulus-level Words table shares neither a trial ID
|
|
888
895
|
nor a ``text_id`` with any trial (or, multipart, with every screen a trial
|
|
889
896
|
has fixations on). A ``ValueError``.
|
|
890
|
-
data.UnplacedFixationsError: The fixations
|
|
891
|
-
|
|
897
|
+
data.UnplacedFixationsError: The fixations have no x/y, and not one of their
|
|
898
|
+
word IDs names a word: text IDs the Words table does not have, or numbers
|
|
899
|
+
against a Words table of text IDs. A ``ValueError``.
|
|
892
900
|
"""
|
|
893
901
|
_check_names_choice(names)
|
|
894
902
|
if words is None and fixations is None:
|
|
@@ -158,6 +158,7 @@ from scanpath_studio.controls import (
|
|
|
158
158
|
background_swatch,
|
|
159
159
|
clear_trial_filter,
|
|
160
160
|
clear_trial_filters,
|
|
161
|
+
column_mapping_from_state,
|
|
161
162
|
column_mapping_ui,
|
|
162
163
|
has_active_trial_filters,
|
|
163
164
|
read_trial_filters,
|
|
@@ -304,7 +305,7 @@ from scanpath_studio.session_keys import (
|
|
|
304
305
|
PARAM_CORPUS,
|
|
305
306
|
PARAM_DATASET,
|
|
306
307
|
)
|
|
307
|
-
from scanpath_studio.styles import get_app_css, widen_menu
|
|
308
|
+
from scanpath_studio.styles import compact_css, get_app_css, widen_menu
|
|
308
309
|
from scanpath_studio.tabs import (
|
|
309
310
|
_EDITOR_KEY_NOISE,
|
|
310
311
|
_REMAP_DIRTY_KEY,
|
|
@@ -730,7 +731,7 @@ def configure_page() -> None:
|
|
|
730
731
|
page_icon="👀",
|
|
731
732
|
layout="wide",
|
|
732
733
|
)
|
|
733
|
-
st.
|
|
734
|
+
st.html(_app_stylesheet())
|
|
734
735
|
# Seeded here, not later in the run: the stylesheet below hides one mode's
|
|
735
736
|
# containers, so it needs the mode on the very first run.
|
|
736
737
|
ui_mode.seed_mode(st.session_state, st.query_params, _cookies())
|
|
@@ -752,6 +753,19 @@ LOGO_PATH = Path(__file__).parent / "assets" / "scanpath_studio_title_logo.png"
|
|
|
752
753
|
LOGO_DARK_PATH = LOGO_PATH.with_name("scanpath_studio_title_logo_dark.png")
|
|
753
754
|
|
|
754
755
|
|
|
756
|
+
@functools.cache
|
|
757
|
+
def _app_stylesheet() -> str:
|
|
758
|
+
"""The app stylesheet as sent: compacted, for a style-only `st.html`.
|
|
759
|
+
|
|
760
|
+
Not a markdown block: Streamlit applies a markdown block's raw HTML only
|
|
761
|
+
once its own HTML renderer has loaded and parsed the whole block, which on
|
|
762
|
+
a slow connection left the first screen unstyled for seconds. Built once
|
|
763
|
+
per process — the sheet depends on nothing but module constants, and
|
|
764
|
+
compacting it costs ~10 ms, on every rerun otherwise.
|
|
765
|
+
"""
|
|
766
|
+
return compact_css(get_app_css())
|
|
767
|
+
|
|
768
|
+
|
|
755
769
|
@functools.cache
|
|
756
770
|
def _logo_theme_css() -> str:
|
|
757
771
|
"""CSS that swaps the header wordmark for its dark variant in dark mode.
|
|
@@ -3775,7 +3789,8 @@ def reset_column_mapping() -> None:
|
|
|
3775
3789
|
#: ✕ Cancel puts the keys back.
|
|
3776
3790
|
BUILTIN_MAPPING_HELD_KEY = "_builtin_mapping_held"
|
|
3777
3791
|
#: The panels' current picks, ``{"words": schema, "fixations": schema}``,
|
|
3778
|
-
#: written by `prepare_data` on every run that
|
|
3792
|
+
#: written by `prepare_data` on every run of a source that has them, drawn or
|
|
3793
|
+
#: read from their keys.
|
|
3779
3794
|
BUILTIN_MAPPING_PENDING_KEY = "_builtin_mapping_pending"
|
|
3780
3795
|
#: Set by ✅ Save changes for the success line on the screen it returns to.
|
|
3781
3796
|
BUILTIN_MAPPING_SAVED_KEY = "_builtin_mapping_saved"
|
|
@@ -4040,12 +4055,18 @@ def prepare_data(
|
|
|
4040
4055
|
declared_fix_schema: dict | None = None,
|
|
4041
4056
|
mapping_dataset: object = None,
|
|
4042
4057
|
held_schemas: dict | None = None,
|
|
4058
|
+
render_mapping: bool = True,
|
|
4043
4059
|
) -> tuple[pd.DataFrame, pd.DataFrame, list]:
|
|
4044
4060
|
"""Infer schemas and normalize incoming dataframes to canonical column names.
|
|
4045
4061
|
|
|
4046
|
-
When ``allow_override`` is True,
|
|
4047
|
-
|
|
4048
|
-
|
|
4062
|
+
When ``allow_override`` is True, the user's picks in the mapping panels
|
|
4063
|
+
(pre-filled with auto-detection) decide each field. Otherwise just
|
|
4064
|
+
auto-detect.
|
|
4065
|
+
|
|
4066
|
+
``render_mapping`` says whether those panels are drawn this run. They are
|
|
4067
|
+
only on screen while ✏️ Edit dataset is open, so every other run reads the
|
|
4068
|
+
same picks from session state (`controls.column_mapping_from_state`)
|
|
4069
|
+
instead of building sixty-odd hidden selectboxes.
|
|
4049
4070
|
|
|
4050
4071
|
Returns ``(words_norm, fixations_norm, problems)``. ``problems`` is a list
|
|
4051
4072
|
of human-readable strings; when it's non-empty the column mapping isn't
|
|
@@ -4066,8 +4087,9 @@ def prepare_data(
|
|
|
4066
4087
|
happen to match (``controls.forget_mapping_for_other_table``).
|
|
4067
4088
|
|
|
4068
4089
|
``held_schemas`` (``{"words": …, "fixations": …}``) is the mapping the
|
|
4069
|
-
dataset keeps while ✏️ Edit dataset is open: the panels
|
|
4070
|
-
their
|
|
4090
|
+
dataset keeps while ✏️ Edit dataset is open: the panels' picks (drawn, or
|
|
4091
|
+
read from their keys on another view) are published as the draft
|
|
4092
|
+
(``BUILTIN_MAPPING_PENDING_KEY``),
|
|
4071
4093
|
but the frames are normalized under the held mapping until ✅ Save changes.
|
|
4072
4094
|
"""
|
|
4073
4095
|
has_words = not words_df.empty
|
|
@@ -4081,7 +4103,15 @@ def prepare_data(
|
|
|
4081
4103
|
word_proposed = _apply_declared_schema(
|
|
4082
4104
|
_proposed_schema("words", words_df), declared_word_schema
|
|
4083
4105
|
)
|
|
4084
|
-
if allow_override:
|
|
4106
|
+
if allow_override and not render_mapping:
|
|
4107
|
+
word_schema = column_mapping_from_state(
|
|
4108
|
+
words_df,
|
|
4109
|
+
"col_map_words",
|
|
4110
|
+
WORD_FIELD_SPECS,
|
|
4111
|
+
word_proposed,
|
|
4112
|
+
dataset=mapping_dataset,
|
|
4113
|
+
)
|
|
4114
|
+
elif allow_override:
|
|
4085
4115
|
word_schema = column_mapping_ui(
|
|
4086
4116
|
words_df,
|
|
4087
4117
|
table_label="Words (interest areas)",
|
|
@@ -4099,6 +4129,7 @@ def prepare_data(
|
|
|
4099
4129
|
stack_labels=True,
|
|
4100
4130
|
dataset=mapping_dataset,
|
|
4101
4131
|
)
|
|
4132
|
+
if allow_override:
|
|
4102
4133
|
pending["words"] = word_schema
|
|
4103
4134
|
if held_schemas and held_schemas.get("words") is not None:
|
|
4104
4135
|
word_schema = held_schemas["words"]
|
|
@@ -4112,7 +4143,15 @@ def prepare_data(
|
|
|
4112
4143
|
fix_proposed = _apply_declared_schema(
|
|
4113
4144
|
_proposed_schema("fixations", fixations_df), declared_fix_schema
|
|
4114
4145
|
)
|
|
4115
|
-
if allow_override:
|
|
4146
|
+
if allow_override and not render_mapping:
|
|
4147
|
+
fix_schema = column_mapping_from_state(
|
|
4148
|
+
fixations_df,
|
|
4149
|
+
"col_map_fix",
|
|
4150
|
+
FIX_FIELD_SPECS,
|
|
4151
|
+
fix_proposed,
|
|
4152
|
+
dataset=mapping_dataset,
|
|
4153
|
+
)
|
|
4154
|
+
elif allow_override:
|
|
4116
4155
|
fix_schema = column_mapping_ui(
|
|
4117
4156
|
fixations_df,
|
|
4118
4157
|
table_label="Fixations",
|
|
@@ -4126,6 +4165,7 @@ def prepare_data(
|
|
|
4126
4165
|
stack_labels=True,
|
|
4127
4166
|
dataset=mapping_dataset,
|
|
4128
4167
|
)
|
|
4168
|
+
if allow_override:
|
|
4129
4169
|
pending["fixations"] = fix_schema
|
|
4130
4170
|
if held_schemas and held_schemas.get("fixations") is not None:
|
|
4131
4171
|
fix_schema = held_schemas["fixations"]
|
|
@@ -9553,11 +9593,14 @@ def _run_app() -> None:
|
|
|
9553
9593
|
# The page is built on EVERY run and merely hidden when another view is
|
|
9554
9594
|
# active (`DATA_PAGE_OFFSCREEN_KEY` → `display: none` in styles.py). That is
|
|
9555
9595
|
# not laziness: the slots below are filled by the loaders' directory input,
|
|
9556
|
-
# ⬇ Download button
|
|
9557
|
-
#
|
|
9558
|
-
#
|
|
9559
|
-
#
|
|
9560
|
-
#
|
|
9596
|
+
# ⬇ Download button and source options, all of which *drive* the load on
|
|
9597
|
+
# every rerun — and Streamlit drops the key of a widget that did not
|
|
9598
|
+
# render. Rendering-then-hiding keeps every loader's every-run semantics
|
|
9599
|
+
# without splitting each into a render/resolve pair. The one exception is
|
|
9600
|
+
# the built-in sources' column-mapping panels, sixty-odd selectboxes and a
|
|
9601
|
+
# sixth of every rerun's bytes: they are split that way
|
|
9602
|
+
# (`controls.column_mapping_from_state`) and drawn only while ✏️ Edit
|
|
9603
|
+
# dataset shows them.
|
|
9561
9604
|
#
|
|
9562
9605
|
# The page has two *parts*, both built every run for the same reason the
|
|
9563
9606
|
# page itself is (above): the overview always shows, and the editor opens
|
|
@@ -9949,8 +9992,9 @@ def _run_app() -> None:
|
|
|
9949
9992
|
else:
|
|
9950
9993
|
deep_link_pid = None
|
|
9951
9994
|
raw_gaze_df: pd.DataFrame | None = None
|
|
9952
|
-
#
|
|
9953
|
-
#
|
|
9995
|
+
# Is the open source one whose mapping the editable pre-normalization
|
|
9996
|
+
# panels decide, in the page's Column mapping section? (Mode A — see the
|
|
9997
|
+
# dispatch below. They are drawn only while ✏️ Edit dataset is open.)
|
|
9954
9998
|
mapping_editor_rendered = False
|
|
9955
9999
|
# Start each load with a clean column-mapping stash; each branch below
|
|
9956
10000
|
# records the schema it used for the Data page's mapping section.
|
|
@@ -10107,8 +10151,11 @@ def _run_app() -> None:
|
|
|
10107
10151
|
# default first-load source; pre-filled with auto-detection, so an
|
|
10108
10152
|
# untouched mapping normalizes identically.
|
|
10109
10153
|
allow_override=mapping_editor_rendered,
|
|
10110
|
-
# Mode A of the Data page's one Column mapping section
|
|
10154
|
+
# Mode A of the Data page's one Column mapping section, drawn only
|
|
10155
|
+
# while ✏️ Edit dataset shows it; every other run reads the same
|
|
10156
|
+
# picks from their keys.
|
|
10111
10157
|
mapping_host=mapping_body_slot,
|
|
10158
|
+
render_mapping=data_view and editing,
|
|
10112
10159
|
# A corpus that publishes its schema starts from it, not from
|
|
10113
10160
|
# auto-detection. The panels stay editable — this only changes what
|
|
10114
10161
|
# they start at.
|
|
@@ -10154,6 +10201,11 @@ def _run_app() -> None:
|
|
|
10154
10201
|
_render_dataset_load_failure(
|
|
10155
10202
|
_dataset_display_name(_dataset_owner), mapping_problems
|
|
10156
10203
|
)
|
|
10204
|
+
# The mapping panels on ✏️ Edit dataset are a draft until ✅ Save
|
|
10205
|
+
# changes, and the dataset keeps the mapping that cannot load — so the
|
|
10206
|
+
# editor needs its Save here too, or a field fixed there never applies.
|
|
10207
|
+
if data_view and editing and mapping_editor_rendered:
|
|
10208
|
+
_render_builtin_editor_footer(editor_footer_slot)
|
|
10157
10209
|
_render_offpage_setup_notice(data_view)
|
|
10158
10210
|
_finish_page()
|
|
10159
10211
|
_render_datasets_table(None, None, None)
|
|
@@ -19,7 +19,7 @@ import json
|
|
|
19
19
|
from collections.abc import Iterable, Mapping
|
|
20
20
|
from dataclasses import dataclass, field
|
|
21
21
|
|
|
22
|
-
from .constants import SAMPLE_INDEX
|
|
22
|
+
from .constants import SAMPLE_INDEX, WORD_KEY
|
|
23
23
|
|
|
24
24
|
#: How a canonical column came to be.
|
|
25
25
|
MAPPED = "mapped" # read from one source column, unchanged
|
|
@@ -104,6 +104,7 @@ _CANONICAL_LABELS: dict[str, str] = {
|
|
|
104
104
|
"line_idx": "Line",
|
|
105
105
|
"text": "Word",
|
|
106
106
|
"word_id": "Word #",
|
|
107
|
+
WORD_KEY: "Word ID",
|
|
107
108
|
"x": "X",
|
|
108
109
|
"y": "Y",
|
|
109
110
|
"saccade_amplitude": "Saccade amplitude (px)",
|
|
@@ -147,6 +148,7 @@ ROLE_LABELS: dict[str, str] = {
|
|
|
147
148
|
"unique_text_id": "Text",
|
|
148
149
|
"screen_id": "Screen",
|
|
149
150
|
"word_id": "Word #",
|
|
151
|
+
WORD_KEY: "Word ID",
|
|
150
152
|
"text": "Word",
|
|
151
153
|
"line_idx": "Line",
|
|
152
154
|
"x": "X",
|
|
@@ -205,10 +207,23 @@ IDENTITY_COLUMNS = frozenset(
|
|
|
205
207
|
"unique_text_id",
|
|
206
208
|
"screen_id",
|
|
207
209
|
"word_id",
|
|
210
|
+
WORD_KEY,
|
|
208
211
|
}
|
|
209
212
|
)
|
|
210
213
|
|
|
211
214
|
|
|
215
|
+
def _text_word_id_entries(entry: SourceName | None, how: str) -> dict:
|
|
216
|
+
"""The entries for a Word ID read as text (:meth:`ColumnNames.with_rewrites`),
|
|
217
|
+
from the ``word_id`` entry the schema implied; none unless that names the
|
|
218
|
+
file's column."""
|
|
219
|
+
if entry is None or entry.kind != MAPPED or len(entry.sources) != 1:
|
|
220
|
+
return {}
|
|
221
|
+
return {
|
|
222
|
+
WORD_KEY: SourceName(entry.sources, MAPPED),
|
|
223
|
+
"word_id": SourceName((), GENERATED, entry.display + how),
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
212
227
|
def canonical_label(column) -> str:
|
|
213
228
|
"""A readable label for a column the app made (a measure, a run, an angle …)."""
|
|
214
229
|
from .data import READING_MEASURE_FIELDS
|
|
@@ -446,9 +461,17 @@ class ColumnNames:
|
|
|
446
461
|
``table``) marked converted: a padded id, a shifted word id, positions
|
|
447
462
|
filled from word boxes are no longer what the file held under its name,
|
|
448
463
|
so the column is labelled "<source><how>" and exported under its
|
|
449
|
-
internal name.
|
|
464
|
+
internal name.
|
|
465
|
+
|
|
466
|
+
A rewrite of :data:`~scanpath_studio.constants.WORD_KEY` is a Word ID
|
|
467
|
+
read as text: the text is the file's column, so ``word_key`` takes
|
|
468
|
+
the Word ID's source name, and ``word_id`` — the number the app gave
|
|
469
|
+
each text id — becomes generated, "<source><how>" its note."""
|
|
450
470
|
changed = dict(self.entries)
|
|
451
471
|
for rewritten_table, column, how in rewrites or ():
|
|
472
|
+
if rewritten_table == table and column == WORD_KEY:
|
|
473
|
+
changed.update(_text_word_id_entries(changed.get("word_id"), how))
|
|
474
|
+
continue
|
|
452
475
|
entry = changed.get(column)
|
|
453
476
|
if rewritten_table != table or entry is None or entry.kind != MAPPED:
|
|
454
477
|
continue
|
|
@@ -267,15 +267,50 @@ REGISTER: tuple[Computation, ...] = (
|
|
|
267
267
|
unit="px",
|
|
268
268
|
precedence="Only when x/y are absent; recorded coordinates always win.",
|
|
269
269
|
missing=(
|
|
270
|
-
"No matching word box, or no word id (blank, or
|
|
271
|
-
"fixation keeps no coordinates — a missing id never
|
|
272
|
-
"with none. No x/y
|
|
273
|
-
"
|
|
270
|
+
"No matching word box, or no word id (blank, or a text id that names "
|
|
271
|
+
"no word) ⇒ the fixation keeps no coordinates — a missing id never "
|
|
272
|
+
"matches a box with none. No x/y, text Word IDs involved "
|
|
273
|
+
"(`norm.text_word_ids`) and not one fixation placed ⇒ "
|
|
274
|
+
"UnplacedFixationsError, never an empty figure."
|
|
274
275
|
),
|
|
275
276
|
tiers="A, C",
|
|
276
277
|
status=STATUS_VERIFIED,
|
|
277
278
|
consumers=(_UI, _API, _CLI),
|
|
278
|
-
tests=("tests/test_data.py",),
|
|
279
|
+
tests=("tests/test_data.py", "tests/test_text_word_ids.py"),
|
|
280
|
+
),
|
|
281
|
+
Computation(
|
|
282
|
+
id="norm.text_word_ids",
|
|
283
|
+
name="Text word IDs",
|
|
284
|
+
category=CATEGORY_NORMALIZATION,
|
|
285
|
+
summary="Read a Word ID column of text (w1, Hello_3) as keys, and number them.",
|
|
286
|
+
formula=(
|
|
287
|
+
"A Word ID column with filled cells and not one number is text: "
|
|
288
|
+
"each cell, stripped, is kept in `word_key`. Words: `word_id` = 1, "
|
|
289
|
+
"2, … per text (`text_id`, else the trial; and screen), one number "
|
|
290
|
+
"for every reader: the keys of the reading that lists the most of "
|
|
291
|
+
"them in its row order, then any other reading's keys in the order "
|
|
292
|
+
"the table first lists them. Fixations: `word_id` = the `word_id` "
|
|
293
|
+
"of the word whose key equals the fixation's exactly, in the same "
|
|
294
|
+
"trial."
|
|
295
|
+
),
|
|
296
|
+
code="scanpath_studio/data.py:match_text_word_ids",
|
|
297
|
+
output="word_id, word_key",
|
|
298
|
+
grouping="Words: (text_id[, screen_id]); fixations: (participant_id, trial_id[, screen_id])",
|
|
299
|
+
precedence=(
|
|
300
|
+
"Only for a column with no number in it: a column with any number is "
|
|
301
|
+
"read as numbers, its other cells as blanks."
|
|
302
|
+
),
|
|
303
|
+
missing=(
|
|
304
|
+
"A blank cell or `.` is no key, so no number. A fixation key its "
|
|
305
|
+
"trial does not list ⇒ no word, warned with the count. A text id "
|
|
306
|
+
"never matches a number: numeric fixation ids against text word ids "
|
|
307
|
+
"are set aside (warned), and with x/y the word is then assigned by "
|
|
308
|
+
"geometry. A key a trial lists twice is one word."
|
|
309
|
+
),
|
|
310
|
+
tiers="A, C",
|
|
311
|
+
status=STATUS_VERIFIED,
|
|
312
|
+
consumers=(_UI, _API, _CLI, _EXPORT),
|
|
313
|
+
tests=("tests/test_text_word_ids.py",),
|
|
279
314
|
),
|
|
280
315
|
Computation(
|
|
281
316
|
id="norm.participant_metadata",
|
|
@@ -13,6 +13,14 @@ PACKAGE_NAME = "scanpath_studio"
|
|
|
13
13
|
#: number, so the plot can colour by order without calling it time.
|
|
14
14
|
SAMPLE_INDEX = "sample_index"
|
|
15
15
|
|
|
16
|
+
#: A Word ID the data spells as text (``w1``, ``Hello_3``) rather than as a
|
|
17
|
+
#: number, as the file spells it. The numeric ``word_id`` every consumer joins
|
|
18
|
+
#: on is then the word's position in its text, one number for every reader
|
|
19
|
+
#: (`data.normalize_words`), and a fixation's is looked up through this key
|
|
20
|
+
#: (`data.match_text_word_ids`). A
|
|
21
|
+
#: column the user sees and exports, under the Word ID column's own name.
|
|
22
|
+
WORD_KEY = "word_key"
|
|
23
|
+
|
|
16
24
|
|
|
17
25
|
# --- Experimental features ----------------------------------------------------
|
|
18
26
|
# Features that are built but not yet validated or fully integrated are off
|
|
@@ -849,7 +857,8 @@ RAW_GAZE_LINK_FOR_KEY = "_raw_gaze_link_for"
|
|
|
849
857
|
#: metadata tables, preprocessing). Both are built every run and switched by key,
|
|
850
858
|
#: for exactly the reason the page itself is (see below): the editor is nothing
|
|
851
859
|
#: but widgets that drive `prepare_data`, and Streamlit drops the key of a widget
|
|
852
|
-
#: that did not render.
|
|
860
|
+
#: that did not render. The built-in sources' column-mapping panels are the
|
|
861
|
+
#: exception, drawn only while it is open (`controls.column_mapping_from_state`).
|
|
853
862
|
DATASET_EDITOR_OPEN_KEY = "_dataset_editor_open"
|
|
854
863
|
DATA_OVERVIEW_KEY = "data_overview"
|
|
855
864
|
DATA_EDITOR_KEY = "data_dataset_editor"
|
|
@@ -859,8 +868,8 @@ DATA_EDITOR_OFFSCREEN_KEY = "data_dataset_editor_offscreen"
|
|
|
859
868
|
# that view is active, off-screen otherwise.
|
|
860
869
|
#
|
|
861
870
|
# The setup widgets (the loaders' directory input and ⬇ Download button, the
|
|
862
|
-
# source options
|
|
863
|
-
#
|
|
871
|
+
# source options) *drive* the load on every rerun, and Streamlit drops the key
|
|
872
|
+
# of a widget that did not render. A
|
|
864
873
|
# page body would only execute while its view is selected, so the page is
|
|
865
874
|
# rendered every run either way and simply hidden — `styles.py` gives the
|
|
866
875
|
# off-screen key `display: none` — rather than splitting every loader into a
|