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?
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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With