QGraphicsItem移动按照指定格子对齐

QGraphicsScene如何绘制网格

为了获得snap-to-grid功能,我们需要执行以下操作

  • 子类化QGraphicsScene , 重写drawBackground()虚函数
  • 子类化QGraphicsItem , 重写itemChange()
  • 重写继承QGraphicsItem 子类的mousePressEvent()

绘制网格

void Scene::drawBackground(QPainter *painter, const QRectF &rect)
{
    QPen pen;
    painter->setPen(pen);
/*计算起始位置*/
    qreal left = int(rect.left()) - (int(rect.left()) % gridSize);
    qreal top = int(rect.top()) - (int(rect.top()) % gridSize);
    QVector<QPointF> points;
    /*保存所有点位*/
    for (qreal x = left; x < rect.right(); x += gridSize){
        for (qreal y = top; y < rect.bottom(); y += gridSize){
            points.append(QPointF(x,y));
        }
    }
    /*绘制所有的点位*/
    painter->drawPoints(points.data(), points.size());
}

item与网格对齐

子类化QGraphicsRectItem

class CustomRectItem : public QGraphicsRectItem
{
public:
    CustomRectItem(const QRect& rect, QGraphicsItem* parent,
                                   QGraphicsScene* scene);
protected:
    QVariant itemChange(GraphicsItemChange change,
                        const QVariant &value);
};

启用item的可移动可选中可变化属性

CustomRectItem::CustomRectItem(const QRect& rect, QGraphicsItem* parent,
                               QGraphicsScene* scene):
    QGraphicsRectItem(rect, parent, scene)
{
    setFlags(QGraphicsItem::ItemIsSelectable |
            QGraphicsItem::ItemIsMovable |
            QGraphicsItem::ItemSendsGeometryChanges);
}

重写itemChange()
核心算法, 计算最近的网格点的坐标
qreal xV = round(newPos.x()/gridSize)*gridSize;
qreal yV = round(newPos.y()/gridSize)*gridSize;
return QPointF(xV, yV);

QVariant CustomRectItem::itemChange(GraphicsItemChange change,
const QVariant &value)
{
/*不返回原始位置而是返回最近的网格点*/
    if (change == QGraphicsItem::ItemPositionChange && scene()) {
        QPointF newPos = value.toPointF();
        if(QApplication::mouseButtons() == Qt::LeftButton &&
                qobject_cast<Scene*> (scene())){
            Scene* customScene = qobject_cast<Scene*> (scene());
            int gridSize = customScene->getGridSize();
            qreal xV = round(newPos.x()/gridSize)*gridSize;
            qreal yV = round(newPos.y()/gridSize)*gridSize;
            return QPointF(xV, yV);
        }
        else
            return newPos;
    }
    else
        return QGraphicsItem::itemChange(change, value);
}

移动时按网格来移动

子类化

class CustomRectItem : public QGraphicsRectItem
{
public:
    CustomRectItem(const QRect& rect, QGraphicsItem* parent,
                                   QGraphicsScene* scene);
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    QVariant itemChange(GraphicsItemChange change,
    const QVariant &value);
private:
    QPointF offset;//item与其最近左上角坐标的偏移量
    QPointF computeTopLeftGridPoint(const QPointF &pointP); //用于捕获到pointP最近的左上角网格点
};
void CustomRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
    offset = pos() - computeTopLeftGridPoint(pos());
    QGraphicsRectItem::mousePressEvent(event);
}
QPointF CustomRectItem::computeTopLeftGridPoint(const QPointF& pointP){
    Scene* customScene = qobject_cast<Scene*> (scene());
    int gridSize = customScene->getGridSize();
    qreal xV = floor(pointP.x()/gridSize)*gridSize;
    qreal yV = floor(pointP.y()/gridSize)*gridSize;
    return QPointF(xV, yV);
}

重写itemChange

QVariant CustomRectItem::itemChange(GraphicsItemChange change,
const QVariant &value)
{
    if (change == ItemPositionChange && scene()) {
        QPointF newPos = value.toPointF();
        if(QApplication::mouseButtons() == Qt::LeftButton &&
            qobject_cast<Scene*> (scene())){
                QPointF closestPoint = computeTopLeftGridPoint(newPos);
                return  closestPoint+=offset;
                /*每次移动都返回当前点击位置与最近的左上角网格点的偏移量*/
            }
        else
            return newPos;
    }
    else
        return QGraphicsItem::itemChange(change, value);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值