PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/views/wxspreadsheet.py

https://bitbucket.org/ccoughlin/nditoolbox
Python | 208 lines | 205 code | 0 blank | 3 comment | 1 complexity | b2dd49eaa48a38e89e4b6ae585446da2 MD5 | raw file
  1. """
  2. wxspreadsheet.py: single-file extension of wxPython's CSheet (grid) with basic spreadsheet
  3. functionality. Original code: https://bitbucket.org/ccoughlin/wxspreadsheet/overview
  4. """
  5. __author__ = 'Chris R. Coughlin'
  6. import wx.lib.sheet
  7. import wx.grid
  8. import csv
  9. class ContextMenu(wx.Menu):
  10. """Basic right-click popup menu for CSheet controls. Currently
  11. implements copy-paste selected cell(s), insert row / column, delete
  12. row / column."""
  13. def __init__(self, parent):
  14. wx.Menu.__init__(self)
  15. self.parent = parent
  16. insertrow = wx.MenuItem(self, wx.NewId(), 'Insert Row(s)')
  17. self.AppendItem(insertrow)
  18. self.Bind(wx.EVT_MENU, self.OnInsertRow, id=insertrow.GetId())
  19. deleterow = wx.MenuItem(self, wx.NewId(), 'Delete Row(s)')
  20. self.AppendItem(deleterow)
  21. self.Bind(wx.EVT_MENU, self.OnDeleteRow, id=deleterow.GetId())
  22. insertcol = wx.MenuItem(self, wx.NewId(), 'Insert Column(s)')
  23. self.AppendItem(insertcol)
  24. self.Bind(wx.EVT_MENU, self.OnInsertCol, id=insertcol.GetId())
  25. deletecol = wx.MenuItem(self, wx.NewId(), 'Delete Column(s)')
  26. self.AppendItem(deletecol)
  27. self.Bind(wx.EVT_MENU, self.OnDeleteCol, id=deletecol.GetId())
  28. self.AppendSeparator()
  29. copy = wx.MenuItem(self, wx.NewId(), 'Copy')
  30. self.AppendItem(copy)
  31. self.Bind(wx.EVT_MENU, self.OnCopy, id=copy.GetId())
  32. paste = wx.MenuItem(self, wx.NewId(), 'Paste')
  33. self.AppendItem(paste)
  34. self.Bind(wx.EVT_MENU, self.OnPaste, id=paste.GetId())
  35. clear = wx.MenuItem(self, wx.NewId(), 'Clear Selected Cells')
  36. self.AppendItem(clear)
  37. self.Bind(wx.EVT_MENU, self.OnClear, id=clear.GetId())
  38. def OnInsertRow(self, event):
  39. """Basic "Insert Row(s) Here" function"""
  40. self.parent.SelectRow(self._getRow())
  41. self.parent.InsertRows(self._getRow(), self._getSelectionRowSize())
  42. def OnDeleteRow(self, event):
  43. """Basic "Delete Row(s)" function"""
  44. self.parent.DeleteRows(self._getRow(), self._getSelectionRowSize())
  45. def OnInsertCol(self, event):
  46. """Basic "Insert Column(s) Here" function"""
  47. self.parent.InsertCols(self._getCol(), self._getSelectionColSize())
  48. def OnDeleteCol(self, event):
  49. """Basic "Delete Column(s)" function"""
  50. self.parent.DeleteCols(self._getCol(), self._getSelectionColSize())
  51. def OnClear(self, event):
  52. """Erases the contents of the currently selected cell(s)."""
  53. self.parent.Clear()
  54. def OnCopy(self, event):
  55. """Copies the contents of the currently selected cell(s)
  56. to the clipboard."""
  57. self.parent.Copy()
  58. def OnPaste(self, event):
  59. """Pastes the clipboard's contents to the currently
  60. selected cell(s)."""
  61. self.parent.Paste()
  62. def _getRow(self):
  63. """Returns the first (top) row in the selected row(s) if any,
  64. otherwise returns the row of the current cursor position."""
  65. selected_row = self.parent.GetSelectedRows()
  66. if selected_row:
  67. return selected_row[0]
  68. else:
  69. return self.parent.GetGridCursorRow()
  70. def _getCol(self):
  71. """Returns the first (left) row in the selected column(s) if any,
  72. otherwise returns the column of the current cursor position."""
  73. selected_col = self.parent.GetSelectedCols()
  74. if selected_col:
  75. return selected_col[0]
  76. else:
  77. return self.parent.GetGridCursorCol()
  78. def _getSelectionRowSize(self):
  79. """Returns the number of selected rows, number of rows in the
  80. current selection, or 1 in order of preference."""
  81. numrows = 1
  82. if self.parent.GetSelectionBlockTopLeft():
  83. numrows = self.parent.GetSelectionBlockBottomRight()[0][0] -\
  84. self.parent.GetSelectionBlockTopLeft()[0][0] + 1
  85. else:
  86. numrows = len(self.parent.GetSelectedRows())
  87. return numrows
  88. def _getSelectionColSize(self):
  89. """Returns the number of selected columns, number of columns in the
  90. current selection, or 1 in order of preference."""
  91. numcols = 1
  92. if self.parent.GetSelectionBlockTopLeft():
  93. numcols = self.parent.GetSelectionBlockBottomRight()[0][1] -\
  94. self.parent.GetSelectionBlockTopLeft()[0][1] + 1
  95. else:
  96. numcols = len(self.parent.GetSelectedCols())
  97. return numcols
  98. class SpreadsheetTextCellEditor(wx.TextCtrl):
  99. """ Custom text control for cell editing """
  100. def __init__(self, parent, id, grid):
  101. wx.TextCtrl.__init__(self, parent, id, "",
  102. style=wx.NO_BORDER | wx.TE_PROCESS_ENTER)
  103. self._grid = grid # Save grid reference
  104. self.Bind(wx.EVT_CHAR, self.OnChar)
  105. def OnChar(self, evt): # Hook OnChar for custom behavior
  106. """Customizes char events """
  107. key = evt.GetKeyCode()
  108. if key == wx.WXK_DOWN or key == wx.WXK_RETURN:
  109. self._grid.DisableCellEditControl() # Commit the edit
  110. self._grid.MoveCursorDown(False) # Change the current cell
  111. elif key == wx.WXK_UP:
  112. self._grid.DisableCellEditControl() # Commit the edit
  113. self._grid.MoveCursorUp(False) # Change the current cell
  114. elif key == wx.WXK_LEFT:
  115. self._grid.DisableCellEditControl() # Commit the edit
  116. self._grid.MoveCursorLeft(False) # Change the current cell
  117. elif key == wx.WXK_RIGHT:
  118. self._grid.DisableCellEditControl() # Commit the edit
  119. self._grid.MoveCursorRight(False) # Change the current cell
  120. evt.Skip() # Continue event
  121. class SpreadsheetCellEditor(wx.lib.sheet.CCellEditor):
  122. """ Custom cell editor """
  123. def __init__(self, grid):
  124. super(SpreadsheetCellEditor, self).__init__(grid)
  125. def Create(self, parent, id, evtHandler):
  126. """ Create the actual edit control. Must derive from wxControl.
  127. Must Override
  128. """
  129. self._tc = SpreadsheetTextCellEditor(parent, id, self._grid)
  130. self._tc.SetInsertionPoint(0)
  131. self.SetControl(self._tc)
  132. if evtHandler:
  133. self._tc.PushEventHandler(evtHandler)
  134. class Spreadsheet(wx.lib.sheet.CSheet):
  135. """Child class of CSheet (child of wxGrid) that implements a basic
  136. right-click popup menu."""
  137. def __init__(self, parent):
  138. self.parent = parent
  139. super(Spreadsheet, self).__init__(self.parent)
  140. def OnRightClick(self, event):
  141. """Defines the right click popup menu for the spreadsheet"""
  142. # Move the cursor to the cell clicked
  143. self.SetGridCursor(event.GetRow(), event.GetCol())
  144. self.PopupMenu(ContextMenu(self), event.GetPosition())
  145. def ReadCSV(self, csvfile, _delimiter=',', _quotechar='#'):
  146. """Reads a CSV file into the current spreadsheet, replacing
  147. existing contents (if any)."""
  148. self.ClearGrid()
  149. with open(csvfile, 'rU') as inputfile:
  150. csv_reader = csv.reader(inputfile,
  151. delimiter=_delimiter, quotechar=_quotechar)
  152. try:
  153. self.SetNumberRows(0)
  154. self.SetNumberCols(0)
  155. rownum = 0
  156. for row in csv_reader:
  157. self.AppendRows(1)
  158. numcols = len(row)
  159. if self.GetNumberCols() < numcols:
  160. self.SetNumberCols(numcols)
  161. colnum = 0
  162. for cell in row:
  163. self.SetCellValue(rownum, colnum, str(cell))
  164. colnum += 1
  165. rownum += 1
  166. except csv.Error as err:
  167. print("Skipping line {0}: {1}".format(csv_reader.line_num, err))
  168. def WriteCSV(self, csvfile, _delimiter=',', _quotechar='#', _quoting=csv.QUOTE_MINIMAL):
  169. """Writes the current contents of the spreadsheet to a CSV file"""
  170. csv_writer = csv.writer(open(csvfile, 'wb'), delimiter=_delimiter, quotechar=_quotechar,
  171. quoting=_quoting)
  172. for rownum in range(self.GetNumberRows()):
  173. rowdata = []
  174. for colnum in range(self.GetNumberCols()):
  175. rowdata.append(self.GetCellValue(rownum, colnum))
  176. csv_writer.writerow(rowdata)