How to pass mouse clicked co-ordinates got through QMouseEvent to different class in Qt?
-
I have a event filter through which I am handling QEvent::MouseButtonPress:.
In this event I am getting co-ordinates of mouse clicked.
I want to pass those co-ordinates to different function which presents in different class file.Mouse click point I am getting in widget.cpp and I want to use them in MyPoly.cpp
Widget.cpp
Widget::Widget(QWidget *parent) : QGraphicsView(parent) , ui(new Ui::Widget) { ui->setupUi(this); scene = new QGraphicsScene(this); view = new QGraphicsView(this); view->setScene(scene); view->viewport()->installEventFilter(this); ui->verticalLayout_2->addWidget(view); } void Widget::on_schematicButoon_clicked() { QPolygon net0; net0 << QPoint(50,180); net0 << QPoint(600,180); MyPoly* _poly0 = new MyPoly(); _poly0->DrawPolyline(net0); scene->addItem(static_cast<QGraphicsPathItem*>(_poly0)); //Same logic for other lines }
bool Widget::eventFilter(QObject* watched, QEvent* event) { bool filterEvent = false; switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); mousePoint = view->mapToScene(mouseEvent->pos()); // want ot pass mousePoint to different function present in class file MyPoly.cpp break; } default: break; } return filter; }
MyPoly.h
class MyPoly : public QGraphicsPathItem { public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void DrawPolyline(QPolygon polygon); QPointF mouseCurrentPosition; // Here I want to store mouse click position. private: QPolygon polygon_; public: bool contains(const QPointF &point) const; };
MyPoly.cpp
void MyPoly::DrawPolyline(QPolygon polygon) { this->polygon_ = polygon; QPainterPath pPath; pPath.addPolygon(polygon); this->setPen(QPen(QColor("blue"), 2)); this->setPath(pPath); this->setFlag(QGraphicsItem::ItemIsSelectable); //this->setBoundingRegionGranularity(1.0); this has no effect }
-
Hi
A good way would be to use signal & slot between Widget and MyPoly classesso in Widget define new signal
void MouseCoordinate(QPointF pos)
and in MyPoly, define a slot with the same parameter (and same name if you wish)
void MouseCoordinate(QPointF pos)
(in this function/slot, you store param pos into currentMouseClickPosition)Then in the place where you have access to both a Widget pointer and a MyPoly pointer
(often in MainWindow if you have such)connect the signal and the slot in the classes
connect( WidgetPTR, &Widget::MouseCoordinate, MyPolyPTR, &MyPolyMouseCoordinate );
make sure you added Q_OBJECT macro to both classes when adding signal/slot.
then in your Widget::eventFilter
doemit MouseCoordinate (mousePoint );
to actually send it.
-
Hi
A good way would be to use signal & slot between Widget and MyPoly classesso in Widget define new signal
void MouseCoordinate(QPointF pos)
and in MyPoly, define a slot with the same parameter (and same name if you wish)
void MouseCoordinate(QPointF pos)
(in this function/slot, you store param pos into currentMouseClickPosition)Then in the place where you have access to both a Widget pointer and a MyPoly pointer
(often in MainWindow if you have such)connect the signal and the slot in the classes
connect( WidgetPTR, &Widget::MouseCoordinate, MyPolyPTR, &MyPolyMouseCoordinate );
make sure you added Q_OBJECT macro to both classes when adding signal/slot.
then in your Widget::eventFilter
doemit MouseCoordinate (mousePoint );
to actually send it.
-
@mrjj Thank for your reply.
But in code, there are many object of MyPoly. And I did not get WidgetPTR (pointer) concept.
Can you help me ?
I have updated full code.@tushu
Hi
WidgetPTR means a pointer to an instance of Widget class.
If you use Mypoly in the Widget class, then you can just use "this" for the pointer.- But in code, there are many object of MyPoly
Do you want all of them to get the same QPointF ? (mouseCurrentPosition)
If yes, you need to hook them up when you create them then they all get the signal.
If think you create new Poly in
void Widget::on_schematicButoon_clicked()
Is that the place ?
- But in code, there are many object of MyPoly
-
@tushu
Hi
WidgetPTR means a pointer to an instance of Widget class.
If you use Mypoly in the Widget class, then you can just use "this" for the pointer.- But in code, there are many object of MyPoly
Do you want all of them to get the same QPointF ? (mouseCurrentPosition)
If yes, you need to hook them up when you create them then they all get the signal.
If think you create new Poly in
void Widget::on_schematicButoon_clicked()
Is that the place ?
@mrjj
Yes, I want to create mouseCurrentPosition like global variable. So that everyone ( every MyPoly object ) can access it.I have created new Poly in
void Widget::on_schematicButoon_clicked()
and I tried like thisvoid Widget::on_schematicButoon_clicked() { QPolygon net0; net0 << QPoint(50,180); net0 << QPoint(600,180); MyPoly* _poly0 = new MyPoly(); _poly0->DrawPolyline(net0); scene->addItem(static_cast<QGraphicsPathItem*>(_poly0)); connect(this, MouseCoordinate, _poly0, setMouseCoordinate(mousePoint) ); //Same logic for other lines }
In MyPoly.cpp
void MyPoly::setMouseCoOrdinate(QPointF mousePoint) { this->mouseCoordinate = mousePoint; }
But I think I have made a mistake in connect().
- But in code, there are many object of MyPoly
-
@mrjj
Yes, I want to create mouseCurrentPosition like global variable. So that everyone ( every MyPoly object ) can access it.I have created new Poly in
void Widget::on_schematicButoon_clicked()
and I tried like thisvoid Widget::on_schematicButoon_clicked() { QPolygon net0; net0 << QPoint(50,180); net0 << QPoint(600,180); MyPoly* _poly0 = new MyPoly(); _poly0->DrawPolyline(net0); scene->addItem(static_cast<QGraphicsPathItem*>(_poly0)); connect(this, MouseCoordinate, _poly0, setMouseCoordinate(mousePoint) ); //Same logic for other lines }
In MyPoly.cpp
void MyPoly::setMouseCoOrdinate(QPointF mousePoint) { this->mouseCoordinate = mousePoint; }
But I think I have made a mistake in connect().
@tushu
Hi
first point
Did remember to add Q_OBJECT to class ?
like
class MyPoly : public QGraphicsPathItem
{
Q_OBJECT
public:for both classes so you can use signals
second the connect.
connect(this, MouseCoordinate, _poly0, setMouseCoordinate(mousePoint) );
includes mousePoint which is not right.
also you forgot the classtype in front and the &
so its more like
connect(this, &Widget::MouseCoordinate, _poly0, &MyPoly::setMouseCoordinate);btw is setMouseCoordinate listed under public slots: in .h ?
(not sure as code don't show it)the & is very important or it will say nasty things to you :)
-
@tushu
Hi
first point
Did remember to add Q_OBJECT to class ?
like
class MyPoly : public QGraphicsPathItem
{
Q_OBJECT
public:for both classes so you can use signals
second the connect.
connect(this, MouseCoordinate, _poly0, setMouseCoordinate(mousePoint) );
includes mousePoint which is not right.
also you forgot the classtype in front and the &
so its more like
connect(this, &Widget::MouseCoordinate, _poly0, &MyPoly::setMouseCoordinate);btw is setMouseCoordinate listed under public slots: in .h ?
(not sure as code don't show it)the & is very important or it will say nasty things to you :)
@mrjj
Currently I have 3 lines ,connect(this,&Widget::MouseCoordinate,_poly,&MyPoly::setMouseCoordinate); connect(this,&Widget::MouseCoordinate,_poly0,&MyPoly::setMouseCoordinate); connect(this,&Widget::MouseCoordinate,_poly1,&MyPoly::setMouseCoordinate);
In filterEvent
case QEvent::MouseButtonPress: { QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); mousePoint = view->mapToScene(mouseEvent->pos()); emit MouseCoordinate (mousePoint); }
In Widget.h
class Widget : public QGraphicsView { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); bool eventFilter(QObject* watched, QEvent* event); QPointF mousePoint; signals: void MouseCoordinate(QPointF);
MyPoly.h
class MyPoly : public QGraphicsPathItem { Q_OBJECT public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget); bool contains(const QPointF &point) const; void DrawPolyline(QPolygon polygon); QPointF currentCursorPos; private: QPolygon polygon_; public slots: void setMouseCoordinate(QPointF); };
MyPoly.cpp
void MyPoly::setMouseCoordinate(QPointF pos) { this-> currentCursorPos = pos; }
Error :
C:\Qt\5.15.2\mingw81_64\include\QtCore\qobject.h:264: error: no matching function for call to 'QObject::connectImpl(const Object*&, void**, const Object*&, void**, QtPrivate::QSlotObject<void (MyPoly::)(QPointF), QtPrivate::List<QPointF>, void>, Qt::ConnectionType&, const int*&, const QMetaObject*)' -
@mrjj
Currently I have 3 lines ,connect(this,&Widget::MouseCoordinate,_poly,&MyPoly::setMouseCoordinate); connect(this,&Widget::MouseCoordinate,_poly0,&MyPoly::setMouseCoordinate); connect(this,&Widget::MouseCoordinate,_poly1,&MyPoly::setMouseCoordinate);
In filterEvent
case QEvent::MouseButtonPress: { QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); mousePoint = view->mapToScene(mouseEvent->pos()); emit MouseCoordinate (mousePoint); }
In Widget.h
class Widget : public QGraphicsView { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); bool eventFilter(QObject* watched, QEvent* event); QPointF mousePoint; signals: void MouseCoordinate(QPointF);
MyPoly.h
class MyPoly : public QGraphicsPathItem { Q_OBJECT public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget); bool contains(const QPointF &point) const; void DrawPolyline(QPolygon polygon); QPointF currentCursorPos; private: QPolygon polygon_; public slots: void setMouseCoordinate(QPointF); };
MyPoly.cpp
void MyPoly::setMouseCoordinate(QPointF pos) { this-> currentCursorPos = pos; }
Error :
C:\Qt\5.15.2\mingw81_64\include\QtCore\qobject.h:264: error: no matching function for call to 'QObject::connectImpl(const Object*&, void**, const Object*&, void**, QtPrivate::QSlotObject<void (MyPoly::)(QPointF), QtPrivate::List<QPointF>, void>, Qt::ConnectionType&, const int*&, const QMetaObject*)'@tushu
AH sorry my bad.
I missed you are using QGraphicsPathItem in MyPoly.
Those are not QObject so default no signals/slots.try change it into
class MyPoly : public QObject, public QGraphicsPathItem {(we also inherited a QObject so we can use signal/slots)
Then completely clean the build folder and try a full rebuild.
-
@tushu
AH sorry my bad.
I missed you are using QGraphicsPathItem in MyPoly.
Those are not QObject so default no signals/slots.try change it into
class MyPoly : public QObject, public QGraphicsPathItem {(we also inherited a QObject so we can use signal/slots)
Then completely clean the build folder and try a full rebuild.
-
@tushu
AH sorry my bad.
I missed you are using QGraphicsPathItem in MyPoly.
Those are not QObject so default no signals/slots.try change it into
class MyPoly : public QObject, public QGraphicsPathItem {(we also inherited a QObject so we can use signal/slots)
Then completely clean the build folder and try a full rebuild.
-
@mrjj
I have a 1 more doubt reagarding another question which is extension of this question.
Can I post here ?@tushu
well if its related , its fine to put here.btw one note.
since you want
QPointF currentCursorPos;
to be shared between all instances, you could also make it static.That in effect makes it a class variable and its truly shared amount all.
If this is needed/better, depends on how many Mypoly you are using as each will get a signal and copy the QPopintf
so if you have like 1000, its more efficient with a shared member but if only a few, don't bother. -
@tushu
well if its related , its fine to put here.btw one note.
since you want
QPointF currentCursorPos;
to be shared between all instances, you could also make it static.That in effect makes it a class variable and its truly shared amount all.
If this is needed/better, depends on how many Mypoly you are using as each will get a signal and copy the QPopintf
so if you have like 1000, its more efficient with a shared member but if only a few, don't bother.@mrjj
I am having 3 lines like this with co-ordinates of each points. But their mouse selection is not sharp. If I click on s1, P1 gets selected. Even selection of P1 is also not sharp. If I click around P1 ( not on P1) it gets selected.
So I was suggested that override contains() . And write own logic of selecting lines. So I did like this.void MyPoly::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { auto copied_option = *option; copied_option.state &= ~QStyle::State_Selected; auto selected = option->state & QStyle::State_Selected; QGraphicsPathItem::paint(painter, &copied_option, widget); if (selected) { painter->save(); painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine)); if(contains(currentCursorPos)) // mouse click co -ordinates painter->drawPath(shape()); painter->restore(); } }
bool MyPoly::contains(const QPointF &point) const { for(int i=0;i < polygon_.count() - 1; i++) { QPoint firstPoint = polygon_.at(i); QPoint lastPoint = polygon_.at(i+1); if(firstPoint.x() == lastPoint.x()) { qDebug()<<"Inside vertical line "; //It is a vertical line if(firstPoint.x() == point.x() && point.y() >= firstPoint.y() && point.y() <= lastPoint.y()) return true; } else { // it is a horizontal line if(point.x() >= firstPoint.x() && point.x() <= lastPoint.x() && point.y() == firstPoint.y() ) return true; } } return false; }
Now I am getting mouse click points in paint() method by the method as suggested by you. I am passing those points to contains(). I am able to select S2 line but S1 line is still unselected. If I click on start and end side of S2, it gets selected properly.
Can you help me in this ? -
@tushu
well if its related , its fine to put here.btw one note.
since you want
QPointF currentCursorPos;
to be shared between all instances, you could also make it static.That in effect makes it a class variable and its truly shared amount all.
If this is needed/better, depends on how many Mypoly you are using as each will get a signal and copy the QPopintf
so if you have like 1000, its more efficient with a shared member but if only a few, don't bother.@mrjj
Logic for selecting line :
If the line you want to check against is horizontal, then test if the mouse-click's y location is equal to the line's y value (or perhaps within a few pixels of it, to allow for user-slop), and if the mouse-click's x location is between the line's two endpoint x values. Checking against a vertical line is similar. -
@tushu
well if its related , its fine to put here.btw one note.
since you want
QPointF currentCursorPos;
to be shared between all instances, you could also make it static.That in effect makes it a class variable and its truly shared amount all.
If this is needed/better, depends on how many Mypoly you are using as each will get a signal and copy the QPopintf
so if you have like 1000, its more efficient with a shared member but if only a few, don't bother.@mrjj
One key observation :
Addition sequence of lines into scene , matters a lot.
If I (first) add P1 -> S1 -> (last) S2 -------- everything works perfectly
If I (first) add S2 -> S1 -> (last) P1 -------- S2 Can not be selected
If I (first) add S1 -> S2 -> (last) P1 ---------- S1 can not be selectedWhy ?
And how to tackle it ? -
@tushu
well if its related , its fine to put here.btw one note.
since you want
QPointF currentCursorPos;
to be shared between all instances, you could also make it static.That in effect makes it a class variable and its truly shared amount all.
If this is needed/better, depends on how many Mypoly you are using as each will get a signal and copy the QPopintf
so if you have like 1000, its more efficient with a shared member but if only a few, don't bother.@mrjj
I found this quote :While selecting any of the overlapping lines, the one with bounding rect on top is selected.
It is happening with me. If I add P1 line at the end, it will not let me select other lines properly.
I found the help : Help
I tried their help, but it did not work out with me.
They gave eample of single line, but I have QPolygon. ( You can see, in MyPoly.h , you will seeprivate: QPolygon polygon_;
My try: ( did not work )
QPainterPath MyPoly::shape() { QPainterPath result1; // Here 1st QPainterPath for(int i=0;i < polygon_.count() - 1; i++) { QPointF firstPoint = polygon_.at(i); QPointF lastPoint = polygon_.at(i+1); qDebug()<<"First Point = "<< firstPoint; qDebug()<<"Second Point = "<< lastPoint; static const qreal kClickTolerance = 10; QPointF vec = lastPoint - firstPoint; vec = vec*(kClickTolerance/qSqrt(QPointF::dotProduct(vec, vec))); QPointF orthogonal(vec.y(), -vec.x()); QPainterPath result(firstPoint-vec+orthogonal); // here 1 more QPainterPath result.lineTo(firstPoint-vec-orthogonal); result.lineTo(lastPoint+vec-orthogonal); result.lineTo(lastPoint+vec+orthogonal); result.closeSubpath(); result.addPolygon(polygon_); result1 = result; } return result1; }
-
@tushu
well if its related , its fine to put here.btw one note.
since you want
QPointF currentCursorPos;
to be shared between all instances, you could also make it static.That in effect makes it a class variable and its truly shared amount all.
If this is needed/better, depends on how many Mypoly you are using as each will get a signal and copy the QPopintf
so if you have like 1000, its more efficient with a shared member but if only a few, don't bother.@mrjj
The more I am debugging, I am closing to the real problem in my code.
Now I think , I found real problem of my code.
For polyline I have used QPolygonF which internally joins start point with end point and make polygon close. And because of that , internally it is becoming closed bounding rect and if I click in that area, only polyline will be selected, no other line passing through that area can be selected.
If I click on red color area, only P1 will be selected. No S1 or S2 can be selected. So how to tackle this issue ? Or how to avoid polygon to be closed ?
Full code and description is in this link. link -