Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fade in/out the background color of wxpython grid cell

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?

like image 370
Ryan Ginstrom Avatar asked Dec 29 '25 12:12

Ryan Ginstrom


2 Answers

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()
like image 50
Frank Niessink Avatar answered Jan 01 '26 03:01

Frank Niessink


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.

like image 33
Mike Driscoll Avatar answered Jan 01 '26 01:01

Mike Driscoll



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!