Pull to refresh

Comments 12

Хорошая статья, важное замечание.

Предложил бы еще так же сказать пару слов о макросе Q_DECLARE_OPAQUE_POINTER и смежных.

Не совсем согласен с утверждением:
Почему то мы не можем использовать ValueType в методах помеченными Q_INVOKABLE.

Утверждаю, что следующий код будет работать в 5.6.1:
// record.h
class Record
{
  Q_GADGET
...
}

// listener.h
class Listener: public QObject
{
  Q_OBJECT
...
  Q_INVOKABLE Record foo ();
}

//main.cpp
...
qRegisterMetaType<Record>();
qmlRegisterType<Listener>("MyModule", 1, 0, "Listener");
...


// main.qml
...
Component.onCompleted: {
    var  r = someListener.foo();

    print(r)
    print(typeof r)
    print(Qt.isQtObject(r))

    r.text = "hooray";
    print(r.text)
}
...

// output:
qml:  Record()
qml:  object
qml:  false
qml:  hooray



Отлично! Теперь буду знать.

В принципе, отличный пример. Q_GADGET действительно хорошо подходят для обертывания структур и использования их в QML. Ключевой недостаток Q_GADGET'ов в том, что Q_PROPERTY без сигналов (об изменении значения) в лучшем случае неудобны, а в худшем — бесполезны

п.с. В моделях всё-таки лучше использовать не одну роль, возвращающую объект, а много ролей, возвращающих простые значения. Производительнее и нагляднее

Например если делать клиент для сервиса(на скриншоте клиент futuron.tv), то сервер использует пагинацию, что уменьшает число элементов, то вопрос производительности особо не стоит. А вот когда количество полей у структуры очень много:


{
      "id" : 1708722,
      "type" : 0,
      "rate" : 3,
      "creationTime" : 1460376607027,
      "downloads" : 0,
      "rusName" : "Пришельцы 3: Взятие Бастилии",
      "engName" : "Les Visiteurs: La Révolution",
      "releaseYear" : 2016,
      "description" : "В третьей части граф с оруженосцем отправляются в самое сложное для Франции время — период Французской революции.",
      "youtubeId" : "FmpaphvPiTY",
      "imdbRate" : "4.4",
      "kpRate" : "5.8",
      "duration" : "1ч.50мин.",
      "officialSite" : "http://lesvisiteurs-lefilm.com/,http://vk.com/lesvisiteurs",
      "video3d" : false,
      "nextSeazonId" : 0,
      "rusAgeRate" : "12+",
      "studies" : "Ciné, TF1 Films Production, Canal+ [fr], Nexus Factory, Gaumont, Gaumont, The Czech Republic State Fund for Support and Development of Cinematography, La Wallonie, Okko Productions, Ouille Productions, BNP Paribas Fortis Film Finance",
      "directors" : "Жан-Мари Пуаре",
      "actors" : "Сильви Тестю, Кристиан Клавье, Жан Рено, Эри Абиттан, Карин Вьяр, Лоран Дойч, Франк Дюбоск, Фредерик Бель, Мари-Анн Шазель, Алекс Лутс, Вероника Буланжер, Стефани де Крэенкур",
      "countries" : "Франция",
      "genres" : "Комедии",
      "translationTypes" : "Проф. многоголосый",
      "translationAuthors" : "",
      "videoQuality" : "HD",
      "audioQuality" : "чистый звук",
      "languages" : "",
      "coverURL" : "http://static-gw.futuron.name/static/mobile/71/CF/71CF47394B5BA22ADDA68C66F29019E5.jpeg",
      "coverMaxiURL" : "http://static-gw.futuron.name/static/mobile/71/CF/71CF47394B5BA22ADDA68C66F29019E5.jpeg",
      "posterURL" : "http://static-gw.futuron.name/static/torrents/DA/69/DA69912E9EE01738090984132A70DEBA.jpg",
      "screenshotsURL" : [ "http://static-gw.futuron.name/static/torrents/32/5A/325A0ADEABE689251170180309FFF167.jpg", "http://static-gw.futuron.name/static/torrents/2D/2E/2D2E3DC8B83DA2FF7764BB6AE4E50398.jpg", "http://static-gw.futuron.name/static/torrents/D7/71/D771B2DA86EDE8EC444D80E7E2D6BA1B.jpg", "http://static-gw.futuron.name/static/torrents/F0/E6/F0E6AB1DCA3366E0E1C3BE3C5A09E11B.jpg", "http://static-gw.futuron.name/static/torrents/6F/C0/6FC0895E1E265C0D3DBF4671FD330595.jpg", "http://static-gw.futuron.name/static/torrents/D3/05/D3059FCE4D46B868ADB88FD4FC1C0241.jpg" ]
    }

то создавать роли может быть неудобно. Тут как всегда, нужно смотреть по ситуации.

В этом случае оборачивание в QVariant повлечет за собой:


  • написание Q_GADGET wrapper-а с пачкой Q_PROPERTY, которые будет также неудобно описывать (если я правильно Вас понял.);
  • копирование данных при выполнении QVariant::fromValue(), что при большой структуре отразится на производительности;
  • необходимость в qml-коде обращаться к конкретному свойству через объект: model.object.property (сахар, конечно, но это будет раздражать).

С другой стороны, сохранение значения model.object позволит минимизировать обращение к QAbstractItemModel::data() со всеми вытекающими.


Так что соглашусь: нужно смотреть по ситуации.

Наоборот, чем больше структура, тем накладнее её постоянное копирование с целью вытащить из модели значение всего одного поля…
Преждевременная оптимизация — корень всех зол.

Д. Кнут.


Часть типов в Qt implicitly shared, а значит копирование приводит к увеличению атомарных счетчиков, а не выделение и перемещение(std::memmove) памяти. А если уж хочется минимезировать инкриментирование атомарных счетчиков, то можно pimpl применить:


pimpl
class Film
{
    Q_GADGET

    Q_PROPERTY(quint64 id READ id)
    Q_PROPERTY(QString endName  READ engName CONSTANT FINAL)
    Q_PROPERTY(QString rusName  READ rusName CONSTANT FINAL)
    Q_PROPERTY(QUrl coverUrl READ coverUrl CONSTANT FINAL)
    Q_PROPERTY(QUrl coverMaxiURL READ coverMaxiURL CONSTANT FINAL)
    Q_PROPERTY(QStringList screenshotsURL READ screenshotsURL CONSTANT FINAL)
    Q_PROPERTY(QString duration READ duration CONSTANT FINAL)
    Q_PROPERTY(QString description READ description CONSTANT FINAL)
    Q_PROPERTY(quint32 year READ year CONSTANT FINAL)
    Q_PROPERTY(QString countries READ countries CONSTANT FINAL)
    Q_PROPERTY(QString actors READ actors CONSTANT FINAL)
    Q_PROPERTY(QString genres READ genres CONSTANT FINAL)
    Q_PROPERTY(bool isValid READ isValid CONSTANT FINAL)

public:

    typedef QList<QUrl> Urls;

    Film();
    Film(Film&& obj);
    Film(const Film& obj) = default;

    static Film formJson(const QJsonObject& obj);

    quint64  id() const noexcept ;
    const QString& engName() const noexcept;
    const QString& rusName() const noexcept;

    const QUrl& coverUrl() const noexcept;
    const QUrl& coverMaxiURL() const noexcept;

    const QStringList& screenshotsURL() const noexcept;
    const QString& duration() const noexcept;

    const QString& description() const noexcept;

    quint32 year() const noexcept;

    const QString& countries() const noexcept;
    const QString& actors() const noexcept;
    const QString& genres() const noexcept;

    bool isValid() const noexcept;
    bool operator==(const Film& film) const noexcept;

private:

    struct FilmImpl;
    std::shared_ptr<FilmImpl> m_impl;
};

#include <QtCore/QDebug>
#include <QtCore/QVariant>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>

#include "Film.h"

struct Film::FilmImpl
{
    bool m_isValid = false;
    quint64 m_id   = 0;

    QString m_engName;
    QString m_russName;
    QUrl m_coverURL;
    QUrl m_coverMaxiURL;
    QStringList m_screenshotsURL;
    QString m_duration;
    QString m_description;
    QString countries;
    QString actors;
    QString genres;
    quint32 year;
};

Film::Film()
    : m_impl(std::make_shared<FilmImpl>())
{

}

Film::Film(Film&& obj):
    m_impl(std::move(obj.m_impl))
{
}

quint64 Film::id() const noexcept
{
    return m_impl->m_id;
}

const QStringList& Film::screenshotsURL() const noexcept
{
    return m_impl->m_screenshotsURL;
}

const QString& Film::rusName() const noexcept
{
    return m_impl->m_russName;
}

const QString& Film::engName() const noexcept
{
    return m_impl->m_engName;
}

const QUrl& Film::coverUrl() const noexcept
{
    return m_impl->m_coverURL;
}

const QUrl& Film::coverMaxiURL() const noexcept
{
    return m_impl->m_coverMaxiURL;
}
bool Film::isValid() const noexcept
{
    return m_impl->m_isValid;
}

const QString& Film::duration() const noexcept
{
    return m_impl->m_duration;
}

const QString& Film::description() const noexcept
{
    return m_impl->m_description;
}

bool Film::operator==(const Film& film) const noexcept
{
    return m_impl->m_id == film.m_impl->m_id;
}

Film Film::formJson(const QJsonObject &obj)
{
    Film film;
    film.m_impl->m_isValid      = true;
    film.m_impl->m_id           = obj.value("id").toVariant().toULongLong();
    film.m_impl->m_russName     = obj.value("rusName").toString();
    film.m_impl->m_engName      = obj.value("engName").toString();
    film.m_impl->m_description  = obj.value("description").toString();
    film.m_impl->countries      = obj.value("countries").toString();

    film.m_impl->m_coverURL     = QUrl(obj.value("coverURL").toString());
    film.m_impl->m_coverMaxiURL = QUrl(obj.value("coverMaxiURL").toString());
    film.m_impl->m_duration     = obj.value("duration").toString();

    film.m_impl->year           = obj.value("releaseYear").toVariant().toUInt();
    film.m_impl->actors         = obj.value("actors").toString();
    film.m_impl->genres         = obj.value("genres").toString();

    for(const auto& val : obj.value("screenshotsURL").toArray())
    {
        film.m_impl->m_screenshotsURL.push_back(val.toString());
    }

    return film;
}
quint32 Film::year() const noexcept
{
    return m_impl->year;
}
const QString& Film::countries() const noexcept
{
    return m_impl->countries;
}

const QString& Film::actors() const noexcept
{
    return m_impl->actors;
}

const QString& Film::genres() const noexcept
{
    return m_impl->genres;
}

Есть QML профайлер, всегда можно посмотреть что подтормаживает.

написание обвязки для одной роли модели требует меньше кода, чем оборачивание этого же значения в Q_PROPERTY
Я для такого пилил обертку, чтобы rolenames() и data() автоматом генерились и вообще не надо было заморачиваться с внутренностями модели. Просто получаешь списочный json, скармливаешь в модель и пользуешь где нужно.
Биндинги я так понимаю не обновляются (раз сигналов нет)?

Нет, но можно создать свойство с var и при изменение значения, значение в контролах поменяются.

Sign up to leave a comment.

Articles