static QObject* cpp_qobject_singletontype_provider(QQmlEngine* engine,QJSEngine* scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return cppobject;
}
qmlRegisterSingletonType<CppObject>("cppobject", 1, 0, "CppObject",cppobject_qobject_singletontype_provider);
auto box = new QVBoxLayout();
qml = new QQuickWidget();
setLayout(box);
qml->setResizeMode(QQuickWidget::SizeRootObjectToView);
qml->setClearColor(Qt::gray);
QMLObjectInterface* face = new QMLObjectInterface(this);
qml->engine()->rootContext()->setContextProperty("cplusplusObject",face);
qml->setSource(QUrl("QML.qml"));
foreach(auto error, qml->errors())
qDebug() << "error:" << error;
auto object = qml->rootObject();
object->setProperty("width",480);
auto items = object->findChildren<QQuickItem*>("button");
if (!items.isEmpty())
items[0]->setProperty("text",QStringLiteral("按键"));
box->addWidget(qml);
QML
import QtQuick 2.11
import QtQuick.Controls 2.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import QtQuick.Window 2.11
import "qmls" as QMLS
import cplusplus 1.0
Rectangle
{
id: base
width:640
height:480
ColumnLayout
{
anchors.fill:parent
anchors.margins: 6
spacing: 4
Rectangle
{
id:image
Layout.fillWidth: true
Layout.fillHeight: true
color:"#cdcdc0"
TextEdit
{
id:text
anchors.centerIn: parent
width:parent.width
text:"请点击下方按键"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font.pointSize: 12
}
}
RowLayout
{
Rectangle
{
Layout.fillWidth: true
}
Button
{
id:button
property int index: 0
property string buttonText:"Click"
text:buttonText
objectName: "button"
onClicked:
{
text.text = cplusplusObject.getString()
}
}
}
}
}
import QtQuick
2.11
import QtQuick.Controls 2.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
import QtQuick.Window 2.11
import cplusplus 1.0
Rectangle
{
id: base
width:640
height:480
ColumnLayout
{
anchors.fill:parent
anchors.margins: 6
spacing: 4
Rectangle
{
id:image
Layout.fillWidth: true
Layout.fillHeight: true
color:"#cdcdc0"
TextEdit
{
id:text
anchors.centerIn: parent
width:parent.width
text:"请点击下方按键"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font.pointSize: 12
}
}
RowLayout
{
Rectangle
{
Layout.fillWidth: true
}
Button
{
property int index: 0
text:"点击"
onClicked:
{
text.text = cplusplusObject.getString()
}
}
}
}
}
ListView
{
id: view
orientation: Qt.Horizontal
Layout.fillWidth: true
height:64
//model: 180
spacing: -24
clip: true
delegate: Rectangle
{
width: 50
height: parent.height
Image
{
id:imageItem
width:64
height:64
anchors.centerIn: parent
source:getFileName(index*Math.floor(2560/180))
property int current: 0
onSourceChanged:
{
current = index;
}
MouseArea
{
id: mouseArea
anchors.fill: parent
onClicked:
{
var filename = getFileName(Math.floor(2560/180)*imageItem.current)
image.source = filename
}
}
}
function getFileName(index)
{
return "images/"+index+".png"
}
}
}
import QtQuick 2.11
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
Rectangle
{
id: base
width:640
height:480
property int current: 0
ColumnLayout
{
anchors.fill:parent
anchors.margins: 6
spacing: 4
Image
{
id:image
Layout.fillWidth: true
Layout.fillHeight: true
source:"2436.png"
}
ProgressBar
{
id:progresBar
visible: false
Layout.fillWidth: true
value:0.0
}
Slider
{
id:slider1
Layout.fillWidth: true
orientation: Qt.Horizontal
onValueChanged:
{
base.current = Math.floor(2560*value)
var filename = "images/"+base.current+".png";
image.source = filename
}
}
Timer
{
interval: 40
running: true
triggeredOnStart: true;
repeat: true
onTriggered:
{
base.current = base.current + 1
if(base.current > 2560)
base.current = 0;
image.source = "images/" + base.current + ".png";
}
}
RowLayout
{
Rectangle
{
Layout.fillWidth: true
}
Button
{
text:"预览"
onClicked:
{
if(progresBar.visible)
{
progresBar.visible = false;
}
else
{
progresBar.visible = true;
}
}
}
Button
{
text:"剪切"
onClicked:
{
if(progresBar.visible)
{
progresBar.visible = false;
}
else
{
progresBar.visible = true;
}
}
}
}
}
}
#ifndef KEYWORD_HIGHLIGHTER_H
#define KEYWORD_HIGHLIGHTER_H
#include <QSyntaxHighlighter>
#include <QTextCharFormat>
#include <QRegularExpression>
#include <QTextDocument>
class KeyWordHighlighter final : public QSyntaxHighlighter
{
Q_OBJECT
public:
KeyWordHighlighter(QTextDocument* parent = nullptr);
void setKeyWords(const QStringList& keywords);
protected:
void highlightBlock(const QString& text)override;
private:
struct HighlightingRule
{
QRegularExpression pattern;
QTextCharFormat format;
};
QVector<HighlightingRule> highlightingRules;
QTextCharFormat keywordFormat;
};
#include "KeyWordHighLighter.h"
KeyWordHighlighter::KeyWordHighlighter(QTextDocument* parent):
QSyntaxHighlighter(parent)
{
}
void KeyWordHighlighter::setKeyWords(const QStringList& words)
{
HighlightingRule rule;
keywordFormat.setForeground(Qt::black);
keywordFormat.setFontWeight(QFont::Bold);
foreach(auto word,words)
{
rule.pattern = QRegularExpression(word);
rule.format = keywordFormat;
highlightingRules.append(rule);
}
}
void KeyWordHighlighter::highlightBlock(const QString& text)
{
foreach(auto rule,highlightingRules)
{
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
while(matchIterator.hasNext())
{
QRegularExpressionMatch match = matchIterator.next();
setFormat(match.capturedStart(),match.capturedLength(),rule.format);
}
}
}
#include <QStringList>
#include <QJsonArray>
#include <QJSonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QFile>
#include <QDebug>
#include "test.h"
#define STR(o) #o
#define INSERT_ITEM(object,o) object.insert(STR(o),o);
#define INSERT_LIST(object,o)\
{\
auto array = QJsonArray::fromStringList(o);\
object.insert(STR(o),array);\
}
struct Unit
{
QString result;
QString author;
QString description;
QString notes;
QStringList key;
int minValue;
QStringList items;
QStringList attachment;
QString toString()
{
QJsonObject object;
INSERT_ITEM(object,result)
INSERT_ITEM(object,author)
INSERT_ITEM(object,description)
INSERT_ITEM(object,notes)
INSERT_LIST(object,key)
INSERT_ITEM(object,minValue)
INSERT_LIST(object,items)
INSERT_LIST(object,attachment)
QJsonDocument doc;
doc.setObject(object);
return doc.toJson(QJsonDocument::JsonFormat::Compact);
}
bool loadFromFile(const QString& filename)
{
QFile file(filename);
if(!file.open(QIODevice::ReadOnly))
return false;
QByteArray buffer = file.readAll();
file.close();
QJsonParseError error;
QJsonDocument doc(QJsonDocument::fromJson(buffer,&error));
if(error.error != QJsonParseError::NoError)
{
qDebug() << "parse json file:"<<filename<< " failed!";
return false;
}
auto object = doc.object();
if(!object.contains("result") || !object.contains("key"))
{
qDebug() << "invalid json file:" << filename << " structure!";
return false;
}
result = object[STR(result)].toString();
author = object[STR(author)].toString();
description = object[STR(description)].toString();
notes = object[STR(notes)].toString();
minValue = object[STR(minValue)].toInt();
auto fn = [](const QJsonArray& array)->QStringList
{
QStringList items;
foreach(auto item,array)
items += item.toString();
return items;
};
key = fn(object[STR(key)].toArray());
items = fn(object[STR(items)].toArray());
attachment = fn(object[STR(attachment)].toArray());
return true;
}
};
void test()
{
Unit unit;
unit.attachment.append("acchemtn1");
unit.attachment.append("acchemtn2");
unit.key.append("key1");
unit.author = "author";
unit.description = "this is a remark";
unit.items.append("item1");
unit.items.append("item2");
unit.items.append("item3");
unit.minValue = 1;
unit.notes = "notes";
QFile file("file.json");
file.open(QIODevice::WriteOnly);
file.write(unit.toString().toLocal8Bit().data());
file.close();
qDebug() << unit.toString();
}
class VideoRenderer : public QObject, public QQuickFramebufferObject::Renderer, public QOpenGLFunctions
{
Q_OBJECT
public:
explicit VideoRenderer(QQuickFramebufferObject* object);
~VideoRenderer();
QOpenGLFramebufferObject* createFramebufferObject(const QSize& size);
void render();
public slots:
void updateVideoFrame(AVFrame* frame);
void paint();
private:
std::mutex mux;
QGLShaderProgram program;
GLuint yuv[3] = { 0 };
GLuint textures[3] = { 0 };
unsigned char* datas[3] = { 0 };
QSize videoSize;
QQuickFramebufferObject* fbo;
};
VideoRenderer::VideoRenderer(QQuickFramebufferObject* object):
QObject(object)
{
fbo = object;
initializeOpenGLFunctions();
qDebug() << program.addShaderFromSourceCode(QGLShader::Fragment, tString);
qDebug() << program.addShaderFromSourceCode(QGLShader::Vertex, vString);
program.bindAttributeLocation("vertexIn", A_VER);
program.bindAttributeLocation("textureIn", T_VER);
qDebug() << "program.link() = " << program.link();
qDebug() << "program.bind() = " << program.bind();
static const GLfloat ver[] = {
-1.0f,-1.0f,
1.0f,-1.0f,
-1.0f, 1.0f,
1.0f,1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(A_VER);
glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(T_VER);
yuv[0] = program.uniformLocation("tex_y");
yuv[1] = program.uniformLocation("tex_u");
yuv[2] = program.uniformLocation("tex_v");
mux.unlock();
glGenTextures(3, textures);
}
VideoRenderer::~VideoRenderer()
{
if(datas[0])
{
for(int i = 0; i < 3; i++)
delete datas[i];
}
}
void SaveAvFrame(const QString& file,AVFrame* avFrame)
{
FILE *fDump = fopen(file.toLocal8Bit().data(), "ab");
uint32_t pitchY = avFrame->linesize[0];
uint32_t pitchU = avFrame->linesize[1];
uint32_t pitchV = avFrame->linesize[2];
uint8_t *avY = avFrame->data[0];
uint8_t *avU = avFrame->data[1];
uint8_t *avV = avFrame->data[2];
for (uint32_t i = 0; i < avFrame->height; i++) {
fwrite(avY, avFrame->width, 1, fDump);
avY += pitchY;
}
for (uint32_t i = 0; i < avFrame->height / 2; i++) {
fwrite(avU, avFrame->width / 2, 1, fDump);
avU += pitchU;
}
for (uint32_t i = 0; i < avFrame->height / 2; i++) {
fwrite(avV, avFrame->width / 2, 1, fDump);
avV += pitchV;
}
fclose(fDump);
}
void VideoRenderer::updateVideoFrame(AVFrame* frame)
{
if(!frame)
return;
mux.lock();
if (frame->width != videoSize.width() || frame->height != videoSize.height())
{
videoSize.setWidth(frame->width);
videoSize.setHeight(frame->height);
if (datas[0])
{
for (int i = 0; i < 3; i++)
{
delete datas[i];
datas[i] = 0;
}
}
datas[0] = new unsigned char[frame->width*frame->height]; //Y
datas[1] = new unsigned char[frame->width*frame->height / 4]; //U
datas[2] = new unsigned char[frame->width*frame->height / 4]; //V
}
if (!datas[0])
{
datas[0] = new unsigned char[frame->width*frame->height]; //Y
datas[1] = new unsigned char[frame->width*frame->height / 4]; //U
datas[2] = new unsigned char[frame->width*frame->height / 4]; //V
}
if (videoSize.width() == frame->linesize[0])
{
memcpy(datas[0], frame->data[0], videoSize.width()*videoSize.height());
memcpy(datas[1], frame->data[1], videoSize.width()*videoSize.height() / 4);
memcpy(datas[2], frame->data[2], videoSize.width()*videoSize.height() / 4);
}
else
{
for (int i = 0; i < videoSize.height(); i++) //Y
memcpy(datas[0] + videoSize.width()*i, frame->data[0] + frame->linesize[0] * i, videoSize.width());
for (int i = 0; i < videoSize.height() / 2; i++) //U
memcpy(datas[1] + videoSize.width() / 2 * i, frame->data[1] + frame->linesize[1] * i, videoSize.width());
for (int i = 0; i < videoSize.height() / 2; i++) //V
memcpy(datas[2] + videoSize.width() / 2 * i, frame->data[2] + frame->linesize[2] * i, videoSize.width());
}
mux.unlock();
av_frame_free(&frame);
update();
}
void VideoRenderer::render()
{
paint();
}
void VideoRenderer::paint()
{
if (videoSize.isEmpty())
return;
mux.lock();
//Y
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, videoSize.width(), videoSize.height(), 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//U
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, videoSize.width() / 2, videoSize.height() / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
glBindTexture(GL_TEXTURE_2D, textures[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, videoSize.width() / 2, videoSize.height() / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]); // to y
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoSize.width(), videoSize.height(), GL_RED, GL_UNSIGNED_BYTE, datas[0]);
glUniform1i(yuv[0], 0);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, textures[1]); // 1 to u
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoSize.width() / 2, videoSize.height() / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
glUniform1i(yuv[1], 1);
glActiveTexture(GL_TEXTURE0 + 2);
glBindTexture(GL_TEXTURE_2D, textures[2]); // 2 to v
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, videoSize.width() / 2, videoSize.height() / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
glUniform1i(yuv[2], 2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
mux.unlock();
}
QOpenGLFramebufferObject* VideoRenderer::createFramebufferObject(const QSize &size)
{
QOpenGLFramebufferObjectFormat format; //当大小发生变化时,会调用此函数生成对应大小的FBO
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
format.setSamples(4);
return new QOpenGLFramebufferObject(size,format);
}
#ifndef APPLICATION_H
#define APPLICATION_H
#include <QApplication>
#include <QSharedMemory>
class Application : public QApplication
{
Q_OBJECT
public:
Application(int argc,char** argv,const QString& app);
~Application();
public:
bool isRunning();
private:
QSharedMemory sharedMemory;
};
#endif
#include "Application.h"
Application::Application(int argc,char** argv,const QString& app):
QApplication(argc,argv),
sharedMemory(app)
{
if(!sharedMemory.isAttached())
sharedMemory.create(app.size()+1);
}
Application::~Application()
{
}
bool Application::isRunning()
{
return sharedMemory.attach();
}
QML
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
Column
{
width:640
height:400
Image
{
id:image
width:640
cache: false
source: "image://colors/red"
}
Connections
{
target: imagecontext
onCallQmlRefeshImg:
{
image.source = ""
image.source = "image://imageprovider"
console.log("refresh.");
}
}
}
ImageProvider
class ColorImageProvider : public QQuickImageProvider
{
public:
ColorImageProvider()
: QQuickImageProvider(QQuickImageProvider::Pixmap)
{
}
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override
{
int width = 100;
int height = 50;
if (size)
*size = QSize(width, height);
QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width,
requestedSize.height() > 0 ? requestedSize.height() : height);
pixmap.fill(QColor(id).rgba());
return pixmap;
}
};
class ImageProvider : public QQuickImageProvider
{
public:
ImageProvider::ImageProvider(ImageType type, Flags flags = Flags()) :
QQuickImageProvider(type, flags)
{
}
~ImageProvider(){}
QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
return image;
}
QImage image;
};
ImageContext
class ImageContext : public QObject
{
Q_OBJECT
public:
ImageContext(QObject* parent = 0);
ImageProvider* m_imageProvider = nullptr;
public slots:
void setImage(const QImage& image);
signals:
void callQmlRefeshImg();
};
ImageContext::ImageContext(QObject* parent):
QObject(parent)
{
m_imageProvider = new ImageProvider(QQuickImageProvider::Image);
}
void ImageContext::setImage(const QImage& image)
{
m_imageProvider->image = image;
emit callQmlRefeshImg();
}
注册和相应
qml->engine()->addImageProvider(QLatin1String("colors"),new ColorImageProvider);
ImageContext* context = new ImageContext(this);
qml->engine()->rootContext()->setContextProperty("imagecontext",context);
qml->engine()->addImageProvider(QLatin1String("imageprovider"),context->m_imageProvider);
qml->setSource(QUrl("image.qml"));
QDir dir("E:/MyVideoPlayer/Win32/Release/capture");
QStringList files = dir.entryList(QDir::Files,QDir::SortFlag::Time | QDir::SortFlag::Reversed);
qDebug() << "files:" << files.size();
connect(button, &QPushButton::clicked,[=]()
{
static int current = 0;
auto list = qml->rootObject()->childItems();
if(current == files.size())
current = 0;
QImage image;
QString file = QString("E:/MyVideoPlayer/Win32/Release/capture/") + files.at(current);
qDebug() << "loaded:" << image.load(file) << "," << file<<","<<current;
context->setImage(image);
current++;
}
);