Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Customizing shape of bounding rect

I am drawing a line using mouse clicks. The line is drawn using paint function as:

painter->drawLine(start_p, end_p);

The bounding rect of line is defined as:

QRectF Line::boundingRect() const
{
  // bounding rectangle for line
  return QRectF(start_p, end_p).normalized();
}

enter image description here

This shows the line painted. I get the bounding rect for this as shown:

enter image description here

I want to have the bounding rect according to the shape of the item, something like:enter image description here

How to achieve this?

Edit

While selecting any of the overlapping lines, the one with bounding rect on top is selected(see figure below). Even making use of setZValue won't work here. I want to implement this by minimizing the bounding rect to the shape of line.

enter image description here

like image 838
Kamalpreet Grewal Avatar asked Oct 09 '14 06:10

Kamalpreet Grewal


3 Answers

If you have an item that is not shaped like a rectangle, or is a rotated rectangle use QGraphicsItem::shape.

This function should return a QPainterPath. You should be able to create your path by using QPainterPath::addPolygon.

Here is a small example:

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;
}

You of course should calculate your points inside the path in a different way, but you get the point. Now when you click on an item, it will only select it if the click happened inside the shape defined by the QPainterPath.

If you ever need to make curvy lines, you can use QPainterPathStroker::createStroke as suggested by cmannett85.

like image 68
thuga Avatar answered Nov 15 '22 15:11

thuga


There are two relevant functions in a QGraphicsItem that you should be interested in. The first is boundingRect. This, as you probably realise is a rectangle which encompasses the whole item. Qt uses this for such things as quickly calculating how much of an item is visible and simple item collision.

That's great if you have rectangular items; you can just override boundingRect() in any items you inherit from QGraphicsItem or QGraphicsObject.

If you have a shape that isn't regular and you want to do things such as collision with an item's shape, then theshape() function needs overriding too in your class.

This returns a QPainterPath, so you can do something like this: -

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
}

Now, you can use a painter to draw the shape() item, instead of the boundingRect() and collision will work as expected.

like image 3
TheDarkKnight Avatar answered Nov 15 '22 15:11

TheDarkKnight


boundingRect is always used for optimize painting process of of scene. So you have have no room for manipulation here.

BUT if you want change area for mouse interaction there is shape method. By default this method returns QPainterPath rectangle received from boundingRect method.
So just override this method and provide desired shape.

QPainterPath YourGraphicsItem::shape() const {
     static const qreal kClickTolerance = 10;

     QPointF vec = end_p-start_p;
     vec = vec*(kClickTolerance/qSqrt(QPointF::dotProduct(vec, vec)));
     QPointF orthogonal(vec.y(), -vec.x());

     QPainterPath result(start_p-vec+orthogonal);
     result.lineTo(start_p-vec-orthogonal);
     result.lineTo(end_p+vec-orthogonal);
     result.lineTo(end_p+vec+orthogonal);
     result.closeSubpath();

     return result;
}
like image 2
Marek R Avatar answered Nov 15 '22 14:11

Marek R