Customizing shape of bounding rect
Asked Answered
S

4

10

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

Supersonic answered 9/10, 2014 at 6:21 Comment(7)
This is a cross post. :)Supersonic
Use QGraphicsItem::shape.Kauslick
I am a newbie. Can you please provide some demo code.Supersonic
Did you check the docs I linked? There is an example there. Also check the docs for QPainterPath. You might be able to create your shape with QPainterPath::addPolygon.Kauslick
You cannot change the shape or orientation of the bounding box. But you can change the shape used for collision and hit testing (as shown by @thuga) - is that what you meant?Desdemona
@Kauslick Make your comment an answer, you may want to mention QPainterPathStroker::createStroke(..) if the user ever wants to create curvy lines.Desdemona
For future reference: parts of the shape outside of the bounding rect are not interacted with.Deplore
K
10

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.

Kauslick answered 9/10, 2014 at 8:3 Comment(0)
N
3

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.

Nelrsa answered 9/10, 2014 at 7:32 Comment(0)
S
2

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;
}
Starstarboard answered 9/10, 2014 at 8:41 Comment(0)
S
-1

You must draw yourself bounding if you want some thing like this. let Qt have it's QRect for bounding and define your new QRect dependent to the corner of previous QRect, top-left and bottom-right. for example if the top-left corner is (2,2) your new QRect top-left is (1,2) and top-right is (2,1) and ....

Safeconduct answered 9/10, 2014 at 6:51 Comment(3)
"You must draw yourself bounding if you want some thing like this" This is not true at all.Desdemona
it is simple to reduce my point and say (it is not true at all). if you can answer the question do. I create a whole project by QGraphicsView with all of these features. If you did not see this thing you can not say it is not true.Safeconduct
I can answer the question, but @Kauslick has provided the answer in a comment 45mins ago and the rep is his/hers to claim. Your answer is wrong because you do not need to draw a bounds in order in set them, you can override QGraphicsItem::boundingRect(). Further more the bounding box is axis aligned so drawing a rotated rectangle will cause the actual bounding box to be much larger.Desdemona

© 2022 - 2024 — McMap. All rights reserved.