How do I create a wxPython Frame with OpenCV webcam video and other wx components (within the same frame/panel)?

Viewed 703

I am trying to use wxPython for a GUI that has one half being OpenCV webcam video, and the other half being other components from wxPython (buttons, textboxes, etc.).

I have been able to get an OpenCV window into my wxPython frame with this code:

import wx
import cv2

vs = cv2.VideoCapture(0)

class ImagePanel(wx.Panel):

    def __init__(self, parent, fps=15):
        super().__init__(parent)

        ret, frame = vs.read()
        frame = cv2.flip(frame, 1)
        height, width = frame.shape[:2]
        parent.SetSize((width, height))
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        self.bmp = wx.Bitmap.FromBuffer(width, height, frame)

        self.timer = wx.Timer(self)
        self.timer.Start(1000. / fps)

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

    def OnPaint(self, evt):
        dc = wx.BufferedPaintDC(self)
        dc.DrawBitmap(self.bmp, 0, 0)

    def NextFrame(self, event):
        ret, frame = vs.read()
        if ret:
            frame = cv2.flip(frame, 1)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.bmp.CopyFromBuffer(frame)
            self.Refresh()


class MainFrame(wx.Frame):

    def __init__(self):
        super().__init__(None, title='User Capture')
        self.vs = vs
        ImagePanel(self)
        self.Show()


if __name__ == '__main__':
    app = wx.App(redirect=False)
    frame = MainFrame()
    app.MainLoop()

Which results in this enter image description here

I then tried to create a main panel that will hold the OpenCV panel and other components (right now a button)

import wx
import cv2
import threading

vs = cv2.VideoCapture(0)

class ImagePanel(wx.Panel):

    def __init__(self, parent, fps=15):
        super().__init__(parent)

        ret, frame = vs.read()
        frame = cv2.flip(frame, 1)
        height, width = frame.shape[:2]
        parent.SetSize((width, height))
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        self.bmp = wx.Bitmap.FromBuffer(width, height, frame)

        self.timer = wx.Timer(self)
        self.timer.Start(1000. / fps)

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

    def OnPaint(self, evt):
        dc = wx.BufferedPaintDC(self)
        dc.DrawBitmap(self.bmp, 0, 0)

    def NextFrame(self, event):
        ret, frame = vs.read()
        if ret:
            frame = cv2.flip(frame, 1)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            self.bmp.CopyFromBuffer(frame)
            self.Refresh()


class MainPanel(wx.Panel):

    def __init__(self, frame):
        wx.Panel.__init__(self, frame)
        main_sizer = wx.BoxSizer(wx.VERTICAL)

        button_sizer = self._button_sizer(frame)
        img = ImagePanel(self)
        main_sizer.Add(img)
        main_sizer.Add(button_sizer)
        self.SetSizer(main_sizer)
        self.Show()

    def _button_sizer(self, frame):
        snap = wx.Button(self, label='Snap')
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(snap)
        button_sizer.Add((-1, -1), proportion=1)
        return button_sizer


class MainFrame(wx.Frame):

    def __init__(self):
        super().__init__(None, title='User Capture')
        self.vs = vs
        MainPanel(self)
        self.Show()


if __name__ == '__main__':
    app = wx.App(redirect=False)
    frame = MainFrame()
    app.MainLoop()

Which results in this:

enter image description here

I can say that my implementation of wxPython is clearly not good, so I need some help on how to do this properly

1 Answers

You are already as close as damn it.
Try not to use frame as parent or any other word that looks like a reserved word.
It's not wrong, just confusing.
Here's your edited code. (note: there are many ways to skin a cat)

import wx

class ImagePanel(wx.Panel):

    def __init__(self, parent, fps=15):
        super().__init__(parent)

        self.bmp = wx.StaticBitmap(self, -1, wx.Bitmap("frame1.png"))

class MainPanel(wx.Panel):

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        img = ImagePanel(self)
        snap = wx.Button(self, -1, label='Snap')

        main_sizer.Add(img, 1, wx.EXPAND, 0)
        button_sizer.Add(snap)
        main_sizer.Add(button_sizer)
        self.SetSizer(main_sizer)
        snap.Bind(wx.EVT_BUTTON, self.OnButton)

    def OnButton(self, event):
        print("Button Pressed")

class MainFrame(wx.Frame):

    def __init__(self):
        super().__init__(None, title='User Capture')
        MainPanel(self)
        self.Show()

if __name__ == '__main__':
    app = wx.App(redirect=False)
    frame = MainFrame()
    app.MainLoop()

enter image description here

Related