code: def do_paste(self, event):
          
          # find where the user has clicked
          col_ind = self.GetGridCursorCol()
          row_ind = self.GetGridCursorRow()
          # read in clipboard text
          text_df = pd.read_clipboard(header=None, sep='\t').fillna('')
          # add extra rows if need to accomadate clipboard text
          row_length_diff = len(text_df) - (len(self.row_labels) - row_ind)
          if row_length_diff > 0:
              for n in range(row_length_diff):
                  self.add_row()
          # ignore excess columns if present
          col_length_diff = len(text_df.columns) - (len(self.col_labels) - col_ind)
          if col_length_diff > 0:
              text_df = text_df.iloc[:, :-col_length_diff].copy()
          # go through copied text and parse it into the grid rows
          for label, row_data in text_df.iterrows():
              col_range = list(range(col_ind, col_ind + len(row_data)))
              if len(row_data) > 1:
                  cols = list(zip(col_range, row_data.index))
                  for column in cols:
                      value = row_data[column[1]]
                      this_col = column[0]
                      self.SetCellValue(row_ind, this_col, str(value))
              else:
                  value = row_data[0]
                  self.SetCellValue(row_ind, col_ind, str(value))
              row_ind += 1
          # could instead use wxPython clipboard here
          # see old git history for that
          self.size_grid()
          event.Skip()
docs: Read clipboard into dataframe
          Paste data into grid, adding extra rows if needed
          and ignoring extra columns.