views:

57

answers:

2

I've got a wxpython grid, and I'm changing the background color of a cell to show that something has happened to it.

I'd like to fade the color change in/out (like JavaScript in the browser) for a smoother look. Is this possible to do?

Right now, I'm just changing the background color, and then changing it back after a 1.5-second interval.

def do_stuf(self):
    # ... stuff ...
    wx.CallAfter(self.HighlightCell, row, col)

def HighlightCell(self, row, col):
    self.grid.Table.highlight = (row, col)
    self.grid.ForceRefresh()
    wx.CallLater(1500, self.ClearCellHighlight)

def ClearCellHighlight(self):
    self.grid.Table.highlight = None
    self.grid.ForceRefresh()

Then in the virtual table, I check if the cell needs highlighting:

def GetAttr(self, row, col, kind):
    """
    Use this callback to set the cell's background color
    """
    attr = wx.grid.GridCellAttr()
    if (row, col) == self.highlight:
        attr.SetBackgroundColour("green")
    elif row % 2:
        attr.SetBackgroundColour("white")
    else:
        attr.SetBackgroundColour("#e7ffff")

    return attr

Alternatively, is there another pretty way to indicate that a cell's contents have changed?

+1  A: 

To my knowledge, you can only set the transparency on the frame widget and see its affect on all the children. I don't recall why setting transparency on individual widgets doesn't work. Anyway, a decent way to simulate something like this would be to use a wx.Timer that cycles through a list of different shades of one color and then when the iteration is done, reset it back to the normal color. That should simulate the look well enough.

Mike Driscoll
+1  A: 

This is something I did a while ago to get a ListCtrl with items that fade out when deleted. Save the code to fade.py and run it to see the demo. Shouldn't be too hard to adapt it to a Grid.

import wx

class FadeMixin(object):
    ''' FadeMixin provides one public method: DeleteItem. It is meant to
    be mixed in with a ListCtrl to 'fade out' items before they are
    really deleted. Mixin like this:

    Assumption: the background colour of the control is wx.WHITE

    class MyListCtrl(FadeMixin, wx.ListCtrl):
        ...
    '''
    def __init__(self, *args, **kwargs):
        self.__bgColour = wx.WHITE
        super(FadeMixin, self).__init__(*args, **kwargs)

    def DeleteItem(self, index, fadeStep=10, fadeSpeed=50):
        if self.IsEnabled():
            self.__startDeleteItem(index)
        fgColour, bgColour, transparentColour = self.__getColours(index)
        if fgColour == bgColour == transparentColour:
            self.__finishDeleteItem(index)
        else:
            for colour, setColour in [(fgColour, self.SetItemTextColour), 
                                      (bgColour, self.SetItemBackgroundColour)]:
                fadedColour = self.__fadeColour(colour, transparentColour, 
                                                fadeStep)
                setColour(index, fadedColour)
            wx.FutureCall(50, self.DeleteItem, index, fadeStep, fadeSpeed)

    def SetBackgroundColour(self, colour):
        self.__bgColour = colour
        super(FadeMixin, self).SetBackgroundColour(colour)

    def GetBackgroundColour(self):
        return self.__bgColour

    def __startDeleteItem(self, index):
        # Prevent user input during deletion. Things could get messy if
        # the user deletes another item when we're still busy fading out the 
        # first one:
        self.Disable()
        # Unselect the item that is to be deleted to make the fading visible:
        currentState = self.GetItemState(index, wx.LIST_STATE_SELECTED)
        self.SetItemState(index, ~currentState, wx.LIST_STATE_SELECTED)

    def __finishDeleteItem(self, index):
        super(FadeMixin, self).DeleteItem(index)
        self.Enable()

    def __getColours(self, index):
        fgColour = self.GetItemTextColour(index)
        bgColour = self.GetItemBackgroundColour(index)
        transparentColour = self.GetBackgroundColour()
        if not bgColour:
            bgColour = transparentColour
        return fgColour, bgColour, transparentColour

    def __fadeColour(self, colour, transparentColour, fadeStep):
        newColour = []
        for GetIntensity in wx.Colour.Red, wx.Colour.Green, wx.Colour.Blue:
            currentIntensity = GetIntensity(colour) 
            transparentIntensity = GetIntensity(transparentColour)
            if currentIntensity < transparentIntensity:
                newIntensity = min(transparentIntensity,
                                   currentIntensity + fadeStep)
            elif currentIntensity > transparentIntensity:
                newIntensity = max(transparentIntensity, 
                                currentIntensity - fadeStep)
            else:
                newIntensity = transparentIntensity
            newColour.append(newIntensity)
        return wx.Colour(*newColour)


class ListCtrl(FadeMixin, wx.ListCtrl):
    pass


class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)
        self.list = ListCtrl(self, style=wx.LC_REPORT)
        self.list.InsertColumn(0, 'Column 0')
        self.list.InsertColumn(1, 'Column 1')
        self.fillList()
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onSelected)
        self.Bind(wx.EVT_LIST_DELETE_ITEM, self.onDeleted)

    def onSelected(self, event):
        self.list.DeleteItem(event.GetIndex())

    def onDeleted(self, event):
        if self.list.GetItemCount() == 1:
            wx.CallAfter(self.fillList, False)

    def fillList(self, firstTime=True):
        for row in range(10):
            self.list.InsertStringItem(row, 'Item %d, Column 0'%row)
            self.list.SetStringItem(row, 1, 'Item %d, Column 1'%row)
        self.list.SetItemBackgroundColour(1, wx.BLUE)
        self.list.SetItemTextColour(2, wx.BLUE)
        self.list.SetItemBackgroundColour(3, wx.GREEN)
        self.list.SetItemTextColour(4, wx.GREEN)
        self.list.SetItemBackgroundColour(5, wx.RED)
        self.list.SetItemTextColour(6, wx.RED)
        self.list.SetItemBackgroundColour(7, wx.BLACK)
        self.list.SetItemTextColour(7, wx.WHITE)
        self.list.SetItemBackgroundColour(8, wx.WHITE)
        self.list.SetItemTextColour(8, wx.BLACK)
        if not firstTime:
            self.list.SetBackgroundColour(wx.BLUE)


app = wx.App(False)
frame = Frame(None, title='Select an item to fade it out')
frame.Show()
app.MainLoop()
Frank Niessink