QGraphicsLineItem的轮廓

       在一个简单的画图程序中,其中一个最基本的目标是在QGraphicsView上画一根线,线的两端可以进行拖拽,这在QGraphics View框架中实现其实是很简单的,处理一下鼠标事件,在paint函数中给选中状态下的线段两端各画一个小矩形,简单的线段编辑就可以实现了。

QGraphicsLineItem的轮廓_第1张图片

    好了,开始拖拽了,这时意外出现了

QGraphicsLineItem的轮廓_第2张图片

随着鼠标的拖动,线段两端的小矩形会出现残影

这应该是item的update区域没有包含那些残影的范围

QGraphicsLine的update区域应该是它的boundingRect函数所返回的矩形区域,重新实现一下boundingRect函数

QRectF rc = QGraphicsLineItem::boundingRect();

rc.adjust(-w, -w, w, w);

在往外扩大端点小矩形的宽度大小,这样再拖动就不会出现问题了

拖动整根线段是QGraphics View框架自带的功能,线段宽度很小的时候,尤其为1的时候,非常难选中,虽然可以进行框选,但选中线段后对整根线段进行拖拽就显得很困难了,要用鼠标仔细移到线段上才能进行拖动

QGraphicsItem的鼠标操作区域应该是shape函数返回的QPainterPath区域,于是我决定重新实现shape函数,实现一块包含线段和线段端点,斜率和线段相同的四边形区域,这样原来boundingRect函数的实现也可以去掉了,直接可以用系统默认的

写了一段代码

    QPointF pt1 = line().p1();
    QPointF pt2 = line().p2();
    if (pt1.x() > pt2.x()) {
        QPointF&& tmp = std::move(pt2);
        pt2 = std::move(pt1);
        pt1 = std::move(tmp);
    }
    QPolygonF polygon;
    polygon << QPointF(pt1.x() - w, pt1.y() - w) << QPointF(pt1.x() - w, pt1.y() + w)
            << QPointF(pt2.x() + w, pt2.y() + w) << QPointF(pt2.x() + w, pt2.y() - w)

            <     QPainterPath path;
    path.addPolygon(polygon);

预想的实现应该是这样

QGraphicsLineItem的轮廓_第3张图片

实际上在拖动以后却会变成这样:

QGraphicsLineItem的轮廓_第4张图片

很明显,这种实现方法是错误的

如果直接把QGraphicsLineItem的shape画出来,那应该是这样的:

QGraphicsLineItem的轮廓_第5张图片

很完美的斜率与线段相同的矩形,手头上一下子没有QGraphicsLineItem的源码,加上对Graphics View框架也是学习不多,没明白源码是怎么实现的,于是决定还是自己实现一个,实现的目标是这样:

QGraphicsLineItem的轮廓_第6张图片

首先取线段的斜率,并计算角度

 
  
double angle = std::atan((down_point.y() - up_point.y()) / (down_point.x() - up_point.x()));

再用绕某点旋转的三角函数公式

x=(x1-x2)cosθ-(y1-y2)sinθ+x2
y=(y1-y2)cosθ+(x1-x2)sinθ+y2

将线段旋转成水平状态,在水平状态下,矩形的四个顶点是很容易计算的:

	w *= 1.414;
	QPointF top_left = p1 + QPointF(-w * val, -w);
	QPointF bottom_left = p1 + QPointF(-w * val, w);
	QPointF top_right = p2 + QPointF(w * val, -w);
	QPointF bottom_right = p2 + QPointF(w * val, w);

然后在将这4个点转回去,好了,现在看上去一切都很好

心里还是有个疙瘩,QGraphicsLineItem的shape是怎么实现的,如果我用同样的方法岂不是更简单

还是觉得下载一个源代码来看下这一段的实现

打开一看,原来就是一个QPainterPathStroker类的使用,查了一下这个类的文档,顿时感觉自己做的全是无用功,就用QPainterPathStroker重新实现一下自己的shape函数来扩大原本的shape区域吧,看看用系统类来实现有多么的简单

先计算一下线段端点小矩形的宽度 w,然后就

    w *= 1.414;
    path = QGraphicsLineItem::shape();
    QPainterPathStroker stroker;
    stroker.setWidth(w);
    path = stroker.createStroke(path);
    return path;

看看这样实现出来的效果

QGraphicsLineItem的轮廓_第7张图片

没有任何的问题

最后只需要在paint函数中去掉测试代码

painter->drawPath(shape());

这段代码就可以正常使用了


如果能多吸收些知识,早知道QPainterPathStroker的用法,必然能少走这些弯路

你可能感兴趣的:(Qt)