3

I am attempting to properly constrain the movement of a QGraphicsItem (specifically QGraphicsRectItem) without changing the native behavior to function as a scrollbar on the X-axis.

I tried overriding the mouseMoveEvent function, but then I need to re-write the behavior for the rectangle in both the X and Y directions. At best, I can get the rectangle to snap to a single position with the mouse. (Here the rectangle will snap so the mouse holds it at the midpoint):

void SegmentItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
  setY(0);
  setX(event->scenePos().x() - boundingRect().width()/2);
}

I'm looking at itemChange right now, as described here, but it looks a little unwieldy and not exactly elegant. EDIT: This should work, but I currently cannot coerce it to work.

Is there a way to just constrain the y-axis movement? (I will also need to create endstops for the scrollbar, but later.)

Graeme Rock
  • 601
  • 5
  • 21
  • 1
    The itemChange solution linked is The solution, and also very elegant indeed – Fabio Oct 24 '16 at 15:46
  • Cool, but it does not seem to constrain anything currently. I read _"The default implementation does nothing, and returns value."_ on the [Class Reference Page](http://doc.qt.io/qt-4.8/qgraphicsitem.html#itemChange) Do you know what I need to change? – Graeme Rock Oct 24 '16 at 16:16

2 Answers2

2

I tinkered with the code from the itemChange Class Reference Page, and enhanced it so all four corners of my QGraphicsRectItem would stay within the QGraphicsScene:

QVariant SegmentItem::itemChange(GraphicsItemChange change, const QVariant &value)
 {
     if (change == ItemPositionChange && scene()) {
         // value is the new position.
         QPointF newPos = value.toPointF();
         QRectF rect = scene()->sceneRect();
         rect.setWidth(rect.width() - boundingRect().width());
         rect.setHeight(0);
         if (!rect.contains(newPos)) {
             // Keep the item inside the scene rect.
             newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
             newPos.setY(2);
             return newPos;
         }
     }
     return QGraphicsItem::itemChange(change, value);
 }
Graeme Rock
  • 601
  • 5
  • 21
1

To answer the other part of your question about constraining movement to just one direction...use the same itemChange structure as laid out in the above answer. The only additional thing you need to do is transfer your item's current X or Y coordinate to the new position before returning it. This line allows Y to track the mouse, but keeps X the same (i.e. movement is restricted to vertical):

newPos.setX (this->pos().x());

Similarly, to allow X to track the mouse, but keep Y the same (i.e. movement is restricted to horizontal):

newPos.setY (this->pos().y());

With the ItemPositionChange notification, the item's current position hasn't yet changed, so you can manipulate the new position any way you want before returning the new value.

goug
  • 2,294
  • 1
  • 11
  • 15