Selection of lines through mouse click is not sharp in Qt
-
In my view there are 3 lines. 1 Polyline, and 2 straight lines. I have named them as P1 and s1 and s2
I am not able to select s1 line. If I try to select it , P1 gets selected ( though I have not clicked on P1 )
Selection of p1 is also not sharp. If I clicked somewhere around P1 ( not on P1 ) still P1 gets selected.
void Widget::on_designButoon_clicked() { // For line S2 QPolygonF net0; net0 << QPointF(50,180); net0 << QPointF(600,180); QPainterPath pPath0; pPath0.addPolygon(net0); MyPoly* _poly0 = new MyPoly(); _poly0->DrawPolyline(pPath0); scene->addItem(static_cast<QGraphicsPathItem*>(_poly0)); // Same logic for Line S1 and P1 }
MyPoly.h
class MyPoly : public QGraphicsPathItem { //Q_OBJECT public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void DrawPolyline(QPainterPath pPath); };
MyPoly.cpp
void MyPoly::DrawPolyline(QPainterPath pPath) { this->setPen(QPen(QColor("red"), 2)); this->setPath(pPath); this->setFlag(QGraphicsItem::ItemIsSelectable); //this->setBoundingRegionGranularity(1.0); this has no effect }
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->setBrush(Qt::NoBrush); painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine)); painter->drawPath(shape()); painter->restore(); } }
-
In my view there are 3 lines. 1 Polyline, and 2 straight lines. I have named them as P1 and s1 and s2
I am not able to select s1 line. If I try to select it , P1 gets selected ( though I have not clicked on P1 )
Selection of p1 is also not sharp. If I clicked somewhere around P1 ( not on P1 ) still P1 gets selected.
void Widget::on_designButoon_clicked() { // For line S2 QPolygonF net0; net0 << QPointF(50,180); net0 << QPointF(600,180); QPainterPath pPath0; pPath0.addPolygon(net0); MyPoly* _poly0 = new MyPoly(); _poly0->DrawPolyline(pPath0); scene->addItem(static_cast<QGraphicsPathItem*>(_poly0)); // Same logic for Line S1 and P1 }
MyPoly.h
class MyPoly : public QGraphicsPathItem { //Q_OBJECT public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void DrawPolyline(QPainterPath pPath); };
MyPoly.cpp
void MyPoly::DrawPolyline(QPainterPath pPath) { this->setPen(QPen(QColor("red"), 2)); this->setPath(pPath); this->setFlag(QGraphicsItem::ItemIsSelectable); //this->setBoundingRegionGranularity(1.0); this has no effect }
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->setBrush(Qt::NoBrush); painter->setPen(QPen(option->palette.windowText(), 0, Qt::SolidLine)); painter->drawPath(shape()); painter->restore(); } }
@JonB @SGaist @ChrisW67 @jsulm
Sorry for last 2 times , I was not framing my problem properly. So it was annoying you all. But this time I have framed it as simple as possible.
Moreover I got to know that, in such cases I need to override QGraphicsItem::contains() and write my own logic to check whenther, clicked point is inside any QGraphicsItem.
I tried to override it but I could not do.
bool MyPoly::contains(const QPointF &point) const { qreal x = point.x(); qreal y = point.y(); if(shape().contains(point)) { qDebug()<<"Shape found "; qDebug()<< "shape point is = " << shape().elementAt(0); return true; } else { qDebug()<<"Shape not found "; return false; } } }
Will overriding contains() solve my issue ? or do you know any better way ?
-
@JonB @SGaist @ChrisW67 @jsulm
Sorry for last 2 times , I was not framing my problem properly. So it was annoying you all. But this time I have framed it as simple as possible.
Moreover I got to know that, in such cases I need to override QGraphicsItem::contains() and write my own logic to check whenther, clicked point is inside any QGraphicsItem.
I tried to override it but I could not do.
bool MyPoly::contains(const QPointF &point) const { qreal x = point.x(); qreal y = point.y(); if(shape().contains(point)) { qDebug()<<"Shape found "; qDebug()<< "shape point is = " << shape().elementAt(0); return true; } else { qDebug()<<"Shape not found "; return false; } } }
Will overriding contains() solve my issue ? or do you know any better way ?
@tushu said in Selection of lines through mouse click is not sharp in Qt:
if(shape().contains(point))
Will overriding contains() solve my issue ?
bool QGraphicsItem::contains(const QPointF &point) const:
By default, this function calls
shape()
, but you can reimplement it in a subclass to provide a (perhaps more efficient) implementation.So what does your override do/achieve that is any different?
If anything I would have thought virtual QPainterPath QGraphicsItem::shape() const:
The default implementation calls
boundingRect()
to return a simple rectangular shape, but subclasses can reimplement this function to return a more accurate shape for non-rectangular items. For example, a round item may choose to return an elliptic shape for better collision detection. For example:Maybe this is what you need to override (I don't know)?
-
@tushu said in Selection of lines through mouse click is not sharp in Qt:
if(shape().contains(point))
Will overriding contains() solve my issue ?
bool QGraphicsItem::contains(const QPointF &point) const:
By default, this function calls
shape()
, but you can reimplement it in a subclass to provide a (perhaps more efficient) implementation.So what does your override do/achieve that is any different?
If anything I would have thought virtual QPainterPath QGraphicsItem::shape() const:
The default implementation calls
boundingRect()
to return a simple rectangular shape, but subclasses can reimplement this function to return a more accurate shape for non-rectangular items. For example, a round item may choose to return an elliptic shape for better collision detection. For example:Maybe this is what you need to override (I don't know)?
@JonB Thank you for your reply.
I understood what you want to say. I should override shape() instead of contains(). I should create a shape using stroke using QPainterPathStroker as I want to include outline in the item's shape.
But main problem is, it is too tough for me. Because from start to end , process is not clear.
I saw the example,QPainterPath RoundItem::shape() const { QPainterPath path; path.addEllipse(boundingRect()); return path; }
but not understanding, which item I need to add ? when I will return it, where will it go ? How my paint() will get signal and it will highlight that item ?
Confusing. Can you help me in it ? -
@tushu said in Selection of lines through mouse click is not sharp in Qt:
if(shape().contains(point))
Will overriding contains() solve my issue ?
bool QGraphicsItem::contains(const QPointF &point) const:
By default, this function calls
shape()
, but you can reimplement it in a subclass to provide a (perhaps more efficient) implementation.So what does your override do/achieve that is any different?
If anything I would have thought virtual QPainterPath QGraphicsItem::shape() const:
The default implementation calls
boundingRect()
to return a simple rectangular shape, but subclasses can reimplement this function to return a more accurate shape for non-rectangular items. For example, a round item may choose to return an elliptic shape for better collision detection. For example:Maybe this is what you need to override (I don't know)?
@JonB I understood the problem exactly.
While selecting any of the overlapping lines, the one with bounding rect on top is selected. And this is the exact my problem.
For that I need to override shape() ( you were right as usual ).
I googled and I got a few code samples.QPainterPath Item::shape() const { QPainterPath path; QPolygon polygon; polygon << QPoint(0, 0); polygon << QPoint(5, 5); polygon << QPoint(width, height); polygon << QPoint(width - 5, height - 5); path.addPolygon(polygon); return path; }
QPainterPath Line::shape() { QRectF rect(start_p, end_p).normalized(); // increase the rect beyond the width of the line rect.adjust(-2, -2, 2, 2); QPainterPath path; path.addRect(rect); return path; // return the item's defined shape }
So here the question is, how will I know start_p, end_p ( starting and ending points of a line ) width, height ?
-
@JonB I understood the problem exactly.
While selecting any of the overlapping lines, the one with bounding rect on top is selected. And this is the exact my problem.
For that I need to override shape() ( you were right as usual ).
I googled and I got a few code samples.QPainterPath Item::shape() const { QPainterPath path; QPolygon polygon; polygon << QPoint(0, 0); polygon << QPoint(5, 5); polygon << QPoint(width, height); polygon << QPoint(width - 5, height - 5); path.addPolygon(polygon); return path; }
QPainterPath Line::shape() { QRectF rect(start_p, end_p).normalized(); // increase the rect beyond the width of the line rect.adjust(-2, -2, 2, 2); QPainterPath path; path.addRect(rect); return path; // return the item's defined shape }
So here the question is, how will I know start_p, end_p ( starting and ending points of a line ) width, height ?
-
Have your sub-classed QGraphicsItem keep track of them. Store the points and/or polygon to a member variable. Then you can use them in your custom shape() method.
@mchinand I am following below approach. So not understanding how to keep remeber co-ordinates in a variables.
QPolygon net; net << QPoint(67,202); net << QPoint(120,202); net<< QPoint(120,67); net <<QPoint(320,67); net <<QPoint(320,202); net <<QPoint(520,202); net <<QPoint(520,67); QPainterPath pPath; pPath.addPolygon(net); MyPoly* _poly = new MyPoly(); _poly->DrawPolyline(pPath); scene->addItem(static_cast<QGraphicsPathItem*>(_poly));
-
class MyPoly : public QGraphicsPathItem { //Q_OBJECT public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void DrawPolyline(QPainterPath pPath); private: QPolygon polygon_; };
void MyPoly::DrawPolyline(QPolygon polygon) { polygon_ = polygon; QPainterPath pPath; pPath.addPolygon(polygon); this->setPen(QPen(QColor("red"), 2)); this->setPath(pPath); this->setFlag(QGraphicsItem::ItemIsSelectable); //this->setBoundingRegionGranularity(1.0); this has no effect }
QPolygon net; net << QPoint(67,202); net << QPoint(120,202); net<< QPoint(120,67); net <<QPoint(320,67); net <<QPoint(320,202); net <<QPoint(520,202); net <<QPoint(520,67); MyPoly* _poly = new MyPoly(); _poly->DrawPolyline(net);
-
class MyPoly : public QGraphicsPathItem { //Q_OBJECT public: explicit MyPoly(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void DrawPolyline(QPainterPath pPath); private: QPolygon polygon_; };
void MyPoly::DrawPolyline(QPolygon polygon) { polygon_ = polygon; QPainterPath pPath; pPath.addPolygon(polygon); this->setPen(QPen(QColor("red"), 2)); this->setPath(pPath); this->setFlag(QGraphicsItem::ItemIsSelectable); //this->setBoundingRegionGranularity(1.0); this has no effect }
QPolygon net; net << QPoint(67,202); net << QPoint(120,202); net<< QPoint(120,67); net <<QPoint(320,67); net <<QPoint(320,202); net <<QPoint(520,202); net <<QPoint(520,67); MyPoly* _poly = new MyPoly(); _poly->DrawPolyline(net);
@mchinand Thank you for your reply.
I have a doubt :- QPolygon has many points ( in above example it has 7 points ) How the polygon gets drawn ? Segment by segment ? Means 1st 2 points are taken QPoint(67,202) and QPoint(120,202) and then line is drawn. Then next 2 points.
- In shape() , how am I going to access all these points ?
-
I thought your code already worked for drawing, is that not the case? I thought you just needed to figure out how to use the same points that are used for drawing the polyline in the custom shape() method as well.
-
I thought your code already worked for drawing, is that not the case? I thought you just needed to figure out how to use the same points that are used for drawing the polyline in the custom shape() method as well.
@mchinand Yes, my code is working for drawing. But all these concept ( QPolygon, QPainterPath, QPainterPathStroker, shape() , boundingRect() are new to me. ) I just learned how to extract points from QPolygon. Qt is new to me. I am learning from taking help from people like you.
Sorry if you get irritated. But this is how the inexperienced people learn.P.S. Just few days ago I learned the concept behind paint() from JonB.