switching from QGraphicsView to QML



  • Hi,

    I need some guideline, how to achieve in QML what I have done in QGraphicsView.
    I have a QGraphicsView in which I have one big custom QGraphicsPixmapItem with around 100 smaller custom QGraphicsPixmapItem at specific x,y positions. At application start mainItem is scaled to fit the view, then user can stretch this item with pinch gesture (child items are stretched too), then user can move mainItem in view but with restrictions - top left corner of mainItem must not cross top left corner of the view, and bottom right corner must not cross bottom left corner of the view - classic case, picture in view is twice the size of the view itself.
    So I need QML equivalent for QGrapvicsView signal that view is ready and has width and height

    void showEvent(QShowEvent * event);
    void resizeEvent(QResizeEvent *event);
    

    setting scale for mainView item (and its children), something like:

    floorData->transform.setMatrix(floorData->scale/(double)100,0,0,0,floorData->scale/(double)100,0,0,0,1);
    floorData->mainviewItem->setTransform(floorData->transform);
    

    and some way to control mainItem movement, which is done in subclassed item:

    QVariant MainItem::itemChange(GraphicsItemChange change, const QVariant &value)
    

    Is this doable in QML, well I'm sure it is, but what is the concept for that?

    Best Regards
    Marek


  • Moderators

    Some basic way to achieve this:

    Item { // Assuming this is your floor item
      onXChanged: if (x < 0) x = 0
      onYChanged: if (y < 0) y = 0
    }
    

    Or if you use anchors/ layouts to position items, you kind of get this for free. Modifying position can then be achieved by specifying anchor margins.



  • @sierdzio thanks for hint. I'm quite new to QML although I'm writing widget apps for some years.
    So now I have main.qml and SwipeView with two pages, second one is for QGraphicsView equivalent
    Is there any equivalent in QML ?

    With anchor approach it would be something like ?:

    MainItem {
        anchor.left: parent.left - (calculated size diff between MainItem width and screen width)
    |
    

    this size should be calculated whenever user stretches MainItem, right?


  • Moderators

    There is no equivalent ot QGraphicsView in QML. They work on different principles. I strongly recommend doing some simple apps first, just to get the hang of it. QML is easy to learn, but you will need some time to get adjusted to different way of thinking than in imperative code.

    Regarding anchors. You can't add or substract anything from an anchor. You can modify the margin, however.

    MainItem {
        anchors.left: parent.left
        anchors.leftMargin: 20
    }
    

    Another possibility: consider using Flickable element. I think it might work out very well in your use case. It already supports zooming and flicking, just like you need. And you can get the exact behaviour you need using boundsBehaviour property. Exact position of content item (your floor) can be accessed with contentY and contentX properties, etc.



  • @sierdzio Thanks for flickable example, there is also one in QtCreator which I will try to implement.
    I have an impression that I have seen somewhere in examples that right part of the binding was an expression calculated from some function in javascript or from C++ bindings.

    Best Regards
    Marek


  • Moderators

    @Marek said in switching from QGraphicsView to QML:

    I have an impression that I have seen somewhere in examples that right part of the binding was an expression calculated from some function in javascript or from C++ bindings.

    Yes, that is correct. You can easily write stuff like:

    x: 15 * someProperty
    y: 80 + SomeQObject.someFunction()
    

    etc. However, in case of anchors property, it expects not a numerical value, but an actual anchor from another object. Hence in that case, margins are needed.



  • @sierdzio I have copied flick-resize into my app and played for a while, seems to be what I need.
    Howver, I cant work out how to have two images parent and a children, where children is also affected by pinch gesture
    For instance, child image is positioned in (100,100)px of the main mapImage, then pinch gesture resizes the mapImage but child image remains in the same scale and in fixed position over the scaled image.

    Flickable {
            id: flick
            anchors.fill: parent
            contentWidth: 4109
            contentHeight: 824
    
            PinchArea {
                id:pinch
                width: Math.max(flick.contentWidth, flick.width)
                height: Math.max(flick.contentHeight, flick.height)
    
                property real initialWidth
                property real initialHeight
                //![0]
                onPinchStarted: {
                    initialWidth = flick.contentWidth
                    initialHeight = flick.contentHeight
                }
    
                onPinchUpdated: {
                    // adjust content pos due to drag
                    flick.contentX += pinch.previousCenter.x - pinch.center.x
                    flick.contentY += pinch.previousCenter.y - pinch.center.y
    
                    // resize content
                    if(initialWidth * pinch.scale>500 && initialHeight * pinch.scale>200)
                        flick.resizeContent(initialWidth * pinch.scale, initialHeight * pinch.scale, pinch.center)
                }
    
                onPinchFinished: {
                    // Move its content within bounds.
                    flick.returnToBounds()
                    console.log("pinch updated width:"+flick.contentWidth+" height:"+flick.contentHeight+" scale:"+pinch.scale)
                }
                //![0]
    
                Rectangle {
                    width: flick.contentWidth
                    height: flick.contentHeight
                    color: "white"
                    Image {
                        id: mapImage
                        anchors.fill: parent
                        source: "images/mapa.jpg"
                        MouseArea {
                            anchors.fill: parent
                            onDoubleClicked: {
                                flick.contentWidth = 4109
                                flick.contentHeight = 824
                            }
                        }
                        onScaleChanged: {
                            console.log("scale changed:"+scale)
                        }
                        Image {
                            id: childImage
                            x: 100
                            y: 100
                            source: "images/seat_icon.jpg"
                        }
                    }
                }
            }
        }
    

    Is this a parent-child relation problem ? Scale of the mapImage does not change.
    I'm looking for a similar behavior as with QGraphicsPixmapItem. When I have parent and child and I apply setScale to parent child is also scaled and his position is in parent coordinates so it seems it moves with parent image when it is resized.

    Best Regards
    Marek



  • I have modified example to work with onWheel signal, easier to work with on desktop.
    Child image item is repositioned, but there is slight error with position, due to setting "scale" for child item.
    Child Image moves towards bottom right corner when I scale down main image.
    Can someone advice me on how to keep this child image in fixed position when flickable element is resized ?

    Flickable {
            id: flick
            anchors.fill: parent
            contentWidth: 4109
            contentHeight: 824
    
            Image {
                id: mapImage
                anchors.fill: parent
                source: "images/mapa.jpg"
                property real itemScale: 1
                MouseArea {
                    anchors.fill: parent
    
                    onWheel: {
                        if(wheel.angleDelta.y>0)
                            parent.itemScale+=0.1
                        else
                            parent.itemScale+=-0.1 
                         flick.resizeContent(mapImage.sourceSize.width*parent.itemScale,mapImage.sourceSize.height*parent.itemScale,Qt.point(mouseX,mouseY))
    
                        flick.returnToBounds()
                    }
                }
                Image {
                    id: childImage
                    x: 100*parent.itemScale
                    y: 100*parent.itemScale
                    scale: parent.itemScale
                    source: "images/seat_icon.jpg"
    
                }
            }
        }
    

    Best Regards
    Marek


  • Moderators

    @Marek said in switching from QGraphicsView to QML:

    anchors.fill: parent

    I suspect that might be the issue: you are trying to anchor to an object that is designed for scrolling (that is: it is designed to handle components larger than itself).

    So, my recommendations:

    • remove anchors from mapImage
    • give mapImage a fixed size
    • when zooming/ pinching, modify scale of mapImage

    Feel free to take a look at/clone/reuse my code from CCF it's old, very old code, but it used to work and scale contents of a Flickable correctly.



  • @sierdzio Thanks for hint and for Your help, I will look into your code.
    I have just returned from the gym and during workout I have figured out the problem ;) Probably more oxygen for the brain.
    Instead of child Image which need to be scaled (and then the problem appears) I have Rectangle which is scaled, and inside rectangle there is an image. Works very well.

    Rectangle {
        width:sourceImageWidth*parent.itemScale
        height:sourceImageHeight*parent.itemScale
        x: 100*parent.itemScale
        y: 100*parent.itemScale
        Image {
            id: childImage
            anchors.fill: parent
            source: "images/seat_icon.jpg"
    
        }
    }
    

    Just need to check whether this will work well with 100 child Images on top the mapImage.
    So partly solved ;) Now I need to load them dynamically so next questions will follow ;)

    Best Regards
    Marek


  • Moderators

    OK, good news. Happy coding :-)


Log in to reply
 

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