Qt学习笔记:画一条带箭头的线

来源:互联网 发布:grub引导ubuntu 编辑:程序博客网 时间:2024/05/20 19:45

方法1:常规实现

带箭头的线,在很多地方都会用到,以致于一开始我认为Qt会提供这样一个类。。。没想到的是Qt不仅没有提供相关的类,自己实现的时候还颇为复杂。。

其实我比较不理解。。为什么Qt不提供一个带箭头的线的类呢。。为什么为什么呢?这个应该不少人会用到吧。。。

废话不多说,下面开始:

带箭头的线,简单来讲就是一条线加上一个三角形,我们可以通过自定义一个继承自QGraphicsLineItem的类,并重写他的paint()方法来实现:

我们先画一条没有箭头的线:

class MyWidget(QGraphicsView):    def __init__(self):        super(MyWidget, self).__init__()        self.setFixedSize(300, 300)        self.setSceneRect(0, 0, 250, 250)        self.scene = QGraphicsScene()        self.setScene(self.scene)        self.scene.addItem(MyArrow())class MyArrow(QGraphicsLineItem):    def __init__(self):        super(MyArrow, self).__init__()        self.source = QPointF(0, 250)        self.dest = QPointF(120, 120)        self.line = QLineF(self.source, self.dest)        self.line.setLength(self.line.length() - 20)    def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None):        # setPen        pen = QPen()        pen.setWidth(5)        pen.setJoinStyle(Qt.MiterJoin) #让箭头变尖        QPainter.setPen(pen)        # draw line        QPainter.drawLine(self.line)if __name__ == '__main__':    import sys    app = QApplication(sys.argv)    w = MyWidget()    w.show()    sys.exit(app.exec_())

效果如图:

这里写图片描述


然后我们再在这条线的基础上画上箭头:

首先利用QLineF().unitVector()函数得到它的单位向量,并将它移到原线的终点位置,注意这里的偏移量。

    v = self.line.unitVector()    v.setLength(20) # 改变单位向量的大小,实际就是改变箭头长度    v.translate(QPointF(self.line.dx(), self.line.dy()))

这时候我们就得到这样一条线:

这里写图片描述


然后我们利用normalVector()函数得到他的法向量,然后再利用normalVector()得到法向量的反方向的向量。

        n = v.normalVector() # 法向量        n.setLength(n.length() * 0.5) # 这里设定箭头的宽度        n2 = n.normalVector().normalVector() # 两次法向量运算以后,就得到一个反向的法向量

这里写图片描述


然后我们取得 3 个向量的终点 为箭头的三个端点,并以这三点为顶点画出三角形

        p1 = v.p2()        p2 = n.p2()        p3 = n2.p2()                QPainter.drawPolygon(p1, p2, p3)

这里写图片描述


至此,箭头就算完成了!
如果你喜欢,你还可以填充箭头

        brush = QBrush()        brush.setColor(Qt.black)        brush.setStyle(Qt.SolidPattern)        QPainter.setBrush(brush)

最后完成如下:

这里写图片描述这里写图片描述


方法2:利用QPainterPath实现

QPainterPath其实是一个容器,他可以包含一个或者多个不同的绘画步骤,通过这些步骤组成较为复杂的图案,然后使用QPainter.drawPath()将这些图案一次性画出来。

实现的方式和普通方法的区别在于:

普通方法分两步画出图形,先画线,再画箭头

        QPainter.drawLine(self.line)        QPainter.drawPolygon(p1, p2, p3)

而利用QPainterPath,则是先将整个绘制过程设置好,然后一次性画出整个path

        arrow = QPolygonF([p1, p2, p3, p1])        path = QPainterPath()        path.moveTo(self.source) # 移动到线原点        path.lineTo(self.dest) # 添加线的路径        path.addPolygon(arrow) # 添加箭头路径        QPainter.drawPath(path) # 画出整个路径

总结:

  1. 箭头相当于是这条线的额外部分,如果你对线的端点很敏感的话,要注意实际的长度 = 线原长 + 箭头的长度。
  2. 三角形算是最简单的图形了,并没有完全发挥QPainterPath的威力,事实上,你可以画朵花儿在线上。^0^

最后附上完整代码:

# coding:utf-8from PyQt4.QtCore import *from PyQt4.QtGui import *class MyWidget(QGraphicsView):    def __init__(self):        super(MyWidget, self).__init__()        self.setFixedSize(300, 300)        self.setSceneRect(0, 0, 250, 250)        self.scene = QGraphicsScene()        self.setScene(self.scene)        self.scene.addItem(MyArrow())class MyArrow(QGraphicsLineItem):    def __init__(self):        super(MyArrow, self).__init__()        self.source = QPointF(0, 250)        self.dest = QPointF(120, 120)        self.line = QLineF(self.source, self.dest)        self.line.setLength(self.line.length() - 20)    def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None):        # setPen        pen = QPen()        pen.setWidth(5)        pen.setJoinStyle(Qt.MiterJoin)        QPainter.setPen(pen)        # setBrush        brush = QBrush()        brush.setColor(Qt.black)        brush.setStyle(Qt.SolidPattern)        QPainter.setBrush(brush)        v = self.line.unitVector()        v.setLength(20)        v.translate(QPointF(self.line.dx(), self.line.dy()))        n = v.normalVector()        n.setLength(n.length() * 0.5)        n2 = n.normalVector().normalVector()        p1 = v.p2()        p2 = n.p2()        p3 = n2.p2()        # 方法1        QPainter.drawLine(self.line)        QPainter.drawPolygon(p1, p2, p3)        # 方法2        # arrow = QPolygonF([p1, p2, p3, p1])        # path = QPainterPath()        # path.moveTo(self.source)        # path.lineTo(self.dest)        # path.addPolygon(arrow)        # QPainter.drawPath(path)if __name__ == '__main__':    import sys    app = QApplication(sys.argv)    w = MyWidget()    w.show()    sys.exit(app.exec_())
0 0
原创粉丝点击