I have my own derived class of type QGraphicsLineItem where I override paint() in order to render it as an arrow.
My test line is 160, 130, 260, 230
And my paint() implementation:
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 );
}
This draws an arrow which looks like this:
What I would like to do is be able to calculate the "outline" of this item, such that I can draw a filled QPolygon from the data.
I can't use any shortcuts such as drawing two lines with different pen widths because I want the outline to be an animated "dashed" line (aka marching ants).
I'm sure this is simple to calculate but my maths skills are very bad - I attempt to create a parallel line by doing the following:
Hopefully someone can put me on the right track to creating a thick QPolygonF (or anything else if it makes sense) from this line which can then have an outline and fill set for painting.
Also I plan to have 1000's of these in my scene so ideally I'd also want a solution which won't take too much execution time or has a simple way of being optimized.
This image here is what I'm trying to achieve - imagine the red line is a qt dashed line rather than my very bad mspaint attempt at drawing it!
I almost forgot about this question, here was my PyQt solution, I'm not sure if there is any way its performance can be improved.
class 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
.. and of course set the item to be movable/selectable.
And so you can see the required class to get the "outlines" is QPainterPathStroker.