Skip to content

Commit 2637689

Browse files
Merge pull request #951 from annie-xd-wang/communicating-from-model-to-controller
Communicating from model to controller
2 parents 779429a + 1231b3b commit 2637689

File tree

13 files changed

+163
-97
lines changed

13 files changed

+163
-97
lines changed

src/navigate/controller/controller.py

Lines changed: 51 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -174,27 +174,24 @@ def __init__(
174174
logger.info(f"Spec - Waveform Constants Path: {waveform_constants_path}")
175175
logger.info(f"Spec - Rest API Path: {rest_api_path}")
176176

177-
# Wire up pipes
178177
#: mp.Pipe: Pipe for sending images from model to view.
179178
self.show_img_pipe = self.model.create_pipe("show_img_pipe")
180179

181-
# save default experiment file
182180
#: string: Path to the default experiment yaml file.
183181
self.default_experiment_file = experiment_path
184182

185-
# waveform setting file
186183
#: string: Path to the waveform constants yaml file.
187184
self.waveform_constants_path = waveform_constants_path
188185

189-
# Configuration Reader
190186
#: ConfigurationController: Configuration Controller object.
191187
self.configuration_controller = ConfigurationController(self.configuration)
192188

193-
# Initialize the View
194189
#: View: View object in MVC architecture.
195190
self.view = view(root)
196191

197-
# Sub Gui Controllers
192+
#: dict: Event listeners for the controller.
193+
self.event_listeners = {}
194+
198195
#: AcquireBarController: Acquire Bar Sub-Controller.
199196
self.acquire_bar_controller = AcquireBarController(self.view.acqbar, self)
200197

@@ -234,7 +231,7 @@ def __init__(
234231
#: KeystrokeController: Keystroke Sub-Controller.
235232
self.keystroke_controller = KeystrokeController(self.view, self)
236233

237-
# Exit
234+
# Exit the program when the window is closed
238235
self.view.root.protocol(
239236
"WM_DELETE_WINDOW", self.acquire_bar_controller.exit_program
240237
)
@@ -248,22 +245,19 @@ def __init__(
248245
# self.microscope = self.configuration['configuration']
249246
# ['microscopes'].keys()[0] # Default to the first microscope
250247

251-
# Initialize the menus
252248
#: MenuController: Menu Sub-Controller.
253249
self.menu_controller = MenuController(view=self.view, parent_controller=self)
254250
self.menu_controller.initialize_menus()
255251

256252
#: dict: acquisition modes from plugins
257253
self.plugin_acquisition_modes = {}
258254

259-
# add plugin menus
260255
#: PluginsController: Plugin Sub-Controller
261256
self.plugin_controller = PluginsController(
262257
view=self.view, parent_controller=self
263258
)
264259
self.plugin_controller.load_plugins()
265260

266-
# Create default data buffer
267261
#: int: Number of x_pixels from microscope configuration file.
268262
self.img_width = 0
269263

@@ -473,6 +467,16 @@ def update_experiment_setting(self):
473467
"multiposition_count"
474468
] = len(positions)
475469

470+
if (
471+
self.configuration["experiment"]["MicroscopeState"]["is_multiposition"]
472+
and len(positions) == 0
473+
):
474+
# Update the view and override the settings.
475+
self.configuration["experiment"]["MicroscopeState"][
476+
"is_multiposition"
477+
] = False
478+
self.channels_tab_controller.is_multiposition_val.set(False)
479+
476480
# TODO: validate experiment dict
477481

478482
channel_warning = self.channels_tab_controller.verify_experiment_values()
@@ -1225,54 +1229,13 @@ def update_event(self):
12251229
# Display a warning that arises from the model as a top-level GUI popup
12261230
messagebox.showwarning(title="Navigate", message=value)
12271231

1228-
elif event == "waveform":
1229-
# Update the waveform plot.
1230-
self.waveform_tab_controller.update_waveforms(
1231-
waveform_dict=value,
1232-
sample_rate=self.configuration_controller.daq_sample_rate,
1233-
)
1234-
12351232
elif event == "multiposition":
12361233
# Update the multi-position tab without appending to the list
12371234
update_table(
12381235
table=self.multiposition_tab_controller.table,
12391236
pos=value,
12401237
)
12411238
self.channels_tab_controller.is_multiposition_val.set(True)
1242-
self.channels_tab_controller.toggle_multiposition()
1243-
1244-
elif event == "disable_multiposition":
1245-
self.channels_tab_controller.is_multiposition_val.set(False)
1246-
self.channels_tab_controller.toggle_multiposition()
1247-
1248-
elif event == "ilastik_mask":
1249-
# Display the ilastik mask
1250-
self.camera_view_controller.display_mask(mask=value)
1251-
1252-
elif event == "autofocus":
1253-
# Display the autofocus plot
1254-
if hasattr(self, "af_popup_controller"):
1255-
self.af_popup_controller.display_plot(
1256-
data=value[0], line_plot=value[1], clear_data=value[2]
1257-
)
1258-
1259-
elif event == "tonywilson":
1260-
if hasattr(self, "ao_popup_controller"):
1261-
# self.ao_popup_controller.set_widgets_from_coef(value['coefs'])
1262-
self.ao_popup_controller.plot_tonywilson(value)
1263-
# self.ao_popup_controller.plot_mirror(value)
1264-
if value["done"]:
1265-
print("Tony Wilson done! Updating expt...")
1266-
self.ao_popup_controller.update_experiment_values()
1267-
1268-
elif event == "mirror_update":
1269-
if hasattr(self, "ao_popup_controller"):
1270-
self.ao_popup_controller.set_widgets_from_coef(value["coefs"])
1271-
self.ao_popup_controller.plot_mirror(value)
1272-
1273-
elif event == "ao_save_report":
1274-
if hasattr(self, "ao_popup_controller"):
1275-
self.ao_popup_controller.save_report_to_file(value)
12761239

12771240
elif event == "stop":
12781241
# Stop the software
@@ -1287,17 +1250,47 @@ def update_event(self):
12871250
time.sleep(0.001)
12881251
pass
12891252

1290-
elif event == "remove_positions":
1291-
self.multiposition_tab_controller.remove_positions(value)
1292-
1293-
elif event == "exposure_time":
1294-
self.channels_tab_controller.set_exposure_time(value[0], value[1])
1295-
elif event == "display_camera_parameters":
1296-
self.camera_setting_controller.update_camera_parameters_silent(*value)
1253+
elif event in self.event_listeners.keys():
1254+
try:
1255+
self.event_listeners[event](value)
1256+
except Exception:
1257+
print(f"*** unhandled event: {event}, {value}")
12971258

12981259
def add_acquisition_mode(self, name, acquisition_obj):
1260+
"""Add and Acquisition Mode.
1261+
1262+
Parameters
1263+
----------
1264+
name : string
1265+
Name of the acquisition mode.
1266+
acquisition_obj : object
1267+
Object of the acquisition mode.
1268+
"""
12991269
if name in self.plugin_acquisition_modes:
13001270
print(f"*** plugin acquisition mode {name} exists, can't add another one!")
13011271
return
13021272
self.plugin_acquisition_modes[name] = acquisition_obj(name)
13031273
self.acquire_bar_controller.add_mode(name)
1274+
1275+
def register_event_listener(self, event_name, event_handler):
1276+
"""Register an event listener.
1277+
1278+
Parameters
1279+
----------
1280+
event_name : string
1281+
Name of the event.
1282+
event_handler : function
1283+
Function to handle the event.
1284+
"""
1285+
self.event_listeners[event_name] = event_handler
1286+
1287+
def register_event_listeners(self, events):
1288+
"""Register multiple event listeners.
1289+
1290+
Parameters
1291+
----------
1292+
events : dict
1293+
Dictionary of event names and handlers.
1294+
"""
1295+
for event_name, event_handler in events.items():
1296+
self.register_event_listener(event_name, event_handler)

src/navigate/controller/sub_controllers/adaptive_optics.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ def plot_mirror(self, data):
370370

371371
try:
372372
coefs = data["coefs"]
373+
self.set_widgets_from_coef(coefs)
373374
self.coefs_bar.clear()
374375
self.coefs_bar.bar(range(len(coefs)), coefs)
375376
self.coefs_bar.set_title("Current Coefs")
@@ -410,6 +411,10 @@ def plot_tonywilson(self, data):
410411

411412
self.plot_tw_trace()
412413

414+
if data["done"]:
415+
print("Tony Wilson done! Updating expt...")
416+
self.update_experiment_values()
417+
413418
def plot_tw_trace(self):
414419
"""Plot the tony wilson trace data."""
415420
mode = self.parent_controller.configuration["experiment"][
@@ -435,3 +440,12 @@ def plot_tw_trace(self):
435440
# To redraw the plot
436441
self.fig_tw.tight_layout()
437442
self.fig_tw.canvas.draw_idle()
443+
444+
@property
445+
def custom_events(self):
446+
"""dict: Custom events for this controller"""
447+
return {
448+
"ao_save_report": self.save_report_to_file,
449+
"mirror_update": self.plot_mirror,
450+
"tonywilson": self.plot_tonywilson,
451+
}

src/navigate/controller/sub_controllers/autofocus.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,19 +204,20 @@ def func(*args):
204204

205205
return func
206206

207-
def display_plot(self, data, line_plot=False, clear_data=True):
207+
def display_plot(self, data_and_flags):
208208
"""Displays the autofocus plot
209209
210-
data : numpy.ndarray
211-
The data to be plotted.
212-
line_plot : bool
213-
If True, the plot will be a line plot.
214-
If False, the plot will be a scatter plot.
215-
clear_data : bool
216-
If True, the plot will be cleared before plotting.
217-
If False, the plot will be added to the existing plot.
210+
data : tuple (numpy.ndarray, bool, bool)
211+
(data, line_plot, clear_data)
212+
data: The data to be plotted.
213+
line_plot:
214+
If True, the plot will be a line plot.
215+
If False, the plot will be a scatter plot.
216+
clear_data:
217+
If True, the plot will be cleared before plotting.
218+
If False, the plot will be added to the existing plot.
218219
"""
219-
220+
data, line_plot, clear_data = data_and_flags
220221
data = np.asarray(data)
221222
coarse_range = self.setting_dict.get("coarse_range", 500)
222223
coarse_step = self.setting_dict.get("coarse_step_size", 50)
@@ -270,3 +271,8 @@ def display_plot(self, data, line_plot=False, clear_data=True):
270271
self.autofocus_coarse.xaxis.set_minor_locator(tck.AutoMinorLocator())
271272
self.autofocus_fig.tight_layout()
272273
self.autofocus_fig.canvas.draw_idle()
274+
275+
@property
276+
def custom_events(self):
277+
"""dict: Custom events for this controller"""
278+
return {"autofocus": self.display_plot}

src/navigate/controller/sub_controllers/camera_settings.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,10 +580,23 @@ def update_camera_device_related_setting(self):
580580
self.roi_widgets["Center_X"].set(self.default_width / 2)
581581
self.roi_widgets["Center_Y"].set(self.default_height / 2)
582582

583-
def update_camera_parameters_silent(self, sensor_mode=None, readout_direction=None, number_of_pixels=None):
583+
def update_camera_parameters_silent(self, value):
584+
"""Update GUI camera parameters
585+
586+
Parameters
587+
----------
588+
value : tuple
589+
(sensor_mode, readout_direction, number_of_pixels)
590+
"""
591+
sensor_mode, readout_direction, number_of_pixels = value
584592
if sensor_mode:
585593
self.update_sensor_mode(sensor_mode)
586594
if readout_direction:
587595
self.mode_widgets["Readout"].set(readout_direction)
588596
if number_of_pixels:
589597
self.mode_widgets["Pixels"].set(number_of_pixels)
598+
599+
@property
600+
def custom_events(self):
601+
"""dict: Custom events for this controller"""
602+
return {"display_camera_parameters": self.update_camera_parameters_silent}

src/navigate/controller/sub_controllers/camera_view.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,3 +1238,8 @@ def try_to_display_image(self, image_id):
12381238

12391239
display_thread = threading.Thread(target=self.display_image, args=(image_id,))
12401240
display_thread.start()
1241+
1242+
@property
1243+
def custom_events(self):
1244+
"""dict: Custom events for this controller"""
1245+
return {"ilastik_mask": self.display_mask}

src/navigate/controller/sub_controllers/channels_tab.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,8 @@ def __init__(self, view, parent_controller=None):
103103
self.stack_acq_vals["step_size"].trace_add("write", self.update_z_steps)
104104
self.stack_acq_vals["start_position"].trace_add("write", self.update_z_steps)
105105
self.stack_acq_vals["end_position"].trace_add("write", self.update_z_steps)
106-
self.stack_acq_vals["start_focus"].trace_add(
107-
"write", self.update_z_steps
108-
) # TODO: could be remove later
106+
# TODO: could be remove later
107+
self.stack_acq_vals["start_focus"].trace_add("write", self.update_z_steps)
109108
self.stack_acq_buttons["set_start"].configure(
110109
command=self.update_start_position
111110
)
@@ -158,9 +157,8 @@ def __init__(self, view, parent_controller=None):
158157

159158
#: bool: Whether or not the user has selected to use multiposition.
160159
self.is_multiposition_val = self.view.multipoint_frame.on_off
161-
self.view.multipoint_frame.save_check.configure(
162-
command=self.toggle_multiposition
163-
)
160+
self.is_multiposition_val.trace_add("write", self.toggle_multiposition)
161+
164162
self.view.multipoint_frame.buttons["tiling"].configure(
165163
command=self.launch_tiling_wizard
166164
)
@@ -350,12 +348,10 @@ def set_mode(self, mode):
350348
# multi-position flag
351349
if mode == "stop":
352350
self.is_multiposition_val.set(self.is_multiposition_cache)
353-
self.toggle_multiposition()
354351
else:
355352
self.is_multiposition_cache = self.is_multiposition
356353
if mode == "customized":
357354
self.is_multiposition_val.set(False)
358-
self.toggle_multiposition()
359355

360356
if image_mode == "customized" or mode != "stop":
361357
self.disable_multiposition_btn()
@@ -712,7 +708,7 @@ def update_timepoint_setting(self, call_parent=False):
712708
"timepoint settings on channels tab have been changed and recalculated"
713709
)
714710

715-
def toggle_multiposition(self):
711+
def toggle_multiposition(self, *args):
716712
"""Toggle Multi-position Acquisition.
717713
718714
Recalculates the experiment duration.
@@ -835,17 +831,23 @@ def verify_experiment_values(self):
835831
return "Timepoints should be at least 1!"
836832
return None
837833

838-
def set_exposure_time(self, channel, exposure_time):
834+
def set_exposure_time(self, channel_exposure_time):
839835
"""Set exposure time for a specified channel
840836
841837
Parameters
842838
----------
843-
channel : str
839+
channel_exposure_time : tuple(str, float)
840+
(channel_name, exposure_time)
844841
Channel name, such as "channel_1", "channel_2",...
845-
exposure_time : float
846842
Exposure time in milliseconds.
847843
"""
844+
channel, exposure_time = channel_exposure_time
848845
idx = int(channel[channel.index("_") + 1 :]) - 1
849846
self.channel_setting_controller.in_initialization = True
850847
self.channel_setting_controller.view.exptime_variables[idx].set(exposure_time)
851848
self.channel_setting_controller.in_initialization = False
849+
850+
@property
851+
def custom_events(self):
852+
"""Custom events for the channels tab."""
853+
return {"exposure_time": self.set_exposure_time}

0 commit comments

Comments
 (0)