在下图中,我有 QGraphicsPathItem
场景中的红色部分,并覆盖它的形状为蓝色部分。 我希望当红色空间被拖动并移动时,项目被线性延长或缩短,而当蓝色空间被拖动时,整个项目必须被移动。
import sys
from PyQt5.QtCore import QRectF, Qt, QPointF
from PyQt5.QtGui import QPainterPath, QPen, QPainterPathStroker, QPainter
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsPathItem, QGraphicsItem
class Item(QGraphicsPathItem):
circle = QPainterPath()
circle.addEllipse(QRectF(-5, -5, 10, 10))
def __init__(self):
super(Item, self).__init__()
self.setPath(Item.circle)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
def paint(self, painter, option, widget):
color = Qt.red if self.isSelected() else Qt.black
painter.setPen(QPen(color, 2, Qt.SolidLine))
painter.drawPath(self.path())
# To paint path of shape
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))
painter.drawPath(self.shape())
def shape(self):
startPoint = self.mapFromScene(self.pos())
endPoint = self.mapFromScene(QPointF(10, 10))
path = QPainterPath(startPoint)
path.lineTo(endPoint)
stroke = QPainterPathStroker()
stroke.setWidth(10)
return stroke.createStroke(path)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QMainWindow()
window.show()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 200, 200)
view = QGraphicsView()
view.setScene(scene)
window.setCentralWidget(view)
scene.addItem(Item())
sys.exit(app.exec_())
在同一个项目中处理调整大小和拉伸的任务很复杂,所以为了避免这种情况,我使用了2个项目。一个手柄和一个管子. 因此,每个项目都管理自己的任务,并更新其他元素的位置。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class HandleItem(QtWidgets.QGraphicsPathItem):
def __init__(self, parent=None):
super().__init__(parent)
path = QtGui.QPainterPath()
path.addEllipse(QtCore.QRectF(-5, -5, 10, 10))
self.setPath(path)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self._pipe_item = None
@property
def pipe_item(self):
return self._pipe_item
@pipe_item.setter
def pipe_item(self, item):
self._pipe_item = item
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
ip = self.pipe_item.mapFromScene(value)
self.pipe_item.end_pos = ip
elif change == QtWidgets.QGraphicsItem.ItemSelectedChange:
color = QtCore.Qt.red if value else QtCore.Qt.black
self.setPen(QtGui.QPen(color, 2, QtCore.Qt.SolidLine))
return super().itemChange(change, value)
class PipeItem(QtWidgets.QGraphicsPathItem):
def __init__(self, parent=None):
super().__init__(parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self._end_pos = QtCore.QPointF()
self._handle = HandleItem()
self.handle.pipe_item = self
self.end_pos = QtCore.QPointF(10, 10)
self.handle.setPos(self.end_pos)
self.setPen(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine))
@property
def handle(self):
return self._handle
@property
def end_pos(self):
return self._end_pos
@end_pos.setter
def end_pos(self, p):
path = QtGui.QPainterPath()
path.lineTo(p)
stroke = QtGui.QPainterPathStroker()
stroke.setWidth(10)
self.setPath(stroke.createStroke(path))
self._end_pos = p
def paint(self, painter, option, widget):
option.state &= ~QtWidgets.QStyle.State_Selected
super().paint(painter, option, widget)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemSceneHasChanged:
if self.scene():
self.scene().addItem(self.handle)
elif change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
p = self.mapToScene(self.end_pos)
self.handle.setPos(p)
return super().itemChange(change, value)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
scene = QtWidgets.QGraphicsScene(sceneRect=QtCore.QRectF(0, 0, 200, 200))
item = PipeItem()
scene.addItem(item)
view = QtWidgets.QGraphicsView(scene)
window = QtWidgets.QMainWindow()
window.setCentralWidget(view)
window.resize(640, 480)
window.show()
sys.exit(app.exec_())
UPDATE:
如果你想要实现你想要的逻辑,那就更复杂了。错误的原因是paint()方法使用boundingRect()来设置油漆区域,但在你的情况下,它没有考虑到它的变化,一个可能的解决方案是以下。
class Item(QGraphicsPathItem):
circle = QPainterPath()
circle.addEllipse(QRectF(-5, -5, 10, 10))
# ...
def boundingRect(self):
return self.shape().boundingRect()