Hope you kind folk can spare a bit of advice. I'm trying to find a clever way to modifying a part of an image without replacing the image completely. For instance, let's say I have an image of a car and I would like to show the tire is flat. Instead of replacing the image completely with another, I would like to layer a new image of a flat tire image on top of current tire image. Eventually I would like to extend this to show multiple stats of the car, flat tires, ajar doors, headlight burnt out, etc etc. Since each of these stats can occur independently, just replacing one entire image for each combination of stats is crazy inefficient.

I've been using a bit of code that layers a bitmap (with a transparent background) on top another. What I'm not sure about is how to modify the layered images. Let's say I have 5 smaller images layered on top of the large car image, is there a way I can change the bitmap of one of the layered images in real time and delete those I no longer wish to display?

In the below code, I draw the "low_tire.png" image on top the "car.png". Then when the button is clicked I would like the "low_tire.png" to be changed with the "flat_tire.png" image. How would I accomplish this? And how would I delete the layered image completely?

Here's my code thus far:

import wx

class Frame1(wx.Frame):
    def __init__(self, prnt):
        wx.Frame.__init__(self, id=-1, name='', parent=prnt,
              pos=wx.Point(0, 0), size=wx.Size(600, 600),
              style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
        self.panel1 = BmpPanel(self)
        self.Show()

class BmpPanel(wx.Panel):
    def __init__(self, parent, id = -1):
        wx.Panel.__init__(self, parent, id)

        self.Bind(wx.EVT_PAINT, self.OnPaint)

        self.button = wx.Button(self, id, 'click to change layered image')
        self.Bind(wx.EVT_BUTTON, self.modTheImage, id=self.button.GetId())

        self.BackBmp = wx.Bitmap('car.png')
        self.FrontBmp = wx.Bitmap('low_tire.png')
        self.hiBmp = wx.Bitmap('door_ajar.png')

    def OnPaint(self, event):
        self.dc = wx.PaintDC(self)        
        self.Update()

    def Update(self):
        self.dc.DrawBitmap(self.BackBmp, 96, 72, True)
        self.dc.DrawBitmap(self.FrontBmp, 150, 110, True)
        
    def modTheImage(self, event):
        self.FrontBmp = wx.Bitmap('flat_tire.png') # ATTEMPT TO CHANCE IMAGE FILE (DOES NOT WORK)
        #self.Update
        #self.Refresh() # USING THIS SEEMS TO CRASH THE APPLICATION
    
App = wx.PySimpleApp()
application = Frame1(None)
application.Show()
App.MainLoop()

Thanks all!

Recommended Answers

All 7 Replies

Whoops, just found a quick typo. Should be:

def modTheImage(self, event):
        self.dc.Clear()
        self.FrontBmp = wx.Bitmap('flat_tire.png')
        self.Update() # Forgot the brackets! :)

which does indeed replace the image. Still looking for a way to delete an image, and of course any suggestions if anyone has any!

del self.FrontBmp does not function, you mean?

With image stuff. Try and refresh after update. This will show image nicely else the output of your image will differ from OS to OS and you dont need that do you ?
:)

Thanks folks. tonyjv, I believe del self.FrontBmp only delete the handle so self.FrontBmp cannot be used anymore. Doesn't actually delete the image. I think the easiest way would be to have a transparent image and just replace the image I wish to delete with this. Not the most elegant solution, but should work.

I've made some revision to the code (old code was very buggy) and I'm still stuck on a way to replace an image with a new one. So let's say my onPaint method looks like the following:

def OnPaint(self, evt):
    dc = wx.PaintDC(self)
    dc.Clear()
    dc.DrawBitmap(self.photo1, 0, 0, True)
    dc.DrawBitmap(self.photo2, 0, 0, True)

Then let's say I have a simple button and I bind a button event that
calls a function to replace the self.photo1 with another image.
Something like this:

self.button = wx.Button(self, -1, 'switch', pos=(0, 0))
self.Bind(wx.EVT_BUTTON, self.goSwitch, id=self.button.GetId())

How would I go about modifying the contents then refreshing the
display to observe the new change? I was thinking about something
like:

def goSwitch(self, event):
    self.img = wx.Image("anotherimage.png")
    self.photo1 = self.img.ConvertToBitmap()

But the new image is not replaced since wx.EVT_PAINT is not invoked
and onPaint() is not called. And I can't call
dc.DrawBitmap(self.photo2, 0, 0, True) since it's only available in
the onPaint() method. Any ideas how I can accomplish this? Here's the complete code:

import wx 
import win32api
import win32con

class ImagePlacementWindow(wx.Panel): 
    def __init__(self, parent): 
        wx.Panel.__init__(self, parent) 

        self.button = wx.Button(self, -1, 'switch', pos=(0, 0)) 
        self.Bind(wx.EVT_BUTTON, self.goSwitch, id=self.button.GetId())

        self.img = wx.Image("carSide2.png")
        self.img2 = wx.Image("carSide.png") 
        self.photo = self.img.ConvertToBitmap()
        self.photo2 = self.img2.ConvertToBitmap()  
            
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    # NEED HELP FIGURING WITH THIS FUNCTION
    def goSwitch(self, event):
        self.img = wx.Image("level0.png")
        self.photo2 = self.img.ConvertToBitmap()
        
    def OnPaint(self, evt): 
        dc = wx.PaintDC(self)
        brush = wx.Brush("#444444") 
        dc.SetBackground(brush) 
        dc.Clear()
        
        dc.DrawBitmap(self.photo2, 0, 0, True)
        dc.DrawBitmap(self.photo, 0, 0, True) 

class TestFrame(wx.Frame): 
    def __init__(self): 
        wx.Frame.__init__(self, None, title="Loading Images", size=(640,480))
        win = ImagePlacementWindow(self)

        def SetCompositeMode(self, on=True):
            exstyle = win32api.GetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE)
            if on: exstyle |= win32con.WS_EX_COMPOSITED
            else: exstyle &= ~win32con.WS_EX_COMPOSITED
            win32api.SetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE, exstyle) 
        SetCompositeMode(self, True)
        
app = wx.PySimpleApp() 
frm = TestFrame() 
frm.Show() 
app.MainLoop()

Thanks
again

Hmmm, okay here's my attempt at solving this issue (sorry I'm using
this thread as a personal notepad, but I hope this will help someone
with similar problems). I call doDrawing() when I need something
drawn, and I modify the contents of self.photo1 with the image I would
like to change to. How does this look to everyone? I'm just beginning
to learn to use DC, so I may have missed something import, but it
seems to work for this basic example. I can post the full code if
someone is interested.

def goSwitch(self, event):
        self.img = wx.Image("level0.png")
        self.photo1 = self.img.ConvertToBitmap()
        self.doDrawing()

    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        brush = wx.Brush("#444444")
        dc.SetBackground(brush)
        dc.Clear()
        self.doDrawing()

    def doDrawing(self):
        dc = wx.ClientDC(self)
        dc.DrawBitmap(self.photo1, 0, 0, True)
        dc.DrawBitmap(self.photo2, 0, 0, True)

I have not experience in wx, I work in Tkinter, but this does seem to work for me also:

import wx
import win32api
import win32con

class ImagePlacementWindow(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.count = 0
        self.button = wx.Button(self, -1, 'switch', pos=(0, 0))
        self.Bind(wx.EVT_BUTTON, self.goSwitch, id=self.button.GetId())

        self.current = self.img = wx.Image("ysisoft1.jpg")
        self.img2 = wx.Image("ysisoft2.jpg")

        self.Bind(wx.EVT_PAINT, self.OnPaint)

    # NEED HELP FIGURING WITH THIS FUNCTION
    def goSwitch(self, event):
        self.current = self.img if self.current != self.img else self.img2
        self.GetEventHandler().ProcessEvent(wx.PaintEvent( ))

    def OnPaint(self, evt):
        self.count+=1
        print self.count, self.current == self.img
        dc = wx.PaintDC(self)
        brush = wx.Brush("#444444")
        dc.SetBackground(brush)
        dc.DrawBitmap(self.current.ConvertToBitmap(), 100, 100, True)

class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Loading Images", size=(640,480))
        win = ImagePlacementWindow(self)

        def SetCompositeMode(self, on=True):
            exstyle = win32api.GetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE)
            if on: exstyle |= win32con.WS_EX_COMPOSITED
            else: exstyle &= ~win32con.WS_EX_COMPOSITED
            win32api.SetWindowLong(self.GetHandle(), win32con.GWL_EXSTYLE, exstyle)
        SetCompositeMode(self, True)

app = wx.PySimpleApp()
frm = TestFrame()
frm.Show()
app.MainLoop()

To do some transparent stuff worth looking into PIL module. combine PIL and wx as wx can not manipulate image well. It give very basic image manipulation features and that is all. wx was designed with GUI in mind. More on frames,windows, etc.

For example to write a GUI application to send and recieve msgs. You will need wx,socket,time... modules for more complex threading module also. For proper image manipulation wx and PILL module at least.

PIL will give you more including normal mixing and transformation animation style.
wx will not. mix the 2 modules else you can not achieve what you want.It very easy to use PIL
Very simple. ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.