title: Work with ctrl-c and ctrl-v to copy and paste into a wx.Grid in wxPython
body: <p>I am working on an program with a number of input tables for which I am using wxPython wx.Grid (primarily for Windows). I noticed that ctrl-c and ctrl-v to copy and paste doe snot simply work and I searched for solutions to prevent having to type in all numbers in the tables by hand. I found an old post by Ruben Charles here:
  <a href="http://comments.gmane.org/gmane.comp.python.wxpython/26387" rel="nofollow noreferrer">http://comments.gmane.org/gmane.comp.python.wxpython/26387</a></p>
  
  <p>That seems to do more or less what I wanted, so I started working with that and made some, what I hope are improvements. (I added functionality to 'undo' with ctrl-Z, for working with single cells and for pasting if the last row or column falls outside of the grid table.)</p>
  
  <p>Are there better ways to do this, or might you have advice for improvement?
  Particularly: How to make this work with Python 3.5?</p>
  
  <pre><code>import wx
  import wx.grid
  
  class MyFrame(wx.Frame):
      def __init__(self, parent, ID, title, pos=wx.DefaultPosition, size=wx.Size(800, 400), style=wx.DEFAULT_FRAME_STYLE):
          wx.Frame.__init__(self, parent, ID, title, pos, size, style)
          agrid = MyGrid(self, -1, wx.WANTS_CHARS)
          agrid.CreateGrid(7, 7)
          for count in range(3):
              for count2 in range(3):
                  agrid.SetCellValue(count, count2, str(count + count2))
  
  class MyGrid(wx.grid.Grid):
      """ A Copy&amp;Paste enabled grid class"""
      def __init__(self, parent, id, style):
          wx.grid.Grid.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, style)
          wx.EVT_KEY_DOWN(self, self.OnKey)
          self.data4undo = [0, 0, '']
  
      def OnKey(self, event):
          # If Ctrl+C is pressed...
          if event.ControlDown() and event.GetKeyCode() == 67:
              self.copy()
          # If Ctrl+V is pressed...
          if event.ControlDown() and event.GetKeyCode() == 86:
              self.paste('clip')
          # If Ctrl+Z is pressed...
          if event.ControlDown() and event.GetKeyCode() == 90:
              if self.data4undo[2] != '':
                  self.paste('undo')
          # If del is pressed...
          if event.GetKeyCode() == 127:
              # Call delete method
              self.delete()
          # Skip other Key events
          if event.GetKeyCode():
              event.Skip()
              return
  
      def copy(self):
          # Number of rows and cols
          print self.GetSelectionBlockBottomRight()
          print self.GetGridCursorRow()
          print self.GetGridCursorCol()
          if self.GetSelectionBlockTopLeft() == []:
              rows = 1
              cols = 1
              iscell = True
          else:
              rows = self.GetSelectionBlockBottomRight()[0][0] - self.GetSelectionBlockTopLeft()[0][0] + 1
              cols = self.GetSelectionBlockBottomRight()[0][1] - self.GetSelectionBlockTopLeft()[0][1] + 1
              iscell = False
          # data variable contain text that must be set in the clipboard
          data = ''
          # For each cell in selected range append the cell value in the data variable
          # Tabs '\t' for cols and '\r' for rows
          for r in range(rows):
              for c in range(cols):
                  if iscell:
                      data += str(self.GetCellValue(self.GetGridCursorRow() + r, self.GetGridCursorCol() + c))
                  else:
                      data += str(self.GetCellValue(self.GetSelectionBlockTopLeft()[0][0] + r, self.GetSelectionBlockTopLeft()[0][1] + c))
                  if c &lt; cols - 1:
                      data += '\t'
              data += '\n'
          # Create text data object
          clipboard = wx.TextDataObject()
          # Set data object value
          clipboard.SetText(data)
          # Put the data in the clipboard
          if wx.TheClipboard.Open():
              wx.TheClipboard.SetData(clipboard)
              wx.TheClipboard.Close()
          else:
              wx.MessageBox("Can't open the clipboard", "Error")
  
      def paste(self, stage):
          if stage == 'clip':
              clipboard = wx.TextDataObject()
              if wx.TheClipboard.Open():
                  wx.TheClipboard.GetData(clipboard)
                  wx.TheClipboard.Close()
              else:
                  wx.MessageBox("Can't open the clipboard", "Error")
              data = clipboard.GetText()
              if self.GetSelectionBlockTopLeft() == []:
                  rowstart = self.GetGridCursorRow()
                  colstart = self.GetGridCursorCol()
              else:
                  rowstart = self.GetSelectionBlockTopLeft()[0][0]
                  colstart = self.GetSelectionBlockTopLeft()[0][1]
          elif stage == 'undo':
              data = self.data4undo[2]
              rowstart = self.data4undo[0]
              colstart = self.data4undo[1]
          else:
              wx.MessageBox("Paste method "+stage+" does not exist", "Error")
          text4undo = ''
          # Convert text in a array of lines
          for y, r in enumerate(data.splitlines()):
              # Convert c in a array of text separated by tab
              for x, c in enumerate(r.split('\t')):
                  if y + rowstart &lt; self.NumberRows and x + colstart &lt; self.NumberCols :
                      text4undo += str(self.GetCellValue(rowstart + y, colstart + x)) + '\t'
                      self.SetCellValue(rowstart + y, colstart + x, c)
              text4undo = text4undo[:-1] + '\n'
          if stage == 'clip':
              self.data4undo = [rowstart, colstart, text4undo]
          else:
              self.data4undo = [0, 0, '']
  
      def delete(self):
          # print "Delete method"
          # Number of rows and cols
          if self.GetSelectionBlockTopLeft() == []:
              rows = 1
              cols = 1
          else:
              rows = self.GetSelectionBlockBottomRight()[0][0] - self.GetSelectionBlockTopLeft()[0][0] + 1
              cols = self.GetSelectionBlockBottomRight()[0][1] - self.GetSelectionBlockTopLeft()[0][1] + 1
          # Clear cells contents
          for r in range(rows):
              for c in range(cols):
                  if self.GetSelectionBlockTopLeft() == []:
                      self.SetCellValue(self.GetGridCursorRow() + r, self.GetGridCursorCol() + c, '')
                  else:
                      self.SetCellValue(self.GetSelectionBlockTopLeft()[0][0] + r, self.GetSelectionBlockTopLeft()[0][1] + c, '')
  
  class MyApp(wx.App):
      def OnInit(self):
          frame = MyFrame(None, -1, "Copy and paste enabled only for a single range")
          frame.Show(True)
          self.SetTopWindow(frame)
          return True
  def main():
      app = MyApp()
      app.MainLoop()
  
  if __name__ == '__main__':
      main()
  </code></pre>