How to align Graphics items in Graphic scene based

2019-08-24 03:56发布

问题:

I have Graphic scene in that i have to alight right,left,top or bottom based on first selected item(Reference Item). i searched i got some code but in this its aligning to scene right position. I have to align items based on first selected item. how can i do this?

    void GraphicScene::ItemsRightAlign()
    {
      if (selectedItems().isEmpty())
          return;
      QRectF refRect = selectedItems().first()->boundingRect();
      QList<QGraphicsItem*> sel =selectedItems();  //   for example
      foreach(QGraphicsItem* selItem, sel)
      {
         qreal dx = 0, dy = 0;
         QRectF itemRect = selItem->mapToScene(selItem->boundingRect()).boundingRect();

         //if(align_right)
         dx = refRect.right() - itemRect.right();
         qDebug() << "item pos "<< dx << dy << selItem->mapToScene(selItem->boundingRect()).boundingRect() ;
         selItem->moveBy(dx, dy);
      }

    }

For more details

Output should be like this output

回答1:

The resolution method is to map the point that determines the right, left, up, down to the scene of the first item and the other items obtaining the difference that must be compensated.

graphicsscene.h

#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H

#include <QGraphicsScene>
#include <QGraphicsItem>

class GraphicsScene: public QGraphicsScene{
    Q_OBJECT
public:
    GraphicsScene(QObject *parent=nullptr);
    void moveSelecteds(Qt::Alignment aligment);
private slots:
    void onSelectionChanged();
private:
    void move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment);
    QGraphicsItem *mRef;
};
#endif // GRAPHICSSCENE_H

graphicsscene.cpp

#include "graphicsscene.h"

GraphicsScene::GraphicsScene(QObject *parent):
    QGraphicsScene(parent),
    mRef(nullptr)
{
    connect(this, &GraphicsScene::selectionChanged, this, &GraphicsScene::onSelectionChanged);
}

void GraphicsScene::moveSelecteds(Qt::Alignment aligment){
    QList<QGraphicsItem *> its= selectedItems();
    if(its.size() < 2)
        return;
    if(!its.removeOne(mRef))
        return;
    move(mRef, its, aligment);
}

void GraphicsScene::onSelectionChanged(){
    if(selectedItems().isEmpty()){
        mRef = nullptr;
    }
    else if(selectedItems().size() == 1){
        mRef = selectedItems().first();
    }
}

void GraphicsScene::move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment){
    QPointF p;
    switch (aligment) {
    case Qt::AlignLeft:
        p = QPointF(ref->mapToScene(ref->boundingRect().topLeft()).x(), 0);
        break;
    case Qt::AlignRight:
        p = QPointF(ref->mapToScene(ref->boundingRect().topRight()).x(), 0);
        break;
    case Qt::AlignTop:
        p = QPointF(0, ref->mapToScene(ref->boundingRect().topLeft()).y());
        break;
    case Qt::AlignBottom:
        p = QPointF(0, ref->mapToScene(ref->boundingRect().bottomLeft()).y());
        break;
    }

    for(QGraphicsItem *o: others){
        QPointF delta;
        switch (aligment) {
        case Qt::AlignLeft:{
            delta = p - QPointF(o->mapToScene(o->boundingRect().topLeft()).x(), 0);
            break;
        }
        case Qt::AlignRight:{
            delta = p - QPointF(o->mapToScene(o->boundingRect().topRight()).x(), 0);
            break;
        }
        case Qt::AlignTop:{
            delta = p - QPointF(0, o->mapToScene(o->boundingRect().topLeft()).y());
            break;
        }
        case Qt::AlignBottom:{
            delta = p - QPointF(0, o->mapToScene(o->boundingRect().bottomLeft()).y());
            break;
        }
        }
        o->moveBy(delta.x(), delta.y());
    }
}

In this example you can use the up, down, left, right keys to move the items.

main.cpp

#include "graphicsscene.h"

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QShortcut>
#include <random>

static void create_items(QGraphicsScene  & scene){

    std::default_random_engine generator;
    std::uniform_int_distribution<int> dist_size(30, 40);
    std::uniform_int_distribution<int> dist_pos(-50, 50);

    for(const QString & colorname : {"red", "green", "blue", "gray", "orange"}){
        QRectF r(QPointF(dist_pos(generator), dist_pos(generator)),
                 QSizeF(dist_size(generator), dist_size(generator)));
        auto item = new QGraphicsRectItem(r);
        item->setPos(QPointF(dist_pos(generator), dist_pos(generator)));
        item->setBrush(QColor(colorname));
        item->setFlag(QGraphicsItem::ItemIsSelectable);
        scene.addItem(item);
    }
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    GraphicsScene scene;
    create_items(scene);
    QGraphicsView view(&scene);

    const QList<QPair<Qt::Key, Qt::Alignment>> k_a {
        {Qt::Key_Up, Qt::AlignTop},
        {Qt::Key_Down, Qt::AlignBottom},
        {Qt::Key_Left, Qt::AlignLeft},
        {Qt::Key_Right, Qt::AlignRight}
    };

    for(const QPair<Qt::Key, Qt::Alignment> & p : k_a){
        QShortcut *shorcut = new QShortcut(p.first, &view);
        QObject::connect(shorcut, &QShortcut::activated, std::bind(&GraphicsScene::moveSelecteds, &scene, p.second));
    }

    view.resize(640, 480);
    view.show();

    return a.exec();
}

The complete example can be found in the following link.



回答2:

Replace topLeft.x -- for Right align with topRight.x ,For Top Align replace topLeft.y and dx with dy,For Bottom Align replace bottomLeft.y and dx with dy

    void GraphicScene::ItemsLeftAlign
    {
       if (selectedItems().isEmpty())
           return;
       QGraphicsItem *FirstSelItem = selectedItems().first();
       QList<QGraphicsItem*> sel =selectedItems();  //   for example
       foreach(QGraphicsItem* selItem, sel)
       {
           qreal dx = 0, dy = 0;
           dx = (FirstSelItem->mapToScene(FirstSelItem->boundingRect().topLeft()).x()) -
                (selItem->mapToScene(selItem->boundingRect().topLeft()).x());
           selItem->moveBy(dx, dy);
       }
    }