Dynamic Table Sample

I thought this might be of interest or use to others.

Essentially, it is a wrapper class around wxGrid, that allows for easy
configuration, and shelving of data.

And just a warning, its using the new wx namespace...

Sean

"""
DynamicTable.py - A generic table/grid wrapper that allows for easy
configuration.
License: MIT
"""
from wx import Frame, Panel, BoxSizer, VERTICAL, GROW, ALL, PySimpleApp
from wx.grid import PyGridTableBase, GridTableMessage,
GRIDTABLE_NOTIFY_ROWS_APPENDED, Grid
from wx.grid import GRIDTABLE_NOTIFY_ROWS_DELETED, GRID_VALUE_STRING,
EVT_GRID_CELL_LEFT_CLICK
from wx.grid import GRIDTABLE_NOTIFY_COLS_DELETED,
GRIDTABLE_NOTIFY_COLS_APPENDED
from wx.grid import GRID_VALUE_BOOL, GRID_VALUE_NUMBER, GRID_VALUE_FLOAT,
GRID_VALUE_CHOICE
from wx.grid import GRID_VALUE_TEXT, GRID_VALUE_LONG,
GRID_VALUE_CHOICEINT, GRID_VALUE_DATETIME
import os, shelve

···

#---------------------------------------------------------------------------

class DynamicTable(PyGridTableBase):
    """
       DynamicTable: A table with dynamic column names, types and data.
       Data is retrieved from a shelved file, specified in the
constructor.
       The table can be configured using:
           AddColumn
           DeleteColumn
           AddRow
           SaveDataToFile
    """

    def __init__(self,parent_grid, file_name):
        PyGridTableBase.__init__(self)
        self.file_name = file_name
        self.parent_grid = parent_grid

        self.column_names, self.column_types, self.column_configs,
self.grid_data = self.GetDataFromFile(file_name)

    def __del__(self):
        self.SaveDataToFile(self.file_name)

    def GetDataFromFile(self, file_name):
        """
        GetDataFromFile(self, file_name) returns a tuple of data from the
file
        in the following format:
        (list of column names, list of column types, list of column config
data, list of data)
        """
        if not os.path.exists(file_name):
            return [],[],[], []

        shelf = shelve.open(file_name)
        column_names = shelf['column_names']
        column_types = shelf['column_types']
        column_configs = shelf['column_configs']
        grid_data = shelf['grid_data']
        shelf.close()
        return column_names, column_types, column_configs, grid_data

    def SaveDataToFile(self, file_name):
        """
        SaveDataToFile(self, file_name) saves the column name list, column
type list,
        column config data and data list to the file specified.
        """
        if file_name == '':
            file_name = "%s\\%s" % (os.path.curdir, 'DynamicTable.dat')
        shelf = shelve.open(file_name,'c',True)
        shelf['column_names'] = self.column_names
        shelf['column_types'] = self.column_types
        shelf['column_configs'] = self.column_configs
        shelf['grid_data'] = self.grid_data
        shelf.close()

    def GetNumberRows(self):
        "GetNumberRows is required by the PyGridTableBase interface."
        return len(self.grid_data) + 1

    def GetNumberCols(self):
        "GetNumberCols is required by the PyGridTableBase interface."
        return len(self.column_names)

    def IsEmptyCell(self, row, col):
        "IsEmptyCell is required by the PyGridTableBase interface."
        try:
            return not self.grid_data[row][col]
        except IndexError:
            return True

    def GetValue(self, row, col):
        try:
            return self.grid_data[row][col]
        except IndexError:
            return ''

    def SetValue(self, row, col, value):
        try:
            if self.column_types[col] == GRID_VALUE_BOOL and value == '':
                value = 0
            self.grid_data[row][col] = value
        except IndexError:
            # add a new row
            self.AddRow([''] * self.GetNumberCols())

            #This is called recursively: new_row# - cur_max_row# times.
            self.SetValue(row, col, value)

    def GetColLabelValue(self, col):
        "Used to display column names"
        return self.column_names[col]

    def GetTypeName(self, row, col):
        "Returns the column's type"
        return "%s:%s" %(self.column_types[col], self.column_configs[col])

    def CanGetValueAs(self, row, col, typeName):
        "Returns whether or not the type Name matches the column type."
        return typeName == self.column_types[col]

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

    def AddColumn(self, column_name, column_type, column_config,
default_value = None):
        "Adds a grid column using the data specified. Note: default_value
only applies to EXISTING rows."
        self.column_names.append(column_name)
        self.column_types.append(column_type)
        self.column_configs.append(column_config)

        if len(self.grid_data) > 0:
            new_data = []
            for row in self.grid_data:
                new_data.append(row.append(default_value))
            self.grid_data = new_data
        self.NotifyGrid(GRIDTABLE_NOTIFY_COLS_APPENDED, 1)
    def DeleteColumn(self, column_name):
        "Deletes the column specified by the column_name."
        error_text = "Specified column name can't be deleted because it
doesn't exist."
        assert column_name in self.column_names, error_text

        column_loc = column_names.index(column_name)
        self.column_names.pop(column_loc)
        self.column_types.pop(column_loc)
        self.column_configs.pop(column_loc)

        if len(self.grid_data) > 0:
            new_data = []
            for row in self.grid_data:
                row.pop(column_loc)
                new_data.append(row)

            self.grid_data = new_data
        self.NotifyGrid(GRIDTABLE_NOTIFY_COLS_DELETED, 1)
    def AddRow(self, row):
        "Adds the specified row to the grid."
        error_text = "AddRowError: Number of columns in row to add doesn't
match the number of columns in the grid."
        assert len(row) == len(self.column_names), error_text

        self.grid_data.append(row)
        self.NotifyGrid(GRIDTABLE_NOTIFY_ROWS_APPENDED, 1)
               
    def DeleteRow(self, row_number):
        assert row_number < self.GetNumberRows(), "Cannot delete a
non-existant row."
        self.grid_data.pop(row_number)
        tbl_msg = GridTableMessage(self, GRIDTABLE_NOTIFY_ROWS_DELETED, 1)
                               
        self.GetView().ProcessTableMessage(tbl_msg)
        
    def NotifyGrid(self, msg, count):
        "Notifies the gird of the message and the affected count."
        tbl_msg = GridTableMessage(self, msg, count)
         
        self.GetView().ProcessTableMessage(tbl_msg)
                
class DynamicGrid(Grid):
    def __init__(self, parent, file_name):
        Grid.__init__(self, parent, -1)

        table = DynamicTable(self,file_name)

        self.SetTable(table, True)

        self.SetRowLabelSize(0)
        self.SetMargins(0,0)
        self.AutoSizeColumns(False)

class TestFrame(Frame):
    def __init__(self, parent, file_name):
        Frame.__init__(self, parent, -1, "Custom Table, data driven Grid
Demo", size=(640,480))
        p = Panel(self, -1, style=0)
        grid = DynamicGrid(p, file_name)
        if not os.path.exists(file_name):
            #Ensure this only runs once!
            tbl = grid.GetTable()
            tbl.AddColumn("First Name", GRID_VALUE_STRING, '','')
            tbl.AddColumn("Last Name", GRID_VALUE_STRING, '', '')
            tbl.AddColumn("Likes Python", GRID_VALUE_BOOL, '', True)
            tbl.AddColumn("Age", GRID_VALUE_NUMBER, '1,120', 0)
            tbl.AddColumn("Pocket Change", GRID_VALUE_FLOAT, '3,2', 0.0)
            tbl.AddColumn("OS Used", GRID_VALUE_CHOICE,
'Linux,Mac,Win95,Win98,WinMe,Win2k,WinNT,WinXP,Other','Other')
            tbl.AddColumn("What's This??", GRID_VALUE_TEXT,
'Bold,Italic,','Text here\nor there')
            tbl.AddColumn("Distance from AlphaCentaur", GRID_VALUE_LONG,
'',111111111111111111)
            tbl.AddColumn("Birth Date", GRID_VALUE_CHOICEINT, '1,31',1)
            tbl.AddColumn("Today", GRID_VALUE_DATETIME, '', '')
            tbl.AddRow(["Sean", "McKay", True, 28, 1.23,'Win98','Text
here\nor there', 111111111111111,23,'6/23/2003'])
            tbl.SaveDataToFile(file_name)
        bs = BoxSizer(VERTICAL)
        bs.Add(grid, 1, GROW|ALL, 5)
        p.SetSizer(bs)
        self.grid = grid

if __name__ == '__main__':
    import sys
    app = PySimpleApp()
    frame = TestFrame(None, "c:\windows\desktop\grid_test.data")
    frame.Show(True)
    app.frame = frame
    app.MainLoop()

=====
Don't be anxious about tomorrow. God will take care of your tomorrow too. Live one day at a time. -- Matthew 6:34 TLB

__________________________________
Do you Yahoo!?
SBC Yahoo! DSL - Now only $29.95 per month!
http://sbc.yahoo.com