2012-12-20 58 views
0

我有我自己的類型QGraphicsLineItem的派生類,其中我重寫paint()以將其呈現爲箭頭。查找QGraphicsItem的大綱

我的測試線是160,130,260,230

我的paint()方法實現:

void MyQGraphicsLineItem::paint(QPainter* aPainter, const QStyleOptionGraphicsItem*  aOption, QWidget* aWidget /*= nullptr*/) 
{ 
Q_UNUSED(aWidget); 

aPainter->setClipRect(aOption->exposedRect); 

// Get the line and its angle 
QLineF cLine = line(); 
const qreal cLineAngle = cLine.angle(); 

// Create two copies of the line 
QLineF head1 = cLine; 
QLineF head2 = cLine; 

// Shorten each line and set its angle relative to the main lines angle 
// this gives up the "arrow head" lines 
head1.setLength(12); 
head1.setAngle(cLineAngle+-32); 

head2.setLength(12); 
head2.setAngle(cLineAngle+32); 

// Draw shaft 
aPainter->setPen(QPen(Qt::black, 1, Qt::SolidLine)); 
aPainter->drawLine(cLine); 

// Draw arrow head 
aPainter->setPen(QPen(Qt::red, 1, Qt::SolidLine)); 
aPainter->drawLine(head1); 
aPainter->setPen(QPen(Qt::magenta, 1, Qt::SolidLine)); 
aPainter->drawLine(head2); 
} 

這繪製了一個箭頭,看起來像這樣:

enter image description here

我想要做的是能夠計算這個項目的「輪廓」,以便我可以從數據中繪製一個填充的QPolygon。

我不能使用任何快捷方式,例如用不同的筆寬度繪製兩條線,因爲我希望輪廓是動畫「虛線」(又名遊行螞蟻)。

我敢肯定,這是簡單的計算,但我的數學技能是非常糟糕 - 我試圖做創建平行線以下:

  1. 商店的線的角度。
  2. 將角度設置爲0.
  3. 複製線條。
  4. 在副本上使用QLineF :: translate()。
  5. 將兩個線的角度都設置回您存儲在1中的值 - 這會導致每行的開始和結束位置未對齊。

希望有人可以把我放在正確的軌道上,從這條線上創建一個厚的QPolygonF(或其他任何有意義的),然後可以有一個輪廓和填充設置爲繪畫。

另外我打算在我的場景中有1000個這樣的理想情況,我也想要一個解決方案,它不會花費太多的執行時間或者有一個簡單的優化方法。

enter image description here

這裏這張圖片就是我試圖實現 - 想象中的紅線是Qt的虛線,而不是在畫是我非常糟糕MSPAINT嘗試!

回答

0

我幾乎忘了這個問題,這裏是我的PyQt解決方案,我不確定是否有任何方法可以提高它的性能。

類ArrowItem(QGraphicsLineItem):

def __init__(self, x, y , w, h, parent = None): 
    super(ArrowItem, self).__init__(x, y, w, h, parent) 
    self.init() 

def paint(self, painter, option, widget): 
    painter.setClipRect(option.exposedRect) 
    painter.setBrush(Qt.yellow) 

    if self.isSelected(): 
     p = QPen(Qt.red, 2, Qt.DashLine) 
     painter.setPen(p) 
    else: 
     p = QPen(Qt.black, 2, Qt.SolidLine) 
     p.setJoinStyle(Qt.RoundJoin) 
     painter.setPen(p) 

    painter.drawPath(self.shape()) 

def shape(self): 
    # Calc arrow head lines based on the angle of the current line 
    cLine = self.line() 

    kArrowHeadLength = 13 
    kArrowHeadAngle = 32 

    cLineAngle = cLine.angle() 
    head1 = QLineF(cLine) 
    head2 = QLineF(cLine) 
    head1.setLength(kArrowHeadLength) 
    head1.setAngle(cLineAngle+-kArrowHeadAngle) 
    head2.setLength(kArrowHeadLength) 
    head2.setAngle(cLineAngle+kArrowHeadAngle) 

    # Create paths for each section of the arrow 
    mainLine = QPainterPath() 
    mainLine.moveTo(cLine.p2()) 
    mainLine.lineTo(cLine.p1()) 

    headLine1 = QPainterPath() 
    headLine1.moveTo(cLine.p1()) 
    headLine1.lineTo(head1.p2()) 

    headLine2 = QPainterPath() 
    headLine2.moveTo(cLine.p1()) 
    headLine2.lineTo(head2.p2()) 

    stroker = QPainterPathStroker() 
    stroker.setWidth(4) 

    # Join them together 
    stroke = stroker.createStroke(mainLine) 
    stroke.addPath(stroker.createStroke(headLine1)) 
    stroke.addPath(stroker.createStroke(headLine2)) 

    return stroke.simplified() 

def boundingRect(self): 
    pPath = self.shape() 
    bRect = pPath.controlPointRect() 
    adjusted = QRectF(bRect.x()-1, bRect.y()-1, bRect.width()+2, bRect.height()+2) 
    return adjusted 

..當然並設置項目可移動/可選擇的。

所以你可以看到所需的類來獲得「輪廓」是QPainterPathStroker。

http://doc.qt.io/qt-5/qpainterpathstroker.html#details

+0

你是天才!感謝名單。現在我可以更簡潔地繪製我的箭頭,並用鼠標點擊它們! – 2016-06-24 21:46:10

1

即使箭頭移動和旋轉場景後來此方法適用於:

arrow.h

#ifndef ARROW_H 
#define ARROW_H 

#include <QGraphicsLineItem> 
#include <QObject> 
#include <QtCore/qmath.h> 

class Arrow : public QGraphicsLineItem, public QObject 
{ 
public: 
    Arrow(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem* parent = 0); 
    virtual ~Arrow(); 

    QPointF  objectEndPoint1(); 
    QPointF  objectEndPoint2(); 

    void setObjectEndPoint1(qreal x1, qreal y1); 
    void setObjectEndPoint2(qreal x2, qreal y2); 

protected: 
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); 
    void timerEvent(QTimerEvent* event); 

private: 
    inline qreal pi() { return (qAtan(1.0)*4.0); } 
    inline qreal radians(qreal degrees) { return (degrees*pi()/180.0); } 

    void createArrow(qreal penWidth); 

    QPainterPath arrowPath; 
    QPainterPath strokePath; 
    QPainterPath fillPath; 

    int timerID_Anim; 
    int animFrame; 
    qreal animLength; 
    QVector<qreal> dashPattern; 
}; 

#endif 

arrow.cpp

#include "arrow.h" 
#include <QPen> 
#include <QPainter> 
#include <QTimerEvent> 

Arrow::Arrow(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem* parent) : QGraphicsLineItem(0, 0, x2, y2, parent) 
{ 
    setFlag(QGraphicsItem::ItemIsSelectable, true); 

    setObjectEndPoint1(x1, y1); 
    setObjectEndPoint2(x2, y2); 

    qreal dashLength = 3; 
    qreal dashSpace = 3; 
    animLength = dashLength + dashSpace; 
    dashPattern << dashLength << dashSpace; 

    createArrow(1.0); 

    animFrame = 0; 
    timerID_Anim = startTimer(100); 
} 

Arrow::~Arrow() 
{ 
} 

void Arrow::timerEvent(QTimerEvent* event) 
{ 
    if(event->timerId() == timerID_Anim) 
    { 
     animFrame++; 
     if(animFrame >= animLength) animFrame = 0; 
    } 

    update(); //This forces a repaint, even if the mouse isn't moving 
} 

void Arrow::createArrow(qreal penWidth) 
{ 
    QPen arrowPen = pen(); 
    arrowPen.setWidthF(penWidth); 
    arrowPen.setDashPattern(dashPattern); 
    setPen(arrowPen); 

    QPointF p1 = line().p1(); 
    QPointF p2 = line().p2(); 
    qreal angle = line().angle(); 
    qreal arrowHeadAngle = 32.0; 
    qreal length = line().length(); 
    qreal arrowHeadLength = length/10.0; 
    QLineF arrowLine1(p1, p2); 
    QLineF arrowLine2(p1, p2); 
    arrowLine1.setAngle(angle + arrowHeadAngle); 
    arrowLine2.setAngle(angle - arrowHeadAngle); 
    arrowLine1.setLength(arrowHeadLength); 
    arrowLine2.setLength(arrowHeadLength); 

    QPainterPath linePath; 
    linePath.moveTo(p1); 
    linePath.lineTo(p2); 
    QPainterPath arrowheadPath; 
    arrowheadPath.moveTo(arrowLine1.p2()); 
    arrowheadPath.lineTo(p1); 
    arrowheadPath.lineTo(arrowLine2.p2()); 
    arrowheadPath.lineTo(p1); 
    arrowheadPath.lineTo(arrowLine1.p2()); 

    arrowPath = QPainterPath(); 
    arrowPath.addPath(linePath); 
    arrowPath.addPath(arrowheadPath); 
} 

void Arrow::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) 
{ 
    QPen paintPen = pen(); 

    QPainterPathStroker stroker; 
    stroker.setWidth(paintPen.widthF()); 
    stroker.setCapStyle(Qt::FlatCap); 
    stroker.setJoinStyle(Qt::MiterJoin); 

    strokePath = stroker.createStroke(arrowPath); 
    strokePath = strokePath.simplified(); 

    stroker.setDashOffset(animFrame); 
    stroker.setDashPattern(dashPattern); 
    fillPath = stroker.createStroke(strokePath); 

    paintPen.setDashOffset(animFrame); 
    painter->fillPath(fillPath, QBrush(QColor(255,0,0))); 
    painter->fillPath(strokePath, QBrush(QColor(0,255,0))); 
} 

QPointF Arrow::objectEndPoint1() 
{ 
    return scenePos(); 
} 

QPointF Arrow::objectEndPoint2() 
{ 
    QLineF lyne = line(); 
    qreal rot = radians(rotation()); 
    qreal cosRot = qCos(rot); 
    qreal sinRot = qSin(rot); 
    qreal x2 = lyne.x2(); 
    qreal y2 = lyne.y2(); 
    qreal rotEnd2X = x2*cosRot - y2*sinRot; 
    qreal rotEnd2Y = x2*sinRot + y2*cosRot; 

    return (scenePos() + QPointF(rotEnd2X, rotEnd2Y)); 
} 

void Arrow::setObjectEndPoint1(qreal x1, qreal y1) 
{ 
    QPointF endPt2 = objectEndPoint2(); 
    qreal x2 = endPt2.x(); 
    qreal y2 = endPt2.y(); 
    qreal dx = x2 - x1; 
    qreal dy = y2 - y1; 
    setRotation(0); 
    setLine(0, 0, dx, dy); 
    setPos(x1, y1); 
} 

void Arrow::setObjectEndPoint2(qreal x2, qreal y2) 
{ 
    QPointF endPt1 = scenePos(); 
    qreal x1 = endPt1.x(); 
    qreal y1 = endPt1.y(); 
    qreal dx = x2 - x1; 
    qreal dy = y2 - y1; 
    setRotation(0); 
    setLine(0, 0, dx, dy); 
    setPos(x1, y1); 
}