作者:虚坏叔叔

早餐店不会开到晚上,想吃的人早就来了!😄

重载qt显示视频窗口完成图像设置和视频绘制

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_ffmpeg

一、创建显示视频窗口OpenGl Widget

打开qt设计师

​Qwidget​​​绘制视频效率低,可以使用​​OpenGl Widget​​。

拖动​​OpenGl Widget​​​到窗口,修改名称为​​view​

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_python_02

将打开按钮放到前面:

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_ffmpeg_03

宽度和高度都调整为​​600*400​

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_视频_04

​PyPlayer.cpp​​中修改视口大小:

if (width > 0 && height > 0)
{
resize(width, height);
ui.view->resize(width, height);
}

为了控制​​opengl​​窗口,需要提升view

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_视频_05

二、创建类VideoView用于重载widget

在​​Visual studio​​​中添加类​​VideoView​​​用于重载​​widget​

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_python_06

修改​​VideoView.h​

#pragma once

#include <QOpenGLWidget>
//Q_OBJECT表示自动会生成信号槽相关代码
class VideoView:public QOpenGLWidget
{
Q_OBJECT
public:
VideoView(QWidget *p = NULL);
~VideoView();

// 重载显示函数
void paintEvent(QPaintEvent *e);

public slots:
void SetImage(unsigned char* rgb); // 槽函数
signals:
void SetImageSig(unsigned char *rgb); // 信号函数

protected:
//存放显示图像
QImage *img = NULL;
};

修改​​VideoView.cpp​

#include "VideoView.h"
#include <QPainter>

void VideoView::SetImage(unsigned char* rgb)
{
if (!rgb)return;
if (!img)
{
/// 本窗口宽度
uchar *buf = new uchar[width()*height() * 4];

// 宽高发生变化需要再次分配空间
img = new QImage(buf, width(), height(), QImage::Format_ARGB32);
}
uchar *bits = img->bits();
memcpy(bits, rgb, width()*height() * 4);

// 刷新显示
update();
}

// 重载显示函数
void VideoView::paintEvent(QPaintEvent *e)
{
if (!img || img->isNull()) return;

QPainter painter;
painter.begin(this);
painter.drawImage(QPoint(0, 0), *img);
painter.end();
}

VideoView::VideoView(QWidget *p):QOpenGLWidget(p)
{
QObject::connect(
this, SIGNAL(SetImageSig(unsigned char*)),
this, SLOT(SetImage(unsigned char*))
);


}


VideoView::~VideoView()
{
}

三、Python调用SetImage实现图像显示

修改​​pyqt.py​​,调用SetImage来在线程中显示图像

isRunning = True
#输出的宽高,根据窗口尺寸
winWidth = 1280
winHeight = 720

#主函数 在子线程中调用,线程是c++创建
def main():
print("Python main")
global ff;
global winWidth;
global winHeight;
while isRunning:
#解封装
re = ff.read()
if re:
print(".", end='', flush = True) #flush输出缓冲
#解码图像
re = ff.decode(winWidth, winWidth)
print(re, end='', flush = True)
#显示图像
if re:
SetImage(ff)
time.sleep(0.02)
else:
time.sleep(1)

修改​​PyPlayer.cpp​​​添加函数​​SetImage​​,用于被python调用

#include <Python.h>
#include "PyPlayer.h"
#include <iostream>
#include <QFileDialog>
#include <thread>
#include "PyFFmpeg.h"
#include "XFFmpeg.h"
using namespace std;
static PyObject *pModule = 0;
static VideoView *view = 0;
PyObject *SetImage(PyObject *self, PyObject *arg)
{
PyFFmpeg *obj = (PyFFmpeg *)arg;
view->SetImageSig( obj->ff->rgb);
Py_RETURN_TRUE;

}

这里需要引入​​PyFFmpeg​​头文件路径

..\PyFFmpeg_qt

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_qt_07

由于需要用到​​rgb​​​,所以需要在​​XFFmpeg.h​​​中,将​​rgb​​改为public变量

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_qt_08

在​​PyPlayer.cpp​​构造函数中注册进去,这样python才能g狗调用到

Py_XDECREF(conf);

// 开放选择文件的接口给python
static PyMethodDef meths[] = {
{ "OpenDialog", (PyCFunction)OpenDialog, METH_NOARGS, 0 }
,{ "SetImage", (PyCFunction)SetImage, METH_O, 0 } // 一个参数
,{ NULL }
};

在​​PyPlayer​​类中,需要控制播放器窗口,所以定义一个静态view,然后在构造函数中初始化它:

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_c++_09

PyPlayer::PyPlayer(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
view = ui.view;

运行:

Python&C++相互混合调用编程全面实战-35重载qt显示视频窗口完成图像设置和视频绘制_python_10

四、总结

  • 本文重载qt显示视频窗口完成图像设置和视频绘制 。