code: def get_bbox(self):
          
  
          bb_top, bb_left, bb_bottom, bb_right = [None] * 4
  
          # Block selections
  
          for top_left, bottom_right in zip(self.block_tl, self.block_br):
              top, left = top_left
              bottom, right = bottom_right
  
              if bb_top is None or bb_top > top:
                  bb_top = top
              if bb_left is None or bb_left > left:
                  bb_left = left
              if bb_bottom is None or bb_bottom < bottom:
                  bb_bottom = bottom
              if bb_right is None or bb_right > right:
                  bb_right = right
  
          # Row and column selections
  
          for row in self.rows:
              if bb_top is None or bb_top > row:
                  bb_top = row
              if bb_bottom is None or bb_bottom < row:
                  bb_bottom = row
  
          for col in self.cols:
              if bb_left is None or bb_left > col:
                  bb_left = col
              if bb_right is None or bb_right < col:
                  bb_right = col
  
          # Cell selections
  
          for cell in self.cells:
              cell_row, cell_col = cell
  
              if bb_top is None or bb_top > cell_row:
                  bb_top = cell_row
              if bb_left is None or bb_left > cell_col:
                  bb_left = cell_col
              if bb_bottom is None or bb_bottom < cell_row:
                  bb_bottom = cell_row
              if bb_right is None or bb_right < cell_col:
                  bb_right = cell_col
  
          if all(val is None for val in [bb_top, bb_left, bb_bottom, bb_right]):
              return None
  
          return ((bb_top, bb_left), (bb_bottom, bb_right))
docs: Returns ((top, left), (bottom, right)) of bounding box
  
          A bounding box is the smallest rectangle that contains all selections.
          Non-specified boundaries are None.