[wxPython-users] Creating and placing images/graphics on a panel

332 views
Skip to first unread message

WinCrazy

unread,
Apr 22, 2010, 12:45:42 PM4/22/10
to wxPython-users
I'm creating a graphic, later to be a photo, and placing it onto a
panel. While this works fine in itself, there are annoying artifacts
during subsequent frame resizings. A wx.StaticBitmap is created to
hold the image.

Would creating a custom bitmapped button or frame eliminate the
"ghosts" of all the old images ? Is there a "prescribed" technique ?

(running 32-bit python/Pil/wxPython on 64-bit Win7)
==================================================

import wx
import Image # rectangle image creation
import random # rectangle colors

#--------------------------------------------------------------------

class MainFrame( wx.Frame ):

def __init__( self, parent, id, title ):

wx.Frame.__init__( self, parent,-4, title, size=(400, 400),
pos=(800, 100),
style=wx.DEFAULT_FRAME_STYLE )

self.panel = wx.Panel( self, -1 )
self.backgroundColor = NewRandomColor()
self.panel.SetBackgroundColour( self.backgroundColor )

self.currClientSizePrev = self.GetClientSize() # Init

self.Show( True )

#--------------------

# Overlay a bordered rectangle on the panel
# after a retriggerable one-shot timeout and
# after the last resize event. This avoids unecessary
redraws.
self.hasBeenResized = False
self.Bind( wx.EVT_SIZE, self.OnSize ) # this event only sets
a redraw flag

self.timer = wx.Timer( self ) # redraw only on
timeout and redraw flag is set
self.Bind (wx.EVT_TIMER, self.OnTimer )
self.timerInterval = 200 #
delay value chosen heuristically
self.timer.Start( self.timerInterval, oneShot=True ) #
Enable one-shot timer IRQ's

#end def __init__

#---------------------------------------------

def DisplayBorderedRectangle( self, size, color, pos ):

if ( (size[0] > 0) and (size[1] > 0) ) :

imgblank = Image.new( 'RGB', size )
margin = 2 # Leave a
black border
imgblank.paste( color, (margin, margin,
size[0]-margin, size[1]-margin ) )

imgdata = imgblank.tostring( 'raw', 'RGB' )
emptyimage = wx.EmptyImage( size[0], size[1] ) # An
image display container
emptyimage.SetData( imgdata ) # data
conversion
self.blankbitmap = emptyimage.ConvertToBitmap()

wx.StaticBitmap( self.panel, -1, self.blankbitmap,
size=size, pos=pos )

self.panel.Refresh()

#end if

#end def

#--------------------------------------------

def OnSize( self, event ):

currClientSize = self.GetClientSize()
if ( currClientSize != self.currClientSizePrev ) :

self.hasBeenResized = True
self.currClientSizePrev = currClientSize

self.timer.Start( self.timerInterval,
oneShot=True )

#end if

event.Skip( True )

#end def

#--------------------------------------------

def OnTimer( self, event ):

if self.hasBeenResized :

# Display a new graphic
currClientSize = self.GetClientSize()
currClientSizeX, currClientSizeY = (currClientSize[0],
currClientSize[1])

reductionBorderX = 30
reductionBorderX, reductionBorderY = (25, 25)

newImageSizeX = currClientSizeX - reductionBorderX
newImageSizeY = currClientSizeY - reductionBorderY
newImageSize = ( newImageSizeX, newImageSizeY )
newImagePos = ( reductionBorderX/2, reductionBorderY/2 )

newImageColor = NewRandomColor()

# "Erase" the viewable area
self.DisplayBorderedRectangle( currClientSize,
self.backgroundColor, (0, 0) )
# Paint the new graphic
self.DisplayBorderedRectangle( newImageSize,
newImageColor, newImagePos )

self.hasBeenResized = False # make ready for next
resize/repaint

#end if

self.timer.Start( self.timerInterval, oneShot=True )

event.Skip( True )

#end def

#end class MainFrame

#------------------------------------------------------------------------------

def NewRandomColor():

return (random.randint(0, 255), random.randint(0, 255),
random.randint(0, 255))

#end def

#==============================================================================

MyApp = wx.PySimpleApp( False ) # Send errors to command window, not
a pop-up.
MyFrame = MainFrame( None, -1, 'Image Fit' )
MyApp.MainLoop()
==================================================

--
To unsubscribe, send email to wxPython-user...@googlegroups.com
or visit http://groups.google.com/group/wxPython-users?hl=en

Mike Driscoll

unread,
Apr 22, 2010, 3:19:45 PM4/22/10
to wxpytho...@googlegroups.com


On Thu, Apr 22, 2010 at 11:45 AM, WinCrazy <pas...@verizon.net> wrote:
I'm creating a graphic, later to be a photo, and placing it onto a
panel. While this works fine in itself, there are annoying artifacts
during subsequent frame resizings. A wx.StaticBitmap is created to
hold the image.

Would creating a custom bitmapped button or frame eliminate the
"ghosts" of all the old images ?  Is there a "prescribed" technique ?

(running 32-bit python/Pil/wxPython on 64-bit Win7)

<code snipped>

I think you just need to call self.panel.Layout() after the resize is done (so put that near the end of OnTimer(). If the flickering is annoying, add a Freeze() and Thaw() around the resizing code to see if that helps.

--
-----------------
Mike Driscoll

Blog:   http://blog.pythonlibrary.org

WinCrazy

unread,
Apr 23, 2010, 1:31:58 PM4/23/10
to wxPython-users
Neither suggestions work, even when used together. Apparently, there
is a DC-like object that does not get cleared out. I made a
wx.BitmapButton to get a borderless frame around it. Even deleting and
recreating the panel+button changes nothing.

When resizing, all the previous bitmaps are redrawn in order which
makes the refresh longer as the number of user's resizes increase
( like a DC ).

? Is there actually a DC buried in the wxWidget code that can be
accessed in order to delete all it's drawing objects ?

If not, I will have to resort to destroying and then recreating the
whole frame to achieve what I think should be the expected operation.
This seems like an incredible waste of CPU cycles - imagine if the
main frame is very complicated !

Andrea Gavana

unread,
Apr 23, 2010, 3:26:46 PM4/23/10
to wxpytho...@googlegroups.com
Hi,

On 23 April 2010 18:31, WinCrazy wrote:
> Neither suggestions work, even when used together. Apparently, there
> is a DC-like object that does not get cleared out. I made a
> wx.BitmapButton to get a borderless frame around it. Even deleting and
> recreating the panel+button changes nothing.
>
> When resizing, all the previous bitmaps are redrawn in order which
> makes the refresh longer as the number of user's resizes increase
> ( like a DC ).
>
> ?   Is there actually a DC buried in the wxWidget code that can be
> accessed in order to delete all it's drawing objects ?
>
> If not, I will have to resort to destroying and then recreating the
> whole frame to achieve what I think should be the expected operation.
> This seems like an incredible waste of CPU cycles - imagine if the
> main frame is very complicated !

My suggestion would be not to use a wx.StaticBitmap or a
wx.BitmapButton, but to draw the image yourself in the control. As an
example (hiiiiighly untested):

class PaintMePanel(wx.Panel):

def __init__(self, parent, bitmap):

wx.Panel.__init__(self, parent)
self.bitmap = bitmap

self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)


def OnPaint(self, event):

dc = wx.BufferedPaintDC(self)
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()

dc.DrawBitmap(0, 0, self.bitmap)


def OnSize(self, event):

event.Skip()
self.Refresh()


def OnErase(self, event):

pass


Something along these lines. The code is highly approximated as I just
wrote it down on Gmail, but it should give you a start.

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/

==> Never *EVER* use RemovalGroup for your house removal. You'll
regret it forever.
http://thedoomedcity.blogspot.com/2010/03/removal-group-nightmare.html <==

Robin Dunn

unread,
Apr 23, 2010, 3:30:21 PM4/23/10
to wxpytho...@googlegroups.com
On 4/23/10 10:31 AM, WinCrazy wrote:
> Neither suggestions work, even when used together. Apparently, there
> is a DC-like object that does not get cleared out. I made a
> wx.BitmapButton to get a borderless frame around it. Even deleting and
> recreating the panel+button changes nothing.
>
> When resizing, all the previous bitmaps are redrawn in order which
> makes the refresh longer as the number of user's resizes increase
> ( like a DC ).
>
> ? Is there actually a DC buried in the wxWidget code that can be
> accessed in order to delete all it's drawing objects ?

No. wx.StaticBitmap is not a "drawing object" it is a window or widget.
It sounds like you are creating a new wx.StaticBitmap (or is it
wx.BitmapButton now?) for each new image you want to display, and so
what you are seeing is each of them repainting itself. Don't do that.
Just create one and call its SetBitmap method when you want to display a
new image.

--
Robin Dunn
Software Craftsman
http://wxPython.org

WinCrazy

unread,
Apr 26, 2010, 10:46:13 AM4/26/10
to wxPython-users
The optimal solution I found was to use a wx.ClientDC. The very few DC
examples I found used a DC in the wrong ways. Only derived classes of
wx.DC should ever be used according to the documentation.

----------------------------------------------------------------------------
On Apr 23, 3:30 pm, Robin Dunn <ro...@alldunn.com> wrote:
> On 4/23/10 10:31 AM, WinCrazy wrote:
>
> > Neither suggestions work, even when used together. Apparently, there
> > is a DC-like object that does not get cleared out. I made a
> > wx.BitmapButton to get a borderless frame around it. Even deleting and
> > recreating the panel+button changes nothing.
>
> > When resizing, all the previous bitmaps are redrawn in order which
> > makes the refresh longer as the number of user's resizes increase
> > ( like a DC ).
>
> > ?   Is there actually a DC buried in the wxWidget code that can be
> > accessed in order to delete all it's drawing objects ?
>
> No.  wx.StaticBitmap is not a "drawing object" it is a window or widget.
>   It sounds like you are creating a new wx.StaticBitmap (or is it
> wx.BitmapButton now?) for each new image you want to display, and so
> what you are seeing is each of them repainting itself.  Don't do that.
> Just create one and call its SetBitmap method when you want to display a
> new image.
>
> --
> Robin Dunn
> Software Craftsmanhttp://wxPython.org
Reply all
Reply to author
Forward
0 new messages