PyQt5 视频播放器:转换为面向对象的代码会阻止播放

Posted

技术标签:

【中文标题】PyQt5 视频播放器:转换为面向对象的代码会阻止播放【英文标题】:PyQt5 VideoPlayer: Converting to Object Orientated Code Prevents Playback 【发布时间】:2020-06-11 12:35:44 【问题描述】:

我一直在研究如何将视频或直播整合到应用中。我发现了一些可以正常工作的功能代码:

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

import cv2 # OpenCV
import qimage2ndarray # for a memory leak,see gist
import sys # for exiting

# Minimal implementation...

def displayFrame():
    ret, frame = cap.read()
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    image = qimage2ndarray.array2qimage(frame)
    label.setPixmap(QPixmap.fromImage(image))

app = QApplication([])
window = QWidget()

# OPENCV

cap = cv2.VideoCapture("Vid.mp4")
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

# timer for getting frames

timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(60)
label = QLabel('No Camera Feed')
button = QPushButton("Quiter")
button.clicked.connect(sys.exit) # quiter button
layout = QVBoxLayout()
layout.addWidget(button)
layout.addWidget(label)
window.setLayout(layout)
window.show()
app.exec_()

我正在尝试在一些面向对象的代码中使用它,目的是创建一个视频播放小部件以合并到其他应用程序中:

import cv2
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

import qimage2ndarray # for a memory leak,see gist
import sys # for exiting

# Minimal implementation...
class basicWindow(QMainWindow):
    def __init__(self):
        super(basicWindow, self).__init__()

        # OPENCV

        cap = cv2.VideoCapture("Vid.mp4")
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

        # timer for getting frames

        timer = QTimer()
        timer.timeout.connect(displayFrame)
        timer.start(60)
        label = QLabel('No Camera Feed')
        button = QPushButton("Quiter")
        button.clicked.connect(sys.exit)  # quiter button
        layout = QVBoxLayout()
        layout.addWidget(button)
        layout.addWidget(label)
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

def displayFrame():
    ret, frame = cap.read()
    if ret:
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        image = qimage2ndarray.array2qimage(frame)
        try:
            label.setPixmap(QPixmap.fromImage(image))
        except Exception as e:
            print(e)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    windowExample = basicWindow()
    windowExample.show()
    sys.exit(app.exec_())

我是 OO 编码和 PyQt5 的新手,所以任何关于我如何误解代码的工作原理或我缺少什么的建议都会很棒。我已经尝试将标签设置为全局变量,因为我不确定函数 displayFrame() 是否知道要使用label.setPixmap 更改什么标签,但否则我有点迷失了。

【问题讨论】:

【参考方案1】:

在您的第一个示例中它可以工作,因为label 是一个全局 变量,所以displayFrame 可以这样访问它。 在另一种情况下,label 仅在basicWindow 实例的__init__ 范围内声明,因此displayFrame 对此一无所知。

使label成为实例的成员(self.label = QLabel()),displayFramebasicWindow类的方法(def displayFrame(self):)然后正确访问label;请注意,您还需要将captimer 都设为实例(self)的成员,否则它们的对象将在__init__ 返回后立即被“垃圾收集”。

class basicWindow(QMainWindow):
    def __init__(self):
        super(basicWindow, self).__init__()
        # ...
        self.cap = cv2.VideoCapture("Vid.mp4")
        # ...
        self.timer = QTimer()
        self.timer.timeout.connect(self.displayFrame)
        self.timer.start(60)
        self.label = QLabel('No Camera Feed')
        # ...

    def displayFrame(self):
        ret, frame = self.cap.read()
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            image = qimage2ndarray.array2qimage(frame)
            try:
                self.label.setPixmap(QPixmap.fromImage(image))
            except Exception as e:
                print(e)

由于您是 OO 编程的新手,我建议您先学习 classes and instances 和 name resolution 在 python 中的工作原理。

【讨论】:

啊,我确实尝试将其设为全局变量,但没有考虑将其设为类变量。生病试试,现在欢呼。编辑:恐怕仍然无法正常工作。 Edit2:它甚至没有运行 self.displayFrame 函数,放一个print("here") 来检查。 应该避免使用全局变量,除非绝对必要并且只有当你真的知道你在做什么时(这通常意味着你可能做错了什么,因为你应该知道你不应该使用它们 :-) )。在任何情况下,只有在需要“全局”访问时才应该使用全局函数(那些不是类成员的函数)。因为displayFrame 只对basicWindow 类实例很重要,所以无论如何它都应该是该类的成员,这是OO 编程的一个非常重要的方面。 @Jkind9 对不起,我的错。将 timer 设为类的成员 (self.timer = ...) 或为其设置父级 (timer = QTimer(self))。 尝试了这两种方法,但由于某种原因程序崩溃了。在崩溃 eitehr 之前仍然无法看到打印语句 不幸的是我无法测试它,因为我现在不能使用 cv2。我刚刚发现cap 也是本地的;使其也成为实例的成员,然后如果仍然不起作用,请尝试从 shell/命令提示符运行代码以查看完整的回溯。

以上是关于PyQt5 视频播放器:转换为面向对象的代码会阻止播放的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5 - 无法从流中播放视频

PyQt5 - 在新窗口中打开 QMediaplayer 并播放视频

pyqt5 qwebenginview 不会自动播放 youtube 视频

PyQt5 QMediaPlayer播放不了视频

Pyqt5:使用Qlabel标签进行视频播放

使用 OpenCV 和 PyQT5 视频播放速度过快