1#!/usr/bin/env python
2
3import wx
4import wx.grid as gridlib
5
6#---------------------------------------------------------------------------
7
8class CustomDataTable(gridlib.GridTableBase):
9    def __init__(self, log):
10        gridlib.GridTableBase.__init__(self)
11        self.log = log
12
13        self.colLabels = ['ID', 'Description', 'Severity', 'Priority', 'Platform',
14                          'Opened?', 'Fixed?', 'Tested?', 'TestFloat']
15
16        self.dataTypes = [gridlib.GRID_VALUE_NUMBER,
17                          gridlib.GRID_VALUE_STRING,
18                          gridlib.GRID_VALUE_CHOICE + ':only in a million years!,wish list,minor,normal,major,critical',
19                          gridlib.GRID_VALUE_NUMBER + ':1,5',
20                          gridlib.GRID_VALUE_CHOICE + ':all,MSW,GTK,other',
21                          gridlib.GRID_VALUE_BOOL,
22                          gridlib.GRID_VALUE_BOOL,
23                          gridlib.GRID_VALUE_BOOL,
24                          gridlib.GRID_VALUE_FLOAT + ':6,2',
25                          ]
26
27        self.data = [
28            [1010, "The foo doesn't bar", "major", 1, 'MSW', 1, 1, 1, 1.12],
29            [1011, "I've got a wicket in my wocket", "wish list", 2, 'other', 0, 0, 0, 1.50],
30            [1012, "Rectangle() returns a triangle", "critical", 5, 'all', 0, 0, 0, 1.56]
31
32            ]
33
34
35    #--------------------------------------------------
36    # required methods for the wxPyGridTableBase interface
37
38    def GetNumberRows(self):
39        return len(self.data) + 1
40
41    def GetNumberCols(self):
42        return len(self.data[0])
43
44    def IsEmptyCell(self, row, col):
45        try:
46            return not self.data[row][col]
47        except IndexError:
48            return True
49
50    # Get/Set values in the table.  The Python version of these
51    # methods can handle any data-type, (as long as the Editor and
52    # Renderer understands the type too,) not just strings as in the
53    # C++ version.
54    def GetValue(self, row, col):
55        try:
56            return self.data[row][col]
57        except IndexError:
58            return ''
59
60    def SetValue(self, row, col, value):
61        def innerSetValue(row, col, value):
62            try:
63                self.data[row][col] = value
64            except IndexError:
65                # add a new row
66                self.data.append([''] * self.GetNumberCols())
67                innerSetValue(row, col, value)
68
69                # tell the grid we've added a row
70                msg = gridlib.GridTableMessage(self,            # The table
71                        gridlib.GRIDTABLE_NOTIFY_ROWS_APPENDED, # what we did to it
72                        1                                       # how many
73                        )
74
75                self.GetView().ProcessTableMessage(msg)
76        innerSetValue(row, col, value)
77
78    #--------------------------------------------------
79    # Some optional methods
80
81    # Called when the grid needs to display labels
82    def GetColLabelValue(self, col):
83        return self.colLabels[col]
84
85    # Called to determine the kind of editor/renderer to use by
86    # default, doesn't necessarily have to be the same type used
87    # natively by the editor/renderer if they know how to convert.
88    def GetTypeName(self, row, col):
89        return self.dataTypes[col]
90
91    # Called to determine how the data can be fetched and stored by the
92    # editor and renderer.  This allows you to enforce some type-safety
93    # in the grid.
94    def CanGetValueAs(self, row, col, typeName):
95        colType = self.dataTypes[col].split(':')[0]
96        if typeName == colType:
97            return True
98        else:
99            return False
100
101    def CanSetValueAs(self, row, col, typeName):
102        return self.CanGetValueAs(row, col, typeName)
103
104
105#---------------------------------------------------------------------------
106
107
108class CustTableGrid(gridlib.Grid):
109    def __init__(self, parent, log):
110        gridlib.Grid.__init__(self, parent, -1)
111
112        table = CustomDataTable(log)
113
114        # The second parameter means that the grid is to take ownership of the
115        # table and will destroy it when done.  Otherwise you would need to keep
116        # a reference to it and call it's Destroy method later.
117        self.SetTable(table, True)
118
119        self.SetRowLabelSize(0)
120        self.SetMargins(0,0)
121        self.AutoSizeColumns(False)
122
123        self.Bind(gridlib.EVT_GRID_CELL_LEFT_DCLICK, self.OnLeftDClick)
124
125
126    # I do this because I don't like the default behaviour of not starting the
127    # cell editor on double clicks, but only a second click.
128    def OnLeftDClick(self, evt):
129        if self.CanEnableCellControl():
130            self.EnableCellEditControl()
131
132
133#---------------------------------------------------------------------------
134
135class TestFrame(wx.Frame):
136    def __init__(self, parent, log):
137
138        wx.Frame.__init__(
139            self, parent, -1, "Custom Table, data driven Grid  Demo", size=(640,480)
140            )
141
142        p = wx.Panel(self, -1, style=0)
143        grid = CustTableGrid(p, log)
144        b = wx.Button(p, -1, "Testing with another control...")
145        b.SetDefault()
146        self.Bind(wx.EVT_BUTTON, self.OnButton, b)
147        b.Bind(wx.EVT_SET_FOCUS, self.OnButtonFocus)
148        bs = wx.BoxSizer(wx.VERTICAL)
149        bs.Add(grid, 1, wx.GROW|wx.ALL, 5)
150        bs.Add(b)
151        p.SetSizer(bs)
152
153    def OnButton(self, evt):
154        print("button selected")
155
156    def OnButtonFocus(self, evt):
157        print("button focus")
158
159
160#---------------------------------------------------------------------------
161
162if __name__ == '__main__':
163    import sys
164    app = wx.App()
165    frame = TestFrame(None, sys.stdout)
166    frame.Show(True)
167    app.MainLoop()
168
169
170#---------------------------------------------------------------------------
171