From b2f8a3193a4488bebcf4d57ef41483cf0c81808e Mon Sep 17 00:00:00 2001 From: "Kevin M. Dean" Date: Sat, 21 Dec 2024 15:34:04 -0600 Subject: [PATCH 1/5] Added some shortcuts Escape to close windows, new menu entry for quitting, adding shortcuts to stage tab. --- .../controller/sub_controllers/autofocus.py | 17 +++++++---- .../controller/sub_controllers/menus.py | 16 +++++++++- .../controller/sub_controllers/tiling.py | 16 +++++++--- .../sub_controllers/waveform_popup.py | 23 +++++++------- .../view/main_window_content/stage_tab.py | 30 +++++++++++++++++++ .../view/popups/tiling_wizard_popup2.py | 2 +- 6 files changed, 80 insertions(+), 24 deletions(-) diff --git a/src/navigate/controller/sub_controllers/autofocus.py b/src/navigate/controller/sub_controllers/autofocus.py index a62f73301..a3c509bbf 100644 --- a/src/navigate/controller/sub_controllers/autofocus.py +++ b/src/navigate/controller/sub_controllers/autofocus.py @@ -76,12 +76,10 @@ def __init__(self, view, parent_controller): #: object: The autofocus coarse plot. self.coarse_plot = None - # add saving function to the function closing the window - exit_func = combine_funcs( - self.view.popup.dismiss, - lambda: delattr(self.parent_controller, "af_popup_controller"), - ) - self.view.popup.protocol("WM_DELETE_WINDOW", exit_func) + # Dismiss popup. + self.view.popup.protocol("WM_DELETE_WINDOW", self.close_popup) + self.view.popup.bind("", self.close_popup) + self.view.autofocus_btn.configure(command=self.start_autofocus) self.view.inputs["device"].get_variable().trace_add( "write", self.update_device_ref @@ -92,6 +90,13 @@ def __init__(self, view, parent_controller): for k in self.view.setting_vars: self.view.setting_vars[k].trace_add("write", self.update_setting_dict(k)) + def close_popup(self, *args) -> None: + """Closes the popup window""" + # We should add saving function to the function closing the window + + self.view.popup.dismiss() + delattr(self.parent_controller, "af_popup_controller") + def populate_experiment_values(self): """Populate Experiment Values diff --git a/src/navigate/controller/sub_controllers/menus.py b/src/navigate/controller/sub_controllers/menus.py index 685b9f629..cc643bf3a 100644 --- a/src/navigate/controller/sub_controllers/menus.py +++ b/src/navigate/controller/sub_controllers/menus.py @@ -244,7 +244,13 @@ def initialize_menus(self): None, ], "add_separator_1": [None, None, None, None, None], - "Open Log Files": ["standard", self.open_log_files, None, None, None], + "Open Log Files": [ + "standard", + self.open_log_files, + None, + None, + None + ], "Open Configuration Files": [ "standard", self.open_configuration_files, @@ -252,6 +258,14 @@ def initialize_menus(self): None, None, ], + "add_separator_2": [None, None, None, None, None], + "Quit": [ + "standard", + lambda *args: self.parent_controller.acquire_bar_controller.exit_program(), + "Control+q", + "", + "" + ], } } self.populate_menu(file_menu) diff --git a/src/navigate/controller/sub_controllers/tiling.py b/src/navigate/controller/sub_controllers/tiling.py index 76b6600a7..dfd89b2a0 100644 --- a/src/navigate/controller/sub_controllers/tiling.py +++ b/src/navigate/controller/sub_controllers/tiling.py @@ -234,12 +234,20 @@ def __init__(self, view, parent_controller): # Properly Closing Popup with parent controller self.view.popup.protocol( "WM_DELETE_WINDOW", - combine_funcs( - self.view.popup.dismiss, - lambda: delattr(self.parent_controller, "tiling_wizard_controller"), - ), + self.close_window, ) + self.view.popup.bind("", self.close_window) + + + def close_window(self, *args) -> None: + """Close the tiling wizard popup + + Closes the tiling wizard popup and deletes the controller + """ + self.view.popup.dismiss() + delattr(self.parent_controller, "tiling_wizard_controller") + def set_table(self): """Set the multipoint table to the values in the tiling wizard diff --git a/src/navigate/controller/sub_controllers/waveform_popup.py b/src/navigate/controller/sub_controllers/waveform_popup.py index 5798fdc08..bc4a0e370 100644 --- a/src/navigate/controller/sub_controllers/waveform_popup.py +++ b/src/navigate/controller/sub_controllers/waveform_popup.py @@ -199,20 +199,12 @@ def __init__(self, view, parent_controller, waveform_constants_path): ) # Save waveform constants upon closing the popup window - self.view.popup.protocol( - "WM_DELETE_WINDOW", - combine_funcs( - self.restore_amplitude, - self.save_waveform_constants, - self.view.popup.dismiss, - lambda: delattr(self.parent_controller, "waveform_popup_controller"), - ), - ) + self.view.popup.protocol("WM_DELETE_WINDOW", self.close_window) + self.view.popup.bind("", self.close_window) + # All channels use the same galvo parameters - self.widgets["all_channels"].widget.configure( - command=self.set_galvo_to_all_channels - ) + self.widgets["all_channels"].widget.configure(command=self.set_galvo_to_all_channels) # Populate widgets self.widgets["Mode"].widget["values"] = list( @@ -221,6 +213,13 @@ def __init__(self, view, parent_controller, waveform_constants_path): self.widgets["Mode"].widget["state"] = "readonly" self.widgets["Mag"].widget["state"] = "readonly" + def close_window(self, *args) -> None: + """Close the window.""" + self.restore_amplitude() + self.save_waveform_constants() + self.view.popup.dismiss() + delattr(self.parent_controller, "waveform_popup_controller") + def configure_widget_range(self): """Update widget ranges and precisions based on the current resolution mode. diff --git a/src/navigate/view/main_window_content/stage_tab.py b/src/navigate/view/main_window_content/stage_tab.py index c30b782ff..fc77c9439 100644 --- a/src/navigate/view/main_window_content/stage_tab.py +++ b/src/navigate/view/main_window_content/stage_tab.py @@ -531,6 +531,13 @@ def __init__( self.inputs[entry_names[i]].grid(row=i, column=0) self.frame_back_list[i].lower() + # Add a divider + ttk.Separator(self, orient="horizontal").grid(row=5, column=0, sticky="ew") + + self.stack_shortcuts = StackShortcuts(self) + self.stack_shortcuts.grid(row=6, column=0, sticky="ew") + + def get_widgets(self) -> dict: """Get all widgets in the position frame @@ -592,6 +599,29 @@ def toggle_entry_states( frame_back_counter += 1 +class StackShortcuts(ttk.LabelFrame): + def __init__(self, position_frame: PositionFrame, *args: Iterable, **kwargs: dict) -> None: + """Initialize the stack shortcuts frame. + + Parameters + ---------- + position_frame : PositionFrame + The position frame that the stack shortcuts frame is in + *args : Iterable + Positional arguments for the ttk.Labelframe + **kwargs : Iterable + Keyword arguments for the ttk.Labelframe + """ + ttk.Labelframe.__init__( + self, position_frame, text="Z-Stack Start/Stop", *args, **kwargs + ) + + # Add two buttons + self.set_start_button = tk.Button(self, text="Set Start Pos/Foc") + self.set_start_button.grid(row=0, column=0, sticky="ew") + self.set_end_button = tk.Button(self, text="Set End Pos/Foc") + self.set_end_button.grid(row=1, column=0, sticky="ew") + class XYFrame(ttk.Labelframe): """Frame for the x and y movement buttons. diff --git a/src/navigate/view/popups/tiling_wizard_popup2.py b/src/navigate/view/popups/tiling_wizard_popup2.py index 0cfce8c20..78df4d3d5 100644 --- a/src/navigate/view/popups/tiling_wizard_popup2.py +++ b/src/navigate/view/popups/tiling_wizard_popup2.py @@ -144,7 +144,7 @@ def __init__(self, root, *args, **kwargs): self.inputs[dist_var].widget.state(["disabled"]) # FOV width for this particular axis (e.g. for X it is the number - # of vertical pixels on the camera multipled by the effective + # of vertical pixels on the camera multiplied by the effective # pixel size) fov_var = f"{ax.lower()}_fov" self.inputs[fov_var] = LabelInput( From 69fef1c05d2264be45ba8ad554b39345b582dbd8 Mon Sep 17 00:00:00 2001 From: "Kevin M. Dean" Date: Sun, 22 Dec 2024 06:16:50 -0600 Subject: [PATCH 2/5] Shortcut buttons, typehints Adjusted the stage tab to include short cuts for setting the start and stop positions of a z-stack. This needs to be tested. Also went through and cleaned up the channels_tab.py module to include typehints. One significant change included removing the call_parent input argument from update_timepoint_settings. This was unused... --- .../sub_controllers/channels_tab.py | 230 +++++++++--------- .../controller/sub_controllers/stages.py | 17 ++ .../main_window_content/settings_notebook.py | 2 +- .../view/main_window_content/stage_tab.py | 29 ++- 4 files changed, 147 insertions(+), 131 deletions(-) diff --git a/src/navigate/controller/sub_controllers/channels_tab.py b/src/navigate/controller/sub_controllers/channels_tab.py index 63e4cde2c..fd1ecd5ec 100644 --- a/src/navigate/controller/sub_controllers/channels_tab.py +++ b/src/navigate/controller/sub_controllers/channels_tab.py @@ -33,21 +33,19 @@ # Standard Library Imports import logging import datetime +from typing import Optional, Dict, Any # Third Party Imports import numpy as np import tkinter as tk +import navigate # Local Imports from navigate.controller.sub_controllers.gui import GUIController from navigate.controller.sub_controllers.channels_settings import ( - ChannelSettingController, -) -from navigate.controller.sub_controllers.tiling import ( - TilingWizardController, -) - -# View Imports that are not called on startup + ChannelSettingController) +from navigate.controller.sub_controllers.tiling import TilingWizardController +from navigate.view.main_window_content.channels_tab import ChannelsTab from navigate.view.popups.tiling_wizard_popup2 import TilingWizardPopup # Logger Setup @@ -58,19 +56,23 @@ class ChannelsTabController(GUIController): """Controller for the channels tab in the main window.""" - def __init__(self, view, parent_controller=None): + def __init__( + self, + view: ChannelsTab, + parent_controller: Optional['navigate.controller.controller.Controller'] + ) -> None: """Initialize the ChannelsTabController. Parameters ---------- - view : tkinter.Frame - The view that this controller will control. - parent_controller : navigate.controller.main_controller.MainController + view : ChannelsTab + The ChannelsTab Window. + parent_controller : navigate.controller.controller.Controller The parent controller of this controller. """ super().__init__(view, parent_controller) - #: bool: Whether or not the user has selected to save the data. + #: bool: Whether the user has selected to save the data. self.is_save = False #: str: The current acquisition mode. @@ -88,7 +90,6 @@ def __init__(self, view, parent_controller=None): self.view.stack_timepoint_frame.exp_time_spinbox.set_precision(0) - # Get Widgets and Buttons from stack_acquisition_settings in view #: dict: The widgets in the stack acquisition settings frame. self.stack_acq_widgets = self.view.stack_acq_frame.get_widgets() @@ -102,11 +103,8 @@ def __init__(self, view, parent_controller=None): self.stack_acq_vals["step_size"].trace_add("write", self.update_z_steps) self.stack_acq_vals["start_position"].trace_add("write", self.update_z_steps) self.stack_acq_vals["end_position"].trace_add("write", self.update_z_steps) - # TODO: could be remove later self.stack_acq_vals["start_focus"].trace_add("write", self.update_z_steps) - self.stack_acq_buttons["set_start"].configure( - command=self.update_start_position - ) + self.stack_acq_buttons["set_start"].configure(command=self.update_start_position) self.stack_acq_buttons["set_end"].configure(command=self.update_end_position) # stack acquisition_variables @@ -138,23 +136,23 @@ def __init__(self, view, parent_controller=None): "timepoint_interval": temp.timepoint_interval_spinval, } - # timepoint event binds + # time point event binds self.timepoint_vals["is_save"].trace_add("write", self.update_save_setting) self.timepoint_vals["timepoints"].trace_add( - "write", lambda *args: self.update_timepoint_setting(True) + "write", lambda *args: self.update_timepoint_setting() ) self.timepoint_vals["stack_pause"].trace_add( - "write", lambda *args: self.update_timepoint_setting(True) + "write", lambda *args: self.update_timepoint_setting() ) # Multi Position Acquisition - #: bool: Whether or not the user has selected to use multiposition. + #: bool: Whether the user has selected to use multi-position. self.is_multiposition = False - #: bool: cache multiposition flag + #: bool: cache multi-position flag self.is_multiposition_cache = False - #: bool: Whether or not the user has selected to use multiposition. + #: bool: Whether the user has selected to use multi-position. self.is_multiposition_val = self.view.multipoint_frame.on_off self.is_multiposition_val.trace_add("write", self.toggle_multiposition) @@ -174,8 +172,13 @@ def __init__(self, view, parent_controller=None): self.initialize() - def initialize(self): - """Initializes widgets and gets other necessary configuration.""" + def initialize(self) -> None: + """Initializes widgets and gets other necessary configuration. + + The `initialize` method in the `ChannelsTabController` class is responsible + for setting up the initial configuration and state of the channels tab in the + main window. + """ config = self.parent_controller.configuration_controller self.stack_acq_widgets["cycling"].widget["values"] = ["Per Z", "Per Stack"] @@ -187,9 +190,16 @@ def initialize(self): self.set_spinbox_range_limits(self.parent_controller.configuration["gui"]) self.show_verbose_info("channels tab has been initialized") - def populate_experiment_values(self): + def populate_experiment_values(self) -> None: """Distribute initial MicroscopeState values to this and sub-controllers and associated views. + + The populate_experiment_values method in the ChannelsTabController class is + responsible for distributing initial MicroscopeState values to this and + sub-controllers and associated views. It sets the initial values for various + settings, validates the configuration for multi-position settings, + and updates the GUI accordingly. + """ self.in_initialization = True self.microscope_state_dict = self.parent_controller.configuration["experiment"][ @@ -200,10 +210,9 @@ def populate_experiment_values(self): "step_size" ] self.set_info(self.stack_acq_vals, self.microscope_state_dict) - # self.set_info(self.conpro_acq_vals, self.microscope_state_dict) self.set_info(self.timepoint_vals, self.microscope_state_dict) - # check configuration for multiposition settings + # check configuration for multi-position settings self.is_multiposition_val.set(self.microscope_state_dict["is_multiposition"]) self.is_multiposition_cache = self.is_multiposition self.toggle_multiposition() @@ -240,12 +249,16 @@ def populate_experiment_values(self): self.show_verbose_info("Channels tab has been set new values") - def set_spinbox_range_limits(self, settings): - """This function will set the spinbox widget's values of from_, to, step + def set_spinbox_range_limits(self, settings: Dict[str, Any]) -> None: + """Sets range limits for various spinbox widgets based on the provided settings. + + This method configures the minimum, maximum, and increment values for the spinbox + widgets in the stack acquisition settings frame and the time point settings + frame based on the provided configuration settings. Parameters ---------- - settings : dict + settings : Dict[str, Any] dictionary of settings from configuration file """ @@ -301,9 +314,12 @@ def set_spinbox_range_limits(self, settings): # Channel settings self.channel_setting_controller.set_spinbox_range_limits(settings) - def set_mode(self, mode): + def set_mode(self, mode: str) -> None: """Change acquisition mode. + The set_mode method changes the acquisition mode and updates the state of + various widgets accordingly + Parameters ---------- mode : str @@ -354,18 +370,17 @@ def set_mode(self, mode): self.show_verbose_info("acquisition mode has been changed to", mode) - def update_z_steps(self, *args): + def update_z_steps(self, *_: tuple[str]) -> None: """Recalculates the number of slices that will be acquired in a z-stack. Requires GUI to have start position, end position, or step size changed. - Sets the number of slices in the model and the GUI. - Sends the current values to central/parent controller + Sets the number of slices in the model and the GUI. Sends the current values + to central/parent controller Parameters ---------- - args : dict - Values is a dict as follows {'step_size': 'start_position': , - 'end_position': ,'number_z_steps'} + _ : tuple[str] + Values is a tuple of strings. e.g., ('PY_VAR0', '', 'write') """ # won't do any calculation when initialization @@ -374,7 +389,7 @@ def update_z_steps(self, *args): # Calculate the number of slices and set GUI try: - # validate the spinbox's value + # validate the spin box's value start_position = float(self.stack_acq_vals["start_position"].get()) end_position = float(self.stack_acq_vals["end_position"].get()) step_size = float(self.stack_acq_vals["step_size"].get()) @@ -383,7 +398,7 @@ def update_z_steps(self, *args): self.stack_acq_vals["abs_z_start"].set(0) self.stack_acq_vals["abs_z_end"].set(0) return - except tk._tkinter.TclError: + except tk.TclError: self.stack_acq_vals["number_z_steps"].set(0) self.stack_acq_vals["abs_z_start"].set(0) self.stack_acq_vals["abs_z_end"].set(0) @@ -392,10 +407,6 @@ def update_z_steps(self, *args): logger.error("Error caught: updating z_steps") return - # if step_size < 0.001: - # step_size = 0.001 - # self.stack_acq_vals['step_size'].set(step_size) - number_z_steps = int( np.ceil(np.abs((end_position - start_position) / step_size)) ) @@ -425,13 +436,13 @@ def update_z_steps(self, *args): self.microscope_state_dict["start_focus"] = self.stack_acq_vals[ "start_focus" ].get() - except tk._tkinter.TclError: + except tk.TclError: self.microscope_state_dict["start_focus"] = 0 try: self.microscope_state_dict["end_focus"] = self.stack_acq_vals[ "end_focus" ].get() - except tk._tkinter.TclError: + except tk.TclError: self.microscope_state_dict["end_focus"] = 0 self.microscope_state_dict["stack_z_origin"] = self.z_origin self.microscope_state_dict["stack_focus_origin"] = self.focus_origin @@ -442,14 +453,13 @@ def update_z_steps(self, *args): "recalculated" ) - def update_start_position(self, *args): + def update_start_position(self, *_: tuple[str]) -> None: """Get new z starting position from current stage parameters. Parameters ---------- - args : dict - Values is a dict as follows {'start_position': , 'abs_z_start': , - 'stack_z_origin': } + _ : tuple[str] + Values is a tuple of strings. e.g., ('PY_VAR0', '', 'write') """ # We have a new origin @@ -471,14 +481,13 @@ def update_start_position(self, *args): # Propagate parameter changes to the GUI self.update_z_steps() - def update_end_position(self, *args): + def update_end_position(self, *_: tuple[str]) -> None: """Get new z ending position from current stage parameters Parameters ---------- - args : dict - Values is a dict as follows {'end_position': , 'abs_z_end': , - 'stack_z_origin': } + _ : tuple[str] + Values is a tuple of strings. e.g., ('PY_VAR0', '', 'write') """ # Grab current values @@ -516,21 +525,20 @@ def update_end_position(self, *args): self.stack_acq_vals["end_focus"].set(end_focus) self.update_z_steps() - def update_cycling_setting(self, *args): + def update_cycling_setting(self, *_: tuple[str]) -> None: """Update the cycling settings in the model and the GUI. You can collect different channels in different formats. - In the perZ format: Slice 0/Ch0, Slice0/Ch1, Slice1/Ch0, Slice1/Ch1, etc + In the perZ format: Slice 0/Ch0, Slice0/Ch1, Slice1/Ch0, Slice1/Ch1, etc. in the perStack format: Slice 0/Ch0, Slice1/Ch0... SliceN/Ch0. Then it repeats with Ch1 Parameters ---------- - args : dict - Values is a dict as follows {'cycling_setting': , 'cycling_setting': , - 'stack_z_origin': } - + _ : tuple[str] + Values is a tuple of strings. e.g., ('PY_VAR0', '', 'write') """ + # won't do any calculation when initializing if self.in_initialization: return @@ -546,43 +554,35 @@ def update_cycling_setting(self, *args): self.show_verbose_info("Cycling setting on channels tab has been changed") - def update_save_setting(self, *args): - """Tell the centrol/parent controller 'save_data' is selected. + def update_save_setting(self, *_: tuple[str]) -> None: + """Tell the parent controller 'save_data' is selected. Does not do any calculation when initializing the software. Parameters ---------- - args : dict - Values is a dict as follows {'save_data': , 'save_data': , - 'stack_z_origin': } + _ : tuple[str] + Values is a tuple of strings. e.g., ('PY_VAR0', '', 'write') """ if self.in_initialization: return self.is_save = self.timepoint_vals["is_save"].get() - # update experiment MicroscopeState dict self.microscope_state_dict["is_save"] = self.is_save self.parent_controller.execute("set_save", self.is_save) self.show_verbose_info("Save data option has been changed to", self.is_save) - def update_timepoint_setting(self, call_parent=False): + def update_timepoint_setting(self) -> None: """Automatically calculates the stack acquisition time based on the number of time points, channels, and exposure time. - TODO: Add necessary computation for 'Stack Acq.Time', 'Timepoint Interval', + TODO: Add necessary computation for 'Stack Acq.Time', 'Time point Interval', 'Experiment Duration'? Does not do any calculation when initializing the software. - Order of priority for perStack: timepoints > positions > channels > z-steps + Order of priority for per_stack: timepoints > positions > channels > z-steps > delay - ORder of priority for perZ: timepoints > positions > z-steps > delays > channels - - Parameters - ---------- - call_parent : bool - Tell parent controller that time point setting has changed. - + Order of priority for perZ: timepoints > positions > z-steps > delays > channels """ if self.in_initialization: @@ -594,7 +594,7 @@ def update_timepoint_setting(self, call_parent=False): else 1 ) channel_exposure_time = [] - # validate the spinbox's value + # validate the spin box's value try: number_of_timepoints = int(float(self.timepoint_vals["timepoints"].get())) number_of_slices = int(self.stack_acq_vals["number_z_steps"].get()) @@ -604,22 +604,22 @@ def update_timepoint_setting(self, call_parent=False): channel_exposure_time.append(float(channel["camera_exposure_time"])) if len(channel_exposure_time) == 0: return - except (tk._tkinter.TclError, ValueError): + except (tk.TclError, ValueError): self.timepoint_vals["experiment_duration"].set("0") self.timepoint_vals["stack_acq_time"].set("0") return except (KeyError, AttributeError): - logger.error("Error caught: updating timepoint setting") + logger.error("Error caught: updating time point setting") return - perStack = self.stack_acq_vals["cycling"].get() == "Per Stack" + per_stack = self.stack_acq_vals["cycling"].get() == "Per Stack" # Initialize variable to keep track of how long the entire experiment will take. # Includes time, positions, channels... experiment_duration = 0 # Initialize variable to calculate how long it takes to acquire a single volume - # for all of the channels. Only calculate once at the beginning. + # for all the channels. Only calculate once at the beginning. stack_acquisition_duration = 0 for position_idx in range(number_of_positions): @@ -635,7 +635,7 @@ def update_timepoint_setting(self, call_parent=False): # distance = [x2-x1, y2-y1, z2-z1, theta2-theta1, f2-f1] # max_distance_idx = np.argmax(distance) # Now if we are going to do this properly, we would need to do this for - # all of the positions so that we can calculate the total experiment + # all the positions so that we can calculate the total experiment # time. Probably assemble a matrix of all the positions and then do # the calculations. @@ -647,8 +647,8 @@ def update_timepoint_setting(self, call_parent=False): experiment_duration = experiment_duration + stage_delay for channel_idx in range(len(channel_exposure_time)): - if perStack: - # In the perStack mode, we only need to account for the time + if per_stack: + # In the per_stack mode, we only need to account for the time # necessary for the filter wheel to change between each # image stack. if channel_idx == 0 and position_idx == 0: @@ -675,7 +675,7 @@ def update_timepoint_setting(self, call_parent=False): # Change the filter wheel here before the start of the acquisition. if len(channel_exposure_time) > 1: filter_wheel_change_times = number_of_timepoints * ( - 1 if perStack else number_of_slices + 1 if per_stack else number_of_slices ) experiment_duration += ( sum(self.filter_wheel_delay) * filter_wheel_change_times @@ -694,41 +694,45 @@ def update_timepoint_setting(self, call_parent=False): self.microscope_state_dict["stack_pause"] = self.timepoint_vals[ "stack_pause" ].get() - # self.microscope_state_dict['timepoint_interval'] self.microscope_state_dict["stack_acq_time"] = stack_acquisition_duration self.microscope_state_dict["experiment_duration"] = experiment_duration self.show_verbose_info( - "timepoint settings on channels tab have been changed and recalculated" + "time point settings on channels tab have been changed and recalculated" ) - def toggle_multiposition(self, *args): + def toggle_multiposition(self, *_: tuple[str]) -> None: """Toggle Multi-position Acquisition. Recalculates the experiment duration. + + Parameters + ---------- + _ : tuple[str] + Values is a tuple of strings. e.g., ('PY_VAR0', '', 'write') """ self.is_multiposition = self.is_multiposition_val.get() self.microscope_state_dict["is_multiposition"] = self.is_multiposition self.update_timepoint_setting() self.show_verbose_info("Multi-position:", self.is_multiposition) - def disable_multiposition_btn(self): - """Disable multiposition button""" + def disable_multiposition_btn(self) -> None: + """Disable multi-position button""" self.view.multipoint_frame.save_check.config(state="disabled") - def enable_multiposition_btn(self): - """Enable multiposition button""" + def enable_multiposition_btn(self) -> None: + """Enable multi-position button""" self.view.multipoint_frame.save_check.config(state="normal") - def launch_waveform_parameters(self): + def launch_waveform_parameters(self) -> None: """Launches waveform parameters popup.""" self.parent_controller.menu_controller.popup_waveform_setting() - def launch_autofocus_settings(self): + def launch_autofocus_settings(self) -> None: """Launches autofocus settings popup.""" self.parent_controller.menu_controller.popup_autofocus_setting() - def launch_tiling_wizard(self): + def launch_tiling_wizard(self) -> None: """Launches tiling wizard popup. Will only launch when button in GUI is pressed, and will not duplicate. @@ -741,21 +745,22 @@ def launch_tiling_wizard(self): tiling_wizard = TilingWizardPopup(self.view) self.tiling_wizard_controller = TilingWizardController(tiling_wizard, self) - def set_info(self, vals, values): + @staticmethod + def set_info(vals: Dict[str, Any], values: Dict[str, Any]) -> None: """Set values to a list of variables. Parameters ---------- - vals : list - List of variables to set. - values : list - List of values to set to variables. + vals : Dict[str, Any] + A dictionary of variables to set. + values : Dict[str, Any] + A dictionary of values to set to variables. """ for name in values.keys(): if name in vals: vals[name].set(values[name]) - def execute(self, command, *args): + def execute(self, command: str, *args: tuple[str]) -> None: """Execute Command in the parent controller. Parameters @@ -763,13 +768,8 @@ def execute(self, command, *args): command : str recalculate_timepoint, channel, move_stage_and_update_info, get_stage_position - args : list - List of arguments to pass to the command. - - Returns - ------- - command : object - Returns parent_controller.execute(command) if command = 'get_stage_position' + args : tuple[str] + A tuple of arguments to pass to the command. """ if command == "recalculate_timepoint": self.update_timepoint_setting() @@ -792,18 +792,17 @@ def execute(self, command, *args): self.show_verbose_info("Received command from child", command, args) - def update_experiment_values(self): + def update_experiment_values(self) -> None: """Update experiment values""" self.channel_setting_controller.update_experiment_values() self.update_z_steps() - - def verify_experiment_values(self): + def verify_experiment_values(self) -> str: """Verify channel tab settings and return warning info Returns ------- - string + string: str Warning info """ warning = self.channel_setting_controller.verify_experiment_values() @@ -819,13 +818,14 @@ def verify_experiment_values(self): return "The number of Z steps should be at least 1!" try: float(self.microscope_state_dict["stack_pause"]) - except Exception: + except Exception as e: + logger.exception(e) return "Stack pause should be a valid number!" if self.microscope_state_dict["timepoints"] < 1: return "Timepoints should be at least 1!" return "" - def set_exposure_time(self, channel_exposure_time): + def set_exposure_time(self, channel_exposure_time: tuple[str, float]) -> None: """Set exposure time for a specified channel Parameters @@ -842,6 +842,6 @@ def set_exposure_time(self, channel_exposure_time): self.channel_setting_controller.in_initialization = False @property - def custom_events(self): + def custom_events(self) -> dict[str, callable]: """Custom events for the channels tab.""" return {"exposure_time": self.set_exposure_time} diff --git a/src/navigate/controller/sub_controllers/stages.py b/src/navigate/controller/sub_controllers/stages.py index 5b6f35e96..cce7dc3ed 100644 --- a/src/navigate/controller/sub_controllers/stages.py +++ b/src/navigate/controller/sub_controllers/stages.py @@ -157,6 +157,16 @@ def __init__( self.initialize() self.set_hover_descriptions() + # Bind the buttons for the z-stack start and stop. + self.view.stack_shortcuts.set_start_button.configure( + command=self.parent_controller.channels_tab_controller.update_start_position + ) + self.view.stack_shortcuts.set_end_button.configure( + command=self.parent_controller.channels_tab_controller.update_end_position + ) + + + def stage_key_press(self, event: tk.Event) -> None: """The stage key press. @@ -661,6 +671,13 @@ def set_hover_descriptions(self) -> None: "Theta stage position in degrees." ) + self.view.stack_shortcuts.set_start_button.hover.setdescription( + "Sets the start positions for Z and F for a Z-Stack." + ) + self.view.stack_shortcuts.set_end_button.hover.setdescription( + "Sets the end positions for Z and F for a Z-Stack." + ) + # Stop button. self.view.stop_frame.joystick_btn.hover.setdescription( "Enables/Disables joystick mode." diff --git a/src/navigate/view/main_window_content/settings_notebook.py b/src/navigate/view/main_window_content/settings_notebook.py index 450d33ee5..c8b2b844e 100644 --- a/src/navigate/view/main_window_content/settings_notebook.py +++ b/src/navigate/view/main_window_content/settings_notebook.py @@ -58,7 +58,7 @@ class SettingsNotebook(DockableNotebook): - Channels - Camera Settings - Stage Control - - Multiposition Table + - Multi-position Table """ def __init__(self, diff --git a/src/navigate/view/main_window_content/stage_tab.py b/src/navigate/view/main_window_content/stage_tab.py index fc77c9439..fcd9e8d1f 100644 --- a/src/navigate/view/main_window_content/stage_tab.py +++ b/src/navigate/view/main_window_content/stage_tab.py @@ -156,29 +156,33 @@ def __init__( #: PositionFrame: Position frame. self.position_frame = PositionFrame(self) - self.position_frame.grid(row=0, column=0, sticky=tk.NSEW, padx=3, pady=3) + self.position_frame.grid(row=0, column=0, rowspan=1, sticky=tk.NSEW, padx=3, pady=3) + + #: StackShortcuts: Stack shortcuts. + self.stack_shortcuts = StackShortcuts(self) + self.stack_shortcuts.grid(row=1, column=0, rowspan=1, sticky=tk.NSEW) #: XYFrame: XY frame. self.xy_frame = XYFrame(self) - self.xy_frame.grid(row=0, column=1, sticky=tk.NSEW, padx=3, pady=3) + self.xy_frame.grid(row=0, column=1, rowspan=2, sticky=tk.NSEW, padx=3, pady=3) #: OtherAxisFrame: Z frame. self.z_frame = OtherAxisFrame(stage_control_tab=self, name="Z") - self.z_frame.grid(row=0, column=2, sticky=tk.NSEW, padx=3, pady=3) + self.z_frame.grid(row=0, column=2, rowspan=2, sticky=tk.NSEW, padx=3, pady=3) #: OtherAxisFrame: Theta frame. self.theta_frame = OtherAxisFrame(stage_control_tab=self, name="Theta") - self.theta_frame.grid(row=1, column=2, sticky=tk.NSEW, padx=3, pady=3) + self.theta_frame.grid(row=2, column=2, rowspan=2, sticky=tk.NSEW, padx=3, pady=3) # OtherAxisFrame: Focus frame. self.f_frame = OtherAxisFrame(stage_control_tab=self, name="Focus") - self.f_frame.grid(row=1, column=0, sticky=tk.NSEW, padx=3, pady=3) + self.f_frame.grid(row=2, column=0, rowspan=2, sticky=tk.NSEW, padx=3, pady=3) #: StopFrame: Stop frame. self.stop_frame = StopFrame( stage_control_tab=self, name="Stage Movement Interrupt" ) - self.stop_frame.grid(row=1, column=1, sticky=tk.NSEW, padx=3, pady=3) + self.stop_frame.grid(row=2, column=1, rowspan=2, sticky=tk.NSEW, padx=3, pady=3) def load_images(self) -> None: """Load images for the stage control tab.""" @@ -531,13 +535,6 @@ def __init__( self.inputs[entry_names[i]].grid(row=i, column=0) self.frame_back_list[i].lower() - # Add a divider - ttk.Separator(self, orient="horizontal").grid(row=5, column=0, sticky="ew") - - self.stack_shortcuts = StackShortcuts(self) - self.stack_shortcuts.grid(row=6, column=0, sticky="ew") - - def get_widgets(self) -> dict: """Get all widgets in the position frame @@ -617,11 +614,13 @@ def __init__(self, position_frame: PositionFrame, *args: Iterable, **kwargs: dic ) # Add two buttons - self.set_start_button = tk.Button(self, text="Set Start Pos/Foc") + self.set_start_button = HoverTkButton(self, text="Set Start Pos/Foc") self.set_start_button.grid(row=0, column=0, sticky="ew") - self.set_end_button = tk.Button(self, text="Set End Pos/Foc") + + self.set_end_button = HoverTkButton(self, text="Set End Pos/Foc") self.set_end_button.grid(row=1, column=0, sticky="ew") + class XYFrame(ttk.Labelframe): """Frame for the x and y movement buttons. From dabf9a9992c201fd663f3153026e946d3c171abd Mon Sep 17 00:00:00 2001 From: "Kevin M. Dean" Date: Sun, 22 Dec 2024 06:34:37 -0600 Subject: [PATCH 3/5] Typehint autofocus controller. --- .../controller/sub_controllers/autofocus.py | 81 +++++++++++++------ 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/src/navigate/controller/sub_controllers/autofocus.py b/src/navigate/controller/sub_controllers/autofocus.py index a3c509bbf..979f52385 100644 --- a/src/navigate/controller/sub_controllers/autofocus.py +++ b/src/navigate/controller/sub_controllers/autofocus.py @@ -31,6 +31,7 @@ # POSSIBILITY OF SUCH DAMAGE. # Standard Library Imports +import logging # Third Party Imports import numpy as np @@ -38,11 +39,9 @@ from tkinter import messagebox # Local Imports +import navigate from navigate.controller.sub_controllers.gui import GUIController -from navigate.tools.common_functions import combine_funcs - - -import logging +from navigate.view.popups.autofocus_setting_popup import AutofocusPopup # Logger Setup p = __name__.split(".")[1] @@ -52,13 +51,16 @@ class AutofocusPopupController(GUIController): """Class creates the popup to configure autofocus parameters.""" - def __init__(self, view, parent_controller): + def __init__( + self, + view: AutofocusPopup, + parent_controller: 'navigate.controller.controller.Controller') -> None: """ Parameters ---------- - view : navigate.view.popups.autofocus_setting_popup.AutofocusPopup + view : AutofocusPopup The view of the autofocus popup. - parent_controller : navigate.controller.main_controller.MainController + parent_controller : navigate.controller.controller.Controller The parent controller of the autofocus popup. """ super().__init__(view, parent_controller) @@ -66,6 +68,12 @@ def __init__(self, view, parent_controller): #: dict: The autofocus setting dictionary. self.widgets = self.view.get_widgets() + #: str: The microscope name. + self.microscope_name = None + + #: dict: The autofocus setting dictionary. + self.setting_dict = None + #: object: The autofocus figure. self.autofocus_fig = self.view.fig @@ -90,26 +98,31 @@ def __init__(self, view, parent_controller): for k in self.view.setting_vars: self.view.setting_vars[k].trace_add("write", self.update_setting_dict(k)) - def close_popup(self, *args) -> None: - """Closes the popup window""" + def close_popup(self, *_: tuple[str]) -> None: + """Closes the popup window + + Parameters + ---------- + _ : tuple[str] + The event arguments. + """ # We should add saving function to the function closing the window self.view.popup.dismiss() delattr(self.parent_controller, "af_popup_controller") - def populate_experiment_values(self): + def populate_experiment_values(self) -> None: """Populate Experiment Values Populates the experiment values from the experiment settings dictionary """ - #: dict: The autofocus setting dictionary. self.setting_dict = self.parent_controller.configuration["experiment"][ "AutoFocusParameters" ] - #: str: The microscope name. self.microscope_name = self.parent_controller.configuration["experiment"][ "MicroscopeState" ]["microscope_name"] + setting_dict = self.setting_dict[self.microscope_name] # Default to stages, if they exist. @@ -131,12 +144,12 @@ def populate_experiment_values(self): for k in self.view.setting_vars: self.view.setting_vars[k].set(setting_dict[device][device_ref][k]) - def showup(self): + def showup(self) -> None: """Shows the popup window""" self.view.popup.deiconify() self.view.popup.attributes("-topmost", 1) - def start_autofocus(self): + def start_autofocus(self) -> None: """Starts the autofocus process.""" device = self.widgets["device"].widget.get() device_ref = self.widgets["device_ref"].widget.get() @@ -146,6 +159,7 @@ def start_autofocus(self): self.parent_controller.configuration["experiment"]["MicroscopeState"][ "autofocus_device_ref" ] = device_ref + # verify autofocus parameters setting_dict = self.setting_dict[self.microscope_name][device][device_ref] warning_message = "" @@ -156,7 +170,8 @@ def start_autofocus(self): value = float(setting_dict[f"{k}_range"]) if step <= 0 or value < step: warning_message += f"{k} settings are not correct!\n" - except Exception: + except Exception as e: + logger.exception(e) warning_message += f"{k} settings are not correct!\n" if warning_message: messagebox.showerror( @@ -166,24 +181,26 @@ def start_autofocus(self): return self.parent_controller.execute("autofocus", device, device_ref) - def update_device_ref(self, *args): + def update_device_ref(self, *_: tuple[str]) -> None: """Update device reference name Parameters ---------- - args: tk event arguments + _: tuple[str] + The event arguments. """ device = self.widgets["device"].widget.get() device_refs = self.setting_dict[self.microscope_name][device].keys() self.widgets["device_ref"].widget["values"] = device_refs self.widgets["device_ref"].widget.set(device_refs[0]) - def show_autofocus_setting(self, *args): + def show_autofocus_setting(self, *_: tuple[str]) -> None: """Show Autofocus Parameters Parameters ---------- - args: tk event arguments + _: tuple[str] + The event arguments. """ device = self.widgets["device"].widget.get() device_ref = self.widgets["device_ref"].widget.get() @@ -191,16 +208,21 @@ def show_autofocus_setting(self, *args): for k in self.view.setting_vars: self.view.setting_vars[k].set(setting_dict[device][device_ref][k]) - def update_setting_dict(self, parameter): + def update_setting_dict(self, parameter: str) -> callable: """Show Autofocus Parameters Parameters ---------- parameter : str The parameter to be updated. + + Returns + ------- + callable + The function to update the parameter """ - def func(*args): + def func(*_: tuple[str]) -> None: device = self.widgets["device"].widget.get() device_ref = self.widgets["device_ref"].widget.get() self.setting_dict[self.microscope_name][device][device_ref][ @@ -209,10 +231,13 @@ def func(*args): return func - def display_plot(self, data_and_flags): + def display_plot( + self, + data_and_flags: tuple[np.ndarray, bool, bool] + ) -> None: """Displays the autofocus plot - data : tuple (numpy.ndarray, bool, bool) + data : tuple[np.ndarray, bool, bool] (data, line_plot, clear_data) data: The data to be plotted. line_plot: @@ -278,6 +303,12 @@ def display_plot(self, data_and_flags): self.autofocus_fig.canvas.draw_idle() @property - def custom_events(self): - """dict: Custom events for this controller""" + def custom_events(self) -> dict[str, callable]: + """dict: Custom events for this controller + + Returns + ------- + dict[str, callable] + The custom events for this controller. + """ return {"autofocus": self.display_plot} From c95151317fc910639b51cd6cf30ce8871af6c533 Mon Sep 17 00:00:00 2001 From: "Kevin M. Dean" Date: Sun, 22 Dec 2024 13:42:06 -0600 Subject: [PATCH 4/5] Clean up camera_tab --- .../view/main_window_content/camera_tab.py | 78 +++++++------------ 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/src/navigate/view/main_window_content/camera_tab.py b/src/navigate/view/main_window_content/camera_tab.py index 32ce62055..5ac66a202 100644 --- a/src/navigate/view/main_window_content/camera_tab.py +++ b/src/navigate/view/main_window_content/camera_tab.py @@ -118,56 +118,44 @@ def __init__(self, settings_tab, *args, **kwargs): Keyword arguments for ttk.LabelFrame """ # Init Frame - text_label = "Camera Modes" + text_label = "Camera Mode" ttk.Labelframe.__init__(self, settings_tab, text=text_label, *args, **kwargs) - # Formatting - tk.Grid.columnconfigure(self, "all", weight=1) - tk.Grid.rowconfigure(self, "all", weight=1) - - # Holds dropdowns, this is done in case more widgets are to be added in a - # different frame, these can be grouped together - #: ttk.Frame: The parent frame for any widgets you add. - content_frame = ttk.Frame(self) - content_frame.grid(row=0, column=0, sticky=tk.NSEW, pady=5) - - # Formatting - tk.Grid.columnconfigure(content_frame, "all", weight=1) - tk.Grid.rowconfigure(content_frame, "all", weight=1) - - # Dictionary for all the variables, this will be used by the controller #: dict: Dictionary of all the widgets in the frame. self.inputs = {} + #: list: List of all the labels for the widgets. self.labels = ["Sensor Mode", "Readout Direction", "Number of Pixels"] + #: list: List of all the names for the widgets. self.names = ["Sensor", "Readout", "Pixels"] + for i in range(len(self.labels)): + self.rowconfigure(i, weight=1, uniform="1") + for i in range(2): + self.columnconfigure(i, weight=1, uniform="1") + # Dropdown loop for i in range(len(self.labels)): + label = ttk.Label(self, text=self.labels[i]) + label.grid(row=i, column=0, pady=3, padx=5, sticky=tk.W) + if i < len(self.labels) - 1: self.inputs[self.names[i]] = LabelInput( - parent=content_frame, - label=self.labels[i], + parent=self, input_class=ttk.Combobox, input_var=tk.StringVar(), input_args={"width": 12}, ) - self.inputs[self.names[i]].grid(row=i, column=0, pady=3, padx=5) else: self.inputs[self.names[i]] = LabelInput( - parent=content_frame, - label=self.labels[i], + parent=self, input_class=ValidatedSpinbox, input_var=tk.StringVar(), input_args={"from_": 0, "to": 10000, "increment": 1, "width": 5}, ) - self.inputs[self.names[i]].grid(row=i, column=0, pady=3, padx=5) - - # Additional widget settings - self.inputs["Sensor"].label.grid(padx=(0, 36)) - self.inputs["Readout"].label.grid(padx=(0, 10)) - self.inputs["Pixels"].label.grid(padx=(0, 15)) + self.inputs[self.names[i]].grid( + row=i, column=1, pady=3, padx=5, sticky=tk.W) def get_variables(self): """Get Variables. @@ -229,20 +217,9 @@ def __init__(self, settings_tab, *args, **kwargs): text_label = "Framerate Info" ttk.LabelFrame.__init__(self, settings_tab, text=text_label, *args, **kwargs) - # Holds widgets, this is done in case more widgets are to be - # added in a different frame, these can be grouped together - content_frame = ttk.Frame(self) - content_frame.grid(row=0, column=0, sticky=tk.NSEW, pady=5, padx=5) - - # Formatting - tk.Grid.columnconfigure(self, "all", weight=1) - tk.Grid.rowconfigure(self, "all", weight=1) - tk.Grid.columnconfigure(content_frame, "all", weight=1) - tk.Grid.rowconfigure(content_frame, "all", weight=1) - - # Dictionary for all the variables, this will be used by the controller #: dict: Dictionary of all the widgets in the frame. self.inputs = {} + #: list: List of all the labels for the widgets. self.labels = [ "Exposure Time (ms)", @@ -250,6 +227,7 @@ def __init__(self, settings_tab, *args, **kwargs): "Framerate (Hz)", "Images to Average", ] + #: list: List of all the names for the widget values. self.names = [ "exposure_time", @@ -257,35 +235,37 @@ def __init__(self, settings_tab, *args, **kwargs): "max_framerate", "frames_to_average", ] + + for i in range(len(self.labels)): + self.rowconfigure(i, weight=1, uniform="1") + for i in range(2): + self.columnconfigure(i, weight=1, uniform="1") + #: list: List of all the read only values for the widgets. self.read_only = [True, True, True, False] # Dropdown loop for i in range(len(self.labels)): + label = ttk.Label(self, text=self.labels[i]) + label.grid(row=i, column=0, pady=1, padx=5, sticky=tk.W) + if self.read_only[i]: self.inputs[self.names[i]] = LabelInput( - parent=content_frame, - label=self.labels[i], + parent=self, input_class=ValidatedEntry, input_var=tk.DoubleVar(), input_args={"width": 6}, ) self.inputs[self.names[i]].widget["state"] = "readonly" - self.inputs[self.names[i]].grid(row=i, column=0, pady=1) else: self.inputs[self.names[i]] = LabelInput( - parent=content_frame, - label=self.labels[i], + parent=self, input_class=ValidatedSpinbox, input_var=tk.DoubleVar(), input_args={"from_": 1, "to": 1000, "increment": 1.0, "width": 6}, ) - self.inputs[self.names[i]].grid(row=i, column=0, pady=1) + self.inputs[self.names[i]].grid(row=i, column=1, pady=1, padx=5, sticky=tk.W) - self.inputs["exposure_time"].label.grid(padx=(0, 10)) - self.inputs["readout_time"].label.grid(padx=(0, 14)) - self.inputs["max_framerate"].label.grid(padx=(0, 10)) - self.inputs["frames_to_average"].label.grid(padx=(0, 16)) def get_variables(self): """Get Variables From 1b080bf4d9c5891a727b2ea4a599741808eb6f06 Mon Sep 17 00:00:00 2001 From: "Kevin M. Dean" Date: Sun, 22 Dec 2024 13:56:31 -0600 Subject: [PATCH 5/5] Update camera_tab.py --- .../view/main_window_content/camera_tab.py | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/navigate/view/main_window_content/camera_tab.py b/src/navigate/view/main_window_content/camera_tab.py index 5ac66a202..95112e42b 100644 --- a/src/navigate/view/main_window_content/camera_tab.py +++ b/src/navigate/view/main_window_content/camera_tab.py @@ -38,6 +38,7 @@ # Third Party Imports # Local Imports +import navigate from navigate.view.custom_widgets.LabelInputWidgetFactory import LabelInput from navigate.view.custom_widgets.validation import ValidatedSpinbox, ValidatedEntry @@ -46,7 +47,7 @@ logger = logging.getLogger(p) -class CameraSettingsTab(tk.Frame): +class CameraSettingsTab(ttk.Frame): """ This class holds and controls the layout of the major label frames for the camera settings tab in the settings notebook. Any imported classes are children @@ -54,7 +55,12 @@ class CameraSettingsTab(tk.Frame): the frames follow the children. """ - def __init__(self, setntbk, *args, **kwargs): + def __init__( + self, + setntbk: "navigate.view.main_window_content.settings_notebook", + *args: tuple, + **kwargs: dict + ) -> None: """Initialize the Camera Settings Tab Parameters @@ -67,15 +73,14 @@ def __init__(self, setntbk, *args, **kwargs): Keyword arguments for ttk.Frame """ # Init Frame - tk.Frame.__init__(self, setntbk, *args, **kwargs) + ttk.Frame.__init__(self, setntbk, *args, **kwargs) + #: The index of the tab in the notebook self.index = 1 - # Formatting tk.Grid.columnconfigure(self, "all", weight=1) tk.Grid.rowconfigure(self, "all", weight=1) - # Camera Modes Frame #: tk.Frame: The camera mode frame self.camera_mode = CameraMode(self) self.camera_mode.grid(row=0, column=0, sticky=tk.NSEW, padx=10, pady=10) @@ -105,12 +110,17 @@ class CameraMode(ttk.Labelframe): the widget directly or with the dictionary generated by get_variables. """ - def __init__(self, settings_tab, *args, **kwargs): + def __init__( + self, + settings_tab: "CameraSettingsTab", + *args, + **kwargs + ) -> None: """Initialize the Camera Mode Frame Parameters ---------- - settings_tab : ttk.Frame + settings_tab : CameraSettingsTab The settings tab that the frame will be placed in. *args : tuple Positional arguments for ttk.LabelFrame @@ -157,7 +167,7 @@ def __init__(self, settings_tab, *args, **kwargs): self.inputs[self.names[i]].grid( row=i, column=1, pady=3, padx=5, sticky=tk.W) - def get_variables(self): + def get_variables(self) -> dict: """Get Variables. This function returns a dictionary of all the variables that are tied to each @@ -175,7 +185,7 @@ def get_variables(self): variables[key] = widget.get() return variables - def get_widgets(self): + def get_widgets(self) -> dict: """Get Widgets. This function returns the dictionary that holds the widgets. @@ -201,12 +211,17 @@ class FramerateInfo(ttk.LabelFrame): the dictionary generated by get_variables. """ - def __init__(self, settings_tab, *args, **kwargs): + def __init__( + self, + settings_tab: CameraSettingsTab, + *args: tuple, + **kwargs: dict + ) -> None: """Initialize the Framerate Info Frame Parameters ---------- - settings_tab : ttk.Frame + settings_tab : CameraSettingsTab The settings tab that the frame will be placed in. *args : tuple Positional arguments for ttk.LabelFrame @@ -267,7 +282,7 @@ def __init__(self, settings_tab, *args, **kwargs): self.inputs[self.names[i]].grid(row=i, column=1, pady=1, padx=5, sticky=tk.W) - def get_variables(self): + def get_variables(self) -> dict: """Get Variables This function returns a dictionary of all variables tied to each widget. @@ -285,7 +300,7 @@ def get_variables(self): variables[key] = widget.get() return variables - def get_widgets(self): + def get_widgets(self) -> dict: """Get Widgets This function returns the dictionary that holds the widgets. @@ -311,12 +326,16 @@ class ROI(ttk.Labelframe): get_variables. """ - def __init__(self, settings_tab, *args, **kwargs): + def __init__( + self, + settings_tab: CameraSettingsTab, + *args: tuple, + **kwargs: dict) -> None: """Initialize the ROI Frame Parameters ---------- - settings_tab : ttk.Frame + settings_tab : CameraSettingsTab The settings tab that the frame will be placed in. *args : tuple Positional arguments for ttk.LabelFrame @@ -363,9 +382,9 @@ def __init__(self, settings_tab, *args, **kwargs): tk.Grid.columnconfigure(self.roi_boundary_frame, "all", weight=1) tk.Grid.rowconfigure(self.roi_boundary_frame, "all", weight=1) - # Dictionary for all the variables, this will be used by the controller #: dict: Dictionary of all the widgets in the frame. self.inputs = {} + #: dict: Dictionary of all the buttons in the frame. self.buttons = {} @@ -377,6 +396,7 @@ def __init__(self, settings_tab, *args, **kwargs): roi_boundary_names = ["Top_X", "Top_Y", "Bottom_X", "Bottom_Y"] roi_boundary_labels = ["X", "Y", "X", "Y"] + #: str: The binning label. self.binning = "Binning" @@ -451,18 +471,18 @@ def __init__(self, settings_tab, *args, **kwargs): ) self.inputs[self.binning].grid(row=3, column=0, pady=5, padx=5) - # Additional formatting # Number of Pixels self.inputs["Width"].grid(pady=(10, 5)) self.inputs["Width"].label.grid(padx=(0, 13)) self.inputs["Height"].label.grid(padx=(0, 10)) self.inputs["Binning"].label.grid(padx=(0, 6)) + # FOV self.inputs["FOV_X"].grid(pady=(10, 5)) self.inputs["FOV_X"].label.grid(padx=(0, 10)) self.inputs["FOV_Y"].label.grid(padx=(0, 10)) - def get_variables(self): + def get_variables(self) -> dict: """Get Variables. This function returns a dictionary of all the variables that are tied to each @@ -480,7 +500,7 @@ def get_variables(self): variables[key] = widget.get() return variables - def get_widgets(self): + def get_widgets(self) -> dict: """Get Widgets. This function returns the dictionary that holds the widgets. @@ -493,7 +513,7 @@ def get_widgets(self): """ return self.inputs - def get_buttons(self): + def get_buttons(self) -> dict: """Get Buttons. This function returns the dictionary that holds the buttons.