Black texture blinking using QQuickImageProvider - qt

I'm using QQuickImageProvider to pass 3d scanner images from C++ to QML and then use these images as customer texture for model (using qt quick 3d here). I faced a problem that texture blinks getting to black for a second. It repeats quite often.
I used to investigate streaming images as VideoOutput but this approach doesn't suit me as I have to use geometry data of surface and in case of VideoOutput it doesn't allow to map these images on geometry coordinates. Could you advise something in this case?
Below is example of my code.
QML:
Image {
id: textureImage
source: renderControl.sourceUrl
anchors.fill: parent
visible: false // tried with cache true and false both
}
Node {
id: mainScene
DirectionalLight {
position: Qt.vector3d(-500, 500, -100)
ambientColor: Qt.rgba(1.0, 1.0, 1.0, 0.5)
}
Model {
id: viewModel
position: Qt.vector3d(0, 50, 0)
visible: true
scale: Qt.vector3d(10, 11, 4)
geometry: RenderData{ // C++ class which sends url to image provider
id: renderControl
objectName: "geometryData"
}
DefaultMaterial {
id: defaultMaterial
Texture {
id: baseColorMap
sourceItem: textureImage
}
cullMode: DefaultMaterial.NoCulling
diffuseMap: baseColorMap
}
materials: [
defaultMaterial
]
}
C++
.h
class TextureImageProvider : public QObject, public QQuickImageProvider
{
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
Q_SIGNALS:
void imageUpdated(const QUrl& imageUrl);
std::shared_ptr<QImage> textureImage_;
}
class RenderData : public QQuick3DGeometry
{
Q_OBJECT
Q_PROPERTY(QUrl sourceUrl READ sourceUrl WRITE setSourceUrl NOTIFY sourceUrlChanged)
TextureImageProvider* imageProvider_;
}
.cpp
void RenderData::updateData(QImage& textureImage)
{
const QUrl imageUrl = QString("image://texture_image_provider/%1").arg(Uuid::createUuid().toString().c_str());
textureImage_ = std::make_shared<QImage>(image);
Q_EMIT imageUpdated(url);
}
QImage TextureImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize)
{
return *textureImage_;
}
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
engine_->addImageProvider("texture_image_provider", &renderData_-getImageProvider());
}

Related

Drawing QQuickItem's texture in a "draw-chain" through 2 QQuickFramebufferObjects fails

First off, sorry for the long source code but I couldn't reduce it any further while still having the bug happen. Most of it is uninteresting boilerplate though, so you can mostly get away with just my human-readable summary below.
Abbreviations used in this post:
QQFBO = QQuickFramebufferObject
Observed result: Two rotating squares, that's all.
What's missing from the observed result: A third, additional, non-rotating square, which is actually a static snapshot from the second one.
What my code does, in short:
class ClonerItem is a QQFBO just fetches a texture from a QQuickItem and draws it to the screen.
The ClonerItemQml is a Qml wrapper of ClonerItem that adds a Timer to it. Depending on the ClonerItem.live property, the timer either calls ClonerItem.update() constantly or not at all. (basically the same the live property of Qt's ShaderEffectSource)
In main.qml I create a "producer-consumer-chain" of 3 items in a RowLayout.
The first item is a rotating rectangle with layer.enabled: true.
The second item is a ClonerItemQml (with live: true) that has the previous one as a texture source.
The third item is a ClonerItemQml (called "snapshotter", with live: false) that has the the previous one as a texture source.
"snapshotter" has its update method connected to 2 signals:
A firstRedraw signal provided by item "2", which is called at the end of the first redraw of item "2"
For testing, the clicked signal for "snapshotter", so you can see that the "snapshotter" does work correctly when you force it to update later during the program's execution.
Any hints what I'm doing wrong?
Note: When I remove the Timer and instead of it, uncomment the if(m_live) update(); in render, it works fine.
My code:
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickFramebufferObject>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QOpenGLFramebufferObject>
#include <QQuickWindow>
// propertyhelper.h is from http://syncor.blogspot.bg/2014/11/qt-auto-property.html
#include "propertyhelper.h"
#include <QSGTextureProvider>
class ClonerItem : public QQuickFramebufferObject {
Q_OBJECT
AUTO_PROPERTY(QQuickItem*, sourceItem)
AUTO_PROPERTY(bool, live)
public:
Renderer* createRenderer() const;
signals:
void firstRedraw();
};
class ClonerItemRenderer : public QObject, public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions {
Q_OBJECT
public:
ClonerItemRenderer() {
initializeOpenGLFunctions();
m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader.vert.glsl");
m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shader.frag.glsl");
m_program.link();
createGeometry();
}
void render() {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
m_program.bind();
glActiveTexture(GL_TEXTURE0);
m_tex->bind();
m_program.setUniformValue("uTex", 0);
paintGeometry();
glFlush();
glFinish();
m_window->resetOpenGLState();
if(!m_haveRedrawnAtLeastOnce)
emit firstRedraw();
m_haveRedrawnAtLeastOnce = true;
//if(m_live)
// update();
}
void paintGeometry() {
m_program.enableAttributeArray("aPos");
m_program.setAttributeArray("aPos", m_vertices.constData());
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
}
void createGeometry() {
m_vertices << QVector2D(-1, -1) << QVector2D(-1, +1) << QVector2D(+1, +1);
m_vertices << QVector2D(-1, -1) << QVector2D(+1, -1) << QVector2D(+1, +1);
}
QOpenGLFramebufferObject* createFramebufferObject(const QSize &size) {
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
return new QOpenGLFramebufferObject(size, format);
}
private:
QVector<QVector2D> m_vertices;
QOpenGLShaderProgram m_program;
QQuickWindow* m_window;
QSGTexture* m_tex;
bool m_haveRedrawnAtLeastOnce = false;
bool m_live;
protected:
void synchronize(QQuickFramebufferObject* qqfbo) {
ClonerItem* parentItem = (ClonerItem*)qqfbo;
m_window = parentItem->window();
m_live = parentItem->live();
// fetch texture pointer
QQuickItem* sourceItem = parentItem->sourceItem();
QSGTextureProvider* sourceTexProvider = sourceItem->textureProvider();
m_tex = sourceTexProvider->texture();
}
signals:
void firstRedraw();
};
QQuickFramebufferObject::Renderer* ClonerItem::createRenderer() const {
auto renderer = new ClonerItemRenderer();
connect(renderer, &ClonerItemRenderer::firstRedraw, this, &ClonerItem::firstRedraw);
return renderer;
}
int main(int argc, char **argv) {
QGuiApplication app(argc, argv);
qmlRegisterType<ClonerItem>("ClonerItem", 1, 0, "ClonerItem");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
ClonerItem.qml:
import QtQuick 2.0
import ClonerItem 1.0
ClonerItem {
id: root
Timer {
id: renderTimer
running: root.live
interval: 1000/60
repeat: true
onTriggered: {
root.update();
}
}
}
main.qml:
import QtQuick 2.0
import ClonerItem 1.0
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
Window {
visible: true
width: 600
height: 200
RowLayout {
id: rowLayout
anchors.fill: parent
Item {
id: original
layer.enabled: true
Layout.fillWidth: true
Layout.fillHeight: true
RotatingSquare { }
}
ClonerItemQml {
id: liveClone
layer.enabled: true
sourceItem: original
live: true
Layout.fillWidth: true
Layout.fillHeight: true
onFirstRedraw: {
snapshotter.update();
}
}
ClonerItemQml {
id: snapshotter
sourceItem: liveClone
live: false
Layout.fillWidth: true
Layout.fillHeight: true
MouseArea {
anchors.fill: parent
onClicked: {
snapshotter.update();
}
}
}
}
}
RotatingSquare.qml:
import QtQuick 2.0
Rectangle {
color: "red"
border.color: "black"
width: parent.width / 2
height: parent.height / 2
anchors.centerIn: parent
NumberAnimation on rotation {
running: true
loops: Animation.Infinite
duration: 1000
from: 0
to: 360
}
}
shader.frag.glsl:
varying highp vec2 vTexCoord;
uniform sampler2D uTex;
void main() {
gl_FragColor = texture2D(uTex, vTexCoord);
}
shader.vert.glsl
attribute highp vec2 aPos;
varying highp vec2 vTexCoord;
void main() {
gl_Position = vec4(aPos, 0.0, 1.0);
// aPos's components are in [-1, 1], transform them to the range [0, 1] for use as texcoords.
vTexCoord = aPos * .5 + vec2(.5);
}
If you're wondering why I'm doing such an elaborate/weird setup, I can elucidate: in my real app the second item in the "chain" is not a simple "cloner" but actually uses its source texture(s) for some more-complex rendering (which is also memory-hungry). And the third item in the "chain" is necessary to snapshot the result of the second one, so the second one can be destroyed when not animated, freeing up memory.
The reason I implement my own ClonerItem component, which does basically the same as Qt's ShaderEffectSource, is this Qt bug.

How to take a screen shot of the window and display it on an image using QML?

I want to take a screen shot using the code
QImage img = currentView_->grabWindow();
and i want to display this screen shot on my GUI using QML.
How to do this ?
If you want to transfer an image from C++ to QML the best way I guess is QQuickImageProvider. The small example bellow is maybe a bit different from what you want but anyway:
QQuickImageProvider class:
#ifndef IMAGEPROVIDER_H
#define IMAGEPROVIDER_H
#include <QQuickImageProvider>
#include <QQuickView>
#include <QImage>
class ImageProvider : public QObject, public QQuickImageProvider
{
Q_OBJECT
public:
ImageProvider(QObject *parent = 0, Flags flags = Flags());
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
protected:
QMap<int, QImage> m_images;
public slots:
void makeScreenshot();
};
#endif // IMAGEPROVIDER_H
#include "imageprovider.h"
ImageProvider::ImageProvider(QObject *parent, Flags flags) :
QQuickImageProvider(QQmlImageProviderBase::Image, flags),
QObject(parent)
{
}
QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
bool ok = false;
QImage img = m_images[id.toInt(&ok)];
return img;
}
void ImageProvider::makeScreenshot()
{
QQuickWindow *view = static_cast<QQuickWindow *>(sender());
static int pos = 0;
QImage img = view->grabWindow();
m_images.insert(pos,img);
QVariant returnedValue;
QMetaObject::invokeMethod(view, "setImage",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, pos++));
}
Registering it in main.cpp:
QQmlApplicationEngine engine;
ImageProvider *imageProvider = new ImageProvider();
engine.addImageProvider(QLatin1String("screenshots"), imageProvider);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QQuickWindow *view = dynamic_cast<QQuickWindow *>(engine.rootObjects().at(0));
QObject::connect(view, SIGNAL(getScreenshot()),
imageProvider, SLOT(makeScreenshot()));
Usage:
import QtQuick 2.7
import QtQuick.Window 2.0
Window {
id:container
width: 600
height: 600
visible: true
signal getScreenshot()
Repeater {
model: 20
delegate: Rectangle {
x: Math.random() * 500
y: Math.random() * 500
width: Math.random() * 50 + 50
height: width
radius: width / 2
color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
}
}
Text {
id: txt
anchors.centerIn: parent
text: "Click me"
}
Rectangle {
id: rect
anchors.centerIn: parent
width: 0
height: 0
border.width: 5
border.color: "black"
Image {
id: img
anchors.fill: parent
anchors.margins: 5
source: ""
fillMode: Image.PreserveAspectFit
}
ParallelAnimation {
id: anim
PropertyAnimation {
property: "width"
target: rect
from: 0
to: 200
duration: 500
}
PropertyAnimation {
property: "height"
target: rect
from: 0
to: 200
duration: 500
}
}
}
MouseArea {
anchors.fill: parent
onClicked: {
txt.visible = false;
container.getScreenshot();
}
}
function setImage(id)
{
img.source = "image://screenshots/" + id;
anim.running = true;
}
}

Loading multiple images on a page for Blackberry 10

I am working on BB10 cascades. I am trying to load multiple images from network. I have referred following example and I have modified it to load a single image. Sample code is as follows:
main.qml
import bb.cascades 1.2
Page {
Container {
id: outer
Container {
preferredHeight: 500
preferredWidth: 768
layout: DockLayout {}
onCreationCompleted: {}
// The ActivityIndicator that is only active and visible while the image is loading
ActivityIndicator {
id: activity
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
preferredHeight: 300
visible: _loader.loading
running: _loader.loading
}
// The ImageView that shows the loaded image after loading has finished without error
ImageView {
id: image
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
image: _loader.image
visible: !_loader.loading && _loader.label == ""
}
// The Label that shows a possible error message after loading has finished
Label {
id: lable
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
preferredWidth: 500
visible: !_loader.loading && !_loader.label == ""
text: _loader.label
multiline: true
}
}
Button {
text: "load Image"
onClicked: {
_loader.load();
console.log("loading:::"+_loader.loading);
}
}
}
}
applicatioui.hpp
#ifndef ApplicationUI_HPP_
#define ApplicationUI_HPP_
#include "imageloader.hpp"
#include
namespace bb
{
namespace cascades
{
class LocaleHandler;
}
}
class QTranslator;
class ApplicationUI : public QObject
{
Q_OBJECT
public:
ApplicationUI();
virtual ~ApplicationUI() {}
Q_INVOKABLE void prepareImage();
Q_INVOKABLE void loadImage();
Q_INVOKABLE ImageLoader* getImageloadderInstance();
private slots:
void onSystemLanguageChanged();
private:
ImageLoader* image;
QTranslator* m_pTranslator;
bb::cascades::LocaleHandler* m_pLocaleHandler;
};
#endif /* ApplicationUI_HPP_ */
applicatioui.cpp
#include "applicationui.hpp"
#include
#include
#include
#include
using namespace bb::cascades;
ApplicationUI::ApplicationUI() :
QObject()
{
// prepare the localization
m_pTranslator = new QTranslator(this);
m_pLocaleHandler = new LocaleHandler(this);
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
bool res = QObject::connect(m_pLocaleHandler, SIGNAL(systemLanguageChanged()), this, SLOT(onSystemLanguageChanged()));
// This is only available in Debug builds
Q_ASSERT(res);
// Since the variable is not used in the app, this is added to avoid a
// compiler warning
Q_UNUSED(res);
// initial load
onSystemLanguageChanged();
// Create scene document from main.qml asset, the parent is set
// to ensure the document gets destroyed properly at shut down.
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
qml->setContextProperty("_loader",this);
// Create root object for the UI
AbstractPane *root = qml->createRootObject();
if(root)
{
}
// Set created root object as the application scene
Application::instance()->setScene(root);
}
void ApplicationUI::onSystemLanguageChanged()
{
QCoreApplication::instance()->removeTranslator(m_pTranslator);
// Initiate, load and install the application translation files.
QString locale_string = QLocale().name();
QString file_name = QString("loading_Image_%1").arg(locale_string);
if (m_pTranslator->load(file_name, "app/native/qm")) {
QCoreApplication::instance()->installTranslator(m_pTranslator);
}
}
ImageLoader* ApplicationUI::getImageloadderInstance()
{
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
return (image);
}
void ApplicationUI::prepareImage()
{
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
}
void ApplicationUI::loadImage()
{
image->load();
}
Now I want to load multiple images. I tried to create QList<QObject*>* image;
and add instances of the ImageLoader class, but I don't know how to access it in main.qml.
Any ideas how to do so?
https://github.com/RileyGB/BlackBerry10-Samples/tree/master/WebImageViewSample
follow this link there is a control called "WebImageView". you just have to set the url of image and its done. int main.qml coder have shown user friendly UI.
thanks to #Bojan Kogoj

How to insert ListElement in a QML ListModel using C++?

Well, i'm learning to work with QML and i have one doubt. In my example, i have a ListModel with ListElements at QML and i have a QML main file with a rectangle, PathView etc.
I have a QWidget also, than is my main window. In this QWidget i include the QML UI like a component. Ok!
How can I handle QML ListElements by using C++?
Note: when I say "to handle", I want to say include an element for example.
Below are some parts of my code...
QML containing my ListElement, called "Menu1":
import QtQuick 1.0
ListModel {
id: listMovieModel
ListElement { name: "Image 1"; iconSource: "pics/image_1.jpg" }
ListElement { name: "Image 2"; iconSource: "pics/image_2.jpg" }
ListElement { name: "Image 3"; iconSource: "pics/image_3.jpg" }
ListElement { name: "Image 4"; iconSource: "pics/image_4.jpg" }
ListElement { name: "Image 5"; iconSource: "pics/image_5.jpg" }
ListElement { name: "Image 6"; iconSource: "pics/image_6.jpg" }
}
My main QML:
Rectangle {
width: 500
height: 600
color: "transparent"
PathView {
id: view
focus: true
width: parent.width
height: parent.height + y
y: -150
model: Menu1 {} //First QML showed
delegate: Image {
source: iconSource
width: 64
height: 90
scale: PathView.isCurrentItem ? 3.5 * y / parent.height : 2.0 * y / parent.height
z: y
smooth: true
}
path: MyGeometricFigure { //This a another file, but is confidential
width: view.width
height: view.height
}
preferredHighlightBegin: 0
preferredHighlightEnd: 0
highlightRangeMode: PathView.StrictlyEnforceRange
Keys.onLeftPressed: decrementCurrentIndex()
Keys.onRightPressed: incrementCurrentIndex()
}
}
And as I use QML like a component for my QWidget:
MainForm::MainForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainForm)
{
ui->setupUi(this);
this->resize(1024, 576);
QDeclarativeView *myQMLTest = new QDeclarativeView(QUrl::fromLocalFile("myMainQML.qml"));
myQMLTest->setStyleSheet(QString("background: transparent; width: 600px"));
this->ui->frameListVideoGallery->layout()->addWidget(myQMLTest);
myQMLTest->setFocus();
myQMLTest->installEventFilter(this);
}
I saw some articles about this, but I am not able to change my LisModel using C++. I saw here http://doc.qt.nokia.com/4.7/qdeclarativemodels.html#c-data-models and here in examples using PathView http://doc.qt.nokia.com/4.7/qdeclarativeexamples.html
Can someone help me?
Thanks!
Alright. I think you can do something like this:
In your main QML-file add simple function.
Rectangle {
// ...
function append(newElement) {
view.model.append(newElement)
}
PathView {
id: view
// ...
model: Menu1 {} //First QML showed
// ...
}
}
This method will just call a corresponding method from ListModel. More supported methods you can find there.
Then all you need is to call this method from C++ code. You can do this in such manner:
MainForm::MainForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainForm)
{
ui->setupUi(this);
this->resize(1024, 576);
QDeclarativeView *myQMLTest = new QDeclarativeView(QUrl::fromLocalFile ("myMainQML.qml"));
myQMLTest->setStyleSheet(QString("background: transparent; width: 600px"));
QVariantMap newElement; // QVariantMap will implicitly translates into JS-object
newElement.insert("name", "Image 13" );
newElement.insert("iconSource", "pics/image_13.jpg");
QMetaObject::invokeMethod(
myQMLTest->rootObject(), // for this object we will call method
"append", // actually, name of the method to call
Q_ARG(QVariant, QVariant::fromValue(newElement)) // method parameter
);
this->ui->frameListVideoGallery->layout()->addWidget(myQMLTest);
myQMLTest->setFocus();
myQMLTest->installEventFilter(this);
}
You should check this for more information.
Also you can choose another approach: to pass some data via qml properties (using QDeclarativeEngine and QDeclarativeContext) and then handle this data and your list-model right in JavaScript code.
I want to make the answer from #GooRoo more complete/usefull to Qt beginners. If you use the Qt Creator, you will start with a template using QmlApplicationViewer. To apply the answer from GooRoo you have to do something like this (thanks to http://www.lothlorien.com/kf6gpe/?p=309):
CPP:
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create());
viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer->setMainQmlFile(QLatin1String("qml/mydemo/main.qml"));
viewer->showExpanded();
QMetaObject::invokeMethod(
(QObject *)viewer->rootObject()->findChild<QObject *>("myModel"), // QML item
"test" // name of the method to call
// without parameters
);
return app->exec();
}
QML:
PageStackWindow {
id: mainApp
...
ListModel {
id: myModel
objectName: myModel //THIS IS NEEDED BY findChild()
...
function test() {
console.log("TEST OK");
}
}
Page {
...
}
}
Here is one more complete example of filling data in QML TableView from C++. Please pardon the bad variable names. I was making it for testing purpose only.
I have used QVariantList and filled it with QVariantMap objects.
Here, multiple data has been filled with a for loop in two columns of the QML's table, namely what and session_name by putting them in a QVariantMap and then inserting that QVariantMap in QVariantList.
HH.h
#ifndef HH_H
#define HH_H
#include <QVariant>
class AA : public QObject
{
Q_OBJECT
QVariantList m_gname;
public:
Q_PROPERTY(QVariantList gname READ gname WRITE setGname NOTIFY gnameChanged)
AA()
{
QVariantList q;
for(int i = 0; i < 10; i++)
{
QVariantMap p;
p.insert("what", "qq");
p.insert("session_name", "aa");
q.push_back(p);
}
setGname(q);
}
QVariantList gname() const
{
return m_gname;
}
public slots:
void setGname(QVariantList gname)
{
if (m_gname == gname)
return;
m_gname = gname;
emit gnameChanged(m_gname);
}
signals:
void gnameChanged(QVariantList gname);
};
#endif // HH_H
Here I have use qmlRegisterType to register the class AA which I created above.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "HH.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<AA>("ZZZ", 1, 0, "AA");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
In QML I have created a TableView by QtQuick.Controls 1.4. It has two columns namely what and session_name. I have created an object of the class AA here and on its Component.onCompleted called <model_id> dot append( <QVariantList_name> )
like this:
mymodel.append( gname )
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 1.4 as OldControls
import ZZZ 1.0
Window
{
visible: true
width: 1000
height: 1000
title: qsTr("Hello World")
AA
{
Component.onCompleted:
{
mymodel.append(gname)
}
}
Rectangle
{
id: head
anchors.centerIn: parent; height: 500; width: 500; border.color: "red"
OldControls.TableView
{
anchors.fill: parent
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOn
OldControls.TableViewColumn
{
role: "what"
title: "What"
}
OldControls.TableViewColumn
{
role: "session_name"
title: "Session Name"
}
model: ListModel
{
id: mymodel
}
}
}
}

QT QML import ListModel from C++ to QML

How can i change the model of a PathView with c++ code ?
i add an objectName to my pathView to find it, then i change the property like this, but when i do that, my list is empty :
QDeclarativeItem *listSynergie = myClass.itemMain->findChild<QDeclarativeItem*> ("PathViewInscription");
listSynergie->setProperty("model", QVariant::fromValue(dataList));
My data list is fill like this :
QList<QObject*> dataList;
dataList.append(new synergieListObject("Item 1", "1",false));
dataList.append(new synergieListObject("Item 2", "2",true));
dataList.append(new synergieListObject("Item 3", "3",false));
dataList.append(new synergieListObject("Item 4", "4",false));
This is the code of my PathView :
PathView {
objectName: "PathViewInscription"
Keys.onRightPressed: if (!moving) { incrementCurrentIndex(); console.log(moving) }
Keys.onLeftPressed: if (!moving) decrementCurrentIndex()
id: view
anchors.fill: parent
highlight: Image { source: "../spinner_selecter.png"; width: view.width; height: itemHeight+4; opacity:0.3}
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
dragMargin: view.width/2
pathItemCount: height/itemHeight
path: Path {
startX: view.width/2; startY: -itemHeight/2
PathLine { x: view.width/2; y: view.pathItemCount*itemHeight + itemHeight }
}
}
and the code of ListModel :
ListModel {
id: appModel
ListElement { label: "syn1"; value: "1"; selected:false}
ListElement { label: "syn2"; value: "2" ; selected:false}
ListElement { label: "syn3"; value: "3" ; selected:false}
}
what's wrong ?
Thanks !
EDIT :
the code of the appDelegate :
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors { horizontalCenter: parent.horizontalCenter }
text: label
smooth: true
}
MouseArea {
anchors.fill: parent
onClicked: view.currentIndex = index
}
}
}
the code of my object :
#ifndef SYNERGIELISTOBJECT_H
#define SYNERGIELISTOBJECT_H
#include <QObject>
class synergieListObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
public:
synergieListObject(QObject *parent=0);
synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent=0);
QString label() const;
void setLabel(const QString &label);
QString value() const;
void setValue(const QString &value);
bool selected() const;
void setSelected(const bool &selected);
signals:
void labelChanged();
void valueChanged();
void selectedChanged();
private:
QString m_label;
QString m_value;
bool m_selected;
};
#endif // SYNERGIELISTOBJECT_H
c++ my object :
#include "synergielistobject.h"
synergieListObject::synergieListObject(QObject *parent): QObject(parent)
{
}
synergieListObject::synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent): QObject(parent), m_label(label), m_value(value), m_selected(selected)
{
}
QString synergieListObject::label() const
{
return m_label;
}
void synergieListObject::setLabel(const QString &label)
{
if (label != m_label) {
m_label = label;
emit labelChanged();
}
}
QString synergieListObject::value() const
{
return m_value;
}
void synergieListObject::setValue(const QString &value)
{
if (value != m_value) {
m_value = value;
emit valueChanged();
}
}
bool synergieListObject::selected() const
{
return m_selected;
}
void synergieListObject::setSelected(const bool &selected)
{
if (selected != m_selected) {
m_selected = selected;
emit selectedChanged();
}
}
I have never used QdeclarativeItem to set model in QML . Try this instead
QDeclarativeContext *ctxt = view.rootContext(); ctxt->setContextProperty("model", QVariant::fromValue(dataList));
Declare the model as a property of the root. This way you can set model.Or add a function that takes model as argument and sets the model for the view. Then you can call this function from c++.

Resources