Powerful table widget for a Python GUI
Question: I'd like to program a table-like GUI. Do you know a powerful table widget (for
any GUI), which has ready-made functionality like filtering, sorting, editing
and alike (as seen in Excel)?


Answer: You can use wxGrid - here's some demo code - you need to manage/wire up all
the events yourself on the underlying Table. Its a bit complicated to gove an
explaination in words, here's some code (mostly based on the wx examples
code):

    
    
    import wx
    from wx import EVT_MENU, EVT_CLOSE
    import wx.grid as gridlib
    
    from statusclient import JobDataTable, JobDataGrid
    
    
    app = wx.App()
    
    
    log = Logger(__name__)
    
    
    class JobManager(wx.Frame):
    
        def __init__(self, parent, title):
            super(JobManager, self).__init__(parent, title=title)
            panel = wx.Panel(self, -1)
    
            self.client_id = job_server.register()
            log.info('Registered with server as {}'.format(self.client_id))
            self.jobs = job_server.get_all_jobs()
            grid = self.create_grid(panel, self.jobs)       
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(grid, 1, wx.ALL|wx.EXPAND)
            panel.SetSizer(sizer)
    
            # Bind Close Event
            EVT_CLOSE(self, self.exit)
            self.Center()
            self.Show()
    
        def exit(self, event):
            log.info('Unregistering {0} from server...'.format(self.client_id))
            job_server.unregister(self.client_id)
            job_server.close()
            exit()
    
        def create_grid(self, panel, data):
            table = JobDataTable(jobs=data)
            grid = JobDataGrid(panel)
            grid.CreateGrid(len(data), len(data[0].keys()))
            grid.SetTable(table)
            grid.AutoSize()
            grid.AutoSizeColumns(True)
            return grid
    
    def main():
        frame = JobManager(None, 'Larskhill Job Manager')
        app.MainLoop()
    
    if __name__ == '__main__':
            job_server = zerorpc.Client()
            job_server.connect('tcp://0.0.0.0:4242')
        main()
    
    
    ####
    ui/client.py
    ####
    
    import wx
    import wx.grid as gridlib
    
    EVEN_ROW_COLOUR = '#CCE6FF'
    GRID_LINE_COLOUR = '#ccc'
    COLUMNS = {0:('id', 'ID'), 1:('name', 'Name'), 2:('created_at', 'Created'), 3:('status', 'Current Status')}
    
    log = Logger(__name__)
    
    class JobDataTable(gridlib.PyGridTableBase):
    
        """
        A custom wxGrid Table that expects a user supplied data source.
        """
        def __init__(self, jobs=None):
            gridlib.PyGridTableBase.__init__(self)
            self.headerRows = 0
            self.jobs = jobs
    
    #-------------------------------------------------------------------------------
    # Required methods for the wxPyGridTableBase interface
    #-------------------------------------------------------------------------------
    
        def GetNumberRows(self):
            return len(self.jobs)
    
        def GetNumberCols(self):
            return len(COLUMNS.keys())
    
        #---------------------------------------------------------------------------
        # 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. We load thises directly from the Jobs Data.
        #---------------------------------------------------------------------------
        def GetValue(self, row, col):
            prop, label = COLUMNS.get(col)
            #log.debug('Setting cell value')
            return self.jobs[row][prop]
    
        def SetValue(self, row, col, value):
            pass
    
        #---------------------------------------------------------------------------
        # Some optional methods
        # Called when the grid needs to display labels
        #---------------------------------------------------------------------------
        def GetColLabelValue(self, col):
            prop, label = COLUMNS.get(col)
            return label
    
        #---------------------------------------------------------------------------
        # 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 gridlib.GRID_VALUE_STRING
    
        #---------------------------------------------------------------------------`
        # 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):
            pass
    
        def CanSetValueAs(self, row, col, typeName):
            pass
    
        #---------------------------------------------------------------------------
        # Style the table, stripy rows and also highlight changed rows.
        #---------------------------------------------------------------------------
        def GetAttr(self, row, col, prop):
            attr = gridlib.GridCellAttr()
    
            # Odd Even Rows
            if row % 2 == 1:
                bg_colour = EVEN_ROW_COLOUR
                attr.SetBackgroundColour(bg_colour)
    
            return attr
    
    #-------------------------------------------------------------------------------
    # Custom Job Grid
    #-------------------------------------------------------------------------------
    class JobDataGrid(gridlib.Grid):
        def __init__(self, parent, size=wx.Size(1000, 500), data_table=None):
            self.parent = parent
            gridlib.Grid.__init__(self, self.parent, -1) # so grid references a weak reference to the parent
            self.SetGridLineColour(GRID_LINE_COLOUR)
            self.SetRowLabelSize(0)
            self.SetColLabelSize(30)
            self.table = JobDataTable()
    

Give me a shout if it needs clarification.

