Dear Friends of wxPython, and especially Robin Dunn,
From Markandeya:
Thanks for all the help already given on the internet, what a great resource and thanks for sharing and helping. I'm using the book "wxPython in Action" yet still need your help.
I am populating a grid widget through a PyGridTableBase (receiving a mysql query result as "newdata" paramater) and have a method, copied from a prior Robin Dunn posting which uses this code in the PyGridTableBase sub class
def GetAttr(self, row, col, kind):
attr = [self.even, self.odd][row % 2]
attr.IncRef()
return attr
My errors are (for every cell update):
File ".\myWxPyTest.py", line 114, in GetAttr
attr.IncRef()
File "C:\Python25\Lib\site-packages\wx-2.8-msw-unicode\wx\grid.py", line 528, in IncRef
return _grid.GridCellAttr_IncRef(*args, **kwargs)
TypeError: coercing to Unicode: need string or buffer, NoneType found
here is code for the GridCellAtrr() in the same class
self.odd=wx.grid.GridCellAttr()
self.odd.SetBackgroundColour("sky blue")
self.odd.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD))
self.even=wx.grid.GridCellAttr()
self.even.SetBackgroundColour("sea green")
self.even.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
SO now i don't get any column headers to appear. The rest of the grid/program seems to act properly and rows are of alternating colour and font as well as the grid refreshes with the new query data.
"In another past helping post by Robin Dunn on the same problem(plus others) he said:
I don't think that IncRef uses a dynarray, so the assertion is probably
actually happening in a prior event or callback and not detected until
the IncRef call. So, yes, I'll need a runnable sample of this problem."
here's the whole small program code: any helpful ideas will be very much appreciated. Markandeya I do any sql statement to get a result set that becomes the newdata of the table model. myconf import is a file with host,user,passwrd,db for mysqldb connection.
#!/usr/bin/env python
""" A program to fill and refresh a grid from mysql statements
using wxpython and Mysqldb"""
import wx
import wx.grid
import sys
import MySQLdb
import myconf
class MyApp(wx.App):
def __init__(self, redirect=True):
wx.App.__init__(self, redirect)
def OnInit(self):
self.frame = GridFrame(None)
self.frame.Show()
self.SetTopWindow(self.frame)
return True
class Mycxn():
try:
cxn = MySQLdb.connect(host= myconf.host, user = myconf.user,
passwd = myconf.passwd, db = myconf.db)
except MySQLdb.Error, e:
print "Unable to make db connection to start program"
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit (1)
cursor = cxn.cursor()
def Query1(self):
self.cursor.execute ("SELECT EnggId, Name FROM engineer")
rows = self.cursor.fetchall ()
fields = [rows]
##print fields, "Q1cursor"
for i in range(len( self.cursor.description)):
fields.append(self.cursor.description[ i ][ 0 ])
##print "Q1fields = ", fields[1:]
return fields
def Query2(self):
self.cursor.execute ("SELECT * FROM purchases order by date limit 500, 10 ")
rows = self.cursor.fetchall ()
fields = [rows]
##print len(fields[0]), "Q2cursor Len"
for i in range(len( self.cursor.description)):
fields.append(self.cursor.description[ i ][ 0 ])
##print "Q2fields = ", fields[1:]
return fields
class GenericTable(wx.grid.PyGridTableBase):
"""This allows for data to be an sql result set
which is stored in a MySQLdb.fetchall() call.
the col and row labels are from arrays too. sql call
to get col headers. (see how to!)."""
def __init__(self, data, rowLabels=None, colLabels=None):
wx.grid.PyGridTableBase.__init__(self)
self.data = data
##print self.data
##print self.data[0][2], self.data[1][2], self.data[2][2]
self.rowLabels = rowLabels
self.colLabels = colLabels
self.odd=wx.grid.GridCellAttr()
self.odd.SetBackgroundColour("sky blue")
self.odd.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD))
self.even=wx.grid.GridCellAttr()
self.even.SetBackgroundColour("sea green")
self.even.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
def GetNumberRows(self):
return len(self.data)
def GetNumberCols(self):
return len(self.data[0])
def GetColLabelValue(self, col):
if self.colLabels:
##print col, self.colLabels, "getcolLabelvalue"
return self.colLabels[col]
def GetRowLabelValue(self, row):
if self.rowLabels:
return self.rowLabels[row]
def IsEmptyCell(self, row, col):
return False
def GetValue(self, row, col):
##print row, " r:c ", col
value = self.data[row][col]
##value = self.data.get((row, col))
if value is not None:
##print row, col, "GetValue", value
return value
else:
return ''
##entry = self.data[row]
##return getattr(entry, self.colAttrs[col])
##this is if you declare an colAttrs array earlier
##colAttrs = ("first", "last") of col names
def SetValue(self, row, col, value):
pass
def GetAttr(self, row, col, kind):
attr = [self.even, self.odd][row % 2]
attr.IncRef()
return attr
##def Clear()
def UpdateDataModel(self, newdata):
##print len(newdata), "len newdata", self.colLabels, "colLabels"
##print len(self.data), "len olddata", self.data[0]
# ... set new data ...
self.GetView().BeginBatch()
newNumRows = len(newdata[0])
curNumRows = self.GetNumberRows()
newNumCols = len(newdata) -1
curNumCols = self.GetNumberCols()
##print curNumRows, "curRows", newNumRows, "newRows", curNumCols, "curCols", newNumCols, "newcols"
if newNumRows < curNumRows:
msg = wx.grid.GridTableMessage(self,
wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED,
curNumRows - newNumRows, # position
curNumRows - newNumRows) # how many
self.GetView().ProcessTableMessage(msg)
##print "<Rows"
if newNumRows > curNumRows:
msg = wx.grid.GridTableMessage(self,
wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED,
newNumRows - curNumRows) # how many
self.GetView().ProcessTableMessage(msg)
##print ">Rows"
# ... same thing for columns ....
if newNumCols < curNumCols:
##print newNumCols, "newcols", curNumCols, "curcols"
msg = wx.grid.GridTableMessage(self,
wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED,
curNumCols - newNumCols, # position
curNumCols - newNumCols) # how many
##print "THIS IS THE MSG COLS DELETED"
##print" "
self.GetView().ProcessTableMessage(msg)
if newNumCols > curNumCols:
msg = wx.grid.GridTableMessage(self,
wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED,
newNumCols - curNumCols) # how many
self.GetView().ProcessTableMessage(msg)
##print "THIS IS MSG COLS APPENDED"
self.GetView().EndBatch()
self.data = newdata[0]
##newdata[0:1] =
##self.colLabels = newdata
self.colLabels = newdata[1:]
msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
self.GetView().ProcessTableMessage(msg)
##print self.GetNumberCols(), "new number of cols :", self.GetNumberRows(), "New Num rows"
##print " "
##print self.colLabels , "mycolLabels"
##print self.data, "mydata"
## self.grid.ForceRefresh()
class GridFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, title="Stock Grid",
size=(800, 600), pos=(20, 0))
panel = wx.Panel(self)
btQuery1 = wx.Button(panel, id=-1, label="Send Query 1", pos=(10, 10),
size=(80, 40))
btQuery2 = wx.Button(panel, id=-1, label="Send Query 2", pos=(100, 10),
size=(100, 40))
##self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.Bind(wx.EVT_BUTTON, self.sendQuery1, btQuery1)
self.Bind(wx.EVT_BUTTON, self.sendQuery2, btQuery2)
self.cxn = Mycxn() #instance of connection class
# grid init params (self, parent, id, pos, size, style, name)
grid = wx.grid.Grid(parent = panel, id=-1, pos = (0,100), size = (600,400), name = "MyGrid")
initialdata = (("AUM", "Sri", "Aurobindo", "Mother", "!"),("There", "is", "no", "data yet", "!"), ("Please", "try", "a", "Query", "!"))
table = GenericTable(initialdata)
self.table = table
grid.SetTable(table, True)
##print table.GetNumberRows(), table.GetValue(1,1)
##print table.GetNumberCols(), table.GetValue(2,0)
def sendQuery1(self, event):
q1Data = self.cxn.Query1()
self.table.UpdateDataModel(q1Data)
def sendQuery2(self, event):
q2Data = self.cxn.Query2()
self.table.UpdateDataModel(q2Data)
if __name__ == '__main__':
app = MyApp(False)
app.MainLoop()