How to draw Bezier curves in QML?



  • Hello,
    I need to draw Bezier (preferably, or at least other cubic) curves in QML, is it possible? And if yes, how?
    I'm experienced with Qt C++, but I just have started discovering QML possibilities.

    This far I tried with QML's Pathview (AFAIK can't be displayed directly) and port of HTML5 canvas (which's really great, but I need whole area to be scalable, and since canvas is just a bitmap I'm not interested in it), but to no avail.

    I can draw curves directly in C++, but I need them to dynamically move (each curve is connecting two boxes which can be d&d) - it seems a bit tricky, is there an easier option to achieve this?



  • I was also looking into realising some connected shapes logic with QtQuick some time ago. With standard QtQuick 1.x components it is not possible for sure. I think you have to extend QML with your own components.

    See here: http://doc.troll.no/4.7/qml-extending.html
    and here: http://doc.troll.no/4.7/qml-extending-tutorial-index.html

    Good luck, and let me know if you get it working;)



  • Can you just use SVG as a source for the Image element? You shouldn't need any extension.



  • [quote author="moo1" date="1301956183"]Can you just use SVG as a source for the Image element? You shouldn't need any extension.[/quote]

    I can't, because I need these lines to dynamically adjust their shapes while I'm moving boxes. :/



  • Hmmm... in theory even a border image would do. Compute start and end point, derive angle, rotate the arrow image, etc. Of course the arrow head needs to be fully inside the border.



  • I've made an extension for myself, which allow me to draw line, with cubic option.
    You will need to create a qml plugin. And to something like that
    :
    @void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
    {

    //setting drawing param
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(Qt::NoPen);

    // Construct the main path
    QPainterPath totalPath;
    totalPath.moveTo(m_points.at(0)->x(),m_points.at(0)->y());
    for (int i=1; i<m_points.size(); ++i)
    {
    totalPath.lineTo(m_points.at(i)->x(),m_points.at(i)->y());
    }

    //drawing directly
    painter->strokePath(totalPath, QPen(m_pencolor, m_penWidth));

    }@

    For adding cubic you could look at the "path stroking exampe":http://doc.qt.nokia.com/latest/demos-pathstroke.html

    And no you could not use an svg file because the svg is just render once at start.



  • Merinas,
    I've made similar thing as you and it works literally halfway - when I move one anchor point only the left and upper areas are updated.
    Doeas anyone have an idea how to fix it?

    Here is how it looks like:

    http://student.agh.edu.pl/~wmaciej/nodes.png

    my paint() fction:

    @void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
    {
    QPainterPath path;

    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(Qt::NoPen);
    
    QPointF middle1( (m_startingPoint.x() + m_endingPoint.x())/4, (m_startingPoint.y() + m_endingPoint.y())/2 );
    QPointF middle2( 3*(m_startingPoint.x() + m_endingPoint.x())/4, (m_startingPoint.y() + m_endingPoint.y())/2 );
    
    path.moveTo(m_startingPoint.x(), m_startingPoint.y());
    path.cubicTo( middle1.x(), middle1.y(),
                  middle2.x(), middle2.y(),
                  m_endingPoint.x(), m_endingPoint.y() );
    
    painter->strokePath( path, QPen(m_penColor, m_penWidth) );
    

    // painter->setPen( QPen(Qt::red) );
    // painter->drawRect( path.boundingRect().normalized() );
    }@

    Whole code can be found "here":http://student.agh.edu.pl/~wmaciej/LineTest.zip.



  • Your right image is really strange... Oo Cubic was working fine for me since have use Qt example code so I can't really help you. Maybe you can build a gif showing your issue. This may help us to understand what goes wrong...



  • After some experiments I think it's general QML-related problem, not generated by my implementation.
    But it's behavior is very strange:

    If I connect starting and ending points of my curve to some points on two QML Rectangles it looks fine.
    When I move the rectangles (with MouseArea's drag) Qt updates only the region from (window's) 0,0 to lower right point of moving rectangle, no matter where my line is pinned to. It means in the case the whole curve is placed above moving rect's lower right corner, everything updates as it should; otherwise (= when whole curve's bounding rect is not contained in mentioned area) only that part which's in is updated.

    What's more, when my curve is displayed wrongly and the window needs to be repainted - i.e. I change it's size or click on other window to lose focus - curve * magically * updates correctly.

    Explixitly calling QDeclarativeView object to update itself doesn't help.

    Does anyone have the idea what can I do? It's really important for me to have cubic lines available from QML..

    @Merinas:
    Yeah, it's a bit hard to explain what my particular problem is, so besides my story here is a gif:

    !http://student.agh.edu.pl/~wmaciej/QML-line.gif(my problem)!

    Hope it's gonna help you understand what I mean.



  • The line breaks in windows but I don't see any problem in Qt simulator. Works as expected - no broken line. So it's some bug in QML specific to windows.



  • In the beggining I thought you're talking about the OS, so I switched to Linux - but this error is also present there.

    I found a brute-force workaround: automatically resize top window by 1px back and forth.
    Pitiful and very CPU-eating, but works for now.

    Shold I report this as a bug?



  • Yes, please report, but please add your (minimal) testcase to your bugreport and also link back and forth between this topic and the bug report.



  • When the point of the curve move, Haven't you forget to call the update function ?



  • Hi,

    Does setting QGraphicsView::FullViewportUpdate on the QDeclarativeView work?

    Does your Line element set a width and height? This is needed by QDeclarativeItem's "boundingRect":http://doc.qt.nokia.com/4.7/qgraphicsitem.html#boundingRect implementation (which determines what area needs to be repainted when update() is called for the element).

    Regards,
    Michael



  • [quote author="Merinas" date="1302507353"]When the point of the curve move, Haven't you forget to call the update function ? [/quote]

    No, I haven't forgot;

    [quote author="mbrasser" date="1302567263"]Hi,

    Does setting QGraphicsView::FullViewportUpdate on the QDeclarativeView work?

    Does your Line element set a width and height? This is needed by QDeclarativeItem's "boundingRect":http://doc.qt.nokia.com/4.7/qgraphicsitem.html#boundingRect implementation (which determines what area needs to be repainted when update() is called for the element).

    Regards,
    Michael[/quote]

    Setting QGraphicsView::FullViewportUpdate on QDeclarativeView works fine - it's still CPU-eating, but it better solution than mine previous;
    I didn't implement boundingRect() method at all, nor I set width and height. After doing this it's a bit better - the rectangle in which my line is updated is bigger than previously, but it differs - sometimes whole line is updated, sometimes not.

    Is there any particular order I should set new values in in paint() method? Now it looks like this:
    @
    void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
    {
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(Qt::NoPen);

    QPointF middle1( (m_startingPoint.x() + m_endingPoint.x())/10, m_startingPoint.y() );
    QPointF middle2( 9*(m_startingPoint.x() + m_endingPoint.x())/10, m_endingPoint.y() );
    
    QPainterPath path;
    path.moveTo( m_startingPoint );
    path.cubicTo( middle1, middle2, m_endingPoint );
    
    rect = path.boundingRect();
    setHeight( rect.height() );
    setWidth( rect.width() );
    
    painter->strokePath( path, QPen(m_penColor, m_penWidth) );
    

    // painter->setPen( QPen(Qt::red) );
    // painter->drawRect( boundingRect() );
    }

    QRectF Line::boundingRect()
    { return rect; }
    @

    Anyway - thank you, mbrasser, it's one step closer. :)

    [EDIT:]

    Testing goes on:

    • Calling prepareGeometryChange(); inside a paint() method wasn't best idea. ;)

    • As I see the problem is in these lines:

    @ setHeight( rect.height() );
    setWidth( rect.width() );@

    When I set width and height properly bigger, it works. Just don't know the correct equation for these values to work in every case.



  • @i think that is very easy to do .like below...just a simplest example!

    import QtQuick 1.0

    Rectangle {
    id: rect;
    width: 450;
    height:800;
    PathView {
    id: pathView;
    model: 300;
    delegate: Rectangle {
    id: dot;
    width: 1; height: 1;
    color: "black";
    }
    path: Path {
    startX: 20; startY: 0
    PathCubic {
    x: 180; y: 0
    control1X: -10; control1Y: 90
    control2X: 210; control2Y: 90
    }
    }
    }
    }

    another version! perfect result!

    import QtQuick 1.0

    Rectangle {
    id: rect;
    width: 450;
    height:800;
    PathView {
    id: conrPaV;
    anchors.fill: parent;
    model: 900;
    delegate: Rectangle {
    id: dot;
    width: 1; height: 1;
    color: "red";
    }

        path: Path {
            id: ph;
            startX: 0.0; startY: conrPaV.height/2.0;
            PathQuad {
                x: 40.0; y: conrPaV.height/2.0;
                controlX: 20.0; controlY: ph.startY -60.0;
            }
            PathQuad {
                x: 80.0; y: conrPaV.height/2.0;
                controlX: 60.0; controlY: ph.startY +60.0;
            }
        }
    }
    

    }

    @


Log in to reply
 

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