Неизвестный метод возвращает тип, даже если объявлен - PullRequest
0 голосов
/ 28 июня 2018

Qt5.11 / QML. Я пытаюсь создать двухуровневый QAbstractListModel для просмотра в QML. Думайте как модель папок (первый уровень) и файлов (второй уровень).

Вот мой код:

model.h

class File
{
public:
    File(const QString name) { _name = name; }

    QString name() const { return _name; }
    void setName(const QString &name) { _name = name; }

    QPixmap thumbnail() const { return _thumbnail; }
    void setThumbnail(QPixmap thumbnail) { _thumbnail = thumbnail; }

private:
    QString _name;
    QPixmap _thumbnail;
};

class FilesModel : public QAbstractListModel
{
    Q_OBJECT

public:
    enum FileRoles
    {
        NameRole = Qt::UserRole + 1,
        ThumbnailRole
    };

    FilesModel(QObject *parent = nullptr);
    void addFile(const File &file);
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;

protected:
    QHash<int, QByteArray> roleNames() const;

private:
    QString _name;
    QList<File> _listFiles;

};

class Folder
{
public:
    Folder(const QString name);

    QString name() const { return _name; }
    void setName(const QString &name) { _name = name; }

    FilesModel *model() { return _model; }

private:
   QString _name;
   FilesModel *_model;
};

class FolderModel : public QAbstractListModel
{
    Q_OBJECT

public:
    enum FolderRoles
    {
        NameRole = Qt::UserRole + 1,
        ModelRole
    };

    FolderModel(QObject *parent = nullptr);
    void addFolder(const Folder &folder);
    void clear();
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
    Folder *findFolder(QString name);

    FilesModel *filesModel(QString name);

protected:
    QHash<int, QByteArray> roleNames() const;
    bool removeRows(int row, int count, const QModelIndex &parent);

private:
    QString _name;
    QList<Folder> _list;

};

Q_DECLARE_METATYPE(FilesModel*)

model.cpp

FilesModel::FilesModel(QObject *parent) : QAbstractListModel(parent) { }

void FilesModel::addFile(const File &file)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    _listFiles << file;
    endInsertRows();
}

int FilesModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return _listFiles.count();
}

QVariant FilesModel::data(const QModelIndex &index, int role) const
{
    if (index.row() < 0 || index.row() >= _listFiles.count()) return QVariant();

    const File &item = _listFiles[index.row()];
    switch (role) {
    case NameRole: return item.name(); break;
    case ThumbnailRole: return item.thumbnail(); break;
    default: return QVariant();
    }
}

QHash<int, QByteArray> FilesModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[ThumbnailRole] = "thumbnail";
    return roles;
}

FolderModel::FolderModel(QObject *parent) : QAbstractListModel(parent) { }

void FolderModel::addFolder(const Folder &folder)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    _list << folder;
    endInsertRows();
}

bool FolderModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if (count == 0) return false;
    beginRemoveRows(parent, row, row + count - 1);
    for (int i = 0; i < count; i++) _list.removeAt(row);
    endRemoveRows();
    emit dataChanged(this->index(row, 0), this->index(row, 0));
    return true;
}

void FolderModel::clear()
{
    removeRows(0, rowCount(), QModelIndex());
}

int FolderModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return _list.count();
}

QVariant FolderModel::data(const QModelIndex &index, int role) const
{
    if (index.row() < 0 || index.row() >= _list.count()) return QVariant();

    const Folder &item = _list[index.row()];
    switch (role) {
    case NameRole: return item.name(); break;
    default: return QVariant();
    }
}

Folder *FolderModel::findFolder(QString name)
{
    for (int i = 0; i < _list.size(); i++)
    {
        if (_list[i].name() == name) return &_list[i];
    }
    return NULL;
}

FilesModel *FolderModel::filesModel(QString name)
{
    Folder *folder = findFolder(name);
    if (folder)
    {
        FilesModel *model = folder->model();
        QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
        return model;
    }
    return NULL;
}

QHash<int, QByteArray> FolderModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[ModelRole] = "model";
    return roles;
}

Folder::Folder(const QString name) : _name(name)
{
    _model = new FilesModel();
}

main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    MyApp myapp;

    QQmlApplicationEngine qmlEngine;
    qmlEngine.rootContext()->setContextProperty("MyModel", myapp.model());
    qmlEngine.rootContext()->setContextProperty("MyApp", &myapp);
    qmlEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (qmlEngine.rootObjects().isEmpty()) return -1;

    return app.exec();
}

в MyApp.cpp я заполняю некоторые элементы:

FolderModel _model;
_model.addFolder(Folder("Folder1"));
_model.addFolder(Folder("Folder2"));

// ...

Folder *objFolder = _model.findFolder(folder);
objFolder->model()->addFile(File("File 1"));
objFolder->model()->addFile(File("File 2"));

Код QML в основном основан на демонстрационной программе Photo Viewer . main.qml

ApplicationWindow {
    id: mainWindow
    visible: true

    DelegateModel { id: albumVisualModel; model: MyModel; delegate: AlbumDelegate {} }

    GridView {
        id: albumView        
        width: parent.width
        height: parent.height
        cellWidth: 210
        cellHeight: 220
        model: albumVisualModel.parts.album
        visible: albumsShade.opacity !== 1.0
    }

    Rectangle {
        id: albumsShade; color: mainWindow.color
        width: parent.width; height: parent.height; opacity: 0.0
    }

    ListView {
        anchors.fill: parent
        model: albumVisualModel.parts.browser
        interactive: false
    }    
}

AlbumDelegate.qml (первая часть)

Component {
    id: albumDelegate

    Package {

        Item {
            Package.name: 'browser'
            GridView {
                id: photosGridView; model: visualModel.parts.grid; width: mainWindow.width; height: mainWindow.height - 21
                x: 0; y: 21; cellWidth: 160; cellHeight: 153; interactive: false
                onCurrentIndexChanged: photosListView.positionViewAtIndex(currentIndex, ListView.Contain)
            }
        }

        Item {
            Package.name: 'fullscreen'
            ListView {
                id: photosListView; model: visualModel.parts.list; orientation: Qt.Horizontal
                width: mainWindow.width; height: mainWindow.height; interactive: false
                onCurrentIndexChanged: photosGridView.positionViewAtIndex(currentIndex, GridView.Contain)
                highlightRangeMode: ListView.StrictlyEnforceRange; snapMode: ListView.SnapOneItem
            }
        }

        Item {
            Package.name: 'album'
            id: albumWrapper; width: 210; height: 220

            DelegateModel {
                property string tag
                id: visualModel; delegate: PhotoDelegate { }
                model: MyApp.filesModel(tag)
            }

Правильно отображается модель первого уровня (папки), но происходит сбой при попытке показать файлы в одном из них со следующей ошибкой:

qrc:/AlbumDelegate.qml:36: Error: Unknown method return type: FilesModel*

Но я объявил этот тип, используя Q_DECLARE_METATYPE. Нужно ли добавить что-то еще?

Насколько я знаю, мне не нужно qRegisterMetaType(), потому что я не использую его в сигналах или слотах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...