examples/widgets/graphicsview/dragdroprobot¶
(You can also check this code in the repository)
<RCC>
<qresource prefix="/" >
<file>images/head.png</file>
</qresource>
</RCC>
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
import sys
import math
from PySide6.QtCore import (QEasingCurve, QLineF, QMimeData, QPoint, QPointF,
QRandomGenerator, QRectF, QTimeLine, Qt)
from PySide6.QtGui import (QBrush, QColor, QDrag, QImage, QPainter, QPixmap,
QPen, QTransform)
from PySide6.QtWidgets import (QApplication, QGraphicsItem,
QGraphicsItemAnimation, QGraphicsScene,
QGraphicsView)
import dragdroprobot_rc # noqa: F401
def random(boundary):
return QRandomGenerator.global_().bounded(boundary)
class ColorItem(QGraphicsItem):
n = 0
def __init__(self):
super().__init__()
self.color = QColor(random(256), random(256), random(256))
(r, g, b) = (self.color.red(), self.color.green(), self.color.blue())
self.setToolTip(
f"QColor({r}, {g}, {b})\nClick and drag this color onto the robot!")
self.setCursor(Qt.OpenHandCursor)
self._start_drag_distance = QApplication.startDragDistance()
def boundingRect(self):
return QRectF(-15.5, -15.5, 34, 34)
def paint(self, painter, option, widget):
painter.setPen(Qt.NoPen)
painter.setBrush(Qt.darkGray)
painter.drawEllipse(-12, -12, 30, 30)
painter.setPen(QPen(Qt.black, 1))
painter.setBrush(QBrush(self.color))
painter.drawEllipse(-15, -15, 30, 30)
def mousePressEvent(self, event):
if event.button() != Qt.LeftButton:
event.ignore()
return
self.setCursor(Qt.ClosedHandCursor)
def mouseMoveEvent(self, event):
start = QPointF(event.buttonDownScreenPos(Qt.LeftButton))
if QLineF(event.screenPos(), start).length() < self._start_drag_distance:
return
drag = QDrag(event.widget())
mime = QMimeData()
drag.setMimeData(mime)
ColorItem.n += 1
if ColorItem.n > 2 and random(3) == 0:
image = QImage(':/images/head.png')
mime.setImageData(image)
drag.setPixmap(QPixmap.fromImage(image).scaled(30, 40))
drag.setHotSpot(QPoint(15, 30))
else:
mime.setColorData(self.color)
(r, g, b) = (self.color.red(), self.color.green(), self.color.blue())
mime.setText(f"#{r:02x}{g:02x}{b:02x}")
pixmap = QPixmap(34, 34)
pixmap.fill(Qt.white)
with QPainter(pixmap) as painter:
painter.translate(15, 15)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
self.paint(painter, None, None)
pixmap.setMask(pixmap.createHeuristicMask())
drag.setPixmap(pixmap)
drag.setHotSpot(QPoint(15, 20))
drag.exec()
self.setCursor(Qt.OpenHandCursor)
def mouseReleaseEvent(self, event):
self.setCursor(Qt.OpenHandCursor)
class RobotPart(QGraphicsItem):
def __init__(self, parent=None):
super().__init__(parent)
self.color = QColor(Qt.lightGray)
self.pixmap = None
self._drag_over = False
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if (event.mimeData().hasColor()
or (isinstance(self, RobotHead) and event.mimeData().hasImage())):
event.setAccepted(True)
self._drag_over = True
self.update()
else:
event.setAccepted(False)
def dragLeaveEvent(self, event):
self._drag_over = False
self.update()
def dropEvent(self, event):
self._drag_over = False
if event.mimeData().hasColor():
self.color = QColor(event.mimeData().colorData())
elif event.mimeData().hasImage():
self.pixmap = QPixmap(event.mimeData().imageData())
self.update()
class RobotHead(RobotPart):
def boundingRect(self):
return QRectF(-15, -50, 30, 50)
def paint(self, painter, option, widget=None):
if not self.pixmap:
painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.RelativeSize)
painter.setBrush(Qt.white)
painter.drawEllipse(-7, -3 - 20, 7, 7)
painter.drawEllipse(0, -3 - 20, 7, 7)
painter.setBrush(Qt.black)
painter.drawEllipse(-5, -1 - 20, 2, 2)
painter.drawEllipse(2, -1 - 20, 2, 2)
painter.setPen(QPen(Qt.black, 2))
painter.setBrush(Qt.NoBrush)
painter.drawArc(-6, -2 - 20, 12, 15, 190 * 16, 160 * 16)
else:
painter.scale(.2272, .2824)
painter.drawPixmap(QPointF(-15 * 4.4, -50 * 3.54), self.pixmap)
class RobotTorso(RobotPart):
def boundingRect(self):
return QRectF(-30, -20, 60, 60)
def paint(self, painter, option, widget=None):
painter.setBrush(self._drag_over and self.color.lighter(130)
or self.color)
painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.RelativeSize)
painter.drawEllipse(-25, -20, 20, 20)
painter.drawEllipse(5, -20, 20, 20)
painter.drawEllipse(-20, 22, 20, 20)
painter.drawEllipse(0, 22, 20, 20)
class RobotLimb(RobotPart):
def boundingRect(self):
return QRectF(-5, -5, 40, 10)
def paint(self, painter, option, widget=None):
painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
painter.drawRoundedRect(self.boundingRect(), 50, 50,
Qt.RelativeSize)
painter.drawEllipse(-5, -5, 10, 10)
class Robot(RobotPart):
def __init__(self):
super().__init__()
self.torsoItem = RobotTorso(self)
self.headItem = RobotHead(self.torsoItem)
self.upperLeftArmItem = RobotLimb(self.torsoItem)
self.lowerLeftArmItem = RobotLimb(self.upperLeftArmItem)
self._upper_right_arm_item = RobotLimb(self.torsoItem)
self._lower_right_arm_item = RobotLimb(self._upper_right_arm_item)
self._upper_right_leg_item = RobotLimb(self.torsoItem)
self._lower_right_leg_item = RobotLimb(self._upper_right_leg_item)
self.upperLeftLegItem = RobotLimb(self.torsoItem)
self.lowerLeftLegItem = RobotLimb(self.upperLeftLegItem)
self.timeline = QTimeLine()
settings = [
# item position rotation at
# x y time 0 / 1
(self.headItem, 0, -18, 20, -20), # noqa: E241
(self.upperLeftArmItem, -15, -10, 190, 180), # noqa: E241
(self.lowerLeftArmItem, 30, 0, 50, 10), # noqa: E241
(self._upper_right_arm_item, 15, -10, 300, 310), # noqa: E241
(self._lower_right_arm_item, 30, 0, 0, -70), # noqa: E241
(self._upper_right_leg_item, 10, 32, 40, 120), # noqa: E241
(self._lower_right_leg_item, 30, 0, 10, 50), # noqa: E241
(self.upperLeftLegItem, -10, 32, 150, 80), # noqa: E241
(self.lowerLeftLegItem, 30, 0, 70, 10), # noqa: E241
(self.torsoItem, 0, 0, 5, -20) # noqa: E241
]
self.animations = []
for item, pos_x, pos_y, rotation1, rotation2 in settings:
item.setPos(pos_x, pos_y)
animation = QGraphicsItemAnimation()
animation.setItem(item)
animation.setTimeLine(self.timeline)
animation.setRotationAt(0, rotation1)
animation.setRotationAt(1, rotation2)
self.animations.append(animation)
self.animations[0].setScaleAt(1, 1.1, 1.1)
self.timeline.setUpdateInterval(1000 / 25)
curve = QEasingCurve(QEasingCurve.SineCurve)
self.timeline.setEasingCurve(curve)
self.timeline.setLoopCount(0)
self.timeline.setDuration(2000)
self.timeline.start()
def boundingRect(self):
return QRectF()
def paint(self, painter, option, widget=None):
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
scene = QGraphicsScene(-200, -200, 400, 400)
for i in range(10):
item = ColorItem()
angle = i * 2.0 * math.pi / 10.0
item.setPos(math.sin(angle) * 150, math.cos(angle) * 150)
scene.addItem(item)
robot = Robot()
robot.setTransform(QTransform().scale(1.2, 1.2))
robot.setPos(0, -20)
scene.addItem(robot)
view = QGraphicsView(scene)
view.setRenderHint(QPainter.RenderHint.Antialiasing)
view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
view.setBackgroundBrush(QColor(230, 200, 167))
view.setWindowTitle("Drag and Drop Robot")
view.show()
sys.exit(app.exec())