Tracking Widget Positions within a Qt Scene: Exploring Alternatives to QGraphicsSceneMoveEvent::newPos()


Understanding QGraphicsSceneMoveEvent

  • When a QGraphicsWidget's position changes within the scene, it emits a QGraphicsSceneMoveEvent. This event object encapsulates information about the movement.
  • In Qt, QGraphicsScene manages items within a graphical scene. These items can be shapes, text, images, or custom widgets derived from QGraphicsWidget.

QGraphicsSceneMoveEvent::newPos()

  • It returns a QPointF, which represents a floating-point 2D point suitable for precise positioning within the scene's coordinate system.
  • This member function of QGraphicsSceneMoveEvent returns the new position (i.e., the current position) of the QGraphicsWidget after the move has occurred.

Usage Scenario

void MyWidget::mouseMoveEvent(QMouseEvent *event) {
  if (isDragging) {
    // Update the widget's position based on the mouse movement
    QGraphicsSceneMoveEvent moveEvent(pos() + event->pos() - dragStartPosition);
    scene()->spontaneousEvent(this, &moveEvent);

    // Optionally, access the new position after the move:
    QPointF newPosition = moveEvent.newPos();
    // Use newPosition for further processing or calculations
  }
}

In this example:

  1. The mouseMoveEvent handler checks if the widget is being dragged.
  2. If dragging, it calculates the new position based on the mouse movement.
  3. A QGraphicsSceneMoveEvent object is created with the calculated new position.
  4. scene()->spontaneousEvent() sends the event to the scene, simulating the move and notifying other items if necessary.
  5. Optionally, you can retrieve the final new position after the move using moveEvent.newPos(). This is useful if you need to perform actions based on the exact location of the widget after dragging.
  • The returned QPointF allows for precise positioning with floating-point coordinates.
  • It's commonly used in conjunction with event handling (e.g., mouseMoveEvent) to track widget movement within the scene.
  • newPos() provides the new position of the QGraphicsWidget after the move has been completed.


#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QMouseEvent>

class MovableRect : public QGraphicsRectItem {
  Q_OBJECT

public:
  MovableRect(const QRectF &rect) : QGraphicsRectItem(rect) {
    setFlag(QGraphicsItem::ItemIsMovable, true);
    setAcceptHoverEvents(true);
  }

protected:
  void mousePressEvent(QMouseEvent *event) override {
    if (event->button() == Qt::LeftButton) {
      dragStartPosition = event->pos();
      event->accept(); // Accept the event to initiate dragging
    }
  }

  void mouseMoveEvent(QMouseEvent *event) override {
    if (dragStartPosition.isNull()) {
      return;
    }

    QGraphicsSceneMoveEvent moveEvent(pos() + event->pos() - dragStartPosition);
    scene()->spontaneousEvent(this, &moveEvent);

    // Update the rectangle's position based on the final newPos()
    setPos(moveEvent.newPos());
    update(); // Update the widget's appearance on the scene

    event->accept();
  }

  void mouseReleaseEvent(QMouseEvent *event) override {
    if (event->button() == Qt::LeftButton) {
      dragStartPosition = QPoint();
    }
  }

private:
  QPoint dragStartPosition;
};

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);

  // Create a graphics scene and view
  QGraphicsScene scene;
  QGraphicsView view(&scene);
  view.setFixedSize(400, 300);
  view.setWindowTitle("Movable Rectangle");

  // Create a movable rectangle item
  MovableRect *rect = new MovableRect(QRectF(50, 50, 100, 100));
  scene.addItem(rect);

  view.show();

  return app.exec();
}
  1. The MovableRect class inherits from QGraphicsRectItem and implements event handling for dragging.
  2. In mousePressEvent, it checks for left-click and stores the starting position.
  3. mouseMoveEvent calculates the new position based on mouse movement and creates a QGraphicsSceneMoveEvent.
  4. scene()->spontaneousEvent() simulates the move event.
  5. Crucially, setPos(moveEvent.newPos()) updates the rectangle's position using the final new position from the event.
  6. update() ensures the rectangle's visual representation is refreshed on the scene.
  7. mouseReleaseEvent resets the drag start position when the mouse is released.


Using pos() within the Event Handler

  • This approach can be simpler for basic scenarios:
  • Instead of relying on the event object, you can directly calculate the new position based on the mouse movement within the event handler.
void MyWidget::mouseMoveEvent(QMouseEvent *event) {
  if (isDragging) {
    QPoint newPos = pos() + event->pos() - dragStartPosition;
    setPos(newPos);
    // ... other processing
  }
}

Overriding QGraphicsWidget::itemChange()

  • You can override it to react to position changes and perform necessary actions:
  • This virtual function is called whenever a property of the QGraphicsWidget changes, including its position.
QGraphicsItem::ItemChange QMyWidget::itemChange(GraphicsItemChange change, const QVariant &value) {
  if (change == QGraphicsItem::ItemPositionChange) {
    // Handle position change here (e.g., update other items or data)
  }
  return QGraphicsWidget::itemChange(change, value);
}

Storing the Previous Position

  • Update this variable within the event handler and use the difference to calculate the new position when needed.
  • Maintain a member variable to store the widget's position before the move.

These alternatives offer different levels of control and might be suitable depending on your application's requirements.

  • Consider the complexity of your scenario and the level of control you need over position changes when making your choice.
  • If you prefer to directly manage the position updates within the event handler or need to perform actions whenever the position changes (not just during move events), using pos() or overriding itemChange() might be more suitable.
  • If you simply need the new position after a move event for calculations or further processing within the event handler, QGraphicsSceneMoveEvent::newPos() is a straightforward option.