Qt: Setting custom roles on QAbstractListModel

Created on 7 Dec 2016  路  40Comments  路  Source: therecipe/qt

Thanks for your great work on this library! Curious if there's a way to set custom roles on a QAbstractListModel. I don't see the roleNames() function exposed?

I'm trying figure out how to use a list of golang structs in QML ListView. Very similar to this example. However, I'd like the data() function to support custom roles rather than just the default "display" role.

Here's some psuedo code for what I'm trying to do:

type Person struct {
    First string
    Last  string
}

var list = arraylist.New()
list.Add(&Person{First: "jane", Last: "doe"})

var model = core.NewQAbstractListModel(nil)
model.ConnectData(data)
model.ConnectRowCount(rowCount)
view.RootContext().SetContextProperty("PersonModel", model)

func rowCount(parent *core.QModelIndex) int {
    return list.Size()
}

func data(index *core.QModelIndex, role int) *core.QVariant {
    // XXX Want role == "first" and role == "last" here
    if role == 0 && index.IsValid() {
    }
}

And the QML:

import QtQuick 2.0

ListView {
    model: PersonModel
    delegate: Text {
        text: first + "  " + last
    }
}

How do I expose the first and last properties from go to QML? I thought of using the data() function on the QAbstractListModel and defining custom roles "first" and "last" but doesn't seem to be way to do that? Is there another way to go about this?

Thanks in advance for any pointers.

All 40 comments

Hey

Yes, http://doc.qt.io/qt-5/qabstractitemmodel.html#roleNames is currently not supported.
So you unfortunately can't register custom roles yet.
Also generic lists/containers in general are only partially supported currently.

But I found a way to work around your problem.

You could use qtmoc to subclass core.QObject with a custom slot.
(I tried to subclass QAbstractListModel so that there would be only one ContextProperty, but there is a bug that doesn't allow that currently.)

main.go

package main

import (
    "os"
    "time"

    "github.com/emirpasic/gods/lists/arraylist"

    "github.com/therecipe/qt/core"
    "github.com/therecipe/qt/quick"
    "github.com/therecipe/qt/widgets"
)

type Person struct {
    First string
    Last  string
}

//go:generate qtmoc
type modelHelper struct {
    core.QObject

    _ func(row int, role string) *core.QVariant `slot:"getData"`
    _ func()                                    `signal:"dataChanged"`
}

var list *arraylist.List

func main() {

    widgets.NewQApplication(len(os.Args), os.Args)

    var view = quick.NewQQuickView(nil)

    list = arraylist.New()
    list.Add(&Person{First: "jane", Last: "doe"})
    list.Add(&Person{First: "jon", Last: "doe"})

    var model = core.NewQAbstractListModel(nil)
    model.ConnectData(data)
    model.ConnectRowCount(rowCount)
    view.RootContext().SetContextProperty("PersonModel", model)

    var helper = NewModelHelper(nil)
    helper.ConnectGetData(getData)
    view.RootContext().SetContextProperty("ModelHelper", helper)

    view.SetSource(core.NewQUrl3("qrc:///qml/main.qml", 0))
    view.SetResizeMode(quick.QQuickView__SizeRootObjectToView)
    view.Show()

    go func() {
        time.Sleep(5 * time.Second)

        var row = 0
        var iData, _ = list.Get(row)
        var data = iData.(*Person)

        data.First = "Bob"
        helper.DataChanged()
    }()

    widgets.QApplication_Exec()
}

func rowCount(parent *core.QModelIndex) int {
    return list.Size()
}

func data(index *core.QModelIndex, role int) *core.QVariant {
    // XXX Want role == "first" and role == "last" here
    if role == 0 && index.IsValid() {
    }
    return core.NewQVariant()
}

func getData(row int, role string) *core.QVariant {

    var iData, exists = list.Get(row)
    if !exists {
        return core.NewQVariant()
    }

    var data = iData.(*Person)
    if role == "first" {
        return core.NewQVariant14(data.First)
    }
    return core.NewQVariant14(data.Last)
}

qml/main.qml

import QtQuick 2.0

ListView {
    id: listView
    model: PersonModel
    delegate: Text {
        text: ModelHelper.getData(index, "first") + "  " + ModelHelper.getData(index, "last")
    }
    Connections {
      target: ModelHelper
      onDataChanged: { listView.model = 0; listView.model = PersonModel }
    }
}

This is perfect. Thanks very much for the example.

Any thoughts on how to have the view updated when a list item is changed? For example:

// Update person #3
var iData, _ = list.Get(3)
var data = iData.(*Person)
data.First = "Bob"
// XXX send some signal to let the view know item #3 changed

Is this possible? Seems like dataChanged() would be appropriate but appears to rely on the roles being supported.

Thanks again!

I updated the example above with another workaround.

The signal will simply remove and then re-add the model to the ListView.
It works, but it's probably really inefficient.

I tried to invoke PersonModel.dataChanged in QML as well, but didn't managed to get it working.
That would have probably been the most efficient way to work around this.

Sounds good. Thanks. What are the chances of getting custom roles supported? Is this on the road map? Would be great to have this functionality.

Yes, it's definitely going to be added.
It will come along with the generic list/container support for input parameters.

I'm currently cleaning up internal/binding and depending on how fast I'm done with that, I will either start working on this or on the update to Qt 5.8.

So it's probably going to be added in the end of December or mid January.

This is great. Thanks! I'd be more than happy to test once you get things going.

(I tried to subclass QAbstractListModel so that there would be only one ContextProperty, but there is a bug that doesn't allow that currently.)

Is this related to generic list/container support etc. or is this a separate bug. I'm finding it would be very nice to subclass QAbstractListModel.

This is great. Thanks! I'd be more than happy to test once you get things going.

Okay, just keep this issue open and I will ping you once I'm done with the generic containers.

Is this related to generic list/container support etc. or is this a separate bug. I'm finding it would be very nice to subclass QAbstractListModel.

There were 1-2 separate bugs that caused some trouble, but it should work with https://github.com/therecipe/qt/commit/d10ed7da323f1e96248df5f46d062c561b8dd987 now.

Sounds great. Thanks. I tried d10ed7d and was able to subclass QAbstractListModel and compile ok but QML doesn't appear to recognize the PersonModel ContextProperty as a list model. I keep getting these errors in the QML delegate when attempting to call model.display:

Unable to assign [undefined] to QString

Here's what I get when I dump the PersonModel item object properties in QML (from the delegate):

[D] expression for source:77 - Property: objectName , Value: [D] expression for source:77 - Property: index , Value: 33 [D] expression for source:77 - Property: model , Value: QQmlDMAbstractItemModelData(0xb606630) [D] expression for source:77 - Property: hasModelChildren , Value: true [D] expression for source:77 - Property: decoration , Value: undefined [D] expression for source:77 - Property: statusTip , Value: undefined [D] expression for source:77 - Property: whatsThis , Value: undefined [D] expression for source:77 - Property: edit , Value: undefined [D] expression for source:77 - Property: toolTip , Value: undefined [D] expression for source:77 - Property: display , Value: undefined [D] expression for source:77 - Property: destroyed , Value: undefined [D] expression for source:77 - Property: destroyed , Value: undefined [D] expression for source:77 - Property: objectNameChanged , Value: function() { [code] } [D] expression for source:77 - Property: deleteLater , Value: undefined [D] expression for source:77 - Property: _q_reregisterTimers , Value: undefined [D] expression for source:77 - Property: modelIndexChanged , Value: function() { [code] } [D] expression for source:77 - Property: __0 , Value: function() { [code] } [D] expression for source:77 - Property: __1 , Value: function() { [code] } [D] expression for source:77 - Property: __2 , Value: function() { [code] } [D] expression for source:77 - Property: __3 , Value: function() { [code] } [D] expression for source:77 - Property: __4 , Value: function() { [code] } [D] expression for source:77 - Property: __5 , Value: function() { [code] }

So it looks like QML gets a QQmlDMAbstractItemModelData but none of the properties are available. Seems like the PersonModel type needs to be registered so QML knows it's a QAbstractListModel?

It seems like properties in general are not available for "moc" subclasses, once they are passed to QML.

import QtQuick 2.0

ListView {

    //Doesn't work
    model: PersonModel
    Component.onCompleted:
    {
        console.log(PersonModel, model)
        console.log(PersonModel.count, model.count)
    }

    /*
    //Works
    model: ListModel { id: personModel }
    Component.onCompleted:
    {
        console.log(personModel, model)
        console.log(personModel.count, model.count)
    }
    */
}

Could it be that the QML ListModel (even through it is derived from QAbstractListModel, like PersonModel is), has some extra code (macros or something) that makes these properties available?
Or does it need to be registered like you suggested? Or should this work out of the box?

You can register the metatype of the moc class with CLASS-NAME__QRegisterMetaType(), but that doesn't work for properties.

I will have to port this over to c++, to see if this is an expected behaviour.

Not sure if this helps at all but here's rough port of the list model in C++. Would love to be able to do this in go:

#include <QAbstractListModel>

class PersonModel : public QAbstractListModel
{
    Q_OBJECT
    Q_PROPERTY(int count READ count NOTIFY countChanged)
public:
    enum PersonRoles {
        FirstRole = Qt::UserRole + 1,
        LastRole
    };

    explicit PersonModel(QObject *parent = 0);
    virtual ~PersonModel();

    void addPerson(Person *p);
    Person* getPerson(int index);
    void clear();
    int count() const;

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    virtual QHash<int, QByteArray> roleNames() const { return _roles; }

Q_SIGNALS:
    void countChanged();

private:
    QHash<int, QByteArray> _roles;
    QList<Person *> _people;
};
PersonModel::PersonModel(QObject *parent) :
    QAbstractListModel(parent)
{
    _roles[FirstRole] = "first";
    _roles[LastRole] = "last";
}

PersonModel::~PersonModel()
{
    _people.clear();
}

int PersonModel::count() const
{
    return rowCount();
}

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

QVariant PersonModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= _people.count())
        return QVariant();

    Person *p = _people.at(index.row());

    switch (role) {
    case FirstRole:
        return QVariant::fromValue(p->first());
        break;
    case LastRole:
        return QVariant::fromValue(p->last());
        break;
    default:
        return QVariant();
        break;
    }
}

void PersonModel::clear()
{
    qDeleteAll(_people);
    _people.clear();
}

Person* PersonModel::getPerson(int index)
{
    return _people.at(index); 
}

void PersonModel::addPerson(Person *p)
{
    beginInsertRows(QModelIndex(),_people.count(),_people.count());
    _people.append(p);
    endInsertRows();
    Q_EMIT countChanged();
}

Yes, it's helpful thank you :)
It's always good to have already working code to compare.

From glancing over it seems like the only things that are missing are the creation of custom properties with Q_PROPERTY, but I already wrote a bit about it here https://github.com/therecipe/qt/issues/170#issuecomment-270264344 so maybe it's partially supported in the next few days.

And full generic list / containers support to get PersonModel::roleNames working. I already started with that a month ago, but I stopped halfway though as it would have taken to long to finish (and I was busy otherwise at the time).

But both features have high priority for me, so I will try to work on them in the next few weeks.

Awesome, all sounds great. Q_PROPERTY support would be amazing and I'll keep an eye out for it. Thanks again.

@aebruno
qtmoc now supports Q_PROPERTY with https://github.com/therecipe/qt/commit/1748f5d34a38a04dde6dbae1ba92c891d08d2fcc

Here is an example that shows how to use it: https://github.com/therecipe/qt/tree/master/internal/examples/qml/prop2

There will be getter and setter functions created + the *Changed signal.

But it's not yet possible to override/connect the getter or setter functions like you did in your example above.

int PersonModel::count() const
{
    return rowCount();
}

But I will probably add that as well.

@therecipe Finally had a chance to test this out. qtmoc Q_PROPERTY support works great! Thanks for including the example. I also tested this out with QAbstractListModel and it seems to work quite well. Until custom roles and dataChanged are supported, I was able to store QObjects with custom properties in the list and return them as QVariants in the data() function. I can now update the properties of individual items in the list and those changes are reflected in QML. For example, here's the changes from the above example with the updated data function:

//go:generate qtmoc
type PersonObject struct {
        core.QObject
        _ string          `property:"first"`
}

...
...
func data(index *core.QModelIndex, role int) *core.QVariant {
    var iData, exists = list.Get(row)
    if !exists {
        return core.NewQVariant()
    }
    var data = iData.(*PersonObject)
    return data.ToVariant() 
}

func updateRow(index int) {
        var iData, _ = list.Get(index)
        o := iData.(*PersonObject)
        o.SetFirst("Roger")
        o.FirstChanged("Roger")
}
import QtQuick 2.0

ListView {
    model: PersonModel
    delegate: Text {
        text: model.display.first
    }
}

For some reason I'm still not able to subclass QAbstractListModel so end up having to embed this in a QObject and have an extra context variable. It's a very minor annoyance but would be a nice to have. My previous comment has info on the error details.

Thanks for all your hard work on this. Great job!

@aebruno Could I please trouble you for a full example, based on your most recent post? I'm not familiar with Qt C++, so I'm trying to wrap my head around it all.

Something like this should work:

main.go

package main

import (
    "os"
    "time"

    "github.com/emirpasic/gods/lists/arraylist"

    "github.com/therecipe/qt/core"
    "github.com/therecipe/qt/quick"
    "github.com/therecipe/qt/widgets"
)

//go:generate qtmoc
type PersonObject struct {
        core.QObject
        _ string `property:"first"`
        _ string `property:"last"`
}

var list *arraylist.List

func main() {
    widgets.NewQApplication(len(os.Args), os.Args)

    var view = quick.NewQQuickView(nil)

    list = arraylist.New()
        var po = NewPersonObject(nil)
        po.SetFirst("Jane")
        po.SetLast("Doe")
    list.Add(po)

    var model = core.NewQAbstractListModel(nil)
    model.ConnectData(data)
    model.ConnectRowCount(rowCount)
    view.RootContext().SetContextProperty("PersonModel", model)

    view.SetSource(core.NewQUrl3("qrc:///qml/main.qml", 0))
    view.SetResizeMode(quick.QQuickView__SizeRootObjectToView)
    view.Show()

    go func() {
        time.Sleep(5 * time.Second)

        var row = 0
        var iData, _ = list.Get(row)
        var data = iData.(*PersonObject)

        data.SetFirst("Bob")
                data.FirstChanged("Bob")
    }()

    widgets.QApplication_Exec()
}

func rowCount(parent *core.QModelIndex) int {
    return list.Size()
}

func data(index *core.QModelIndex, role int) *core.QVariant {
    if !index.IsValid() {
        return core.NewQVariant()
    }

    var iData, exists = list.Get(index.Row())
    if !exists {
        return core.NewQVariant()
    }

    o := iData.(*PersonObject)

    return o.ToVariant()
}

qml/main.qml

import QtQuick 2.0

ListView {
    id: listView
    model: PersonModel
    delegate: Text {
        text: model.display.first + "  " + model.display.last
    }
}

Perfect, thanks!

@aebruno

Great to hear that the properties are useful :)

For some reason I'm still not able to subclass QAbstractListModel so end up having to embed this in a QObject and have an extra context variable.

I suspect that the cause of the undefined model.display is the missing roleNames function.
I will look into it again, once the function is supported.

@aebruno

generic lists / containers are now supported with https://github.com/therecipe/qt/commit/3456e5280a438b31c6c4529ef86e465ba190ef7d

but there are still a few things missing like support for nested maps, multidimensional arrays and there are also problems if you want to use them with slots currently.

and I added a new example here: https://github.com/therecipe/qt/tree/master/internal/examples/quick/listview

Trying out list/container support and getting these errors when compiling:

moc.cpp:1420:89: error: macro "Q_PROPERTY" passed 2 arguments, but takes just 1
Q_PROPERTY(QHash<qint32, QByteArray> roles READ roles WRITE setRoles NOTIFY rolesChanged)


moc.cpp:1420:1: error: 'Q_PROPERTY' does not name a type
Q_PROPERTY(QHash<qint32, QByteArray> roles READ roles WRITE setRoles NOTIFY rolesChanged)

ah, sorry made some last minute changes and didn't re-ran all tests ...
it should work now with https://github.com/therecipe/qt/commit/e70f310ce583a0253804d02b53eca8e59d72a16c (I hope, as I got a different error message)

Same errors.. I'm on the latest commit 0adc942142400d3697b2170a2f999e211585df20 with QT 5.2.0

moc.cpp:1540:89: error: macro "Q_PROPERTY" passed 2 arguments, but takes just 1
 Q_PROPERTY(QHash<qint32, QByteArray> roles READ roles WRITE setRoles NOTIFY rolesChanged)

moc.cpp:1540:1: error: 'Q_PROPERTY' does not name a type
 Q_PROPERTY(QHash<qint32, QByteArray> roles READ roles WRITE setRoles NOTIFY rolesChanged)

okay, will test it with 5.2.0 and report back

okay, should be fixed for real this time: https://github.com/therecipe/qt/commit/7d9b08f5015194cac78c279024ca9909df2d44a4
I tested map[T]T and []T properties on the sailfish emulator.

signals/slots with map[T]T or []T didn't threw a compiler error, so they probably work as well, but I didn't explicitly tested them, I will do that tomorrow.

The problems was probably caused by the old gcc compiler, or maybe there were some changes between Qt 5.2 and 5.7, but it's probably the compiler because there were similar problems on windows with 5.7 and the mingw version of gcc as well.

I just needed to typedef the property types first because the Q_PROPERTY macro couldn't handle custom generic containers like QHash<qint32, QByteArray>, the same needed to be already done earlier for slots on 5.7 with the Q_ARG/Q_RETURN_ARG macros.

signals/slots seem to work as well.
I was able to compile and run this for the emulator.

package main

import (
    "fmt"
    "os"

    "github.com/therecipe/qt/core"
)

type testStruct struct {
    core.QObject

    _ []string          `property:"prop1"`
    _ map[string]string `property:"prop2"`

    _ func([]string)          `signal:"signal1"`
    _ func(map[string]string) `signal:"signal2"`

    _ func([]string) []string                   `slot:"slot1"`
    _ func(map[string]string) map[string]string `slot:"slot2"`
}

func main() {
    core.NewQCoreApplication(len(os.Args), os.Args)

    var ts = NewTestStruct(nil)

    ts.SetProp1([]string{"test", "from", "go"})
    fmt.Println("test 1", ts.Prop1())

    ts.SetProp2(map[string]string{"a": "test", "b": "from", "c": "go"})
    fmt.Println("test 2", ts.Prop2())

    ts.ConnectSignal1(func(v0 []string) {
        fmt.Println("test 3", v0)
    })
    ts.Signal1([]string{"test", "from", "go"})

    ts.ConnectSignal2(func(v0 map[string]string) {
        fmt.Println("test 4", v0)
    })
    ts.Signal2(map[string]string{"a": "test", "b": "from", "c": "go"})

    ts.ConnectSlot1(func(v0 []string) []string {
        fmt.Println("test 5", v0)
        return v0
    })
    fmt.Println("test 6", ts.Slot1([]string{"test", "from", "go"}))

    ts.ConnectSlot2(func(v0 map[string]string) map[string]string {
        fmt.Println("test 7", v0)
        return v0
    })
    fmt.Println("test 8", ts.Slot2(map[string]string{"a": "test", "b": "from", "c": "go"}))

    core.QCoreApplication_Exec()
}

It compiles ok but unfortunately, this is still not working. Seems like I'm back to the errors I was getting in my previous comment here. In QML get these errors again:

Unable to assign [undefined] to QString

Dumping the model in QML I get:

[D]  objectName (string) = 
[D]  index (number) = 0
[D]  model (object) = QQmlDMAbstractItemModelData(0x9b05d40)
[D]  hasModelChildren (boolean) = true
[D]  whatsThis (undefined) = undefined
[D]  statusTip (undefined) = undefined
[D]  toolTip (undefined) = undefined
[D]  edit (undefined) = undefined
[D]  decoration (undefined) = undefined
[D]  display (undefined) = undefined
[D]  destroyed (undefined) = undefined
[D]  destroyed (undefined) = undefined
[D]  objectNameChanged (function) = function() { [code] }
[D]  deleteLater (undefined) = undefined
[D]  _q_reregisterTimers (undefined) = undefined
[D]  modelIndexChanged (function) = function() { [code] }
[D]  __0 (function) = function() { [code] }
[D]  __1 (function) = function() { [code] }
[D]  __2 (function) = function() { [code] }
[D]  __3 (function) = function() { [code] }
[D]  __4 (function) = function() { [code] }
[D]  __5 (function) = function() { [code] }

The "data" function of the model is never called. This is when trying to subclass QAbstractListModel.

The latest commit also seems to have broken this which was working before. I can no longer return the QObject as a QVariant in QML using QAbstractListModel directly. I get the same error Unable to assign [undefined] to QString.

FWIW, being able to return a QObject from the data function was very nice to have. In some ways this feels a bit cleaner than having the extra code that duplicates the properties as roles.

It compiles ok but unfortunately, this is still not working. Seems like I'm back to the errors I was getting in my previous comment here. In QML get these errors again:
The "data" function of the model is never called. This is when trying to subclass QAbstractListModel.

It could be that you need to explicitly connect the columnCount function to return 1, like I did here:
https://github.com/therecipe/qt/blob/master/internal/examples/quick/listview/personmodel.go#L83-L85
The problem is that upon "subclassing" with qtmoc the QAbstractListModel::columnCount function will return 0, if it's not "connected".

This differs from the normal behavior:

Note that QAbstractListModel provides a default implementation of columnCount() that informs views that there is only a single column of items in this model.

taken from here: http://doc.qt.io/qt-5/qabstractlistmodel.html#subclassing

this happens because the code generator only sees that columnCount in QAbstractListModel is derived from this pure function http://doc.qt.io/qt-5/qabstractitemmodel.html#columnCount, because the *.index files don't contain the "new default implementation" in QAbstractListModel.

The latest commit also seems to have broken this which was working before. I can no longer return the QObject as a QVariant in QML using QAbstractListModel directly. I get the same error Unable to assign [undefined] to QString.

Your example from here: https://github.com/therecipe/qt/issues/140#issuecomment-274301807 works for me on macOS without any chances. (maybe this is the same columCount issue as above?)

I will port the quick/listview example to sailfish.
edit: ported the example to sailfish, and it doesn't work ...

It could be that you need to explicitly connect the columnCount function to return 1

I tried this and got the same result

I will port the quick/listview example to sailfish.

Curious if you try this, modify the QML to use SilicaListView. Something like:

import QtQuick 2.0
import Sailfish.Silica 1.0

SilicaListView {
    id: listView
    model: PersonModel
    delegate: Text {
        text: model.firstName + "  " + model.lastName
    }
}

It seems as though this commit 1748f5d34a38a04dde6dbae1ba92c891d08d2fcc which added properties worked great. I was able to use QAbstractListModel directly and return QObjects as QVariants into QML without issues (however subclassing still did not work). The most recent few commits seems to have broken this functionality. I'll continue testing and see if I can get more info.

okay, this https://github.com/therecipe/qt/tree/master/internal/examples/sailfish/listview should work.
it's the same go code as in quick/listview, only the qml code is changed to use the sailfish components such as SilicaListView

I'm not exactly sure why, but it seems like on sailfish you need to work with the Page components and such otherwise the SilicaListView or ListView won't update the changes.
you can test the quick/listview example, to see that it won't update. (also you need to wrap the ListView there inside a

{

    width: 320
    height: 240

    ....
}

otherwise it won't even open the app)

I will create another example where the object is returned as a variant.

I'm not sure how your examples are working. Are you sure you're using QT 5.2.0? When I use qtminimal against QT 5.2.0 these functions are different: core.NewQVariant14 or core.NewQByteArray2. The string QVariant is actually core.NewQVariant18 and the QByteArray becomes core.NewQByteArray3. I found you need to really clean out the qt library and rebuild or it may link older library code (unless I'm missing something).

In my experience, you shouldn't be able to actually compile and link on the sailfish emulator unless you use qtminimal with the correct QT 5.2.0 *.index files. If you use these, then you should end up with these functions generated in core/core.go:

func NewQVariant18(val string) *QVariant
func NewQByteArray3(data string, size int) *QByteArray

I'm using Qt 5.7.0.

And I got these for NewQByteArray:

func NewQByteArray() *QByteArray
func NewQByteArray2(data string, size int) *QByteArray
func NewQByteArray3(size int, ch string) *QByteArray
func NewQByteArray5(other QByteArray_ITF) *QByteArray
func NewQByteArray6(other QByteArray_ITF) *QByteArray

Using the 5.2.0 *.index files is probably better, but as long as you don't use a function that does not exists in 5.2.0, then it should work normaly (because on the C++ side
func NewQByteArray3(data string, size int) *QByteArray
and
func NewQByteArray2(data string, size int) *QByteArray will be the same)

also qtminimal will strip everything that is not used (or was introduced after 5.2 for the sailfish targets) (I fixed it yesterday, it may have been broken before)

I tried to test 5.2.0 as a "desktop" target, but iirc the index files were in a different place, and I didn't want to manually fix all these things by symlinking.

I'm currently trying to get the QObject to QVariant example working, but I just get

[F] QQmlNotifierEndpoint::connect:109 - QQmlEngine: Illegal attempt to connect to Person(0x3650c240) that is in a different thread than the QML engine QQmlEngine(0x841bc68).

How did you start the QQmlEngine? As QQuickView or QQmlApplicationEngine? Did you set the engine as the parent for your Person objects?

Yeah I hit that too. I believe it's an issue with connecting to signals/slots across threads. You can only modify the qabstractlistmodel from the main thread. So your go run function needs to use a signal to tell the model to update instead of updating it directly. See here.

the object to variant example should work now as well: https://github.com/therecipe/qt/tree/master/internal/examples/sailfish/listview_variant

it's the listview examples, with these changes:

in main.go:

  • p.MoveToThread(view.Engine().Thread()) to move the person object to the engine thread

in personmodel.go:

  • comment out the roleNames connect function //m.ConnectRoleNames(m.roleNames)
  • changed the data function to return a variant of the object

in qml/pages/page.qml:

  • use display.firstName + " " + display.lastName to get the properties of the person object variant

edit:

Yeah I hit that too. I believe it's an issue with connecting to signals/slots across threads. You can only modify the qabstractlistmodel from the main thread. So your go run function needs to use a signal to tell the model to update instead of updating it directly. See here.

yes, generally you need to use signals/slots to cross threads.
but in this case I was able to move the person object to the qmlengine thread by using moveToThread (inside the other thread entered through the go func(){ .... })

also qtminimal will strip everything that is not used (or was introduced after 5.2 for the sailfish targets) (I fixed it yesterday, it may have been broken before)

Nice.. I will give this a try.

Nice.. I will give this a try.

okay, I'm off to have dinner now and will be offline for today.
I hope qtminimal works for your project, as it can be buggy from time to time.

if there are problems about missing functions for abstract classes, then you just need to write code with an object that makes use of these functions and then simply comment that code out.
(this should work because qtminimal simply scans your code for the function names, most times this is enought to make it work)

but I'm planning to make some fundamental changes to the code generator in general, which will hopefully make qtminimal more stable.

Great.. thanks for all the help!

no problem :)

Okay this is working great for me now. subclassing QAbstractListModel works and can successfully update items in QML. I had missed connecting the roles m.ConnectRoleNames(m.roleNames) which was the cause of my errors before.

This is really awesome. Thanks!

You are welcome :)

And yes, it's difficult sometimes to figure out what went wrong with the boilerplate code to get the models working, because the error messages don't contain the cause of the problem.
I spend at least 2 hours to figure out that I need to connect the columnCount after subclassing the QAbstractListModel with qtmoc. And the only error message I got from qml was that there is no "firstName" for "undefined" ...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tapir picture tapir  路  5Comments

LimEJET picture LimEJET  路  6Comments

iMaxopoly picture iMaxopoly  路  3Comments

quantonganh picture quantonganh  路  6Comments

woodyjon picture woodyjon  路  5Comments