trajectopy 6.0.1__tar.gz → 6.2.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.
- {trajectopy-6.0.1 → trajectopy-6.2.0}/PKG-INFO +1 -1
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_gui_models.py +327 -242
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_gui_smoke.py +648 -603
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_project.py +302 -297
- trajectopy-6.2.0/trajectopy/__init__.py +245 -0
- trajectopy-6.2.0/trajectopy/__version__.py +1 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/app.py +18 -9
- trajectopy-6.2.0/trajectopy/gui/log.py +98 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/plot_manager.py +2 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/requests.py +257 -243
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/ui_manager.py +36 -2
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/models/entries.py +504 -496
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/models/result_model.py +83 -79
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/models/selection.py +4 -0
- trajectopy-6.2.0/trajectopy/gui/models/table_model.py +489 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/models/trajectory_model.py +12 -4
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/project_format.py +5 -1
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/themes.py +33 -4
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/utils.py +26 -7
- trajectopy-6.2.0/trajectopy/gui/views/entry_table_view.py +241 -0
- trajectopy-6.2.0/trajectopy/gui/views/log_panel.py +69 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/main_window.py +20 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/manual_alignment_window.py +2 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/menu_helpers.py +44 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/progress_window.py +1 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/result_context_menu.py +18 -1
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/result_selection_window.py +2 -4
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/result_table_view.py +1 -1
- trajectopy-6.2.0/trajectopy/gui/views/splash_screen.py +61 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/trajectory_context_menu.py +16 -4
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/trajectory_table_view.py +59 -58
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/user_guide.md +433 -425
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy.spec +2 -2
- trajectopy-6.0.1/trajectopy/__init__.py +0 -172
- trajectopy-6.0.1/trajectopy/__version__.py +0 -1
- trajectopy-6.0.1/trajectopy/gui/models/table_model.py +0 -152
- trajectopy-6.0.1/trajectopy/gui/views/entry_table_view.py +0 -100
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.gitattributes +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.github/workflows/ci.yml +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.github/workflows/release.yml +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.gitignore +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.pre-commit-config.yaml +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/.pylintrc +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/CITATION.cff +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/LICENSE +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/README.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_data/KITTI_ORB.traj +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_data/KITTI_SPTAM.traj +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_data/KITTI_gt.traj +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_scripts/align.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_scripts/alignment.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_scripts/ate.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_scripts/deviation_plots.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_scripts/rpe.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/example_scripts/trajectory_plot.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/installer/trajectopy.iss +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/pyproject.toml +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/requirements.txt +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/abs_dev.result +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/align_entry.result +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/generated_trajectory.traj +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/noisy_trajectory.traj +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/open_loop_trajectory.traj +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/rel_dev_meter.result +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/data/rel_dev_second.result +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_alignment.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_approximation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_architecture.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_comparison.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_context_menus.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_correlation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_fast_reading.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_gui_operations.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_inplace_contract.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_io.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_kml.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_manual_alignment.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_matching.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_merging.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_pipeline.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_positions_rotations.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_processing_edge_cases.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_processing_errors.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_readers_edge_cases.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_readers_errors.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_regressions.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_settings.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_settings_edge_cases.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_settings_serialization.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_settings_validation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_sorting.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_trajectory.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_trajectory_validation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_update_check.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/test_visualization.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/testdata.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/test/util.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/LICENSE +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/__main__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/cli/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/cli/main.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/core/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/core/parameters.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/core/positions.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/core/rotations.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/core/settings.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/core/trajectory.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/exceptions.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/file_manager.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/project_manager.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/managers/trajectory_manager.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/models/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/models/property_model.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/project.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/resources/full-icon-poppins.png +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/resources/icon-bg.ico +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/resources/icon-bg.png +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/resources/icon.png +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/update_check.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/about_window.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/alignment_edit_window.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/dof_organizer.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/json_settings_view.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/kml_dialog.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/playback_window.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/preview_plot.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/preview_plot_3d.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/properties_panel.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/views/timeline_widget.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/gui/worker.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/ascii.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/header.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/kml.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/results_io.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/rosbag.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/rosmsg.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/trajectory_io.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/io/utils.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/alignment.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/approximation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/evaluation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/interpolation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/data.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/direct.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/egrad_interface.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/equations.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/estimation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/alignment/parameters.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/approximation/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/approximation/cubic_approximation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/approximation/line_approximation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/lib/approximation/voxelizer.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/matching.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/merging.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/processing/sorting.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/py.typed +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/readers/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/readers/ascii.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/readers/rosbag.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/readers/rosmsg.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/readers/utils.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/results/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/results/alignment_result.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/results/ate_result.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/results/correlation.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/results/rpe_result.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/colors.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/common.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/definitions.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/formatting.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/geometry.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/linalg.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/numeric.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/resources.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/utils/statistics.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/visualization/__init__.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/visualization/_svg_export.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/visualization/axes.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/visualization/figure.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/visualization/plots.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy/visualization/style.py +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/core/parameters.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/core/positions.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/core/rotations.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/core/settings.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/core/trajectory.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/index.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/io/kml.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/io/results_io.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/io/rosbag.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/io/trajectory_io.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/alignment.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/approximation.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/evaluation.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/interpolation.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/matching.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/merging.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/processing/sorting.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/results/alignment_result.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/results/ate_result.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/results/rpe_result.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/visualization/display.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/api/visualization/figures.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/assets/icon.png +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/file_formats.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/index.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/javascripts/mathjax.js +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/settings.md +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/docs/stylesheets/extra.css +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/trajectopy-docs/mkdocs.yml +0 -0
- {trajectopy-6.0.1 → trajectopy-6.2.0}/uv.lock +0 -0
|
@@ -1,242 +1,327 @@
|
|
|
1
|
-
"""Table models keep the selection on the right entries, and sub-windows are freed and left open independently.
|
|
2
|
-
|
|
3
|
-
Runs headless (``QT_QPA_PLATFORM=offscreen``) and is skipped if the GUI extras are not installed.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import unittest
|
|
8
|
-
|
|
9
|
-
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
10
|
-
|
|
11
|
-
try:
|
|
12
|
-
from PySide6 import QtWidgets
|
|
13
|
-
|
|
14
|
-
from trajectopy.core.settings import PlotSettings
|
|
15
|
-
from trajectopy.gui.managers.plot_manager import PlotManager
|
|
16
|
-
from trajectopy.gui.managers.requests import (
|
|
17
|
-
PlotRequest,
|
|
18
|
-
PlotRequestType,
|
|
19
|
-
TrajectoryModelRequest,
|
|
20
|
-
TrajectoryModelRequestType,
|
|
21
|
-
UIRequest,
|
|
22
|
-
UIRequestType,
|
|
23
|
-
)
|
|
24
|
-
from trajectopy.gui.managers.ui_manager import UIManager
|
|
25
|
-
from trajectopy.gui.models.entries import TrajectoryEntry
|
|
26
|
-
from trajectopy.gui.models.selection import TrajectorySelection
|
|
27
|
-
from trajectopy.gui.models.trajectory_model import TrajectoryTableModel
|
|
28
|
-
from trajectopy.gui.views.trajectory_table_view import TrajectoryTableView
|
|
29
|
-
except ImportError: # pragma: no cover - GUI extras not installed
|
|
30
|
-
QtWidgets = None
|
|
31
|
-
|
|
32
|
-
from test.testdata import noisy_trajectory
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _entry(name: str, group_id: str | None = None) -> "TrajectoryEntry":
|
|
36
|
-
trajectory = noisy_trajectory.copy()
|
|
37
|
-
trajectory.name = name
|
|
38
|
-
entry = TrajectoryEntry(full_filename="", trajectory=trajectory)
|
|
39
|
-
if group_id is not None:
|
|
40
|
-
entry.group_id = group_id
|
|
41
|
-
return entry
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@unittest.skipIf(QtWidgets is None, "GUI dependencies not installed")
|
|
45
|
-
class TestTableModelSelection(unittest.TestCase):
|
|
46
|
-
@classmethod
|
|
47
|
-
def setUpClass(cls) -> None:
|
|
48
|
-
cls.app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])
|
|
49
|
-
|
|
50
|
-
def setUp(self) -> None:
|
|
51
|
-
self.model = TrajectoryTableModel()
|
|
52
|
-
self.view = TrajectoryTableView(parent=None, trajectory_table_model=self.model)
|
|
53
|
-
self.entries = [_entry(f"T{i}") for i in range(4)]
|
|
54
|
-
for entry in self.entries:
|
|
55
|
-
self.model.add(entry)
|
|
56
|
-
|
|
57
|
-
def selected_names(self) -> list[str]:
|
|
58
|
-
return [entry.name for entry in self.view.selected_entries]
|
|
59
|
-
|
|
60
|
-
def test_selection_stays_on_entry_when_row_above_is_removed(self):
|
|
61
|
-
self.view.
|
|
62
|
-
self.model.remove([self.entries[0].entry_id])
|
|
63
|
-
self.assertEqual(self.selected_names(), ["T1"])
|
|
64
|
-
|
|
65
|
-
def test_removing_the_selected_entry_clears_the_selection(self):
|
|
66
|
-
self.view.
|
|
67
|
-
self.model.remove([self.entries[2].entry_id])
|
|
68
|
-
self.assertEqual(self.selected_names(), [])
|
|
69
|
-
self.assertEqual([entry.name for entry in self.model.items], ["T0", "T1", "T3"])
|
|
70
|
-
|
|
71
|
-
def test_selection_follows_entry_when_reordered(self):
|
|
72
|
-
self.view.
|
|
73
|
-
order = [self.entries[i].entry_id for i in (3, 2, 1, 0)]
|
|
74
|
-
self.model.sort_items(TrajectoryModelRequest(type=TrajectoryModelRequestType.SORT, index_list=order))
|
|
75
|
-
self.assertEqual([entry.name for entry in self.model.items], ["T3", "T2", "T1", "T0"])
|
|
76
|
-
self.assertEqual(self.selected_names(), ["T1"])
|
|
77
|
-
|
|
78
|
-
def test_entries_missing_in_the_order_are_kept(self):
|
|
79
|
-
order = [self.entries[2].entry_id, self.entries[0].entry_id]
|
|
80
|
-
self.model.sort_items(TrajectoryModelRequest(type=TrajectoryModelRequestType.SORT, index_list=order))
|
|
81
|
-
self.assertEqual([entry.name for entry in self.model.items], ["T2", "T0", "T1", "T3"])
|
|
82
|
-
|
|
83
|
-
def test_remove_group_removes_only_that_group(self):
|
|
84
|
-
grouped = [_entry("G0", group_id="group"), _entry("G1", group_id="group")]
|
|
85
|
-
for entry in grouped:
|
|
86
|
-
self.model.add(entry)
|
|
87
|
-
self.view.
|
|
88
|
-
self.model.remove_group("group")
|
|
89
|
-
self.assertEqual([entry.name for entry in self.model.items], ["T0", "T1", "T2", "T3"])
|
|
90
|
-
self.assertEqual(self.selected_names(), ["T3"])
|
|
91
|
-
|
|
92
|
-
def
|
|
93
|
-
self.
|
|
94
|
-
self.model.
|
|
95
|
-
self.assertEqual(self.model.
|
|
96
|
-
self.assertEqual(self.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
self.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
self.assertEqual(
|
|
125
|
-
self.assertEqual(self.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
self.
|
|
137
|
-
self.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
self.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
self.
|
|
155
|
-
self.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
self.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
self.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
1
|
+
"""Table models keep the selection on the right entries, and sub-windows are freed and left open independently.
|
|
2
|
+
|
|
3
|
+
Runs headless (``QT_QPA_PLATFORM=offscreen``) and is skipped if the GUI extras are not installed.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import unittest
|
|
8
|
+
|
|
9
|
+
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from PySide6 import QtCore, QtWidgets
|
|
13
|
+
|
|
14
|
+
from trajectopy.core.settings import PlotSettings
|
|
15
|
+
from trajectopy.gui.managers.plot_manager import PlotManager
|
|
16
|
+
from trajectopy.gui.managers.requests import (
|
|
17
|
+
PlotRequest,
|
|
18
|
+
PlotRequestType,
|
|
19
|
+
TrajectoryModelRequest,
|
|
20
|
+
TrajectoryModelRequestType,
|
|
21
|
+
UIRequest,
|
|
22
|
+
UIRequestType,
|
|
23
|
+
)
|
|
24
|
+
from trajectopy.gui.managers.ui_manager import UIManager
|
|
25
|
+
from trajectopy.gui.models.entries import TrajectoryEntry
|
|
26
|
+
from trajectopy.gui.models.selection import TrajectorySelection
|
|
27
|
+
from trajectopy.gui.models.trajectory_model import TrajectoryTableModel
|
|
28
|
+
from trajectopy.gui.views.trajectory_table_view import TrajectoryTableView
|
|
29
|
+
except ImportError: # pragma: no cover - GUI extras not installed
|
|
30
|
+
QtWidgets = None
|
|
31
|
+
|
|
32
|
+
from test.testdata import noisy_trajectory
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _entry(name: str, group_id: str | None = None) -> "TrajectoryEntry":
|
|
36
|
+
trajectory = noisy_trajectory.copy()
|
|
37
|
+
trajectory.name = name
|
|
38
|
+
entry = TrajectoryEntry(full_filename="", trajectory=trajectory)
|
|
39
|
+
if group_id is not None:
|
|
40
|
+
entry.group_id = group_id
|
|
41
|
+
return entry
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@unittest.skipIf(QtWidgets is None, "GUI dependencies not installed")
|
|
45
|
+
class TestTableModelSelection(unittest.TestCase):
|
|
46
|
+
@classmethod
|
|
47
|
+
def setUpClass(cls) -> None:
|
|
48
|
+
cls.app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])
|
|
49
|
+
|
|
50
|
+
def setUp(self) -> None:
|
|
51
|
+
self.model = TrajectoryTableModel()
|
|
52
|
+
self.view = TrajectoryTableView(parent=None, trajectory_table_model=self.model)
|
|
53
|
+
self.entries = [_entry(f"T{i}") for i in range(4)]
|
|
54
|
+
for entry in self.entries:
|
|
55
|
+
self.model.add(entry)
|
|
56
|
+
|
|
57
|
+
def selected_names(self) -> list[str]:
|
|
58
|
+
return [entry.name for entry in self.view.selected_entries]
|
|
59
|
+
|
|
60
|
+
def test_selection_stays_on_entry_when_row_above_is_removed(self):
|
|
61
|
+
self.view.select_entries([self.entries[1]])
|
|
62
|
+
self.model.remove([self.entries[0].entry_id])
|
|
63
|
+
self.assertEqual(self.selected_names(), ["T1"])
|
|
64
|
+
|
|
65
|
+
def test_removing_the_selected_entry_clears_the_selection(self):
|
|
66
|
+
self.view.select_entries([self.entries[2]])
|
|
67
|
+
self.model.remove([self.entries[2].entry_id])
|
|
68
|
+
self.assertEqual(self.selected_names(), [])
|
|
69
|
+
self.assertEqual([entry.name for entry in self.model.items], ["T0", "T1", "T3"])
|
|
70
|
+
|
|
71
|
+
def test_selection_follows_entry_when_reordered(self):
|
|
72
|
+
self.view.select_entries([self.entries[1]])
|
|
73
|
+
order = [self.entries[i].entry_id for i in (3, 2, 1, 0)]
|
|
74
|
+
self.model.sort_items(TrajectoryModelRequest(type=TrajectoryModelRequestType.SORT, index_list=order))
|
|
75
|
+
self.assertEqual([entry.name for entry in self.model.items], ["T3", "T2", "T1", "T0"])
|
|
76
|
+
self.assertEqual(self.selected_names(), ["T1"])
|
|
77
|
+
|
|
78
|
+
def test_entries_missing_in_the_order_are_kept(self):
|
|
79
|
+
order = [self.entries[2].entry_id, self.entries[0].entry_id]
|
|
80
|
+
self.model.sort_items(TrajectoryModelRequest(type=TrajectoryModelRequestType.SORT, index_list=order))
|
|
81
|
+
self.assertEqual([entry.name for entry in self.model.items], ["T2", "T0", "T1", "T3"])
|
|
82
|
+
|
|
83
|
+
def test_remove_group_removes_only_that_group(self):
|
|
84
|
+
grouped = [_entry("G0", group_id="group"), _entry("G1", group_id="group")]
|
|
85
|
+
for entry in grouped:
|
|
86
|
+
self.model.add(entry)
|
|
87
|
+
self.view.select_entries([self.entries[3]])
|
|
88
|
+
self.model.remove_group("group")
|
|
89
|
+
self.assertEqual([entry.name for entry in self.model.items], ["T0", "T1", "T2", "T3"])
|
|
90
|
+
self.assertEqual(self.selected_names(), ["T3"])
|
|
91
|
+
|
|
92
|
+
def test_derived_entries_are_grouped_after_their_source(self):
|
|
93
|
+
derived = self.entries[1].derive(noisy_trajectory.copy())
|
|
94
|
+
self.model.add(derived)
|
|
95
|
+
self.assertEqual(self.model.items.index(derived), 2)
|
|
96
|
+
self.assertEqual(self.model.rowCount(), 4) # T0, group of T1, T2, T3
|
|
97
|
+
group = self.model.index(1, 0)
|
|
98
|
+
self.assertEqual(self.model.group_at(group), self.entries[1].group_id)
|
|
99
|
+
self.assertEqual(self.model.rowCount(group), 2)
|
|
100
|
+
self.assertEqual(self.model.data(group), "T1 (2)")
|
|
101
|
+
|
|
102
|
+
def test_selecting_a_group_selects_its_entries(self):
|
|
103
|
+
self.model.add(self.entries[1].derive(noisy_trajectory.copy()))
|
|
104
|
+
group = self.model.index(1, 0)
|
|
105
|
+
self.view.selectionModel().select(
|
|
106
|
+
group, QtCore.QItemSelectionModel.SelectionFlag.Select | QtCore.QItemSelectionModel.SelectionFlag.Rows
|
|
107
|
+
)
|
|
108
|
+
self.assertEqual(len(self.view.selected_entries), 2)
|
|
109
|
+
self.assertEqual(set(self.view.selection.groups), {self.entries[1].group_id})
|
|
110
|
+
|
|
111
|
+
def test_group_rename_ungroup(self):
|
|
112
|
+
ids = {self.entries[0].entry_id, self.entries[2].entry_id}
|
|
113
|
+
self.view.select_entries([self.entries[2]])
|
|
114
|
+
self.model.group_entries(ids, "Run 1")
|
|
115
|
+
self.assertEqual([entry.name for entry in self.model.items], ["T0", "T2", "T1", "T3"])
|
|
116
|
+
self.assertEqual(list(self.model.groups().values()), ["Run 1"])
|
|
117
|
+
self.assertEqual(self.selected_names(), ["T2"]) # the selection follows the entry into the group
|
|
118
|
+
|
|
119
|
+
group_id = self.entries[0].group_id
|
|
120
|
+
self.model.rename_groups({group_id}, "Run 2")
|
|
121
|
+
self.assertEqual(self.model.data(self.model.index(0, 0)), "Run 2 (2)")
|
|
122
|
+
|
|
123
|
+
self.model.move_to_group({self.entries[3].entry_id}, group_id)
|
|
124
|
+
self.assertEqual([entry.name for entry in self.model.items], ["T0", "T2", "T3", "T1"])
|
|
125
|
+
self.assertEqual(self.entries[3].group_name, "Run 2")
|
|
126
|
+
|
|
127
|
+
self.model.ungroup_entries({self.entries[0].entry_id, self.entries[2].entry_id})
|
|
128
|
+
self.assertEqual(list(self.model.groups().values()), ["Run 2"]) # a named group stays with one entry
|
|
129
|
+
self.model.ungroup_entries({self.entries[3].entry_id})
|
|
130
|
+
self.assertEqual(self.model.groups(), {})
|
|
131
|
+
self.assertEqual(self.model.rowCount(), 4)
|
|
132
|
+
|
|
133
|
+
def test_dropping_entries(self):
|
|
134
|
+
self.model.group_entries({self.entries[0].entry_id, self.entries[1].entry_id}, "Run")
|
|
135
|
+
data = self.model.mimeData([self.model.entry_index(self.entries[3].entry_id)])
|
|
136
|
+
group = self.model.group_index(self.entries[0].group_id)
|
|
137
|
+
self.assertTrue(self.model.dropMimeData(data, QtCore.Qt.DropAction.MoveAction, -1, -1, group))
|
|
138
|
+
self.assertEqual(self.entries[3].group_id, self.entries[0].group_id)
|
|
139
|
+
|
|
140
|
+
# onto an entry outside of groups: a group with that entry
|
|
141
|
+
data = self.model.mimeData([self.model.entry_index(self.entries[3].entry_id)])
|
|
142
|
+
target = self.model.entry_index(self.entries[2].entry_id)
|
|
143
|
+
self.assertTrue(self.model.dropMimeData(data, QtCore.Qt.DropAction.MoveAction, -1, -1, target))
|
|
144
|
+
self.assertEqual(self.entries[3].group_id, self.entries[2].group_id)
|
|
145
|
+
self.assertEqual(len(self.model.groups()), 2)
|
|
146
|
+
|
|
147
|
+
# between the rows of the top level: out of the group
|
|
148
|
+
data = self.model.mimeData([self.model.entry_index(self.entries[3].entry_id)])
|
|
149
|
+
self.assertTrue(self.model.dropMimeData(data, QtCore.Qt.DropAction.MoveAction, 0, 0, QtCore.QModelIndex()))
|
|
150
|
+
self.assertEqual(len(self.model.groups()), 1)
|
|
151
|
+
|
|
152
|
+
def test_collapsed_groups_stay_collapsed(self):
|
|
153
|
+
self.model.group_entries({self.entries[0].entry_id, self.entries[1].entry_id}, "Run")
|
|
154
|
+
group = self.model.group_index(self.entries[0].group_id)
|
|
155
|
+
self.assertTrue(self.view.isExpanded(group))
|
|
156
|
+
self.view.collapse(group)
|
|
157
|
+
self.model.remove([self.entries[3].entry_id])
|
|
158
|
+
self.assertFalse(self.view.isExpanded(self.model.group_index(self.entries[0].group_id)))
|
|
159
|
+
|
|
160
|
+
def test_reset_clears_the_selection(self):
|
|
161
|
+
self.view.select_entries([self.entries[0]])
|
|
162
|
+
self.model.reset()
|
|
163
|
+
self.assertEqual(self.model.rowCount(), 0)
|
|
164
|
+
self.assertEqual(self.selected_names(), [])
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@unittest.skipIf(QtWidgets is None, "GUI dependencies not installed")
|
|
168
|
+
class TestSubWindows(unittest.TestCase):
|
|
169
|
+
@classmethod
|
|
170
|
+
def setUpClass(cls) -> None:
|
|
171
|
+
cls.app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])
|
|
172
|
+
|
|
173
|
+
def setUp(self) -> None:
|
|
174
|
+
self.main_window = QtWidgets.QMainWindow()
|
|
175
|
+
self.addCleanup(self.main_window.deleteLater)
|
|
176
|
+
self.selection = TrajectorySelection(entries=[_entry("T0")])
|
|
177
|
+
|
|
178
|
+
def _process_deferred_deletes(self) -> None:
|
|
179
|
+
for _ in range(5):
|
|
180
|
+
self.app.sendPostedEvents(None, 0) # 0: QEvent.DeferredDelete included
|
|
181
|
+
self.app.processEvents()
|
|
182
|
+
|
|
183
|
+
def test_closed_plot_windows_are_freed(self):
|
|
184
|
+
manager = PlotManager(parent=self.main_window)
|
|
185
|
+
request = PlotRequest(
|
|
186
|
+
type=PlotRequestType.TRAJECTORIES, trajectory_selection=self.selection, plot_settings=PlotSettings()
|
|
187
|
+
)
|
|
188
|
+
for _ in range(3):
|
|
189
|
+
manager.handle_request(request)
|
|
190
|
+
manager._windows[-1].close()
|
|
191
|
+
self._process_deferred_deletes()
|
|
192
|
+
self.assertEqual(manager._windows, [])
|
|
193
|
+
self.assertEqual(self.main_window.findChildren(QtWidgets.QMainWindow), [])
|
|
194
|
+
|
|
195
|
+
def test_opening_a_window_leaves_the_others_open(self):
|
|
196
|
+
manager = UIManager(parent=self.main_window)
|
|
197
|
+
manager.handle_request(UIRequest(type=UIRequestType.TRAJ_SETTINGS, trajectory_selection=self.selection))
|
|
198
|
+
manager.handle_request(UIRequest(type=UIRequestType.DOF_ORGANIZER, trajectory_selection=self.selection))
|
|
199
|
+
windows = self.main_window.findChildren(QtWidgets.QMainWindow)
|
|
200
|
+
self.assertEqual(len(windows), 2)
|
|
201
|
+
self.assertTrue(all(window.isVisible() for window in windows))
|
|
202
|
+
for window in windows:
|
|
203
|
+
window.close()
|
|
204
|
+
self._process_deferred_deletes()
|
|
205
|
+
self.assertEqual(self.main_window.findChildren(QtWidgets.QMainWindow), [])
|
|
206
|
+
|
|
207
|
+
def test_windows_open_where_the_main_window_is(self):
|
|
208
|
+
from trajectopy.gui.utils import center_window
|
|
209
|
+
|
|
210
|
+
available = self.main_window.screen().availableGeometry()
|
|
211
|
+
self.main_window.setGeometry(available.left() + 50, available.top() + 50, 600, 400)
|
|
212
|
+
self.main_window.show()
|
|
213
|
+
window = QtWidgets.QMainWindow(self.main_window)
|
|
214
|
+
window.resize(200, 100)
|
|
215
|
+
center_window(window)
|
|
216
|
+
self.assertEqual(window.frameGeometry().center(), self.main_window.frameGeometry().center())
|
|
217
|
+
|
|
218
|
+
self.main_window.move(available.left(), available.top()) # partly beyond the screen if centered
|
|
219
|
+
window.resize(available.width(), 100)
|
|
220
|
+
center_window(window)
|
|
221
|
+
self.assertEqual(window.frameGeometry().left(), available.left())
|
|
222
|
+
self.main_window.hide()
|
|
223
|
+
|
|
224
|
+
def _visible_windows(self) -> list:
|
|
225
|
+
return [window for window in self.main_window.findChildren(QtWidgets.QMainWindow) if window.isVisible()]
|
|
226
|
+
|
|
227
|
+
def _close_all(self) -> None:
|
|
228
|
+
for window in self.main_window.findChildren(QtWidgets.QMainWindow):
|
|
229
|
+
window.close()
|
|
230
|
+
self._process_deferred_deletes()
|
|
231
|
+
|
|
232
|
+
def test_settings_of_a_trajectory_open_only_once(self):
|
|
233
|
+
manager = UIManager(parent=self.main_window)
|
|
234
|
+
request = UIRequest(type=UIRequestType.TRAJ_SETTINGS, trajectory_selection=self.selection)
|
|
235
|
+
manager.handle_request(request)
|
|
236
|
+
first = self._visible_windows()
|
|
237
|
+
manager.handle_request(request)
|
|
238
|
+
|
|
239
|
+
self.assertEqual(self._visible_windows(), first)
|
|
240
|
+
self.assertEqual(len(first), 1)
|
|
241
|
+
|
|
242
|
+
# once closed, it opens again
|
|
243
|
+
self._close_all()
|
|
244
|
+
manager.handle_request(request)
|
|
245
|
+
self.assertEqual(len(self._visible_windows()), 1)
|
|
246
|
+
self._close_all()
|
|
247
|
+
|
|
248
|
+
def test_a_window_for_the_current_selection_replaces_the_open_one(self):
|
|
249
|
+
manager = UIManager(parent=self.main_window)
|
|
250
|
+
request = UIRequest(type=UIRequestType.DOF_ORGANIZER, trajectory_selection=self.selection)
|
|
251
|
+
manager.handle_request(request)
|
|
252
|
+
first = self._visible_windows()
|
|
253
|
+
manager.handle_request(request)
|
|
254
|
+
second = self._visible_windows()
|
|
255
|
+
|
|
256
|
+
self.assertEqual(len(second), 1)
|
|
257
|
+
self.assertIsNot(second[0], first[0])
|
|
258
|
+
self._close_all()
|
|
259
|
+
|
|
260
|
+
def test_applying_settings_keeps_values_outside_of_the_usual_range(self):
|
|
261
|
+
from trajectopy.core.settings import ProcessingSettings
|
|
262
|
+
from trajectopy.gui.views.json_settings_view import JSONViewer
|
|
263
|
+
|
|
264
|
+
settings = ProcessingSettings()
|
|
265
|
+
settings.alignment.preprocessing.max_samples = 250_000
|
|
266
|
+
settings.alignment.preprocessing.time_start = -2.5
|
|
267
|
+
viewer = JSONViewer(parent=self.main_window, settings=settings)
|
|
268
|
+
viewer.apply_changes()
|
|
269
|
+
viewer.close()
|
|
270
|
+
self._process_deferred_deletes()
|
|
271
|
+
|
|
272
|
+
self.assertEqual(settings.alignment.preprocessing.max_samples, 250_000)
|
|
273
|
+
self.assertEqual(settings.alignment.preprocessing.time_start, -2.5)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@unittest.skipIf(QtWidgets is None, "GUI dependencies not installed")
|
|
277
|
+
class TestUniqueIds(unittest.TestCase):
|
|
278
|
+
@classmethod
|
|
279
|
+
def setUpClass(cls) -> None:
|
|
280
|
+
cls.app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])
|
|
281
|
+
|
|
282
|
+
def test_a_result_file_opened_twice_gives_two_independent_entries(self):
|
|
283
|
+
import tempfile
|
|
284
|
+
|
|
285
|
+
from trajectopy.gui.models.entries import AbsoluteDeviationEntry
|
|
286
|
+
from trajectopy.gui.models.result_model import ResultTableModel
|
|
287
|
+
from trajectopy.processing.evaluation import ate
|
|
288
|
+
|
|
289
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
290
|
+
filename = os.path.join(directory, "ate.result")
|
|
291
|
+
AbsoluteDeviationEntry(deviations=ate(noisy_trajectory, noisy_trajectory, align=False)).to_file(filename)
|
|
292
|
+
first, second = AbsoluteDeviationEntry.from_file(filename), AbsoluteDeviationEntry.from_file(filename)
|
|
293
|
+
|
|
294
|
+
model = ResultTableModel()
|
|
295
|
+
model.add(first)
|
|
296
|
+
model.add(second)
|
|
297
|
+
model.remove([second.entry_id])
|
|
298
|
+
|
|
299
|
+
self.assertEqual(model.items, [first])
|
|
300
|
+
|
|
301
|
+
def test_operation_results_keep_the_current_reference(self):
|
|
302
|
+
import copy
|
|
303
|
+
|
|
304
|
+
from trajectopy.gui.managers.requests import TrajectoryModelRequest, TrajectoryModelRequestType
|
|
305
|
+
|
|
306
|
+
model = TrajectoryTableModel()
|
|
307
|
+
first, second = _entry("first"), _entry("second")
|
|
308
|
+
first.set_as_reference = True
|
|
309
|
+
model.add(first)
|
|
310
|
+
model.add(second)
|
|
311
|
+
result = copy.deepcopy(first) # e.g. an EPSG change, computed while the reference is changed
|
|
312
|
+
model.handle_request(
|
|
313
|
+
TrajectoryModelRequest(
|
|
314
|
+
type=TrajectoryModelRequestType.SET_REFERENCE, selection=TrajectorySelection(entries=[second])
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
model.handle_request(
|
|
318
|
+
TrajectoryModelRequest(
|
|
319
|
+
type=TrajectoryModelRequestType.UPDATE, selection=TrajectorySelection(entries=[result])
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
self.assertEqual([entry.set_as_reference for entry in model.items], [False, True])
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
if __name__ == "__main__":
|
|
327
|
+
unittest.main()
|