wxPyGridCellRenderer
The cell renderer allows you to customize the display of a particular data type within the grid. For instance, it allows you to display a "colour" data type as a swatch of colour, rather than as a mere plain-text label. The class has a very simple API, using a standard wxDC for drawing into the cell's rectangle. A secondary function of the renderer is the determination of the "best size" for the cell.
Note: under wxPython 2.3.2.1 wxPyGridCellRenderer's Draw method never got called. As a result, any attempt to customise drawing under that version of wxPython will just silently fail. The problem is fixed with wxPython 2.3.3pre2 and beyond.
void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool isSelected) -- Draw the given cell on the provided DC inside the given rectangle using the style specified by the attribute and the default or selected state corresponding to the isSelected value. Keep in mind that this method will be called once for every instance of the data type, which may result in hundreds of calls for every display update of the grid. Care should be taken to make this a very fast method.
grid -- pointer to your wxGrid instance, you can use this to retrieve current data values from the associated table. In wxPython 2.3.3 (at least), this really is your Python instance object, so you can get a pointer to your wxPyGridTableBase from it.
attr -- wxGridCellAttr object, provides information about the rendering properties to be used for the particular cell
dc -- wxDC instance for use in drawing. You can use the passed dc for drawing (you don't need to create a wxPaintDC, the dc is already a painting dc)
rect -- wxRect object in window coordinates telling you where to draw your data
row, col -- wxGrid row and column being edited. Combined with the grid, allows you to determine the current cell value
- isSelected -- if true, the cell is currently in a selection set. Note that you should be using different background and foreground colours in this case.
The default implementation will draw the rectangle with the background colour from attr and set the text colour and font. Not sure what it does if the attribute values are null, or whether any attribute showing up here must be fully specified (in testing, the attribute seems to have all the values available, but I'm not sure if that's guaranteed). The attribute doesn't change the value of attr.GetBackgroundColour() to reflect the selected status, so you'll need to figure out the background colour in those cases regardless (see note below). I'm guessing the default implementation just reverses the text and background colour roles. Note: This method is used for drawing both selected and unselected cells. To make your renderer consistent with the built-in renderers, you will need to use the colours and fonts used by the grid. As far as I can see, these are the normal default values:
font -- wxNORMAL_FONT (I'm guessing that grid.GetFont() would be the real source?)
unselected text -- wxSystemSettings_GetColour( wxSYS_COLOUR_WINDOWTEXT )
unselected background -- wxSystemSettings_GetColour( wxSYS_COLOUR_WINDOW )
selected text -- wxSystemSettings_GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT )
selected background -- wxSystemSettings_GetColour( wxSYS_COLOUR_HIGHLIGHT )
wxSize GetBestSize(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row, int col) -- Get the preferred size of the cell for its contents. Note: this size determines both the viewing and editing size of the cell, so care should be taken to allow enough space for the editing controls for the data type.
grid -- pointer to your wxGrid instance, you can use this to retrieve current data values from the associated table. In wxPython 2.3.3 (at least), this really is your Python instance object, so you can get a pointer to your wxPyGridTableBase from it.
attr -- wxGridCellAttr object, provides information about the rendering properties to be used for the particular cell
dc -- wxDC instance for use in drawing. You can use the passed dc for drawing (you don't need to create a wxPaintDC, the dc is already a painting dc)
row, col -- wxGrid row and column being edited. Combined with the grid, allows you to determine the current cell value
Note: a bug in wxPython 2.3.2.1 (and earlier?) required that you return an explicit wxSize object, rather than a two-tuple of (width,height). The two-tuple didn't cause an exception, the value was just silently ignored.
Clone() -- Create a duplicate renderer, normally you can use return self.__class__().
Example of use
from wxPython.wx import * from wxPython.grid import * from wxprop import basetableworker class BaseViewer( basetableworker.BaseTableWorker, wxPyGridCellRenderer): """Base class for editors""" BACKGROUND_SELECTED = wxSystemSettings_GetColour( wxSYS_COLOUR_HIGHLIGHT ) TEXT_SELECTED = wxSystemSettings_GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) BACKGROUND = wxSystemSettings_GetColour( wxSYS_COLOUR_WINDOW ) TEXT = wxSystemSettings_GetColour( wxSYS_COLOUR_WINDOWTEXT ) ### Customisation points def GetValueAsText( self, row, col ): """Customisation Point: Retrieve the current value for row,col as a text string""" return str( self.GetCurrentTableValue( row, col ) ) def Draw(self, grid, attr, dc, rect, row, col, isSelected): """Customisation Point: Draw the data from grid in the rectangle with attributes using the dc""" self.Clip(dc, rect) self.Background( grid, attr, dc, rect, row, col, isSelected) try: value = self.GetValueAsText( row, col ) dc.SetFont( wxNORMAL_FONT ) return self.SimpleText( value, attr, dc, rect, isSelected) finally: self.Unclip(dc) def GetBestSize(self, grid, attr, dc, row, col): """Customisation Point: Determine the appropriate (best) size for the control, return as wxSize Note: You _must_ return a wxSize object. Returning a two-value-tuple won't raise an error, but the value won't be respected by wxPython. """ x,y = dc.GetTextExtent( self.GetValueAsText( row, col ) ) # note that the two-tuple returned by GetTextExtent won't work, # need to give a wxSize object back! return wxSize( x,y ) ### Utility methods def SimpleText( self, value, attr, dc, rect, isSelected): """Draw a simple text label in appropriate colours with background Uses the system settings at application load to draw a rectangle (rect) in either system window background or system selected window background. Then draws the string "value" using either selected text or normal text colours. """ previousText = dc.GetTextForeground() if isSelected: dc.SetTextForeground( self.TEXT_SELECTED ) else: dc.SetTextForeground( self.TEXT ) try: dc.DrawText( value, rect.x+2,rect.y+2 ) finally: dc.SetTextForeground( previousText ) def Background( self, grid, attr, dc, rect, row, col, isSelected): """Draw an appropriate background based on selection state""" if isSelected: dc.SetBrush( wxBrush( self.BACKGROUND_SELECTED, wxSOLID) ) else: dc.SetBrush( wxBrush( self.BACKGROUND, wxSOLID) ) try: dc.SetPen(wxTRANSPARENT_PEN) dc.DrawRectangle( rect.x, rect.y, rect.width, rect.height ) finally: dc.SetPen( wxNullPen ) dc.SetBrush( wxNullBrush ) def Clip( self, dc, rect ): """Setup the clipping rectangle""" dc.SetClippingRegion( rect.x, rect.y, rect.width, rect.height ) def Unclip( self, dc ): """Destroy the clipping rectangle""" dc.DestroyClippingRegion( )
Back to the wxGrid Manual
Can anyone add some more code to the above example? I mean, it's nice to see the code of a proper renderer, but what I'm missing is the code to "attach" this renderer to a wxGrid object. A renderer on its own won't do much, will it?
-- Sybren Stüvel
There are examples in the wxPython demo's, download from http://www.wxpython.org/download.php
-- Martijn