magscope 0.1.0__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.
magscope/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ from magscope.utils import (
2
+ AcquisitionMode,
3
+ crop_stack_to_rois,
4
+ date_timestamp_str,
5
+ Message,
6
+ numpy_type_to_qt_image_type,
7
+ PoolVideoFlag,
8
+ registerwithscript,
9
+ Units,
10
+ )
11
+ import magscope.gui
12
+ from magscope.gui import ControlPanelBase, WindowManager, TimeSeriesPlotBase
13
+ from magscope.processes import ManagerProcessBase
14
+ from magscope.hardware import HardwareManagerBase
15
+ from magscope.camera import CameraBase, CameraManager
16
+ from magscope.datatypes import MatrixBuffer
17
+ from magscope.scope import MagScope
18
+ from magscope.scripting import Script
magscope/beadlock.py ADDED
@@ -0,0 +1,231 @@
1
+ from math import copysign, isnan
2
+ import numpy as np
3
+ from time import time
4
+
5
+ from magscope.gui import WindowManager
6
+ from magscope.processes import ManagerProcessBase
7
+ from magscope.utils import Message, registerwithscript
8
+
9
+
10
+ class BeadLockManager(ManagerProcessBase):
11
+ def __init__(self):
12
+ super().__init__()
13
+
14
+ # XY-Lock Properties
15
+ self.xy_lock_on: bool = False
16
+ self.xy_lock_interval: float
17
+ self.xy_lock_max: float
18
+ self._xy_lock_last_time: float = 0.
19
+ self._xy_lock_pending_moves: list[int] = []
20
+
21
+ # Z-Lock Properties
22
+ self.z_lock_on: bool = False
23
+ self.z_lock_bead: int = 0
24
+ self.z_lock_target: float | None = None
25
+ self.z_lock_interval: float
26
+ self.z_lock_max: float
27
+ self._z_lock_last_time: float = 0.
28
+
29
+ def setup(self):
30
+ self.xy_lock_interval = self.settings['xy-lock default interval']
31
+ self.xy_lock_max = self.settings['xy-lock default max']
32
+ self.z_lock_interval = self.settings['z-lock default interval']
33
+ self.z_lock_max = self.settings['z-lock default max']
34
+
35
+ def do_main_loop(self):
36
+ # XY-Lock Enabled
37
+ if self.xy_lock_on:
38
+ # Timer
39
+ if (now:=time()) - self._xy_lock_last_time > self.xy_lock_interval:
40
+ self.do_xy_lock(now=now)
41
+
42
+ # Z-Lock Enabled
43
+ if self.z_lock_on:
44
+ # Timer
45
+ if (now:=time()) - self._z_lock_last_time > self.z_lock_interval:
46
+ self.do_z_lock(now=now)
47
+
48
+ @registerwithscript('do_xy_lock')
49
+ def do_xy_lock(self, now=None):
50
+ """ Centers the bead-rois based on their tracked position """
51
+
52
+ # Gather information
53
+ width = self.settings['bead roi width']
54
+ half_width = width // 2
55
+ tracks = self.tracks_buffer.peak_unsorted().copy()
56
+ if now is None: now = time()
57
+ self._xy_lock_last_time = now
58
+
59
+ # For each bead calculate if/how much to move
60
+ for id, roi in self.bead_rois.items():
61
+
62
+ # Get the track for this bead
63
+ track = tracks[tracks[:, 4] == id, :]
64
+
65
+ # Check there is track data
66
+ if track.shape[0] == 0:
67
+ continue
68
+
69
+ # Get the latest position
70
+ idx = np.argmax(track[:, 0])
71
+ t, x, y, roi_x, roi_y = track[idx, [0, 1, 2, 5, 6]].tolist()
72
+
73
+ # Check the position is valid
74
+ if isnan(t) or isnan(x) or isnan(y):
75
+ continue
76
+
77
+ # Check the position was recent
78
+ if now - t > 3*self.xy_lock_interval:
79
+ continue
80
+
81
+ # Check the bead-roi is current
82
+ if roi[0] != int(roi_x) or roi[2] != int(roi_y):
83
+ continue
84
+
85
+ # Check the bead started the last move
86
+ if id in self._xy_lock_pending_moves:
87
+ continue
88
+
89
+ # Calculate the move
90
+ nm_per_px = self.camera_type.nm_per_px / self.settings['magnification']
91
+ dx = (x / nm_per_px) - half_width - roi_x
92
+ dy = (y / nm_per_px) - half_width - roi_y
93
+ if abs(dx) <= 1:
94
+ dx = 0.
95
+ if abs(dy) <= 1:
96
+ dy = 0.
97
+ dx = round(dx)
98
+ dy = round(dy)
99
+
100
+ # Limit movement to the maximum threshold
101
+ dx = copysign(min(abs(dx), self.xy_lock_max), dx)
102
+ dy = copysign(min(abs(dy), self.xy_lock_max), dy)
103
+
104
+ # Move the bead as needed
105
+ if abs(dx) > 0. or abs(dy) > 0.:
106
+ self._xy_lock_pending_moves.append(id)
107
+ message = Message(
108
+ to=WindowManager,
109
+ meth=WindowManager.move_bead,
110
+ args=(id, dx, dy)
111
+ )
112
+ self.send_ipc(message)
113
+
114
+ @registerwithscript('do_z_lock')
115
+ def do_z_lock(self, now=None):
116
+ # Gather information
117
+ if now is None: now = time()
118
+ self._z_lock_last_time = now
119
+
120
+ raise NotImplementedError
121
+
122
+ def set_bead_rois(self, value: dict[int, tuple[int, int, int, int]]):
123
+ super().set_bead_rois(value)
124
+
125
+ # Check if any of the beads have been deleted
126
+ keys = list(self._xy_lock_pending_moves) # copy
127
+ for id in keys:
128
+ if id not in self.bead_rois:
129
+ self._xy_lock_pending_moves.pop(id)
130
+
131
+ def remove_bead_from_xy_lock_pending_moves(self, id: int):
132
+ if id in self._xy_lock_pending_moves:
133
+ self._xy_lock_pending_moves.remove(id)
134
+
135
+ @registerwithscript('set_xy_lock_on')
136
+ def set_xy_lock_on(self, value: bool):
137
+ self.xy_lock_on = value
138
+
139
+ from magscope.gui import WindowManager
140
+ message = Message(
141
+ to=WindowManager,
142
+ meth=WindowManager.update_xy_lock_enabled,
143
+ args=(value,)
144
+ )
145
+ self.send_ipc(message)
146
+
147
+ @registerwithscript('set_xy_lock_interval')
148
+ def set_xy_lock_interval(self, value: float):
149
+ self.xy_lock_interval = value
150
+
151
+ from magscope.gui import WindowManager
152
+ message = Message(
153
+ to=WindowManager,
154
+ meth=WindowManager.update_xy_lock_interval,
155
+ args=(value,)
156
+ )
157
+ self.send_ipc(message)
158
+
159
+ @registerwithscript('set_xy_lock_max')
160
+ def set_xy_lock_max(self, value: float):
161
+ value = max(1, round(value))
162
+ self.xy_lock_max = value
163
+
164
+ from magscope.gui import WindowManager
165
+ message = Message(
166
+ to=WindowManager,
167
+ meth=WindowManager.update_xy_lock_max,
168
+ args=(value,)
169
+ )
170
+ self.send_ipc(message)
171
+
172
+ @registerwithscript('set_z_lock_on')
173
+ def set_z_lock_on(self, value: bool):
174
+ self.z_lock_on = value
175
+
176
+ from magscope.gui import WindowManager
177
+ message = Message(
178
+ to=WindowManager,
179
+ meth=WindowManager.update_z_lock_enabled,
180
+ args=(value,)
181
+ )
182
+ self.send_ipc(message)
183
+
184
+ @registerwithscript('set_z_lock_bead')
185
+ def set_z_lock_bead(self, value: int):
186
+ value = int(value)
187
+ self.z_lock_bead = value
188
+
189
+ from magscope.gui import WindowManager
190
+ message = Message(
191
+ to=WindowManager,
192
+ meth=WindowManager.update_z_lock_bead,
193
+ args=(value,)
194
+ )
195
+ self.send_ipc(message)
196
+
197
+ @registerwithscript('set_z_lock_target')
198
+ def set_z_lock_target(self, value: float):
199
+ self.z_lock_target = value
200
+
201
+ from magscope.gui import WindowManager
202
+ message = Message(
203
+ to=WindowManager,
204
+ meth=WindowManager.update_z_lock_target,
205
+ args=(value,)
206
+ )
207
+ self.send_ipc(message)
208
+
209
+ @registerwithscript('set_z_lock_interval')
210
+ def set_z_lock_interval(self, value: float):
211
+ self.z_lock_interval = value
212
+
213
+ from magscope.gui import WindowManager
214
+ message = Message(
215
+ to=WindowManager,
216
+ meth=WindowManager.update_z_lock_interval,
217
+ args=(value,)
218
+ )
219
+ self.send_ipc(message)
220
+
221
+ @registerwithscript('set_z_lock_max')
222
+ def set_z_lock_max(self, value: float):
223
+ self.z_lock_max = value
224
+
225
+ from magscope.gui import WindowManager
226
+ message = Message(
227
+ to=WindowManager,
228
+ meth=WindowManager.update_z_lock_max,
229
+ args=(value,)
230
+ )
231
+ self.send_ipc(message)