Kikomi
Kikomi

Reputation: 49

Url not defined using Signals and slots PyQt5 in different classes

I am trying to run a Video with its parameter in parallel using Threads so when I click on the SD button its activates TP1 and TP2 buttons, and for each has a different Url.

I want to display the video and start the ProgressBar and show the result at the end.

from multiprocessing import Process

import sys
import json
import shlex
import threading
import subprocess
import webbrowser
from QLed import QLed
from functools import partial
from PyQt5.QtGui import QColor,QFont
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtGui import QPainter, QPen
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtCore import QDir, Qt, QUrl, QSize, QPoint, QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMainWindow
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
from PyQt5.QtWidgets import (QWidget, QPushButton, QApplication,QGridLayout, QLCDNumber)
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QGridLayout, QLCDNumber





class Analyzer(QtCore.QObject):


    result_ready = QtCore.pyqtSignal(object)

    def do_work(self, myurl):


        cmd = "ffprobe -v quiet -print_format json -show_streams"
        args = shlex.split(cmd)

        args.append(myurl)

        ffprobeOutput = subprocess.check_output(args).decode('utf-8')
        ffprobeOutput = json.loads(ffprobeOutput)

        result = ffprobeOutput['streams'][0]
        self.result_ready.emit(result)



class MainProg(QtWidgets.QMainWindow):

    def __init__(self):


        super(MainProg, self).__init__()

        self.resize(870, 525)
        self.centralwidget = QtWidgets.QWidget(self)
        self.centralwidget.setObjectName("centralwidget")

        ############################      The Viedeo and frame  ######

        self.frame = QtWidgets.QFrame(self)
        self.frame.setGeometry(QtCore.QRect(450, 40, 391, 291))
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)

        self.mediaPlayer = QtMultimedia.QMediaPlayer(self.frame)
        self.viewer1 = QtMultimediaWidgets.QVideoWidget(self.frame)
        self.mediaPlayer.setVideoOutput(self.viewer1)
        layout1 = QtWidgets.QGridLayout(self.frame)
        layout1.addWidget(self.viewer1, 0, 0, 1, 2)
       #############################################################

        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(110, 470, 143, 25))
        self.progressBar.setProperty("value", 0)
        self.progressBar.setTextVisible(True)

        self.lcd = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd.setGeometry(QtCore.QRect(220, 50, 146, 50))
        self.lcd1 = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd1.setGeometry(QtCore.QRect(220, 100, 146, 50))
        self.lcd2 = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd2.setGeometry(QtCore.QRect(220, 150, 146, 50))
        self.lcd3 = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd3.setGeometry(QtCore.QRect(220, 200, 146, 50))
        self.lcd4 = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd4.setGeometry(QtCore.QRect(220, 250, 146, 50))
        self.lcd5 = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd5.setGeometry(QtCore.QRect(220, 300, 146, 50))
        self.lcd6 = QtWidgets.QLCDNumber(self.centralwidget)
        self.lcd6.setGeometry(QtCore.QRect(220, 350, 146, 50))


        self.txtt = QtWidgets.QLabel(self.centralwidget)
        self.txtt.setFont(QFont('Arial', 12))
        self.txtt.setGeometry(QtCore.QRect(20, 0, 300, 400))

        self.txtt.setText("Video"
                          "\nCode Name .................."
                          "\n\nHorizont........................"
                          "\n\nVertical.........................."
                          "\n\nDisplay Aspect Ratio......"
                          "\n\nRefrence........................."
                          "\n\nB frames........................."
                          "\n\nStart Bits......................."
                          "\n\nSample Aspect ratio......."
                          "\n\nBit Rate.........................")

        self.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(self)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)




       #########################The buttons#################################

        self.AASD = QtWidgets.QToolButton(self)
        self.AASD.setGeometry(QtCore.QRect(140, 20, 31, 32))
        self.AASD.setObjectName("AASD")
        self.AASD.setText("SD")
        self.AASD.clicked.connect(self.funcchoos)
        QTimer.singleShot(5000, lambda: self.AASD.setDisabled(False))

        self.Testpunk1 = QtWidgets.QToolButton(self)
        self.Testpunk1.setGeometry(QtCore.QRect(150, 400, 31, 32))

        self.Testpunk2 = QtWidgets.QToolButton(self)
        self.Testpunk2.setGeometry(QtCore.QRect(150, 430, 31, 32))

        self.Testpunk1.setText("TP1")
        self.Testpunk2.setText("TP2")

        self.Testpunk1.setObjectName("TP1")
        self.Testpunk2.setObjectName("TP2")

        self.Tp1 = QLed(self, onColour=QLed.Orange, shape=QLed.Circle)
        self.Tp1.setGeometry(QtCore.QRect(185, 415, 25, 25))
        self.Tp1.value = False
    ########################### the functions############################

    def funcchoos (self):

        QtCore.QTimer.singleShot(500, self.TPLed) # Using timer as QLed uses it in its tests

        if self.sender().objectName() == "AASD":
            self.Testpunk1.clicked.connect(self.MyUrl)
            self.Testpunk2.clicked.connect(self.MyUrl)
            return

    def MyUrl(self):
        TP1 = "293.168.1.6:1115"
        TP2 = "239.168.1.7:1116"


        if self.sender().objectName() == "TP1":
            myurl = TP1
            print("TP1 is playing")
            self.dep3(myurl)

            return
        if self.sender().objectName() == "TP2":
            myurl = TP2
            self.dep3(myurl)

            print(myurl)
            print("TP2 is playing")
            return

##############################################################
    def TPLed(self):
        self.Tp1.setValue(True)  # the LED ON code


#########################################################################

    def dep3(self,myurl):
        # set progress bar to undetermined state and disable button
        self.progressBar.setRange(0,0)
        self.Testpunk1.setEnabled(False)
        self.mediaPlayer.setMedia(QMediaContent(QUrl(myurl)))
        self.mediaPlayer.play()
        # create thread for doing heavy work
        self.thread = QtCore.QThread()
        self.worker = Analyzer()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.do_work(myurl))
        self.thread.finished.connect(self.worker.deleteLater)
        self.worker.result_ready.connect(self.process_result)
        self.worker.result_ready.connect(self.worker.do_work(myurl))


        self.thread.start()

    def process_result(self, result):

        codec_name = result['codec_name']
        width = result['width']
        height = result['height']
        display_aspect_ratio = result['display_aspect_ratio']
        sample_aspect_ratio = result['sample_aspect_ratio']
        refs = result['refs']
        has_b_frames = result['has_b_frames']

        self.lcd.display(has_b_frames)
        self.lcd1.display(codec_name)
        self.lcd2.display(width)
        self.lcd3.display(height)
        self.lcd4.display(display_aspect_ratio)
        self.lcd5.display(sample_aspect_ratio)
        self.lcd6.display(refs)

        # reset progress bar and push button
        self.progressBar.setRange(0,100)
        self.progressBar.setValue(100)
        self.pushButton.setEnabled(True)



        print("done!!")



if __name__ == "__main__":
    import sys



    app = QtWidgets.QApplication(sys.argv)


    player = MainProg()
    player.show()
    sys.exit(app.exec_())

Upvotes: 1

Views: 160

Answers (1)

eyllanesc
eyllanesc

Reputation: 244003

The main problem is that when you use:

foo.signal.connect(function(args))

equals

value = function(args)
foo.signal.connect(value)

which causes the error since connect expects a callable, and in your case value is None causing the error.

The solution in general is to use lambda or partials, but the first will cause the function to be executed in the main thread since it is invoked in that thread so it must be discarded, instead partials only add arguments.

  • lambda:
foo.signal.connect(lambda *_, args=args : function(args))
  • functools.partial
foo.signal.connect(functools.partial(function, args))

In addition you have other errors such as every time you invoke "funcchoos" a new connection is created between the "TestpunkX" and MyUrl causing that every time you press "TestpunkX", "MyUrl" is invoked as many times as "funcchoos is invoked ".

Considering the above, I rewrote your code as there may be other errors.

from functools import partial
import json
import shlex
import subprocess
import sys


from QLed import QLed

from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets


class Analyzer(QtCore.QObject):
    result_ready = QtCore.pyqtSignal(object)

    @QtCore.pyqtSlot(str)
    def do_work(self, myurl):
        cmd = "ffprobe -v quiet -print_format json -show_streams"
        args = shlex.split(cmd)

        args.append(myurl)

        ffprobeOutput = subprocess.check_output(args).decode("utf-8")
        ffprobeOutput = json.loads(ffprobeOutput)

        result = ffprobeOutput["streams"][0]

        self.result_ready.emit(result)


class MainProg(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainProg, self).__init__(parent)

        self.setFont(QtGui.QFont("Arial", 12))

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        hlay = QtWidgets.QHBoxLayout(central_widget)

        left_widget = QtWidgets.QWidget()

        self.mediaPlayer = QtMultimedia.QMediaPlayer()
        self.video_widget = QtMultimediaWidgets.QVideoWidget()
        self.video_widget.setContentsMargins(30, 30, 30, 30)
        self.mediaPlayer.setVideoOutput(self.video_widget)

        self.sd_button = QtWidgets.QToolButton(text="SD", checkable=True)
        self.sd_button.setFixedSize(31, 32)
        self.code_lcd = QtWidgets.QLCDNumber()
        self.horizontal_lcd = QtWidgets.QLCDNumber()
        self.vertical_lcd = QtWidgets.QLCDNumber()
        self.display_aspect_ratio_lcd = QtWidgets.QLCDNumber()
        self.reference_lcd = QtWidgets.QLCDNumber()
        self.b_frames_lcd = QtWidgets.QLCDNumber()
        self.start_bits_lcd = QtWidgets.QLCDNumber()
        self.sample_aspect_ratio_lcd = QtWidgets.QLCDNumber()
        self.bit_rate_lcd = QtWidgets.QLCDNumber()
        self.tp1_button = QtWidgets.QToolButton(text="TP1")
        self.tp2_button = QtWidgets.QToolButton(text="TP2")
        self.led = QLed(self, onColour=QLed.Orange, shape=QLed.Circle)
        self.progressbar = QtWidgets.QProgressBar()

        for lcd in (
            self.code_lcd,
            self.horizontal_lcd,
            self.vertical_lcd,
            self.display_aspect_ratio_lcd,
            self.reference_lcd,
            self.b_frames_lcd,
            self.start_bits_lcd,
            self.sample_aspect_ratio_lcd,
            self.bit_rate_lcd,
        ):
            lcd.setFixedSize(146, 50)

        hlay.addWidget(left_widget)
        hlay.addWidget(self.video_widget, stretch=1)

        lay = QtWidgets.QGridLayout(left_widget)
        lay.setVerticalSpacing(5)

        for i, (text, widget) in enumerate(
            zip(
                (
                    "Video",
                    "Codec Name:",
                    "Horizontal:",
                    "Vertical:",
                    "Display Aspect Ratio:",
                    "Refrence:",
                    "B frames:",
                    "Start Bits:",
                    "Sample Aspect ratio:",
                    "Bit Rate:",
                ),
                (
                    self.sd_button,
                    self.code_lcd,
                    self.horizontal_lcd,
                    self.vertical_lcd,
                    self.display_aspect_ratio_lcd,
                    self.reference_lcd,
                    self.b_frames_lcd,
                    self.start_bits_lcd,
                    self.sample_aspect_ratio_lcd,
                    self.bit_rate_lcd,
                ),
            )
        ):
            label = QtWidgets.QLabel(text)
            lay.addWidget(label, i, 0)
            lay.addWidget(widget, i, 1, alignment=QtCore.Qt.AlignCenter)

        vlay = QtWidgets.QVBoxLayout()
        vlay.addWidget(self.tp1_button)
        vlay.addWidget(self.tp2_button)

        hlay2 = QtWidgets.QHBoxLayout()
        hlay2.addStretch(0)
        hlay2.addLayout(vlay)
        hlay2.addWidget(self.led)
        hlay2.addStretch(0)

        lay.addLayout(hlay2, lay.rowCount(), 0, 1, 2)
        lay.addWidget(self.progressbar, lay.rowCount(), 0, 1, 2)

        lay.setRowStretch(lay.rowCount(), 1)
        self.resize(960, 480)

        self.sd_button.toggled.connect(self.led.setValue)
        self.tp1_button.clicked.connect(self.on_tp_clicked)
        self.tp2_button.clicked.connect(self.on_tp_clicked)

        self.current_button = None

        thread = QtCore.QThread(self)
        thread.start()

        self.worker = Analyzer()
        self.worker.moveToThread(thread)
        self.worker.result_ready.connect(self.process_result)

    @QtCore.pyqtSlot()
    def on_tp_clicked(self):
        if self.sd_button.isChecked():
            urls_map = {
                self.tp1_button: "293.168.1.6:1115",
                self.tp2_button: "239.168.1.7:1116",
            }
            url = urls_map.get(self.sender(), "")
            if url:
                self.play(url)
                self.current_button = self.sender()
                self.current_button.setEnabled(False)

    def play(self, url):
        self.progressbar.setRange(0, 0)
        self.mediaPlayer.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl(url)))
        self.mediaPlayer.play()
        wrapper = partial(self.worker.do_work, url)
        QtCore.QTimer.singleShot(0, wrapper)

    @QtCore.pyqtSlot(object)
    def process_result(self, result):
        self.current_button.setEnabled(True)
        self.current_button = None
        self.progressbar.setRange(0, 1)
        self.progressbar.setValue(0)

        for lcd, key in zip(
            (
                self.code_lcd,
                self.b_frames_lcd,
                self.horizontal_lcd,
                self.vertical_lcd,
                self.display_aspect_ratio_lcd,
                self.sample_aspect_ratio_lcd,
                self.reference_lcd,
            ),
            (
                "codec_name",
                "has_b_frames",
                "width",
                "height",
                "display_aspect_ratio",
                "sample_aspect_ratio",
                "refs",
            ),
        ):
            value = result.get(key, 0)
            lcd.display(value)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainProg()
    w.show()
    sys.exit(app.exec_())

Although I don't see the need to use threads, in this case it is easier to use QProcess.

from functools import partial
import json
import shlex
import sys


from QLed import QLed

from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets


class MainProg(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainProg, self).__init__(parent)

        self.setFont(QtGui.QFont("Arial", 12))

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        hlay = QtWidgets.QHBoxLayout(central_widget)

        left_widget = QtWidgets.QWidget()

        self.mediaPlayer = QtMultimedia.QMediaPlayer()
        self.video_widget = QtMultimediaWidgets.QVideoWidget()
        self.video_widget.setContentsMargins(30, 30, 30, 30)
        self.mediaPlayer.setVideoOutput(self.video_widget)

        self.sd_button = QtWidgets.QToolButton(text="SD", checkable=True)
        self.sd_button.setFixedSize(31, 32)
        self.code_lcd = QtWidgets.QLCDNumber()
        self.horizontal_lcd = QtWidgets.QLCDNumber()
        self.vertical_lcd = QtWidgets.QLCDNumber()
        self.display_aspect_ratio_lcd = QtWidgets.QLCDNumber()
        self.reference_lcd = QtWidgets.QLCDNumber()
        self.b_frames_lcd = QtWidgets.QLCDNumber()
        self.start_bits_lcd = QtWidgets.QLCDNumber()
        self.sample_aspect_ratio_lcd = QtWidgets.QLCDNumber()
        self.bit_rate_lcd = QtWidgets.QLCDNumber()
        self.tp1_button = QtWidgets.QToolButton(text="TP1")
        self.tp2_button = QtWidgets.QToolButton(text="TP2")
        self.led = QLed(self, onColour=QLed.Orange, shape=QLed.Circle)
        self.progressbar = QtWidgets.QProgressBar()

        for lcd in (
            self.code_lcd,
            self.horizontal_lcd,
            self.vertical_lcd,
            self.display_aspect_ratio_lcd,
            self.reference_lcd,
            self.b_frames_lcd,
            self.start_bits_lcd,
            self.sample_aspect_ratio_lcd,
            self.bit_rate_lcd,
        ):
            lcd.setFixedSize(146, 50)

        hlay.addWidget(left_widget)
        hlay.addWidget(self.video_widget, stretch=1)

        lay = QtWidgets.QGridLayout(left_widget)
        lay.setVerticalSpacing(5)

        for i, (text, widget) in enumerate(
            zip(
                (
                    "Video",
                    "Codec Name:",
                    "Horizontal:",
                    "Vertical:",
                    "Display Aspect Ratio:",
                    "Refrence:",
                    "B frames:",
                    "Start Bits:",
                    "Sample Aspect ratio:",
                    "Bit Rate:",
                ),
                (
                    self.sd_button,
                    self.code_lcd,
                    self.horizontal_lcd,
                    self.vertical_lcd,
                    self.display_aspect_ratio_lcd,
                    self.reference_lcd,
                    self.b_frames_lcd,
                    self.start_bits_lcd,
                    self.sample_aspect_ratio_lcd,
                    self.bit_rate_lcd,
                ),
            )
        ):
            label = QtWidgets.QLabel(text)
            lay.addWidget(label, i, 0)
            lay.addWidget(widget, i, 1, alignment=QtCore.Qt.AlignCenter)

        vlay = QtWidgets.QVBoxLayout()
        vlay.addWidget(self.tp1_button)
        vlay.addWidget(self.tp2_button)

        hlay2 = QtWidgets.QHBoxLayout()
        hlay2.addStretch(0)
        hlay2.addLayout(vlay)
        hlay2.addWidget(self.led)
        hlay2.addStretch(0)

        lay.addLayout(hlay2, lay.rowCount(), 0, 1, 2)
        lay.addWidget(self.progressbar, lay.rowCount(), 0, 1, 2)

        lay.setRowStretch(lay.rowCount(), 1)
        self.resize(960, 480)

        self.sd_button.toggled.connect(self.led.setValue)
        self.tp1_button.clicked.connect(self.on_tp_clicked)
        self.tp2_button.clicked.connect(self.on_tp_clicked)

        self.current_button = None

        self.process = QtCore.QProcess(self)
        self.process.finished.connect(self.on_finish)

    @QtCore.pyqtSlot()
    def on_tp_clicked(self):
        if self.sd_button.isChecked():
            urls_map = {
                self.tp1_button: "293.168.1.6:1115",
                self.tp2_button: "239.168.1.7:1116",
            }
            url = urls_map.get(self.sender(), "")
            if url:
                self.play(url)
                self.current_button = self.sender()
                self.current_button.setEnabled(False)

    def play(self, url):
        self.progressbar.setRange(0, 0)
        self.mediaPlayer.setMedia(QtMultimedia.QMediaContent(QtCore.QUrl(url)))
        self.mediaPlayer.play()

        cmd = "ffprobe -v quiet -print_format json -show_streams"
        program, *args = shlex.split(cmd)
        args.append(url)
        self.process.start(program, args)

    @QtCore.pyqtSlot()
    def on_finish(self):
        data = self.process.readAllStandardOutput().data()
        if data:
            ffprobeOutput = json.loads(data)
            result = ffprobeOutput['streams'][0]
            for lcd, key in zip(
                (
                    self.code_lcd,
                    self.b_frames_lcd,
                    self.horizontal_lcd,
                    self.vertical_lcd,
                    self.display_aspect_ratio_lcd,
                    self.sample_aspect_ratio_lcd,
                    self.reference_lcd,
                ),
                (
                    "codec_name",
                    "has_b_frames",
                    "width",
                    "height",
                    "display_aspect_ratio",
                    "sample_aspect_ratio",
                    "refs",
                ),
            ):
                value = result.get(key, 0)
                lcd.display(value)


        self.current_button.setEnabled(True)
        self.current_button = None
        self.progressbar.setRange(0, 1)
        self.progressbar.setValue(0)



if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainProg()
    w.show()
    sys.exit(app.exec_())

Upvotes: 3

Related Questions