OpenCV and PyQt5 window position



  • Hey all, I'm trying to position my camera feed window in the same window of my application, specifically inside the camera frame widget. Currently the camera feed opens in a separate window.

    My question is how would I go about positioning the camera feed in the same window?
    Do your best to ignore some of the variable names as i've scraped some things out from current project.

    # -*- coding: utf-8 -*-
    
    # Form implementation generated from reading ui file 'camera.ui'
    #
    # Created by: PyQt5 UI code generator 5.13.0
    #
    # WARNING! All changes made in this file will be lost!
    
    import cv2
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class Capture():
        def __init__(self):
            self.capturing = False
            self.c = cv2.VideoCapture(0) #where camera feed is pulled from
    
        def startCapture(self):
            print ("pressed start")
            self.capturing = True
            cap = self.c
            while(self.capturing):
                ret, frame = cap.read()
                cv2.imshow("Capture", frame)
                cv2.waitKey(5)
            cv2.destroyAllWindows()
            
        def endCapture(self):
            print ("pressed End")
            self.capturing = False
    
    class Ui_Camera(object):
        def setupUi(self, Camera):
            self.capture = Capture()
            Camera.setObjectName("Camera")
            Camera.resize(1099, 623)
            sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(Camera.sizePolicy().hasHeightForWidth())
            Camera.setSizePolicy(sizePolicy)
            self.centralwidget = QtWidgets.QWidget(Camera)
            self.centralwidget.setObjectName("centralwidget")
            self.ControlBox = QtWidgets.QGroupBox(self.centralwidget)
            self.ControlBox.setGeometry(QtCore.QRect(590, 20, 491, 571))
            self.ControlBox.setTitle("")
            self.ControlBox.setObjectName("ControlBox")
            self.groupBox_4 = QtWidgets.QGroupBox(self.ControlBox)
            self.groupBox_4.setGeometry(QtCore.QRect(10, 20, 231, 251))
            self.groupBox_4.setAlignment(QtCore.Qt.AlignCenter)
            self.groupBox_4.setObjectName("groupBox_4")
            self.zoom_out = QtWidgets.QPushButton(self.groupBox_4)
            self.zoom_out.setGeometry(QtCore.QRect(20, 70, 75, 23))
            self.zoom_out.setObjectName("zoom_out")
            self.zoom_out.clicked.connect(self.capture.endCapture)
            self.Camera_start = QtWidgets.QPushButton(self.groupBox_4)
            self.Camera_start.setGeometry(QtCore.QRect(20, 20, 75, 23))
            self.Camera_start.setObjectName("Camera_start")
            self.Camera_start.clicked.connect(self.capture.startCapture)
            
            self.groupBox_6 = QtWidgets.QGroupBox(self.centralwidget)
            self.groupBox_6.setGeometry(QtCore.QRect(10, 20, 571, 571))
            self.groupBox_6.setAlignment(QtCore.Qt.AlignCenter)
            self.groupBox_6.setObjectName("groupBox_6")
            self.cameraFrame = QtWidgets.QFrame(self.groupBox_6)
            self.cameraFrame.setGeometry(QtCore.QRect(10, 30, 551, 521))
            self.cameraFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
            self.cameraFrame.setFrameShadow(QtWidgets.QFrame.Raised)
            self.cameraFrame.setObjectName("cameraFrame")
            Camera.setCentralWidget(self.centralwidget)
            self.statusbar = QtWidgets.QStatusBar(Camera)
            self.statusbar.setObjectName("statusbar")
            Camera.setStatusBar(self.statusbar)
    
            self.retranslateUi(Camera)
            QtCore.QMetaObject.connectSlotsByName(Camera)
    
        def retranslateUi(self, Camera):
            _translate = QtCore.QCoreApplication.translate
            Camera.setWindowTitle(_translate("Camera", "Camera"))
            self.groupBox_4.setTitle(_translate("Camera", "Camera"))
            self.zoom_out.setText(_translate("Camera", "Stop"))
            self.Camera_start.setText(_translate("Camera", "Start"))
            self.groupBox_6.setTitle(_translate("Camera", "Camera Feed"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        Camera = QtWidgets.QMainWindow()
        ui = Ui_Camera()
        ui.setupUi(Camera)
        Camera.show()
        sys.exit(app.exec_())
    
    
    


  • If your restructured things slightly could spin a DockWidget off your QMainWindow for each of the Cameras you have. But not fully understanding what you want where I am not sure exactly how to advise you on that but let us say that the large "Camera Feed" box is where the active video goes and the smaller "Camera" box with the Start/Stop buttons are your various mini-camera feeds would reside.

    I would design the Main Window with the Camera Feed as your Central Panel and then create the "Camera Stop/Start Mini-Vid" as Dockwidgets that way you could spawn numerous Cameras and even undock the mini-windows and such.

    On the flip-side if you have more details of what you are striving to create perhaps I can whip up a framework for you to help you out and get you away from using that extremely ugly Designer junk you are using ;-)



  • Great! honestly not a fan of the Designer more so because I have not quite taken the time to fully understand it, basically what i'm trying to accomplish is just a user interface that displays an openCV stream while also having buttons on the side within the same window. My end goal is to also have this scale-able to full screen but that can come later.

    I didn't make it clear that there is only one camera, back to my main post I just need the camera stream to stay within the camera feed frame when I start it. but as of right now it creates a separate window with the camera feed.

    Heres an example of how it looks when start the camera feed:

    https://imgur.com/a/wbFmLVn

    As for the "camera" tab on the right with the butons its there for controls to the camera feed



  • Okay I could not acquire this CV2 module but I have created the basic framework (and a bit more) for implementing your Camera Feed -- assuming your class works as I imagined it might. Also as I have denoted within the code -- you are most likely going to either launch a multiprocess or thread in order to do this and have the window remain responsive to user input as it stands once the camera feed starts the entire program would be locked down until forcefully abended. So if you have any questions on what I have outlined and/or on how to implement a thread I will be more than happy to help you out. If you have Discord available I have a server message where I tutor folks a bit more interactively and I can share that with you.

    # import cv2
    from sys import exit as sysExit
    
    # from PyQt5.QtCore    import ??
    # from PyQt5.QtGui     import ??
    # Container Widgets
    from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDockWidget
    from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QGroupBox
    # Action Widgets
    from PyQt5.QtWidgets import QPushButton, QAction, QStyleFactory, QTextEdit
    
    # Note I am including your class as is for the most part but it has
    # a major issue and that is once the Camera Feed is started it will
    # block anything else from occurring including stopping the camera
    # feed.  You will have to either implement a multiprocess or a 
    # thread to do this -- as it is I have given you a framework to 
    # plug your code into if you just want to see it work which is 
    # always a plus as it lets you know you are at least on the 
    # right track
    class Capture(QWidget):
        def __init__(self):
            QWidget.__init__(self)
            self.CapMode = False
          # Where the camera feed is pulled from  
            self.CamFeed = QTextEdit()     # cv2.VideoCapture(0)
            
            hbxCamContnt = QHBoxLayout()
            hbxCamContnt.addWidget(self.CamFeed)
            self.setLayout(hbxCamContnt)
    
        def StartCapture(self):
            print("Started Camera Feed")
            if self.CapMode:
                pass
            else:
                self.CapMode = True
                self.CamFeed.setText('Camera Feed Started and Displaying Here')
    #            while(self.CapMode):
    #                ret, frame = self.CamFeed.read()
    #                cv2.imshow("Capture", frame)
    #                cv2.waitKey(5)
    #            cv2.destroyAllWindows()
            
        def StopCapture(self):
            print ("Stopped Camera Feed")
            self.CapMode = False
            self.CamFeed.clear()
    
    # This can be removed from the main program and imported if desired aka
    # it could be turned into a simple swapable Database module based on an
    # import statement.  This allows for using different databases easily
    class MyDatabase:
        # This would handle everything pertinent to a Database
        # If not ever going to be used then this could be hard-coded
        # into the Main program - framework added for templating
        def __init__(self, SourceDbase):
            self.FullPathDbName = SourceDbase
    
          # Establish Full Path to Database
          # More aspects should be checked if a source is
          # supplied but I leave that to you if you even
          # decide to implement a database
            if not len(self.FullPathDbName):
                self.FullPathDbName = self.SetDatabase()
    
        def GetToolBarLayout(self):
           # This simulates reading this data in from a database
           # how that data is actually returned might be a bit 
           # different but that could either be handled here or
           # altered in the calling Class
            ToolBarLayout = {0:'StartAct',
                             1:'StopAct'}
    
            return ToolBarLayout
    
        @staticmethod
        def SetDatabase():
            filter  = "sql(*.sql)"
            caption = "Please Select a Valid Project File"
          # ... more code here implementing a file dialog lookup
            filePathName = 'FullPathAndNameToDatabaseToBeUsed'
    
            return filePathName
    
    # This could now be removed from the main program and imported if desired
    # aka it could be turned into a simple swapable MenuToolBar based on an
    # import statement or a copy/paste as well.  Also note everything found in 
    # a Toolbar comes from the Menu but not everything in the Menu is always 
    # found in the Toolbar this is why the Toolbar is a subset of the Menu here
    class MenuToolBar(QDockWidget):
        def __init__(self, parent):
            QDockWidget.__init__(self)
            self.Parent = parent
            self.MainMenu = parent.menuBar()
    
          # This is used to have a handle to the Menu Items
          # should you implement a Tool Bar
            self.MenuActRef = {'Start':0,
                               'Stop':0}
    
            # ******* Create the Camera Menu *******
            self.CameraMenu  = self.MainMenu.addMenu('Camera')
    
            # ******* Create Camera Menu Items *******
            self.StartAct = QAction('&Start', self)
          # In case you have or want to include an Icon
          #  self.StartAct = QAction(QIcon('Images/start.ico'), '&Start', self)
            self.StartAct.setShortcut("Ctrl+S")
            self.StartAct.setStatusTip('Start the Camera Feed')
            self.StartAct.triggered.connect(self.StartCamera)
            self.MenuActRef['StartAct'] = self.StartAct
    
            self.StopAct = QAction('Stop', self)
          #  self.ResetAct = QAction(QIcon('Images/stop.ico'), 'Stop', self)
            self.StopAct.setShortcut("Ctrl+Q")
            self.StopAct.setStatusTip('Stop the Camera Feed')
            self.StopAct.triggered.connect(self.StopCamera)
            self.MenuActRef['StopAct'] = self.StopAct
    
            # ******* Setup the Camera Menu *******
            self.CameraMenu.addAction(self.StartAct)
            self.CameraMenu.addSeparator()
            self.CameraMenu.addAction(self.StopAct)
    
            self.InitToolBar()
    
        def InitToolBar(self):
            # Add Items to the Toolbar
            # This is done so that the Toolbar can be handled dynamically
            # Allowing the user to adjust what appears on it and such
            # although this has not been implemented in this version just
            # setup to allow for it should it be desired later but if you
            # use the icons accordingly the buttons look rather nice and
            # are located at the top outside the center pane
            self.mainToolBar = self.Parent.addToolBar("Quick Access")
    
            ToolBarLayout = self.Parent.GetToolBarLayout()
    
            for idx in ToolBarLayout:
                item = ToolBarLayout[idx]
    
                if item == 'Spacer':
                    self.mainToolBar.addSeparator()
                
                else:
                    self.mainToolBar.addAction(self.MenuActRef[item])  
    
    # These are the Menu/Tool Bar Actions
        def StartCamera(self):
            self.Parent.StartCamera()
    
        def StopCamera(self):
            self.Parent.StopCamera()
     
     # Again if need be this can be made swappable should a different 
     # center panel be desired for a program by pulling this out and
     # importing it as a separate module
    class CentralPanel(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
            self.Toggle = True
    
            self.btnStrt = QPushButton()
            self.btnStrt.setText('Start')
            self.btnStrt.clicked.connect(self.StartCamera)
    
            self.btnStop = QPushButton()
            self.btnStop.setText('Stop')
            self.btnStop.clicked.connect(self.StopCamera)
    
            vbxComnds = QVBoxLayout()
            vbxComnds.addWidget(self.btnStrt)
            vbxComnds.addWidget(self.btnStop)
            vbxComnds.addStretch(1)
    
            gbxRiteVert = QGroupBox()
            gbxRiteVert.setTitle('Camera')
            gbxRiteVert.setLayout(vbxComnds)
    
            self.CameraFeed = Capture()
    
            hbxCamFeed = QHBoxLayout()
            hbxCamFeed.addWidget(self.CameraFeed)
            hbxCamFeed.addStretch(1)
    
            gbxLeftArea = QGroupBox()
            gbxLeftArea.setTitle('Camera Feed')
            gbxLeftArea.setLayout(hbxCamFeed)
            
            HBox = QHBoxLayout()
            HBox.addWidget(gbxLeftArea)
            HBox.addWidget(gbxRiteVert)
    
            self.setLayout(HBox)
    
        def setTextHelloWorld(self):
            if self.Toggle:
                self.Toggle = False
                self.lblFooBar.setText("Hello World")
            else:
                self.Toggle = True
                self.lblFooBar.setText("Narf!")
    
    # These are the same as the Menu/Tool Bar Actions
    # but if the Menu/Tool Bar is not desired then these
    # commands could be moved fully here and thus completely
    # encapsulated within your Center Panel Class
        def StartCamera(self):
            self.Parent.StartCamera()
    
        def StopCamera(self):
            self.Parent.StopCamera()
    
    # This is not the Main Gui as much as it is your Main Coordinator
    # and the Gui has been mostly passed off to and encapsulated
    # within the Central Panel Class
    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            self.title = 'Camera'
            self.setWindowTitle(self.title)
    
            left=100;top=100;width=1100;height=625
            self.setGeometry(left,top,width,height)
    
          # Establish Valid Database Object
            self.dbase = MyDatabase('')
    
          # A Window is made of of these regions from top to bottom: 
          # Menu Bar, Toolbar, Center Pane, and Status Bar
            self.CenterPane = CentralPanel(self)
            self.setCentralWidget(self.CenterPane)
    
          # Everything found in the Toolbar comes from the Menu but 
          # not everything in the Menu is found in the Toolbar
            self.MenuToolBar = MenuToolBar(self)
    
            self.SetStatusBar(self)
            self.setStyle(QStyleFactory.create('Cleanlooks'))
    
        def SetStatusBar(self, parent):
            StatusMsg = ''
            parent.StatBar = parent.statusBar()
    
            if len(StatusMsg) < 1:
                StatusMsg = 'Ready'
    
        def GetToolBarLayout(self):
          # Currently just a simply pass through method
            return self.dbase.GetToolBarLayout()
    
      # These are your Menu/Tool Bar and PushButton Actions
        def StartCamera(self):
            self.CenterPane.CameraFeed.StartCapture()
    
        def StopCamera(self):
            self.CenterPane.CameraFeed.StopCapture()
    
    if __name__ == '__main__':
      # Named this because this is its primary and perhaps sole purpose
        MainThred = QApplication([])
    
      # Named this because this is its primary purpose
        MainApp = MainWindow()
        MainApp.show()
    
        sysExit(MainThred.exec_())
    

Log in to reply