motile-tracker 0.0.1.dev0__py3-none-any.whl

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.
Files changed (69) hide show
  1. installer_hooks/hook-OpenGL.py +73 -0
  2. installer_hooks/hook-fonticon_fa6.py +8 -0
  3. installer_hooks/hook-freetype.py +4 -0
  4. installer_hooks/hook-ilpy.py +5 -0
  5. installer_hooks/hook-ipykernel.py +1 -0
  6. installer_hooks/hook-motile_tracker.py +5 -0
  7. installer_hooks/hook-napari.py +19 -0
  8. installer_hooks/hook-parso.py +5 -0
  9. installer_hooks/hook-superqt.py +7 -0
  10. installer_hooks/hook-vispy.py +11 -0
  11. motile_tracker/__init__.py +5 -0
  12. motile_tracker/_version.py +21 -0
  13. motile_tracker/application_menus/__init__.py +3 -0
  14. motile_tracker/application_menus/editing_menu.py +100 -0
  15. motile_tracker/application_menus/main_app.py +26 -0
  16. motile_tracker/application_menus/menu_widget.py +32 -0
  17. motile_tracker/data_model/__init__.py +4 -0
  18. motile_tracker/data_model/action_history.py +66 -0
  19. motile_tracker/data_model/actions.py +327 -0
  20. motile_tracker/data_model/node_type.py +11 -0
  21. motile_tracker/data_model/solution_tracks.py +186 -0
  22. motile_tracker/data_model/tracks.py +706 -0
  23. motile_tracker/data_model/tracks_controller.py +602 -0
  24. motile_tracker/data_views/__init__.py +7 -0
  25. motile_tracker/data_views/views/__init__.py +0 -0
  26. motile_tracker/data_views/views/layers/__init__.py +0 -0
  27. motile_tracker/data_views/views/layers/track_graph.py +133 -0
  28. motile_tracker/data_views/views/layers/track_labels.py +460 -0
  29. motile_tracker/data_views/views/layers/track_points.py +237 -0
  30. motile_tracker/data_views/views/layers/tracks_layer_group.py +168 -0
  31. motile_tracker/data_views/views/tree_view/__init__.py +0 -0
  32. motile_tracker/data_views/views/tree_view/flip_axes_widget.py +29 -0
  33. motile_tracker/data_views/views/tree_view/navigation_widget.py +186 -0
  34. motile_tracker/data_views/views/tree_view/tree_view_feature_widget.py +60 -0
  35. motile_tracker/data_views/views/tree_view/tree_view_mode_widget.py +57 -0
  36. motile_tracker/data_views/views/tree_view/tree_widget.py +749 -0
  37. motile_tracker/data_views/views/tree_view/tree_widget_utils.py +247 -0
  38. motile_tracker/data_views/views_coordinator/__init__.py +0 -0
  39. motile_tracker/data_views/views_coordinator/node_selection_list.py +65 -0
  40. motile_tracker/data_views/views_coordinator/tracks_list.py +239 -0
  41. motile_tracker/data_views/views_coordinator/tracks_viewer.py +246 -0
  42. motile_tracker/example_data.py +284 -0
  43. motile_tracker/import_export/__init__.py +2 -0
  44. motile_tracker/import_export/load_tracks.py +271 -0
  45. motile_tracker/import_export/menus/__init__.py +0 -0
  46. motile_tracker/import_export/menus/csv_widget.py +213 -0
  47. motile_tracker/import_export/menus/import_external_tracks_dialog.py +216 -0
  48. motile_tracker/import_export/menus/measurement_widget.py +100 -0
  49. motile_tracker/import_export/menus/metadata_menu.py +58 -0
  50. motile_tracker/import_export/menus/segmentation_widget.py +193 -0
  51. motile_tracker/launcher.py +91 -0
  52. motile_tracker/motile/backend/__init__.py +3 -0
  53. motile_tracker/motile/backend/motile_run.py +281 -0
  54. motile_tracker/motile/backend/solve.py +127 -0
  55. motile_tracker/motile/backend/solver_params.py +50 -0
  56. motile_tracker/motile/menus/__init__.py +1 -0
  57. motile_tracker/motile/menus/motile_widget.py +185 -0
  58. motile_tracker/motile/menus/param_values.py +76 -0
  59. motile_tracker/motile/menus/params_editor.py +183 -0
  60. motile_tracker/motile/menus/params_viewer.py +124 -0
  61. motile_tracker/motile/menus/run_editor.py +255 -0
  62. motile_tracker/motile/menus/run_viewer.py +152 -0
  63. motile_tracker/napari.yaml +46 -0
  64. motile_tracker-0.0.1.dev0.dist-info/METADATA +128 -0
  65. motile_tracker-0.0.1.dev0.dist-info/RECORD +69 -0
  66. motile_tracker-0.0.1.dev0.dist-info/WHEEL +5 -0
  67. motile_tracker-0.0.1.dev0.dist-info/entry_points.txt +5 -0
  68. motile_tracker-0.0.1.dev0.dist-info/licenses/LICENSE +28 -0
  69. motile_tracker-0.0.1.dev0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,73 @@
1
+ # ------------------------------------------------------------------
2
+ # Copyright (c) 2020 PyInstaller Development Team.
3
+ #
4
+ # This file is distributed under the terms of the GNU General Public
5
+ # License (version 2.0 or later).
6
+ #
7
+ # The full license is available in LICENSE.GPL.txt, distributed with
8
+ # this software.
9
+ #
10
+ # SPDX-License-Identifier: GPL-2.0-or-later
11
+ # ------------------------------------------------------------------
12
+
13
+
14
+ """
15
+ Hook for PyOpenGL 3.x versions from 3.0.0b6 up. Previous versions have a
16
+ plugin system based on pkg_resources which is problematic to handle correctly
17
+ under pyinstaller; 2.x versions used to run fine without hooks, so this one
18
+ shouldn't hurt.
19
+ """
20
+
21
+
22
+ from PyInstaller.compat import is_win, is_darwin
23
+ from PyInstaller.utils.hooks import collect_data_files, exec_statement
24
+ import os
25
+ import glob
26
+
27
+
28
+ def opengl_arrays_modules():
29
+ """
30
+ Return list of array modules for OpenGL module.
31
+ e.g. 'OpenGL.arrays.vbo'
32
+ """
33
+ statement = 'import OpenGL; print(OpenGL.__path__[0])'
34
+ opengl_mod_path = exec_statement(statement)
35
+ arrays_mod_path = os.path.join(opengl_mod_path, 'arrays')
36
+ files = glob.glob(arrays_mod_path + '/*.py')
37
+ modules = []
38
+
39
+ for f in files:
40
+ mod = os.path.splitext(os.path.basename(f))[0]
41
+ # Skip __init__ module.
42
+ if mod == '__init__':
43
+ continue
44
+ modules.append('OpenGL.arrays.' + mod)
45
+
46
+ return modules
47
+
48
+
49
+ # PlatformPlugin performs a conditional import based on os.name and
50
+ # sys.platform. PyInstaller misses this so let's add it ourselves...
51
+ if is_win:
52
+ hiddenimports = ['OpenGL.platform.win32']
53
+ elif is_darwin:
54
+ hiddenimports = ['OpenGL.platform.darwin']
55
+ # Use glx for other platforms (Linux, ...)
56
+ else:
57
+ hiddenimports = ['OpenGL.platform.glx', 'OpenGL.platform.egl']
58
+
59
+
60
+ # Arrays modules are needed too.
61
+ hiddenimports += opengl_arrays_modules()
62
+
63
+
64
+ # PyOpenGL 3.x uses ctypes to load DLL libraries. PyOpenGL windows installer
65
+ # adds necessary dll files to
66
+ # DLL_DIRECTORY = os.path.join( os.path.dirname( OpenGL.__file__ ), 'DLLS')
67
+ # PyInstaller is not able to find these dlls. Just include them all as data
68
+ # files.
69
+ if is_win:
70
+ datas = collect_data_files('OpenGL')
71
+
72
+ print("Loaded OpenGL!", hiddenimports)
73
+
@@ -0,0 +1,8 @@
1
+ from PyInstaller.utils.hooks import (collect_submodules,
2
+ collect_data_files)
3
+
4
+ datas = collect_data_files('fonticon_fa6')
5
+
6
+ hiddenimports = collect_submodules('fonticon_fa6')
7
+
8
+ print("Loaded fonticon_fa6!", datas, hiddenimports)
@@ -0,0 +1,4 @@
1
+ # Hook required for IPython autocomplete
2
+
3
+
4
+ hiddenimports = ["freetype"]
@@ -0,0 +1,5 @@
1
+ from PyInstaller.utils.hooks import collect_all
2
+
3
+ datas, binaries, hiddenimports = collect_all('ilpy.impl.solvers')
4
+
5
+ print("Loaded ilpy!", datas, binaries, hiddenimports)
@@ -0,0 +1 @@
1
+ hiddenimports = ["ipykernel.datapub"]
@@ -0,0 +1,5 @@
1
+ from PyInstaller.utils.hooks import collect_all
2
+
3
+ datas, binaries, hiddenimports = collect_all('motile_tracker')
4
+
5
+ print("Loaded motile_tracker!", datas, binaries, hiddenimports)
@@ -0,0 +1,19 @@
1
+ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
2
+
3
+ datas = collect_data_files('napari')
4
+ datas += collect_data_files('napari_builtins')
5
+ datas += collect_data_files('napari_svg')
6
+
7
+ hiddenimports = collect_submodules("napari")
8
+ hiddenimports = collect_submodules("napari_svg")
9
+
10
+ hiddenimports += [
11
+ 'napari.viewer',
12
+ 'napari.plugins',
13
+ 'napari._event_loop',
14
+ 'napari.__main__',
15
+ 'napari._qt',
16
+ 'napari._qt.qt_main_window',
17
+ 'napari_builtins',
18
+ 'napari_plugin_manager',
19
+ ]
@@ -0,0 +1,5 @@
1
+ # Hook required for IPython autocomplete
2
+
3
+ from PyInstaller.utils.hooks import collect_data_files
4
+
5
+ datas = collect_data_files('parso')
@@ -0,0 +1,7 @@
1
+ from PyInstaller.utils.hooks import collect_submodules
2
+
3
+ hiddenimports = collect_submodules('superqt')
4
+
5
+ hiddenimports += collect_submodules('superqt.fonticon')
6
+
7
+ print("Loaded superqt!", hiddenimports)
@@ -0,0 +1,11 @@
1
+ from PyInstaller.utils.hooks import collect_data_files
2
+ from vispy.app.backends import CORE_BACKENDS
3
+
4
+
5
+ datas = collect_data_files('vispy')
6
+
7
+ hiddenimports = ["vispy.ext"]
8
+
9
+ # adding all backends is required for vispy.sys_info() to work
10
+ hiddenimports += ["vispy.app.backends." + b[1] for b in CORE_BACKENDS]
11
+ hiddenimports += ["vispy.app.backends._test"]
@@ -0,0 +1,5 @@
1
+ try:
2
+ from ._version import version as __version__
3
+ except ImportError:
4
+ # package is not installed
5
+ __version__ = "uninstalled"
@@ -0,0 +1,21 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
6
+ TYPE_CHECKING = False
7
+ if TYPE_CHECKING:
8
+ from typing import Tuple
9
+ from typing import Union
10
+
11
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
12
+ else:
13
+ VERSION_TUPLE = object
14
+
15
+ version: str
16
+ __version__: str
17
+ __version_tuple__: VERSION_TUPLE
18
+ version_tuple: VERSION_TUPLE
19
+
20
+ __version__ = version = '0.0.1.dev0'
21
+ __version_tuple__ = version_tuple = (0, 0, 1, 'dev0')
@@ -0,0 +1,3 @@
1
+ from .main_app import MainApp # noqa
2
+ from .editing_menu import EditingMenu # noqa
3
+ from .menu_widget import MenuWidget # noqa
@@ -0,0 +1,100 @@
1
+ import napari
2
+ from qtpy.QtWidgets import (
3
+ QGroupBox,
4
+ QPushButton,
5
+ QVBoxLayout,
6
+ QWidget,
7
+ )
8
+
9
+ from motile_tracker.data_views.views_coordinator.tracks_viewer import TracksViewer
10
+
11
+
12
+ class EditingMenu(QWidget):
13
+ def __init__(self, viewer: napari.Viewer):
14
+ super().__init__()
15
+
16
+ self.tracks_viewer = TracksViewer.get_instance(viewer)
17
+ self.tracks_viewer.selected_nodes.list_updated.connect(self.update_buttons)
18
+ layout = QVBoxLayout()
19
+
20
+ node_box = QGroupBox("Edit Node(s)")
21
+ node_box.setMaximumHeight(60)
22
+ node_box_layout = QVBoxLayout()
23
+
24
+ self.delete_node_btn = QPushButton("Delete [D]")
25
+ self.delete_node_btn.clicked.connect(self.tracks_viewer.delete_node)
26
+ self.delete_node_btn.setEnabled(False)
27
+ # self.split_node_btn = QPushButton("Set split [S]")
28
+ # self.split_node_btn.clicked.connect(self.tracks_viewer.set_split_node)
29
+ # self.split_node_btn.setEnabled(False)
30
+ # self.endpoint_node_btn = QPushButton("Set endpoint [E]")
31
+ # self.endpoint_node_btn.clicked.connect(self.tracks_viewer.set_endpoint_node)
32
+ # self.endpoint_node_btn.setEnabled(False)
33
+ # self.linear_node_btn = QPushButton("Set linear [C]")
34
+ # self.linear_node_btn.clicked.connect(self.tracks_viewer.set_linear_node)
35
+ # self.linear_node_btn.setEnabled(False)
36
+
37
+ node_box_layout.addWidget(self.delete_node_btn)
38
+ # node_box_layout.addWidget(self.split_node_btn)
39
+ # node_box_layout.addWidget(self.endpoint_node_btn)
40
+ # node_box_layout.addWidget(self.linear_node_btn)
41
+
42
+ node_box.setLayout(node_box_layout)
43
+
44
+ edge_box = QGroupBox("Edit Edge(s)")
45
+ edge_box.setMaximumHeight(100)
46
+ edge_box_layout = QVBoxLayout()
47
+
48
+ self.delete_edge_btn = QPushButton("Break [B]")
49
+ self.delete_edge_btn.clicked.connect(self.tracks_viewer.delete_edge)
50
+ self.delete_edge_btn.setEnabled(False)
51
+ self.create_edge_btn = QPushButton("Add [A]")
52
+ self.create_edge_btn.clicked.connect(self.tracks_viewer.create_edge)
53
+ self.create_edge_btn.setEnabled(False)
54
+
55
+ edge_box_layout.addWidget(self.delete_edge_btn)
56
+ edge_box_layout.addWidget(self.create_edge_btn)
57
+
58
+ edge_box.setLayout(edge_box_layout)
59
+
60
+ self.undo_btn = QPushButton("Undo (Z)")
61
+ self.undo_btn.clicked.connect(self.tracks_viewer.undo)
62
+
63
+ self.redo_btn = QPushButton("Redo (R)")
64
+ self.redo_btn.clicked.connect(self.tracks_viewer.redo)
65
+
66
+ layout.addWidget(node_box)
67
+ layout.addWidget(edge_box)
68
+ layout.addWidget(self.undo_btn)
69
+ layout.addWidget(self.redo_btn)
70
+
71
+ self.setLayout(layout)
72
+ self.setMaximumHeight(300)
73
+
74
+ def update_buttons(self):
75
+ """Set the buttons to enabled/disabled depending on the currently selected nodes"""
76
+
77
+ n_selected = len(self.tracks_viewer.selected_nodes)
78
+ if n_selected == 0:
79
+ self.delete_node_btn.setEnabled(False)
80
+ # self.split_node_btn.setEnabled(False)
81
+ # self.endpoint_node_btn.setEnabled(False)
82
+ # self.linear_node_btn.setEnabled(False)
83
+ self.delete_edge_btn.setEnabled(False)
84
+ self.create_edge_btn.setEnabled(False)
85
+
86
+ elif n_selected == 2:
87
+ self.delete_node_btn.setEnabled(True)
88
+ # self.split_node_btn.setEnabled(True)
89
+ # self.endpoint_node_btn.setEnabled(True)
90
+ # self.linear_node_btn.setEnabled(True)
91
+ self.delete_edge_btn.setEnabled(True)
92
+ self.create_edge_btn.setEnabled(True)
93
+
94
+ else:
95
+ self.delete_node_btn.setEnabled(True)
96
+ # self.split_node_btn.setEnabled(True)
97
+ # self.endpoint_node_btn.setEnabled(True)
98
+ # self.linear_node_btn.setEnabled(True)
99
+ self.delete_edge_btn.setEnabled(False)
100
+ self.create_edge_btn.setEnabled(False)
@@ -0,0 +1,26 @@
1
+ import napari
2
+ from qtpy.QtWidgets import (
3
+ QVBoxLayout,
4
+ QWidget,
5
+ )
6
+
7
+ from motile_tracker.data_views.views.tree_view.tree_widget import TreeWidget
8
+
9
+ from .menu_widget import MenuWidget
10
+
11
+
12
+ class MainApp(QWidget):
13
+ """Combines the different tracker widgets for faster dock arrangement"""
14
+
15
+ def __init__(self, viewer: napari.Viewer):
16
+ super().__init__()
17
+
18
+ menu_widget = MenuWidget(viewer)
19
+ tree_widget = TreeWidget(viewer)
20
+
21
+ viewer.window.add_dock_widget(tree_widget, area="bottom", name="Tree View")
22
+
23
+ layout = QVBoxLayout()
24
+ layout.addWidget(menu_widget)
25
+
26
+ self.setLayout(layout)
@@ -0,0 +1,32 @@
1
+ import napari
2
+ from qtpy.QtWidgets import QScrollArea, QTabWidget, QVBoxLayout
3
+
4
+ from motile_tracker.application_menus.editing_menu import EditingMenu
5
+ from motile_tracker.data_views.views_coordinator.tracks_viewer import TracksViewer
6
+ from motile_tracker.motile.menus.motile_widget import MotileWidget
7
+
8
+
9
+ class MenuWidget(QScrollArea):
10
+ """Combines the different tracker menus into tabs for cleaner UI"""
11
+
12
+ def __init__(self, viewer: napari.Viewer):
13
+ super().__init__()
14
+
15
+ tracks_viewer = TracksViewer.get_instance(viewer)
16
+
17
+ motile_widget = MotileWidget(viewer)
18
+ editing_widget = EditingMenu(viewer)
19
+
20
+ tabwidget = QTabWidget()
21
+
22
+ tabwidget.addTab(motile_widget, "Track with Motile")
23
+ tabwidget.addTab(editing_widget, "Edit Tracks")
24
+ tabwidget.addTab(tracks_viewer.tracks_list, "Results List")
25
+
26
+ layout = QVBoxLayout()
27
+ layout.addWidget(tabwidget)
28
+
29
+ self.setWidget(tabwidget)
30
+ self.setWidgetResizable(True)
31
+
32
+ self.setLayout(layout)
@@ -0,0 +1,4 @@
1
+ from .tracks import Tracks # noqa
2
+ from .solution_tracks import SolutionTracks # noqa
3
+ from .node_type import NodeType # noqa
4
+ from .tracks_controller import TracksController # noqa
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from .actions import TracksAction
7
+
8
+
9
+ class ActionHistory:
10
+ """An action history implementing the ideas from this blog:
11
+ https://github.com/zaboople/klonk/blob/master/TheGURQ.md
12
+ Essentially, if you go back and change something after undo-ing, you can always get
13
+ back to every state if you undo far enough (instead of throwing out
14
+ the undone actions)
15
+ """
16
+
17
+ def __init__(self):
18
+ self.undo_stack: list[TracksAction] = [] # list of actions that can be undone
19
+ self.redo_stack: list[TracksAction] = [] # list of actions that can be redone
20
+
21
+ @property
22
+ def undo_pointer(self):
23
+ return len(self.undo_stack) - len(self.redo_stack) - 1
24
+
25
+ def add_new_action(self, action: TracksAction) -> None:
26
+ """Add a newly performed action to the history.
27
+ Args:
28
+ action (TracksAction): The new action to be added to the history.
29
+ """
30
+ if len(self.redo_stack) > 0:
31
+ # add all the redo stuff to the undo stack, so that both the originial and
32
+ # inverse are on the stack
33
+ self.undo_stack.extend(self.redo_stack)
34
+ self.redo_stack = []
35
+ self.undo_stack.append(action)
36
+
37
+ def undo(self) -> bool:
38
+ """Undo the last performed action
39
+
40
+ Returns:
41
+ bool: True if an action was undone, and False
42
+ if there was no previous action to undo.
43
+ """
44
+ if self.undo_pointer < 0:
45
+ return False
46
+ else:
47
+ action = self.undo_stack[self.undo_pointer]
48
+ inverse = action.inverse()
49
+ self.redo_stack.append(inverse)
50
+ return True
51
+
52
+ def redo(self) -> bool:
53
+ """Redo the last undone action
54
+
55
+ Returns:
56
+ bool: True if an action was redone, and False
57
+ if there was no undone action to redo.
58
+ """
59
+ if len(self.redo_stack) == 0:
60
+ return False
61
+ else:
62
+ action = self.redo_stack.pop(-1)
63
+ # apply the inverse but don't save it
64
+ # (the original is already on the undo stack)
65
+ action.inverse()
66
+ return True