Url 未在不同 类 中使用信号和槽 PyQt5 定义

Url not defined using Signals and slots PyQt5 in different classes

我正在尝试 运行 使用线程并行处理带有参数的视频 所以当我点击 SD 按钮时,它会激活 TP1 和 TP2 按钮,并且每个按钮都有不同的 Url.

我想显示视频并启动进度条并在最后显示结果。

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_())

主要问题是当你使用:

foo.signal.connect(function(args))

等于

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

这会导致错误,因为 connect 需要一个可调用对象,在您的情况下,值是 None 导致错误。

通常的解决方案是使用 lambda 或 partials,但第一个会导致函数在主线程中执行,因为它是在该线程中调用的,因此必须丢弃它,而 partials 仅添加参数。

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

此外还有其他错误,例如每次调用 "funcchoos" 都会在 "TestpunkX" 和 MyUrl 之间创建一个新连接,导致每次按下 "TestpunkX"、"MyUrl" 被调用的次数与 "funcchoos is invoked ".

一样多

考虑到以上情况,可能还有其他错误,我重写了你的代码。

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_())

虽然我没有看到使用线程的必要性,但在这种情况下使用 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_())