I've been playing around with customising PyQt widgets using paint events. I've been trying to customise the QSlider widget and have had some success, mostly with CSS styling. However, I'm having difficulty making it curved with a QPainterPath as it always seems flat. Is this something that is beyond the capability of this widget (which would surprise me)? The below is my most recent attempt of many with no success. I tried path points instead of cubicTo() with the same. Can anyone help or point me in the right direction?
from PyQt5 import QtGui, QtWidgets, QtCore
class slider(QtWidgets.QSlider):
def __init__(self, parent=None):
super(slider, self).__init__(parent)
self.parent = parent
self.setMinimum(10)
self.setMaximum(30)
self.setValue(20)
self.setTickPosition(QtWidgets.QSlider.TicksBelow)
self.setTickInterval(5)
def paintEvent(self, event):
qp = QtGui.QPainter()
qp.begin(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing)
self.drawCurve(qp)
qp.end()
def drawCurve(self, qp):
path = QtGui.QPainterPath()
path.moveTo(30, 30)
path.cubicTo(30, 30, 200, 350, 200, 30)
qp.drawPath(path)
To have the sensation of depth it is only to choose the correct colors, for this QPainterPathStroker is also used. On the other hand I added the functionality that the QPainterPath is scaled:
from PyQt5 import QtCore, QtGui, QtWidgets
class PathSlider(QtWidgets.QAbstractSlider):
def __init__(self, path=QtGui.QPainterPath(), *args, **kwargs):
super(PathSlider, self).__init__(*args, **kwargs)
self._path = path
self.stroke_path = self._path
self.scale_path = self._path
def setPath(self, path):
path.translate(-path.boundingRect().topLeft())
self._path = path
self.update()
def path(self):
return self._path
path = QtCore.pyqtProperty(QtGui.QPainterPath, fget=path, fset=setPath)
def paintEvent(self, event):
border = 10
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
sx, sy = (self.rect().width() -2*border)/self.path.boundingRect().width(), \
(self.rect().height() -2*border)/self.path.boundingRect().height()
tr = QtGui.QTransform()
tr.translate(border, border)
tr.scale(sx, sy)
self.scale_path = tr.map(self.path)
stroker = QtGui.QPainterPathStroker()
stroker.setCapStyle(QtCore.Qt.RoundCap)
stroker.setWidth(4)
stroke_path = stroker.createStroke(self.scale_path).simplified()
painter.setPen(QtGui.QPen(self.palette().color(QtGui.QPalette.Shadow), 1))
painter.setBrush(QtGui.QBrush(self.palette().color(QtGui.QPalette.Midlight)))
painter.drawPath(stroke_path)
stroker.setWidth(20)
self.stroke_path = stroker.createStroke(self.scale_path).simplified()
percentage = (self.value() - self.minimum())/(self.maximum() - self.minimum())
highlight_path = QtGui.QPainterPath()
highlight_path.moveTo(self.scale_path.pointAtPercent(0))
n_p = int((self.maximum() + 1 - self.minimum())/self.singleStep())
for i in range(n_p+1):
d = i*percentage/n_p
p = self.scale_path.pointAtPercent(d)
highlight_path.lineTo(p)
stroker.setWidth(3)
new_phighlight_path = stroker.createStroke(highlight_path).simplified()
activeHighlight = self.palette().color(QtGui.QPalette.Highlight)
painter.setPen(activeHighlight)
painter.setBrush(QtGui.QBrush(QtGui.QColor(activeHighlight)))
painter.drawPath(new_phighlight_path)
opt = QtWidgets.QStyleOptionSlider()
r = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderHandle, self)
pixmap = QtGui.QPixmap(r.width() + 2*2, r.height() + 2*2)
pixmap.fill(QtCore.Qt.transparent)
r = pixmap.rect().adjusted(2, 2, -2, -2)
pixmap_painter = QtGui.QPainter(pixmap)
pixmap_painter.setRenderHint(QtGui.QPainter.Antialiasing)
pixmap_painter.setPen(QtGui.QPen(self.palette().color(QtGui.QPalette.Shadow), 2))
pixmap_painter.setBrush(self.palette().color(QtGui.QPalette.Base))
pixmap_painter.drawRoundedRect(r, 4, 4)
pixmap_painter.end()
r.moveCenter(p.toPoint())
painter.drawPixmap(r, pixmap)
def minimumSizeHint(self):
return QtCore.QSize(15, 15)
def sizeHint(self):
return QtCore.QSize(336, 336)
def mousePressEvent(self, event):
self.update_pos(event.pos())
super(PathSlider, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
self.update_pos(event.pos())
super(PathSlider, self).mouseMoveEvent(event)
def update_pos(self, point):
if self.stroke_path.contains(point):
n_p = int((self.maximum() + 1 - self.minimum())/self.singleStep())
ls = []
for i in range(n_p):
p = self.scale_path.pointAtPercent(i*1.0/n_p)
ls.append(QtCore.QLineF(point, p).length())
j = ls.index(min(ls))
val = int(j*(self.maximum() + 1 - self.minimum())/n_p)
self.setValue(val)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
s1 = PathSlider(minimum=0, maximum=100)
s2 = PathSlider(minimum=0, maximum=100)
s = QtWidgets.QSlider(minimum=0, maximum=100)
s.valueChanged.connect(s1.setValue)
s.valueChanged.connect(s2.setValue)
s1.valueChanged.connect(s.setValue)
s2.valueChanged.connect(s.setValue)
c1 = QtCore.QPointF(5, -15)
c2 = QtCore.QPointF(220, -15)
path = QtGui.QPainterPath(QtCore.QPointF(5, 100))
path.cubicTo(c1, c2, QtCore.QPointF(235, 100))
s1.setPath(path)
c1 = QtCore.QPointF(5, 15)
c2 = QtCore.QPointF(220, 15)
path = QtGui.QPainterPath(QtCore.QPointF(5, -100))
path.cubicTo(c1, c2, QtCore.QPointF(235, -100))
s2.setPath(path)
lay = QtWidgets.QHBoxLayout(w)
lay.addWidget(s1)
lay.addWidget(s2)
lay.addWidget(s)
w.show()
sys.exit(app.exec_())
Related
I'm currently learning how to use Qt's Qgraphics and i cant get the hoverEnterEvent to work on an QGraphicsEllipseItem object. I am setting setAcceptHoverEvents to True and implemented the hoverEnterEvent function, yet the function isn't getting called.
here's my code:
import sys
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QGraphicsView,
QGraphicsScene,
QGraphicsEllipseItem,
)
from PySide6.QtGui import QPainterPath, QTransform, QPen, QBrush, QColor, QPainter
from PySide6.QtCore import Qt
PORT_PEN_COLOR = "#000000"
PORT_BRUSH_COLOR = "#ebebeb"
EDGE_PEN_COLOR = "#474747"
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 800, 600)
self.setCentralWidget(GraphicsView())
self.show()
class GraphicsView(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setMouseTracking(True)
self.setScene(GraphicsScene())
self.setRenderHint(QPainter.RenderHint.Antialiasing)
class GraphicsScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.setSceneRect(-10000, -10000, 20000, 20000)
self._port_pen = QPen(QColor(PORT_PEN_COLOR))
self._port_brush = QBrush(QColor(PORT_BRUSH_COLOR))
self._edge_pen = QPen(QColor(EDGE_PEN_COLOR))
self._edge_pen.setWidth(4)
def mousePressEvent(self, event):
clicked_item = self.itemAt(event.scenePos(), QTransform())
if event.buttons() == Qt.MouseButton.LeftButton:
if clicked_item is not None:
# edge item
pos = clicked_item.scenePos()
pos.setX(pos.x() + 6)
pos.setY(pos.y() + 6)
self.edge = self.addPath(QPainterPath())
self.edge.setPen(self._edge_pen)
self.start_pos = pos
self.end_pos = self.start_pos
self.update_path()
else:
x = event.scenePos().x()
y = event.scenePos().y()
# port item
start_port = Ellipse()
start_port.setPos(x - 6, y - 6)
start_port.setPen(self._port_pen)
start_port.setBrush(self._port_brush)
start_port.setZValue(10000.0)
self.addItem(start_port)
# edge item
self.edge = self.addPath(QPainterPath())
self.edge.setPen(self._edge_pen)
self.start_pos = event.scenePos()
self.end_pos = self.start_pos
self.update_path()
def mouseMoveEvent(self, event):
if event.buttons() == Qt.MouseButton.LeftButton:
print(f"moving, x : {event.scenePos().x()}, y : {event.scenePos().y()}")
self.end_pos = event.scenePos()
try:
self.update_path()
except AttributeError:
pass
def mouseReleaseEvent(self, event) -> None:
released_item = self.itemAt(event.scenePos(), QTransform())
if event.button() == Qt.MouseButton.LeftButton:
if released_item is not None and released_item.type() != 2:
self.end_pos = released_item.scenePos()
self.end_pos.setX(self.end_pos.x() + 6)
self.end_pos.setY(self.end_pos.y() + 6)
if not self.start_pos.isNull() and not self.end_pos.isNull():
path = QPainterPath()
path.moveTo(self.start_pos.x() - 1, self.start_pos.y() - 1)
path.lineTo(self.end_pos)
self.edge.setPath(path)
else:
x = event.scenePos().x() + 1
y = event.scenePos().y() + 1
end_port = QGraphicsEllipseItem(0, 0, 10, 10)
end_port.setPos(x - 6, y - 6)
end_port.setPen(self._port_pen)
end_port.setBrush(self._port_brush)
end_port.setZValue(10000.0)
self.addItem(end_port)
def update_path(self):
if not self.start_pos.isNull() and not self.end_pos.isNull():
path = QPainterPath()
path.moveTo(self.start_pos.x() - 1, self.start_pos.y() - 1)
path.lineTo(self.end_pos)
self.edge.setPath(path)
class Ellipse(QGraphicsEllipseItem):
def __init__(self):
super().__init__()
self.setRect(0, 0, 10, 10)
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event) -> None:
print("hovered")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
sys.exit(app.exec())
What am i doing wrong here, how do i get the hover event to work please ?
Thank you
I'm trying to rotate the background of my QGraphicsView object an I am getting in trouble. The red pen describes the problem what I'm getting.
What I want:
I want to keep the arrow centered in the middle of my screen.
I want the background image to rotate, taking the center of my screen as reference
I want to define a rectangle inside my QGraphicsView. that will be defined as the region where it will delimit what will be shown. Thus, every time I rotate my screen. The region "not covered" by the image will always be outside the boundary of the rectangle.
I want define the reference point of rotation, in the middle of my screen (x=VIEW_WIDTH/2, y=VIEW_HEIGHT/2)
And here is my code:
First, I'll show the arrow class:
from PyQt5 import QtCore, QtGui, QtWidgets # importation of some libraries
# Construnction of an arrow item, it'll be used in the QGraphicsView
class ArrowItem(QtWidgets.QGraphicsPathItem): # it inherit QgraphicsPathItem, which allows to handle it
# in the QgraphicsView
def __init__(self, parent=None): # The init method
super().__init__(parent)
self._length = -1
self._angle = 0
self._points = QtCore.QPointF(), QtCore.QPointF(), QtCore.QPointF() # with three points we
# construct a triangle.
self.length = 40.0 # the basic triangle length
self.rotate(180) # the triangle was built upside down, though I've just ran the function 'rotate'
#property
def angle(self):
"""
angle of the arrow
:return:
"""
return self._angle
#angle.setter
def angle(self, angle):
self._angle = angle
#property
def length(self):
return self._length
#length.setter
def length(self, l):
self._length = l
pos_top = QtCore.QPointF(0, l * 4 / 5)
pos_left = QtCore.QPointF(-l * 3 / 5, -l / 5)
pos_right = QtCore.QPointF(
l * 3 / 5,
-l / 5,
)
path = QtGui.QPainterPath()
path.moveTo(pos_top)
path.lineTo(pos_right)
path.lineTo(pos_left)
self.setPath(path)
self._points = pos_top, pos_left, pos_right
def paint(self, painter, option, widget):
pos_top, pos_left, pos_right = self._points
left_color = QtGui.QColor("#cc0000")
right_color = QtGui.QColor("#ff0000")
bottom_color = QtGui.QColor("#661900")
path_left = QtGui.QPainterPath()
path_left.lineTo(pos_top)
path_left.lineTo(pos_left)
path_right = QtGui.QPainterPath()
path_right.lineTo(pos_top)
path_right.lineTo(pos_right)
path_bottom = QtGui.QPainterPath()
path_bottom.lineTo(pos_left)
path_bottom.lineTo(pos_right)
painter.setPen(QtGui.QColor("black"))
painter.setBrush(left_color)
painter.drawPath(path_left)
painter.setBrush(right_color)
painter.drawPath(path_right)
painter.setBrush(bottom_color)
painter.drawPath(path_bottom)
def rotate(self, value):
"""
:param value:
"""
self._angle += value
transform = QtGui.QTransform()
transform.translate(20, 100)
transform.rotate(self._angle)
self.setTransform(transform)
# Just a function to translate the arrow, it'll not be used now!
def moveTo(self, next_position, duration=100):
self._animation = QtCore.QVariantAnimation()
self._animation.setStartValue(self.pos())
self._animation.setEndValue(next_position)
self._animation.setDuration(duration)
self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
self._animation.valueChanged.connect(self.setPos)
Here follows the main window code:
# Just setting up the main Window
class WorkScreen(QtWidgets.QMainWindow):
# Some constant variable to keep things easy to change in the future
VIEW_HEIGHT = 600
VIEW_WIDTH = 900
FLAT_BUTTON = True
"""
Attempt to paint the work screen.
"""
def __init__(self, **kwargs):
# initializing the parent class, using a 2.7 pythonic style
super(WorkScreen, self).__init__(**kwargs)
# customizing the main window.
self.setStyleSheet("""
QMainWindow{
background: rgb(180, 180, 180)}
QPushButton{
background-color: rgb(0, 200, 0);
}
""")
# the attribute 'rotation' must be zero.
self.rotation = 0
# defining some attributes
self.pixmap = QtGui.QPixmap()
self.scene = QtWidgets.QGraphicsScene()
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.pushButton_status = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_settings = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_analyse = QtWidgets.QPushButton(self.centralwidget)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.view = QtWidgets.QGraphicsView(self.centralwidget)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.pushButton_field = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_guide = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_mapping = QtWidgets.QPushButton(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.arrowItem = ArrowItem()
self.set_up()
self.view.setScene(self.scene)
self.view.scale(-1, 1)
self.image = QtGui.QImage("ola.png")
self.image = self.image.scaled(self.VIEW_WIDTH+300, self.VIEW_HEIGHT+300)
self.pixmap.convertFromImage(self.image)
self.scene.addPixmap(self.pixmap)
self.add_arrow()
def set_up(self):
"""
build the main window.
"""
self.pushButton_guide.setFlat(self.FLAT_BUTTON)
self.pushButton_mapping.setFlat(self.FLAT_BUTTON)
self.pushButton_field.setFlat(self.FLAT_BUTTON)
self.pushButton_status.setFlat(self.FLAT_BUTTON)
self.pushButton_settings.setFlat(self.FLAT_BUTTON)
self.pushButton_analyse.setFlat(self.FLAT_BUTTON)
self.setObjectName("MainWindow")
self.setMaximumWidth(1024)
self.setMaximumHeight(600)
self.resize(1024, 600)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout_3.setContentsMargins(-1, -1, 0, -1)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.pushButton_status.setObjectName("pushButton_status")
self.verticalLayout_3.addWidget(self.pushButton_status)
self.pushButton_settings.setObjectName("pushButton_settings")
self.verticalLayout_3.addWidget(self.pushButton_settings)
self.pushButton_analyse.setObjectName("pushButton_analyse")
self.verticalLayout_3.addWidget(self.pushButton_analyse)
self.horizontalLayout.addLayout(self.verticalLayout_3)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.view.setRenderHints(QtGui.QPainter.HighQualityAntialiasing | QtGui.QPainter.TextAntialiasing)
self.view.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
self.view.scale(-1, 1)
self.view.setFixedHeight(self.VIEW_HEIGHT)
self.view.setFixedWidth(self.VIEW_WIDTH)
self.view.setObjectName("view")
# self.view.setFixedWidth(self.VIEW_WIDTH)
# self.view.setFixedHeight(self.VIEW_HEIGHT)
self.verticalLayout_2.addWidget(self.view)
self.horizontalLayout.addLayout(self.verticalLayout_2)
self.verticalLayout.setContentsMargins(-1, -1, 0, -1)
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton_field.setObjectName("pushButton_field")
self.verticalLayout.addWidget(self.pushButton_field)
self.pushButton_guide.setObjectName("pushButton_guide")
self.verticalLayout.addWidget(self.pushButton_guide)
self.pushButton_mapping.setObjectName("pushButton_mapping")
self.verticalLayout.addWidget(self.pushButton_mapping)
self.horizontalLayout.addLayout(self.verticalLayout)
self.setCentralWidget(self.centralwidget)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
self.menubar.setObjectName("menubar")
self.setMenuBar(self.menubar)
self.statusbar.setObjectName("statusbar")
self.setStatusBar(self.statusbar)
self.retranslateUi()
QtCore.QMetaObject.connectSlotsByName(self)
# Just to test
self.pushButton_status.clicked.connect(self.rotate_arrow)
self.pushButton_guide.clicked.connect(self.rotate_pixmap)
# Created just to emit a signal
def rotate_arrow(self):
"""
It'll not be used after.
"""
self.arrowItem.rotate(value=30)
def rotate_pixmap(self):
pixmap = self.pixmap
self.rotation += 15
transform = QtGui.QTransform().rotate(self.rotation, axis=QtCore.Qt.ZAxis)
pixmap = pixmap.transformed(transform, QtCore.Qt.SmoothTransformation)
self.scene.addPixmap(pixmap)
self.arrowItem.setPos(self.arrowItem.pos())
self.arrowItem.rotate(self.rotation)
self.arrowItem.setZValue(self.arrowItem.zValue()+1)
def add_arrow(self):
"""
:cvar
"""
self.scene.addItem(self.arrowItem)
self.arrowItem.setPos(QtCore.QPointF(self.VIEW_WIDTH / 2 - 50, self.VIEW_HEIGHT / 2 - 100))
def retranslateUi(self):
_translate = QtCore.QCoreApplication.translate
self.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton_status.setText(_translate("MainWindow", "Situação"))
self.pushButton_settings.setText(_translate("MainWindow", "Configurações"))
self.pushButton_analyse.setText(_translate("MainWindow", "Analisar"))
self.pushButton_field.setText(_translate("MainWindow", "Campo"))
self.pushButton_guide.setText(_translate("MainWindow", "Guia"))
self.pushButton_mapping.setText(_translate("MainWindow", "Mapeamento"))
# calling the application
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = WorkScreen()
ui.show()
sys.exit(app.exec_())
Here is the image that I am using to display on the background:
If you want to rotate a QGraphicsItem it is not necessary to use QTransform, just use the setRotation method. On the other hand, the image must be scaled so that the rotated area intersected by the visible area is the same as the visible area and that minimum area is calculated as: sqrt(2) x max(width, height). Considering the above, the solution is:
from PyQt5 import QtCore, QtGui, QtWidgets
class ArrowItem(QtWidgets.QGraphicsPathItem):
def __init__(self, parent=None):
super().__init__(parent)
self._length = -1
self._points = (
QtCore.QPointF(),
QtCore.QPointF(),
QtCore.QPointF(),
)
self.length = 40.0
#property
def length(self):
return self._length
#length.setter
def length(self, l):
self._length = l
pos_top = QtCore.QPointF(0, l * 4 / 5)
pos_left = QtCore.QPointF(-l * 3 / 5, -l / 5)
pos_right = QtCore.QPointF(
l * 3 / 5,
-l / 5,
)
path = QtGui.QPainterPath()
path.moveTo(pos_top)
path.lineTo(pos_right)
path.lineTo(pos_left)
self.setPath(path)
self._points = pos_top, pos_left, pos_right
def paint(self, painter, option, widget):
pos_top, pos_left, pos_right = self._points
left_color = QtGui.QColor("#cc0000")
right_color = QtGui.QColor("#ff0000")
bottom_color = QtGui.QColor("#661900")
path_left = QtGui.QPainterPath()
path_left.lineTo(pos_top)
path_left.lineTo(pos_left)
path_right = QtGui.QPainterPath()
path_right.lineTo(pos_top)
path_right.lineTo(pos_right)
path_bottom = QtGui.QPainterPath()
path_bottom.lineTo(pos_left)
path_bottom.lineTo(pos_right)
painter.setPen(QtGui.QColor("black"))
painter.setBrush(left_color)
painter.drawPath(path_left)
painter.setBrush(right_color)
painter.drawPath(path_right)
painter.setBrush(bottom_color)
painter.drawPath(path_bottom)
def moveTo(self, next_position, duration=100):
self._animation = QtCore.QVariantAnimation()
self._animation.setStartValue(self.pos())
self._animation.setEndValue(next_position)
self._animation.setDuration(duration)
self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
self._animation.valueChanged.connect(self.setPos)
class MainWindow(QtWidgets.QMainWindow):
VIEW_HEIGHT = 600
VIEW_WIDTH = 900
def __init__(self, parent=None):
super().__init__(parent)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self.scene)
self.status_btn = QtWidgets.QPushButton("Status")
self.settings_btn = QtWidgets.QPushButton("Settings")
self.analyze_btn = QtWidgets.QPushButton("Analyze")
self.field_btn = QtWidgets.QPushButton("Field")
self.guide_btn = QtWidgets.QPushButton("Guide")
self.mapping_btn = QtWidgets.QPushButton("Mapping")
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
vlayl = QtWidgets.QVBoxLayout()
vlayl.addWidget(self.status_btn)
vlayl.addWidget(self.settings_btn)
vlayl.addWidget(self.analyze_btn)
vlayr = QtWidgets.QVBoxLayout()
vlayr.addWidget(self.field_btn)
vlayr.addWidget(self.guide_btn)
vlayr.addWidget(self.mapping_btn)
hlay = QtWidgets.QHBoxLayout(central_widget)
hlay.addLayout(vlayl)
hlay.addWidget(self.view)
hlay.addLayout(vlayr)
self.status_btn.clicked.connect(self.rotate_arrow)
self.guide_btn.clicked.connect(self.rotate_pixmap)
self.view.setFixedSize(self.VIEW_WIDTH, self.VIEW_HEIGHT)
r = self.view.mapToScene(self.view.viewport().rect()).boundingRect()
self.view.setSceneRect(r)
factor = 1.5 * max(self.VIEW_WIDTH, self.VIEW_HEIGHT)
pixmap = QtGui.QPixmap("ola.png").scaled(factor, factor)
self.pixmap_item = self.scene.addPixmap(pixmap)
center = self.pixmap_item.boundingRect().center()
self.pixmap_item.setPos(-center)
self.pixmap_item.setTransformOriginPoint(center)
self.arrow_item = ArrowItem()
self.scene.addItem(self.arrow_item)
def rotate_arrow(self):
delta = 30.0
self.arrow_item.setRotation(self.arrow_item.rotation() + delta)
def rotate_pixmap(self):
delta = 15.0
self.pixmap_item.setRotation(self.pixmap_item.rotation() + delta)
self.arrow_item.setRotation(self.arrow_item.rotation() + delta)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I would like explaining what is it I want to achieve, and what is it, that does not work probably. When a user draw a rectangular or rounded shape, a ring inside rounded shape, and a rectangular ring with rounded edge inside rectangular shape want to be drawn. So far I have achieved with my code... shown below
As shown above, inner rectangular should be subtracted from outer rectangular. And edges should be rounded.
What I want to achieve and similar for rounded shape
The code :
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 800, 800))
self.button = Button()
self.paint = Createpaintwidget()
self.button.valuesChanged.connect(self.paint.set_size_squares)
self.button.valueChanged.connect(self.paint.set_size_round)
self.lay = QtWidgets.QVBoxLayout(self)
self.lay.addWidget(self.paint)
self.lay.addWidget(self.button)
class Createpaintwidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.sizeHint()
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self._size = QtCore.QSizeF()
self._path = QtGui.QPainterPath()
self._rect = QtCore.QRectF()
self._type = QtGui.QRegion.Rectangle
self._factor = 1.0
self._sizeouter = QtCore.QSizeF()
self._rectouter = QtCore.QRectF()
self._sizeinner = QtCore.QSizeF()
self._rectinner = QtCore.QRectF()
self._pos = QtCore.QPointF()
self._initial_flag = False
fnt = self.font()
fnt.setPointSize(20)
self.setFont(fnt)
print(self._size, self._rect, self._type, self._pos)
def showEvent(self, event):
if not self._initial_flag:
self._pos = self.rect().center()
self._initial_flag = True
#QtCore.pyqtSlot(int, int)
def set_size_squares(self, w, h):
cb, ct, yb = 25, 25, 8
self._path = QtGui.QPainterPath()
self._size = QtCore.QSizeF(w, h)
self._sizeouter = QtCore.QSizeF(w-cb, h-ct)
self._sizeinner = QtCore.QSizeF(w-cb-yb, h-ct-yb)
self._type = QtGui.QRegion.Rectangle
self.updatePath()
print(self._size, self._rect, self._type, self._pos)
#QtCore.pyqtSlot(int)
def set_size_round(self, v):
cb, yb = 25, 8
self._path = QtGui.QPainterPath()
self._size = QtCore.QSizeF(v, v)
self._sizeouter = QtCore.QSizeF(v-cb, v-cb)
self._sizeinner = QtCore.QSizeF(v-cb-yb, v-cb-yb)
self._type = QtGui.QRegion.Ellipse
self.updatePath()
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush(QtCore.Qt.black)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(pen)
painter.setBrush(brush)
painter.translate(self.rect().center())
painter.scale(self._factor, self._factor)
painter.translate(-self.rect().center())
painter.translate(self._pos)
painter.drawPath(self._path)
if self._type == QtGui.QRegion.Rectangle:
painter.fillRect(self._rectouter, QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
painter.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
painter.drawRect(self._rectouter)
painter.fillRect(self._rect, QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
painter.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
painter.drawRect(self._rect)
painter.fillRect(self._rectinner, QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
painter.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
painter.drawRect(self._rectinner)
elif self._type == QtGui.QRegion.Ellipse:
painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
painter.drawEllipse(self._rect)
painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.SolidPattern))
painter.drawEllipse(self._rectouter)
painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
painter.drawEllipse(self._rectinner)
def mousePressEvent(self, event):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
self._initial_pos = event.pos()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
delta = event.pos() - self._initial_pos
self._path.translate(delta)
self._rect.translate(delta)
self._rectinner.translate(delta)
self._rectouter.translate(delta)
self.update()
self._initial_pos = event.pos()
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
QtWidgets.QApplication.restoreOverrideCursor()
super().mouseReleaseEvent(event)
def updatePath(self):
fm = QtGui.QFontMetrics(self.font())
r = QtCore.QRectF(QtCore.QPointF(), self._size)
ro = QtCore.QRectF(QtCore.QPointF(), self._sizeouter)
ri = QtCore.QRectF(QtCore.QPointF(), self._sizeinner)
r.moveCenter(QtCore.QPointF())
ro.moveCenter(QtCore.QPointF())
ri.moveCenter(QtCore.QPointF())
r.moveCenter(QtCore.QPointF())
self._rectouter = QtCore.QRectF(ro)
self._rectinner = QtCore.QRectF(ri)
self._rect = QtCore.QRectF(r)
self._path.moveTo(QtCore.QPointF())
self.update()
def wheelEvent(self, event):
self._factor *= 1.01**(event.angleDelta().y()/15.0)
self.update()
super().wheelEvent(event)
class Button(QtWidgets.QWidget):
valueChanged = QtCore.pyqtSignal(int)
valuesChanged = QtCore.pyqtSignal(int,int)
def __init__(self, parent=None):
super(Button, self).__init__(parent)
roundbutton = QtWidgets.QPushButton('Round')
squarebutton = QtWidgets.QPushButton('Square')
Alay = QtWidgets.QVBoxLayout(self)
Alay.addWidget(roundbutton)
Alay.addWidget(squarebutton)
self.value = QtWidgets.QLabel()
roundbutton.clicked.connect(self.getbuttonfunc)
squarebutton.clicked.connect(self.sqaurebuttonfunc)
#QtCore.pyqtSlot()
def getbuttonfunc(self):
number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
self.tr("Input:"), 1, 1)
if ok:
self.valueChanged.emit(number)
#QtCore.pyqtSlot()
def sqaurebuttonfunc(self):
number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
self.tr("Input:"), 1, 1)
if ok:
self.valuesChanged.emit(number, number)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
For these cases it is better to use a QPainterPathStroker and pass it a QPainterPath that I have the round rectangle with addRoundedRect():
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush(QtCore.Qt.black)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(pen)
painter.setBrush(brush)
painter.translate(self.rect().center())
painter.scale(self._factor, self._factor)
painter.translate(-self.rect().center())
painter.translate(self._pos)
painter.drawPath(self._path)
S = (self._rectouter.size() + self._rectinner.size())/2
s = (self._rectouter.size() - self._rectinner.size())/2
r = QtCore.QRectF(QtCore.QPointF(), S)
r.moveCenter(self._rectouter.center())
path = QtGui.QPainterPath()
painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))
if self._type == QtGui.QRegion.Rectangle:
painter.drawRect(self._rect)
path.addRoundedRect(r, 20, 20)
elif self._type == QtGui.QRegion.Ellipse:
painter.drawEllipse(self._rect)
path.addEllipse(r)
stroker = QtGui.QPainterPathStroker()
stroker.setWidth(s.width())
stroke_path = stroker.createStroke(path)
# painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
painter.drawPath(stroke_path)
I would like to draw some lines with QPainter, then move the start (x1,y1) coordinates of lines to centerpoint and some other lines which want to offset from center. Those lines should behave programmatically based on value of ellipse or other values. I have tried myself different ways to come around it but does not work.
To QRect can utilize codes such as
moveCenter, moveTopLeft, etc...
But for Qline there are not such methods. According to PyQt doc, a line can be drawn by this:
QLine(int x1, int y1, int x2, int y2)
QLine(const QPoint &p1, const QPoint &p2)
Maybe this line should be used in order to offset it. But no knowlegde to do it.
translated(const QPoint &offset)
On another hand would like to draw some texts and offset them in similar way as Qline.
Look at figures below to see what is that I exactly want to do?
Visualisation
What I have achieved so far.
What I want to achieve.
The code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 700, 600))
self.paint = Paint()
self.sizeHint()
self.lay = QtWidgets.QVBoxLayout()
self.lay.addWidget(self.paint)
self.setLayout(self.lay)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
xl = self.rect().center().x()
yl = self.rect().center().y()
self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
self.l = QtCore.QLine(QtCore.QPoint(xl, yl) , QtCore.QPoint(self.width(), self.height()/2))
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setBrush(brush)
painter.setPen(pen)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.e.moveCenter(self.rect().center())
painter.drawEllipse(self.e)
painter.drawLine(self.l)
# painter.drawText('D')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
The Update issue:
I have updated the code, I manage to draw what I want. But having another challenge. You could see on figure below.
When code runs and displays
When Widget minimizes or maximizes, some lines disappears?
The Update code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 700, 600))
self.paint = Paint()
self.sizeHint()
self.lay = QtWidgets.QVBoxLayout()
self.lay.addWidget(self.paint)
self.setLayout(self.lay)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
self.l1 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(200, 0))
self.l2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -200))
self.vl = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -125))
delta = QtCore.QPoint(20, 0)
self.hl = QtCore.QLine(-delta, self.e.topRight() + delta)
self.al = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(-20, -20))
self.a2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(-20, 20))
self.a3 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(20, 20))
self.a4 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(-20, 20))
self._factor = 1.0
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setBrush(brush)
painter.setPen(pen)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.e.moveCenter(self.rect().center())
self.l1.translate(self.rect().center() - self.l1.p1())
self.l2.translate(self.rect().center() - self.l2.p1())
self.al.translate(self.l1.p2())
self.a2.translate(self.l1.p2())
painter.translate(self.rect().center())
painter.scale(self._factor, self._factor)
painter.translate(-self.rect().center())
painter.drawEllipse(self.e)
painter.drawLine(self.l1)
painter.drawLine(self.l2)
fnt = painter.font()
fnt.setPointSize(20)
painter.setFont(fnt)
h = QtGui.QFontMetrics(painter.font()).height()
p = QtCore.QPoint(self.rect().center().x(), self.e.top() - 3*h)
u = QtCore.QPoint(self.e.right() + 3*h, self.rect().center().y())
painter.drawText(p, "y")
painter.drawText(u, "x")
r = QtCore.QRect(QtCore.QPoint(self.e.x(), self.e.bottom()), QtCore.QSize(self.e.width(), h))
painter.drawText(r, QtCore.Qt.AlignCenter, "D = 350mm")
offset = QtCore.QPoint(0, 1.5*h)
self.vl.translate(self.e.bottomLeft() - self.vl.p1() + offset)
painter.drawLine(self.vl)
self.vl.translate(self.e.width(), 0)
painter.drawLine(self.vl)
self.hl.translate(self.e.bottomLeft() + offset - QtCore.QPoint(0, 0.4*h))
self.a3.translate(self.l2.p2())
self.a4.translate(self.l2.p2())
painter.drawLine(self.hl)
painter.drawLine(self.al)
painter.drawLine(self.a2)
painter.drawLine(self.a3)
painter.drawLine(self.a4)
def wheelEvent(self, event):
self._factor *= 1.01**(event.angleDelta().y()/15.0)
self.update()
super(Paint, self).wheelEvent(event)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
I do not know why it behaves like this? any comment or help I appreciate much.
Qt does not know what size the widget will have at the beginning so self.rect() will have any size, for example in my test self.rect() in __init__ returns PyQt5.QtCore.QRect(0, 0, 640, 480) but in paintEvent() it returns PyQt5.QtCore.QRect(0, 0, 678, 578) so that's why the calculation is incorrect.
the solution is to move the line with translate()(do not use translated() because this creates a new QLine and that's what I do not want)
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.setGeometry(QtCore.QRect(200, 100, 700, 600))
self.paint = Paint()
self.sizeHint()
self.lay = QtWidgets.QVBoxLayout()
self.lay.addWidget(self.paint)
self.setLayout(self.lay)
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
self.l1 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(200, 0))
self.l2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -200))
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setBrush(brush)
painter.setPen(pen)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.e.moveCenter(self.rect().center())
self.l1.translate(self.rect().center() - self.l1.p1())
self.l2.translate(self.rect().center() - self.l2.p1())
painter.drawEllipse(self.e)
painter.drawLine(self.l1)
painter.drawLine(self.l2)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Foo()
w.show()
sys.exit(app.exec_())
Update:
class Paint(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Paint, self).__init__(parent)
self.setBackgroundRole(QtGui.QPalette.Base)
self.setAutoFillBackground(True)
self.e = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(250, 250))
self.l1 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(200, 0))
self.l2 = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -200))
self.vl = QtCore.QLine(QtCore.QPoint(), QtCore.QPoint(0, -125))
delta = QtCore.QPoint(20, 0)
self.hl = QtCore.QLine(-delta, self.e.topRight() + delta)
def paintEvent(self, event):
pen = QtGui.QPen()
brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense7Pattern)
painter = QtGui.QPainter(self)
painter.setBrush(brush)
painter.setPen(pen)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.e.moveCenter(self.rect().center())
self.l1.translate(self.rect().center() - self.l1.p1())
self.l2.translate(self.rect().center() - self.l2.p1())
painter.drawEllipse(self.e)
painter.drawLine(self.l1)
painter.drawLine(self.l2)
fnt = painter.font()
fnt.setPointSize(25)
painter.setFont(fnt)
h = QtGui.QFontMetrics(painter.font()).height()
p = QtCore.QPoint(self.rect().center().x(), self.e.bottom() + 0.8*h)
r = QtCore.QRect(QtCore.QPoint(self.e.x(), self.e.bottom()), QtCore.QSize(self.e.width(), h))
painter.drawText(r, QtCore.Qt.AlignCenter, "D")
offset = QtCore.QPoint(0, 1.5*h)
self.vl.translate(self.e.bottomLeft() - self.vl.p1() + offset)
painter.drawLine(self.vl)
self.vl.translate(self.e.width(), 0)
painter.drawLine(self.vl)
self.hl.translate(self.e.bottomLeft() + offset - QtCore.QPoint(0, 20))
painter.drawLine(self.hl)
I am trying to build a arc with some text. I am able to create the arc and I can place the text along with curve. But so far I cant find a way rotate text perpendicular to the curve.
Here is the code I am trying
from __future__ import division
import os
import sys
from PyQt4 import QtGui,QtCore
import math
class PathPaintTest(QtGui.QFrame):
def __init__(self, *args):
super (PathPaintTest, self).__init__(*args)
self.setMaximumSize(250, 110)
self.setMinimumSize(250, 110)
self.setFrameShape(QtGui.QFrame.WinPanel)
self.setFrameShadow(QtGui.QFrame.Sunken)
def paintEvent(self, event):
hw = QtCore.QString("Hello World")
drawWidth = self.width() / 100
painter = QtGui.QPainter(self)
pen = painter.pen()
pen.setWidth(drawWidth)
pen.setColor(QtGui.QColor(QtCore.Qt.red))
painter.setPen(pen)
painter.translate(5,0)
cc1 = QtCore.QPointF(5, -15)
cc2 = QtCore.QPointF(220, -15)
path1 = QtGui.QPainterPath(QtCore.QPointF(5, 140))
path1.cubicTo(cc1, cc2, QtCore.QPointF(240, 140))
painter.drawPath(path1)
pen.setColor(QtGui.QColor(QtCore.Qt.yellow))
painter.setPen(pen)
font = painter.font()
font.setPixelSize(drawWidth * 5)
painter.setFont(font)
percentIncrease = 1 / (hw.size() + 1)
perecent = 0
for i in range(hw.size()):
perecent+=percentIncrease
point = QtCore.QPointF(path1.pointAtPercent(perecent))
painter.drawText(point,QtCore.QString(hw[i]))
QtGui.QFrame.paintEvent(self,event)
class TextTest(QtGui.QWidget):
def __init__(self):
super(TextTest, self).__init__()
self.initUI()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.close()
def initUI(self):
self.mypb = PathPaintTest()
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.mypb)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(hbox)
self.setLayout(vbox)
self.setGeometry(1900, 500, 450, 180)
self.setWindowTitle('Text Test')
def run():
app = QtGui.QApplication(sys.argv)
ex = TextTest()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
run()
But I am trying to achieve something close to this post http://zrusin.blogspot.com/2006/11/text-on-path.html . Like text want to be rotated based on the angle. Any idea how I can do with QPainterPath and QPainter or any other methods ?
I am looking a output like this
Sorry for putting the image link because of less reputation i am unable to add image to my post.
EDIT :
Here is some updated version .. its python version of qt-project.org/faq/answer/how_do_i_make_text_follow_the_line_curve_and_angle_of_the_qpainterpath
from __future__ import division
import os
import sys
from PyQt4 import QtGui,QtCore
import math
class PathPaintTest(QtGui.QFrame):
def __init__(self, *args):
super (PathPaintTest, self).__init__(*args)
self.setMaximumSize(250, 110)
self.setMinimumSize(250, 110)
self.setFrameShape(QtGui.QFrame.WinPanel)
self.setFrameShadow(QtGui.QFrame.Sunken)
def paintEvent(self, event):
hw = QtCore.QString("Hello World")
drawWidth = self.width() / 100
painter = QtGui.QPainter(self)
pen = painter.pen()
pen.setWidth(drawWidth)
pen.setColor(QtGui.QColor(QtCore.Qt.red))
painter.setPen(pen)
painter.translate(5,0)
c1 = QtCore.QPointF(5, -15)
c2 = QtCore.QPointF(220, -15)
path = QtGui.QPainterPath(QtCore.QPointF(5, 140))
path.cubicTo(c1, c2, QtCore.QPointF(240, 140))
painter.drawPath(path)
pen.setColor(QtGui.QColor(QtCore.Qt.green))
painter.setPen(pen)
font = painter.font()
font.setPixelSize(drawWidth * 10)
painter.setFont(font)
perecentIncrease = 1 / (hw.size() + 1)
perecent = 0
for i in range(hw.size()):
perecent+=perecentIncrease
point = QtCore.QPointF(path.pointAtPercent(perecent))
angle = path.angleAtPercent(perecent)
rad = math.radians(angle)
sina = math.sin(rad)
cosa = math.cos(rad)
deltaPenX = cosa * pen.width()
deltaPenY = sina * pen.width()
newX = (cosa * point.x()) - (sina * point.y())
newY = (cosa * point.y()) + (sina * point.x())
deltaX = newX - point.x()
deltaY = newY - point.y()
tran = QtGui.QTransform(cosa,sina,-sina,cosa,-deltaX + deltaPenX,-deltaY - deltaPenY)
painter.setWorldTransform(tran)
painter.drawText(point,QtCore.QString(hw[i]))
QtGui.QFrame.paintEvent(self,event)
class TextTest(QtGui.QWidget):
def __init__(self):
super(TextTest, self).__init__()
self.initUI()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.close()
def initUI(self):
self.mypb = PathPaintTest()
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.mypb)
vbox = QtGui.QVBoxLayout()
vbox.addLayout(hbox)
self.setLayout(vbox)
self.setGeometry(300, 200, 500, 250)
self.setWindowTitle('Text Test')
def run():
app = QtGui.QApplication(sys.argv)
ex = TextTest()
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
run()
but still I cant manage to get the rotation .
UPDATE :
I got it working now here is the updated section, maybe it will help some one else also.
painter.save()
painter.translate(point)
painter.rotate(-angle)
painter.drawText(QtCore.QPoint(0, -pen.width()),QtCore.QString(hw[i]))
painter.restore();
painter.save()
painter.translate(point)
painter.rotate(-angle)
painter.drawText(QtCore.QPoint(0, -pen.width()),QtCore.QString(hw[i]))
painter.restore();
Answering my own question because of the comment and sorry for the late reply :)