np-workflows 1.6.89__py3-none-any.whl → 1.6.91__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.
- np_workflows/__init__.py +3 -5
- np_workflows/experiments/dynamic_routing/main.py +20 -41
- np_workflows/experiments/dynamic_routing/widgets.py +29 -24
- np_workflows/experiments/openscope_P3/P3_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_P3/__init__.py +1 -1
- np_workflows/experiments/openscope_P3/main_P3_pilot.py +66 -68
- np_workflows/experiments/openscope_barcode/__init__.py +1 -1
- np_workflows/experiments/openscope_barcode/barcode_workflow_widget.py +14 -20
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_mapping_script.py +8 -14
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_opto_script.py +121 -68
- np_workflows/experiments/openscope_barcode/main_barcode_pilot.py +69 -69
- np_workflows/experiments/openscope_loop/__init__.py +1 -1
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_mapping_script.py +8 -14
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_opto_script.py +121 -68
- np_workflows/experiments/openscope_loop/loop_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_loop/main_loop_pilot.py +66 -68
- np_workflows/experiments/openscope_psycode/__init__.py +1 -1
- np_workflows/experiments/openscope_psycode/main_psycode_pilot.py +69 -69
- np_workflows/experiments/openscope_psycode/psycode_workflow_widget.py +14 -20
- np_workflows/experiments/openscope_v2/__init__.py +1 -1
- np_workflows/experiments/openscope_v2/main_v2_pilot.py +66 -68
- np_workflows/experiments/openscope_v2/v2_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_vippo/__init__.py +1 -1
- np_workflows/experiments/openscope_vippo/main_vippo_pilot.py +66 -68
- np_workflows/experiments/openscope_vippo/vippo_workflow_widget.py +14 -20
- np_workflows/experiments/task_trained_network/__init__.py +1 -1
- np_workflows/experiments/task_trained_network/camstim_scripts/make_tt_stims.py +24 -14
- np_workflows/experiments/task_trained_network/camstim_scripts/oct22_tt_stim_script.py +54 -41
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_main_script.py +19 -22
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_mapping_script.py +8 -14
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_opto_script.py +121 -68
- np_workflows/experiments/task_trained_network/main_ttn_pilot.py +73 -68
- np_workflows/experiments/task_trained_network/ttn_session_widget.py +11 -19
- np_workflows/experiments/task_trained_network/ttn_stim_config.py +23 -19
- np_workflows/experiments/templeton/main.py +18 -41
- np_workflows/experiments/templeton/widgets.py +26 -23
- np_workflows/shared/__init__.py +1 -1
- np_workflows/shared/base_experiments.py +430 -308
- np_workflows/shared/npxc.py +85 -53
- np_workflows/shared/widgets.py +374 -224
- {np_workflows-1.6.89.dist-info → np_workflows-1.6.91.dist-info}/METADATA +7 -21
- np_workflows-1.6.91.dist-info/RECORD +48 -0
- {np_workflows-1.6.89.dist-info → np_workflows-1.6.91.dist-info}/WHEEL +2 -1
- np_workflows-1.6.91.dist-info/entry_points.txt +2 -0
- np_workflows-1.6.91.dist-info/top_level.txt +1 -0
- np_workflows/assets/images/logo_np_hab.png +0 -0
- np_workflows/assets/images/logo_np_vis.png +0 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_00.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_01.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_02.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_03.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_04.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_05.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_06.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_07.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_08.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_09.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_10.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_11.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_12.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_13.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_14.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_15.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_16.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_17.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_18.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/flash_250ms.stim +0 -20
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/gabor_20_deg_250ms.stim +0 -30
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/old_stim.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_1st.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_2nd.stim +0 -5
- np_workflows/shared/camstim_scripts/flash_250ms.stim +0 -20
- np_workflows/shared/camstim_scripts/gabor_20_deg_250ms.stim +0 -30
- np_workflows-1.6.89.dist-info/RECORD +0 -76
- np_workflows-1.6.89.dist-info/entry_points.txt +0 -4
np_workflows/shared/widgets.py
CHANGED
|
@@ -21,17 +21,20 @@ import np_workflows.shared.npxc as npxc
|
|
|
21
21
|
|
|
22
22
|
logger = np_logging.getLogger(__name__)
|
|
23
23
|
|
|
24
|
-
np_logging.getLogger(
|
|
25
|
-
np_logging.getLogger(
|
|
24
|
+
np_logging.getLogger("Comm").propagate = False
|
|
25
|
+
np_logging.getLogger("PIL").propagate = False
|
|
26
26
|
|
|
27
27
|
global_state = {}
|
|
28
28
|
"""Global variable for persisting widget states."""
|
|
29
29
|
|
|
30
|
+
|
|
30
31
|
def elapsed_time_widget() -> IPython.display.DisplayHandle | None:
|
|
31
32
|
"""Displays a clock showing the elapsed time since the cell was first run."""
|
|
32
33
|
|
|
33
34
|
clock_widget = ipw.Label("")
|
|
34
|
-
reminder_widget = ipw.Label(
|
|
35
|
+
reminder_widget = ipw.Label(
|
|
36
|
+
"Remember to restart JupyterLab and run update.bat before every experiment!"
|
|
37
|
+
)
|
|
35
38
|
global start_time
|
|
36
39
|
if "start_time" not in globals():
|
|
37
40
|
start_time = time.time()
|
|
@@ -44,11 +47,13 @@ def elapsed_time_widget() -> IPython.display.DisplayHandle | None:
|
|
|
44
47
|
elapsed_sec = time.time() - start_time
|
|
45
48
|
hours, remainder = divmod(elapsed_sec, 3600)
|
|
46
49
|
minutes, seconds = divmod(remainder, 60)
|
|
47
|
-
clock_widget.value =
|
|
48
|
-
int(hours)
|
|
50
|
+
clock_widget.value = (
|
|
51
|
+
f"Elapsed time: {int(hours):02}h {int(minutes):02}m {int(seconds):02}s"
|
|
49
52
|
)
|
|
50
53
|
if hours > 4: # ipywidgets >= 8.0
|
|
51
|
-
clock_widget.style = dict(
|
|
54
|
+
clock_widget.style = dict(
|
|
55
|
+
text_color="red",
|
|
56
|
+
)
|
|
52
57
|
time.sleep(0.2)
|
|
53
58
|
|
|
54
59
|
thread = threading.Thread(target=update_timer, args=())
|
|
@@ -61,12 +66,14 @@ def user_and_mouse_widget() -> tuple[np_session.User, np_session.Mouse]:
|
|
|
61
66
|
user_description = "User:"
|
|
62
67
|
mouse_description = "Mouse:"
|
|
63
68
|
user_widget = ipw.Select(options=npxc.lims_user_ids, description=user_description)
|
|
64
|
-
mouse_widget = ipw.Text(
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
mouse_widget = ipw.Text(
|
|
70
|
+
value=str(npxc.default_mouse_id), description=mouse_description
|
|
71
|
+
)
|
|
72
|
+
for widget, string in zip((user_widget, mouse_widget), ("user", "mouse")):
|
|
73
|
+
if selected := global_state.get(f"selected_{string}"):
|
|
67
74
|
widget.value = selected
|
|
68
75
|
with console:
|
|
69
|
-
print(f
|
|
76
|
+
print(f"Current {string}: {selected}")
|
|
70
77
|
user = np_session.User(str(user_widget.value))
|
|
71
78
|
mouse = np_session.Mouse(str(mouse_widget.value))
|
|
72
79
|
|
|
@@ -74,7 +81,7 @@ def user_and_mouse_widget() -> tuple[np_session.User, np_session.Mouse]:
|
|
|
74
81
|
if str(user) == (new := str(new_user).strip()):
|
|
75
82
|
return
|
|
76
83
|
user.__init__(new)
|
|
77
|
-
global_state[
|
|
84
|
+
global_state["selected_user"] = new
|
|
78
85
|
with console:
|
|
79
86
|
print(f"User updated: {user}")
|
|
80
87
|
|
|
@@ -83,26 +90,28 @@ def user_and_mouse_widget() -> tuple[np_session.User, np_session.Mouse]:
|
|
|
83
90
|
return
|
|
84
91
|
if len(new) < 6:
|
|
85
92
|
return
|
|
86
|
-
global_state[
|
|
93
|
+
global_state["selected_mouse"] = new
|
|
87
94
|
mouse.__init__(new)
|
|
88
95
|
with console:
|
|
89
96
|
print(f"Mouse updated: {mouse}")
|
|
90
|
-
|
|
97
|
+
|
|
91
98
|
def new_value(change) -> None:
|
|
92
|
-
if change[
|
|
99
|
+
if change["name"] != "value":
|
|
93
100
|
return
|
|
94
|
-
if (options := getattr(change[
|
|
101
|
+
if (options := getattr(change["owner"], "options", None)) and change[
|
|
102
|
+
"new"
|
|
103
|
+
] not in options:
|
|
95
104
|
return
|
|
96
|
-
if change[
|
|
105
|
+
if change["new"] == change["old"]:
|
|
97
106
|
return
|
|
98
|
-
if (desc :=
|
|
99
|
-
|
|
107
|
+
if (desc := change["owner"].description) == user_description:
|
|
108
|
+
update_user(change["new"])
|
|
100
109
|
elif desc == mouse_description:
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
update_mouse(change["new"])
|
|
111
|
+
|
|
103
112
|
user_widget.observe(new_value)
|
|
104
113
|
mouse_widget.observe(new_value)
|
|
105
|
-
|
|
114
|
+
|
|
106
115
|
IPython.display.display(ipw.VBox([user_widget, mouse_widget, console]))
|
|
107
116
|
return user, mouse
|
|
108
117
|
|
|
@@ -110,8 +119,7 @@ def user_and_mouse_widget() -> tuple[np_session.User, np_session.Mouse]:
|
|
|
110
119
|
def mtrain_widget(
|
|
111
120
|
labtracks_mouse_id: str | int | np_session.Mouse,
|
|
112
121
|
) -> IPython.display.DisplayHandle | None:
|
|
113
|
-
"""Displays a widget to view and edit MTrain regimen/stage for a mouse.
|
|
114
|
-
"""
|
|
122
|
+
"""Displays a widget to view and edit MTrain regimen/stage for a mouse."""
|
|
115
123
|
if not isinstance(labtracks_mouse_id, np_session.Mouse):
|
|
116
124
|
mtrain = np_session.MTrain(labtracks_mouse_id)
|
|
117
125
|
else:
|
|
@@ -162,9 +170,9 @@ def mtrain_widget(
|
|
|
162
170
|
reset_update_button()
|
|
163
171
|
if change["new"] is None:
|
|
164
172
|
return
|
|
165
|
-
if change["new"] != stage_label.value or str(
|
|
166
|
-
|
|
167
|
-
)
|
|
173
|
+
if change["new"] != stage_label.value or str(regimen_dropdown.value) != str(
|
|
174
|
+
regimen_label.value
|
|
175
|
+
):
|
|
168
176
|
# enable button if stage name changed, or regimen name changed (some
|
|
169
177
|
# regimens have the same stage names as other regimens)
|
|
170
178
|
update_button.disabled = False
|
|
@@ -218,20 +226,24 @@ def mtrain_widget(
|
|
|
218
226
|
|
|
219
227
|
return IPython.display.display(display)
|
|
220
228
|
|
|
229
|
+
|
|
221
230
|
def check_widget(check: str, *checks: str) -> ipw.Widget:
|
|
222
231
|
layout = ipw.Layout(min_width="600px")
|
|
223
|
-
widget = ipw.VBox(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
widget = ipw.VBox(
|
|
233
|
+
[
|
|
234
|
+
ipw.Label(check, layout=layout),
|
|
235
|
+
*(ipw.Checkbox(description=_, layout=layout) for _ in checks),
|
|
236
|
+
# ipw.Button(description="Continue", disabled=True)
|
|
237
|
+
]
|
|
238
|
+
)
|
|
228
239
|
return widget
|
|
229
240
|
|
|
241
|
+
|
|
230
242
|
def await_all_checkboxes(widget: ipw.Box) -> None:
|
|
231
243
|
while any(_.value is False for _ in widget.children if isinstance(_, ipw.Checkbox)):
|
|
232
244
|
time.sleep(0.1)
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
|
|
246
|
+
|
|
235
247
|
def check_openephys_widget() -> None:
|
|
236
248
|
check = "OpenEphys checks:"
|
|
237
249
|
checks = (
|
|
@@ -242,7 +254,8 @@ def check_openephys_widget() -> None:
|
|
|
242
254
|
)
|
|
243
255
|
IPython.display.display(widget := check_widget(check, *checks))
|
|
244
256
|
|
|
245
|
-
|
|
257
|
+
|
|
258
|
+
def check_hardware_widget() -> None:
|
|
246
259
|
check = "Stage checks:"
|
|
247
260
|
checks = (
|
|
248
261
|
"Cartridge raised (fully retract probes before raising!)",
|
|
@@ -252,181 +265,273 @@ def check_hardware_widget() -> None:
|
|
|
252
265
|
)
|
|
253
266
|
IPython.display.display(widget := check_widget(check, *checks))
|
|
254
267
|
|
|
268
|
+
|
|
255
269
|
def check_mouse_widget() -> None:
|
|
256
270
|
check = "Mouse checks before lowering cartridge:"
|
|
257
271
|
checks = (
|
|
258
272
|
"Stabilization screw",
|
|
259
|
-
(
|
|
273
|
+
(
|
|
274
|
+
"Silicon oil applied"
|
|
275
|
+
if npxc.RIG.idx == 0
|
|
276
|
+
else "Quickcast removed, agarose applied"
|
|
277
|
+
),
|
|
260
278
|
"Tail cone down",
|
|
261
279
|
"Continuity/Resistance check",
|
|
262
280
|
)
|
|
263
281
|
IPython.display.display(widget := check_widget(check, *checks))
|
|
264
282
|
|
|
283
|
+
|
|
265
284
|
def pre_stim_check_widget() -> None:
|
|
266
285
|
check = "Before running stim:"
|
|
267
286
|
checks = (
|
|
268
287
|
"Behavior cameras are in focus",
|
|
269
|
-
"Eye-tracking mirror in place",
|
|
288
|
+
"Eye-tracking mirror in place",
|
|
270
289
|
"Windows minimized on Stim computer (Win+D)",
|
|
271
290
|
"Monitor closed",
|
|
272
291
|
"Photodoc light off",
|
|
273
|
-
"Curtain down",
|
|
292
|
+
"Curtain down",
|
|
274
293
|
)
|
|
275
294
|
IPython.display.display(widget := check_widget(check, *checks))
|
|
276
|
-
|
|
295
|
+
|
|
296
|
+
|
|
277
297
|
def finishing_checks_widget() -> None:
|
|
278
298
|
check = "Finishing checks:"
|
|
279
299
|
checks = (
|
|
280
300
|
"Add quickcast etc.",
|
|
281
|
-
"Remove and water mouse",
|
|
282
|
-
"Dip probes",
|
|
301
|
+
"Remove and water mouse",
|
|
302
|
+
"Dip probes",
|
|
283
303
|
)
|
|
284
304
|
IPython.display.display(widget := check_widget(check, *checks))
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def wheel_height_widget(
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def wheel_height_widget(
|
|
308
|
+
session: np_session.PipelineSession,
|
|
309
|
+
) -> IPython.display.DisplayHandle | None:
|
|
288
310
|
"Saves wheel height to platform_json and stores in `mouse.state['wheel_height']`."
|
|
289
|
-
|
|
290
|
-
layout = ipw.Layout(max_width=
|
|
291
|
-
|
|
292
|
-
prev_height = session.mouse.state.get(
|
|
293
|
-
height_counter = ipw.BoundedFloatText(
|
|
294
|
-
|
|
311
|
+
|
|
312
|
+
layout = ipw.Layout(max_width="130px")
|
|
313
|
+
|
|
314
|
+
prev_height = session.mouse.state.get("wheel_height", 0)
|
|
315
|
+
height_counter = ipw.BoundedFloatText(
|
|
316
|
+
value=prev_height,
|
|
317
|
+
min=0,
|
|
318
|
+
max=10,
|
|
319
|
+
step=0.1,
|
|
320
|
+
description="Wheel height",
|
|
321
|
+
layout=layout,
|
|
322
|
+
)
|
|
323
|
+
save_button = ipw.Button(description="Save", button_style="warning", layout=layout)
|
|
295
324
|
|
|
296
325
|
def on_click(b):
|
|
297
326
|
session.platform_json.wheel_height = height_counter.value
|
|
298
|
-
session.mouse.state[
|
|
299
|
-
save_button.button_style =
|
|
300
|
-
save_button.description =
|
|
327
|
+
session.mouse.state["wheel_height"] = height_counter.value
|
|
328
|
+
save_button.button_style = "success"
|
|
329
|
+
save_button.description = "Saved"
|
|
330
|
+
|
|
301
331
|
save_button.on_click(on_click)
|
|
302
|
-
return IPython.display.display(ipw.VBox([height_counter,save_button]))
|
|
303
|
-
|
|
332
|
+
return IPython.display.display(ipw.VBox([height_counter, save_button]))
|
|
333
|
+
|
|
304
334
|
|
|
305
|
-
def di_widget(
|
|
335
|
+
def di_widget(
|
|
336
|
+
session: np_session.PipelineSession,
|
|
337
|
+
) -> IPython.display.DisplayHandle | None:
|
|
306
338
|
"Supply a path or a platform json instance. Saves a JSON file with the dye used in the session and a timestamp."
|
|
307
|
-
|
|
339
|
+
|
|
308
340
|
di_info: dict[str, int | str] = dict(
|
|
309
|
-
EndTime=0,
|
|
341
|
+
EndTime=0,
|
|
342
|
+
StartTime=npxc.now(),
|
|
343
|
+
dii_description="",
|
|
344
|
+
times_dipped=0,
|
|
345
|
+
previous_uses="",
|
|
310
346
|
)
|
|
311
347
|
di_info.update(session.platform_json.DiINotes)
|
|
312
|
-
|
|
313
|
-
layout = ipw.Layout(max_width=
|
|
314
|
-
dipped_counter = ipw.IntText(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
348
|
+
|
|
349
|
+
layout = ipw.Layout(max_width="180px")
|
|
350
|
+
dipped_counter = ipw.IntText(
|
|
351
|
+
value=0, min=0, max=99, description="Dipped count", layout=layout
|
|
352
|
+
)
|
|
353
|
+
usage_counter = ipw.IntText(
|
|
354
|
+
value=0, min=0, max=99, description="Previous uses", layout=layout
|
|
355
|
+
)
|
|
356
|
+
dye_dropdown = ipw.Dropdown(options=["CM-DiI 100%", "DiO"], layout=layout)
|
|
357
|
+
save_button = ipw.Button(description="Save", button_style="warning", layout=layout)
|
|
358
|
+
|
|
319
359
|
def update_di_info():
|
|
320
|
-
di_info[
|
|
321
|
-
di_info[
|
|
322
|
-
di_info[
|
|
323
|
-
di_info[
|
|
324
|
-
|
|
360
|
+
di_info["EndTime"] = npxc.now()
|
|
361
|
+
di_info["times_dipped"] = str(dipped_counter.value)
|
|
362
|
+
di_info["dii_description"] = str(dye_dropdown.value)
|
|
363
|
+
di_info["previous_uses"] = str(usage_counter.value)
|
|
364
|
+
|
|
325
365
|
def on_click(b):
|
|
326
366
|
update_di_info()
|
|
327
367
|
session.platform_json.DiINotes = di_info
|
|
328
|
-
save_button.description =
|
|
329
|
-
save_button.button_style =
|
|
330
|
-
|
|
368
|
+
save_button.description = "Saved"
|
|
369
|
+
save_button.button_style = "success"
|
|
370
|
+
|
|
331
371
|
save_button.on_click(on_click)
|
|
332
|
-
return IPython.display.display(
|
|
333
|
-
dipped_counter, dye_dropdown,
|
|
334
|
-
|
|
372
|
+
return IPython.display.display(
|
|
373
|
+
ipw.VBox([dipped_counter, dye_dropdown, usage_counter, save_button])
|
|
374
|
+
)
|
|
335
375
|
|
|
336
|
-
|
|
337
|
-
def dye_info_widget(
|
|
376
|
+
|
|
377
|
+
def dye_info_widget(
|
|
378
|
+
session: np_session.PipelineSession,
|
|
379
|
+
) -> IPython.display.DisplayHandle | None:
|
|
338
380
|
"""
|
|
339
381
|
- scan barcode or enter ID number for the dye used
|
|
340
382
|
- change dye description if incorrect (DiI, DiO)
|
|
341
383
|
- increment number of times probes were dipped this session
|
|
342
384
|
- hit `Save` to store info in platform.json
|
|
343
385
|
"""
|
|
344
|
-
|
|
386
|
+
|
|
345
387
|
di_info: dict[str, int | str] = dict(
|
|
346
|
-
EndTime=0,
|
|
388
|
+
EndTime=0,
|
|
389
|
+
StartTime=npxc.now(),
|
|
390
|
+
dii_description="",
|
|
391
|
+
times_dipped=0,
|
|
392
|
+
previous_uses="",
|
|
347
393
|
)
|
|
348
394
|
di_info.update(session.platform_json.DiINotes)
|
|
349
|
-
|
|
395
|
+
|
|
350
396
|
def width(w):
|
|
351
|
-
return ipw.Layout(max_width=f
|
|
352
|
-
|
|
353
|
-
dye_id_entry = ipw.Text(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
397
|
+
return ipw.Layout(max_width=f"{w}px")
|
|
398
|
+
|
|
399
|
+
dye_id_entry = ipw.Text(
|
|
400
|
+
value=None,
|
|
401
|
+
description="Dye ID",
|
|
402
|
+
layout=width(250),
|
|
403
|
+
placeholder="Enter ID or scan barcode",
|
|
404
|
+
)
|
|
405
|
+
ipw.Button(
|
|
406
|
+
description="Record single use", button_style="warning", layout=width(180)
|
|
407
|
+
)
|
|
408
|
+
first_usage = ipw.Text(
|
|
409
|
+
value="", description="First use", layout=width(250), disabled=True
|
|
410
|
+
)
|
|
411
|
+
dye_dropdown = ipw.Dropdown(
|
|
412
|
+
description="Description:",
|
|
413
|
+
options=np_session.Dye.descriptions,
|
|
414
|
+
layout=width(180),
|
|
415
|
+
)
|
|
416
|
+
dipped_counter = ipw.IntText(
|
|
417
|
+
value=int(di_info["times_dipped"] or 0),
|
|
418
|
+
min=0,
|
|
419
|
+
max=99,
|
|
420
|
+
description="Dipped count",
|
|
421
|
+
layout=width(150),
|
|
422
|
+
)
|
|
423
|
+
usage_counter = ipw.IntText(
|
|
424
|
+
value=int(di_info["previous_uses"] or 0),
|
|
425
|
+
min=0,
|
|
426
|
+
max=99,
|
|
427
|
+
description="Previous uses",
|
|
428
|
+
layout=width(180),
|
|
429
|
+
disabled=True,
|
|
430
|
+
)
|
|
431
|
+
save_button = ipw.Button(
|
|
432
|
+
description="Save", button_style="warning", layout=width(180)
|
|
433
|
+
)
|
|
434
|
+
if (desc := di_info["dii_description"]) in np_session.Dye.descriptions:
|
|
361
435
|
dye_dropdown.value = desc
|
|
362
|
-
|
|
436
|
+
|
|
363
437
|
def update_display(_):
|
|
364
438
|
dye = np_session.Dye(int(str(dye_id_entry.value)))
|
|
365
439
|
dye_dropdown.value = dye.description
|
|
366
440
|
usage_counter.value = dye.previous_uses
|
|
367
|
-
first_usage.value = f
|
|
368
|
-
|
|
369
|
-
|
|
441
|
+
first_usage.value = f"{dye.first_use}"
|
|
442
|
+
|
|
443
|
+
dye_id_entry.observe(update_display, "value")
|
|
444
|
+
|
|
370
445
|
def record_dye_usage():
|
|
371
446
|
dye = np_session.Dye(int(str(dye_id_entry.value)))
|
|
372
447
|
dye.description = dye_dropdown.value
|
|
373
448
|
dye.increment_uses()
|
|
374
|
-
|
|
449
|
+
|
|
375
450
|
def update_di_info():
|
|
376
|
-
di_info[
|
|
377
|
-
di_info[
|
|
378
|
-
di_info[
|
|
379
|
-
di_info[
|
|
380
|
-
|
|
451
|
+
di_info["EndTime"] = npxc.now()
|
|
452
|
+
di_info["times_dipped"] = str(dipped_counter.value)
|
|
453
|
+
di_info["dii_description"] = str(dye_dropdown.value)
|
|
454
|
+
di_info["previous_uses"] = str(usage_counter.value)
|
|
455
|
+
|
|
381
456
|
def on_click(b):
|
|
382
457
|
update_di_info()
|
|
383
458
|
record_dye_usage()
|
|
384
459
|
session.platform_json.DiINotes = di_info
|
|
385
|
-
save_button.description =
|
|
386
|
-
save_button.button_style =
|
|
387
|
-
|
|
460
|
+
save_button.description = "Saved"
|
|
461
|
+
save_button.button_style = "success"
|
|
462
|
+
|
|
388
463
|
save_button.on_click(on_click)
|
|
389
|
-
return IPython.display.display(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
464
|
+
return IPython.display.display(
|
|
465
|
+
ipw.VBox(
|
|
466
|
+
[
|
|
467
|
+
dye_id_entry,
|
|
468
|
+
dipped_counter,
|
|
469
|
+
dye_dropdown,
|
|
470
|
+
usage_counter,
|
|
471
|
+
first_usage,
|
|
472
|
+
save_button,
|
|
473
|
+
]
|
|
474
|
+
)
|
|
475
|
+
)
|
|
476
|
+
|
|
393
477
|
|
|
394
478
|
def dye_widget(session_folder: pathlib.Path) -> IPython.display.DisplayHandle | None:
|
|
395
479
|
"Supply a path - saves a JSON file with the dye used in the session and a timestamp."
|
|
396
480
|
|
|
397
481
|
dict(
|
|
398
|
-
EndTime=0,
|
|
482
|
+
EndTime=0,
|
|
483
|
+
StartTime=0,
|
|
484
|
+
dii_description="DiI",
|
|
485
|
+
times_dipped=0,
|
|
399
486
|
)
|
|
400
|
-
|
|
487
|
+
|
|
401
488
|
class DyeRecorder(np_services.JsonRecorder):
|
|
402
|
-
log_name = f
|
|
489
|
+
log_name = f"{session_folder.name}_dye.json"
|
|
403
490
|
log_root = session_folder
|
|
404
491
|
|
|
405
|
-
dye_dropdown = ipw.Dropdown(options=[
|
|
406
|
-
save_button = ipw.Button(description=
|
|
492
|
+
dye_dropdown = ipw.Dropdown(options=["DiI", "DiO"])
|
|
493
|
+
save_button = ipw.Button(description="Save", button_style="warning")
|
|
494
|
+
|
|
407
495
|
def on_click(b):
|
|
408
|
-
DyeRecorder.write(
|
|
409
|
-
|
|
410
|
-
|
|
496
|
+
DyeRecorder.write(
|
|
497
|
+
dict(
|
|
498
|
+
dye=dye_dropdown.value,
|
|
499
|
+
datetime=datetime.datetime.now(),
|
|
500
|
+
time=time.time(),
|
|
501
|
+
)
|
|
502
|
+
)
|
|
503
|
+
save_button.button_style = "success"
|
|
504
|
+
save_button.description = "Saved"
|
|
505
|
+
|
|
411
506
|
save_button.on_click(on_click)
|
|
412
507
|
return IPython.display.display(ipw.VBox([dye_dropdown, save_button]))
|
|
413
508
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
509
|
+
|
|
510
|
+
ISICoords = list[dict[Literal["x", "y", "z"], float]]
|
|
511
|
+
ISISpaces = dict[Literal["image_space", "reticle_space"], ISICoords | None]
|
|
512
|
+
ISITargets = dict[
|
|
513
|
+
Literal["insertion_targets", "intended_insertion", "actual_insertion"], ISISpaces
|
|
514
|
+
]
|
|
515
|
+
|
|
417
516
|
|
|
418
517
|
def isi_targets(
|
|
419
518
|
labtracks_mouse_id: str | int | np_session.LIMS2MouseInfo,
|
|
420
|
-
)-> None | ISITargets:
|
|
421
|
-
mouse =
|
|
519
|
+
) -> None | ISITargets:
|
|
520
|
+
mouse = (
|
|
521
|
+
np_session.LIMS2MouseInfo(labtracks_mouse_id)
|
|
522
|
+
if not isinstance(labtracks_mouse_id, np_session.LIMS2MouseInfo)
|
|
523
|
+
else labtracks_mouse_id
|
|
524
|
+
)
|
|
422
525
|
if (exp_id := mouse.isi_id) is None:
|
|
423
526
|
return None
|
|
424
|
-
exps = mouse.isi_info[
|
|
425
|
-
isi = [e for e in exps if e[
|
|
426
|
-
return isi[0][
|
|
427
|
-
|
|
527
|
+
exps = mouse.isi_info["isi_experiments"]
|
|
528
|
+
isi = [e for e in exps if e["id"] == exp_id]
|
|
529
|
+
return isi[0]["targets"] if isi else None
|
|
530
|
+
|
|
531
|
+
|
|
428
532
|
def isi_widget(
|
|
429
|
-
labtracks_mouse_id: str | int | np_session.LIMS2MouseInfo,
|
|
533
|
+
labtracks_mouse_id: str | int | np_session.LIMS2MouseInfo,
|
|
534
|
+
colormap: bool = False,
|
|
430
535
|
) -> IPython.display.DisplayHandle | None:
|
|
431
536
|
"""Displays ISI target map from lims (contours only), or colormap overlay if
|
|
432
537
|
`show_colormap = True`."""
|
|
@@ -434,13 +539,13 @@ def isi_widget(
|
|
|
434
539
|
mouse_info = np_session.LIMS2MouseInfo(labtracks_mouse_id)
|
|
435
540
|
else:
|
|
436
541
|
mouse_info = labtracks_mouse_id
|
|
437
|
-
mouse_info.fetch()
|
|
542
|
+
mouse_info.fetch() # refresh in case targets were updated recently
|
|
438
543
|
|
|
439
544
|
if colormap:
|
|
440
545
|
key = "isi_image_overlay_path"
|
|
441
546
|
else:
|
|
442
547
|
key = "target_map_image_path"
|
|
443
|
-
|
|
548
|
+
|
|
444
549
|
try:
|
|
445
550
|
lims_path = mouse_info.isi_info[key]
|
|
446
551
|
except ValueError:
|
|
@@ -457,30 +562,39 @@ def isi_widget(
|
|
|
457
562
|
print(f"ISI map found for {mouse_info.np_id}:\n{path}")
|
|
458
563
|
img = PIL.Image.open(path)
|
|
459
564
|
if all_targets := isi_targets(mouse_info):
|
|
460
|
-
colors = {
|
|
565
|
+
colors = {
|
|
566
|
+
"insertion_targets": "red",
|
|
567
|
+
"intended_insertion": "yellow",
|
|
568
|
+
"actual_insertion": "blue",
|
|
569
|
+
}
|
|
461
570
|
for targets, spaces in all_targets.items():
|
|
462
|
-
coords = spaces[
|
|
571
|
+
coords = spaces["image_space"]
|
|
463
572
|
if coords is None:
|
|
464
573
|
continue
|
|
465
574
|
draw = PIL.ImageDraw.Draw(img)
|
|
466
|
-
draw.line(
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
else:
|
|
470
|
-
logger.debug(
|
|
575
|
+
draw.line(
|
|
576
|
+
[(_["x"], _["y"]) for _ in coords], fill=colors[targets], width=3
|
|
577
|
+
)
|
|
578
|
+
else:
|
|
579
|
+
logger.debug(
|
|
580
|
+
"No ISI targets found for %r in lims, ISI experiment id %s",
|
|
581
|
+
mouse_info,
|
|
582
|
+
mouse_info.isi_id,
|
|
583
|
+
)
|
|
471
584
|
## displaying img directly no longer works (due to jupyterlab 4.0?)
|
|
472
585
|
# return IPython.display.display(img)
|
|
473
586
|
membuf = io.BytesIO()
|
|
474
|
-
img.save(membuf, format="png")
|
|
587
|
+
img.save(membuf, format="png")
|
|
475
588
|
return IPython.display.display(ipw.VBox([ipw.Image(value=membuf.getvalue())]))
|
|
476
|
-
|
|
477
589
|
|
|
478
590
|
|
|
479
591
|
def insertion_notes_widget(session: np_session.PipelineSession):
|
|
480
|
-
|
|
481
|
-
probes =
|
|
592
|
+
|
|
593
|
+
probes = "ABCDEF"
|
|
594
|
+
|
|
482
595
|
def probe(_):
|
|
483
|
-
return f
|
|
596
|
+
return f"Probe{_}"
|
|
597
|
+
|
|
484
598
|
fields = (
|
|
485
599
|
"FailedToInsert",
|
|
486
600
|
# "ProbeLocationChanged",
|
|
@@ -488,86 +602,110 @@ def insertion_notes_widget(session: np_session.PipelineSession):
|
|
|
488
602
|
# "ProbeBendingElsewhere",
|
|
489
603
|
)
|
|
490
604
|
# "NumAgarInsertions",
|
|
491
|
-
|
|
605
|
+
|
|
492
606
|
def get_notes(_):
|
|
493
|
-
return session.platform_json.InsertionNotes.get(probe(_), {}).get(
|
|
607
|
+
return session.platform_json.InsertionNotes.get(probe(_), {}).get("Notes", "")
|
|
608
|
+
|
|
494
609
|
def get_field(_, field):
|
|
495
610
|
return session.platform_json.InsertionNotes.get(probe(_), {}).get(field, None)
|
|
496
|
-
|
|
497
|
-
def disp_str(s):
|
|
498
|
-
matches = re.finditer(
|
|
499
|
-
|
|
611
|
+
|
|
612
|
+
def disp_str(s): # split PascalCase fieldname into 'Title case' words
|
|
613
|
+
matches = re.finditer(
|
|
614
|
+
".+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)", s
|
|
615
|
+
)
|
|
616
|
+
return " ".join([m.group(0) for m in matches]).lower().capitalize()
|
|
617
|
+
|
|
500
618
|
def save_str(s):
|
|
501
|
-
return
|
|
502
|
-
|
|
619
|
+
return "".join([_.capitalize() for _ in s.split(" ")])
|
|
620
|
+
|
|
503
621
|
def row(*args):
|
|
504
622
|
return ipw.HBox([*args])
|
|
623
|
+
|
|
505
624
|
def probe_row(p):
|
|
506
|
-
return row(
|
|
507
|
-
|
|
625
|
+
return row(
|
|
626
|
+
ipw.Text(
|
|
627
|
+
value=get_notes(p),
|
|
628
|
+
placeholder="Insertion notes",
|
|
629
|
+
description=disp_str(probe(p).strip("Probe ")),
|
|
630
|
+
layout=ipw.Layout(width="auto", min_width="400px"),
|
|
631
|
+
),
|
|
632
|
+
*(
|
|
633
|
+
ipw.Checkbox(value=get_field(p, field), description=disp_str(field))
|
|
634
|
+
for field in fields
|
|
635
|
+
),
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
button = ipw.Button(description="Save", button_style="warning")
|
|
508
639
|
console = ipw.Output()
|
|
509
|
-
|
|
640
|
+
|
|
510
641
|
rows = [probe_row(p) for p in probes]
|
|
511
642
|
widget = ipw.VBox([*rows, button, console])
|
|
512
|
-
|
|
643
|
+
|
|
513
644
|
def save(b):
|
|
514
645
|
d = {}
|
|
515
646
|
for letter, row in zip(probes, rows):
|
|
516
647
|
p = d.get(probe(letter), {})
|
|
517
648
|
for widget in row.children:
|
|
518
649
|
if isinstance(widget, ipw.Text):
|
|
519
|
-
p[
|
|
650
|
+
p["Notes"] = widget.value
|
|
520
651
|
elif isinstance(widget, ipw.Checkbox):
|
|
521
652
|
p[save_str(widget.description)] = widget.value
|
|
522
653
|
else:
|
|
523
654
|
continue
|
|
524
655
|
if p:
|
|
525
|
-
d[probe(letter)] = p
|
|
526
|
-
|
|
527
|
-
session.platform_json.InsertionNotes = d
|
|
656
|
+
d[probe(letter)] = p
|
|
657
|
+
|
|
658
|
+
session.platform_json.InsertionNotes = d
|
|
528
659
|
with console:
|
|
529
|
-
print(
|
|
530
|
-
button.button_style =
|
|
531
|
-
|
|
660
|
+
print("Updated notes")
|
|
661
|
+
button.button_style = "success"
|
|
662
|
+
|
|
532
663
|
button.on_click(save)
|
|
533
664
|
return IPython.display.display(widget)
|
|
534
665
|
|
|
535
666
|
|
|
536
667
|
def probe_depth_widget(session: np_session.PipelineSession):
|
|
537
|
-
|
|
538
|
-
probes =
|
|
539
|
-
|
|
668
|
+
|
|
669
|
+
probes = "ABCDEF"
|
|
670
|
+
|
|
540
671
|
def coords():
|
|
541
672
|
return session.platform_json.manipulator_coordinates
|
|
542
|
-
|
|
673
|
+
|
|
543
674
|
if not coords():
|
|
544
675
|
logger.warning("No photodocs have been captured yet.")
|
|
545
|
-
|
|
676
|
+
|
|
546
677
|
def probe_coords(img):
|
|
547
678
|
return coords().get(img, dict.fromkeys(probes, dict(x=None, y=None, z=None)))
|
|
679
|
+
|
|
548
680
|
def field_str(s):
|
|
549
|
-
return
|
|
550
|
-
|
|
681
|
+
return "_".join(s.split(" ")).lower() + "_surface_image" if s else ""
|
|
682
|
+
|
|
551
683
|
selection = ipw.ToggleButtons(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
684
|
+
options=[
|
|
685
|
+
" ".join(_.strip("_surface_image").split("_")).capitalize()
|
|
686
|
+
for _ in coords().keys()
|
|
687
|
+
],
|
|
688
|
+
description="Depth",
|
|
689
|
+
disabled=False,
|
|
690
|
+
button_style="", # 'success', 'info', 'warning', 'danger' or ''
|
|
691
|
+
tooltips=[field_str(_) for _ in coords().keys()],
|
|
557
692
|
)
|
|
558
|
-
|
|
693
|
+
|
|
559
694
|
def update(_):
|
|
560
695
|
for probe in probes:
|
|
561
696
|
depth = probe_coords(field_str(selection.value))[probe]["z"]
|
|
562
|
-
textbox[probe].value = f
|
|
563
|
-
|
|
697
|
+
textbox[probe].value = f"{depth:6.1f}" if depth is not None else ""
|
|
698
|
+
|
|
564
699
|
textbox = {
|
|
565
700
|
probe: ipw.Text(
|
|
566
|
-
|
|
567
|
-
|
|
701
|
+
value="",
|
|
702
|
+
description=probe,
|
|
703
|
+
disabled=True,
|
|
704
|
+
layout=ipw.Layout(max_width="150px"),
|
|
705
|
+
)
|
|
568
706
|
for probe in probes
|
|
569
707
|
}
|
|
570
|
-
selection.observe(update,
|
|
708
|
+
selection.observe(update, "value")
|
|
571
709
|
update(None)
|
|
572
710
|
widget = ipw.VBox([selection, ipw.HBox([*textbox.values()])])
|
|
573
711
|
return IPython.display.display(widget)
|
|
@@ -575,96 +713,106 @@ def probe_depth_widget(session: np_session.PipelineSession):
|
|
|
575
713
|
|
|
576
714
|
def photodoc_widget(img_name: str) -> IPython.display.DisplayHandle | None:
|
|
577
715
|
"Captures and displays snapshot from image camera, appending `img_name` to the filename."
|
|
578
|
-
image = ipw.Image(
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
716
|
+
image = ipw.Image(
|
|
717
|
+
value=b"", format="png", width="80%", layout=ipw.Layout(visibility="hidden")
|
|
718
|
+
)
|
|
719
|
+
widget = ipw.VBox(
|
|
720
|
+
[
|
|
721
|
+
image,
|
|
722
|
+
button := ipw.Button(description="Re-capture", button_style="warning"),
|
|
723
|
+
console := ipw.Output(),
|
|
724
|
+
]
|
|
725
|
+
)
|
|
726
|
+
|
|
585
727
|
def capture() -> pathlib.Path:
|
|
586
|
-
image.value = b
|
|
587
|
-
image.layout.visibility =
|
|
588
|
-
button.button_style =
|
|
589
|
-
button.description =
|
|
728
|
+
image.value = b""
|
|
729
|
+
image.layout.visibility = "hidden"
|
|
730
|
+
button.button_style = ""
|
|
731
|
+
button.description = "Capturing new image..."
|
|
590
732
|
button.disabled = True
|
|
591
733
|
return npxc.photodoc(img_name)
|
|
592
|
-
|
|
734
|
+
|
|
593
735
|
def disp(img_path) -> None:
|
|
594
736
|
image.value = img_path.read_bytes()
|
|
595
|
-
image.layout.visibility =
|
|
596
|
-
button.button_style =
|
|
597
|
-
button.description =
|
|
737
|
+
image.layout.visibility = "visible"
|
|
738
|
+
button.button_style = "warning"
|
|
739
|
+
button.description = "Re-capture"
|
|
598
740
|
button.disabled = False
|
|
599
741
|
with console:
|
|
600
742
|
print(img_path)
|
|
601
|
-
|
|
743
|
+
|
|
602
744
|
def capture_and_display(*args):
|
|
603
745
|
disp(capture())
|
|
604
|
-
|
|
746
|
+
|
|
605
747
|
button.on_click(capture_and_display)
|
|
606
|
-
|
|
607
|
-
if
|
|
748
|
+
|
|
749
|
+
if matches := [
|
|
750
|
+
_
|
|
751
|
+
for _ in (np_services.Cam3d.data_files or np_services.ImageMVR.data_files or [])
|
|
752
|
+
if img_name in _.stem
|
|
753
|
+
]:
|
|
608
754
|
disp(sorted(matches)[-1])
|
|
609
755
|
else:
|
|
610
756
|
capture_and_display()
|
|
611
|
-
|
|
757
|
+
|
|
612
758
|
return IPython.display.display(widget)
|
|
613
759
|
|
|
760
|
+
|
|
614
761
|
def probe_targeting_widget(session_folder) -> IPython.display.DisplayHandle | None:
|
|
615
762
|
from np_probe_targets.implant_drawing import CurrentWeek, DRWeeklyTargets
|
|
763
|
+
|
|
616
764
|
CurrentWeek.display()
|
|
617
765
|
IPython.display.display(DRWeeklyTargets())
|
|
618
|
-
|
|
766
|
+
|
|
767
|
+
|
|
619
768
|
def quiet_mode_widget() -> IPython.display.DisplayHandle | None:
|
|
620
769
|
"""Displays a toggle button that switches logging level INFO <-> DEBUG and
|
|
621
770
|
hides/shows tracebacks.
|
|
622
771
|
"""
|
|
623
772
|
debug_mode_toggle = ipw.ToggleButton(
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
773
|
+
value=True,
|
|
774
|
+
description="Quiet mode is on",
|
|
775
|
+
disabled=False,
|
|
776
|
+
button_style="info", # 'success', 'info', 'warning', 'danger' or ''
|
|
777
|
+
icon="check",
|
|
778
|
+
tooltip="Quiet mode: tracebacks hidden, logging level set to INFO.",
|
|
779
|
+
)
|
|
780
|
+
|
|
632
781
|
def set_debug_mode(value: bool) -> None:
|
|
633
782
|
if value:
|
|
634
783
|
npxc.show_tracebacks()
|
|
635
784
|
for handler in np_logging.getLogger().handlers:
|
|
636
785
|
if isinstance(handler, logging.StreamHandler):
|
|
637
|
-
handler.setLevel(
|
|
786
|
+
handler.setLevel("DEBUG")
|
|
638
787
|
else:
|
|
639
788
|
npxc.hide_tracebacks()
|
|
640
789
|
for handler in np_logging.getLogger().handlers:
|
|
641
790
|
if isinstance(handler, logging.StreamHandler):
|
|
642
|
-
handler.setLevel(
|
|
643
|
-
|
|
791
|
+
handler.setLevel("INFO")
|
|
792
|
+
|
|
644
793
|
def on_click(b) -> None:
|
|
645
794
|
if not debug_mode_toggle.value:
|
|
646
795
|
set_debug_mode(True)
|
|
647
|
-
debug_mode_toggle.description =
|
|
648
|
-
debug_mode_toggle.button_style =
|
|
649
|
-
debug_mode_toggle.icon =
|
|
796
|
+
debug_mode_toggle.description = "Quiet mode is off"
|
|
797
|
+
debug_mode_toggle.button_style = ""
|
|
798
|
+
debug_mode_toggle.icon = "times"
|
|
650
799
|
else:
|
|
651
800
|
set_debug_mode(False)
|
|
652
|
-
debug_mode_toggle.description =
|
|
653
|
-
debug_mode_toggle.button_style =
|
|
654
|
-
debug_mode_toggle.icon =
|
|
655
|
-
|
|
801
|
+
debug_mode_toggle.description = "Quiet mode is on"
|
|
802
|
+
debug_mode_toggle.button_style = "info"
|
|
803
|
+
debug_mode_toggle.icon = "check"
|
|
804
|
+
|
|
656
805
|
debug_mode_toggle.observe(on_click)
|
|
657
|
-
|
|
806
|
+
|
|
658
807
|
return IPython.display.display(debug_mode_toggle)
|
|
659
808
|
|
|
660
809
|
|
|
661
810
|
def task_select_widget(
|
|
662
811
|
experiment,
|
|
663
812
|
) -> None:
|
|
664
|
-
"""Select a task name for controlling behavior of TaskControl.
|
|
665
|
-
"""
|
|
813
|
+
"""Select a task name for controlling behavior of TaskControl."""
|
|
666
814
|
experiment.task_name = experiment.preset_task_names[0]
|
|
667
|
-
|
|
815
|
+
|
|
668
816
|
task_dropdown = ipw.Select(
|
|
669
817
|
options=tuple(experiment.preset_task_names),
|
|
670
818
|
description="Presets",
|
|
@@ -676,7 +824,7 @@ def task_select_widget(
|
|
|
676
824
|
)
|
|
677
825
|
console = ipw.Output()
|
|
678
826
|
with console:
|
|
679
|
-
if last_task:= experiment.mouse.state.get(
|
|
827
|
+
if last_task := experiment.mouse.state.get("last_task"):
|
|
680
828
|
print(f"{experiment.mouse} last task: {last_task}")
|
|
681
829
|
|
|
682
830
|
def update(change):
|
|
@@ -698,8 +846,10 @@ def task_select_widget(
|
|
|
698
846
|
task_dropdown.value = None
|
|
699
847
|
with console:
|
|
700
848
|
print(f"Updated task: {experiment.task_name}")
|
|
701
|
-
task_dropdown.observe(update, names='value')
|
|
702
|
-
task_input_box.observe(update, names='value')
|
|
703
849
|
|
|
704
|
-
|
|
705
|
-
|
|
850
|
+
task_dropdown.observe(update, names="value")
|
|
851
|
+
task_input_box.observe(update, names="value")
|
|
852
|
+
|
|
853
|
+
IPython.display.display(
|
|
854
|
+
ipw.VBox([ipw.HBox([task_dropdown, task_input_box]), console])
|
|
855
|
+
)
|