I'm passing a shared_ptr<Track> to a QtQuick2ApplicationViewer via setContextProperty.
The passed value (inputStream) is then assigned to a property of PianoRoll, a custom QQuickItem which has a property stream of type shared_ptr<Track>.
I get the following error message:
PianoRollDemo.qml:10: unable to assign shared_ptr<Track> to [unknown property type]
main.cpp
Q_DECLARE_METATYPE(shared_ptr<Track>)
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// qmlRegisterType<Track>("Track", 1, 0, "Track");
qmlRegisterType<PianoRoll>("PianoRoll", 1, 0, "PianoRoll");
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/Diplomarbeit/PianoRollDemo.qml"));
viewer.showExpanded();
if (argc >= 2)
{
if (strcmp(argv[1],"-readFile") == 0)
{
string fileName = argv[2];
cout << "Reading from file " << fileName << endl;
GP5Reader reader;
MainGame mainGame;
reader.read(mainGame.score, fileName);
mainGame.playerInputs.push_back(shared_ptr<Track>(new Track(0)));
mainGame.onPlayerInput(0, shared_ptr<Note>(new Note(295309, 100, 69, 92)));
mainGame.onPlayerInput(0, shared_ptr<Note>(new Note(295306, 100, 64, 92)));
mainGame.onPlayerInput(0, shared_ptr<Note>(new Note(295300, 100, 57, 92)));
mainGame.onPlayerInput(0, shared_ptr<Note>(new Note(295315, 100, 45, 92)));
}
else if(strcmp(argv[1],"-listenToMidi") == 0)
{
int port = atoi(argv[2]);
cout << "Listening to port " << port << endl;
MidiInput* midiIn = new MidiInput();
midiIn->listen(port);
viewer.rootContext()->setContextProperty("inputStream", QVariant::fromValue(midiIn->track));
/*
QDeclarativeView view;
QUrl url = QUrl::fromLocalFile("qml/Diplomarbeit/PianoRollDemo.qml");
bool valid = url.isValid();
view.setSource(url);
view.show();
*/
}
}
else
{
cout << "No arguments received." << endl;
}
#ifdef Q_OS_ANDROID
GP5Reader reader;
Score score;
reader.read(score, "/storage/emulated/0/test.gp5");
#endif
return app.exec();
}
pianoroll.h
#ifndef PIANOROLL_H
#define PIANOROLL_H
#include <QQuickItem>
#include <QSGGeometry>
#include <QSGFlatColorMaterial>
#include <track.h>
class PianoRoll : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(shared_ptr<Track> stream READ stream WRITE setStream NOTIFY streamChanged)
public:
PianoRoll(QQuickItem* parent = 0);
// Get Methods
shared_ptr<Track> stream() const;
// Set Methods
void setStream(shared_ptr<Track> stream);
protected:
QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data);
private:
shared_ptr<Track> m_stream;
QSGGeometry m_geometry;
QSGFlatColorMaterial m_material;
signals:
void streamChanged();
};
QML_DECLARE_TYPE(PianoRoll)
#endif // PIANOROLL_H
pianoroll.cpp
#include <QQuickItem>
#include <QSGGeometry>
#include <QSGFlatColorMaterial>
#include <QSGGeometryNode>
#include <QSGSimpleRectNode>
#include <iostream>
#include <memory>
#include "pianoroll.h"
#include "note.h"
using namespace std;
PianoRoll::PianoRoll(QQuickItem *parent) :
QQuickItem(parent),
m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4),
m_stream(new Track)
{
// Important, otherwise the paint method is never called
setFlag(ItemHasContents);
m_material.setColor(Qt::red);
// TODO: remove TestData
shared_ptr<Note> noteOn1(new Note(0, 500, 70, 100));
shared_ptr<Note> noteOn2(new Note(500, 500, 40, 100));
shared_ptr<Note> noteOn3(new Note(1000, 200, 30, 100));
m_stream->addNote(noteOn1);
m_stream->addNote(noteOn2);
m_stream->addNote(noteOn3);
//m_stream.addNoteEvent(new NoteEvent(700, 700, 70, 100));
}
shared_ptr<Track> PianoRoll::stream() const
{
return m_stream;
}
void PianoRoll::setStream(shared_ptr<Track> stream)
{
if (m_stream == stream) return;
m_stream = stream;
emit streamChanged();
update();
}
QSGNode *PianoRoll::updatePaintNode(QSGNode *n, QQuickItem::UpdatePaintNodeData *data)
{
QSGGeometryNode *node = static_cast<QSGGeometryNode *>(n);
if (!node)
{
node = new QSGSimpleRectNode(boundingRect(), Qt::white);
}
node->removeAllChildNodes();
qreal msPerScreen = 10000;
qreal pitchesPerScreen = 128;
qreal x_factor = (qreal) boundingRect().width() / msPerScreen;
qreal y_factor = (qreal) boundingRect().height() / pitchesPerScreen;
for (unsigned int i = 0; i < m_stream->notes.size(); i++)
{
shared_ptr<Note> note = m_stream->notes.at(i);
qreal left = boundingRect().left() + note->getTime() * x_factor;
qreal top = boundingRect().top() + note->getPitch() * y_factor;
qreal width = note->getDuration() * x_factor;
qreal height = y_factor;
QRectF noteRectangle = QRectF(left, top, width, height);
node->appendChildNode(new QSGSimpleRectNode(noteRectangle, Qt::black));
}
return node;
}
track.h
#ifndef NOTESTREAM_H
#define NOTESTREAM_H
#include <vector>
#include <memory>
#include <QVariantList>
#include <QQuickView>
#include "note.h"
using namespace std;
class Track : public QObject
{
public:
void print();
//TODO: private:
QList<shared_ptr<Note>> notes;
int gMInstrument;
int trackIndex;
int stringCount;
vector<int> tuning;
bool operator==(Track& other) const;
Track(int trackIndex = -1);
~Track();
public slots:
void addNote(shared_ptr<Note> note);
signals:
};
#endif // NOTESTREAM_H
track.cpp
#include <vector>
#include <memory>
#include <iostream>
#include <QMetaType>
/*
#include <QtDeclarative/QDeclarativeView>
#include <QtDeclarative/QDeclarativeContext>
*/
#include <QQuickView>
#include <QQmlContext>
#include <qqml.h>
#include "track.h"
#include "note.h"
using namespace std;
void Track::print()
{
for (unsigned int i = 0; i < notes.size(); i++)
{
shared_ptr<Note> event = notes.at(i);
cout << event->getTime() << " Pitch: " << event->getPitch() << endl;
}
cout << notes.size() << " notes" << endl;
}
bool Track::operator==(Track &other) const
{
return other.notes == this->notes;
}
Track::Track(int trackIndex)
{
this->trackIndex = trackIndex;
}
Track::~Track()
{
}
void Track::addNote(shared_ptr<Note> note)
{
//print();
this->notes.append(note);
/*
listElems.append(QVariant::fromValue(new NoteEvent(1, 1, 1, 1)));
view->rootContext()->setContextProperty("dataModel",QVariant::fromValue(listElems));
*/
}
PianoRollDemo.qml
import QtQuick 2.0
import PianoRoll 1.0
Rectangle {
width: 500
height: 200
PianoRoll {
stream: inputStream
anchors.fill: parent
}
}
There is little point in passing a shared_ptr (or QSharedPointer) to QML, thus the QML engine doesn't support it. QML supports a very limited list of data types. If you're passing a pointer, it must be a naked pointer to QObject.
You can manage the lifetime of the object either yourself, or let QML do it. You declare your intent via void QQmlEngine::setObjectOwnership(QObject*, ObjectOwnership).
If you want to keep the object around for yourself, you set CppOwnership. If you intend to move the ownership to the QML engine, then set JavaScriptOwnership. That's all there's to it. No need for passing smart pointers anywhere.
The following error:
PianoRollDemo.qml:10: unable to assign Track* to [unknown property type]
happens when you assign Track* to the stream property of PianoRoll. That property is of type shared_ptr<Track>. Again, it can't be. It must be of type Track*. The type you put into Q_PROPERTY is the type visible to external users, and for QObjects it must be simply Track*. Internally you can store it in whichever smart pointer type you wish.
There is also absolutely no point to using Q_DECLARE_METATYPE on types derived from QObject*. You must, of course, call qmlRegisterType on all QObject-derived types exposed to QML.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With