Unsolved 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.
-
@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
-
@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 :)
-
@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.
-
@mrjj
Thanks. It is working now. -
@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. -
@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 ? -
@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. -
@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 ? -
@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; }
-
@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 -
Loop over all edges and find the nearest edge to your mouse click position and then highlight the polygon which includes that edge. If there are overlapped edges, simply choose the first polygon or the one with higher Z value.
-