diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml
index 45a3a066d54121974067f0fd8eea19f534a60bf2..4ebacf82ce1784f36bd542ac279d2989e7d75694 100644
--- a/conda-recipe/meta.yaml
+++ b/conda-recipe/meta.yaml
@@ -1,6 +1,6 @@
 package:
   name: nexus-passoft
-  version: "2023.4.6.2"
+  version: "2023.4.6.3"
 
 source:
   path: ../
@@ -17,6 +17,7 @@ requirements:
   build:
     - python >=3.9
     - pip
+    - setuptools
 
   run:
     - python >=3.9
diff --git a/nexus/__init__.py b/nexus/__init__.py
index a09e3895c6bf6bfca286bd0e048c638c638acb7a..73d1dbe322da7e453e457d4e65b504839fc99486 100644
--- a/nexus/__init__.py
+++ b/nexus/__init__.py
@@ -4,4 +4,4 @@
 
 __author__ = """IRIS PASSCAL"""
 __email__ = 'software-support@passcal.nmt.edu'
-__version__ = '2023.4.6.2'
+__version__ = '2023.4.6.3'
diff --git a/nexus/nexus.py b/nexus/nexus.py
index 25aad307287c4ecdedf09659790c2b8567122c46..53343bf3b802b758652f27d13f77a030de99bf8d 100755
--- a/nexus/nexus.py
+++ b/nexus/nexus.py
@@ -7,15 +7,18 @@ Lloyd Carothers
 
 import os
 import sys
-import traceback
 
-from PySide6 import QtCore, QtGui, QtWidgets
+from PySide6.QtWidgets import (QApplication, QAbstractItemView, QMenu,
+                               QFileDialog, QInputDialog, QDataWidgetMapper,
+                               QMessageBox, QHeaderView)
+from PySide6.QtCore import (QAbstractItemModel, QModelIndex)
+from PySide6.QtGui import (QKeyEvent, QFontMetrics, QFont,
+                           Qt, QColor, QAction)
 from PySide6.QtUiTools import loadUiType
-from PySide6.QtWidgets import QMessageBox, QHeaderView
-from PySide6.QtCore import Qt
 
 from obspy import read_inventory
 from obspy.core.inventory import Network, Station, Equipment
+from obspy.core.inventory.util import Comment
 
 from nexus.nrl_wizard import NRLWizard, DATALOGGER, SENSOR
 from nexus.obspy_improved import utc_to_str, utc_from_str, InventoryIm
@@ -30,27 +33,55 @@ status_message = print
 ELIPSES = '...'
 
 hardware.load()
-DLS = hardware.dataloggers
-SENSORS = hardware.sensors
+DLS = hardware.DATALOGGERS
+SENSORS = hardware.SENSORS
 
 # For Pyside6 `loadUiType()`, no longer takes path, must be in sys.path
 sys.path.append(os.path.dirname(__file__))
 
-def load_ui(filename):
-    '''
+
+def load_ui(filename) -> tuple[object, object]:
+    """
     Helper function
     Load a ui file relative to this source file
-    '''
+
+    Args:
+        filename (str): The name of the .ui file
+
+    Raises:
+        e: Exception raised from PySide6 loadUiType().
+
+    Returns:
+        tuple: Contains the reference to the Python class
+               and the base class.
+    """
     path = os.path.join(os.path.dirname(__file__), filename)
     try:
-            ret = loadUiType(path)
+        ret = loadUiType(path)
     except Exception as e:
         print(e)
         raise e
     return ret
 
+
 class InventoryNode:
-    def __init__(self, name, inv_object=None, parent=None):
+    """
+    Base class for all nodes within the inventory model
+    (NetworkNode, StationNode, ChannelNode).
+    """
+
+    def __init__(self, name: str, inv_object=None, parent=None):
+        """
+        Initializes `InventoryNode`.
+
+        Args:
+            name (str): The code of the node.
+            inv_object (Network | Station | Channel, optional):
+            The obspy inventory object associated with the node.
+            Defaults to None.
+            parent (NetworkNode | StationNode | ChannelNode, optional):
+            The parent of the node. Defaults to None.
+        """
         self._children = []
         self._parent = parent
         self._inv_obj = inv_object
@@ -58,37 +89,104 @@ class InventoryNode:
         if self._parent is not None:
             self._parent.add_child(self)
 
-    def add_child(self, child):
+    def add_child(self, child: object) -> bool:
+        """
+        Adds child nodes to a parent nodes list of children.
+
+        Args:
+            child (NetworkNode | StationNode | ChannelNode):
+            Child of the given node. E.g. assigns ChannelNode as a
+            child of a StationNode.
+
+        Returns:
+            bool: Returns True.
+        """
         self._children.append(child)
         child._parent = self
         return True
 
-    def remove_child(self, child):
+    def remove_child(self, child: object) -> None:
+        """
+        Removes the given child from the node's list of children.
+
+        Args:
+            child (NetworkNode | StationNode | ChannelNode):
+            The child node to be removed from the list.
+        """
         self._children.remove(child)
 
-    def remove(self):
+    def remove(self) -> None:
+        """
+        Removes node from its parent's list of children.
+        """
         self._parent.remove_child(self)
 
-    def child(self, row):
+    def child(self, row: int) -> object:
+        """
+        Returns the node's child node at the given row (index).
+
+        Args:
+            row (int): The child's index in the list of children.
+
+        Returns:
+            (NetworkNode | StationNode | ChannelNode): The child node object.
+        """
         if row >= 0:
             return self._children[row]
+        return None
+
+    def child_count(self) -> int:
+        """
+        Returns the number of children the node has.
 
-    def child_count(self):
+        Returns:
+            int: The number of children the node has.
+        """
         return len(self._children)
 
-    def children(self):
+    def children(self) -> list[object]:
+        """
+        Returns the list of the node's children.
+
+        Returns:
+            (list[NetworkNode | StationNode | ChannelNode]):
+            All of the node's children.
+        """
         return self._children
 
-    def parent(self):
+    def parent(self) -> object:
+        """
+        Returns the parent of the node.
+
+        Returns:
+            (NetworkNode | StationNode): The node's parent.
+        """
         return self._parent
 
-    def row(self):
+    def row(self) -> int:
+        """
+        Returns the row (index) of the node within
+        its parents children list.
+
+        Returns:
+            int: The index of the node within its parents children list.
+        """
         if self._parent is not None:
             return self._parent._children.index(self)
-        else:
-            return 0
+        return 0
 
-    def __str__(self, tab_level=0):
+    def __str__(self, tab_level: int = 0) -> str:
+        """
+        Returns a string representation of the tree structure starting
+        from the current node.
+
+        Args:
+            tab_level (int, optional): The current level of indentation.
+                                       Defaults to 0.
+
+        Returns:
+            str: A formatted string representing the tree structure.
+        """
         if self._parent is None:
             output = 'ROOT NODE\n'
         else:
@@ -99,33 +197,84 @@ class InventoryNode:
         tab_level -= 1
         return output
 
-    def get_data(self, col):
+    def get_data(self, col: int) -> str:
+        """
+        Returns data from the field mapper at the given column.
+
+        Args:
+            col (int): Column (index) to retrieve from the field mapper.
+
+        Returns:
+            str: Data value from the field mapper at the given column.
+        """
         return self.FM[col].fget(self)
 
-    def set_data(self, col, value):
+    def set_data(self, col: int, value: str):
+        """
+        Retrieves the setter for the specified column and applies it
+        to set the value for the field mapper.
+
+        Args:
+            col (int): Column (index) to set for the field mapper.
+            value (str): The value to assign to the specified field.
+
+        Returns:
+            (True | None): Returns the return value of the setter
+                function if there is one.
+        """
         setter = self.FM[col].fset
         if setter:
             return setter(self, value)
+        return None
 
     # Field / column mapper
     FM = []
+
     @classmethod
-    def col(cls, field):
-        '''
-        Returns the field row in model from the field property
-        '''
+    def col(cls: type, field: property) -> int:
+        """
+        Retrieves the index of a field property in the field
+        mapper for the given class.
+
+        Args:
+            cls (type): The class containing the field mapper (FM).
+            field (property): The field property to locate
+                within the field mapper.
+
+        Returns:
+            int: The index of the field in the field mapper (FM).
+        """
         return cls.FM.index(field)
 
     # property fields
-    def get_code(self):
+    def get_code(self) -> str:
+        """
+        Returns the code of the obspy inventory object associated
+        with the node.
+
+        Returns:
+            str: The code value of the obspy inventory object.
+        """
         try:
             return self._inv_obj.code
-        except:
+        except Exception:
             return ''
-    def set_code(self, value):
+
+    def set_code(self, value: str) -> bool:
+        """
+        Sets the code of the obspy inventory object associated
+        with the node to the given value.
+
+        Args:
+            value (str): The code value to be assigned.
+
+        Returns:
+            bool: Returns True.
+        """
         self._inv_obj.code = value
         return True
-    code  = property(get_code, set_code)
+
+    code = property(get_code, set_code)
     FM.append(code)
 
     # Place holder for 2nd col
@@ -134,9 +283,32 @@ class InventoryNode:
     # Place holder for SR
     FM.append('SR')
 
-    def get_start(self):
+    def get_start(self) -> str:
+        """
+        Returns the start date of the obspy inventory object
+        associated with the node.
+
+        Returns:
+            str: The start date of the obspy inventory object.
+        """
         return utc_to_str(self._inv_obj.start_date)
-    def set_start(self, value):
+
+    def set_start(self, value: str) -> bool:
+        """
+        Sets the start date of the obspy inventory object
+        associated with the node and propagates the change
+        up the hierarchy if necessary.
+
+        Args:
+            value (str): The start date value to set.
+
+        Returns:
+            bool: `True` if the operation is successful.
+
+        Raises:
+            Exception: If either the conversion from str to UTC
+                or assignment process fails.
+        """
         try:
             utc = utc_from_str(value)
             self._inv_obj.start_date =  utc
@@ -146,13 +318,37 @@ class InventoryNode:
         if parent and parent.parent() and parent._inv_obj.start_date > utc:
             parent.set_start(value)
         return True
+
     start = property(get_start, set_start)
 
     FM.append(start)
 
-    def get_end(self):
+    def get_end(self) -> str:
+        """
+        Returns the end date of the obspy inventory object
+        associated with the node.
+
+        Returns:
+            str: The end date of the obspy inventory object.
+        """
         return utc_to_str(self._inv_obj.end_date)
-    def set_end(self, value):
+
+    def set_end(self, value: str) -> bool:
+        """
+        Sets the end date of the obspy inventory object associated
+        with the node and propagates the change up the hierarchy
+        if necessary.
+
+        Args:
+            value (str): The end date value to set.
+
+        Returns:
+            bool: `True` if the operation is successful.
+
+        Raises:
+            Exception: If either the conversion from str to UTC
+                or assignment process fails.
+        """
         try:
             utc = utc_from_str(value)
             self._inv_obj.end_date =  utc
@@ -162,49 +358,128 @@ class InventoryNode:
         if parent and parent.parent() and parent._inv_obj.end_date < utc:
             parent.set_end(value)
         return True
+
     end = property(get_end, set_end)
     FM.append(end)
 
-    def has_response(self):
+    def has_response(self) -> bool:
+        """
+        Checks whether the obspy inventory object associated
+        with the node has a response. If not found with the given
+        node, recursively checks all children to determine
+        if any of them have a response.
+
+        Returns:
+            bool: `True` if response is found, else `False`.
+        """
         try:
-            if self._inv_obj.response:
-                return True
-            else:
-                return False
+            return bool(self._inv_obj.response)
         except AttributeError:
             pass
         return all((child.has_response for child in self._children))
+
     has_response = property(has_response)
     FM.append(has_response)
 
 
 class NetworkNode(InventoryNode):
-    def __init__(self, name, inv_object, parent=None):
+    """
+    Extends `InventoryNode` class.
+    Specifically for nodes associated with obspy Network objects.
+
+    Args:
+        InventoryNode (class): Base class.
+    """
+
+    def __init__(self, name: str, inv_object: Network,
+                 parent: InventoryNode = None):
+        """
+        Initializes `NetworkNode`.
+
+        Args:
+            name (str): The network code.
+            inv_object (Network): The obspy Network object
+                                  associated with the node.
+            parent (InventoryNode, optional): The parent of
+                the node. Defaults to None.
+        """
         InventoryNode.__init__(self, name, inv_object, parent)
         self._inv_obj = inv_object
 
     FM = InventoryNode.FM
 
-    def get_description(self):
+    def get_description(self) -> str:
+        """
+        Returns the description of the associated obspy Network object.
+
+        Returns:
+            str: The description of the Network object.
+        """
         return self._inv_obj.description
-    def set_description(self, value):
+
+    def set_description(self, value: str) -> bool:
+        """
+        Sets the description of the associated obspy Network
+        object as the given value.
+
+        Args:
+            value (str): The description to set.
+
+        Returns:
+            bool: Returns `True`.
+        """
         self._inv_obj.description = value
         return True
+
     description = property(get_description, set_description)
     FM.append(description)
 
 
-
 class StationNode(InventoryNode):
-    def __init__(self, name, inv_object, parent):
+    """
+    Extends `InventoryNode` class.
+    Specifically for nodes associated with obspy Station objects.
+
+    Args:
+        InventoryNode (class): Base class.
+    """
+
+    def __init__(self, name: str, inv_object: Station, parent: NetworkNode):
+        """
+        Initializes `StationNode`.
+
+        Args:
+            name (str): The station code.
+            inv_object (Station): The obspy Station object
+                                  associated with the node.
+            parent (NetworkNode): The parent NetworkNode.
+        """
         InventoryNode.__init__(self, name, inv_object, parent)
         self._inv_obj = inv_object
 
     FM = InventoryNode.FM
 
-    def get_latitude(self):
+    def get_latitude(self) -> str:
+        """
+        Returns the latitude of the station.
+
+        Returns:
+            str: The station latitude.
+        """
         return str(self._inv_obj.latitude)
-    def set_latitude(self, value):
+
+    def set_latitude(self, value: str) -> bool:
+        """
+        Assigns the given latitude to the station and all its
+        child channels.
+
+        Args:
+            value (str): Latitude to be set.
+
+        Returns:
+            (True | False): `True` if assignment is successful,
+            `False` if Exception occurs.
+        """
         try:
             value = float(value)
         except ValueError:
@@ -212,12 +487,32 @@ class StationNode(InventoryNode):
         self._inv_obj.latitude = value
         for chan in self._inv_obj.channels:
             chan.latitude = value
+        return True
+
     latitude = property(get_latitude, set_latitude)
     FM.append(latitude)
 
-    def get_longitude(self):
+    def get_longitude(self) -> str:
+        """
+        Returns the longitude of the station.
+
+        Returns:
+            str: The station longitude.
+        """
         return str(self._inv_obj.longitude)
-    def set_longitude(self, value):
+
+    def set_longitude(self, value: str) -> bool:
+        """
+        Assigns the given longitude to the station and all its
+        child channels.
+
+        Args:
+            value (str): Longitude to be set.
+
+        Returns:
+            (True | False): `None` if assignment is successful,
+            `False` if Exception occurs.
+        """
         try:
             value = float(value)
         except ValueError:
@@ -225,12 +520,32 @@ class StationNode(InventoryNode):
         self._inv_obj.longitude = value
         for chan in self._inv_obj.channels:
             chan.longitude = value
+        return True
+
     longitude = property(get_longitude, set_longitude)
     FM.append(longitude)
 
-    def get_elevation(self):
+    def get_elevation(self) -> str:
+        """
+        Returns the elevation of the station.
+
+        Returns:
+            str: The station elevation.
+        """
         return str(self._inv_obj.elevation)
-    def set_elevation(self, value):
+
+    def set_elevation(self, value: str) -> bool:
+        """
+        Assigns the given elevation to the station and all its
+        child channels.
+
+        Args:
+            value (str): Elevation to be set.
+
+        Returns:
+            (True | False): `True` if assignment is successful,
+            `False` if Exception occurs.
+        """
         try:
             value = float(value)
         except ValueError:
@@ -239,13 +554,33 @@ class StationNode(InventoryNode):
         for chan in self._inv_obj.channels:
             chan.elevation = value
         return True
+
     elevation = property(get_elevation, set_elevation)
     FM.append(elevation)
 
-    def get_depth(self):
-        depths = set([str(c.depth) for c in self._inv_obj.channels])
+    def get_depth(self) -> str:
+        """
+        Returns the unique depths of the stations child channels.
+
+        Returns:
+            str: A conjoined string of all unique channel depths.
+                 E.g. '1.1|0.5|0.0', or '0.0'.
+        """
+        depths = {str(c.depth) for c in self._inv_obj.channels}
         return '|'.join(depths)
-    def set_depth(self, value):
+
+    def set_depth(self, value: str) -> bool:
+        """
+        Assigns the given depth to all the station's
+        child channels.
+
+        Args:
+            value (str): Depth to be set.
+
+        Returns:
+            (True | False): `True` if assignment is successful,
+            `False` if Exception occurs.
+        """
         try:
             value = float(value)
         except ValueError:
@@ -254,11 +589,20 @@ class StationNode(InventoryNode):
         for chan in self._inv_obj.channels:
             chan.depth = value
         return True
+
     depth_station = property(get_depth, set_depth)
     FM.append(depth_station)
 
-    def get_dl_sn(self):
-        #No channels
+    def get_dl_sn(self) -> str:
+        """
+        Returns the datalogger serial number of the station's first
+        child channel. If there are no channels or if the serial number
+        hasn't been set, returns `'Na'`.
+
+        Returns:
+            str: The datalogger serial number.
+        """
+        # No channels
         channels = self._inv_obj.channels
         if len(channels) < 1:
             return 'Na'
@@ -266,20 +610,43 @@ class StationNode(InventoryNode):
             return channels[0].data_logger.serial_number
         except AttributeError:
             return 'Na'
-    def set_dl_sn(self, value):
+
+    def set_dl_sn(self, value: str) -> bool:
+        """
+        Assigns the given datalogger serial number to all the
+        station's child channels.
+
+        Args:
+            value (str): The datalogger serial number to assign.
+
+        Returns:
+            bool: Returns `True`.
+        """
         for chan in self._inv_obj.channels:
             try:
                 chan.data_logger.serial_number = value
             except AttributeError:
                 chan.data_logger = Equipment(
-                    serial_number = value)
+                    serial_number=value
+                )
         return True
+
     dl_sn = property(get_dl_sn, set_dl_sn)
     FM.append(dl_sn)
 
-
-    def get_dl_gain(self):
-        #No channels
+    def get_dl_gain(self) -> str:
+        """
+        Returns the datalogger gain for the first valid channel
+        under the station. First checks the gain for the first channel and,
+        if not available, iterates over channels with specific codes
+        ('H', 'L', 'G', 'N') to find a valid response stage gain.
+        If no channels are present or a gain cannot be determined,
+        returns 'Na'.
+
+        Returns:
+            str: The datalogger gain.
+        """
+        # No channels
         channels = self._inv_obj.channels
         try:
             return channels[0].data_logger.gain
@@ -295,11 +662,30 @@ class StationNode(InventoryNode):
             return gain
         return 'Na'
 
-    def set_dl_gain(self, value, keep_response=False):
+    def set_dl_gain(self, value: str, keep_response: bool = False) -> bool:
+        """
+        Assigns the datalogger gain for all the station's child
+        channels to the given value. If the datalogger does not
+        exist, a new `Equipment` object is created. Optionally,
+        it can clear the response associated with each channel
+        unless `keep_response` is set to `True`.
+
+        Args:
+            value (str): The datalogger gain to be assigned.
+                         Must be convertible to an integer.
+            keep_response (bool, optional):
+                If `True`, retains the existing response for each
+                channel. If `False`, the response will be cleared.
+                Defaults to `False`.
+
+        Returns:
+            bool: `True` if the operation was successful;
+                  `False` if the `value` is invalid.
+        """
         try:
             value = int(value)
         except ValueError:
-            return
+            return False
         for chan in self._inv_obj.channels:
             if isinstance(chan.data_logger, Equipment):
                 try:
@@ -314,11 +700,23 @@ class StationNode(InventoryNode):
             if not keep_response:
                 chan.response = None
         return True
+
     dl_gain = property(get_dl_gain, set_dl_gain)
     FM.append(dl_gain)
 
-    def get_dl_type(self):
-        #No channels
+    def get_dl_type(self) -> int:
+        """
+        Returns the index of the datalogger type for the first
+        channel under the station within the `DLS` list. If no
+        channels exist or the datalogger type is unavailable or invalid,
+        returns the index of `'Na'` in `DLS`.
+
+        Returns:
+            int: The index of the datalogger type in the `DLS` list,
+                 or the index of `'Na'`
+            if no valid type is found.
+        """
+        # No channels
         channels = self._inv_obj.channels
         if len(channels) < 1:
             return DLS.index('Na')
@@ -326,7 +724,23 @@ class StationNode(InventoryNode):
             return DLS.index(channels[0].data_logger.type)
         except (AttributeError, ValueError):
             return DLS.index('Na')
-    def set_dl_type(self, value):
+
+    def set_dl_type(self, value: int) -> bool:
+        """
+        Assigns the given datalogger type to all channels under the
+        station. If a channel does not have a datalogger, a new
+        `Equipment` object is created with the specified type.
+        The channel's response is cleared after updating the datalogger
+        type.
+
+        Args:
+            value (int): The index of the datalogger type in the `DLS` list.
+                         This value is converted to the corresponding
+                         datalogger type name.
+
+        Returns:
+            bool: `True` if the operation was successful.
+        """
         for chan in self._inv_obj.channels:
             try:
                 chan.data_logger.type = DLS.name(value)
@@ -334,11 +748,21 @@ class StationNode(InventoryNode):
                 chan.data_logger = Equipment(type=DLS.name(value))
             chan.response = None
         return True
+
     dl_type = property(get_dl_type, set_dl_type)
     FM.append(dl_type)
 
-    def get_sensor_sn(self):
-        #No channels
+    def get_sensor_sn(self) -> str:
+        """
+        Returns the serial number of the sensor associated with the
+        first channel under the station. If no channels exist or the
+        serial number cannot be accessed, returns `'Na'`.
+
+        Returns:
+            str: The serial number of the sensor if available;
+                 otherwise, `'Na'`.
+        """
+        # No channels
         channels = self._inv_obj.channels
         if len(channels) < 1:
             return 'Na'
@@ -346,19 +770,43 @@ class StationNode(InventoryNode):
             return channels[0].sensor.serial_number
         except AttributeError:
             return 'Na'
-    def set_sensor_sn(self, value):
+
+    def set_sensor_sn(self, value) -> bool:
+        """
+        Assigns the serial number of the sensor to all the station's
+        child channels. If a channel does not have a sensor, a new
+        `Equipment` object is created with the given serial number.
+
+        Args:
+            value (str): The serial number to assign to each sensor.
+
+        Returns:
+            bool: `True` if the operation was successful.
+        """
         for chan in self._inv_obj.channels:
             try:
                 chan.sensor.serial_number = value
             except AttributeError:
                 chan.sensor = Equipment(
-                    serial_number = value)
+                    serial_number=value
+                )
         return True
+
     sensor_sn = property(get_sensor_sn, set_sensor_sn)
     FM.append(sensor_sn)
 
-    def get_sensor_type(self):
-        #No channels
+    def get_sensor_type(self) -> int:
+        """
+        Returns the index of the sensor type for the first channel
+        under the station within the `SENSORS` list. If no channels
+        exist or the sensor type is unavailable or invalid,
+        returns the index of `'Na'` in `SENSORS`.
+
+        Returns:
+            int: The index of the sensor type in the `SENSORS` list,
+                 or the index of `'Na'` if no valid type is found.
+        """
+        # No channels
         channels = self._inv_obj.channels
         if len(channels) < 1:
             return SENSORS.index('Na')
@@ -366,7 +814,22 @@ class StationNode(InventoryNode):
             return SENSORS.index(channels[0].sensor.type)
         except (AttributeError, ValueError):
             return SENSORS.index('Na')
-    def set_sensor_type(self, value):
+
+    def set_sensor_type(self, value: int) -> bool:
+        """
+        Sets the sensor type for all the station's child channels.
+        If a channel does not already have a sensor, a new `Equipment`
+        object is created with the specified type. The response
+        for each channel is cleared after updating the sensor type.
+
+        Args:
+            value (int): The index of the sensor type in the `SENSORS` list.
+                         This value is converted to the corresponding sensor
+                         type name.
+
+        Returns:
+            bool: `True` if the operation was successful.
+        """
         for chan in self._inv_obj.channels:
             try:
                 chan.sensor.type = SENSORS.name(value)
@@ -374,208 +837,564 @@ class StationNode(InventoryNode):
                 chan.sensor = Equipment(type=SENSORS.name(value))
             chan.response = None
         return True
+
     sensor_type = property(get_sensor_type, set_sensor_type)
     FM.append(sensor_type)
 
-    def get_site_name(self):
+    def get_site_name(self) -> str:
+        """
+        Returns the station's site name.
+
+        Returns:
+            str: The station's site name.
+        """
         return self._inv_obj.site.name
-    def set_site_name(self, value):
+
+    def set_site_name(self, value: str) -> bool:
+        """
+        Assigns the station's site name to the given value.
+
+        Args:
+            value (str): The site name to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.site.name = value
         return True
+
     site_name = property(get_site_name, set_site_name)
     FM.append(site_name)
 
-    def get_site_description(self):
+    def get_site_description(self) -> str:
+        """
+        Returns the station's site description.
+
+        Returns:
+            str: The station's site description.
+        """
         return self._inv_obj.site.description
-    def set_site_description(self, value):
+
+    def set_site_description(self, value: str) -> bool:
+        """
+        Assigns the station's site description to the given
+        value.
+
+        Args:
+            value (str): The site description to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.site.description = value
         return True
+
     site_description = property(get_site_description, set_site_description)
     FM.append(site_description)
 
-    def get_site_town(self):
+    def get_site_town(self) -> str:
+        """
+        Returns the station's site town.
+
+        Returns:
+            str: The station's site town.
+        """
         return self._inv_obj.site.town
-    def set_site_town(self, value):
+
+    def set_site_town(self, value: str) -> bool:
+        """
+        Assigns the station's site town to the given value.
+
+        Args:
+            value (str): The site town to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.site.town = value
         return True
+
     site_town = property(get_site_town, set_site_town)
     FM.append(site_town)
 
-    def get_site_county(self):
+    def get_site_county(self) -> str:
+        """
+        Returns the station's site county.
+
+        Returns:
+            str: The station's site county.
+        """
         return self._inv_obj.site.county
-    def set_site_county(self, value):
+
+    def set_site_county(self, value: str) -> bool:
+        """
+        Assigns the station's site county to the given value.
+
+        Args:
+            value (str): The site county to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.site.county = value
         return True
+
     site_county = property(get_site_county, set_site_county)
     FM.append(site_county)
 
-    def get_site_region(self):
+    def get_site_region(self) -> str:
+        """
+        Returns the station's site region.
+
+        Returns:
+            str: The station's site region.
+        """
         return self._inv_obj.site.region
-    def set_site_region(self, value):
+
+    def set_site_region(self, value: str) -> bool:
+        """
+        Assigns the station's site region to the given value.
+
+        Args:
+            value (str): The site region to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.site.region = value
         return True
+
     site_region = property(get_site_region, set_site_region)
     FM.append(site_region)
 
-    def get_site_country(self):
+    def get_site_country(self) -> str:
+        """
+        Returns the station's site country.
+
+        Returns:
+            str: The station's site country.
+        """
         return self._inv_obj.site.country
-    def set_site_country(self, value):
+
+    def set_site_country(self, value: str) -> bool:
+        """
+        Assigns the station's site country to the given value.
+
+        Args:
+            value (str): The site country to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.site.country = value
         return True
+
     site_country = property(get_site_country, set_site_country)
     FM.append(site_country)
 
 
 class ChannelNode(InventoryNode):
+    """
+    Extends `InventoryNode` class.
+    Specifically for nodes associated with obspy Channel objects.
+
+    Args:
+        InventoryNode (class): Base class.
+    """
+
     def __init__(self, name, inv_object, parent):
+        """
+        Initializes `ChannelNode`.
+
+        Args:
+            name (str): The channel code.
+            inv_object (Channel): The obspy Channel object
+                                  associated with the node.
+            parent (StationNode): The parent StationNode.
+        """
         InventoryNode.__init__(self, name, inv_object, parent)
         self._inv_obj = inv_object
 
     FM = InventoryNode.FM
-    def get_latitude(self):
+
+    def get_latitude(self) -> str:
+        """
+        Returns the latitude of the channel.
+
+        Returns:
+            str: The latitude of the channel.
+        """
         return str(self._inv_obj.latitude)
-    def set_latitude(self, value):
+
+    def set_latitude(self, value: str) -> bool:
+        """
+        Assigns the latitude of the channel to the given value.
+
+        Args:
+            value (str): The latitude to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.latitude = float(value)
         return True
+
     latitude = property(get_latitude, set_latitude)
     FM.append(latitude)
 
-    def get_longitude(self):
+    def get_longitude(self) -> str:
+        """
+        Returns the longitude of the channel.
+
+        Returns:
+            str: The longitude of the channel.
+        """
         return str(self._inv_obj.longitude)
-    def set_longitude(self, value):
+
+    def set_longitude(self, value: str) -> bool:
+        """
+        Assigns the longitude of the channel to the given value.
+
+        Args:
+            value (str): The longitude to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.longitude = float(value)
         return True
+
     longitude = property(get_longitude, set_longitude)
     FM.append(longitude)
 
-    def get_elevation(self):
+    def get_elevation(self) -> str:
+        """
+        Returns the elevation of the channel.
+
+        Returns:
+            str: The elevation of the channel.
+        """
         return str(self._inv_obj.elevation)
-    def set_elevation(self, value):
+
+    def set_elevation(self, value: str) -> bool:
+        """
+        Assigns the elevation of the channel to the given value.
+
+        Args:
+            value (str): The elevation to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.elevation = float(value)
         return True
+
     elevation = property(get_elevation, set_elevation)
     FM.append(elevation)
 
-    def get_azimuth(self):
+    def get_azimuth(self) -> str:
+        """
+        Returns the azimuth of the channel.
+
+        Returns:
+            str: The azimuth of the channel.
+        """
         return str(self._inv_obj.azimuth)
-    def set_azimuth(self, value):
+
+    def set_azimuth(self, value: str) -> bool:
+        """
+        Assigns the azimuth of the channel to the given value.
+
+        Args:
+            value (str): The azimuth to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.azimuth = float(value)
         return True
+
     azimuth = property(get_azimuth, set_azimuth)
     FM.append(azimuth)
 
-    def get_dip(self):
+    def get_dip(self) -> str:
+        """
+        Returns the dip of the channel.
+
+        Returns:
+            str: The dip of the channel.
+        """
         return str(self._inv_obj.dip)
-    def set_dip(self, value):
+
+    def set_dip(self, value: str) -> bool:
+        """
+        Assigns the dip of the channel to the given value.
+
+        Args:
+            value (str): The dip to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.dip = float(value)
         return True
+
     dip = property(get_dip, set_dip)
     FM.append(dip)
 
-    def get_depth(self):
+    def get_depth(self) -> str:
+        """
+        Returns the depth of the channel.
+
+        Returns:
+            str: The depth of the channel.
+        """
         return str(self._inv_obj.depth)
-    def set_depth(self, value):
+
+    def set_depth(self, value: str) -> bool:
+        """
+        Assigns the depth of the channel to the given value.
+
+        Args:
+            value (str): The depth to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.depth = float(value)
         return True
+
     depth = property(get_depth, set_depth)
     FM.append(depth)
 
-    def get_location_code(self):
-        if type(self) is not ChannelNode:
+    def get_location_code(self) -> str:
+        """
+        Returns the location code of the channel.
+
+        Returns:
+            str: The location code of the channel.
+        """
+        if not isinstance(self, ChannelNode):
             return ''
         return self._inv_obj.location_code
-    def set_location_code(self, value):
-        if type(self) is not ChannelNode:
-            return
+
+    def set_location_code(self, value: str) -> bool:
+        """
+        Assigns the location code of the channel to the given value.
+
+        Args:
+            value (str): The location code to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
+        if not isinstance(self, ChannelNode):
+            return False
         self._inv_obj.location_code = value
         return True
+
     location_code = property(get_location_code, set_location_code)
     FM[InventoryNode.col('LOC')] = location_code
 
-    def get_sample_rate(self):
-        if type(self) is not ChannelNode:
+    def get_sample_rate(self) -> str:
+        """
+        Returns the sample rate of the channel in Hz.
+
+        Returns:
+            str: The sample rate of the channel.
+        """
+        if not isinstance(self, ChannelNode):
             return ''
         return str(self._inv_obj.sample_rate) + 'Hz'
-    def set_sample_rate(self, value):
-        if type(self) is not ChannelNode:
-            return
+
+    def set_sample_rate(self, value: str) -> bool:
+        """
+        Assigns the sample rate of the channel to the given value.
+
+        Args:
+            value (str): The sample rate to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
+        if not isinstance(self, ChannelNode):
+            return False
         value = value.strip('hHzZ')
         self._inv_obj.sample_rate = float(value)
         return True
+
     sample_rate = property(get_sample_rate, set_sample_rate)
     FM[InventoryNode.col('SR')] = sample_rate
 
-    def get_description(self):
+    def get_description(self) -> str:
+        """
+        Returns the sensor description of the channel.
+
+        Returns:
+            str: The sensor description of the channel. If none
+                 exists returns `'Na`.
+        """
         try:
             return self._inv_obj.sensor.description
         except AttributeError:
             return 'Na'
-    def set_description(self, value):
+
+    def set_description(self, value: str) -> bool:
+        """
+        Assigns the sensor description of the channel to the given value.
+
+        Args:
+            value (str): The sensor description to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         self._inv_obj.sensor.description = value
         return True
+
     sensor_description = property(get_description, set_description)
     FM.append(sensor_description)
 
-    def get_comment(self):
+    def get_comment(self) -> str:
+        """
+        Returns the channel's comment.
+
+        Returns:
+            str: Channel comment.
+        """
         try:
             return self._inv_obj.comments[0].value
         except IndexError:
             return ''
-    def set_comment(self, value):
+
+    def set_comment(self, value: str) -> bool:
+        """
+        Assigns the comment of the channel to the given value.
+
+        Args:
+            value (str): The comment to assign.
+
+        Returns:
+            bool: `True` on successful assignment.
+        """
         try:
             self._inv_obj.comments[0].value = value
-        except:
-            self._inv_obj.comments = [obspyImproved.Comment(
-                value=value)]
+        except Exception:
+            self._inv_obj.comments.append(Comment(value))
         return True
+
     channel_comment = property(get_comment, set_comment)
     FM.append(channel_comment)
 
-    def plot_response(self):
+    def plot_response(self) -> None:
+        """
+        Plots the response if the sample rate is greater than 0.
+        """
         if self._inv_obj.sample_rate > 0:
             self._inv_obj.response.plot(1/300)
 
 
-class InventoryModel(QtCore.QAbstractItemModel):
-    def __init__(self, root, parent=None):
+class InventoryModel(QAbstractItemModel):
+    """
+    Custom model for representing and managing
+    inventory nodes (NetworkNode, StationNode, ChannelNode)
+    in a hierarchical structure.
+
+    Args:
+        QAbstractItemModel: Standard interface that
+        item models must use to be able to interoperate
+        with other components in the model/view architecture.
+    """
+
+    def __init__(self, root: InventoryNode, parent=None):
+        """
+        Initializes the InventoryModel.
+
+        Args:
+            root (InventoryNode): The root node of the inventory hierarchy.
+            parent (QObject, optional): The parent object for the model.
+                                        Defaults to None.
+        """
         super().__init__(parent)
         self._root_node = root
 
-    def rowCount(self, parent):
+    def rowCount(self, parent: QModelIndex) -> int:
+        """
+        Returns the number of rows (child nodes) under the given parent.
+
+        Args:
+            parent (QModelIndex): The parent index.
+
+        Returns:
+            int: The number of child nodes.
+        """
         parent_node = self.get_node(parent)
         return parent_node.child_count()
 
-    def columnCount(self, parent):
+    def columnCount(self, parent) -> int:
+        """
+        Returns the number of columns in the model.
+
+        Args:
+            parent (QModelIndex): The parent index.
+
+        Returns:
+            int: The number of columns (fixed at 6).
+        """
         return 6
 
-    def data(self, index, role):
+    def data(self, index: QModelIndex, role: Qt.ItemDataRole):
+        """
+        Returns the data for the specified index and role.
+
+        Args:
+            index (QModelIndex): The index of the item.
+            role (Qt.ItemDataRole): The role for which data is requested.
+
+        Returns:
+            Any: The data for the specified role and index.
+        """
         node = self.get_node(index)
         value = node.get_data(index.column())
-        if role == QtCore.Qt.DisplayRole:
+        if role == Qt.ItemDataRole.DisplayRole:
             # rounded start/end time on left panel
             if index.column() == 3 or index.column() == 4:
-                value = QtGui.QFontMetrics(QtGui.QFont()).elidedText(value,
-                                                                     QtGui.Qt.ElideRight,
-                                                                     100)
+                value = QFontMetrics(
+                    QFont()
+                ).elidedText(value, Qt.TextElideMode.ElideRight, 100)
                 return value
             return value
-        if role == QtCore.Qt.EditRole:
+        if role == Qt.ItemDataRole.EditRole:
             return value
         # show full start/end time as tooltip in mouse hover
-        if role == Qt.ToolTipRole:
+        if role == Qt.ItemDataRole.ToolTipRole:
             if index.column() == 3 or index.column() == 4:
                 return value
-        if role == QtCore.Qt.BackgroundRole:
+        if role == Qt.ItemDataRole.BackgroundRole:
             try:
                 if node._inv_obj._new:
-                    return QtGui.QColor(191, 239, 192, 100)
+                    return QColor(191, 239, 192, 100)
             except AttributeError:
                 return
 
-    def setData(self, index, value, role=QtCore.Qt.EditRole):
+    def setData(self, index: QModelIndex, value,
+                role: Qt.ItemDataRole = Qt.ItemDataRole.EditRole) -> bool:
+        """
+        Sets the data for the specified index and role.
+
+        Args:
+            index (QModelIndex): The index of the item.
+            value (Any): The new value to set.
+            role (Qt.ItemDataRole, optional):
+                The role for which data is being set.
+                Defaults to Qt.EditRole.
+
+        Returns:
+            bool: `True` if the data was successfully set; otherwise `False`.
+        """
         try:
             node = self.get_node(index)
-            if role == QtCore.Qt.EditRole:
+            if role == Qt.ItemDataRole.EditRole:
                 if not node.set_data(index.column(), value):
                     return False
                 self.dataChanged.emit(index, index)
@@ -585,31 +1404,65 @@ class InventoryModel(QtCore.QAbstractItemModel):
                     # self.dataChanged.emit(index, index)
                     # return True
         except Exception as e:
-            traceback.print_exc()
+            print(e)
             status_message(
                 'Failed to set field. See stdout for debugging info.')
         return False
 
-    def headerData(self, section, orientation, role):
-        if role == QtCore.Qt.DisplayRole:
+    def headerData(self, section: int, orientation: Qt.Orientation,
+                   role: Qt.ItemDataRole) -> str:
+        """
+        Returns the header data for the specified section.
+
+        Args:
+            section (int): The section (column index) of the header.
+            orientation (Qt.Orientation): The orientation of the header.
+            role (Qt.ItemDataRole): The role for which data is requested.
+
+        Returns:
+            str: The header title for the specified section and role.
+        """
+        if role == Qt.ItemDataRole.DisplayRole:
             if section == 0:
                 return 'Code'
-            elif section == 1:
+            if section == 1:
                 return 'Loc'
-            elif section == 2:
+            if section == 2:
                 return 'SR'
-            elif section == 3:
+            if section == 3:
                 return 'Start'
-            elif section == 4:
+            if section == 4:
                 return 'End'
-            elif section == 5:
+            if section == 5:
                 return 'Response'
+        return ''
+
+    def flags(self, parent: QModelIndex) -> Qt.ItemFlag:
+        """
+        Returns the item flags for the given parent index.
+
+        Args:
+            parent (QModelIndex): The parent index.
+
+        Returns:
+            Qt.ItemFlags: The flags for the item.
+        """
+        return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable \
+            | Qt.ItemFlag.ItemIsEditable
 
-    def flags(self, parent):
-        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable \
-               | QtCore.Qt.ItemIsEditable
+    def index(self, row: int, column: int, parent: QModelIndex) -> QModelIndex:
+        """
+        Creates an index for the specified row, column,
+        and parent and returns it.
+
+        Args:
+            row (int): The row number.
+            column (int): The column number.
+            parent (QModelIndex): The parent index.
 
-    def index(self, row, column, parent):
+        Returns:
+            QModelIndex: The created index.
+        """
         if not parent.isValid():
             parent_node = self._root_node
         else:
@@ -617,39 +1470,79 @@ class InventoryModel(QtCore.QAbstractItemModel):
         child = parent_node.child(row)
         if child is not None:
             return self.createIndex(row, column, child)
-        else:
-            return QtCore.QModelIndex()
+        return QModelIndex()
 
-    def get_index(self, node):
+    def get_index(self, node) -> QModelIndex:
+        """
+        Returns the index for the specified node.
+
+        Args:
+            node (NetworkNode | StationNode):
+                The node for which the index is required.
+
+        Returns:
+            QModelIndex: The index of the specified node.
+        """
         return self.createIndex(node.row(), 0, node)
 
-    def parent(self, index):
+    def parent(self, index: QModelIndex) -> QModelIndex:
+        """
+        Returns the parent index of the specified index.
+
+        Args:
+            index (QModelIndex): The child index.
+
+        Returns:
+            QModelIndex: The parent index.
+        """
         if not index.isValid():
-            return QtCore.QModelIndex()
+            return QModelIndex()
 
         node = index.internalPointer()
         parent = node.parent()
         if parent == self._root_node:
-            return QtCore.QModelIndex()
+            return QModelIndex()
         return self.createIndex(parent.row(), 0, parent)
 
-    def append_node(self, node, parent=QtCore.QModelIndex()):
+    def append_node(self, node, parent=QModelIndex()) -> None:
+        """
+        Appends a new node to the inventory under the specified parent.
+
+        Args:
+            node (NetworkNode | StationNode | ChannelNode):
+                The node to append.
+            parent (QModelIndex, optional):
+                The parent index. Defaults to QModelIndex().
+        """
         position = node.row()
         self.beginInsertRows(parent, position, position)
-        #success = parent_node.add_child(node)
+        # success = parent_node.add_child(node)
         self.endInsertRows()
-        #return success
+        # return success
+
+    def remove_row(self, node, parent=QModelIndex()) -> None:
+        """
+        Removes a node from the inventory.
 
-    def remove_row(self, node, parent=QtCore.QModelIndex()):
+        Args:
+            node (NetworkNode | StationNode | ChannelNode):
+                The node to remove.
+            parent (QModelIndex, optional):
+                The parent index. Defaults to QModelIndex().
+        """
         row = node.row()
         self.beginRemoveRows(parent, row, row)
         node.remove()
         self.endRemoveRows()
 
-    def clear_data(self, node=None):
-        '''
-        Removes all rows in model recursively.
-        '''
+    def clear_data(self, node=None) -> None:
+        """
+        Recursively removes all rows from the model.
+
+        Args:
+            node (NetworkNode | StationNode | ChannelNode, optional):
+                The starting node for clearing. Defaults to the root node.
+        """
         if node is None:
             node = self._root_node
         while node.child_count() > 0:
@@ -657,11 +1550,19 @@ class InventoryModel(QtCore.QAbstractItemModel):
         if node is not self._root_node:
             self.remove_row(node, self.parent(self.get_index(node)))
 
-    def add_inventory(self, inventory, net_r=False, stat_r=False, chan_r=False):
-        '''
-        Replaces data model with new inventory.
-        If you want to add and inventory add before and call.
-        '''
+    def add_inventory(self, inventory,
+                      net_r: bool = False,
+                      stat_r: bool = False,
+                      chan_r: bool = False) -> None:
+        """
+        Replaces the data model with a new inventory.
+
+        Args:
+            inventory (obspyImproved.InventoryIm): The inventory to add.
+            net_r (bool, optional): Reverse sort networks. Defaults to False.
+            stat_r (bool, optional): Reverse sort stations. Defaults to False.
+            chan_r (bool, optional): Reverse sort channels. Defaults to False.
+        """
         self.clear_data()
         # sort networks
         inventory.networks.sort(key=lambda x: x.code, reverse=net_r)
@@ -682,14 +1583,32 @@ class InventoryModel(QtCore.QAbstractItemModel):
                     chan_node = ChannelNode(chan.code, chan, sta_node)
                     self.append_node(chan_node, sta_index)
 
-    def get_node(self, index):
+    def get_node(self, index: QModelIndex) -> object:
+        """
+        Retrieves the node for the specified index.
+
+        Args:
+            index (QModelIndex): The index of the item.
+
+        Returns:
+            (NetworkNode | StationNode | ChannelNode): The node
+                corresponding to the index, or the root node if invalid.
+        """
         if index.isValid():
             node = index.internalPointer()
             if node:
                 return node
         return self._root_node
 
+
 class NexusWindow(*load_ui("NexusWindow.ui")):
+    """
+    Main window for nexus.
+
+    Args:
+        *load_ui("NexusWindow.ui"): Dynamically loads the UI from the file.
+    """
+
     def __init__(self, parent=None):
         super().__init__(parent)
         self.setupUi(self)
@@ -701,16 +1620,11 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
             ChannelNode: False
         }
 
-        # Quick aliases
-        global status_message
-        status_message = self.status_message
-        self.update_gui = QtWidgets.QApplication.processEvents
-
         self.root_node = InventoryNode('root')
         self.inv_model = InventoryModel(self.root_node, self)
         self.uiInventoryTree.setModel(self.inv_model)
         self.uiInventoryTree.setSelectionMode(
-            QtWidgets.QAbstractItemView.ExtendedSelection)
+            QAbstractItemView.SelectionMode.ExtendedSelection)
         self.selector = self.uiInventoryTree.selectionModel()
 
         self.inventory = InventoryIm()
@@ -747,11 +1661,14 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
         # test
         self.actionDebug.triggered.connect(self.debug)
 
-    def sorting_popup(self, point):
-        '''
-        Bring up menu to let user sort
-        inventory in ascending/descending order
-        '''
+    def sorting_popup(self, point) -> None:
+        """
+        Brings up a QMenu to let user sort selected node
+        in ascending or descending order.
+
+        Args:
+            point (QPoint): The user's mouse location.
+        """
         index = self.uiInventoryTree.indexAt(point)
         node = self.inv_model.get_node(index)
         node_type = ''
@@ -761,19 +1678,31 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
             node_type = 'Stations'
         elif isinstance(node, ChannelNode):
             node_type = 'Channels'
-        menu = QtWidgets.QMenu(self)
-        sort_asc = QtGui.QAction(f"Sort {node_type} 0-Z")
-        sort_asc.triggered.connect(lambda: self.set_sorting_order('ascending', type(node)))
-        sort_desc = QtGui.QAction(f"Sort {node_type} Z-0")
-        sort_desc.triggered.connect(lambda: self.set_sorting_order('descending', type(node)))
-        menu.exec([sort_asc, sort_desc], self.uiInventoryTree.mapToGlobal(point))
-
-    def set_sorting_order(self, order, node):
-        '''
-        Repopulate inventory in ascending or
-        descending order
-        '''
-        sort_value = False if order == 'ascending' else True
+        menu = QMenu(self)
+        sort_asc = QAction(f"Sort {node_type} 0-Z")
+        sort_asc.triggered.connect(
+            lambda: self.set_sorting_order('ascending', type(node))
+        )
+        sort_desc = QAction(f"Sort {node_type} Z-0")
+        sort_desc.triggered.connect(
+            lambda: self.set_sorting_order('descending', type(node))
+        )
+        menu.exec(
+            [sort_asc, sort_desc],
+            self.uiInventoryTree.mapToGlobal(point)
+        )
+
+    def set_sorting_order(self, order: str, node) -> None:
+        """
+        Repopulates inventory tree where the given node type is
+        sorted in the given order.
+
+        Args:
+            order (str): Either 'ascending' or 'descending'.
+            node (NetworkNode | StationNode | ChannelNode):
+            The node type to sort.
+        """
+        sort_value = order != 'ascending'
         self.sorting_dict[node] = sort_value
         # keep scroll bar where it was after
         # sorting inventory
@@ -791,10 +1720,16 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
     def debug(self):
         pass
 
-    def set_ui_nrl_root(self):
+    def set_ui_nrl_root(self) -> None:
+        """
+        Sets the url text for the online NRL root.
+        """
         self.actionNRL_root.setText(hardware.NRL_ROOT)
 
-    def nrl_online_toggled(self):
+    def nrl_online_toggled(self) -> None:
+        """
+        Toggles between online or local NRL root.
+        """
         sender = self.sender()
         if sender.isChecked():
             self.NRL_online = True
@@ -804,8 +1739,11 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
             self.NRL_online = False
             self.nrl_root_local_dialog()
 
-    def nrl_root_local_dialog(self):
-        dir_name = QtWidgets.QFileDialog.getExistingDirectory(
+    def nrl_root_local_dialog(self) -> None:
+        """
+        Opens dialog for user to select local NRL root.
+        """
+        dir_name = QFileDialog.getExistingDirectory(
             self,
             'Local NRL root:')
         if dir_name:
@@ -815,16 +1753,28 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
         else:
             self.actionNRL_online.setChecked(True)
 
-    def status_message(self, message):
+    def status_message(self, message: str) -> None:
+        """
+        Updates status bar to display the given `message`.
+
+        Args:
+            message (str): Text to display in the status bar.
+        """
         self.statusBar().showMessage(message)
-        self.update_gui()
+        QApplication.processEvents()
 
-    def reload_data_changed(self):
-        '''Lazy way to have the datatable reload'''
+    def reload_data_changed(self) -> None:
+        """
+        Lazy way to have the datatable reload.
+        """
         self.inv_model.layoutAboutToBeChanged.emit()
         self.inv_model.layoutChanged.emit()
 
-    def add(self):
+    def add(self) -> None:
+        """
+        Adds a new item to the inventory tree depending on the currently
+        selected item; e.g. if a Network is selected, adds a new Network.
+        """
         item = self.get_inv_obj_from_selection()
         self.inventory.clear_new()
         if isinstance(item, Network):
@@ -836,7 +1786,10 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
         self.inv_model.add_inventory(self.inventory)
         self.reshape_tree()
 
-    def subtract(self):
+    def subtract(self) -> None:
+        """
+        Removes selected items from the inventory tree.
+        """
         items = self.get_inv_obj_from_selection('subtract')
         for item in items:
             i = item.model().get_node(item)._inv_obj
@@ -844,24 +1797,41 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
         self.inv_model.add_inventory(self.inventory)
         self.reshape_tree()
 
-    def get_inv_obj_from_selection(self, caller=''):
+    def get_inv_obj_from_selection(self, caller: str = ''):
+        """
+        If `caller` arg isn't given, returns the obspy inventory object
+        from the selected item in the inventory tree. If `caller = 'subtract'`,
+        returns a list of the selected QModelIndex's.
+
+        Args:
+            caller (str, optional): Influences return value. Defaults to ''.
+
+        Returns:
+            (list[QModelIndex] | Network | Station | Channel): A list of
+                selected QModelIndex's or the obspy inventory object of
+                the selected index.
+        """
         index_list = self.selector.selectedRows()
         if caller == 'subtract':
             return index_list
-        else:
-            if len(index_list) != 1:
-                return
-            index = index_list[0]
-            return index.model().get_node(index)._inv_obj
-
+        if len(index_list) != 1:
+            return None
+        index = index_list[0]
+        return index.model().get_node(index)._inv_obj
 
-    def calculate_responses(self):
+    def calculate_responses(self) -> None:
+        """
+        Calculate responses for current inventory.
+        """
         self.status_message('Calculating responses...')
         self.inventory.calculate_responses()
         self.status_message('Calculating responses... Done.')
         self.reload_data_changed()
 
-    def split_by_epoch(self):
+    def split_by_epoch(self) -> None:
+        """
+        Split station by given epoch.
+        """
         # indexes_selected = self.selector.selectedRows()
         # print(indexes_selected)
         # for i in indexes_selected:
@@ -879,27 +1849,31 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
         # Get time to split
         middle = utc_to_str.utc_to_str(
             self.inventory.middle_epoch(station))
-        text, ok = QtWidgets.QInputDialog.getText(self,
-                                                  'Split station',
-                                                  'Split time:',
-                                                  text=middle)
+        text, ok = QInputDialog.getText(
+            self,
+            'Split station',
+            'Split time:',
+            text=middle
+        )
         if ok:
             time = utc_to_str.utc_from_str(text)
         else:
             return
 
-
         # Split the inventory
         self.inventory.split_station_by_epoch(station, time=time)
 
         # Recreate editor
         self.inv_model.add_inventory(self.inventory)
 
-        #self.reshape_tree()
+        # self.reshape_tree()
 
         return
 
-    def split_by_chan(self):
+    def split_by_chan(self) -> None:
+        """
+        Split station by selected channel(s).
+        """
         station = self.get_inv_obj_from_selection()
         if not isinstance(station, Station):
             self.status_message('Only stations can be split.')
@@ -912,36 +1886,52 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
                 )
             self.inv_model.add_inventory(self.inventory)
 
-    def scan_ms_dialog(self):
-        dir_name = QtWidgets.QFileDialog.getExistingDirectory(self, 'Dir to scan for MSEED')
+    def scan_ms_dialog(self) -> None:
+        """
+        Allow user to scan for mseeds.
+        """
+        dir_name = QFileDialog.getExistingDirectory(
+            self, 'Dir to scan for MSEED'
+        )
         if not dir_name:
             return
         # For testing
         # dir_name = '.'
-        self.status_message('Scanning {}...'.format(dir_name))
+        self.status_message(f'Scanning {dir_name}...')
 
-        #num_mseed = self.inventory.scan_slow(
+        # num_mseed = self.inventory.scan_slow(
         num_mseed = self.inventory.scan_quick(
-            dir_name, new=self._new, log_message=self.status_message)
-        self.status_message('Scanning {}... Done. '
-                            'Found {} MS files.'.format(
-                                dir_name, num_mseed))
+            dir_name, new=self._new, log_message=self.status_message
+        )
+        self.status_message(f'Scanning {dir_name}... Done. '
+                            f'Found {num_mseed} MS files.')
         if self._new is False:
             self.inventory.clear_new()
         self._new = True
         self.inv_model.add_inventory(self.inventory)
         self.reshape_tree()
 
-    def read_xml_dialog(self):
-        # file_name, __ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open StationXML')
+    def read_xml_dialog(self) -> bool:
+        """
+        Populate inventory from selected StationXML.
+
+        Returns:
+            (bool | None): Returns `True` if read is successful.
+                `False` otherwise.
+        """
+        # file_name, __ = QtWidgets.QFileDialog.getOpenFileName(
+        # self, 'Open StationXML')
         # testing
         file_name = './test/data/ANDIVOLC.xml'
-        file_name, __ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open StationXML', './test/data')
+        file_name, __ = QFileDialog.getOpenFileName(
+            self, 'Open StationXML', './test/data'
+        )
 
         if file_name != '':
-            self.status_message('Opening StationXML {}...'.format(file_name))
+            self.status_message(f'Opening StationXML {file_name}...')
             try:
-                #new_inventory = obspy.read_inventory(file_name, format='STATIONXML')
+                # new_inventory = obspy.read_inventory(
+                # file_name, format='STATIONXML')
                 new_inventory = read_inventory(file_name)
                 new_inventory = InventoryIm() + new_inventory
                 if self._new:
@@ -949,56 +1939,61 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
                 self._new = True
                 self.inventory.clear_new()
                 self.inventory += new_inventory
-                #self.inv_model.beginResetModel()
+                # self.inv_model.beginResetModel()
                 self.inv_model.add_inventory(self.inventory)
-                #self.inv_model.endResetModel()
+                # self.inv_model.endResetModel()
                 self.reshape_tree()
-                self.status_message('Opening StationXML {}... Done.'.format(
-                    file_name))
+                self.status_message(f'Opening StationXML {file_name}... Done.')
                 self._new = True
                 return True
             except Exception as e:
-                self.status_message('Opening StationXML {}... Failed.'.format(
-                    file_name))
+                self.status_message(f'Opening StationXML {file_name}... Failed.')
                 print(e)
-                print('Failed to read {} as StationXML.'.format(file_name))
+                print(f'Failed to read {file_name} as StationXML.')
                 return False
         else:
-            self.status_message('Opening StationXML {}... Bad filename'.format(
-                file_name))
-
+            self.status_message(f'Opening StationXML {file_name}... Bad filename')
+            return False
 
-    def plot_stations(self):
+    def plot_stations(self) -> None:
+        """
+        Plot current inventory.
+        """
         try:
             self.status_message('Plotting Stations...')
-            #self.inventory.plot()
+            # self.inventory.plot()
             self.inventory.plot(projection='ortho')
-            #self.inventory.plot(projection='local')
+            # self.inventory.plot(projection='local')
         except Exception as e:
             self.status_message('Plotting Stations... '
-                                         'Failed. {}.'.format(e))
+                                f'Failed. {e}.')
             print(e)
             return
         self.status_message('Plotting Stations... Done.')
 
-    def write_xml_dialog(self):
-        file_name, __ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save '
-                                                          'StationXML')
+    def write_xml_dialog(self) -> None:
+        """
+        Write current inventory to StationXML.
+        """
+        file_name, __ = QFileDialog.getSaveFileName(self, 'Save '
+                                                    'StationXML')
         if file_name == '':
-            self.status_message('Saving{}... Canceled'.format(file_name))
+            self.status_message(f'Saving{file_name}... Canceled')
             return
-        self.status_message('Saving{}...'.format(file_name))
+        self.status_message(f'Saving{file_name}...')
         self.inventory.write(file_name, format='STATIONXML')
-        self.status_message('Saving{}...Done.'.format(file_name))
+        self.status_message(f'Saving{file_name}...Done.')
 
-    def reshape_tree(self):
+    def reshape_tree(self) -> None:
         """
-        Expands entire tree and makes contents fit columns
+        Expands entire inventory tree and makes contents fit columns.
         """
         self.uiInventoryTree.expandAll()
         for col in range(self.inv_model.columnCount(None)):
             self.uiInventoryTree.resizeColumnToContents(col)
-        self.uiInventoryTree.header().setSectionResizeMode(QHeaderView.Interactive)
+        self.uiInventoryTree.header().setSectionResizeMode(
+            QHeaderView.Interactive
+        )
 
     def closeEvent(self, event):
         """
@@ -1023,22 +2018,41 @@ class NexusWindow(*load_ui("NexusWindow.ui")):
             self.write_xml_dialog()
             event.accept()
 
-
-    def keyPressEvent(self, event):
+    def keyPressEvent(self, event: QKeyEvent):
         """
-            Close application from escape key.
-            results in QMessageBox dialog from closeEvent, good but how/why?
+        Close application from escape key.
+        results in QMessageBox dialog from closeEvent, good but how/why?
         """
-        if event.key() == Qt.Key_Escape:
+        if event.key() == Qt.Key.Key_Escape:
             self.close()
 
 
-
 class EditorWiget(*load_ui("EditorWidget.ui")):
+    """
+    Widget for managing and editing inventory data.
+
+    Provides a unified interface for editing network, station,
+    and channel data using a set of general and specific editor widgets.
+    The UI layout is loaded from `EditorWidget.ui`.
+
+    Args:
+        *load_ui("EditorWidget.ui"): Dynamically loads the UI from the file.
+    """
+
     def __init__(self, parent=None):
+        """
+        Initializes `EditorWidget` and its child editors.
+
+        Sets up the editor widgets for network, station, and channel data.
+        Adds these widgets to the general and specific editor layouts and
+        initially hides them.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
         self.setupUi(self)
-        #self.inv_model = None
+        # self.inv_model = None
 
         self.network_gen_editor = NetworkGenWidget(self)
         self.station_gen_editor = StationGenWidget(self)
@@ -1062,7 +2076,14 @@ class EditorWiget(*load_ui("EditorWidget.ui")):
         self.station_editor.setVisible(False)
         self.channel_editor.setVisible(False)
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the editor widget and its child editors.
+
+        Args:
+            model (InventoryModel): The inventory model to bind data to the
+                                    editor widgets.
+        """
         self.inv_model = model
         self.network_gen_editor.setModel(self.inv_model)
         self.station_gen_editor.setModel(self.inv_model)
@@ -1071,7 +2092,17 @@ class EditorWiget(*load_ui("EditorWidget.ui")):
         self.station_editor.setModel(self.inv_model)
         self.channel_editor.setModel(self.inv_model)
 
-    def setSelection(self, current, old):
+    def setSelection(self, current: QModelIndex, old: QModelIndex) -> None:
+        """
+        Updates the visibility and selection state of the editor widgets
+        based on the currently selected item in the inventory model,
+        depending on whether the current selection is a network, station,
+        or channel node.
+
+        Args:
+            current (QModelIndex): The currently selected item in the model.
+            old (QModelIndex): The previously selected item in the model.
+        """
         node = current.internalPointer()
         if node is not None:
             self.network_gen_editor.setVisible(True)
@@ -1125,68 +2156,242 @@ class EditorWiget(*load_ui("EditorWidget.ui")):
 
 
 class WidgetBase():
+    """
+    Base class for creating UI widgets with data binding.
+
+    Provides common functionality for widgets, including setting up the UI,
+    binding a data model to the widget using a QDataWidgetMapper, and managing
+    selections in the data model.
+    """
+
     def __init__(self, *args, **kwargs):
+        """
+        Initializes the widget and sets up the user interface.
+        """
         super().__init__()
         self.setupUi(self)
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the widget and initializes a data mapper.
+
+        The data mapper binds the model's data to the widget's UI elements.
+
+        Args:
+            model (InventoryModel): The data model bound to the widget.
+        """
         self._model = model
-        self._data_mapper = QtWidgets.QDataWidgetMapper()
+        self._data_mapper = QDataWidgetMapper()
         self._data_mapper.setModel(model)
 
-    def map_code_start_end(self):
+    def map_code_start_end(self) -> None:
+        """
+        Maps the `code`, `start`, and `end` fields of the data model to the
+        corresponding UI elements.
+        """
         n = InventoryNode
         self._data_mapper.addMapping(self.uiCode, n.col(n.code))
         self._data_mapper.addMapping(self.uiStart, n.col(n.start))
         self._data_mapper.addMapping(self.uiEnd, n.col(n.end))
 
-    def setSelection(self, current, old):
+    def setSelection(self, current: QModelIndex, old: QModelIndex) -> None:
+        """
+        Updates the widget to reflect the current selection in the data model.
+
+        Updates the root index of the data mapper to the parent of the current
+        selection and sets the current index to the selected row.
+
+        Args:
+            current (QModelIndex): The current index representing the selected
+                                   item.
+            old (QModelIndex): The previous index representing the previously
+                               selected item.
+        """
         parent = current.parent()
         self._data_mapper.setRootIndex(parent)
         self._data_mapper.setCurrentIndex(current.row())
 
 
 class NetworkGenWidget(WidgetBase, *load_ui("NetworkGenWidget.ui")):
+    """
+    Widget for managing general network data.
+
+    Extends `WidgetBase` and uses the UI design from `NetworkGenWidget.ui`.
+    Provides functionality for binding network data to the UI elements
+    using a data model.
+
+    Args:
+        WidgetBase: The base class providing shared widget functionality.
+        *load_ui("NetworkGenWidget.ui"): Dynamically loads the UI from
+                                         the file.
+    """
+
     def __init__(self, parent=None):
+        """
+        Initializes `NetworkGenWidget`.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the widget and maps network fields to the UI.
+
+        Binds the `code`, `start`, and `end` fields of the
+        network data model to the corresponding UI elements.
+
+        Args:
+            model (InventoryModel): The data model containing network metadata.
+        """
         super().setModel(model)
         self.map_code_start_end()
 
 
 class StationGenWidget(WidgetBase, *load_ui("StationGenWidget.ui")):
+    """
+    Widget for managing general station data.
+
+    Extends `WidgetBase` and uses the UI design from `StationGenWidget.ui`.
+    Provides functionality for binding station data to the UI elements
+    using a data model.
+
+    Args:
+        WidgetBase: The base class providing shared widget functionality.
+        *load_ui("StationGenWidget.ui"): Dynamically loads the UI from
+                                         the file.
+    """
+
     def __init__(self, parent=None):
+        """
+        Initializes `StationGenWidget`.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the widget and maps station fields to the UI.
+
+        Binds the `code`, `start`, and `end` fields of the
+        station data model to the corresponding UI elements.
+
+        Args:
+            model (InventoryModel): The data model containing network metadata.
+        """
         super().setModel(model)
         self.map_code_start_end()
 
 
 class ChannelGenWidget(WidgetBase, *load_ui("ChannelGenWidget.ui")):
+    """
+    Widget for managing general channel data.
+
+    Extends `WidgetBase` and uses the UI design from `ChannelGenWidget.ui`.
+    Provides functionality for binding channel data to the UI elements
+    using a data model.
+
+    Args:
+        WidgetBase: The base class providing shared widget functionality.
+        *load_ui("StationGenWidget.ui"): Dynamically loads the UI from the
+                                         file.
+    """
     def __init__(self, parent=None):
+        """
+        Initializes `ChannelGenWidget`.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the widget and maps channel fields to the UI.
+
+        Binds the `code`, `start`, and `end` fields of the
+        channel data model to the corresponding UI elements.
+
+        Args:
+            model (InventoryModel): The data model containing network metadata.
+        """
         super().setModel(model)
         self.map_code_start_end()
         n = ChannelNode
-        self._data_mapper.addMapping(self.uiLocationCode, n.col(n.location_code))
-        self._data_mapper.addMapping(self.uiSampleRate, n.col(n.sample_rate))
+        self._data_mapper.addMapping(
+            self.uiLocationCode, n.col(n.location_code)
+        )
+        self._data_mapper.addMapping(
+            self.uiSampleRate, n.col(n.sample_rate)
+        )
+
 
 class DescriptionWidget(WidgetBase, *load_ui("DescriptionWidget.ui")):
+    """
+    Widget for managing descriptive data.
+
+    Extends `WidgetBase` and uses the UI design from the
+    `DescriptionWidget.ui` file. Provides functionality for
+    binding descriptive data from a model to the UI.
+
+    Args:
+        WidgetBase: The base class for the widget.
+        *load_ui("DescriptionWidget.ui"): Dynamically loads the UI from
+                                          the file.
+    """
     def __init__(self, parent=None):
+        """
+        Initializes the DescriptionWidget.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
 
-    def setModel(self, model, node_type):
+    def setModel(self, model: InventoryModel, node_type):
+        """
+        Sets the data model for the widget and maps the description field
+        to the appropriate UI element.
+
+        Args:
+            model (InventoryModel): The data model containing the description
+                                    data.
+            node_type (NetworkNode | Any): The type of node to determine
+                                           the mapping for the description
+                                           field. Currently is only ever a
+                                           NetworkNode.
+        """
         super().setModel(model)
         n = node_type
         self._data_mapper.addMapping(self.uiDescription, n.col(n.description))
 
 
 class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
+    """
+    Widget for managing station data.
+
+    Extends `WidgetBase` and uses the UI design from the `StationWidget.ui`
+    file. Provides functionality for mapping station data to the UI
+    elements and handling user interactions.
+
+    Args:
+        WidgetBase: The base class for `StationWidget`.
+        *load_ui("StationWidget.ui"): Dynamically loads the UI from the file.
+    """
+
     def __init__(self, parent=None):
+        """
+        Initializes the StationWidget.
+
+        Sets up the UI elements, connects signals to their respective slots,
+        and populates dropdown menus for data loggers and sensors.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
         self.build_dl_combo()
         self.copyDL_button.clicked.connect(self.copy_dltype_and_gain)
@@ -1198,11 +2403,24 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
         self.uiSensorType.currentIndexChanged.connect(self.update_model)
         self.uiDLGain.editingFinished.connect(self.update_model)
 
-    def update_model(self):
+    def update_model(self) -> None:
+        """
+        Updates the station model to reflect changes made in the UI.
+
+        Emits signals to notify that the layout of the model is about to change
+        and has changed.
+        """
         self._model.layoutAboutToBeChanged.emit()
         self._model.layoutChanged.emit()
 
-    def build_dl_combo(self):
+    def build_dl_combo(self) -> None:
+        """
+        Populates the data logger combo box with available data loggers.
+
+        Removes any existing items from the combo box and repopulates it with
+        options from the `DLS.names()` list, adding an ellipsis as the last
+        item.
+        """
         # Remove all items
         for i in reversed(range(self.uiDLType.count())):
             self.uiDLType.removeItem(i)
@@ -1211,7 +2429,14 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
             self.uiDLType.addItem(dl)
         self.uiDLType.addItem(ELIPSES)
 
-    def build_sensor_combo(self):
+    def build_sensor_combo(self) -> None:
+        """
+        Populates the sensor type combo box with available sensors.
+
+        Removes any existing items from the combo box and repopulates it with
+        options from the `SENSORS.names()` list, adding an ellipsis as the last
+        item.
+        """
         # Remove all items
         for i in reversed(range(self.uiSensorType.count())):
             self.uiSensorType.removeItem(i)
@@ -1220,12 +2445,22 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
             self.uiSensorType.addItem(sensor)
         self.uiSensorType.addItem(ELIPSES)
 
-
     # Hack as datamapper wont call setData for comboboxes
-    def combo_changed(self, value):
+    def combo_changed(self, value: int) -> None:
+        """
+        Handles changes in the combo box selections for data loggers and
+        sensors.
+
+        Updates the model with the new selection or adds a new data logger or
+        sensor if the ellipsis option is selected.
+
+        Args:
+            value (int): The index of the selected item in the combo box.
+        """
         parent = self._data_mapper.rootIndex()
         row = self._data_mapper.currentIndex()
         sender_name = self.sender().objectName()
+        column = None
         if sender_name == 'uiDLType':
             column = StationNode.col(StationNode.dl_type)
             if self.sender().itemText(value) == ELIPSES:
@@ -1257,46 +2492,108 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
                         value = SENSORS.index('Na')
                 except Exception as e:
                     status_message('Failed to pick response. See stdout.')
+                    print(e)
                     value = SENSORS.index('Na')
 
         index = self._model.index(row, column, parent)
         self._model.setData(index, value)
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the widget and maps its fields to the UI.
+
+        Args:
+            model: The data model containing station information.
+        """
         super().setModel(model)
         n = StationNode
-        self._data_mapper.addMapping(self.uiLatitude, n.col(n.latitude))
-        self._data_mapper.addMapping(self.uiLongitude, n.col(n.longitude))
-        self._data_mapper.addMapping(self.uiElevation, n.col(n.elevation))
-        self._data_mapper.addMapping(self.uiDepth, n.col(n.depth_station))
-        self._data_mapper.addMapping(self.uiDLSN, n.col(n.dl_sn))
-        self._data_mapper.addMapping(self.uiDLGain, n.col(n.dl_gain))
-        self._data_mapper.addMapping(self.uiDLType, n.col(n.dl_type),
-                                     bytes('currentIndex','ascii'))
-
-        self._data_mapper.addMapping(self.uiSensorSN, n.col(n.sensor_sn))
-        self._data_mapper.addMapping(self.uiSensorType, n.col(n.sensor_type),
-                                     bytes('currentIndex', 'ascii'))
-        self._data_mapper.addMapping(self.uiSiteName, n.col(n.site_name))
-        self._data_mapper.addMapping(self.uiSiteDescription, n.col(n.site_description))
-        self._data_mapper.addMapping(self.uiSiteTown, n.col(n.site_town))
-        self._data_mapper.addMapping(self.uiSiteCounty, n.col(n.site_county))
-        self._data_mapper.addMapping(self.uiSiteRegion, n.col(n.site_region))
-        self._data_mapper.addMapping(self.uiSiteCountry, n.col(n.site_country))
-
-    def copy_dltype_and_gain(self):
+        self._data_mapper.addMapping(
+            self.uiLatitude, n.col(n.latitude)
+        )
+        self._data_mapper.addMapping(
+            self.uiLongitude, n.col(n.longitude)
+        )
+        self._data_mapper.addMapping(
+            self.uiElevation, n.col(n.elevation)
+        )
+        self._data_mapper.addMapping(
+            self.uiDepth, n.col(n.depth_station)
+        )
+        self._data_mapper.addMapping(
+            self.uiDLSN, n.col(n.dl_sn)
+        )
+        self._data_mapper.addMapping(
+            self.uiDLGain, n.col(n.dl_gain)
+        )
+        self._data_mapper.addMapping(
+            self.uiDLType, n.col(n.dl_type),
+            bytes('currentIndex', 'ascii')
+        )
+        self._data_mapper.addMapping(
+            self.uiSensorSN, n.col(n.sensor_sn)
+        )
+        self._data_mapper.addMapping(
+            self.uiSensorType, n.col(n.sensor_type),
+            bytes('currentIndex', 'ascii')
+        )
+        self._data_mapper.addMapping(
+            self.uiSiteName, n.col(n.site_name)
+        )
+        self._data_mapper.addMapping(
+            self.uiSiteDescription, n.col(n.site_description)
+        )
+        self._data_mapper.addMapping(
+            self.uiSiteTown, n.col(n.site_town)
+        )
+        self._data_mapper.addMapping(
+            self.uiSiteCounty, n.col(n.site_county)
+        )
+        self._data_mapper.addMapping(
+            self.uiSiteRegion, n.col(n.site_region)
+        )
+        self._data_mapper.addMapping(
+            self.uiSiteCountry, n.col(n.site_country)
+        )
+
+    def copy_dltype_and_gain(self) -> None:
+        """
+        Begins process to copy either sensor type or
+        dl type/gain to other stations in the inventory
+        model.
+        """
         stations = self.get_stations()
         self.copy_to_stations(stations,
                               'type & gain',
-                              [self.uiDLGain.text(), self.uiDLType.currentText()])
+                              [
+                                  self.uiDLGain.text(),
+                                  self.uiDLType.currentText()
+                              ])
 
-    def copy_sensor(self):
+    def copy_sensor(self) -> None:
+        """
+        Called when user clicks 'Copy Type to More Stations' button
+        under Sensor. Gets all stations from the inventory model
+        and then opens dialog to select which stations to copy
+        the sensor type to.
+        """
         stations = self.get_stations()
         self.copy_to_stations(stations,
                               'sensor',
                               [self.uiSensorType.currentText()])
 
-    def copy_to_stations(self, stations, action, values):
+    def copy_to_stations(self, stations: list[Station], action: str,
+                         values: list[str]) -> None:
+        """
+        Initializes `StationSelectDialog` with the all the stations in
+        the inventory model and handles user interaction.
+
+        Args:
+            stations (list[Station]): List of Station objects from the
+                                      inventory model.
+            action (str): Represents which values to copy. Either 'sensor' or
+                          'type & gain'.
+            values (list[str]): List of values to copy to other stations.
+        """
         dialog = StationSelectDialog(stations, action, values, parent=self)
         if dialog.exec_():
             if dialog.uiSensorLabel.parent() is None:
@@ -1306,7 +2603,18 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
                 copy_this = {"Stype": True}
             self.update_stations(dialog.selected_stations, copy_this)
 
-    def update_stations(self, stations, copy_this):
+    def update_stations(self, stations: list[Station],
+                        copy_this: dict[str, bool]) -> None:
+        """
+        Once user clicks OK on the `StationSelectDialog`, updates
+        the selected stations to have the given sensor type or dl
+        type/gain values.
+
+        Args:
+            stations (list[Station]): List of stations to copy values to.
+            copy_this (dict[str, bool]): Contains which values to copy.
+                                         E.g. {'DLtype': True, 'DLgain': True}
+        """
         for station in stations:
             for node, obj in self.stat_node_to_obj_map.items():
                 if station == obj:
@@ -1318,15 +2626,17 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
                         elif key == "DLgain":
                             node.set_dl_gain(self.uiDLGain.text())
                         else:
-                            node.set_sensor_type(self.uiSensorType.currentIndex())
+                            node.set_sensor_type(
+                                self.uiSensorType.currentIndex()
+                            )
 
-    def get_stations(self):
+    def get_stations(self) -> list[Station]:
         """
-        Get all stations from inventory to
-        add to Station Select Dialogue
+        Returns all the stations within the inventory model
+        to the `StationSelectDialog`.
         """
         # get top most network
-        net = self._model.index(0, 0, QtCore.QModelIndex())
+        net = self._model.index(0, 0, QModelIndex())
         self.stat_node_to_obj_map = {}
         stations = []
         net_row = 0
@@ -1334,7 +2644,8 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
             while True:
                 for r in range(self._model.rowCount(net)):
                     station_index = self._model.index(r, 0, net)
-                    station_node = station_index.model().get_node(station_index)
+                    station_node = (station_index.model().
+                                    get_node(station_index))
                     station_obj = station_node._inv_obj
                     stations.append(station_obj)
                     self.stat_node_to_obj_map[station_node] = station_obj
@@ -1345,24 +2656,80 @@ class StationWidget(WidgetBase, *load_ui("StationWidget.ui")):
 
 
 class ChannelWidget(WidgetBase, *load_ui("ChannelWidget.ui")):
+    """
+    Widget for managing channel data.
+
+    Extends `WidgetBase` and uses the UI design from the `ChannelWidget.ui`
+    file. Provides functionality for mapping channel data to the UI
+    elements and handling user interactions.
+
+    Args:
+        WidgetBase: The base class for `ChannelWidget`.
+        *load_ui("ChannelWidget.ui"): Dynamically loads the UI from the file.
+    """
+
     def __init__(self, parent=None):
+        """
+        Initializes the ChannelWidget.
+
+        Sets up the UI elements and loads the available channel types
+        into the combo box.
+
+        Args:
+            parent (QWidget, optional): The parent widget. Defaults to None.
+        """
         super().__init__(parent)
         self.uiChannelTypeCombo.addItem('GEOPHYSICAL')
         self.uiChannelTypeCombo.addItem('HEALTH')
 
-    def setModel(self, model):
+    def setModel(self, model: InventoryModel) -> None:
+        """
+        Sets the data model for the widget and maps the model's data
+        to the appropriate UI elements. Includes azimuth, dip, depth,
+        comment, sensor description, latitude, longitude, and elevation.
+
+        Args:
+            model (InventoryModel): The data model to be set for the widget.
+        """
         super().setModel(model)
         n = ChannelNode
-        self._data_mapper.addMapping(self.uiAzimuth, n.col(n.azimuth))
-        self._data_mapper.addMapping(self.uiDip, n.col(n.dip))
-        self._data_mapper.addMapping(self.uiDepth, n.col(n.depth))
-        self._data_mapper.addMapping(self.uiComment, n.col(n.channel_comment))
-        self._data_mapper.addMapping(self.uiSensorDesc, n.col(n.sensor_description))
-        self._data_mapper.addMapping(self.uiLatitude, n.col(n.latitude))
-        self._data_mapper.addMapping(self.uiLongitude, n.col(n.longitude))
-        self._data_mapper.addMapping(self.uiElevation, n.col(n.elevation))
-
-    def setSelection(self, current, old):
+        self._data_mapper.addMapping(
+            self.uiAzimuth, n.col(n.azimuth)
+        )
+        self._data_mapper.addMapping(
+            self.uiDip, n.col(n.dip)
+        )
+        self._data_mapper.addMapping(
+            self.uiDepth, n.col(n.depth)
+        )
+        self._data_mapper.addMapping(
+            self.uiComment, n.col(n.channel_comment)
+        )
+        self._data_mapper.addMapping(
+            self.uiSensorDesc, n.col(n.sensor_description)
+        )
+        self._data_mapper.addMapping(
+            self.uiLatitude, n.col(n.latitude)
+        )
+        self._data_mapper.addMapping(
+            self.uiLongitude, n.col(n.longitude)
+        )
+        self._data_mapper.addMapping(
+            self.uiElevation, n.col(n.elevation)
+        )
+
+    def setSelection(self, current: QModelIndex, old: QModelIndex) -> None:
+        """
+        Updates the UI based on the current selection in the model.
+
+        Ensures that the response-related UI elements are updated depending
+        on whether the selected node has a response or not. Disconnects and
+        reconnects appropriate signals to avoid redundancy.
+
+        Args:
+            current (QModelIndex): The current selected item in the model.
+            old (QModelIndex): The previously selected item in the model.
+        """
         super().setSelection(current, old)
         try:
             # There is difference between Python and C++ version of disconnect
@@ -1383,11 +2750,15 @@ class ChannelWidget(WidgetBase, *load_ui("ChannelWidget.ui")):
 
 
 def main():
-    app = QtWidgets.QApplication(sys.argv)
+    """
+    The entry point of the application.
+    """
+    app = QApplication(sys.argv)
     window = NexusWindow()
     window.show()
 
     sys.exit(app.exec_())
 
+
 if __name__ == '__main__':
     main()
diff --git a/nexus/obspy_improved.py b/nexus/obspy_improved.py
index 7f22c55f1f6f5af82ba38c11791cf24730ad3d5a..76fbf8bf87c1c1e84ea73497a4629669696437be 100644
--- a/nexus/obspy_improved.py
+++ b/nexus/obspy_improved.py
@@ -15,6 +15,7 @@ from obspy import Inventory
 from obspy import UTCDateTime
 from obspy.core.inventory import (Network, Station, Channel,
                                   Site, Equipment)
+from obspy.core.inventory.util import Comment
 from obspy.io.mseed.core import _is_mseed
 from obspy.io.mseed import util
 
@@ -298,6 +299,9 @@ class InventoryIm(Inventory):
             inv_cha = inv_sta.channels[inv_sta.channels.index(inv_cha)]
             if self.extend_epoch(inv_cha, start, end):
                 inv_cha._new = new
+        # add Comment object to Channel
+        comment = Comment('')
+        inv_cha.comments.append(comment)
 
     def extend_epoch(self, inv, start: UTCDateTime, end: UTCDateTime) -> bool:
         """
@@ -386,6 +390,7 @@ class InventoryIm(Inventory):
             return True
         return False
 
+
     def populate_from_streams_old(self, streams, new=False):
         """
         Deprecated.
@@ -847,3 +852,31 @@ def utc_from_str(value: str) -> UTCDateTime:
         hour=hour, minute=minute,
         second=second, microsecond=microsecond
     )
+
+def scan_ms(dir_name, status_message=print):
+    # Remove timing test
+    import time
+    start = time.time()
+
+    mseed_files = []
+    streams = []
+    for root, dirs, files in os.walk(dir_name):
+        for name in files:
+            abs_path = os.path.join(root, name)
+            status_message('Scanning {}...'.format(abs_path))
+            if ( os.path.isfile(abs_path) and
+                 _is_mseed(abs_path) ):
+                mseed_files.append(abs_path)
+                try:
+                    st = obspy_read(abs_path, headonly=True)
+                except:
+                    print('Failed to read file {}'.format(abs_path))
+                streams.append(st)
+    print(f'Scan took: {time.time() - start}')
+    return streams, mseed_files
+
+def quick_scan_ms(dir_name, status_message=print):
+    import time
+    start = time.time()
+
+    print(f'Scan took: {time.time() - start}')
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index f47612e18bdcb477ea478b87154196366e33f9c4..b84eb502f8136661bde1c28cab40f7689ff23164 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 2023.4.0.0
+current_version = 2023.4.6.3
 commit = True
 tag = True
 
diff --git a/setup.py b/setup.py
index 6663c670df6e080b09ed779b6afc39481a293b53..904b93a69d4c4c141cf6f929edc581ddd8c404cf 100644
--- a/setup.py
+++ b/setup.py
@@ -52,6 +52,6 @@ setup(
     packages=find_packages(include=['nexus']),
     test_suite='tests',
     url='https://git.passcal.nmt.edu/software_public/passoft/nexus',
-    version='2023.4.6.2',
+    version='2023.4.6.3',
     zip_safe=False,
 )