Skip to content
27 changes: 25 additions & 2 deletions src/navigate/controller/configuration_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,29 @@ def stage_default_position(self) -> dict:
position = {"x": 0, "y": 0, "z": 0, "theta": 0, "f": 0}
return position

@property
def stage_home_position(self) -> dict:
"""Get the home position of the stage. If not set, return None.

Returns
-------
position : dict
Dictionary with all stage home positions.
"""

# Get all stage axes, not just the core X, Y, Z, F, and Theta.
axes = self.all_stage_axes

# Create a dictionary for every axis in axes with a default value of None.
position = {axis: None for axis in axes}
if self.microscope_config is not None:
for axis in axes:
key = f"{axis}_home"
position[axis] = self.microscope_config["stage"].get(key, None)
if position[axis] is None:
del position[axis]
return position

@property
def stage_step(self) -> dict:
"""Get the step size of the stage
Expand Down Expand Up @@ -283,9 +306,9 @@ def get_stage_position_limits(self, suffix: str) -> dict:
if self.microscope_config is not None:
stage_dict = self.microscope_config["stage"]
for a in axes:
position_limits[a] = stage_dict.get(
position_limits[a] = float(stage_dict.get(
a + suffix, 0 if suffix == "_min" else 100
)
))
else:
for a in axes:
position_limits[a] = 0 if suffix == "_min" else 100
Expand Down
102 changes: 71 additions & 31 deletions src/navigate/controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def __init__(

#: Tk top-level widget: Tk.tk GUI instance.
self.root = root

#: bool: Flag to indicate if the GUI is ready for resizing.
self.resize_ready_flag = False

Expand Down Expand Up @@ -277,10 +278,7 @@ def __init__(

#: StageController: Stage Sub-Controller.
self.stage_controller = StageController(
self.view.settings.stage_control_tab,
self.view,
self.camera_view_controller.canvas,
self,
self.view.settings.stage_control_tab, self,
)

#: WaveformTabController: Waveform Display Sub-Controller.
Expand Down Expand Up @@ -571,9 +569,7 @@ def update_experiment_setting(self):
return ""

def enable_resize(self):
"""Enable window resizing.

"""
"""Enable window resizing."""
self.resize_ready_flag = True

def resize(self, event):
Expand All @@ -594,13 +590,12 @@ def refresh(width, height):
Width of the GUI.
height : int
Height of the GUI.
"""
"""
self.view.scroll_frame.resize(width, height)
self.view.right_frame.config(
width=width-self.view.left_frame.winfo_width()-3,
height=height-self.view.left_frame.winfo_height()
width=width - self.view.left_frame.winfo_width() - 3,
height=height - self.view.left_frame.winfo_height(),
)


if not self.resize_ready_flag:
return
Expand Down Expand Up @@ -692,25 +687,48 @@ def execute(self, command, *args):
self.threads_pool.createThread(
resourceName="model",
target=self.move_stage,
args=({args[1] + "_abs": args[0]},)
args=({args[1] + "_abs": args[0]},),
)

elif command == "stop_stage":
"""Creates a thread and uses it to call the model to stop stage"""
self.threads_pool.createThread(
resourceName="stop_stage",
target=self.stop_stage)
resourceName="stop_stage", target=self.stop_stage
)

elif command == "query_stages":
"""Query the stages for their current position in a thread-blocking format.
"""
"""Query the stages for the active microscope's current position in a
thread-blocking format."""
query_thread = self.threads_pool.createThread(
resourceName="model", target=self.stop_stage
)

while query_thread.is_alive():
time.sleep(0.01)

elif command == "query_select_microscope":
"""Query a specific microscope for its current positions in a
thread-blocking format."""
microscope_name = args[0]
query_thread = self.threads_pool.createThread(
resourceName="model",
target=self.stop_stage)
target=self.query_select_microscope,
args=(microscope_name,),
)

while query_thread.is_alive():
time.sleep(0.01)

elif command == "update_stage_limits":
microscope_name = args[0]
if microscope_name == self.configuration_controller.microscope_name:
self.stage_controller.initialize()
self.threads_pool.createThread(
resourceName="model",
target=self.update_stage_limits,
args=(microscope_name,)
)

elif command == "move_stage_and_update_info":
"""update stage view to show the position

Expand All @@ -736,9 +754,9 @@ def execute(self, command, *args):
self.execute("acquire")

elif command == "get_stage_position":
"""Returns the current stage position from the widgets.
Does not communicate with the stages, but rather takes the last known
"""Returns the current stage position from the widgets.

Does not communicate with the stages, but rather takes the last known
position.

Returns
Expand Down Expand Up @@ -790,7 +808,8 @@ def execute(self, command, *args):
self.change_microscope(temp[0], temp[1])
work_thread = self.threads_pool.createThread(
resourceName="model",
target=lambda: self.model.run_command("update_setting", "resolution"))
target=lambda: self.model.run_command("update_setting", "resolution"),
)
work_thread.join()

elif command == "set_save":
Expand Down Expand Up @@ -821,14 +840,14 @@ def execute(self, command, *args):
"""
self.threads_pool.createThread(
resourceName="model",
target=lambda: self.model.run_command("update_setting", *args)
target=lambda: self.model.run_command("update_setting", *args),
)

elif command == "stage_limits":
self.stage_controller.stage_limits = args[0]
self.threads_pool.createThread(
resourceName="model",
target=lambda: self.model.run_command("stage_limits", *args)
target=lambda: self.model.run_command("stage_limits", *args),
)

elif command == "autofocus":
Expand All @@ -842,7 +861,7 @@ def execute(self, command, *args):
elif self.acquire_bar_controller.mode == "live":
self.threads_pool.createThread(
resourceName="model",
target=lambda: self.model.run_command("autofocus", *args)
target=lambda: self.model.run_command("autofocus", *args),
)

elif command == "eliminate_tiles":
Expand All @@ -868,7 +887,7 @@ def execute(self, command, *args):

work_thread = self.threads_pool.createThread(
resourceName="model",
target=lambda: self.model.run_command("load_feature", *args)
target=lambda: self.model.run_command("load_feature", *args),
)
work_thread.join()

Expand Down Expand Up @@ -1012,12 +1031,14 @@ def execute(self, command, *args):
self.sloppy_stop()
self.update_experiment_setting()
file_directory = os.path.join(get_navigate_path(), "config")
for config_name, filename in [("experiment", "experiment.yml"),
("multi_positions", "multi_positions.yml"),
("gui", "gui_configuration.yml"),
("waveform_constants", "waveform_constants.yml"),
("rest_api_config", "rest_api_config.yml"),
("waveform_templates", "waveform_templates.yml")]:
for config_name, filename in [
("experiment", "experiment.yml"),
("multi_positions", "multi_positions.yml"),
("gui", "gui_configuration.yml"),
("waveform_constants", "waveform_constants.yml"),
("rest_api_config", "rest_api_config.yml"),
("waveform_templates", "waveform_templates.yml"),
]:
save_yaml_file(
file_directory=file_directory,
content_dict=self.configuration[config_name],
Expand Down Expand Up @@ -1372,6 +1393,15 @@ def move_stage(self, pos_dict):
# Pass to model
self.model.move_stage(pos_dict)

def query_select_microscope(self, *args):
"""Query a specific microscope for its stage positions."""
microscope_name = args[0]
stage_positions = self.model.query_select_microscope(microscope_name)

# Inject updated positions back into the advanced stage parameters popup.
if hasattr(self, "stage_limits_popup_controller"):
self.stage_limits_popup_controller.positions = stage_positions

def stop_stage(self):
"""Stop the stage.

Expand All @@ -1380,6 +1410,16 @@ def stop_stage(self):
"""
self.model.stop_stage()

def update_stage_limits(self, microscope_name: str) -> None:
"""Update stage limits on the device side

Parameters
----------
microscope_name : str
Microscope name.
"""
self.model.update_stage_limits(microscope_name)

def update_stage_controller_silent(self, ret_pos_dict):
"""Send updates to the stage GUI

Expand Down
29 changes: 23 additions & 6 deletions src/navigate/controller/sub_controllers/stages.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class StageController(GUIController):
def __init__(
self,
view: StageControlTab,
main_view: MainApp,
canvas: tk.Canvas,
parent_controller: "navigate.controller.controller.Controller",
) -> None:
"""Initializes the StageController
Expand All @@ -74,10 +72,6 @@ def __init__(
----------
view : navigate.view.stage_view.StageView
The stage view
main_view : tkinter.Tk
The main view of the microscope
canvas : tkinter.Canvas
The canvas of the microscope
parent_controller : navigate.controller.Controller
The parent controller of the stage controller
"""
Expand Down Expand Up @@ -110,6 +104,7 @@ def __init__(
if axis in ["x", "y", "z", "theta", "f"]:
continue
view.add_additional_stage(axis)

#: dict: The event id
self.event_id = dict(zip(all_stage_axes, [None] * len(all_stage_axes)))

Expand Down Expand Up @@ -271,6 +266,16 @@ def initialize(self) -> None:
self.joystick_axes = self.new_joystick_axes
self.flip_flags = config.stage_flip_flags

# home button
home_dict = self.parent_controller.configuration_controller.stage_home_position
empty_home_dict = all(value is None for value in home_dict.values())
if empty_home_dict:
self.view.stop_frame.home_btn.grid_forget()
else:
self.view.stop_frame.home_btn.grid()
self.view.stop_frame.home_btn.configure(command=self.home_button_handler)


def disable_synthetic_stages(self, config: ConfigurationController) -> None:
"""Disable synthetic stages.

Expand Down Expand Up @@ -511,6 +516,18 @@ def stop_button_handler(self, *args: Iterable) -> None:
"""
self.view.after(250, lambda *args: self.parent_controller.execute("stop_stage"))

# Tai's home button
def home_button_handler(self, *args: Iterable) -> None:
"""This function calls the home method of the stage.

Parameters
----------
*args : Iterable
Variable length argument list
"""
home = self.parent_controller.configuration_controller.stage_home_position
self.set_position(home)

def joystick_button_handler(
self, event: Optional[tk.Event] = None, *args: Iterable
) -> None:
Expand Down
Loading
Loading