5

I've been having some problems with a program that I've been writing and would appreciate some help or input. For some background, I'm using Python 2.7 and wxPython in order to do a streaming webcam client. The client gets the images from the server in its own thread, and puts them into a Queue. The GUI thread then gets those images from the Queue and converts them to a wxBitmap object. This happens every .5 seconds and works just great. I am able to save the wxBitmap object as a file so I know that everything is working properly.

The problem that I'm having is actually getting the wxBitmap object to show up on my GUI. The only thing I seem to be able to make the GUI do is display a gray rectangle where the web cam image should be.

Here is my onPaint() that is called when I want to refresh the screen:

    def onPaint(self,e):
            ## this is the function that actually draws and redraws the window
            ## to be displayed. I think it is something similar to blit()
            ## in other graphical display frameworks
            print "in onPaint"

            ## create the device context object (graphics painter)
            dc = wx.PaintDC(self)
            dc.BeginDrawing()

            ## draw the bitmap to the screen
            dc.DrawBitmap(self.imageBit,0,0,True)
            dc.EndDrawing()            

            ## test code.
            ## the following works and updates, which means that
            ## everything is being converted properly and updated.
            ## not sure why the dc won't paint it to the window. 
            self.imageBit.SaveFile("bit.bmp", wx.BITMAP_TYPE_BMP)

Simply put, I'm at a loss as to why its not working. from my research I've found that because I'm on a windows machine I needed the BeginDrawing() and EndDrawing() functions, so I added them. Still doesn't work. There are no errors or exceptions being thrown.

other questions that might help solve this issue:

  • I'm updating a wxFrame object. Maybe the wxPaintDC needs to operate in another type of container to work?
  • ?

Actually, maybe my __init__ function is whats holding the problem. Am I setting this up properly?

class viewWindow(wx.Frame):
    imgSizer = (480,360)
    def __init__(self, *args, **kw):
            ## this is called when an instance of this class is created
            super(viewWindow,self).__init__(*args,**kw)

            ## here is where the actual stuff inside the frame is set up.

            self.pnl = wx.Panel(self)

            ## create a button that opens up a Connection Window
            #test = wx.Button(self.pnl, label='Connection Settings')
            ## test.Bind(wx.EVT_BUTTON, self.openConnectionWindow)

            ## create the wxImage for the web cam pic
            self.image = wx.EmptyImage(self.imgSizer[0],self.imgSizer[1])

            ## create the wxBitmap so that the wxImage can be displayed
            self.imageBit = wx.BitmapFromImage(self.image)

            ## create a timer that will update the window based of frame rate
            self.timex = wx.Timer(self, wx.ID_OK)
            self.timex.Start(500)
            self.Bind(wx.EVT_TIMER, self.redraw, self.timex)

            ## need to do the following in order to display images in wxPython:
            self.Bind(wx.EVT_PAINT, self.onPaint)

            self.SetSize(self.imgSizer)
            self.SetTitle('View Window')
            self.Show()

Anyways, thanks in advance for your help.

EDIT: I solved the problem accidentally by deleting the line self.pnl = wx.Panel(self).

So apparently it was rendering properly, but the bitmap was underneath the panel. Maybe? I'm not really sure. I'm new to this whole wxPython thing.

Santosh Kumar
  • 26,475
  • 20
  • 67
  • 118
user1626536
  • 793
  • 1
  • 6
  • 14
  • 1
    The image appearing underneath the panel is a possibility. Unless you explicitly set the position of use a `wx.Sizer` the objects default to (0,0). That's why I use a sizer even if I only have 1 item. – acattle Aug 27 '12 at 02:59

3 Answers3

1

That appears to be what the wxPython demo is doing too: dc.DrawBitmap. And it works on Windows! At least, that's what they do in the AlphaDrawing demo. In the DrawImage demo, they use dc.Blit(). You might try that.

However, I wonder if you couldn't do it like I did with my photo viewer. I don't use DCs to draw, but instead just use a wx.StaticBitmap that I update.

Mike Driscoll
  • 32,629
  • 8
  • 45
  • 88
  • I had actually looked at your code for the photo viewer before I asked this question. I had looked at it an couldn't get it to work. There is probably some flaw in my understanding of how wxPython renders things to the screen, and I think it has to do with how wx.App, wx.Frame, and wx.Panel all work together. I notice that your code for the photo viewer is a wx.Sizer and wx.Widgets inside of a wx.Panel, which is inside of a wx.Frame, which is inside of a wx.App, while mine is merely a wx.Frame and a dc object. I'm going to try to restructure my code to be similiar to yours and get back to you. – user1626536 Aug 27 '12 at 15:05
  • Alright. It would be good to see a small runnable example. That would help tremendously. – Mike Driscoll Aug 27 '12 at 16:19
  • here is my new code that works. its based on the example from your blog, the image viewer. I'm happy that it works, but I'm not happy at the flicker every time it refreshes. Oh well. Solve one bug and create another I guess. heres the code: edit: too many characters to leave as a comment. http://pastebin.com/WwHhTUAQ – user1626536 Aug 27 '12 at 23:16
  • Sadly I can't look at pastebins at work. You might be able to reduce flicker using the Freeze and Thaw methods though. – Mike Driscoll Aug 28 '12 at 13:26
1

This code works. It displays the images every time and all that. It does tend to 'flicker', though. So there is probably a better way of doing this that I'm not aware of.

class viewWindow(wx.Frame):
    imgSizer = (480,360)
    def __init__(self, parent, title="View Window"):
            super(viewWindow,self).__init__(parent)
            ## create the menu and its sub trees
            menubar = wx.MenuBar()
            filemenu = wx.Menu()
            menubar.Append(filemenu, 'File')
            self.fitem = filemenu.Append(wx.ID_ANY, 'Open Connection Window')
            self.Bind(wx.EVT_MENU, self.openConnectionWindow, self.fitem)
            self.SetMenuBar(menubar)

            ## here is where the actual stuff inside the frame is set up.
            self.pnl = wx.Panel(self)
            self.vbox = wx.BoxSizer(wx.VERTICAL)

            ## create the wxImage for the web cam pic
            self.image = wx.EmptyImage(self.imgSizer[0],self.imgSizer[1])

            ## create the wxBitmap so that the wxImage can be displayed
            self.imageBit = wx.BitmapFromImage(self.image)
            self.staticBit = wx.StaticBitmap(self.pnl,wx.ID_ANY, self.imageBit)

            ## add the staticBit to the sizer so it is rendered properly on resizes and such
            ## note: not actually needed to get the image to display, but reccommended for ease
            ## of layout
            self.vbox.Add(self.staticBit)

            ## register the sizer with the panel so the panel knows to use it.
            self.pnl.SetSizer(self.vbox)

            ## create a timer that will update the window based on frame rate
            self.timex = wx.Timer(self, wx.ID_OK)
            self.timex.Start(1000/framerate)
            self.Bind(wx.EVT_TIMER, self.redraw, self.timex)

            ## set the size of the frame itself when it is first opened
            self.SetSize(self.imgSizer)
            self.Show()

    def openConnectionWindow(self, e):
            ## this will open a new connection window
            connect = connectionWindow(None)

    def redraw(self,e):
            ## this function updates the frame with the latest web cam image that has been
            ## retrieved by the client thread from the server.

            ## get the newest image in the queue 
            if not imgQ.empty():                        
                    picz = imgQ.get()
                    ## convert the image from a string to something usable (wxImage)
                    self.image.SetData(picz)
                    ## from wxImage to wxBitmap
                    self.imageBit = wx.BitmapFromImage(self.image)
                    self.staticBit = wx.StaticBitmap(self.pnl,wx.ID_ANY, self.imageBit)
                    ## refresh the frame
                    self.Refresh()
user1626536
  • 793
  • 1
  • 6
  • 14
0

Several hours later, and researching a different question I had, I find this:

How to detect motion between two PIL images? (wxPython webcam integration example included)

That has example code in it that works beautifully.

Community
  • 1
  • 1
user1626536
  • 793
  • 1
  • 6
  • 14