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.
- installer_hooks/hook-OpenGL.py +73 -0
- installer_hooks/hook-fonticon_fa6.py +8 -0
- installer_hooks/hook-freetype.py +4 -0
- installer_hooks/hook-ilpy.py +5 -0
- installer_hooks/hook-ipykernel.py +1 -0
- installer_hooks/hook-motile_tracker.py +5 -0
- installer_hooks/hook-napari.py +19 -0
- installer_hooks/hook-parso.py +5 -0
- installer_hooks/hook-superqt.py +7 -0
- installer_hooks/hook-vispy.py +11 -0
- motile_tracker/__init__.py +5 -0
- motile_tracker/_version.py +21 -0
- motile_tracker/application_menus/__init__.py +3 -0
- motile_tracker/application_menus/editing_menu.py +100 -0
- motile_tracker/application_menus/main_app.py +26 -0
- motile_tracker/application_menus/menu_widget.py +32 -0
- motile_tracker/data_model/__init__.py +4 -0
- motile_tracker/data_model/action_history.py +66 -0
- motile_tracker/data_model/actions.py +327 -0
- motile_tracker/data_model/node_type.py +11 -0
- motile_tracker/data_model/solution_tracks.py +186 -0
- motile_tracker/data_model/tracks.py +706 -0
- motile_tracker/data_model/tracks_controller.py +602 -0
- motile_tracker/data_views/__init__.py +7 -0
- motile_tracker/data_views/views/__init__.py +0 -0
- motile_tracker/data_views/views/layers/__init__.py +0 -0
- motile_tracker/data_views/views/layers/track_graph.py +133 -0
- motile_tracker/data_views/views/layers/track_labels.py +460 -0
- motile_tracker/data_views/views/layers/track_points.py +237 -0
- motile_tracker/data_views/views/layers/tracks_layer_group.py +168 -0
- motile_tracker/data_views/views/tree_view/__init__.py +0 -0
- motile_tracker/data_views/views/tree_view/flip_axes_widget.py +29 -0
- motile_tracker/data_views/views/tree_view/navigation_widget.py +186 -0
- motile_tracker/data_views/views/tree_view/tree_view_feature_widget.py +60 -0
- motile_tracker/data_views/views/tree_view/tree_view_mode_widget.py +57 -0
- motile_tracker/data_views/views/tree_view/tree_widget.py +749 -0
- motile_tracker/data_views/views/tree_view/tree_widget_utils.py +247 -0
- motile_tracker/data_views/views_coordinator/__init__.py +0 -0
- motile_tracker/data_views/views_coordinator/node_selection_list.py +65 -0
- motile_tracker/data_views/views_coordinator/tracks_list.py +239 -0
- motile_tracker/data_views/views_coordinator/tracks_viewer.py +246 -0
- motile_tracker/example_data.py +284 -0
- motile_tracker/import_export/__init__.py +2 -0
- motile_tracker/import_export/load_tracks.py +271 -0
- motile_tracker/import_export/menus/__init__.py +0 -0
- motile_tracker/import_export/menus/csv_widget.py +213 -0
- motile_tracker/import_export/menus/import_external_tracks_dialog.py +216 -0
- motile_tracker/import_export/menus/measurement_widget.py +100 -0
- motile_tracker/import_export/menus/metadata_menu.py +58 -0
- motile_tracker/import_export/menus/segmentation_widget.py +193 -0
- motile_tracker/launcher.py +91 -0
- motile_tracker/motile/backend/__init__.py +3 -0
- motile_tracker/motile/backend/motile_run.py +281 -0
- motile_tracker/motile/backend/solve.py +127 -0
- motile_tracker/motile/backend/solver_params.py +50 -0
- motile_tracker/motile/menus/__init__.py +1 -0
- motile_tracker/motile/menus/motile_widget.py +185 -0
- motile_tracker/motile/menus/param_values.py +76 -0
- motile_tracker/motile/menus/params_editor.py +183 -0
- motile_tracker/motile/menus/params_viewer.py +124 -0
- motile_tracker/motile/menus/run_editor.py +255 -0
- motile_tracker/motile/menus/run_viewer.py +152 -0
- motile_tracker/napari.yaml +46 -0
- motile_tracker-0.0.1.dev0.dist-info/METADATA +128 -0
- motile_tracker-0.0.1.dev0.dist-info/RECORD +69 -0
- motile_tracker-0.0.1.dev0.dist-info/WHEEL +5 -0
- motile_tracker-0.0.1.dev0.dist-info/entry_points.txt +5 -0
- motile_tracker-0.0.1.dev0.dist-info/licenses/LICENSE +28 -0
- 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 @@
|
|
|
1
|
+
hiddenimports = ["ipykernel.datapub"]
|
|
@@ -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,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,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,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,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
|