[wxPython] wxPython - wxGrid refreshing

Hi all

I am trying to get a wxGrid with wxPyGridTableBase (using Boa) to refresh
the display of cell contents as a result of a change in another cell's
contents.

The following code generates a "string or unicode required" error message in
line 195, self.grid1.SetData(PipeGridData).

Without line 195, the code runs without errors, but a change in one of the
input cells does not refresh the display of the output cells until the
cursor is moved to that cell.

It would seem to me that the SetData function is ignoring the data types
established in the wxPyGridTableBase class.

The forcerefresh method did not seem to work in refreshing the cell contents
either.

Can anyone enlighten me? Code follows.

TIA

Lynndon Harnell

···

#-===========================
#Boa:Frame:wxFrame1

from wxPython.wx import *
from wxPython.grid import *
from wxPython.lib.buttons import *
from math import *
import string

ColHeaders=["PipeID","Len(m)","OD(mm)","WT(mm)","Gr","SMYS","Vol(m3)","Psmys
"]
RowHeaders=["1","2"]
NCols=len(ColHeaders)
NRows=len(RowHeaders)
PipeGridData = [
            [1, 2120, 114.3, 4.8, "B", 241,0,0],
            [2, 5000, 114.3, 3.2, "X-42",289,0,0]
            ]
def PipeUpdate(PipeGridData):
    z=len(PipeGridData)
    for i in range(0,z):
       # calc volume
        length = PipeGridData[i][1]
        temp1 = PipeGridData[i][3]
        temp1 = float(temp1) / 1000 # wall thickness in m
        idia = float(PipeGridData[i][2]) / 1000
        idia = idia - 2 * temp1
        vol = length * pi/4
        vol = vol * idia * idia # in m3
        PipeGridData[i][6] = vol
       # calc SMYS
        if PipeGridData[i][4] == "B":
            PipeGridData[i][5] = 241
        elif PipeGridData[i][4][0:1] =="X":
            PipeGridData[i][5] = int(float(PipeGridData[i][4][2:4]) * 6.895)

    # calc Psmys
        Psmys = 2 * temp1 * PipeGridData[i][5]
        Psmys = Psmys / idia # in MPa
        PipeGridData[i][7] = Psmys
        
PipeUpdate(PipeGridData)
#---------------------------------------------------------------------------
class PipesTable(wxPyGridTableBase):
    """
    """
    def __init__(self):
        wxPyGridTableBase.__init__(self)
        self.colLabels = ColHeaders
        NCols=len(self.colLabels)
        NRows=len(RowHeaders)

        self.dataTypes = [wxGRID_VALUE_NUMBER,
                          wxGRID_VALUE_NUMBER,
                          wxGRID_VALUE_CHOICE +
':60.3,88.9,114.3,168.3,219.1,273.1,323.9,355.6,406.4,457.0,508.0,559.0,610.
0,660.0,711.0,762.0,813.0,864.0,914.0,1067.0',
                          wxGRID_VALUE_FLOAT + ':4 , 2',
                          wxGRID_VALUE_CHOICE +
':B,X-42,X-48,X-52,X-56,X-65,X-70,X-80',
                          wxGRID_VALUE_NUMBER,
                          wxGRID_VALUE_FLOAT + ':6 , 3',
                          wxGRID_VALUE_FLOAT + ':6, 3']
        self.data = PipeGridData
    #--------------------------------------------------
    # required methods for the wxPyGridTableBase interface

    def GetNumberRows(self):
        return len(self.data) + 1

    def GetNumberCols(self):
        return len(self.data[0])

    def IsEmptyCell(self, row, col):
        return not self.data[row][col]

    # 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] = 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 = wxGridTableMessage(self, # The
table
                                     wxGRIDTABLE_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
    # nativly 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 = string.split(self.dataTypes[col], ':')[0]
        if typeName == colType:
            return true
        else:
            return false

    def CanSetValueAs(self, row, col, typeName):
        return self.CanGetValueAs(row, col, typeName)

# -----------------------------------------
class dbGrid(wxGrid):
    def
__init__(self,parent,id=-1,pos=wxDefaultPosition,size=wxDefaultSize,style=wx
WANTS_CHARS,name="grid"):
        wxGrid.__init__(self, parent, id, pos, size, style, name)

# def __init__(self, *_args, **_kwargs):
# apply(wxGrid.__init__, _args, _kwargs)
        table = PipesTable()

        # 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(table, true)

        self.SetRowLabelSize(0)
        self.SetMargins(0,0)
        self.AutoSizeColumns(false)
        self.SetDefaultCellAlignment(wxALIGN_RIGHT,wxALIGN_CENTER)

# EVT_GRID_CELL_LEFT_DCLICK(self, self.OnLeftDClick)

        for i in range(NCols):
            self.SetColLabelValue(i,ColHeaders[i])
            self.SetColSize(i,50)
        for i in range(NRows):
            self.SetRowLabelValue(i,RowHeaders[i])
            self.SetCellAlignment(i,4,wxALIGN_LEFT,wxALIGN_CENTER)
                
    def SetData(self,data):
        for row in range(len(data)):
            for col in range(len(data[row])):
                self.SetCellValue(row,col,data[row][col])
# ----------------------------------------------
def create(parent):
    return wxFrame1(parent)

[wxID_WXFRAME1, wxID_WXFRAME1GENBUTTON1, wxID_WXFRAME1GRID1,
wxID_WXFRAME1PANEL1] = map(lambda _init_ctrls: wxNewId(), range(4))

class wxFrame1(wxFrame):
    _custom_classes = {'wxGrid':['dbGrid']}
    def _init_utils(self):
        pass

    def _init_ctrls(self, prnt):
        wxFrame.__init__(self, id = wxID_WXFRAME1, name = '', parent = prnt,
pos = wxPoint(233, 110), size = wxSize(455, 409), style =
wxDEFAULT_FRAME_STYLE, title = 'wxFrame1')
        self._init_utils()

        self.panel1 = wxPanel(id = wxID_WXFRAME1PANEL1, name = 'panel1',
parent = self, pos = wxPoint(8, 32), size = wxSize(424, 128), style =
wxTAB_TRAVERSAL)

        self.genButton1 = wxGenButton(ID = wxID_WXFRAME1GENBUTTON1, label =
'genButton1', name = 'genButton1', parent = self, pos = wxPoint(160, 264),
size = wxSize(91, 40), style = 0)

        self.grid1 = dbGrid(id = wxID_WXFRAME1GRID1, name = 'grid1', parent
= self.panel1, pos = wxPoint(8, 8), size = wxSize(408, 104), style =
wxDOUBLE_BORDER)
# EVT_KEY_DOWN(self.grid1, self.OnGrid1KeyDown)
        EVT_MOUSE_EVENTS(self.grid1, self.OnGrid1MouseEvents)
        EVT_GRID_CELL_LEFT_DCLICK(self.grid1, self.OnLeftDClick)
        EVT_GRID_CELL_CHANGE(self.grid1, self.OnCellChange)

    def __init__(self, parent):
        self._init_ctrls(parent)

# def OnGrid1KeyDown(self, event):
# pass

    def OnGrid1MouseEvents(self, event):
        pass

    def OnLeftDClick(self, event):
        pass
# if self.CanEnableCellControl():
# self.EnableCellEditControl()

    def OnCellChange(self, event):
        PipeUpdate(PipeGridData)
        self.grid1.SetData(PipeGridData)
# self.grid1.ForceRefresh

Hi Lynndon,

I'm not at all qualified to fix your problem with wxGrid.SetData(), as I've never used it, but I may be able to help with this bit:

The forcerefresh method did not seem to work in refreshing the cell contents
either.

I had the same problem with my own grids: calling wxGrid.Refresh() doesn't seem to work. I got around it by doing the following instead:

         myGrid.BeginBatch()
         myGrid.EndBatch()

This seemed to force the grid to redraw itself correctly. I've got no idea why calling Refresh() doesn't work, or why the above does -- but in my app, at least, this seems to work fine.

Hope this helps...

  - Erik.

I am trying to get a wxGrid with wxPyGridTableBase (using Boa) to refresh
the display of cell contents as a result of a change in another cell's
contents.

Probably the "proper" way to do it from the table is to send the grid a
wxGRIDTABLE_REQUEST_VIEW_GET_VALUES message.

    self.GetView().ProcessTableMessage(
        wxGridTableMessage(wxGRIDTABLE_REQUEST_VIEW_GET_VALUES))

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!