Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Need some advice: how to draw a line between two objects.
Forum Updated to NodeBB v4.3 + New Features

Need some advice: how to draw a line between two objects.

Scheduled Pinned Locked Moved QML and Qt Quick
15 Posts 6 Posters 11.9k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    excalibur1491
    wrote on last edited by
    #1

    Hi,

    I am working in a project where I have several items in a view (lets say rectangles, for example).
    I want to draw a line between some pairs of them and I want the line to be attached to the rectangles as if they were the edges of a graph. The ends of the line should be in the center of the rectangles. This means that when I move any of the two rectangles, the line will move in consequence.

    I don't really know how to do it, that is why I am asking you some advice ^^
    My only idea is to draw a rectangle (of height = 1) to be the line and set its position to the center of one of the two rectangles, then, compute the angle and distance to get to the other rectangle and apply a rotation (and change the width). But I think it can be very complicated since it need to compute the angle and width of the line each time one of the two rectangles is moved.
    I also though about the anchors, but then the line is horizontal and too thick. I can afterwards apply a rotation and change the width, but I think there must be an easier way.

    Is there any other easy way to do it?

    Thank you again.

    1 Reply Last reply
    0
    • J Offline
      J Offline
      Jens
      wrote on last edited by
      #2

      The probably easiest solution is to cover the scene with a Canvas or a C++ QQuickPaintedItem and then draw the lines between items using QPainter. Note that this works through rasterization so you will loose hardware acceleration and might have significantly higher memory requirements as a tradeoff. But if it works in your use case it would be a lot simpler, and you are not constrained to straight lines only.

      Note this comment applies to Qt5. In Qt4, a standard QDeclarativeItem* would be your best bet.

      *Edit : As Chris pointed out, I incorrectly said QQuickItem here.

      1 Reply Last reply
      0
      • E Offline
        E Offline
        excalibur1491
        wrote on last edited by
        #3

        Hi, thank you for your answer!

        I am using Qt4.8 (I am forced to...) so if I understang well you are telling me to use QQuickItem. But I couldn't find any documentation on that class for Qt4.8, only for Qt5: http://qt-project.org/doc/qt-5.0/qtquick/qquickitem.html

        Also, I don't really like loosing hardware acceleration. But I could give it a try. Could you just explain me a little bit more how to do it?

        Any way, is it really the only good option? I was hoping there was an easy solution using something like Line Element of Qt3D (http://doc.qt.digia.com/qt-quick3d-snapshot/qml-line.html) but for only 2D. (I think I cannot use QtQuick3D for this project)

        Thank you again.

        1 Reply Last reply
        0
        • C Offline
          C Offline
          chrisadams
          wrote on last edited by
          #4

          Hi,

          Jens meant QDeclarativeItem in Qt4, where you can override the paint() function to do whatever painting you like.

          As Jens said Qt5/QtQuick2, you can use either the Canvas type or a QQuickPaintedItem, with a low z-value, as the "background" and dynamically draw lines between x/y coordinates as required (then just have rectangles with higher z-value above the canvas).

          If you're using Qt 4.8, and QDeclarativeItem, you're probably not using hardware acceleration to do your painting anyway, so you shouldn't worry too much about that. Just subclass QDeclarativeItem, override the paint() method, register it as a QML type, and it should work.

          Cheers,
          Chris.

          1 Reply Last reply
          0
          • E Offline
            E Offline
            excalibur1491
            wrote on last edited by
            #5

            Ok thank you, I already thought about that, but it seamed to me a bit "too much" for just drawing a line ^^. But I think that is what I will do (I have already done that for the two "rectangles" that I want to attach, which are no rectangles in fact)

            In that case, my class Line should be something like this?:
            @
            class A : public QDeclarativeItem{ /plenty of code/ };

            class Line : public QDeclarativeItem{
            private:
            A* _attachPoint1;
            A* _attachPoint2;
            public:
            Line(A* pt1 = NULL, A* pt2 = NULL);
            public slots:
            void attachTo(A* pt1, A* pt2);
            void changePosition(A* newPosition);
            };
            @

            and then I have to connect:
            @
            connect(_attachPoint1,signal(onPositionChanged(A*)),this,SLOT(changePosition(A*)))

            void changePosition(A* newPosition){
            //Then I change my Line object properties thanks to the coordenates from the passed A object.
            }
            @

            But there is no onPositionChanged signal in my class A ... How can I code it?
            The position of my A objects changes when it is dragged (setting drag.target in a MouseArea into my object in QML), so, how can connect the fact of being dragged to my Line object?
            @
            A {
            id: myObject
            color: "red"
            width: 100
            height: 100
            MouseArea{
            id: ma;
            anchors.fill : parent;
            drag.target: myObject
            }
            }
            @
            I mean, when I drag it, the x and y properties of the object change, but how can I catch that changing?

            thank you again!

            1 Reply Last reply
            0
            • shavS Offline
              shavS Offline
              shav
              wrote on last edited by
              #6

              [quote author="excalibur1491" date="1357123707"]
              But there is no onPositionChanged signal in my class A … How can I code it?
              The position of my A objects changes when it is dragged (setting drag.target in a MouseArea into my object in QML), so, how can connect the fact of being dragged to my Line object?
              [/quote]

              If I understand you correctly you want to catch when your component is moving, right? If so, you may connect two signals to your slots, for example:
              @
              connect(item, SIGNAL(xChanged()), SLOT(onXChanged()));
              connect(item, SIGNAL(yChanged()), SLOT(onYChanged()));
              @

              In both methods which you set as slot you may get a sender objects and get his X and Y positions. The code for that may looks like:
              @

              void QmlApplicationViewer::onXChanged()
              {
              QDeclarativeItem* item = sender();

              if(item != NULL)
              {
                  qDebug()<<"x: "<<item->x();
              }
              

              }

              void QmlApplicationViewer::onYChanged()
              {
              QDeclarativeItem* item = sender();

              if(item != NULL)
              {
                  qDebug()<<"y: "<<item->y();
              }
              

              }
              @

              I hope this will help you.

              Mac OS and iOS Developer

              1 Reply Last reply
              0
              • E Offline
                E Offline
                excalibur1491
                wrote on last edited by
                #7

                Hi!

                Thanks you a lot! I didn't knew about "sender()" and that "connect".
                Ok, so I see more or less how to do the Line object and the two slots.
                But I dont really see how to emit those signals because when my object is moved, I'm not even sure that the object itself notices since it's moved with a drag in a MouseArea inside the object. So the object won't emit those signals.
                The big difficulty I have is how to emit those signals.
                The only way I see is:
                @
                A{
                id: a
                MouseArea{
                drag.target: a
                onPositionChanged:{
                if (drag.active){
                emit a.onXChanged()
                emit a.onYChanged()
                }
                }
                }
                }
                @

                Is that ven possible?
                There must be a better way to do it, isn't it?

                Thank you again, you're all being of big help :)

                1 Reply Last reply
                0
                • shavS Offline
                  shavS Offline
                  shav
                  wrote on last edited by
                  #8

                  The signals xChanged() and yChanged() is a standard signals for QDeclarativeItem objects. So, If your component is a subclass from QDeclarativeItem you can use it like:
                  @
                  A{
                  id: a

                  onXChanged: {
                  //Some code here
                  }
                  onYChanged: {
                  //Some code here
                  }
                  }
                  @

                  If you need to get information about new position inside QML object. If you need get this information in C++ class which you want to use this signals, you can connect to the signal to some slot insdie you C++ class.

                  If you want to send a some signal you may use something like this:
                  @
                  //<id_of_component>.<signal_name>(<list_of_parameters>);
                  a.onXChanged(a.x);
                  @

                  By the way, when you try to set a new position for object with id like 'a' the signal 'onXChanged' will called automatically for current object. So you don't needed call this signal manually.

                  Mac OS and iOS Developer

                  1 Reply Last reply
                  0
                  • E Offline
                    E Offline
                    excalibur1491
                    wrote on last edited by
                    #9

                    Thank you a lot!
                    I didn't knew those signals were standard. When I read the documntation of QDeclarativeItem I was looking for somethink like "onPoistionChanged" and I forgot that signals don't appear in the methods list. My bad.

                    Any way, thank you a lot to all of you. I will try to do that then.

                    1 Reply Last reply
                    0
                    • U Offline
                      U Offline
                      utcenter
                      wrote on last edited by
                      #10

                      [quote author="Jens" date="1357046647"]... Note that this works through rasterization so you will loose hardware acceleration and might have significantly higher memory requirements as a tradeoff. [/quote]

                      Doesn't this use OpenGL accelerated QPainter, which IIRC draws paths by triangulating and drawing hardware accelerated geometry?

                      On a side note, I myself have done a "mindmap network" style of app and think it will be significantly easier to implement with the trusty old QGraphicsView/Item/scene stack than with QML. Performance was pretty good too.

                      1 Reply Last reply
                      0
                      • E Offline
                        E Offline
                        excalibur1491
                        wrote on last edited by
                        #11

                        Hi again, I have started coding and it works mor or less, but I have a problem that I don't undestand at all:
                        I have a line between two rectangles, and when I move them the line moves. The problem is that when I move one of them, the line moves properly, but when I move the other the line becomes "dashed", and ugly... it does not move properlly, and I don't get why since it only happens on one side.

                        Here is the code:
                        here is the base qml, nothing special abut it:
                        @
                        import QtQuick 1.0

                        Rectangle{
                        color: "white"
                        width: 500
                        height: 500

                        Rectangle{
                        objectName: "rec1"
                        id: rec1
                        x: 10
                        y: 10
                        width: 100
                        height: 100
                        radius: 50;
                        color: "red"
                        MouseArea{
                        id:marec1
                        anchors.fill: parent
                        drag.target: rec1
                        }
                        }

                        Rectangle{
                        objectName: "rec2"
                        id: rec2
                        x: 200
                        y: 200
                        width: 100
                        height: 100
                        radius: 50;
                        color: "blue"

                         MouseArea{
                            id:marec2
                         anchors.fill: parent
                         drag.target: rec2
                         }     
                        

                        }
                        }
                        @

                        Now, the Line class:
                        @

                        #include <QDeclarativeItem>
                        #include <QPainter>
                        #include <QPen>
                        #include <QColor>

                        class Line: public QDeclarativeItem{

                        Q_OBJECT

                        private:
                        QDeclarativeItem* pt1;
                        QDeclarativeItem* pt2;

                        public:
                        Line(QDeclarativeItem* pt1 = NULL, QDeclarativeItem* pt2 = NULL);

                        public slots:
                        void attachPt1(QDeclarativeItem* pt1);
                        void attachPt2(QDeclarativeItem* pt2);
                        void paint(QPainter* p, const QStyleOptionGraphicsItem* o = NULL, QWidget* w= NULL);
                        void _update(); //The aim of this slot is that update() is not a slot, so it only calls update().
                        };
                        @

                        The implementation:
                        @
                        #include "Line.h"
                        #include <QTextStream>
                        #include <QIODevice>

                        Line::Line(QDeclarativeItem* pt1, QDeclarativeItem* pt2):QDeclarativeItem(NULL),pt1(pt1),pt2(pt2){
                        setFlag(QGraphicsItem::ItemHasNoContents, false);
                        }

                        void Line::attachPt1(QDeclarativeItem* pt1){
                        this->pt1 = pt1;
                        update();
                        QObject::connect(pt1,SIGNAL(xChanged()),this,SLOT(_update()));
                        QObject::connect(pt1,SIGNAL(yChanged()),this,SLOT(_update()));
                        }
                        void Line::attachPt2(QDeclarativeItem* pt2){
                        this->pt2 = pt2;
                        update();
                        QObject::connect(pt2,SIGNAL(xChanged()),this,SLOT(_update()));
                        QObject::connect(pt2,SIGNAL(yChanged()),this,SLOT(_update()));
                        }
                        void Line::_update(){update();}

                        void Line::paint(QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w){

                        QTextStream ts (stderr,QIODevice::WriteOnly);
                        ts << "paint " << pt1<< " " << pt2 << " " <<sender()<< endl;
                        QPen pen(QColor("red"),2);
                        p->setPen(pen);
                        QLine line(pt1->x()+pt1->width()/2,
                        pt1->y()+pt1->height()/2,
                        pt2->x()+pt2->width()/2,
                        pt2->y()+pt2->height()/2);
                        p->drawLine(line);

                        }
                        @
                        When the two extremities are set, the connection is done.
                        The drawing function only uses a QLine, nothing special about it.

                        Last, the main:
                        @
                        #include <QApplication>
                        #include <QDebug>
                        #include <QObject>
                        #include <QDeclarativeEngine>
                        #include <QDeclarativeComponent>
                        #include <QDeclarativeItem>
                        #include <QDeclarativeView>
                        #include <QString>
                        #include <QTextStream>
                        #include <QIODevice>

                        #include "Line.h"

                        int main(int argc, char* argv[]){

                        QApplication app(argc, argv);
                        QTextStream ts(stderr,QIODevice::WriteOnly);

                        qmlRegisterType<Line>("LinesTest", 1,0, "Line");

                        QDeclarativeView* view = new QDeclarativeView;
                        view->setSource(QUrl::fromLocalFile("Base.qml";);

                        QDeclarativeEngine* e = view->engine();
                        QDeclarativeComponent c (e,QUrl("example.qml"));
                        Line *l = qobject_cast<Line *>(c.create());

                        qWarning() << c.errors();

                        QDeclarativeItem* r1 = view->rootObject()->findChild<QDeclarativeItem*>("rec1");
                        QDeclarativeItem* r2 = view->rootObject()->findChild<QDeclarativeItem*>("rec2");

                        l->attachPt1(r1);
                        l->attachPt2(r2);

                        l->setParentItem(qobject_cast<QDeclarativeItem*>(view->rootObject()));
                        view->show();

                        return app.exec();
                        }
                        @
                        The example.qml file does not contain anythin special, just this:
                        @
                        import LinesTest 1.0
                        import QtQuick 1.0

                        Line{
                        smooth: true
                        //color: "red";
                        }
                        @

                        There is nt big code, but I really dont see where the bug comes from. It is the first time I use a QLine class, I hope it is OK.

                        Thank you again!

                        1 Reply Last reply
                        0
                        • E Offline
                          E Offline
                          excalibur1491
                          wrote on last edited by
                          #12

                          Any idea, please?

                          thanks again

                          1 Reply Last reply
                          0
                          • j1eloJ Offline
                            j1eloJ Offline
                            j1elo
                            wrote on last edited by
                            #13

                            I didn't review your code at detail but lately I've been doing the same as you (a graph editor) and, while in my case I wanted to find a pure QML+JavaScript solution with Qt 5, I found a Line component example from Nokia, implemented in Qt 4. Maybe you should follow their code:
                            http://www.developer.nokia.com/Community/Wiki/Creating_a_custom_QML_element_with_Qt

                            1 Reply Last reply
                            0
                            • U Offline
                              U Offline
                              utcenter
                              wrote on last edited by
                              #14

                              For taking advantage of the scenegrapgh, you might want to look at this example, which pretty much implements a geometry node for a bezier curve:

                              http://doc-snapshot.qt-project.org/5.0/qtqml/quick-scenegraph-customgeometry.html

                              1 Reply Last reply
                              0
                              • E Offline
                                E Offline
                                excalibur1491
                                wrote on last edited by
                                #15

                                Hi, I have already ued this example: http://www.developer.nokia.com/Community/Wiki/Creating_a_custom_QML_element_with_Qt
                                and it works great.
                                I thought about the Beziers curve, but I need to have a mousearea covering the line so it would be way more dificult.
                                Any way, thank you all

                                1 Reply Last reply
                                0

                                • Login

                                • Login or register to search.
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • Users
                                • Groups
                                • Search
                                • Get Qt Extensions
                                • Unsolved