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_())
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.
foo.signal.connect(lambda *_, args=args : function(args))
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_())