Python Forum
[WxPython] Video in Gui
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[WxPython] Video in Gui
#1
I am trying to display streamed video in a GUI I created using PAGE. I can’t get the references to the Class to work. In line 82 I have: self.Frame1.CanvasZ.after(10, video_stream). Python says: AttributeError: 'Toplevel1' object has no attribute 'CanvasZ'. I’ve tried putting self.Frame1.CanvasZ, Frame1.CanvasZ, etc. but nothing works. The video stream code works fine in a standalone app, but doesn't in the class. I've also tried variations with Canvas.create_image (line 127).
Can someone telling me the secret of getting this to work?
#! /usr/bin/env python
#  -*- coding: utf-8 -*-
#
# GUI module generated by PAGE version 4.24.1
#  in conjunction with Tcl version 8.6
#    Aug 13, 2019 01:18:58 PM MDT  platform: Windows NT

import sys
import tkinter as tk
import tkinter.ttk as ttk
import carCtrl_support
import socket
import time
import cv2
from PIL import ImageTk, Image

# Capture from camera
cap = cv2.VideoCapture('http://192.168.1.1:8080/?action=stream')

def vp_start_gui():
    '''Starting point when module is the main routine.'''
    global val, w, root
    root = tk.Tk()
    carCtrl_support.set_Tk_var()

    top = Toplevel1 (root)
    carCtrl_support.init(root, top)
    root.mainloop()

w = None
def create_Toplevel1(root, *args, **kwargs):
    '''Starting point when module is imported by another program.'''
    global w, w_win, rt
    rt = root
    w = tk.Toplevel (root)
    carCtrl_support.set_Tk_var()
    top = Toplevel1 (w)
    carCtrl_support.init(w, top, *args, **kwargs)
    return (w, top)

def destroy_Toplevel1():
    global w
    w.destroy()
    w = None

class Toplevel1:

    def connect (self):
        """ Make a socket connection to the car and set the data
            timeout (timeout also forms the step duration)
        """
        global s

        if self.sockConnected == True:
            print("sockConnected True")
            s.shutdown(socket.SHUT_RDWR)
            s.close()
            self.connectBtn.configure(text='''Connect''')
            self.sockConnected = False
            print("sockConnected " + str(self.sockConnected))
        else:
            s = socket.socket()
            host = '192.168.1.1'
            port = 2001
            s.connect((host, port))
            data = s.recv(32)
            stringdata = data.decode('utf-8')
            print("Data: " + stringdata)
            s.settimeout(0.1)
            self.sockConnected = True
            self.connectBtn.configure(text='''Disconnect''')

    # function for video streaming
    def video_stream(self):
        _, frame = cap.read()
        cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
        img = Image.fromarray(cv2image)
        imgtk = ImageTk.PhotoImage(image=img)
        self.Frame1.CanvasZ.after(10, video_stream)


    def __init__(self, top=None):
        '''This class configures and populates the toplevel window.
           top is the toplevel containing window.'''
        _bgcolor = '#d9d9d9'  # X11 color: 'gray85'
        _fgcolor = '#000000'  # X11 color: 'black'
        _compcolor = '#d9d9d9' # X11 color: 'gray85'
        _ana1color = '#d9d9d9' # X11 color: 'gray85'
        _ana2color = '#ececec' # Closest X11 color: 'gray92'
        font9 = "-family {Segoe UI} -size 9 -weight normal -slant "  \
            "roman -underline 0 -overstrike 0"
        swVer = "0.1"
        self.sockConnected = False

        top.geometry("583x496+2112+204")
        top.title("Video" + swVer)
        top.configure(background="#d9d9d9")
        top.configure(highlightbackground="#d9d9d9")
        top.configure(highlightcolor="black")

        self.Frame1 = tk.Frame(top)
        self.Frame1.place(relx=0.189, rely=0.081, height=280, width=360)
        self.Frame1.configure(relief='groove')
        self.Frame1.configure(borderwidth="2")
        self.Frame1.configure(relief="groove")
        self.Frame1.configure(background="white")
        self.Frame1.configure(width=385)
        self.Frame1.configure(command = self.video_stream())

        #self.LMain = tk.Label(top)
        #self.LMain.place(relx=0.2, rely=0.2)
        #self.LMain.pack(padx=5, pady=10, side="left")

        self.CanvasZ = tk.Canvas(self.Frame1)
        self.CanvasZ.place(relx=0.05, rely=0.05, height=240, width=320)
        self.CanvasZ.configure(background="#d9d9d9")
        self.CanvasZ.configure(borderwidth="0")
        self.CanvasZ.configure(insertbackground="black")
        self.CanvasZ.configure(relief="ridge")
        self.CanvasZ.configure(selectbackground="#c4c4c4")
        self.CanvasZ.configure(selectforeground="black")
        self.CanvasZ.configure(width=120)
        self.CanvasZ.create_image(10, 10)

        self.pix = self.CanvasZ.create_image(10,10)

if __name__ == '__main__':
    vp_start_gui()
Reply
#2
You certainly can use tkinter for this, but you might want to take a look at wxpython phoenix (The new wxpython for python 3)
It has built in video controls,
see: https://wxpython.org/Phoenix/docs/html/w...aCtrl.html
and quite a few others:

Qt almost certainly has them as well.
I'm partial to wxpython because it's royality free for both commercial and non commercial applications.
Reply
#3
There is also a module for pyqt that I have a working bit of code for that was created by someone else. The module is called CV2
Reply
#4
This is WxPython Phenoix demo code for the video player
#!/usr/bin/env python

import wx
import wx.media
import os

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

class StaticText(wx.StaticText):
    """
    A StaticText that only updates the label if it has changed, to
    help reduce potential flicker since these controls would be
    updated very frequently otherwise.
    """
    def SetLabel(self, label):
        if label != self.GetLabel():
            wx.StaticText.SetLabel(self, label)

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

class TestPanel(wx.Panel):
    def __init__(self, parent, log):
        self.log = log
        wx.Panel.__init__(self, parent, -1,
                          style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)

        # Create some controls
        try:
            backend = "" # let MediaCtrl choose default backend
            #backend=wx.media.MEDIABACKEND_DIRECTSHOW
            #backend=wx.media.MEDIABACKEND_WMP10
            self.mc = wx.media.MediaCtrl()
            ok = self.mc.Create(self, style=wx.SIMPLE_BORDER,
                                szBackend=backend)
            if not ok:
                raise NotImplementedError
        except NotImplementedError:
            self.Destroy()
            raise

        # the following event is not sent with the Windows default backend
        # MEDIABACKEND_DIRECTSHOW
        # choose above e.g. MEDIABACKEND_WMP10 if this is a problem for you
        self.Bind(wx.media.EVT_MEDIA_LOADED, self.OnMediaLoaded)

        btn1 = wx.Button(self, -1, "Load File")
        self.Bind(wx.EVT_BUTTON, self.OnLoadFile, btn1)

        btn2 = wx.Button(self, -1, "Play")
        self.Bind(wx.EVT_BUTTON, self.OnPlay, btn2)
        self.playBtn = btn2

        btn3 = wx.Button(self, -1, "Pause")
        self.Bind(wx.EVT_BUTTON, self.OnPause, btn3)

        btn4 = wx.Button(self, -1, "Stop")
        self.Bind(wx.EVT_BUTTON, self.OnStop, btn4)

        slider = wx.Slider(self, -1, 0, 0, 10)
        self.slider = slider
        slider.SetMinSize((150, -1))
        self.Bind(wx.EVT_SLIDER, self.OnSeek, slider)

        self.st_size = StaticText(self, -1, size=(100,-1))
        self.st_len  = StaticText(self, -1, size=(100,-1))
        self.st_pos  = StaticText(self, -1, size=(100,-1))


        # setup the layout
        sizer = wx.GridBagSizer(5,5)
        sizer.Add(self.mc, (1,1), span=(5,1))#, flag=wx.EXPAND)
        sizer.Add(btn1, (1,3))
        sizer.Add(btn2, (2,3))
        sizer.Add(btn3, (3,3))
        sizer.Add(btn4, (4,3))
        sizer.Add(slider, (6,1), flag=wx.EXPAND)
        sizer.Add(self.st_size, (1, 5))
        sizer.Add(self.st_len,  (2, 5))
        sizer.Add(self.st_pos,  (3, 5))
        self.SetSizer(sizer)

        #self.DoLoadFile(os.path.abspath("data/testmovie.mpg"))
        wx.CallAfter(self.DoLoadFile, os.path.abspath("data/testmovie.mpg"))

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.timer.Start(100)



    def OnLoadFile(self, evt):
        dlg = wx.FileDialog(self, message="Choose a media file",
                            defaultDir=os.getcwd(), defaultFile="",
                            style=wx.FD_OPEN | wx.FD_CHANGE_DIR )
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.DoLoadFile(path)
        dlg.Destroy()


    def DoLoadFile(self, path):

        if not self.mc.Load(path):
            wx.MessageBox("Unable to load %s: Unsupported format?" % path,
                          "ERROR",
                          wx.ICON_ERROR | wx.OK)
            self.playBtn.Disable()
        else:
            self.mc.SetInitialSize()
            self.GetSizer().Layout()
            self.slider.SetRange(0, self.mc.Length())
            self.playBtn.Enable()

    def OnMediaLoaded(self, evt):
        self.playBtn.Enable()

    def OnPlay(self, evt):
        if not self.mc.Play():
            wx.MessageBox("Unable to Play media : Unsupported format?",
                          "ERROR",
                          wx.ICON_ERROR | wx.OK)
        else:
            self.mc.SetInitialSize()
            self.GetSizer().Layout()
            self.slider.SetRange(0, self.mc.Length())

    def OnPause(self, evt):
        self.mc.Pause()

    def OnStop(self, evt):
        self.mc.Stop()


    def OnSeek(self, evt):
        offset = self.slider.GetValue()
        self.mc.Seek(offset)

    def OnTimer(self, evt):
        offset = self.mc.Tell()
        self.slider.SetValue(offset)
        self.st_size.SetLabel('size: %s' % self.mc.GetBestSize())
        self.st_len.SetLabel('length: %d seconds' % (self.mc.Length()/1000))
        self.st_pos.SetLabel('position: %d' % offset)

    def ShutdownDemo(self):
        self.timer.Stop()
        del self.timer

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

def runTest(frame, nb, log):
    try:
        win = TestPanel(nb, log)
        return win
    except NotImplementedError:
        from wx.lib.msgpanel import MessagePanel
        win = MessagePanel(nb, 'wx.MediaCtrl is not available on this platform.',
                           'Sorry', wx.ICON_WARNING)
        return win


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



overview = """<html><body>
<h2><center>wx.MediaCtrl</center></h2>

wx.MediaCtrl is a class that allows a way to convieniently display
various types of media, such as videos, audio files, natively through
native codecs.  Several different formats of audio and video files are
supported, but some formats may not be playable on all platforms or
may require specific codecs to be installed.

<p>
wx.MediaCtrl uses native backends to render media, for example on Windows
there is a ActiveMovie/DirectShow backend, and on Macintosh there is a
QuickTime backend.
<p>
wx.MediaCtrl is not currently available on unix systems.

</body></html>
"""



if __name__ == '__main__':
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
in which you can run a multitude of demos via command to the path (linux)
$python3.6 ~/.wxPython-4.0.3/demo/demo.py

Attached Files

Thumbnail(s)
   
Recommended Tutorials:
Reply
#5
I tried running metulburr's example and don't understand. What is the purpose of the section starting at 162? Is this supposed to be in a different file? If I run it as is, I get AttributeError: module 'run' has no attribute 'main'. If I save above 162 to run.py and the rest to foo.py and run foo, it says AttributeError: module 'run' has no attribute 'main'. I am new enough at python to know I'm missing something important.
Reply
#6
Sorry, my fault. That is the example code that works with the embedded demo. There is code in there that expects it to be ran in their demo (the run module, the default video path, etc.)

To split it out on its own you have to not use their run module. That is only for running inside their demo program. Which i modified at the end there for you. The HTML part is just for the demo to show text. You dont need that at all.

The following is that example modified to run by itself.
#!/usr/bin/env python

import wx
import wx.media
import os

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

class StaticText(wx.StaticText):
    """
    A StaticText that only updates the label if it has changed, to
    help reduce potential flicker since these controls would be
    updated very frequently otherwise.
    """
    def SetLabel(self, label):
        if label != self.GetLabel():
            wx.StaticText.SetLabel(self, label)

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

class TestPanel(wx.Panel):
    def __init__(self, parent, log):
        self.log = log
        wx.Panel.__init__(self, parent, -1,
                          style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)

        # Create some controls
        try:
            backend = "" # let MediaCtrl choose default backend
            #backend=wx.media.MEDIABACKEND_DIRECTSHOW
            #backend=wx.media.MEDIABACKEND_WMP10
            self.mc = wx.media.MediaCtrl()
            ok = self.mc.Create(self, style=wx.SIMPLE_BORDER,
                                szBackend=backend)
            if not ok:
                raise NotImplementedError
        except NotImplementedError:
            self.Destroy()
            raise

        # the following event is not sent with the Windows default backend
        # MEDIABACKEND_DIRECTSHOW
        # choose above e.g. MEDIABACKEND_WMP10 if this is a problem for you
        self.Bind(wx.media.EVT_MEDIA_LOADED, self.OnMediaLoaded)

        btn1 = wx.Button(self, -1, "Load File")
        self.Bind(wx.EVT_BUTTON, self.OnLoadFile, btn1)

        btn2 = wx.Button(self, -1, "Play")
        self.Bind(wx.EVT_BUTTON, self.OnPlay, btn2)
        self.playBtn = btn2

        btn3 = wx.Button(self, -1, "Pause")
        self.Bind(wx.EVT_BUTTON, self.OnPause, btn3)

        btn4 = wx.Button(self, -1, "Stop")
        self.Bind(wx.EVT_BUTTON, self.OnStop, btn4)

        slider = wx.Slider(self, -1, 0, 0, 10)
        self.slider = slider
        slider.SetMinSize((150, -1))
        self.Bind(wx.EVT_SLIDER, self.OnSeek, slider)

        self.st_size = StaticText(self, -1, size=(100,-1))
        self.st_len  = StaticText(self, -1, size=(100,-1))
        self.st_pos  = StaticText(self, -1, size=(100,-1))


        # setup the layout
        sizer = wx.GridBagSizer(5,5)
        sizer.Add(self.mc, (1,1), span=(5,1))#, flag=wx.EXPAND)
        sizer.Add(btn1, (1,3))
        sizer.Add(btn2, (2,3))
        sizer.Add(btn3, (3,3))
        sizer.Add(btn4, (4,3))
        sizer.Add(slider, (6,1), flag=wx.EXPAND)
        sizer.Add(self.st_size, (1, 5))
        sizer.Add(self.st_len,  (2, 5))
        sizer.Add(self.st_pos,  (3, 5))
        self.SetSizer(sizer)

        #self.DoLoadFile(os.path.abspath("data/testmovie.mpg"))
        #wx.CallAfter(self.DoLoadFile, os.path.abspath("data/testmovie.mpg"))

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.timer.Start(100)



    def OnLoadFile(self, evt):
        dlg = wx.FileDialog(self, message="Choose a media file",
                            defaultDir=os.getcwd(), defaultFile="",
                            style=wx.FD_OPEN | wx.FD_CHANGE_DIR )
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.DoLoadFile(path)
        dlg.Destroy()


    def DoLoadFile(self, path):

        if not self.mc.Load(path):
            wx.MessageBox("Unable to load %s: Unsupported format?" % path,
                          "ERROR",
                          wx.ICON_ERROR | wx.OK)
            self.playBtn.Disable()
        else:
            self.mc.SetInitialSize()
            self.GetSizer().Layout()
            self.slider.SetRange(0, self.mc.Length())
            self.playBtn.Enable()

    def OnMediaLoaded(self, evt):
        self.playBtn.Enable()

    def OnPlay(self, evt):
        if not self.mc.Play():
            wx.MessageBox("Unable to Play media : Unsupported format?",
                          "ERROR",
                          wx.ICON_ERROR | wx.OK)
        else:
            self.mc.SetInitialSize()
            self.GetSizer().Layout()
            self.slider.SetRange(0, self.mc.Length())

    def OnPause(self, evt):
        self.mc.Pause()

    def OnStop(self, evt):
        self.mc.Stop()


    def OnSeek(self, evt):
        offset = self.slider.GetValue()
        self.mc.Seek(offset)

    def OnTimer(self, evt):
        offset = self.mc.Tell()
        self.slider.SetValue(offset)
        self.st_size.SetLabel('size: %s' % self.mc.GetBestSize())
        self.st_len.SetLabel('length: %d seconds' % (self.mc.Length()/1000))
        self.st_pos.SetLabel('position: %d' % offset)

    def ShutdownDemo(self):
        self.timer.Stop()
        del self.timer

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

def runTest(frame, nb, log):
    try:
        win = TestPanel(nb, log)
        return win
    except NotImplementedError:
        from wx.lib.msgpanel import MessagePanel
        win = MessagePanel(nb, 'wx.MediaCtrl is not available on this platform.',
                           'Sorry', wx.ICON_WARNING)
        return win


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

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,parent=None, title="Video Player")
        self._my_sizer = wx.BoxSizer(wx.VERTICAL)
        panel1 = TestPanel(self, None)
        self._my_sizer.Add(panel1, 1, wx.EXPAND)
        self.SetSizer(self._my_sizer)
        self.Fit()
        self.Show()

if __name__ == '__main__':
    import sys,os
    #import run
    #run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
    app = wx.App()
    top = MyFrame()
    top.Show()
    app.MainLoop()
The modifications are as following:
from this
if __name__ == '__main__':
    import sys,os
    import run
    run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
to this:
if __name__ == '__main__':
    import sys,os
    #import run
    #run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:])
    app = wx.App()
    top = MyFrame()
    top.Show()
    app.MainLoop()
removed the run module and setup an app main loop for it to run on its own. As well as created a frame class. This was required by the way the TestPanel was written in the demo code. Mainly for sending the frame to it.
class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,parent=None, title="Video Player")
        self._my_sizer = wx.BoxSizer(wx.VERTICAL)
        panel1 = TestPanel(self, None)
        self._my_sizer.Add(panel1, 1, wx.EXPAND)
        self.SetSizer(self._my_sizer)
        self.Fit()
        self.Show()
and changed this
        #self.DoLoadFile(os.path.abspath("data/testmovie.mpg"))
        wx.CallAfter(self.DoLoadFile, os.path.abspath("data/testmovie.mpg"))
to this
        #self.DoLoadFile(os.path.abspath("data/testmovie.mpg"))
        #wx.CallAfter(self.DoLoadFile, os.path.abspath("data/testmovie.mpg"))
to stop it from loading a video in the demo that does not exist if you dont have the demo ir are running it directly.
Recommended Tutorials:
Reply
#7
Excellent. The demo does work. I'll try adapting my GUI. Thanks.
Reply
#8
I've adapted my project to PyQt5 but have failed on getting the video to work.
This version fails to "connect() failed between Thread.changePixmap[QImage] and setImage()" in line 76. I've done some other tests where I managed to get it to connect, but it failed with "Thread destroyed before being terminated" (or maybe the opposite of that).


from PyQt5 import QtCore, QtGui, QtWidgets
from pyqt_led import Led
from PyQt5.QtCore import pyqtSlot
import cv2
import socket
import time


class Thread(QtCore.QThread):
    changePixmap = QtCore.pyqtSignal(QtGui.QImage)
    def run(self):
        cap = cv2.VideoCapture('http://192.168.1.1:8080/?action=stream')
        while True:
            ret, frame = cap.read()
            if ret:
                # https://stackoverflow.com/a/55468544/6622587
                rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                h, w, ch = rgbImage.shape
                bytesPerLine = ch * w
                convertToQtFormat = QtGui.QImage(rgbImage.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
                p = convertToQtFormat.scaled(640, 480)
                self.changePixmap.emit(p)


class Ui_MainWindow(object):

    def setupUi(self, MainWindow):
        super().__init__()
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 754)
        font = QtGui.QFont()
        font.setPointSize(10)
        MainWindow.setFont(font)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.vlabel = QtWidgets.QLabel(self.centralwidget)
        self.vlabel.setGeometry(QtCore.QRect(80, 40, 640, 480))
        self.vlabel.setObjectName("vlabel")
        self.vlabel.move(80, 40)
        self.vlabel.resize(640, 480)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
        self.menubar.setObjectName("menubar")
        self.menuCar_Control_v = QtWidgets.QMenu(self.menubar)
        self.menuCar_Control_v.setObjectName("menuCar_Control_v")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.menubar.addAction(self.menuCar_Control_v.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        self.initUI()

    @pyqtSlot(QtGui.QImage)
    def setImage(self, image):
        self.vlabel.setPixmap(QPixmap.fromImage(image))

    def initUI(self):
        th = Thread()
        th.changePixmap.connect(self.setImage)
        th.start()

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.vlabel.setText(_translate("MainWindow", "VLable"))
        self.menuCar_Control_v.setTitle(_translate("MainWindow", "Car Control v"))

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()

    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
Reply
#9
Okay first off you are improperly using QThread probably because the documentation you used is in error (a known issue that has not been resolved yet) as a QThread should not be sub-classed due to its the nature of how it functions - sub-classing it as you did and/or the documentation describes creates a situation that creates unexpected results.

Still with that said - I cannot help you with the QThread issue here but I will state that I know that CV2 does not need to use QThread if you want more help with CV2 and have Discord I can invite you to the Python server (just PM me) as one of my students is using the CV2 module and has already implemented that functionality via a Class

That being said though I will share with you more properly set up program that can handle adding in these features.

In your program you do several unnecessary things:
1) Reference sys.argv but never use it
2) Implement the complexity of the RetranslateUI functionality but not really need it
Which includes: Using retranslateUi function, setting ObjectNames and using QMetaObject

Did a few things improperly and/or nonfunctionally
1) Defined a Menu Action that is never defined
2) Used the Coordinate System instead of PyQt Layout system
3) Used QRect this is rarely if ever needed when using the Layout system
4) Import the entirety of QtCore, QtGui, and QtWidgets only import what you need
-- you then also imported an aspect of QtCore which would have been redundant
5) Import socket and time but I did not see them being used anywhere

So to help here is a redesign of the code that implements everything but QThread and CV2 but gives you a solid template to go forward from should you want to use this to implement your CV2 and/or QThread(if determined you actually need it) from as well as implement additional changes to your Center Panel -- note some of what I have done is pure style but most of my style is meant to make things more easily readable later on since I might have to come back to in a month or two after I have completely forgotten what I was doing with it and believe me that does happen.

from sys import exit as sysExit

#from PyQt5.QtCore    import ??
from PyQt5.QtGui     import QFont
# QtWidgets Container Objects
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDockWidget
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QLabel, QStatusBar, QMenuBar
# QtWidgets Action Objects
from PyQt5.QtWidgets import QAction

# Self Contained Encapsulated MenuToolBar Class
class MenuToolBar(QDockWidget):
    def __init__(self, MainWin):
        QDockWidget.__init__(self)
        self.MainWin = MainWin
        self.MainMenu = MainWin.menuBar()
        
      # Create Menu Action Reference Array in case you use a Tool Bar later
        self.MenuActRef = {'BgnCarVAct':0,
                           'EndCarVAct':0}

      # ******* Create the Car V Menu *******
        self.CarVMenu = self.MainMenu.addMenu('Car Control V')

      # ******* Create Car V Menu Items *******
        self.BgnCarVAct = QAction('&Start', self)
        self.BgnCarVAct.setShortcut("Ctrl+S")
        self.BgnCarVAct.setStatusTip('Start Car V')
        self.BgnCarVAct.triggered.connect(self.StartCarV)
        self.MenuActRef['BgnCarVAct'] = self.BgnCarVAct

        self.EndCarVAct = QAction('&End', self)
        self.EndCarVAct.setShortcut("Ctrl+E")
        self.EndCarVAct.setStatusTip('End Car V')
        self.EndCarVAct.triggered.connect(self.StopCarV)
        self.MenuActRef['EndCarVAct'] = self.EndCarVAct

        # ******* Setup the File Menu *******
        self.CarVMenu.addAction(self.BgnCarVAct)
        self.CarVMenu.addSeparator()
        self.CarVMenu.addAction(self.EndCarVAct)

  # These call back to the Main Window because this class is only about
  # controlling the functionality of the Menu and Tool Bar and could care
  # less about what it actions actual do or do not do
    def StartCarV(self):
        self.MainWin.StartCarV()

    def StopCarV(self):
        self.MainWin.StopCarV()

class CenterPanel(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self)

        self.MyParent = parent

      # Declare Font Aspects just to spice it up a bit
        font = QFont()
        font.setPointSize(14)
        font.setBold(True)
        
        self.lblCarV = QLabel()
        self.lblCarV.setText('Car V')
        self.lblCarV.setFont(font)

        self.lblActions = QLabel()

      # Centering this within the Center Pane
        HBox1 = QHBoxLayout()
        HBox1.addStretch(1)
        HBox1.addWidget(self.lblCarV)
        HBox1.addStretch(1)

        HBox2 = QHBoxLayout()
        HBox2.addStretch(1)
        HBox2.addWidget(self.lblActions)
        HBox2.addStretch(1)

        VBox = QVBoxLayout()
        VBox.addStretch(1)
        VBox.addLayout(HBox1)
        VBox.addWidget(QLabel('  '))  #An Invisible Spacer
        VBox.addLayout(HBox2)
        VBox.addStretch(1)
        
        self.setLayout(VBox)
 
class UI_MainWindow(QMainWindow):
    def __init__(self):
        super(UI_MainWindow, self).__init__()
      # Declare Font Aspects
        font = QFont()
        font.setPointSize(10)
      # Declare Main Window Aspects
        self.resize(800, 754)
        self.setWindowTitle("Main Window")
        self.setFont(font)
      # Define your Center Pane via a Class
        self.CenterPane = CenterPanel(self)
        self.setCentralWidget(self.CenterPane)
      # Define your Menu/Tool Bar via a Class
        self.MainMenu = MenuToolBar(self)
      # Define your Status Bar class not needed for this one
        self.StatusBar = QStatusBar(self)
        self.setStatusBar(self.StatusBar)

    def StartCarV(self):
        self.CenterPane.lblActions.setText('Started Car V')
      # Use this to Launch your, I presume, continuous Thread 
      # which btw can be a self-contained encapsulated Class without
      # needing to subclass QThread which one should not do

    def StopCarV(self):
        self.CenterPane.lblActions.setText('Stopped Car V')
      # Use this to Terminate your, again I presume, continuous Thread 

if __name__ == "__main__":
    MainThred = QApplication([])

    MainGui = UI_MainWindow()
    MainGui.show()

    sysExit(MainThred.exec_())
Reply
#10
Thanks very much Denni, I will look this over and try to figure out the differences. FWIW, I used the Qt Designer for the code and then stripped all of the buttons and boxes that weren't relevant to the video. Some of the things like the menu seem to be from the designer.
I added the thread as "the solution recommended on other posts." They could be outdated. As a standalone app, the sample I took the thread from works fine. I could never get it integrated into my code. I'm all for the simplest solution so lowing the thread isn't an issue.
I have never used "Discord".
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] Embedding a Video in Video Player WhatsupSmiley 0 5,827 Jan-28-2019, 06:24 PM
Last Post: WhatsupSmiley

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020