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);
}