Merged the wxPy_newswig branch into the HEAD branch (main trunk)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@24541 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
		@@ -1,8 +1,332 @@
 | 
			
		||||
# sheet.py
 | 
			
		||||
# CSheet - A wxPython spreadsheet class.
 | 
			
		||||
# This is free software.  Feel free to adapt it as you like.
 | 
			
		||||
# Author: Mark F. Russo (russomf@hotmail.com) 2002/01/31
 | 
			
		||||
 | 
			
		||||
"""Renamer stub: provides a way to drop the wx prefix from wxPython objects."""
 | 
			
		||||
from wxPython.wx import *
 | 
			
		||||
from wxPython.grid import *
 | 
			
		||||
import string
 | 
			
		||||
 | 
			
		||||
from wx import _rename
 | 
			
		||||
from wxPython.lib import sheet
 | 
			
		||||
_rename(globals(), sheet.__dict__, modulename='lib.sheet')
 | 
			
		||||
del sheet
 | 
			
		||||
del _rename
 | 
			
		||||
#---------------------------------------------------------------------------
 | 
			
		||||
class CTextCellEditor(wxTextCtrl):
 | 
			
		||||
    """ Custom text control for cell editing """
 | 
			
		||||
    def __init__(self, parent, id, grid):
 | 
			
		||||
        wxTextCtrl.__init__(self, parent, id, "", style=wxNO_BORDER)
 | 
			
		||||
        self._grid = grid                           # Save grid reference
 | 
			
		||||
        EVT_CHAR(self, self.OnChar)
 | 
			
		||||
 | 
			
		||||
    def OnChar(self, evt):                          # Hook OnChar for custom behavior
 | 
			
		||||
        """Customizes char events """
 | 
			
		||||
        key = evt.GetKeyCode()
 | 
			
		||||
        if   key == WXK_DOWN:
 | 
			
		||||
            self._grid.DisableCellEditControl()     # Commit the edit
 | 
			
		||||
            self._grid.MoveCursorDown(False)        # Change the current cell
 | 
			
		||||
        elif key == WXK_UP:
 | 
			
		||||
            self._grid.DisableCellEditControl()     # Commit the edit
 | 
			
		||||
            self._grid.MoveCursorUp(False)          # Change the current cell
 | 
			
		||||
        elif key == WXK_LEFT:
 | 
			
		||||
            self._grid.DisableCellEditControl()     # Commit the edit
 | 
			
		||||
            self._grid.MoveCursorLeft(False)        # Change the current cell
 | 
			
		||||
        elif key == WXK_RIGHT:
 | 
			
		||||
            self._grid.DisableCellEditControl()     # Commit the edit
 | 
			
		||||
            self._grid.MoveCursorRight(False)       # Change the current cell
 | 
			
		||||
 | 
			
		||||
        evt.Skip()                                  # Continue event
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------
 | 
			
		||||
class CCellEditor(wxPyGridCellEditor):
 | 
			
		||||
    """ Custom cell editor """
 | 
			
		||||
    def __init__(self, grid):
 | 
			
		||||
        wxPyGridCellEditor.__init__(self)
 | 
			
		||||
        self._grid = grid                           # Save a reference to the grid
 | 
			
		||||
 | 
			
		||||
    def Create(self, parent, id, evtHandler):
 | 
			
		||||
        """ Create the actual edit control.  Must derive from wxControl.
 | 
			
		||||
            Must Override
 | 
			
		||||
        """
 | 
			
		||||
        self._tc = CTextCellEditor(parent, id, self._grid)
 | 
			
		||||
        self._tc.SetInsertionPoint(0)
 | 
			
		||||
        self.SetControl(self._tc)
 | 
			
		||||
        if evtHandler:
 | 
			
		||||
            self._tc.PushEventHandler(evtHandler)
 | 
			
		||||
 | 
			
		||||
    def SetSize(self, rect):
 | 
			
		||||
        """ Position/size the edit control within the cell rectangle. """
 | 
			
		||||
        # Size text control to exactly overlay in-cell editing
 | 
			
		||||
        self._tc.SetDimensions(rect.x+3, rect.y+3, rect.width-2, rect.height-2)
 | 
			
		||||
 | 
			
		||||
    def Show(self, show, attr):
 | 
			
		||||
        """ Show or hide the edit control.  Use the attr (if not None)
 | 
			
		||||
            to set colors or fonts for the control.
 | 
			
		||||
        """
 | 
			
		||||
        self.base_Show(show, attr)
 | 
			
		||||
 | 
			
		||||
    def PaintBackground(self, rect, attr):
 | 
			
		||||
        """ Draws the part of the cell not occupied by the edit control.  The
 | 
			
		||||
            base class version just fills it with background colour from the
 | 
			
		||||
            attribute.
 | 
			
		||||
        """
 | 
			
		||||
        # Call base class method.
 | 
			
		||||
        self.base_PaintBackground(self, rect, attr)
 | 
			
		||||
 | 
			
		||||
    def BeginEdit(self, row, col, grid):
 | 
			
		||||
        """ Fetch the value from the table and prepare edit control to begin editing.
 | 
			
		||||
            Set the focus to the edit control.  Must Override.
 | 
			
		||||
        """
 | 
			
		||||
        self._startValue = grid.GetTable().GetValue(row, col)
 | 
			
		||||
        self._tc.SetValue(self._startValue)
 | 
			
		||||
        self._tc.SetFocus()
 | 
			
		||||
 | 
			
		||||
        # Select the text when initiating an edit so that subsequent typing
 | 
			
		||||
        # replaces the contents.
 | 
			
		||||
        self._tc.SetSelection(0, self._tc.GetLastPosition())
 | 
			
		||||
 | 
			
		||||
    def EndEdit(self, row, col, grid):
 | 
			
		||||
        """ Commit editing the current cell. Returns True if the value has changed.
 | 
			
		||||
            If necessary, the control may be destroyed. Must Override.
 | 
			
		||||
        """
 | 
			
		||||
        changed = False                             # Assume value not changed
 | 
			
		||||
        val = self._tc.GetValue()                   # Get value in edit control
 | 
			
		||||
        if val != self._startValue:                 # Compare
 | 
			
		||||
            changed = True                          # If different then changed is True
 | 
			
		||||
            grid.GetTable().SetValue(row, col, val) # Update the table
 | 
			
		||||
        self._startValue = ''                       # Clear the class' start value
 | 
			
		||||
        self._tc.SetValue('')                       # Clear contents of the edit control
 | 
			
		||||
 | 
			
		||||
        return changed
 | 
			
		||||
 | 
			
		||||
    def Reset(self):
 | 
			
		||||
        """ Reset the value in the control back to its starting value. Must Override. """
 | 
			
		||||
        self._tc.SetValue(self._startValue)
 | 
			
		||||
        self._tc.SetInsertionPointEnd()
 | 
			
		||||
 | 
			
		||||
    def IsAcceptedKey(self, evt):
 | 
			
		||||
        """ Return True to allow the given key to start editing.  The base class
 | 
			
		||||
            version only checks that the event has no modifiers.  F2 is special
 | 
			
		||||
            and will always start the editor.
 | 
			
		||||
        """
 | 
			
		||||
        return (not (evt.ControlDown() or evt.AltDown())
 | 
			
		||||
                and  evt.GetKeyCode() != WXK_SHIFT)
 | 
			
		||||
 | 
			
		||||
    def StartingKey(self, evt):
 | 
			
		||||
        """ If the editor is enabled by pressing keys on the grid, this will be
 | 
			
		||||
            called to let the editor react to that first key.
 | 
			
		||||
        """
 | 
			
		||||
        key = evt.GetKeyCode()              # Get the key code
 | 
			
		||||
        ch = None                           # Handle num pad keys
 | 
			
		||||
        if key in [WXK_NUMPAD0, WXK_NUMPAD1, WXK_NUMPAD2, WXK_NUMPAD3, WXK_NUMPAD4,
 | 
			
		||||
                   WXK_NUMPAD5, WXK_NUMPAD6, WXK_NUMPAD7, WXK_NUMPAD8, WXK_NUMPAD9]:
 | 
			
		||||
            ch = chr(ord('0') + key - WXK_NUMPAD0)
 | 
			
		||||
 | 
			
		||||
        elif key == WXK_BACK:               # Empty text control when init w/ back key
 | 
			
		||||
            ch = ""
 | 
			
		||||
                                            # Handle normal keys
 | 
			
		||||
        elif key < 256 and key >= 0 and chr(key) in string.printable:
 | 
			
		||||
            ch = chr(key)
 | 
			
		||||
            if not evt.ShiftDown():
 | 
			
		||||
                ch = ch.lower()
 | 
			
		||||
 | 
			
		||||
        if ch is not None:                  # If are at this point with a key,
 | 
			
		||||
            self._tc.SetValue(ch)           # replace the contents of the text control.
 | 
			
		||||
            self._tc.SetInsertionPointEnd() # Move to the end so that subsequent keys are appended
 | 
			
		||||
        else:
 | 
			
		||||
            evt.Skip()
 | 
			
		||||
 | 
			
		||||
    def StartingClick(self):
 | 
			
		||||
        """ If the editor is enabled by clicking on the cell, this method will be
 | 
			
		||||
            called to allow the editor to simulate the click on the control.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def Destroy(self):
 | 
			
		||||
        """ Final cleanup """
 | 
			
		||||
        self.base_Destroy()
 | 
			
		||||
 | 
			
		||||
    def Clone(self):
 | 
			
		||||
        """ Create a new object which is the copy of this one. Must Override. """
 | 
			
		||||
        return CCellEditor()
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------
 | 
			
		||||
class CSheet(wxGrid):
 | 
			
		||||
    def __init__(self, parent):
 | 
			
		||||
        wxGrid.__init__(self, parent, -1)
 | 
			
		||||
 | 
			
		||||
        # Init variables
 | 
			
		||||
        self._lastCol = -1              # Init last cell column clicked
 | 
			
		||||
        self._lastRow = -1              # Init last cell row clicked
 | 
			
		||||
        self._selected = None           # Init range currently selected
 | 
			
		||||
                                        # Map string datatype to default renderer/editor
 | 
			
		||||
        self.RegisterDataType(wxGRID_VALUE_STRING,
 | 
			
		||||
                              wxGridCellStringRenderer(),
 | 
			
		||||
                              CCellEditor(self))
 | 
			
		||||
 | 
			
		||||
        self.CreateGrid(4, 3)           # By default start with a 4 x 3 grid
 | 
			
		||||
        self.SetColLabelSize(18)        # Default sizes and alignment
 | 
			
		||||
        self.SetRowLabelSize(50)
 | 
			
		||||
        self.SetRowLabelAlignment(wxALIGN_RIGHT, wxALIGN_BOTTOM)
 | 
			
		||||
        self.SetColSize(0, 75)          # Default column sizes
 | 
			
		||||
        self.SetColSize(1, 75)
 | 
			
		||||
        self.SetColSize(2, 75)
 | 
			
		||||
 | 
			
		||||
        # Sink events
 | 
			
		||||
        EVT_GRID_CELL_LEFT_CLICK(  self, self.OnLeftClick)
 | 
			
		||||
        EVT_GRID_CELL_RIGHT_CLICK( self, self.OnRightClick)
 | 
			
		||||
        EVT_GRID_CELL_LEFT_DCLICK( self, self.OnLeftDoubleClick)
 | 
			
		||||
        EVT_GRID_RANGE_SELECT(     self, self.OnRangeSelect)
 | 
			
		||||
        EVT_GRID_ROW_SIZE(         self, self.OnRowSize)
 | 
			
		||||
        EVT_GRID_COL_SIZE(         self, self.OnColSize)
 | 
			
		||||
        EVT_GRID_CELL_CHANGE(      self, self.OnCellChange)
 | 
			
		||||
        EVT_GRID_SELECT_CELL(      self, self.OnGridSelectCell)
 | 
			
		||||
 | 
			
		||||
    def OnGridSelectCell(self, event):
 | 
			
		||||
        """ Track cell selections """
 | 
			
		||||
        # Save the last cell coordinates
 | 
			
		||||
        self._lastRow, self._lastCol = event.GetRow(), event.GetCol()
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnRowSize(self, event):
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnColSize(self, event):
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnCellChange(self, event):
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnLeftClick(self, event):
 | 
			
		||||
        """ Override left-click behavior to prevent left-click edit initiation """
 | 
			
		||||
        # Save the cell clicked
 | 
			
		||||
        currCell = (event.GetRow(), event.GetCol())
 | 
			
		||||
 | 
			
		||||
        # Suppress event if same cell clicked twice in a row.
 | 
			
		||||
        # This prevents a single-click from initiating an edit.
 | 
			
		||||
        if currCell != (self._lastRow, self._lastCol): event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnRightClick(self, event):
 | 
			
		||||
        """ Move grid cursor when a cell is right-clicked """
 | 
			
		||||
        self.SetGridCursor( event.GetRow(), event.GetCol() )
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnLeftDoubleClick(self, event):
 | 
			
		||||
        """ Initiate the cell editor on a double-click """
 | 
			
		||||
        # Move grid cursor to double-clicked cell
 | 
			
		||||
        if self.CanEnableCellControl():
 | 
			
		||||
            self.SetGridCursor( event.GetRow(), event.GetCol() )
 | 
			
		||||
            self.EnableCellEditControl(True)    # Show the cell editor
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def OnRangeSelect(self, event):
 | 
			
		||||
        """ Track which cells are selected so that copy/paste behavior can be implemented """
 | 
			
		||||
        # If a single cell is selected, then Selecting() returns False (0)
 | 
			
		||||
        # and range coords are entire grid.  In this case cancel previous selection.
 | 
			
		||||
        # If more than one cell is selected, then Selecting() is True (1)
 | 
			
		||||
        # and range accurately reflects selected cells.  Save them.
 | 
			
		||||
        # If more cells are added to a selection, selecting remains True (1)
 | 
			
		||||
        self._selected = None
 | 
			
		||||
        if event.Selecting():
 | 
			
		||||
            self._selected = ((event.GetTopRow(), event.GetLeftCol()),
 | 
			
		||||
                              (event.GetBottomRow(), event.GetRightCol()))
 | 
			
		||||
        event.Skip()
 | 
			
		||||
 | 
			
		||||
    def Copy(self):
 | 
			
		||||
        """ Copy the currently selected cells to the clipboard """
 | 
			
		||||
        # TODO: raise an error when there are no cells selected?
 | 
			
		||||
        if self._selected == None: return
 | 
			
		||||
        ((r1, c1), (r2, c2)) = self._selected
 | 
			
		||||
 | 
			
		||||
        # Build a string to put on the clipboard
 | 
			
		||||
        # (Is there a faster way to do this in Python?)
 | 
			
		||||
        crlf = chr(13) + chr(10)
 | 
			
		||||
        tab = chr(9)
 | 
			
		||||
        s = ""
 | 
			
		||||
        for row in range(r1, r2+1):
 | 
			
		||||
            for col in range(c1, c2):
 | 
			
		||||
                s += self.GetCellValue(row,col)
 | 
			
		||||
                s += tab
 | 
			
		||||
            s += self.GetCellValue(row, c2)
 | 
			
		||||
            s += crlf
 | 
			
		||||
 | 
			
		||||
        # Put the string on the clipboard
 | 
			
		||||
        if wxTheClipboard.Open():
 | 
			
		||||
            wxTheClipboard.Clear()
 | 
			
		||||
            wxTheClipboard.SetData(wxTextDataObject(s))
 | 
			
		||||
            wxTheClipboard.Close()
 | 
			
		||||
 | 
			
		||||
    def Paste(self):
 | 
			
		||||
        """ Paste the contents of the clipboard into the currently selected cells """
 | 
			
		||||
        # (Is there a better way to do this?)
 | 
			
		||||
        if wxTheClipboard.Open():
 | 
			
		||||
            td = wxTextDataObject()
 | 
			
		||||
            success = wxTheClipboard.GetData(td)
 | 
			
		||||
            wxTheClipboard.Close()
 | 
			
		||||
            if not success: return              # Exit on failure
 | 
			
		||||
            s = td.GetText()                    # Get the text
 | 
			
		||||
 | 
			
		||||
            crlf = chr(13) + chr(10)            # CrLf characters
 | 
			
		||||
            tab = chr(9)                        # Tab character
 | 
			
		||||
 | 
			
		||||
            rows = s.split(crlf)               # split into rows
 | 
			
		||||
            rows = rows[0:-1]                   # leave out last element, which is always empty
 | 
			
		||||
            for i in range(0, len(rows)):       # split rows into elements
 | 
			
		||||
                rows[i] = rows[i].split(tab)
 | 
			
		||||
 | 
			
		||||
            # Get the starting and ending cell range to paste into
 | 
			
		||||
            if self._selected == None:          # If no cells selected...
 | 
			
		||||
                r1 = self.GetGridCursorRow()    # Start the paste at the current location
 | 
			
		||||
                c1 = self.GetGridCursorCol()
 | 
			
		||||
                r2 = self.GetNumberRows()-1     # Go to maximum row and col extents
 | 
			
		||||
                c2 = self.GetNumberCols()-1
 | 
			
		||||
            else:                               # If cells selected, only paste there
 | 
			
		||||
                ((r1, c1), (r2, c2)) = self._selected
 | 
			
		||||
 | 
			
		||||
            # Enter data into spreadsheet cells one at a time
 | 
			
		||||
            r = r1                              # Init row and column counters
 | 
			
		||||
            c = c1
 | 
			
		||||
            for row in rows:                    # Loop over all rows
 | 
			
		||||
                for element in row:             # Loop over all row elements
 | 
			
		||||
                    self.SetCellValue(r, c, str(element))   # Set cell value
 | 
			
		||||
                    c += 1                      # Increment the column counter
 | 
			
		||||
                    if c > c2: break            # Do not exceed maximum column
 | 
			
		||||
                r += 1
 | 
			
		||||
                if r > r2: break                # Do not exceed maximum row
 | 
			
		||||
                c = c1
 | 
			
		||||
 | 
			
		||||
    def Clear(self):
 | 
			
		||||
        """ Clear the currently selected cells """
 | 
			
		||||
        if self._selected == None:              # If no selection...
 | 
			
		||||
            r = self.GetGridCursorRow()         # clear only current cell
 | 
			
		||||
            c = self.GetGridCursorCol()
 | 
			
		||||
            self.SetCellValue(r, c, "")
 | 
			
		||||
        else:                                   # Otherwise clear selected cells
 | 
			
		||||
            ((r1, c1), (r2, c2)) = self._selected
 | 
			
		||||
            for r in range(r1, r2+1):
 | 
			
		||||
                for c in range(c1, c2+1):
 | 
			
		||||
                    self.SetCellValue(r, c, "")
 | 
			
		||||
 | 
			
		||||
    def SetNumberRows(self, numRows=1):
 | 
			
		||||
        """ Set the number of rows in the sheet """
 | 
			
		||||
        # Check for non-negative number
 | 
			
		||||
        if numRows < 0:  return False
 | 
			
		||||
 | 
			
		||||
        # Adjust number of rows
 | 
			
		||||
        curRows = self.GetNumberRows()
 | 
			
		||||
        if curRows < numRows:
 | 
			
		||||
            self.AppendRows(numRows - curRows)
 | 
			
		||||
        elif curRows > numRows:
 | 
			
		||||
            self.DeleteRows(numRows, curRows - numRows)
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def SetNumberCols(self, numCols=1):
 | 
			
		||||
        """ Set the number of columns in the sheet """
 | 
			
		||||
        # Check for non-negative number
 | 
			
		||||
        if numCols < 0:  return False
 | 
			
		||||
 | 
			
		||||
        # Adjust number of rows
 | 
			
		||||
        curCols = self.GetNumberCols()
 | 
			
		||||
        if curCols < numCols:
 | 
			
		||||
            self.AppendCols(numCols - curCols)
 | 
			
		||||
        elif curCols > numCols:
 | 
			
		||||
            self.DeleteCols(numCols, curCols - numCols)
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user