[wxPython-users] Re: wx.Grid.SetDefaultRenderer doesn't seem
to be working
Grzegorz Adam Hankiewicz
ghankiewicz at rastertech.es
Sat Jan 27 22:53:33 PST 2007
Christian Kristukat wrote:
>> So far I've copied the grid examples from the demo where the cell =
>> renderer is set for individual cells. Since I actually have one
>> type of cell, I loop over all of them setting it. It is however
>> annoying when I try to add new rows, because I have to go again
>> setting their cell renderer.
> =
> If you are using a custom TableBase you can override its GetAttr
> method and and let it return a GridCellAttr with a renderer of your
> choice, e.g.:
> =
> def GetAttr(self, *args): attr =3D wx.grid.GridCellAttr() =
> attr.SetReadOnly() attr.SetRenderer(FloatRenderer()) return attr
Yes, this works.
> I have not tried it but according to what the the docs say I would
> expect it to work. Can you send your code?
Here it is. If you uncomment the lines which set the renderer explicitly
it will work as expected. I'm using 2.8.0.1 under MSW. I wonder if the =
memory leak fixed in 2.8.1.0 (patch 1629949) makes SetDefaultRenderer() =
work.
-- =
Grzegorz Adam Hankiewicz, Jefe de producto de TeraVial
Rastertech Espa=F1a S.A. Tel: +34 918 467 390, ext 18.
http://www.rastertech.es/ ghankiewicz at rastertech.es
-------------- next part --------------
#!/usr/bin/env python
# vim: tabstop=3D4 shiftwidth=3D4
import random
import sys
import wx
import wx.grid
NUM_DATA =3D 200
ROW_SIZE =3D 30
MAGIC_LIST_HEIGHT =3D 5
class Scroll_binder():
"""Inherit to be able to bind vscrolling to another widget."""
def __init__(self):
"""f() -> Scroll_binder
Initialises the internal data required for vertical scrolling.
"""
self._locked =3D False
self._bound_widget =3D None
self._is_list_control =3D hasattr(self, "ScrollList")
self.Bind(wx.EVT_SCROLLWIN, self._did_scroll)
self.Bind(wx.EVT_MOUSEWHEEL, self._mousewheel)
self.Bind(wx.EVT_SCROLLWIN_LINEUP, self._lineup)
self.Bind(wx.EVT_SCROLLWIN_LINEDOWN, self._linedown)
self.Bind(wx.EVT_SCROLLWIN_PAGEUP, self._pageup)
self.Bind(wx.EVT_SCROLLWIN_PAGEDOWN, self._pagedown)
self.Bind(wx.EVT_SCROLLWIN_TOP, self._top)
self.Bind(wx.EVT_SCROLLWIN_BOTTOM, self._bottom)
self.Bind(wx.EVT_KEY_DOWN, self._key_down)
def bind_scroll(self, to):
self._bound_widget =3D to
def _key_down(self, event):
pass
def _mousewheel(self, event):
"""Mouse wheel scrolled. Up or down, give or take."""
if event.m_wheelRotation > 0:
do_scroll =3D self._lineup
else:
do_scroll =3D self._linedown
for r in range(event.m_linesPerAction):
do_scroll()
def _pageup(self, event):
"""Clicked on a scrollbar space, performing a page up."""
if event.GetOrientation() !=3D wx.VERTICAL:
event.Skip()
return
pos =3D self.GetScrollPos(wx.VERTICAL)
if self._is_list_control:
amount =3D self.GetCountPerPage()
else:
amount =3D self.GetScrollPageSize(wx.VERTICAL)
print "PageUp %d from %d" % (amount, pos)
self._bound_widget.scroll_to(max(0, pos - amount))
self.scroll_to(max(0, pos - amount))
def _pagedown(self, event):
"""Clicked on a scrollbar space, performing a page down."""
if event.GetOrientation() !=3D wx.VERTICAL:
event.Skip()
return
pos =3D self.GetScrollPos(wx.VERTICAL)
if self._is_list_control:
amount =3D self.GetCountPerPage()
else:
amount =3D self.GetScrollPageSize(wx.VERTICAL)
print "PageDown %d from %d" % (amount, pos)
self._bound_widget.scroll_to(pos + amount)
self.scroll_to(pos + amount)
def _top(self, event):
"""Event handler for going to the top."""
if event.GetOrientation() !=3D wx.VERTICAL:
event.Skip()
return
print "Top"
self._bound_widget.scroll_to(0)
self.scroll_to(0)
def _bottom(self, event):
"""Event handler for going to the bottom."""
if event.GetOrientation() !=3D wx.VERTICAL:
event.Skip()
return
print "Bottom"
pos =3D 10000000
self._bound_widget.scroll_to(pos)
self.scroll_to(pos)
def _lineup(self, event =3D None):
"""Event handler for pressing the up arrow."""
if event and event.GetOrientation() !=3D wx.VERTICAL:
event.Skip()
return
pos =3D self.GetScrollPos(wx.VERTICAL)
print "LineUp from", pos
self._bound_widget.scroll_to(pos - 1)
self.scroll_to(pos - 1)
def _linedown(self, event =3D None):
"""Event handler for pressing the down arrow."""
if event and event.GetOrientation() !=3D wx.VERTICAL:
event.Skip()
return
pos =3D self.GetScrollPos(wx.VERTICAL)
print "LineDown from", pos
self._bound_widget.scroll_to(pos + 1)
self.scroll_to(pos + 1)
def _did_scroll(self, event):
"""Event handler for manual scrolling."""
try:
if event.GetOrientation() !=3D wx.VERTICAL or self._locked:
return
print "list pos was %d, will be %d" % (
self.GetScrollPos(wx.VERTICAL), event.GetPosition())
self._bound_widget.scroll_to(event.GetPosition())
finally:
event.Skip()
def scroll_to(self, position):
"""f(int) -> None
Scrolls to a specific vertical position.
"""
self._locked =3D True
if self._is_list_control:
dif =3D position - self.GetScrollPos(wx.VERTICAL)
self.ScrollList(-1, dif * ROW_SIZE)
else:
# Presume we are a grid.
self.Scroll(-1, position)
self._locked =3D False
class StupidRenderer(wx.grid.PyGridCellRenderer):
def __init__(self):
wx.grid.PyGridCellRenderer.__init__(self)
def Draw(self, grid, attr, dc, rect, row, col, isSelected):
if isSelected:
dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))
else:
dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
dc.SetPen(wx.TRANSPARENT_PEN)
dc.DrawRectangleRect(rect)
dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
x =3D rect.x + 2
y =3D rect.y + 2
dc.DrawRectangleRect((x, y, 5, 5))
def Clone(self):
print "Are we being called?"
return MyCustomRenderer()
class CustomTableGrid(wx.grid.Grid, Scroll_binder):
def __init__(self, parent):
wx.grid.Grid.__init__(self, parent)
Scroll_binder.__init__(self)
# This should be setting the renderer... but it doesn't do anything.
self.SetDefaultRenderer(StupidRenderer())
self.table =3D CustomDataTable()
# The second parameter means that the grid is to take ownership of the
# table and will destroy it when done. Otherwise you would need to
# keep a reference to it and call it's Destroy method later.
self.SetTable(self.table, True)
#self.SetRowLabelSize(0)
self.SetMargins(0, 0)
self.AutoSizeColumns(True)
# Disallow rows stretching vertically and set a fixed height.
self.DisableDragRowSize()
self.SetRowMinimalAcceptableHeight(ROW_SIZE)
self.SetDefaultRowSize(ROW_SIZE, True)
self.SetScrollRate(self.GetScrollLineX(), ROW_SIZE)
# Don't let the user change the values.
self.EnableEditing(False)
# Uncomment these lines to have the renderer working.
# Putting here the call to SetDefaultRenderer doesn't help.
#for row, row_data in zip(range(len(self.table.data)), self.table.data):
#for col, dummy in zip(range(len(row_data)), row_data):
#print "Setting for row %d, col %d" % (row, col)
#self.SetCellRenderer(row, col, StupidRenderer())
class CustomDataTable(wx.grid.PyGridTableBase):
def __init__(self):
wx.grid.PyGridTableBase.__init__(self)
self.colLabels =3D ['January', 'February', "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December"]
self.dataTypes =3D [wx.grid.GRID_VALUE_FLOAT] * 12
self.data =3D []
for row in range(NUM_DATA):
self.data.append([0] * 12)
for f in range(12):
self.data[row][f] =3D (random.random(), random.random())
# This works if you want to have a default renderer.
#def GetAttr(self, *args):
#attr =3D wx.grid.GridCellAttr()
#attr.SetReadOnly()
#attr.SetRenderer(StupidRenderer())
#return attr
# Required methods for the wxPyGridTableBase interface
def GetNumberRows(self):
return len(self.data)
def GetNumberCols(self):
return len(self.data[0])
def IsEmptyCell(self, row, col):
try:
return not self.data[row][col]
except IndexError:
return True
# Get/Set values in the table. The Python version of these
# methods can handle any data-type, (as long as the Editor and
# Renderer understands the type too,) not just strings as in the
# C++ version.
def GetValue(self, row, col):
try:
return self.data[row][col]
except IndexError:
return ''
def SetValue(self, row, col, value):
try:
self.data[row][col] =3D value
except IndexError:
# add a new row
self.data.append([''] * self.GetNumberCols())
self.SetValue(row, col, value)
# tell the grid we've added a row
msg =3D wx.grid.GridTableMessage(self, # The table
wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, # what we did to it
1 # how many
)
self.GetView().ProcessTableMessage(msg)
#--------------------------------------------------
# Some optional methods
# Called when the grid needs to display labels
def GetColLabelValue(self, col):
return self.colLabels[col]
# Called to determine the kind of editor/renderer to use by
# default, doesn't necessarily have to be the same type used
# natively by the editor/renderer if they know how to convert.
def GetTypeName(self, row, col):
return self.dataTypes[col]
# Called to determine how the data can be fetched and stored by the
# editor and renderer. This allows you to enforce some type-safety
# in the grid.
def CanGetValueAs(self, row, col, typeName):
colType =3D self.dataTypes[col].split(':')[0]
if typeName =3D=3D colType:
return True
else:
return False
def CanSetValueAs(self, row, col, typeName):
return self.CanGetValueAs(row, col, typeName)
class List_control(wx.ListCtrl, Scroll_binder):
def __init__(self, parent, size, style):
wx.ListCtrl.__init__(self, parent =3D parent, size =3D size, style =3D st=
yle)
Scroll_binder.__init__(self)
def fill(self):
# Create the images with the desired height.
self.image_list =3D wx.ImageList(1, ROW_SIZE - 1)
self.SetImageList(self.image_list, wx.IMAGE_LIST_SMALL)
self.InsertColumn(0, "Col 1")
self.InsertColumn(1, "Col 2")
self.InsertColumn(2, "Col 3")
for f in range(NUM_DATA):
self.Append(("id%4d" % (f + 1), "Ref %d" % f, "total"))
class App(wx.App):
"""Application class."""
def OnInit(self):
self.frame =3D wx.Frame(None, title =3D "My title", size =3D (700, 500))
panel =3D wx.Panel(self.frame)
self.SetTopWindow(self.frame)
hbox =3D wx.BoxSizer(wx.HORIZONTAL)
vbox1 =3D wx.BoxSizer(wx.VERTICAL)
vbox2 =3D wx.BoxSizer(wx.VERTICAL)
# The filler panel allows us to set the 'height' of the list.
self.filler =3D wx.Panel(panel)
self.list =3D List_control(panel, (300, -1),
wx.LC_REPORT | wx.ALWAYS_SHOW_SB | wx.VSCROLL)
self.list.fill()
vbox1.Add(self.filler, 0, wx.EXPAND | wx.ALL, 0)
vbox1.Add(self.list, 1, wx.EXPAND | wx.ALL, 5)
self.grid =3D CustomTableGrid(panel)
vbox2.Add(self.grid, 1, wx.EXPAND | wx.ALL, 5)
hbox.Add(vbox1, 0, wx.EXPAND)
hbox.Add(vbox2, 1, wx.EXPAND)
# Bind the scrollbars of the widgets.
self.grid.bind_scroll(self.list)
self.list.bind_scroll(self.grid)
self.resize_filler()
panel.SetSizer(hbox)
self.frame.Show()
return True
def resize_filler(self):
"""f() -> None
Resizes the filler panel to adapt the list control height. The position
of the list control is moved in such a way that its entries correspond
to the height of the grids entries.
"""
# Get the sizes of the list and grid top labels.
grid_size =3D ROW_SIZE
# Unfortunately the height of the list labels is not possible to get,
# hence the use of the magic 4 number.
list_size =3D self.list.GetCharHeight() + MAGIC_LIST_HEIGHT
self.filler.SetSize((-1, grid_size - list_size))
def main():
"""Main entry point."""
app =3D App(redirect =3D False)
app.MainLoop()
print "main() finished running."
if __name__ =3D=3D "__main__":
main()
More information about the wxpython-users
mailing list