How to work on VideoFrame after applying ShaderEffect - qt

(Sorry for my bad english)
I want to work on a QVideoFrame after applying a ShaderEffect. I use a QObject based class with a writable videoSurface property that can accept a QAbstractVideoSurface based class
class VideoProducer : public QMediaPlayer
{
Q_OBJECT
Q_PROPERTY(QAbstractVideoSurface * videoSurface READ videoSurface WRITE setVideoSurface)
public:
....
private:
QAbstractVideoSurface *video_surface;
public slots:
// function to present frame
void onNewVideoContentReceived(const QVideoFrame &frame);
};
In my main.cpp file:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
VideoProducer videoProducer;
QtQuick2ApplicationViewer viewer;
// to use videoProducer in QML
viewer.engine()->rootContext()->setContextProperty("videoProducer", &videoProducer);
....
}
In my main.qml file:
VideoOutput {
id: videoOutput
objectName: "videoOutput"
anchors.fill: parent
source: videoProducer
visible: true
}
And the Shader Effect
ShaderEffect {
property variant source: ShaderEffectSource {
anchors.fill: parent
id: second_window
live: true
sourceItem: videoOutput
}
anchors.fill: parent
fragmentShader: "...."
}
Now I want to bring the Frame (after shader effect) in C++ to make some operations on it... can anyone help me?

Related

QtQuick's ListView fails to take ownership of QAbstractItemModel object

Based on Qt documentation, whenever a QObject pointer type is passed from C++ code to QML, via a Q_INVOKABLE method, there is a set of rules that determine who is responsible for the lifetime of that pointer. Should the QObject be parentless, implicitly the QML engine is responsible for taking ownership of the pointer.
In my scenario, I want my frontend UI to represent a list model which is generated/provided by the backend C++ code. My assumption is that the pointer will stay alive as long as there is a reference to it by the QML code. The code below shows the trimmed down test case:
Main.cpp
#include <QAbstractItemModel>
#include <QDebug>
#include <QGuiApplication>
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStringListModel>
class MyStringListModel : public QStringListModel
{
Q_OBJECT
public:
explicit MyStringListModel(const QStringList &strings, QObject* parent=nullptr) : QStringListModel(strings, parent)
{
qDebug() << "Creation";
}
virtual ~MyStringListModel() override
{
qDebug() << "Destruction";
}
};
class Backend : public QObject
{
Q_OBJECT
public:
Backend(QObject* parent=nullptr) : QObject(parent)
{
}
Q_INVOKABLE QAbstractItemModel* createModel() const
{
static const QStringList months = {
tr("January"),
tr("February"),
tr("March"),
tr("April"),
tr("May"),
tr("June"),
tr("July"),
tr("August"),
tr("September"),
tr("October"),
tr("November"),
tr("December"),
};
return new MyStringListModel(months);
}
};
int main(int argc, char* argv[])
{
QGuiApplication application(argc, argv);
qmlRegisterType<QAbstractItemModel>();
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load("qrc:///ui/main.qml");
return application.exec();
}
#include "main.moc"
Main.qml
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.1
ApplicationWindow {
id: window
width: 200
height: 250
visible: true
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: backend.createModel()
delegate: Text {
anchors.horizontalCenter: parent.horizontalCenter
text: model.display
}
}
Button {
Layout.alignment: Qt.AlignCenter
text: qsTr("Garbage Collect")
onClicked: gc()
}
}
}
This is a screenshot of the program:
The moment the user clicks on the button, the garbage collector runs and destroys the model ptr (destruction is evident by the "Creation" and "Destruction" output in the stdout).
I'm curious to know why the pointer was destroyed? I've noticed that it didn't set the ListView as its parent, which is fair enough, I thought that the QML engine would have used some form of reference pointer to try keep track of who still holds a reference to it. Is there a document which gives greater insight into the way in which garbage collection / ownership is implemented.
Likewise, is there a better way of structuring this code while still meeting the demands of passing a parentless QObject back to QML.
It seems that the reason for the destruction is because the object is not being referenced in QML, for example if it is assigned to a property the garbage collector will not affect it:
ApplicationWindow {
id: window
width: 200
height: 250
visible: true
property var mymodel: backend.createModel()
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: mymodel
delegate: Text {
anchors.horizontalCenter: parent.horizontalCenter
text: display
}
}
Button {
Layout.alignment: Qt.AlignCenter
text: qsTr("Garbage Collect")
onClicked: gc()
}
}
}

QSyntaxHighlighter rehighlight() ignores changes triggered from another thread

I have a spellchecking thread which fires spellcheck() signals from time to time which are connected to my highlighter's rehighlight() method. The latter sets the whole block to have red foreground.
This used to work in Qt 5.6.2 and ceased to work in newer versions. I hopelessly waited for it to get fixed in Qt 5.9.5, but it still does not work (neither in Windows 10, nor in OS X).
Small example which reproduces the problem could be obtained from here https://bitbucket.org/ribtoks/qt-highlighting-issue (in order to repro, type something in the input. rehighlight() will be triggered each 7 seconds from background thread)
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickTextDocument>
#include <QSyntaxHighlighter>
#include <QThread>
#include <QString>
class SpellCheckWorker : public QObject
{
Q_OBJECT
public:
explicit SpellCheckWorker(QObject *parent = 0) : QObject(parent), m_Counter(0), m_IsOK(false)
{ }
public:
bool isOK() { return m_IsOK; }
signals:
void spellcheck();
public slots:
void process() {
qInfo() << "Worker Thread is" << QThread::currentThreadId();
while (1) {
m_Counter++;
m_IsOK = m_Counter % 7 == 0;
QThread::sleep(1);
emit spellcheck();
}
}
private:
int m_Counter;
volatile bool m_IsOK;
};
class SpellCheckErrorsHighlighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
SpellCheckErrorsHighlighter(SpellCheckWorker *worker, QTextDocument *document):
QSyntaxHighlighter(document),
m_Worker(worker)
{ }
virtual ~SpellCheckErrorsHighlighter() {}
protected:
virtual void highlightBlock(const QString &text) override {
if (!m_Worker->isOK()) {
qDebug() << "Worker is not OK" << text;
return;
}
qInfo() << "Reapplied formatting for" << text;
qInfo() << "Highlight thread is" << QThread::currentThreadId();
this->setFormat(0, text.length(), QColor(0xff, 0, 0));
}
private:
SpellCheckWorker *m_Worker;
};
class MainModel : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
explicit MainModel(QObject *parent = 0) : QObject(parent), m_Worker(nullptr)
{ }
public:
QString name() const { return m_name; }
void startChecking() {
qInfo() << "Main thread is" << QThread::currentThreadId();
m_Worker = new SpellCheckWorker();
QThread *thread = new QThread();
m_Worker->moveToThread(thread);
QObject::connect(thread, &QThread::started, m_Worker, &SpellCheckWorker::process);
thread->start();
}
Q_INVOKABLE void initNameHighlighting(QQuickTextDocument *document) {
SpellCheckErrorsHighlighter *highlighter = new SpellCheckErrorsHighlighter(m_Worker, document->textDocument());
QObject::connect(m_Worker, &SpellCheckWorker::spellcheck,
highlighter, &SpellCheckErrorsHighlighter::rehighlight);
}
signals:
void nameChanged();
public slots:
void setName(QString name)
{
if (m_name == name)
return;
m_name = name;
emit nameChanged();
}
private:
SpellCheckWorker *m_Worker;
QString m_name;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
MainModel mainModel;
mainModel.startChecking();
QQmlApplicationEngine engine;
QQmlContext *rootContext = engine.rootContext();
rootContext->setContextProperty("mainModel", &mainModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
and main.qml
import QtQuick 2.6
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
StackView {
id: mainStackView
anchors.fill: parent
focus: true
initialItem: Rectangle {
anchors.fill: parent
Rectangle {
id: titleRect
height: 30
width: 300
anchors.centerIn: parent
color: "#ffffff"
border.color: "#000000"
border.width: titleTextInput.activeFocus ? 1 : 0
clip: true
focus: false
Flickable {
id: titleFlick
contentWidth: titleTextInput.paintedWidth
contentHeight: titleTextInput.paintedHeight
height: parent.height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 5
anchors.rightMargin: 5
clip: true
flickableDirection: Flickable.HorizontalFlick
interactive: false
focus: false
function ensureVisible(r) {
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
}
TextEdit {
id: titleTextInput
width: paintedWidth > titleFlick.width ? paintedWidth : titleFlick.width
height: titleFlick.height
text: mainModel.name
focus: true
onTextChanged: mainModel.name = text
Component.onCompleted: mainModel.initNameHighlighting(titleTextInput.textDocument)
onCursorRectangleChanged: titleFlick.ensureVisible(cursorRectangle)
}
}
}
}
}
}
Is there any way to get it working with workarounds? I need to keep spellchecking logic in the background thread so it's not possible to move it to main thread.
You seem to be referring to two different root causes with the following two statements:
There's NO problem in delivering slot call to Highlighter.
and
m_Worker->isOK() check is there for a reason to execute highlighting only once per 7 seconds. The bug in Qt can only be demonstrated with this.
If I just focus on the second statement, it looks like the problem is that you are not able to hit the statement if (!m_Worker->isOK()) right?
That may well be a problem due to your code or a Qt issue on a specific platform.
Can you change the code to avoid the condition i.e. emit the spellcheck signal only when 7 seconds have passed to avoid making this check from another thread?
Ok, so after messing with your code, The issue is that you are not calling this line inside of the hilightBlock directive in main.cpp near line 55 in order to keep the format from before.
Add this in to fix it (I think) The question was still fairly unclear...
if (!m_Worker->isOK()) {
qDebug() << "Worker is not OK" << text;
this->setFormat(0, text.length(), this->format(0));
return;
}
The other part of the issue is that you don't have any rules defined for the syntax highlighter... so it will always be red once it turns red.

SetProperty for loaded QML component without using SetContextProperty

I have a C++ plugin system, where a QQmlComponent is created and a qml file is loaded when the user requests a new plugin instance.
Currently I am using setContextProperty() to tell QML about a QObject that is needed for proper initialization.
mEngine->rootContext()->setContextProperty("controller", QVariant::fromValue(mController));
mComponent = new QQmlComponent(mEngine);
mComponent->loadUrl(QUrl{ "qrc:///MyPlugin.qml" });
The problem is, when instantiating a second plugin, both will use the controller of the second one because "controller" is global in QML.
Repeater {
model: controller.numEntries
Is there a way to set a context property locally (only for the current instance)?
I found solutions using setProperty() or QQmlIncubator and setInitialState(), but they all seem to require an object that was already created from my component. But in my plugin I only define the component, which is loaded in the main application through a Loader item. So, when trying these approaches, I always ended up in setting the value in a copy of the item, but not the one being created in my backend.
How can I get access to a property of the component that is created in QML?
mComponent->findChild<QQuickItem*>("controller");
does not give me any results, even if I defined the property in MyPlugin.qml.
Maybe you can create a QObject based class and have a slot and instead of using property you can call slot to slot create a new property in c++ and return it to QML
ControllerCreator.h
#ifndef CONTROLLERCREATOR_H
#define CONTROLLERCREATOR_H
#include <QObject>
class ControllerCreator : public QObject {
Q_OBJECT
public:
explicit ControllerCreator(QObject *parent = nullptr);
signals:
public slots:
int propertyCreator();
private:
int m_example;
};
#endif // CONTROLLERCREATOR_H
ControllerCreator.cpp
#include "ControllerCreator.h"
ControllerCreator::ControllerCreator(QObject *parent)
: QObject(parent), m_example(0)
{
}
int ControllerCreator::propertyCreator()
{
m_example++;
return m_example;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "ControllerCreator.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
ControllerCreator controllerCreator;
engine.rootContext()->setContextProperty("creator", &controllerCreator);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Column{
anchors.fill: parent
Text{
text: creator.propertyCreator()
color: "blue"
}
Text{
text: creator.propertyCreator()
color: "red"
}
Text{
text: creator.propertyCreator()
color: "green"
}
}
}

Using QAbstractItemModel to return QQuickPaintedItem for use in QML delegate

I implemented a subclass of QQuickPaintedItem to be used in QML which works on its own when registered through
qmlRegisterType<T>
Instances of this class are created at application startup and put into a QList that is stored inside a subclass of QAbstractItemModel. I thought I could easily return each of those objects in the model's data method and use them as the QML ListViews delegate.
It now looks like this:
Model.cpp:
QVariant AbteilungsModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && role == Qt::DisplayRole)
{
Abteilung* a = static_cast<Abteilung*>(index.internalPointer());
return QVariant::fromValue(a);
}
}
main.qml:
ListView {
id: abteilungenListView
anchors.fill: parent
spacing: 5
model: abteilungen
delegate: modelData
}
I, of course, made the model available in QML via
void QQmlContext::setContextProperty(const QString & name, QObject * value)
but I don't know how to properly declare the ListViews delegate, since "modelData" doesn't work.
Does anyone have an idea if this is even possible or do you guys have a better solution?
Any help is appreciated! :)
It might be possible, but it goes against the whole MVC idea. Your model shouldn't know about your delegates. As a simplified example:
main.cpp
#include <QGuiApplication>
#include <QtQml>
#include <QtQuick>
class Abteilung : public QQuickPaintedItem
{
Q_OBJECT
public:
Abteilung() {
}
void paint(QPainter *painter) {
painter->setPen(Qt::red);
painter->drawRect(boundingRect().adjusted(0, 0, -painter->pen().width(), -painter->pen().width()));
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<Abteilung>("Test", 1, 0, "Abteilung");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.2
import QtQuick.Controls 1.1
import Test 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
ListView {
id: abteilungenListView
anchors.fill: parent
spacing: 5
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < 100; ++i) {
append({name: i});
}
}
}
delegate: Abteilung {
width: abteilungenListView.width
height: 40
}
}
}

How to add context menu to qml QDeclarativeView?

I need ContextMenu in my qml widgets.
I have a solution: create QGraphicsProxyWidget, which contain QMenu, but there is a problem: the context menu is not visible outside the main window. How to set main window as parent of menu?
Custom components is a bad idea - I need possibilities of the QMenu: exec, actions, pop-up and other.
Main.qml
import QtQuick 1.1
import CustomComponents 1.0
Rectangle {
width: 360
height: 360
QMLContextMenu {
id: menu
}
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.LeftButton
onClicked: {
if(mouse.button === Qt.RightButton)
menu.exec(mouse.x, mouse.y);
else
Qt.quit();
}
}
}
main.cpp
#include <QApplication>
#include "qmlapplicationviewer.h"
#include <QtCore>
#include <QtDeclarative>
#include <QtGui>
class QMLContextMenu : public QGraphicsProxyWidget
{
Q_OBJECT
public:
QMLContextMenu(QGraphicsItem* parent = 0) : QGraphicsProxyWidget(parent)
{
menuWidget = new QMenu("my menu");
setWidget(menuWidget);
}
public slots:
QString exec(int x, int y)
{
menuWidget->clear();
menuWidget->addAction("hello world!");
menuWidget->addSeparator();
menuWidget->addAction("or not...");
//menuWidget->show();
QAction *pResultAction = menuWidget->exec(QPoint(x, y));
QString text;
if(pResultAction)
text = pResultAction->text();
return text;
}
private:
QMenu *menuWidget;
};
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
qmlRegisterType<QMLContextMenu>("CustomComponents", 1, 0, "QMLContextMenu");
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/quick1/main.qml"));
viewer.showExpanded();
return app->exec();
}
Create you own rectangle with listview. But in this way there are lots of problems because in qml 1 qml widgets can't be top-level windows. I did that:
Create separate ContextMenu.qml
Use loader to instantiate it
show it when I need it

Resources