Draggable line segment
-
wrote on 19 Dec 2019, 16:31 last edited by
Hi all,
I have a Line segment which is implemented as follows:
void MainWindow::paintEvent(QPaintEvent *e) { QPainter painter1(this); QPen pointpen(Qt::black); pointpen.setWidth(5); QPen linepen1(Qt::red); linepen1.setWidth(2); p1.setX(400); //p1.setY();----> get values through a QSlider p2.setX(450); //p2.setY();----> get values through a QSlider painter1.setPen(linepen1); painter1.drawLine(p1,p2); }
MainWindow
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); ui->verticalSlider->setRange(150,200); connect(ui->verticalSlider, &QSlider::valueChanged, this, &MainWindow::changeP1value); }
and ChangeValue slot is
void MainWindow::changeP1value(int value) { p1.setY(value); p2.setY(value); update(); }
Here the Line segment is movable as I change
QSlider
.I'm aiming to implement the line segment itself draggable. It should be draggable as I move mouse. What are the steps to implement this. It will be great if you can show with a simple example.
Thanks in advance
-
Hi,
Out of curiosity, why not use the graphics view framework where you have the building blocks ready for that kind of development ?
-
Hi
As @SGaist says, the graphics view framework offer alot of features of out the box with little effort.
Check out the mini diagram app.
https://doc.qt.io/qt-5/qtwidgets-graphicsview-diagramscene-example.htmlAnyway, a draggable line when its straight is not super hard to code.
class MainWindow : public QMainWindow { .... private: bool dragging = false; // status var to see if we are dragging protected: // we then override / make our own of these function to track mouse movement and clicks virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; }; Then in.cpp // small helper to give us the distance int distance(QPoint p1, QPoint p2) { return abs(p2.y() - p1.y()); } // when user clicks void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); // where is mouse // test if we hit the line. give user 10 pixels slack as its hard to hit one pixel if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging = true; // flag we are dragging } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) { dragging = false; // if user release mouse we are not draggign anymore } // then when mouse move void MainWindow::mouseMoveEvent(QMouseEvent *event) { // If we are dragging, call your normal slider changed function to update your points. if ( dragging ) { changeP1value(event->y()); } }
And that is it.
-
wrote on 20 Dec 2019, 11:29 last edited by viniltc
@SGaist , @mrjj Thanks a lot for introducing me to Graphic view framework. Will definitely give it a try.
I was trying out the example @mrjj pointed out for the draggable line.Here,
void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); //----> // test if we hit the line. give user 10 pixels slack as its hard to hit one pixel if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging = true; } }
I get the following error:
mainwindow.cpp:44:22: error: member access into incomplete type 'QMouseEvent' qwidget.h:72:7: note: forward declaration of 'QMouseEvent'
Also, I get error related to points
p1
andp2
mainwindow.cpp:28: error: C2065: 'p1': undeclared identifier mainwindow.cpp:28: error: C2228: left of '.x' must have class/struct/union mainwindow.cpp:28: type is 'unknown-type' mainwindow.cpp:28: error: C2065: 'p2': undeclared identifier
I decalred
p1
andp2
are private as:private: Ui::MainWindow *ui; bool dragging = false; QPoint p1, p2;
what might be the issue?
-
@SGaist , @mrjj Thanks a lot for introducing me to Graphic view framework. Will definitely give it a try.
I was trying out the example @mrjj pointed out for the draggable line.Here,
void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); //----> // test if we hit the line. give user 10 pixels slack as its hard to hit one pixel if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging = true; } }
I get the following error:
mainwindow.cpp:44:22: error: member access into incomplete type 'QMouseEvent' qwidget.h:72:7: note: forward declaration of 'QMouseEvent'
Also, I get error related to points
p1
andp2
mainwindow.cpp:28: error: C2065: 'p1': undeclared identifier mainwindow.cpp:28: error: C2228: left of '.x' must have class/struct/union mainwindow.cpp:28: type is 'unknown-type' mainwindow.cpp:28: error: C2065: 'p2': undeclared identifier
I decalred
p1
andp2
are private as:private: Ui::MainWindow *ui; bool dragging = false; QPoint p1, p2;
what might be the issue?
@viniltc said in Draggable line segment:
member access into incomplete type 'QMouseEvent'
include QMouseEvent header
-
wrote on 20 Dec 2019, 11:51 last edited by
@jsulm Thanks.
Still have following issues related to points p1 and p2:
mainwindow.cpp:28: error: C2065: 'p1': undeclared identifier mainwindow.cpp:28: error: C2228: left of '.x' must have class/struct/union mainwindow.cpp:28: type is 'unknown-type' mainwindow.cpp:28: error: C2065: 'p2': undeclared identifier
-
wrote on 20 Dec 2019, 12:01 last edited by viniltc
That's was a typo, my bad. It is working now ! :)
Thanks a lot guys :) -
wrote on 20 Dec 2019, 14:41 last edited by
I wonder if I have multiple line segments to drag, having more drag status variables is the best way to implement?
for example:
bool dragging1 = false; // for line 1 bool dragging2 = false; // fpr line 2
and
void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging1 = true; } else if (distance ( mp, p3) < 10 && ( mp.x() > p3.x() && mp.x() < p4.x() ) ) { dragging2 = true; } } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if ( dragging1 ) { changeP1value(event->y()); } else if (dragging2) { changeP2value(event->y()); } }
Whats your suggestion?
-
I wonder if I have multiple line segments to drag, having more drag status variables is the best way to implement?
for example:
bool dragging1 = false; // for line 1 bool dragging2 = false; // fpr line 2
and
void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging1 = true; } else if (distance ( mp, p3) < 10 && ( mp.x() > p3.x() && mp.x() < p4.x() ) ) { dragging2 = true; } } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if ( dragging1 ) { changeP1value(event->y()); } else if (dragging2) { changeP2value(event->y()); } }
Whats your suggestion?
@viniltc
Hi
Well that is a ok way if you have 2.
But if you had many, i would go with a
QPoint *CurPoint1=nullptr;
QPoint *CurPoint2=nullptr;and then in
void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging = true; CurPoint1 = &p1; CurPoint2 = &p2; } else if (distance ( mp, p3) < 10 && ( mp.x() > p3.x() && mp.x() < p4.x() ) ) { dragging = true; CurPoint1 = &p3; CurPoint1 = &p4; } }
and then use that in to update the points in ValueChanged
as not to have same code over and over for more points.the check and && ( mp.x() > p3.x() && mp.x() < p4.x() ) )
and assignment could also be changed to use the pointers instead as not to replicate code since
its the same but just with other points.
A list could also be used to allow as many as you need.Word of warning. make sure to test if CurPoint1/2 is null before using them as
if you start with dragging slider, the values are null as we first assign on mouse. -
wrote on 20 Dec 2019, 15:59 last edited by
@mrjj Thanks a lot for your feedback, it works well.
I also did this in the constructor to show the line in a predefined height:MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); p1.setY(100); p2.setY(100); p3.setY(100); p4.setY(100); }
what is the best way to set a range to drag, for example from 100 the line should only draggable within (50,150).
-
@mrjj Thanks a lot for your feedback, it works well.
I also did this in the constructor to show the line in a predefined height:MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); p1.setY(100); p2.setY(100); p3.setY(100); p4.setY(100); }
what is the best way to set a range to drag, for example from 100 the line should only draggable within (50,150).
Hi
You need to keep the original y values (in its own variables in .h/class)
and then in mousepress store then values ( or value since they have same y both points)
so in
mouseMoveEvent you can check if its still within allowed drag range and if not then do not call
the ValueChanged.It will then stay with in your range.
-
wrote on 20 Dec 2019, 17:00 last edited by viniltc
I did this:
private: Ui::MainWindow *ui; bool dragging = false; QPoint p1 = QPoint(400,100); QPoint p2 = QPoint(450,100); QPoint p3 = QPoint(450,100); QPoint p4 = QPoint(500,100); QPoint *CurPoint1=nullptr; QPoint *CurPoint2=nullptr;
I'm a bit confused how to implement those conditons in
mousePressEvent
andmouseMoveEvent
If I do this in themouseMoveEvent
if (dragging) { if( CurPoint1->y()>50 && CurPoint1->y()<150) { changeP1value(event->y()); } }
It just stop update if I go beyond the limit. What might be the issue?
-
Hi
If it stops showing the line maybeif (dragging) { if( CurPoint1->y()>50 && CurPoint1->y()<150) { changeP1value(event->y()); } else update(); }
To redraw the point with the last points value.
-
Hi
If it stops showing the line maybeif (dragging) { if( CurPoint1->y()>50 && CurPoint1->y()<150) { changeP1value(event->y()); } else update(); }
To redraw the point with the last points value.
wrote on 22 Dec 2019, 18:20 last edited byActually it just stops when I hit 50 or 150 , it is not updating after that.
I doubt if it's a problem with
mousePressEvent
, now I do this:void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); if (distance ( mp, p1) < 10 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging = true; CurPoint1 = &p1; CurPoint2 = &p2; } else if (distance ( mp, p3) < 10 && ( mp.x() > p3.x() && mp.x() < p4.x() ) ) { dragging = true; CurPoint1 = &p3; CurPoint2 = &p4; } } // then when mouse move void MainWindow::mouseMoveEvent(QMouseEvent *event) { // If we are dragging, call your normal slider changed function to update your points. if (dragging) { if( CurPoint1->y()>50 && CurPoint1->y()<150) { changeP1value(event->y()); } else update(); } } void MainWindow::changeP1value(int value) { CurPoint1->setY(value); CurPoint2->setY(value); update(); }
-
@viniltc said in Draggable line segment:
it is not updating after that.
You mean it stops drawing the line or in what way ?
-
I mean, if I drag beyond 50 or 150 (y values). The line just stops at those points and freezes, can't drag anymore.
@viniltc
Yes, was that not the goal ??
Or do u mean it get stucked and cannot be dragged down again ? -
@viniltc
Yes, was that not the goal ??
Or do u mean it get stucked and cannot be dragged down again ? -
wrote on 23 Dec 2019, 14:58 last edited by viniltc
This is what I'm doing now, but the situation is same, the line gets stuck and can't drag down:
public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void paintEvent(QPaintEvent *e); public slots: void changeP1value(int); private: Ui::MainWindow *ui; bool dragging = false; QPoint p1 = QPoint(400,100); QPoint p2 = QPoint(450,100); QPoint p3 = QPoint(450,100); QPoint p4 = QPoint(500,100); QPoint *CurPoint1=nullptr; QPoint *CurPoint2=nullptr; protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; }; #endif // MAINWINDOW_H
and in
.cpp
int distance(QPoint x1, QPoint x2) { return abs(x2.y() - x1.y()); } void MainWindow::paintEvent(QPaintEvent *e) { QPainter painter1(this); QPen pointpen(Qt::black); pointpen.setWidth(5); QPen linepen1(Qt::red); linepen1.setWidth(2); QPen linepen2(Qt::green); linepen2.setWidth(2); painter1.setPen(linepen1); painter1.drawLine(p1,p2); painter1.setPen(linepen2); painter1.drawLine(p3,p4); qDebug()<<"Values of p1.y and p2.y:"<<p1.y()<<"and"<<p2.y(); } void MainWindow::mousePressEvent(QMouseEvent *event) { QPoint mp = event->pos(); if (distance ( mp, p1) < 20 && ( mp.x() > p1.x() && mp.x() < p2.x() ) ) { dragging = true; CurPoint1 = &p1; CurPoint2 = &p2; } else if (distance ( mp, p3) < 20 && ( mp.x() > p3.x() && mp.x() < p4.x() ) ) { dragging = true; CurPoint1 = &p3; CurPoint2 = &p4; } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) { dragging = false; } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if(CurPoint1!=nullptr) qDebug()<<"Curr point is NOT null"; if (dragging) { if(CurPoint1->y()>50 && CurPoint1->y()<150) { changeP1value(event->y()); } else update(); } } void MainWindow::changeP1value(int value) { CurPoint1->setY(value); CurPoint2->setY(value); update(); }
Can you spot any spot any issues?
-
Hi
There is no errors as such but since we can continue drag mouse after the endpoint,
the mouse event coordinates drifts and hence even dragging is still true some of the other checks is not valid anymore. ( like you are not on the line anymore)I do not know any good solution to it currently.