Search code examples
qthoverqgraphicsitem

How to enlarge the hover area of a QGraphicsItem


I have a QGraphicsScene with rather small point markers. I would like to enlarge the area of these markers to make dragging easier. The marker is a cross, +/- 2 pixels from the origin. I have reimplemented

QGraphicsItem::contains(const QPointF & point ) const
{
  return QRectF(-10,-10,20,20);
}

and

void hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
  setPen(QPen(Qt::red));
  update();
}

but the marker only turns red when it is directly hit by the cursor (and even that is a bit picky). How can I enlarge the "hover area"?


Solution

  • As stated in the short comment:
    Usually those things are handled via the bounding rect or the shape function, try overloading those. Take a look into the qt help of QGraphicsItem under shape (https://doc.qt.io/qt-6/qgraphicsitem.html#shape):

    Returns the shape of this item as a QPainterPath in local coordinates. The shape is used for many things, including collision detection, hit tests, and for the QGraphicsScene::items() functions.

    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:

    QPainterPath RoundItem::shape() const {
        QPainterPath path;
        path.addEllipse(boundingRect());
        return path; }
    

    The outline of a shape can vary depending on the width and style of the pen used when drawing. If you want to include this outline in the item's shape, you can create a shape from the stroke using QPainterPathStroker.

    This function is called by the default implementations of contains() and collidesWithPath().

    So what basically happens is that all functions that want to access the "Zone" which is associated with an item, call shape and then do e.g. a containment or collision detection with the resulting painterpath.
    Thus if you have small items you should enlargen the shape zone.
    Let's for instance consider a line that is your target, than your shape implementation could look like the following:

    QPainterPath Segment::shape() const{
      QLineF temp(qLineF(scaled(Plotable::cScaleFactor)));
      QPolygonF poly;
      temp.translate(0,pen.widthF()/2.0);
      poly.push_back(temp.p1());
      poly.push_back(temp.p2());
      temp.translate(0,-pen.widthF());
      poly.push_back(temp.p2());
      poly.push_back(temp.p1());
      QPainterPath path;
      path.addPolygon(poly);
      return path;
    }
    

    Pen is a member of the segment, and I use its width to enlarge the shape zone. But you can take anything else as well that has a good relation to the actual dimension of your object.