Skip to content

Commit 235263e

Browse files
Merge pull request #930 from TheDeanLab/926
926
2 parents f7e1e4a + 4b43de0 commit 235263e

23 files changed

+490
-386
lines changed

README.md

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ navigate
1111
[![Tests](https://github.com/TheDeanLab/navigate/actions/workflows/push_checks.yaml/badge.svg)](https://github.com/TheDeanLab/navigate/actions/workflows/push_checks.yaml)
1212
[![codecov](https://codecov.io/gh/TheDeanLab/navigate/branch/develop/graph/badge.svg?token=270RFSZGG5)](https://codecov.io/gh/TheDeanLab/navigate)
1313

14-
Navigate is an open source Python package for control of light-sheet microscopes. It allows for easily reconfigurable hardware setups and automated acquisition rotuines.
14+
**navigate** is an open source Python package for control of light-sheet microscopes.
15+
It allows for easily reconfigurable hardware setups and automated acquisition rotuines.
1516

1617
### Quick install
1718

@@ -31,23 +32,27 @@ Please refer to and contribute to the documentation, which can be found on GitHu
3132

3233
### Command Line Arguments
3334

34-
* optional arguments:
35-
* -h, --help show this help message and exit
36-
37-
* Input Arguments:
38-
* -sh, --synthetic_hardware
39-
* --config_file CONFIG_FILE
40-
* --experiment_file EXPERIMENT_FILE
41-
* --etl_const_file ETL_CONST_FILE
42-
* --rest_api_file REST_API_FILE
43-
* --logging_config LOGGING_CONFIG
44-
45-
### Authors
46-
* Kevin Dean
47-
* Zach Marin
48-
* Xiaoding 'Annie' Wang
49-
* Dax Collison
50-
* Sampath Rapuri
51-
* Samir Mamtani
52-
* Renil Gupta
53-
* Andrew Jamieson
35+
Below are the optional arguments that can be passed to the navigate software:
36+
37+
- `-h, --help`
38+
Provides information on the optional arguments that can be passed to **navigate**.
39+
- `-sh, --synthetic_hardware`
40+
Open the software without any hardware attached for testing
41+
and setting up a new system.
42+
- `-c, --configurator`
43+
Open the **navigate** configuration wizard, which provides a
44+
graphical interface for setting up the hardware configuration.
45+
- `-d`
46+
Enables the debugging menu in the software.
47+
- `--config-file`
48+
Pass a non-default `configuration.yaml` file to **navigate**.
49+
- `--experiment_file`
50+
Pass a non-default `experiment.yaml` file to **navigate**.
51+
- `--gui-config-file`
52+
Pass a non-default `gui_config.yaml` file to **navigate**.
53+
- `--waveform-constants-file`
54+
Pass a non-default waveform constants file to **navigate**.
55+
- `--rest_api_file`
56+
Pass a non-default REST API file to **navigate**.
57+
- `--logging_config`
58+
Pass a non-default logging configuration file to **navigate**.

src/navigate/config/config.py

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2021-2022 The University of Texas Southwestern Medical Center.
1+
# Copyright (c) 2021-2024 The University of Texas Southwestern Medical Center.
22
# All rights reserved.
33

44
# Redistribution and use in source and binary forms, with or without
@@ -89,65 +89,38 @@ def get_configuration_paths():
8989
Path to file containing REST API configuration
9090
waveform_templates_path : str
9191
Path to file containing waveform templates
92+
gui_configuration_path : str
93+
Path to file containing GUI configuration
9294
"""
95+
# Create the navigate home directory if it doesn't exist
9396
navigate_directory = get_navigate_path()
9497
if not os.path.exists(navigate_directory):
9598
os.mkdir(navigate_directory)
99+
100+
# Create the configuration directory if it doesn't exist
96101
configuration_directory = Path(os.path.join(navigate_directory, "config"))
97102
if not os.path.exists(configuration_directory):
98103
os.mkdir(configuration_directory)
99104

100-
# Configuration files should be stored in this directory
101-
configuration_path = Path.joinpath(configuration_directory, "configuration.yaml")
102-
experiment_path = Path.joinpath(configuration_directory, "experiment.yml")
103-
waveform_constants_path = Path.joinpath(
104-
configuration_directory, "waveform_constants.yml"
105-
)
106-
rest_api_path = Path.joinpath(configuration_directory, "rest_api_config.yml")
107-
waveform_templates_path = Path.joinpath(
108-
configuration_directory, "waveform_templates.yml"
109-
)
110-
111-
# If they are not already,
112-
# copy the default ones that ship with the software to this folder
113-
if not os.path.exists(configuration_path):
114-
copy_base_directory = Path(__file__).resolve().parent
115-
copy_configuration_path = Path.joinpath(
116-
copy_base_directory, "configuration.yaml"
117-
)
118-
shutil.copyfile(copy_configuration_path, configuration_path)
105+
configuration_files = [
106+
"configuration.yaml",
107+
"experiment.yml",
108+
"waveform_constants.yml",
109+
"rest_api_config.yml",
110+
"waveform_templates.yml",
111+
"gui_configuration.yml",
112+
]
119113

120-
if not os.path.exists(experiment_path):
121-
copy_base_directory = Path(__file__).resolve().parent
122-
copy_experiment_path = Path.joinpath(copy_base_directory, "experiment.yml")
123-
shutil.copyfile(copy_experiment_path, experiment_path)
114+
base_directory = Path(__file__).resolve().parent
115+
paths = []
116+
for file in configuration_files:
117+
copy_file_path = Path.joinpath(base_directory, file)
118+
file_path = Path.joinpath(configuration_directory, file)
119+
paths.append(file_path)
120+
if not os.path.exists(file_path):
121+
shutil.copyfile(copy_file_path, file_path)
124122

125-
if not os.path.exists(waveform_constants_path):
126-
copy_base_directory = Path(__file__).resolve().parent
127-
copy_waveform_constants_path = Path.joinpath(
128-
copy_base_directory, "waveform_constants.yml"
129-
)
130-
shutil.copyfile(copy_waveform_constants_path, waveform_constants_path)
131-
132-
if not os.path.exists(rest_api_path):
133-
copy_base_directory = Path(__file__).resolve().parent
134-
copy_rest_api_path = Path.joinpath(copy_base_directory, "rest_api_config.yml")
135-
shutil.copyfile(copy_rest_api_path, rest_api_path)
136-
137-
if not os.path.exists(waveform_templates_path):
138-
copy_base_directory = Path(__file__).resolve().parent
139-
copy_waveform_templates_path = Path.joinpath(
140-
copy_base_directory, "waveform_templates.yml"
141-
)
142-
shutil.copyfile(copy_waveform_templates_path, waveform_templates_path)
143-
144-
return (
145-
configuration_path,
146-
experiment_path,
147-
waveform_constants_path,
148-
rest_api_path,
149-
waveform_templates_path,
150-
)
123+
return [path for path in paths]
151124

152125

153126
def load_configs(manager, **kwargs):
@@ -942,19 +915,21 @@ def verify_configuration(manager, configuration):
942915
microscope_name_seq.append(microscope_name.strip())
943916
continue
944917

945-
if ")" not in microscope_name[parenthesis_l+1:]:
918+
if ")" not in microscope_name[parenthesis_l + 1 :]:
946919
microscope_name_seq.append(microscope_name.strip())
947920
continue
948921

949-
parenthesis_r = microscope_name[parenthesis_l+1:].index(")")
950-
parent_microscope_name = microscope_name[parenthesis_l+1: parenthesis_l+parenthesis_r+1].strip()
922+
parenthesis_r = microscope_name[parenthesis_l + 1 :].index(")")
923+
parent_microscope_name = microscope_name[
924+
parenthesis_l + 1 : parenthesis_l + parenthesis_r + 1
925+
].strip()
951926

952927
if parent_microscope_name not in microscope_name_seq:
953928
microscope_name_seq.append(parent_microscope_name)
954-
929+
955930
idx = microscope_name_seq.index(parent_microscope_name)
956931
child_microscope_name = microscope_name[:parenthesis_l].strip()
957-
microscope_name_seq.insert(idx+1, child_microscope_name)
932+
microscope_name_seq.insert(idx + 1, child_microscope_name)
958933
inherited_microscope_dict[child_microscope_name] = parent_microscope_name
959934
device_config[child_microscope_name] = device_config.pop(microscope_name)
960935

@@ -964,11 +939,16 @@ def verify_configuration(manager, configuration):
964939
continue
965940
parent_microscope_name = inherited_microscope_dict[microscope_name]
966941
if parent_microscope_name not in device_config.keys():
967-
raise Exception(f"Microscope {parent_microscope_name} is not defined in configuration.yaml")
968-
942+
raise Exception(
943+
f"Microscope {parent_microscope_name} is not "
944+
f"defined in configuration.yaml"
945+
)
946+
969947
for device_name in device_config[parent_microscope_name].keys():
970948
if device_name not in device_config[microscope_name].keys():
971-
device_config[microscope_name][device_name] = device_config[parent_microscope_name][device_name]
949+
device_config[microscope_name][device_name] = device_config[
950+
parent_microscope_name
951+
][device_name]
972952

973953
channel_count = 5
974954
# generate hardware header section
@@ -980,16 +960,26 @@ def verify_configuration(manager, configuration):
980960
"zoom": None,
981961
"mirror": None,
982962
}
983-
required_devices = ["camera", "daq", "filter_wheel", "shutter", "remote_focus_device", "galvo", "stage", "lasers"]
963+
required_devices = [
964+
"camera",
965+
"daq",
966+
"filter_wheel",
967+
"shutter",
968+
"remote_focus_device",
969+
"galvo",
970+
"stage",
971+
"lasers",
972+
]
984973
for microscope_name in device_config.keys():
985974
# camera
986975
# delay_percent -> delay
987976
for device_name in required_devices:
988977
if device_name not in device_config[microscope_name]:
989-
print("**************************************************************************")
990-
print(f"*** Please make sure you have {device_name} in the configuration for microscope {microscope_name}.")
991-
print(f"*** Or please makesure {microscope_name} is inherited from another valid microscope!")
992-
print("**************************************************************************")
978+
print(
979+
f"*** Please make sure you have {device_name} "
980+
f"in the configuration for microscope {microscope_name}, or "
981+
f"{microscope_name} is inherited from another valid microscope!"
982+
)
993983
raise Exception()
994984
camera_config = device_config[microscope_name]["camera"]
995985
if "delay" not in camera_config.keys():
@@ -1029,7 +1019,8 @@ def verify_configuration(manager, configuration):
10291019
# zoom (one zoom)
10301020
if "zoom" not in hardware_dict:
10311021
zoom_config = device_config[microscope_name]["zoom"]["hardware"]
1032-
# zoom_idx = build_ref_name("-", zoom_config["type"], zoom_config["servo_id"])
1022+
# zoom_idx = build_ref_name("-", zoom_config["type"],
1023+
# zoom_config["servo_id"])
10331024
hardware_dict["zoom"] = zoom_config
10341025

10351026
# filter wheel
@@ -1094,7 +1085,9 @@ def verify_configuration(manager, configuration):
10941085
filter_wheel_config["hardware"]["type"],
10951086
filter_wheel_config["hardware"]["wheel_number"],
10961087
)
1097-
filter_wheel_ids.remove(ref_list["filter_wheel"].index(filter_wheel_idx))
1088+
filter_wheel_ids.remove(
1089+
ref_list["filter_wheel"].index(filter_wheel_idx)
1090+
)
10981091
for i in filter_wheel_ids:
10991092
temp_config.insert(i, filter_wheel_seq[i])
11001093

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
channel_settings:
2+
count: 5
3+
laser_power:
4+
step: 1
5+
min: 0
6+
max: 100
7+
exposure_time:
8+
step: 1
9+
min: 1
10+
max: 1000
11+
interval:
12+
step: 1
13+
min: 1
14+
max: 10
15+
defocus:
16+
step: 1
17+
min: 0
18+
max: 100
19+
stack_acquisition:
20+
step_size:
21+
step: 0.01
22+
min: 0.01
23+
max: 1000
24+
z_start_pos:
25+
step: 1.0
26+
min: -10000
27+
max: 10000
28+
z_end_pos:
29+
step: 1.0
30+
min: -10000
31+
max: 10000
32+
f_start_pos:
33+
step: 0.01
34+
min: -200
35+
max: 2000
36+
f_end_pos:
37+
step: 0.01
38+
min: -200
39+
max: 200
40+
time:
41+
stack_pause:
42+
step: 0.1
43+
min: 0
44+
max: 1000
45+
timepoints:
46+
step: 1
47+
min: 1
48+
max: 5000

src/navigate/controller/configuration_controller.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,9 @@ def get_stage_position_limits(self, suffix):
251251
252252
"""
253253
axis = ["x", "y", "z", "theta", "f"]
254+
position_limits = {}
254255
if self.microscope_config is not None:
255256
stage_dict = self.microscope_config["stage"]
256-
position_limits = {}
257257
for a in axis:
258258
position_limits[a] = stage_dict[a + suffix]
259259
else:
@@ -379,9 +379,7 @@ def number_of_channels(self):
379379
Number of channels.
380380
"""
381381
if self.microscope_config is not None:
382-
return self.configuration["configuration"]["gui"]["channels"].get(
383-
"count", 5
384-
)
382+
return self.configuration["gui"]["channel_settings"].get("count", 5)
385383
return 5
386384

387385
@property

src/navigate/controller/controller.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def __init__(
105105
waveform_constants_path,
106106
rest_api_path,
107107
waveform_templates_path,
108+
gui_configuration_path,
108109
args,
109110
):
110111
"""Initialize the Navigate Controller.
@@ -130,6 +131,9 @@ def __init__(
130131
waveform_templates_path : string
131132
Path to the waveform templates yaml file.
132133
Provides waveform templates for each channel.
134+
gui_configuration_path : string
135+
Path to the GUI configuration yaml file.
136+
Provides GUI configuration parameters.
133137
*args :
134138
Command line input arguments for non-default
135139
file paths or using synthetic hardware modes.
@@ -152,6 +156,7 @@ def __init__(
152156
waveform_constants=waveform_constants_path,
153157
rest_api_config=rest_api_path,
154158
waveform_templates=waveform_templates_path,
159+
gui=gui_configuration_path,
155160
)
156161

157162
verify_configuration(self.manager, self.configuration)
@@ -192,33 +197,40 @@ def __init__(
192197
# Sub Gui Controllers
193198
#: AcquireBarController: Acquire Bar Sub-Controller.
194199
self.acquire_bar_controller = AcquireBarController(self.view.acqbar, self)
200+
195201
#: ChannelsTabController: Channels Tab Sub-Controller.
196202
self.channels_tab_controller = ChannelsTabController(
197203
self.view.settings.channels_tab, self
198204
)
205+
199206
#: MultiPositionController: Multi-Position Tab Sub-Controller.
200207
self.multiposition_tab_controller = MultiPositionController(
201208
self.view.settings.multiposition_tab.multipoint_list, self
202209
)
210+
203211
#: CameraViewController: Camera View Tab Sub-Controller.
204212
self.camera_view_controller = CameraViewController(
205213
self.view.camera_waveform.camera_tab, self
206214
)
215+
207216
#: CameraSettingController: Camera Settings Tab Sub-Controller.
208217
self.camera_setting_controller = CameraSettingController(
209218
self.view.settings.camera_settings_tab, self
210219
)
220+
211221
#: StageController: Stage Sub-Controller.
212222
self.stage_controller = StageController(
213223
self.view.settings.stage_control_tab,
214224
self.view,
215225
self.camera_view_controller.canvas,
216226
self,
217227
)
228+
218229
#: WaveformTabController: Waveform Display Sub-Controller.
219230
self.waveform_tab_controller = WaveformTabController(
220231
self.view.camera_waveform.waveform_tab, self
221232
)
233+
222234
#: KeystrokeController: Keystroke Sub-Controller.
223235
self.keystroke_controller = KeystrokeController(self.view, self)
224236

0 commit comments

Comments
 (0)