To create interactive rectangles in a Qt QGraphicsScene, you can subclass QGraphicsRectItem and override its mouse event hendlers. This approach allows you to implement specific behaviors, such as resizing the shape by dragging a specific edge or moving the antire shape across the scene.
Left-Edge Resizing Implementation
The first implementation demonstrates how to restrict resize interactions to the left boundary of the rectangle. By calculating the distance between the mouse cursor and the left edge during a press event, we can activate a resize state only when the cursor falls within a defined pixel threshold. During the move event, the rectangle's geometry is dynamically updated to reflect the new left coordinate and width, while preserving the original height and top position.
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsSceneMouseEvent>
class LeftEdgeResizableRect : public QGraphicsRectItem {
public:
LeftEdgeResizableRect(const QRectF &bounds, QGraphicsItem *parent = nullptr)
: QGraphicsRectItem(bounds, parent),
isResizing(false),
initialWidth(0.0),
resizeOrigin(QPointF()) {}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
QRectF currentBounds = rect();
QPointF cursorPos = event->pos();
const qreal threshold = 8.0; // Detection zone in pixels
// Check if the cursor is near the left edge
if (qAbs(cursorPos.x() - currentBounds.left()) <= threshold) {
isResizing = true;
initialWidth = currentBounds.width();
resizeOrigin = cursorPos;
}
}
QGraphicsRectItem::mousePressEvent(event);
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
if (isResizing) {
QPointF currentCursorPos = event->pos();
qreal deltaX = currentCursorPos.x() - resizeOrigin.x();
qreal updatedWidth = initialWidth - deltaX;
qreal updatedLeft = currentCursorPos.x();
// Prevent the width from collapsing to zero or negative values
if (updatedWidth > 0) {
setRect(updatedLeft, rect().y(), updatedWidth, rect().height());
}
} else {
QGraphicsRectItem::mouseMoveEvent(event);
}
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override {
isResizing = false;
QGraphicsRectItem::mouseReleaseEvent(event);
}
private:
bool isResizing;
qreal initialWidth;
QPointF resizeOrigin;
};
class DisplayView : public QGraphicsView {
public:
DisplayView(QWidget *parent = nullptr) : QGraphicsView(parent) {
QGraphicsScene *canvas = new QGraphicsScene(this);
setScene(canvas);
LeftEdgeResizableRect *shape = new LeftEdgeResizableRect(QRectF(30, 30, 200, 100));
canvas->addItem(shape);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
DisplayView window;
window.resize(400, 300);
window.show();
return app.exec();
}
Whole Shape Dragging Implementation
The second scenario involves moving the entire rectangular shape rather than altering its dimensions. In this case, any left-button click inside the shape initiates a move operation. The displacement between the current and previous cursor positions is calculated during the move event, and the moveBy method is used to shift the item's coordinates accordingly.
#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsSceneMouseEvent>
class FullyDraggableRect : public QGraphicsRectItem {
public:
FullyDraggableRect(const QRectF &bounds, QGraphicsItem *parent = nullptr)
: QGraphicsRectItem(bounds, parent),
isMoving(false) {}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
isMoving = true;
previousCursorPos = event->pos();
}
QGraphicsRectItem::mousePressEvent(event);
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
if (isMoving) {
QPointF displacement = event->pos() - previousCursorPos;
moveBy(displacement.x(), displacement.y());
previousCursorPos = event->pos();
} else {
QGraphicsRectItem::mouseMoveEvent(event);
}
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override {
isMoving = false;
QGraphicsRectItem::mouseReleaseEvent(event);
}
private:
bool isMoving;
QPointF previousCursorPos;
};
class ApplicationWindow : public QMainWindow {
public:
ApplicationWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
QGraphicsScene *canvas = new QGraphicsScene(this);
QGraphicsView *viewport = new QGraphicsView(canvas, this);
setCentralWidget(viewport);
FullyDraggableRect *shape = new FullyDraggableRect(QRectF(50, 50, 180, 120));
canvas->addItem(shape);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ApplicationWindow window;
window.resize(500, 400);
window.show();
return app.exec();
}