I have a shape drawn with QPainter, which I need to be able to update dynamically, from a single variable called 'width'. I have attached an image to hopefully show what I mean...
Basically the shape should have three parts. Taking the existing shape (in blue), I need to split it down the middle and add a horizontal section, who's length is determined by 'width' var. I would like this shape to be a fully enclosed outline, with no breaks or visible joins.
My problem is simply trying to figure out how to use arcs in QPainter. I'm not understanding the difference between 'arcMoveTo', and 'arcTo' and how to get these things to work for me.
Current code of the basic shape is here:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Canvas(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setMouseTracking(True)
def paintEvent(self, event):
qp = QPainter(self)
qp.setRenderHint(QPainter.Antialiasing, True)
pen = QPen(Qt.black, 15)
pen.setJoinStyle(Qt.MiterJoin)
qp.setPen(pen)
qp.setBrush(Qt.blue)
p = QPainterPath(QPointF(0, 100))
p.arcTo(25, 25, 150, 150, 205, 309)
p.lineTo(0, 100)
qp.drawPath(p)
def mouseMoveEvent(self, event):
# show x and y pos on screen
mp = event.pos()
p = self.mapToGlobal(mp)
p.setY(p.y()-60)
p.setX(p.x()+10)
QToolTip.showText(p, "x:{}\ny:{}".format(mp.x(), mp.y()))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Canvas()
window.show()
sys.exit(app.exec())
I would very much appreciate if someone could help out on this. I've been making a mess of this shape all night! ;)
Looking forward to any replies,
Cheers! Ekar
The purpose of arcMoveTo
is to move the current point to a specific angle within an arc, which may be necessary to start to draw a new path from that point; it is actually a "helper" function that could be done with normal trigonometry.
You don't need that, since you're going to continue the existing path: arcTo
actually draws a new arc, and connects its start point with the previous one.
You have to split the arc in 3 sections:
The connecting lines are automatically drawn because we're using arcTo
as explained above.
This means that you need two rectangles as references for the ellipses: the left one (which is the one you're currently using) and the right one, which is translated by the width
.
baseRect = QRectF(25, 25, 150, 150)
p = QPainterPath(QPointF(0, 100))
p.arcTo(baseRect, 205, 65)
p.arcTo(baseRect.translated(self.width, 0), 270, 180)
p.arcTo(baseRect, 90, 65)
p.lineTo(0, 100)
qp.drawPath(p)
To clarify how this works, I've drawn the reference rectangles (and their full ellipses) in the image below:
This is a comprehensive example that shows an application of the above:
class Canvas(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setMouseTracking(True)
self.width = 0
def sizeHint(self):
return QSize(300, 200)
def setWidth(self, width):
self.width = width
self.update()
def paintEvent(self, event):
qp = QPainter(self)
qp.setRenderHint(QPainter.Antialiasing, True)
pen = QPen(Qt.black, 15)
pen.setJoinStyle(Qt.MiterJoin)
qp.setPen(pen)
qp.setBrush(Qt.blue)
baseRect = QRectF(25, 25, 150, 150)
p = QPainterPath(QPointF(0, 100))
p.arcTo(baseRect, 205, 65)
p.arcTo(baseRect.translated(self.width, 0), 270, 180)
p.arcTo(baseRect, 90, 65)
p.lineTo(0, 100)
qp.drawPath(p)
def mouseMoveEvent(self, event):
p = event.globalPos() + QPoint(10, -60)
QToolTip.showText(p, "x:{}\ny:{}".format(p.x(), p.y()))
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = QWidget()
canvas = Canvas()
spin = QSpinBox(maximum=200, singleStep=10)
layout = QVBoxLayout(window)
layout.addWidget(canvas)
layout.addWidget(spin)
spin.valueChanged.connect(canvas.setWidth)
window.show()
sys.exit(app.exec())