Psionman Psionman - 3 months ago 15
Linux Question

wx python image refresh on Windows

I have an application that involves displaying multiple images. This works as I would expect on linux, but on Windows there is an annoying flash as the images are painted. This is best seen as a little square in the top left-hand corner of the screen where a flash of colour appears. Am I not approaching this requirement in the right way? Or is there some fix I should be applying to overcome the Windows effect? Or is it just my version on Windows (I only have one to test it: Windows 7 Ultimate)?

I have tried Freeze and Thaw in refresh_sizer_cell but it didn't behave as I expected

import wx


class ImageSizer(wx.Frame):
BACKGROUND_COLOUR = (246, 244, 242)
def __init__(self, parent, title):
super(ImageSizer, self).__init__(parent, title=title)

self.main_sizer = wx.GridBagSizer()
self.SetSizer(self.main_sizer)

cmd_reset = wx.Button(self, label='Reset')
cmd_reset.Bind(wx.EVT_BUTTON, self.on_cmd_reset_click)

cmd_cancel = wx.Button(self, label='Cancel')
cmd_cancel.Bind(wx.EVT_BUTTON, self.on_cmd_cancel_click)

self.main_sizer.Add((400, 0), pos=(0, 0), span=(1, 2)) # dummy to position Available
self.main_sizer.Add((0, 100), pos=(1, 0), span=(1, 1)) # dummy to position Buttons
self.main_sizer.Add(cmd_reset, pos=(2, 2), flag=wx.LEFT | wx.TOP, border=10)
self.main_sizer.Add(cmd_cancel, pos=(2, 3), flag=wx.RIGHT | wx.BOTTOM | wx.TOP | wx.ALIGN_RIGHT, border=10)

self.SetBackgroundColour(self.BACKGROUND_COLOUR)
self.shape_types = {'available': 0, 'selected': 1}
self.available_shapes = []
self.selected_shapes = []
self.initialise()
self.Center()
self.Fit()
self.Show()

def initialise(self):
self.available_shapes = ['square', 'circle', 'triangle', 'cross']
self.selected_shapes = []
self.display_images()

def display_images(self):
available_sizer = ShapeSizer(self, self.available_shapes, self.shape_types['available'])
self.refresh_sizer_cell(self.main_sizer, available_sizer, (1, 2), (1, 3))
selected_sizer = ShapeSizer(self, self.selected_shapes, self.shape_types['selected'])
self.refresh_sizer_cell(self.main_sizer, selected_sizer, (1, 1), (2, 1))
self.Layout()

@staticmethod
def refresh_sizer_cell(sizer, item, pos, span, flag=wx.ALL, border=10):
old_item = sizer.FindItemAtPosition(pos)
if old_item is not None and old_item.IsWindow():
old_item.GetWindow().Hide()
sizer.Detach(old_item.GetWindow())
sizer.Add(item, pos=pos, span=span, flag=flag, border=border)

def on_available_shape_double_click(self, event):
shape = event.GetEventObject().GetName()
self.available_shapes.remove(shape)
self.selected_shapes.append(shape)
self.display_images()

def on_selected_shape_double_click(self, event):
shape = event.GetEventObject().GetName()
self.selected_shapes.remove(shape)
self.available_shapes.append(shape)
self.display_images()

def on_cmd_reset_click(self, event):
self.initialise()

def on_cmd_cancel_click(self, event):
self.Destroy()


class ShapeSizer(wx.Panel):
def __init__(self, parent, shapes, shape_type):
wx.Panel.__init__(self, parent, id = wx.ID_ANY)

if shape_type == parent.shape_types['available']:
size = 40
action = parent.on_available_shape_double_click
else:
size = 80
action = parent.on_selected_shape_double_click
panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
shapes.sort()
for shape in shapes:
bitmap = wx.Bitmap(shape + '.png', wx.BITMAP_TYPE_PNG)
bitmap = self.scale_bitmap(bitmap, size, size)
img = wx.StaticBitmap(self, wx.ID_ANY, bitmap, name=shape)
img.Bind(wx.EVT_LEFT_DCLICK, action)
panel_sizer.Add(img, flag=wx.RIGHT, border=10)
self.SetSizer(panel_sizer)

@staticmethod
def scale_bitmap(bitmap, width, height):
image = wx.ImageFromBitmap(bitmap)
image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
result = wx.BitmapFromImage(image)
return result

if __name__ == '__main__':
app = wx.App()
ImageSizer(None, title='Image Sizer')
app.MainLoop()


Here are the images:

circle.png

cross.png

square.png

triangle.png

Answer

Every time you double click on a shape your program is creating new instances of the panels and their wx.StaticBitmap widgets, it is these new instances you are seeing as they are initially created with a small default size and then they are repositioned by the next layout. Instead you should reorganize things so you only create the set of panels once, and as the state of the shape selections changes you can have the existing panels update themselves. That will greatly reduce the flicker visible to the user.

Comments