Я передаю shared_ptr<Track> QtQuick2ApplicationViewer через setContextProperty. Переданное значение (inputStream) затем присваивается свойству PianoRoll, настраиваемому QQuickItem, который имеет поток свойств типа shared_ptr<Track>.

Я получаю следующее сообщение об ошибке:

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
    }

}
2
user2052244 16 Окт 2013 в 15:32
Qt ничего не знает о типе shared_ptr . Вам нужно будет как минимум зарегистрировать его с помощью Q_DECLARE_METATYPE / qRegisterMetaType, но я сомневаюсь, что это будет работать с QML. Возможно, вам понадобится использовать необработанный Track *.
 – 
Dan Milburn
16 Окт 2013 в 20:34

1 ответ

Лучший ответ

Нет смысла передавать shared_ptr (или QSharedPointer) в QML, поэтому движок QML его не поддерживает. QML поддерживает очень ограниченный список типов данных. Если вы передаете указатель, это должен быть пустой указатель на QObject.

Вы можете управлять временем жизни объекта либо сами, либо позволить QML сделать это. Вы заявляете о своем намерении через void QQmlEngine::setObjectOwnership(QObject*, ObjectOwnership).

Если вы хотите оставить объект для себя, вы устанавливаете CppOwnership. Если вы намереваетесь передать право владения движку QML, установите JavaScriptOwnership. Вот и все. Нет необходимости никуда передавать умные указатели.

Следующая ошибка:

PianoRollDemo.qml: 10: невозможно назначить дорожку * [неизвестный тип свойства]

Происходит, когда вы назначаете Track* свойству stream объекта PianoRoll. Это свойство имеет тип shared_ptr<Track>. Опять же, этого не может быть. Он должен быть типа Track*. Тип, который вы помещаете в Q_PROPERTY, является типом, видимым для внешних пользователей, а для QObject он должен быть просто Track*. Внутри вы можете сохранить его в любом типе умного указателя, который захотите.

Также нет никакого смысла использовать Q_DECLARE_METATYPE для типов, производных от QObject*. Вы, конечно, должны вызывать qmlRegisterType для всех производных от QObject типов, представленных QML.

4
Kuba hasn't forgotten Monica 17 Окт 2013 в 15:14
Изменение типа потока свойств на Track * и вызов viewer.rootContext()->setContextProperty("inputStream", track), где track является Track *, изменяет сообщение об ошибке на PianoRollDemo.qml:10: unable to assign Track* to [unknown property type]. Я вызываю Q_DECLARE_METATYPE(Track*) прямо перед объявлением int main ().
 – 
user2052244
17 Окт 2013 в 11:02
Если я чего-то не упускаю: «Вот и все». это немного отговорка. Учитывая, что JS собирает мусор, как я могу использовать CppOwnership? В любой момент времени C ++ удаляет объект, JS всегда может иметь указатель на него, верно?
 – 
Ben
26 Ноя 2019 в 18:13