Walletfox.com

Rotate a collection of QGraphicsItems around the center of their mutual bounding rectangle


This article explains how to implement a rotation of multiple QGraphicsItems around the center of their mutual bounding rectangle. The situation is illustrated in the figure below. Naturally, the approach also works for a single item.

After rotation

MainWindow implementation

The rotation is accomplished with the help of the slot MainWindow::rotate90(), this can be seen in the header file of the MainWindow:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
public slots:
    void rotate90();
private:
    QGraphicsView* view;
    QGraphicsScene* scene;

    QToolBar* editToolBar;
    QAction* rotateAction;

    void createSceneAndView();
    void createToolBarAndAction();
    void createGraphicsItems();
};

In the MainWindow constructor we create the actions, view, scene and populate it with items. The details of the implementation can be found in the individual files above. The important thing is to connect the triggered() signal of the rotateAction to the rotate90() slot of the MainWindow.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(250,250);
    createSceneAndView();
    createGraphicsItems();
    createToolBarAndAction();

    QObject::connect(rotateAction, SIGNAL(triggered()),
                     this, SLOT(rotate90()));
}

Notice that I assigned z-values to my items to avoid stacking order problems:

void MainWindow::createGraphicsItems(){
    QGraphicsEllipseItem* ellipseItem1 =
            new QGraphicsEllipseItem(QRectF(0,0,80,120));
    ellipseItem1->setPos(60,40);
    ellipseItem1->setBrush(Qt::darkBlue);
    ellipseItem1->setZValue(1.0);

    QGraphicsRectItem* rectItem =
            new QGraphicsRectItem(QRectF(0,0,50,80));
    rectItem->setPos(120,60);
    rectItem->setBrush(Qt::cyan);
    rectItem->setZValue(2.0);

    QGraphicsEllipseItem* ellipseItem2 =
            new QGraphicsEllipseItem(QRectF(0,0,70,70));
    ellipseItem2->setPos(80,90);
    ellipseItem2->setBrush(Qt::red);
    ellipseItem2->setZValue(3.0);

    scene->addItem(ellipseItem1);
    scene->addItem(rectItem);
    scene->addItem(ellipseItem2);

    foreach(QGraphicsItem* item, scene->items()){
        item->setFlag(QGraphicsItem::ItemIsSelectable);
        item->setFlag(QGraphicsItem::ItemIsMovable);
    }
}

Implementation of the slot rotate90()

Below you can see the details of the implementation of the rotate90() slot. We create a group from the selected items and create a new transform. Then we perform the steps leading to the desired transformation according to the formula above. At the end, we assign the new transform to our group and disassemble the group.

void MainWindow::rotate90(){
    QGraphicsItemGroup* gr = scene->createItemGroup(
                scene->selectedItems());
    QPointF offset = gr->sceneBoundingRect().center();

    QTransform transform;
    transform.translate(offset.x(),offset.y());
    transform.rotate(90);
    transform.translate(-offset.x(),-offset.y());
    gr->setTransform(transform);

    scene->destroyItemGroup(gr);
    scene->update();
}

That's it. To try out the example you need to select one or multiple items and click on the Rotate icon.