wxPython: How to Get Selected Cells in a Grid

wxgridselect.png

Today we will be looking at how to get the selected cells from a wxPython grid object. Most of the time, getting the section is easy, but when the user selects more then one cell, getting the selection becomes more complicated. We will need to create some sample code to show how all this fits together. Let's get started!

Grid Cell Selection

There is an interesting article on the web that covers this topic. You can read it here. However, there are several problems with the article which we will look at as well. Here's the code we'll be looking at:

import wx
import wx.grid as gridlib

########################################################################
class MyPanel(wx.Panel):
    """"""

    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        self.currentlySelectedCell = (0, 0)
         
        self.myGrid = gridlib.Grid(self)
        self.myGrid.CreateGrid(12, 8)
        self.myGrid.Bind(gridlib.EVT_GRID_SELECT_CELL, self.onSingleSelect)
        self.myGrid.Bind(gridlib.EVT_GRID_RANGE_SELECT, self.onDragSelection)
        
        selectBtn = wx.Button(self, label="Get Selected Cells")
        selectBtn.Bind(wx.EVT_BUTTON, self.onGetSelection)
 
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.myGrid, 1, wx.EXPAND)
        sizer.Add(selectBtn, 0, wx.ALL|wx.CENTER, 5)
        self.SetSizer(sizer)
        
    #----------------------------------------------------------------------
    def onDragSelection(self, event):
        """
        Gets the cells that are selected by holding the left
        mouse button down and dragging
        """
        if self.myGrid.GetSelectionBlockTopLeft():
            top_left = self.myGrid.GetSelectionBlockTopLeft()[0]
            bottom_right = self.myGrid.GetSelectionBlockBottomRight()[0]
            self.printSelectedCells(top_left, bottom_right)
            
    #----------------------------------------------------------------------
    def onGetSelection(self, event):
        """
        Get whatever cells are currently selected
        """
        cells = self.myGrid.GetSelectedCells()
        if not cells:
            if self.myGrid.GetSelectionBlockTopLeft():
                top_left = self.myGrid.GetSelectionBlockTopLeft()[0]
                bottom_right = self.myGrid.GetSelectionBlockBottomRight()[0]
                self.printSelectedCells(top_left, bottom_right)
            else:
                print self.currentlySelectedCell
        else:
            print cells
        
    #----------------------------------------------------------------------
    def onSingleSelect(self, event):
        """
        Get the selection of a single cell by clicking or 
        moving the selection with the arrow keys
        """
        print "You selected Row %s, Col %s" % (event.GetRow(),
                                               event.GetCol())
        self.currentlySelectedCell = (event.GetRow(),
                                      event.GetCol())
        event.Skip()
        
    #----------------------------------------------------------------------
    def printSelectedCells(self, top_left, bottom_right):
        """
        Based on code from http://ginstrom.com/scribbles/2008/09/07/getting-the-selected-cells-from-a-wxpython-grid/
        """
        cells = []
        
        rows_start = top_left[0]
        rows_end = bottom_right[0]

        cols_start = top_left[1]
        cols_end = bottom_right[1]

        rows = range(rows_start, rows_end+1)
        cols = range(cols_start, cols_end+1)

        cells.extend([(row, col)
            for row in rows
            for col in cols])
            
        print "You selected the following cells: ", cells
        
        for cell in cells:
            row, col = cell
            print self.myGrid.GetCellValue(row, col)
        
########################################################################
class MyFrame(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, parent=None, title="Single Cell Selection")
        panel = MyPanel(self)
        self.Show()

#----------------------------------------------------------------------
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

Let's take a few moments to break this down. First of all, we create a grid object that we're calling self.myGrid. We bind to two grid specific events, EVT_GRID_SELECT_CELL and EVT_GRID_RANGE_SELECT. This is for demonstration purposes as you usually don't need to bind to EVT_GRID_SELECT_CELL. For the single cell selection event, we call the onSingleSelect handler. In it we use the event object to grab the correct row and column. If you look at the article linked to above, you'll notice that they are using GetGridCursorRow and GetGridCursorCol. I found that these only return the previously selected cell, not the cell that is currently selected. This is the reason we are using the event object's methods instead. Also note that we are updating the value of self.currentlySelectedCell to equal whatever the currently selected cell is.

The other grid event is bound to onDragSelection. In this event handler we call the grid's GetSelectionBlockTopLeft() method and check to make sure it returns something. If it does not, then we do nothing else. But if it does return something, then we grab its contents as well as the contents returned from GetSelectionBlockBottomRight(). Then we pass these to our printSelectedCells method. This code is based on the previously mentioned article, although it has been simplified a bit as I found the original's for loop was throwing an error. Basically all this method does is create two lists of values using Python's range function. Then it extends a list using a nested list comprehension. Finally it prints out the cells that were selected to stdout.

The last method to look at is the button event handler: onGetSelection. This method calls the grid's GetSelectedCells() method. This will return the selected cells that single clicked. It will also work if the user drag selects some cells. If the user just selects one cell, then we will print self.currentlySelectedCell as it will always equal the value of the current selection.

Wrapping Up

As you can see, getting the selected cell or cells from the grid object can be a little tricky. But with a bit of work, we were able to overcome. Hopefully you will find this useful in one of your current or future projects.

Related Reading

Copyright © 2024 Mouse Vs Python | Powered by Pythonlibrary