GridView and transitions not working



  • Hi, I'm trying to get my GridView to animate transitions on operations performed on my C++ model (exposed as a QQmlListProperty). But they don't work.
    The following snippet works:
    @ GridView {
    id: gridview
    anchors.fill: parent
    cellWidth: width / 3
    cellHeight: height / 3

    model: ListModel {
    id: model2
    ListElement {
    name: "red"
    }
    ListElement {
    name: "blue"
    }
    ListElement {
    name: "green"
    }
    ListElement {
    name: "tomato"
    }
    }

    delegate: Item {
    id: gridDelegate
    width: gridview.cellWidth
    height: gridview.cellHeight

    Rectangle {
    id: image
    anchors.fill: parent
    color: name
    Behavior on color {
    PropertyAnimation { duration: 1000 }
    }
    }

    Image {
    width: parent.width / 10
    height: width
    anchors {
    right: image.right
    rightMargin: 10
    bottom: image.bottom
    bottomMargin: 10
    }
    source: "delete-icon.png"
    antialiasing: true
    opacity: mouseArea.containsMouse ? 1 : 0
    Behavior on opacity {
    NumberAnimation { duration: 200 }
    }
    MouseArea {
    id: mouseArea
    anchors.fill: parent
    hoverEnabled: true
    onClicked: model2.remove(index);
    }
    }
    }
    // Transitions
    remove: Transition {
    ParallelAnimation {
    NumberAnimation { property: "opacity"; to: 0; duration: 500 }
    NumberAnimation { properties: "width, height"; to: 0; duration: 500 }
    }
    }
    removeDisplaced: Transition {
    NumberAnimation { properties: "x, y"; duration: 500 }
    }
    }
    @
    The following does not:
    @ GridView {
    id: gridview
    anchors.fill: parent
    cellWidth: width / 3
    cellHeight: height / 3

    • model: imageLoader.allImages*
      delegate: Item {
      id: gridDelegate
      width: gridview.cellWidth
      height: gridview.cellHeight

    • Image {
      id: image
      width: parent.width - anchors.margins / 2
      height: status == Image.Ready ? parent.height - anchors.margins / 2 : 0
      anchors {
      centerIn: parent
      margins: 10
      }
      Behavior on height {
      NumberAnimation { duration: 500; easing.type: Easing.InOutQuad }
      }
      fillMode: Image.PreserveAspectFit
      asynchronous: true
      source: "file:///" + path
      }

    • Image {
      width: parent.width / 10
      height: width
      anchors {
      right: image.right
      rightMargin: 10
      bottom: image.bottom
      bottomMargin: 10
      }
      source: "delete-icon.png"
      antialiasing: true
      opacity: mouseArea.containsMouse ? 1 : 0
      Behavior on opacity {
      NumberAnimation { duration: 200 }
      }

      MouseArea {
      id: mouseArea
      anchors.fill: parent
      hoverEnabled: true

    • onClicked: imageLoader.remove_all_image(index);*
      

      }
      }
      }
      // Transitions
      remove: Transition {
      ParallelAnimation {
      NumberAnimation { property: "opacity"; to: 0; duration: 500 }
      NumberAnimation { properties: "width, height"; to: 0; duration: 500 }
      }
      }
      removeDisplaced: Transition {
      NumberAnimation { properties: "x, y"; duration: 500 }
      }
      }
      @
      I've highlighted the differences: in the first case, the model is a dedicated ListModel QML element.
      In the second case, it's a QQmlListProperty I expose from C++.

    I think the problem is with the slot I call from C++ when clicking the delete-icon:
    @* onClicked: imageLoader.remove_all_image(index);*@
    which is as follows in the code behind:
    @void ImageLoader::remove_all_image(int i)
    {
    m_allImages.removeAt(i);
    emit allImagesChanged();
    }
    @
    I have the feeling that GridView's transitions remove and removeDisplaced are only triggered by a call to the remove function in the ListModel, which fires the proper signals, whereas my function just emits the NOTIFY signal for the QQmlListProperty change and this forces a refresh of the whole Grid. I also noticed that removing an element from the ListModel maintains the currentIndex, whereas calling my function resets the currentIndex to 0 (and if I remove and element from the bottom of the GridView, the grid scroll back to top...).

    So, how can I work this problem around? If I got it right, QQmlListProperty wraps QList<MyCustomQObject*> and offers the append function, the count, the access and the clear function, but no removeAt function (which is why I implemented it manually), how can I get this functionality?? The documentation is quite lacking...



  • "Note that objects cannot be individually added to or removed from the list once created; to modify the contents of a list, it must be reassigned to a new list.":http://qt-project.org/doc/qt-5/qml-list.html



  • Which is what happens when I call the C++ api: the underlying QList<MyImage*> is modified, then the allImagesChanged(); signal fires and the QQmlListProperty getter is called again. Or not?

    In any case, should I dive into the study of QAbstractListModel as an alternative to QQmlListProperty?



  • I think you misunderstand QQmlListProperty. QQmlListProperty is list in QML. It is not a model. QAbstractListModel is a model class.

    I think you should use a model.



  • I first used a list because I only need to show in the UI the data fetched by the C++ engine. So I started with a raw
    @QPROPERTY(QList<QObject*> allImages READ ... )@
    then switched to
    @QPROPERTY(QQmlListProperty<MyImage> allImages READ ... )@
    Both approaches worked and were simple enough...and since I'm quite new to this framework, I didn't want to complicate things from the start...

    I'll try to subclass QAbstractListModel now... anyway I'm still curious to know the exact mechanics that lead to my problem :)



  • Ok so, I got my model to work! Wasn't that hard after all, as this class (QAbstractListModel) is quite documented over the internet, as opposed to QQmlListProperty...

    Anyway, now there's one other thing which is not working: the elements returned by my model have one read AND write property, which only works in read mode. I cannot, from QML, write to it implicitly:
    @// Inside the GridView delegate:
    onClicked: favorite = !favorite;@
    This won't work. I have to call an invokable method (or better, a public Q_SLOT):
    @// Inside the GridView delegate:
    onClicked: gridview.model.get(index).set_favorite(!favorite);@

    Has anyone an idea why writing to properties doesn't work?

    edit: also the notification doesn't seem to work...



  • please show your complete code.



  • I now understand why, in the QML, writing in the grid delegate:
    @onClicked: myProperty = newValue@
    does not work: it's because myProperty, exposed to QML, is actually a role, not the real property...that's why I should explicitly use the setter for that property. I'll see if I can do anything with the Qt::EditRole token...

    Edit: I have overridden QAbstractListIModel::setData with my own implementation and... voilĂ ! Everything working like a charme! There had to be a way ;)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.