You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQt4中播放摄像头采集的实时视频?(树莓派3场景)

在PyQt4的Phonon中播放树莓派摄像头实时视频流

我来帮你搞定这个问题!在树莓派上用PyQt4的Phonon播放摄像头实时流,核心是利用Phonon的GStreamer后端(Linux下默认的后端)来直接读取摄像头的视频流,下面是具体的实现方案和修改后的代码:

前置准备

首先确保树莓派上安装了必要的依赖包:

sudo apt-get update
sudo apt-get install python-pyqt4.phonon gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-tools

实现思路

Phonon本身不直接支持从摄像头捕获帧,但它可以通过GStreamer管道来播放实时流。树莓派的摄像头设备通常是/dev/video0,我们可以构造一个GStreamer管道,让Phonon直接播放这个管道的输出,这样就能实现实时视频流的播放。

修改后的代码

重点修改fourth_view函数,添加摄像头流的播放逻辑,以及启动/停止按钮的槽函数:

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.phonon import Phonon

class Example(QtGui.QWidget):
    def __init__(self):
        super(Example, self).__init__()
        self.setGeometry(10,10,640,480)  # 扩大窗口以适配视频
        self.first_box = QtGui.QVBoxLayout()
        self.second_box = QtGui.QVBoxLayout()
        self.third_box = QtGui.QVBoxLayout()
        vbox = QtGui.QVBoxLayout()
        vbox.addLayout(self.first_box)
        vbox.addLayout(self.second_box)
        vbox.addLayout(self.third_box)
        self.setLayout(vbox)
        self.first_view()
        self.setWindowTitle('Pupilometer')
        # 保存player实例,避免函数结束后被回收
        self.video_player = None

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.close()

    def first_view(self):
        powerButton = QtGui.QPushButton("Power")
        settingButton = QtGui.QPushButton("Settings")
        videoButton = QtGui.QPushButton("Video")
        savedfileButton = QtGui.QPushButton("Saved Records")
        newButton = QtGui.QPushButton("New Record")
        powerButton.clicked.connect(self.first_second_view)
        settingButton.clicked.connect(self.first_third_view)
        videoButton.clicked.connect(self.first_fourth_view)
        savedfileButton.clicked.connect(self.first_fifth_view)
        newButton.clicked.connect(self.first_sixth_view)
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(newButton)
        hbox.addWidget(savedfileButton)
        hbox.addWidget(videoButton)
        hbox.addWidget(settingButton)
        hbox.addWidget(powerButton)
        font = QtGui.QFont()
        font.setBold(True)
        font.setPixelSize(12)
        lbl1 = QtGui.QLabel('Select an option', self)
        lbl1.setFont(font)
        lbl2 = QtGui.QLabel('Welcome to the Pupilometer', self)
        lbl2.setFont(font)
        self.first_box.addWidget(lbl1)
        self.second_box.addWidget(lbl2)
        self.third_box.addLayout(hbox)

    def first_second_view(self):
        print("first_second_view")
        self.clearLayout(self.second_box)
        self.second_view()

    def second_view(self):
        font = QtGui.QFont()
        font.setBold(True)
        font.setPixelSize(12)
        lbl1 = QtGui.QLabel('Power Off?', self)
        lbl1.setFont(font)
        yesButton = QtGui.QPushButton("Yes")
        noButton = QtGui.QPushButton("No")
        yesButton.clicked.connect(self.close_fun)
        noButton.clicked.connect(self.any_first_view)
        self.second_box.addWidget(lbl1)
        self.second_box.addWidget(yesButton)
        self.second_box.addWidget(noButton)

    def any_first_view(self):
        self.clearLayout(self.second_box)
        self.first_from_view()

    def first_from_view(self):
        font = QtGui.QFont()
        font.setBold(True)
        font.setPixelSize(12)
        lbl2 = QtGui.QLabel('Welcome to the Pupilometer', self)
        lbl2.setFont(font)
        self.second_box.addWidget(lbl2)

    def first_third_view(self):
        print("first_third_view")

    def first_fourth_view(self):
        print("first_fourth_view")
        self.clearLayout(self.second_box)
        self.fourth_view()

    def fourth_view(self):
        # 初始化Phonon视频播放器
        self.video_player = Phonon.VideoPlayer(Phonon.VideoCategory, self)
        # 构造GStreamer管道:从摄像头读取视频,转换格式后输出
        # 树莓派摄像头设备通常是/dev/video0,根据实际情况调整
        gstreamer_pipeline = "v4l2src device=/dev/video0 ! videoconvert ! autovideosink"
        # 创建MediaSource,指定GStreamer管道
        media_source = Phonon.MediaSource(gstreamer_pipeline)
        
        startButton = QtGui.QPushButton("Start")
        stopButton = QtGui.QPushButton("Stop")
        startButton.clicked.connect(lambda: self.start_video_stream(media_source))
        stopButton.clicked.connect(self.stop_video_stream)

        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(startButton)
        hbox.addWidget(stopButton)

        self.second_box.addWidget(self.video_player)
        self.second_box.addLayout(hbox)

    def start_video_stream(self, media_source):
        if self.video_player:
            self.video_player.load(media_source)
            self.video_player.play()

    def stop_video_stream(self):
        if self.video_player:
            self.video_player.stop()

    def first_fifth_view(self):
        print("first_fifth_view")

    def first_sixth_view(self):
        print("first_sixth_view")
        self.clearLayout(self.second_box)
        self.sixth_view()

    def sixth_view(self):
        print("hello")
        name = QtGui.QLabel('Name')
        age = QtGui.QLabel('Age')
        gender = QtGui.QLabel('Gender')
        patentID = QtGui.QLabel('Patient ID')
        nameEdit = QtGui.QLineEdit()
        ageEdit = QtGui.QLineEdit()
        patientIDEdit = QtGui.QLineEdit()
        self.b1 = QtGui.QRadioButton("Male")
        self.b2 = QtGui.QRadioButton("Female")
        self.b1.toggled.connect(lambda:self.btnstate(self.b1))
        self.b2.toggled.connect(lambda:self.btnstate(self.b2))
        grid = QtGui.QGridLayout()
        grid.setSpacing(10)
        grid.addWidget(name, 1, 0)
        grid.addWidget(nameEdit, 1, 1)
        grid.addWidget(age, 2, 0)
        grid.addWidget(ageEdit, 2, 1)
        grid.addWidget(gender,3,0)
        grid.addWidget(self.b1,3,1)
        grid.addWidget(self.b2,3,2)
        grid.addWidget(patentID, 4, 0)
        grid.addWidget(patientIDEdit, 4, 1)
        self.second_box.addLayout(grid)

    def btnstate(self,b):
        if b.text() == "Male":
            if b.isChecked():
                print(f"{b.text()} is selected")
            else:
                print(f"{b.text()} is deselected")
        if b.text() == "Female":
            if b.isChecked():
                print(f"{b.text()} is selected")
            else:
                print(f"{b.text()} is deselected")

    def clearLayout(self, layout):
        if layout is not None:
            while layout.count():
                item = layout.takeAt(0)
                widget = item.widget()
                if widget is not None:
                    widget.deleteLater()
                else:
                    self.clearLayout(item.layout())

    def close_fun(self):
        print("Closing the Window")
        sys.exit()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

关键修改点说明

  • 将video_player作为类的成员变量,避免在fourth_view函数结束后被垃圾回收,导致播放异常。
  • 使用GStreamer管道v4l2src device=/dev/video0 ! videoconvert ! autovideosink来读取摄像头流,这个管道会从摄像头设备读取视频,转换为Phonon能处理的格式,然后输出到视频窗口。
  • 添加了start_video_stream和stop_video_stream槽函数,分别处理播放和停止逻辑。
  • 扩大了窗口尺寸,让视频显示更合适。

注意事项

  • 如果你的摄像头设备不是/dev/video0,可以通过ls /dev/video*命令查看实际设备路径,修改管道中的device参数。
  • 确保树莓派的摄像头已经启用:可以通过raspi-config的Interfacing Options -> Camera来开启。

内容的提问来源于stack exchange,提问作者Anuj Rohilla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:30:29