PyQt QGraphicsView 矩形外的暗区
PyQt QGraphicsView Dark Area outside rectangle
我需要为 QgraphicsView 创建一个可调整的选择矩形,这是我目前拥有的:
我继承了 QGraphicsRectItem 对象,添加了句柄和鼠标操作,效果很好!我的代码受此启发:
当前工作代码示例:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class GraphicsRectItem(QGraphicsRectItem):
handleTopLeft = 1
handleTopMiddle = 2
handleTopRight = 3
handleMiddleLeft = 4
handleMiddleRight = 5
handleBottomLeft = 6
handleBottomMiddle = 7
handleBottomRight = 8
handleSpace = -4.0
handleSize = 20
handleCursors = {
handleTopLeft: Qt.SizeFDiagCursor,
handleTopMiddle: Qt.SizeVerCursor,
handleTopRight: Qt.SizeBDiagCursor,
handleMiddleLeft: Qt.SizeHorCursor,
handleMiddleRight: Qt.SizeHorCursor,
handleBottomLeft: Qt.SizeBDiagCursor,
handleBottomMiddle: Qt.SizeVerCursor,
handleBottomRight: Qt.SizeFDiagCursor,
}
def __init__(self, *args):
"""
Initialize the shape.
"""
super().__init__(*args)
self.handles = {}
self.handleSelected = None
self.mousePressPos = None
self.mousePressRect = None
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, False)
self.updateHandlesPos()
def handleAt(self, point):
"""
Returns the resize handle below the given point.
"""
for k, v, in self.handles.items():
if v.contains(point):
return k
return None
def hoverMoveEvent(self, moveEvent):
"""
Executed when the mouse moves over the shape (NOT PRESSED).
"""
if self.isSelected():
handle = self.handleAt(moveEvent.pos())
cursor = Qt.ArrowCursor if handle is None else self.handleCursors[handle]
self.setCursor(cursor)
super().hoverMoveEvent(moveEvent)
def hoverLeaveEvent(self, moveEvent):
"""
Executed when the mouse leaves the shape (NOT PRESSED).
"""
self.setCursor(Qt.ArrowCursor)
super().hoverLeaveEvent(moveEvent)
def mousePressEvent(self, mouseEvent):
"""
Executed when the mouse is pressed on the item.
"""
self.handleSelected = self.handleAt(mouseEvent.pos())
if self.handleSelected:
self.mousePressPos = mouseEvent.pos()
self.mousePressRect = self.boundingRect()
super().mousePressEvent(mouseEvent)
def mouseMoveEvent(self, mouseEvent):
"""
Executed when the mouse is being moved over the item while being pressed.
"""
if self.handleSelected is not None:
self.interactiveResize(mouseEvent.pos())
else:
super().mouseMoveEvent(mouseEvent)
def mouseReleaseEvent(self, mouseEvent):
"""
Executed when the mouse is released from the item.
"""
super().mouseReleaseEvent(mouseEvent)
self.handleSelected = None
self.mousePressPos = None
self.mousePressRect = None
self.update()
def boundingRect(self):
"""
Returns the bounding rect of the shape (including the resize handles).
"""
o = self.handleSize + self.handleSpace
return self.rect().adjusted(-o, -o, o, o)
def updateHandlesPos(self):
"""
Update current resize handles according to the shape size and position.
"""
s = self.handleSize
b = self.boundingRect()
self.handles[self.handleTopLeft] = QRectF(b.left(), b.top(), s, s)
self.handles[self.handleTopMiddle] = QRectF(b.center().x() - s / 2, b.top(), s, s)
self.handles[self.handleTopRight] = QRectF(b.right() - s, b.top(), s, s)
self.handles[self.handleMiddleLeft] = QRectF(b.left(), b.center().y() - s / 2, s, s)
self.handles[self.handleMiddleRight] = QRectF(b.right() - s, b.center().y() - s / 2, s, s)
self.handles[self.handleBottomLeft] = QRectF(b.left(), b.bottom() - s, s, s)
self.handles[self.handleBottomMiddle] = QRectF(b.center().x() - s / 2, b.bottom() - s, s, s)
self.handles[self.handleBottomRight] = QRectF(b.right() - s, b.bottom() - s, s, s)
def interactiveResize(self, mousePos):
"""
Perform shape interactive resize.
"""
offset = self.handleSize + self.handleSpace
boundingRect = self.boundingRect()
rect = self.rect()
diff = QPointF(0, 0)
self.prepareGeometryChange()
if self.handleSelected == self.handleTopLeft:
fromX = self.mousePressRect.left()
fromY = self.mousePressRect.top()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setLeft(toX)
boundingRect.setTop(toY)
rect.setLeft(boundingRect.left() + offset)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleTopMiddle:
fromY = self.mousePressRect.top()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setY(toY - fromY)
boundingRect.setTop(toY)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleTopRight:
fromX = self.mousePressRect.right()
fromY = self.mousePressRect.top()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setRight(toX)
boundingRect.setTop(toY)
rect.setRight(boundingRect.right() - offset)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleMiddleLeft:
fromX = self.mousePressRect.left()
toX = fromX + mousePos.x() - self.mousePressPos.x()
diff.setX(toX - fromX)
boundingRect.setLeft(toX)
rect.setLeft(boundingRect.left() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleMiddleRight:
# print("MR")
fromX = self.mousePressRect.right()
toX = fromX + mousePos.x() - self.mousePressPos.x()
diff.setX(toX - fromX)
boundingRect.setRight(toX)
rect.setRight(boundingRect.right() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomLeft:
fromX = self.mousePressRect.left()
fromY = self.mousePressRect.bottom()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setLeft(toX)
boundingRect.setBottom(toY)
rect.setLeft(boundingRect.left() + offset)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomMiddle:
fromY = self.mousePressRect.bottom()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setY(toY - fromY)
boundingRect.setBottom(toY)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomRight:
fromX = self.mousePressRect.right()
fromY = self.mousePressRect.bottom()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setRight(toX)
boundingRect.setBottom(toY)
rect.setRight(boundingRect.right() - offset)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
self.updateHandlesPos()
def shape(self):
"""
Returns the shape of this item as a QPainterPath in local coordinates.
"""
path = QPainterPath()
path.addRect(self.rect())
if self.isSelected():
for shape in self.handles.values():
path.addEllipse(shape)
return path
def paint(self, painter, option, widget=None):
"""
Paint the node in the graphic view.
"""
painter.setBrush(QBrush(QColor(255, 0, 0, 0)))
painter.setPen(QPen(QColor(127, 127, 127), 2.0, Qt.DashLine))
painter.drawRect(self.rect())
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor(81, 168, 220, 200)))
painter.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
for handle, rect in self.handles.items():
if self.handleSelected is None or handle == self.handleSelected:
painter.drawRect(rect)
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
view = QGraphicsView()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 1000, 1000)
view.setScene(scene)
box = GraphicsRectItem(100, 100, 200, 200)
scene.addItem(box)
view.show()
sys.exit(app.exec_())
为了使所选区域更明显,我想使矩形外的区域更暗,这里是一个快速的 Photoshop 预览:
知道如何实现吗?我试图在顶部添加一个半透明的矩形,它可以工作,但是是否可以将我的选择矩形项目用作 "mask"?
谢谢。
在这个答案中,我没有使用您示例的代码,但我已将 this project 从 C ++ 翻译为 Python,因为它的优点是您可以添加调整器而无需修改自定义代码,唯一的要求是该项目必须具有允许我们调整某些功能大小的方法 rect()
和 setRect()
。
因为我看到你想要使用的项目 select 一个 QGraphicsPixmapItem
的内部部分,那么我将创建 CropItem
class 有一个内部将由 SizeGripItem
处理的间隙,如下所示:
import sys
from PyQt5.QtCore import Qt, QRectF
from PyQt5.QtGui import QBrush, QColor, QPen, QPixmap, QPainterPath, QPainter
from PyQt5.QtWidgets import QGraphicsRectItem, QGraphicsItem, QGraphicsPathItem, QApplication, QGraphicsView, QGraphicsScene
class HandleItem(QGraphicsRectItem):
def __init__(self, position_flags, parent):
QGraphicsRectItem.__init__(self, -10, -10, 20, 20, parent)
self._positionFlags = position_flags
self.setBrush(QBrush(QColor(81, 168, 220, 200)))
self.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.setFlag(self.ItemIsMovable)
self.setFlag(self.ItemSendsGeometryChanges)
def positionflags(self):
return self._positionFlags
def itemChange(self, change, value):
retVal = value
if change == self.ItemPositionChange:
retVal = self.restrictPosition(value)
elif change == self.ItemPositionHasChanged:
pos = value
if self.positionflags() == SizeGripItem.TopLeft:
self.parentItem().setTopLeft(pos)
elif self.positionflags() == SizeGripItem.Top:
self.parentItem().setTop(pos.y())
elif self.positionflags() == SizeGripItem.TopRight:
self.parentItem().setTopRight(pos)
elif self.positionflags() == SizeGripItem.Right:
self.parentItem().setRight(pos.x())
elif self.positionflags() == SizeGripItem.BottomRight:
self.parentItem().setBottomRight(pos)
elif self.positionflags() == SizeGripItem.Bottom:
self.parentItem().setBottom(pos.y())
elif self.positionflags() == SizeGripItem.BottomLeft:
self.parentItem().setBottomLeft(pos)
elif self.positionflags() == SizeGripItem.Left:
self.parentItem().setLeft(pos.x())
return retVal
def restrictPosition(self, newPos):
retVal = self.pos()
if self.positionflags() & SizeGripItem.Top or self.positionflags() & SizeGripItem.Bottom:
retVal.setY(newPos.y())
if self.positionflags() & SizeGripItem.Left or self.positionflags() & SizeGripItem.Right:
retVal.setX(newPos.x())
if self.positionflags() & SizeGripItem.Top and retVal.y() > self.parentItem()._rect.bottom():
retVal.setY(self.parentItem()._rect.bottom())
elif self.positionflags() & SizeGripItem.Bottom and retVal.y() < self.parentItem()._rect.top():
retVal.setY(self.parentItem()._rect.top())
if self.positionflags() & SizeGripItem.Left and retVal.x() > self.parentItem()._rect.right():
retVal.setX(self.parentItem()._rect.right())
elif self.positionflags() & SizeGripItem.Right and retVal.x() < self.parentItem()._rect.left():
retVal.setX(self.parentItem()._rect.left())
return retVal
class SizeGripItem(QGraphicsItem):
Top = 0x01
Bottom = 0x1 << 1
Left = 0x1 << 2
Right = 0x1 << 3
TopLeft = Top | Left
BottomLeft = Bottom | Left
TopRight = Top | Right
BottomRight = Bottom | Right
handleCursors = {
TopLeft: Qt.SizeFDiagCursor,
Top: Qt.SizeVerCursor,
TopRight: Qt.SizeBDiagCursor,
Left: Qt.SizeHorCursor,
Right: Qt.SizeHorCursor,
BottomLeft: Qt.SizeBDiagCursor,
Bottom: Qt.SizeVerCursor,
BottomRight: Qt.SizeFDiagCursor,
}
def __init__(self, parent):
QGraphicsItem.__init__(self, parent)
self._handleItems = []
self._rect = QRectF(0, 0, 0, 0)
if self.parentItem():
self._rect = self.parentItem().rect()
for flag in (self.TopLeft, self.Top, self.TopRight, self.Right,
self.BottomRight, self.Bottom, self.BottomLeft, self.Left):
handle = HandleItem(flag, self)
handle.setCursor(self.handleCursors[flag])
self._handleItems.append(handle)
self.updateHandleItemPositions()
def boundingRect(self):
if self.parentItem():
return self._rect
else:
return QRectF(0, 0, 0, 0)
def paint(self, painter, option, widget):
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(QPen(QColor(127, 127, 127), 2.0, Qt.DashLine))
painter.drawRect(self._rect)
def doResize(self):
self.parentItem().setRect(self._rect)
self.updateHandleItemPositions()
def updateHandleItemPositions(self):
for item in self._handleItems:
item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False)
if item.positionflags() == self.TopLeft:
item.setPos(self._rect.topLeft())
elif item.positionflags() == self.Top:
item.setPos(self._rect.left() + self._rect.width() / 2 - 1,
self._rect.top())
elif item.positionflags() == self.TopRight:
item.setPos(self._rect.topRight())
elif item.positionflags() == self.Right:
item.setPos(self._rect.right(),
self._rect.top() + self._rect.height() / 2 - 1)
elif item.positionflags() == self.BottomRight:
item.setPos(self._rect.bottomRight())
elif item.positionflags() == self.Bottom:
item.setPos(self._rect.left() + self._rect.width() / 2 - 1,
self._rect.bottom())
elif item.positionflags() == self.BottomLeft:
item.setPos(self._rect.bottomLeft())
elif item.positionflags() == self.Left:
item.setPos(self._rect.left(),
self._rect.top() + self._rect.height() / 2 - 1)
item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
def setTop(self, v):
self._rect.setTop(v)
self.doResize()
def setRight(self, v):
self._rect.setRight(v)
self.doResize()
def setBottom(self, v):
self._rect.setBottom(v)
self.doResize()
def setLeft(self, v):
self._rect.setLeft(v)
self.doResize()
def setTopLeft(self, v):
self._rect.setTopLeft(v)
self.doResize()
def setTopRight(self, v):
self._rect.setTopRight(v)
self.doResize()
def setBottomRight(self, v):
self._rect.setBottomRight(v)
self.doResize()
def setBottomLeft(self, v):
self._rect.setBottomLeft(v)
self.doResize()
class CropItem(QGraphicsPathItem):
def __init__(self, parent):
QGraphicsPathItem.__init__(self, parent)
self.extern_rect = parent.boundingRect()
self.intern_rect = QRectF(0, 0, self.extern_rect.width()/2, self.extern_rect.height()/2)
self.intern_rect.moveCenter(self.extern_rect.center())
self.setBrush(QColor(10, 100, 100, 100))
self.setPen(QPen(Qt.NoPen))
SizeGripItem(self)
self.create_path()
def create_path(self):
self._path = QPainterPath()
self._path.addRect(self.extern_rect)
self._path.moveTo(self.intern_rect.topLeft())
self._path.addRect(self.intern_rect)
self.setPath(self._path)
def rect(self):
return self.intern_rect
def setRect(self, rect):
self._intern = rect
self.create_path()
if __name__ == '__main__':
app = QApplication(sys.argv)
view = QGraphicsView()
scene = QGraphicsScene()
view.setScene(scene)
pixmapItem = scene.addPixmap(QPixmap("Lenna.png"))
cropItem = CropItem(pixmapItem)
view.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
view.show()
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setFixedSize(view.size())
sys.exit(app.exec_())
我需要为 QgraphicsView 创建一个可调整的选择矩形,这是我目前拥有的:
我继承了 QGraphicsRectItem 对象,添加了句柄和鼠标操作,效果很好!我的代码受此启发:
当前工作代码示例:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class GraphicsRectItem(QGraphicsRectItem):
handleTopLeft = 1
handleTopMiddle = 2
handleTopRight = 3
handleMiddleLeft = 4
handleMiddleRight = 5
handleBottomLeft = 6
handleBottomMiddle = 7
handleBottomRight = 8
handleSpace = -4.0
handleSize = 20
handleCursors = {
handleTopLeft: Qt.SizeFDiagCursor,
handleTopMiddle: Qt.SizeVerCursor,
handleTopRight: Qt.SizeBDiagCursor,
handleMiddleLeft: Qt.SizeHorCursor,
handleMiddleRight: Qt.SizeHorCursor,
handleBottomLeft: Qt.SizeBDiagCursor,
handleBottomMiddle: Qt.SizeVerCursor,
handleBottomRight: Qt.SizeFDiagCursor,
}
def __init__(self, *args):
"""
Initialize the shape.
"""
super().__init__(*args)
self.handles = {}
self.handleSelected = None
self.mousePressPos = None
self.mousePressRect = None
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, False)
self.updateHandlesPos()
def handleAt(self, point):
"""
Returns the resize handle below the given point.
"""
for k, v, in self.handles.items():
if v.contains(point):
return k
return None
def hoverMoveEvent(self, moveEvent):
"""
Executed when the mouse moves over the shape (NOT PRESSED).
"""
if self.isSelected():
handle = self.handleAt(moveEvent.pos())
cursor = Qt.ArrowCursor if handle is None else self.handleCursors[handle]
self.setCursor(cursor)
super().hoverMoveEvent(moveEvent)
def hoverLeaveEvent(self, moveEvent):
"""
Executed when the mouse leaves the shape (NOT PRESSED).
"""
self.setCursor(Qt.ArrowCursor)
super().hoverLeaveEvent(moveEvent)
def mousePressEvent(self, mouseEvent):
"""
Executed when the mouse is pressed on the item.
"""
self.handleSelected = self.handleAt(mouseEvent.pos())
if self.handleSelected:
self.mousePressPos = mouseEvent.pos()
self.mousePressRect = self.boundingRect()
super().mousePressEvent(mouseEvent)
def mouseMoveEvent(self, mouseEvent):
"""
Executed when the mouse is being moved over the item while being pressed.
"""
if self.handleSelected is not None:
self.interactiveResize(mouseEvent.pos())
else:
super().mouseMoveEvent(mouseEvent)
def mouseReleaseEvent(self, mouseEvent):
"""
Executed when the mouse is released from the item.
"""
super().mouseReleaseEvent(mouseEvent)
self.handleSelected = None
self.mousePressPos = None
self.mousePressRect = None
self.update()
def boundingRect(self):
"""
Returns the bounding rect of the shape (including the resize handles).
"""
o = self.handleSize + self.handleSpace
return self.rect().adjusted(-o, -o, o, o)
def updateHandlesPos(self):
"""
Update current resize handles according to the shape size and position.
"""
s = self.handleSize
b = self.boundingRect()
self.handles[self.handleTopLeft] = QRectF(b.left(), b.top(), s, s)
self.handles[self.handleTopMiddle] = QRectF(b.center().x() - s / 2, b.top(), s, s)
self.handles[self.handleTopRight] = QRectF(b.right() - s, b.top(), s, s)
self.handles[self.handleMiddleLeft] = QRectF(b.left(), b.center().y() - s / 2, s, s)
self.handles[self.handleMiddleRight] = QRectF(b.right() - s, b.center().y() - s / 2, s, s)
self.handles[self.handleBottomLeft] = QRectF(b.left(), b.bottom() - s, s, s)
self.handles[self.handleBottomMiddle] = QRectF(b.center().x() - s / 2, b.bottom() - s, s, s)
self.handles[self.handleBottomRight] = QRectF(b.right() - s, b.bottom() - s, s, s)
def interactiveResize(self, mousePos):
"""
Perform shape interactive resize.
"""
offset = self.handleSize + self.handleSpace
boundingRect = self.boundingRect()
rect = self.rect()
diff = QPointF(0, 0)
self.prepareGeometryChange()
if self.handleSelected == self.handleTopLeft:
fromX = self.mousePressRect.left()
fromY = self.mousePressRect.top()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setLeft(toX)
boundingRect.setTop(toY)
rect.setLeft(boundingRect.left() + offset)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleTopMiddle:
fromY = self.mousePressRect.top()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setY(toY - fromY)
boundingRect.setTop(toY)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleTopRight:
fromX = self.mousePressRect.right()
fromY = self.mousePressRect.top()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setRight(toX)
boundingRect.setTop(toY)
rect.setRight(boundingRect.right() - offset)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleMiddleLeft:
fromX = self.mousePressRect.left()
toX = fromX + mousePos.x() - self.mousePressPos.x()
diff.setX(toX - fromX)
boundingRect.setLeft(toX)
rect.setLeft(boundingRect.left() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleMiddleRight:
# print("MR")
fromX = self.mousePressRect.right()
toX = fromX + mousePos.x() - self.mousePressPos.x()
diff.setX(toX - fromX)
boundingRect.setRight(toX)
rect.setRight(boundingRect.right() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomLeft:
fromX = self.mousePressRect.left()
fromY = self.mousePressRect.bottom()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setLeft(toX)
boundingRect.setBottom(toY)
rect.setLeft(boundingRect.left() + offset)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomMiddle:
fromY = self.mousePressRect.bottom()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setY(toY - fromY)
boundingRect.setBottom(toY)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomRight:
fromX = self.mousePressRect.right()
fromY = self.mousePressRect.bottom()
toX = fromX + mousePos.x() - self.mousePressPos.x()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setRight(toX)
boundingRect.setBottom(toY)
rect.setRight(boundingRect.right() - offset)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
self.updateHandlesPos()
def shape(self):
"""
Returns the shape of this item as a QPainterPath in local coordinates.
"""
path = QPainterPath()
path.addRect(self.rect())
if self.isSelected():
for shape in self.handles.values():
path.addEllipse(shape)
return path
def paint(self, painter, option, widget=None):
"""
Paint the node in the graphic view.
"""
painter.setBrush(QBrush(QColor(255, 0, 0, 0)))
painter.setPen(QPen(QColor(127, 127, 127), 2.0, Qt.DashLine))
painter.drawRect(self.rect())
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor(81, 168, 220, 200)))
painter.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
for handle, rect in self.handles.items():
if self.handleSelected is None or handle == self.handleSelected:
painter.drawRect(rect)
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
view = QGraphicsView()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 1000, 1000)
view.setScene(scene)
box = GraphicsRectItem(100, 100, 200, 200)
scene.addItem(box)
view.show()
sys.exit(app.exec_())
为了使所选区域更明显,我想使矩形外的区域更暗,这里是一个快速的 Photoshop 预览:
知道如何实现吗?我试图在顶部添加一个半透明的矩形,它可以工作,但是是否可以将我的选择矩形项目用作 "mask"?
谢谢。
在这个答案中,我没有使用您示例的代码,但我已将 this project 从 C ++ 翻译为 Python,因为它的优点是您可以添加调整器而无需修改自定义代码,唯一的要求是该项目必须具有允许我们调整某些功能大小的方法 rect()
和 setRect()
。
因为我看到你想要使用的项目 select 一个 QGraphicsPixmapItem
的内部部分,那么我将创建 CropItem
class 有一个内部将由 SizeGripItem
处理的间隙,如下所示:
import sys
from PyQt5.QtCore import Qt, QRectF
from PyQt5.QtGui import QBrush, QColor, QPen, QPixmap, QPainterPath, QPainter
from PyQt5.QtWidgets import QGraphicsRectItem, QGraphicsItem, QGraphicsPathItem, QApplication, QGraphicsView, QGraphicsScene
class HandleItem(QGraphicsRectItem):
def __init__(self, position_flags, parent):
QGraphicsRectItem.__init__(self, -10, -10, 20, 20, parent)
self._positionFlags = position_flags
self.setBrush(QBrush(QColor(81, 168, 220, 200)))
self.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.setFlag(self.ItemIsMovable)
self.setFlag(self.ItemSendsGeometryChanges)
def positionflags(self):
return self._positionFlags
def itemChange(self, change, value):
retVal = value
if change == self.ItemPositionChange:
retVal = self.restrictPosition(value)
elif change == self.ItemPositionHasChanged:
pos = value
if self.positionflags() == SizeGripItem.TopLeft:
self.parentItem().setTopLeft(pos)
elif self.positionflags() == SizeGripItem.Top:
self.parentItem().setTop(pos.y())
elif self.positionflags() == SizeGripItem.TopRight:
self.parentItem().setTopRight(pos)
elif self.positionflags() == SizeGripItem.Right:
self.parentItem().setRight(pos.x())
elif self.positionflags() == SizeGripItem.BottomRight:
self.parentItem().setBottomRight(pos)
elif self.positionflags() == SizeGripItem.Bottom:
self.parentItem().setBottom(pos.y())
elif self.positionflags() == SizeGripItem.BottomLeft:
self.parentItem().setBottomLeft(pos)
elif self.positionflags() == SizeGripItem.Left:
self.parentItem().setLeft(pos.x())
return retVal
def restrictPosition(self, newPos):
retVal = self.pos()
if self.positionflags() & SizeGripItem.Top or self.positionflags() & SizeGripItem.Bottom:
retVal.setY(newPos.y())
if self.positionflags() & SizeGripItem.Left or self.positionflags() & SizeGripItem.Right:
retVal.setX(newPos.x())
if self.positionflags() & SizeGripItem.Top and retVal.y() > self.parentItem()._rect.bottom():
retVal.setY(self.parentItem()._rect.bottom())
elif self.positionflags() & SizeGripItem.Bottom and retVal.y() < self.parentItem()._rect.top():
retVal.setY(self.parentItem()._rect.top())
if self.positionflags() & SizeGripItem.Left and retVal.x() > self.parentItem()._rect.right():
retVal.setX(self.parentItem()._rect.right())
elif self.positionflags() & SizeGripItem.Right and retVal.x() < self.parentItem()._rect.left():
retVal.setX(self.parentItem()._rect.left())
return retVal
class SizeGripItem(QGraphicsItem):
Top = 0x01
Bottom = 0x1 << 1
Left = 0x1 << 2
Right = 0x1 << 3
TopLeft = Top | Left
BottomLeft = Bottom | Left
TopRight = Top | Right
BottomRight = Bottom | Right
handleCursors = {
TopLeft: Qt.SizeFDiagCursor,
Top: Qt.SizeVerCursor,
TopRight: Qt.SizeBDiagCursor,
Left: Qt.SizeHorCursor,
Right: Qt.SizeHorCursor,
BottomLeft: Qt.SizeBDiagCursor,
Bottom: Qt.SizeVerCursor,
BottomRight: Qt.SizeFDiagCursor,
}
def __init__(self, parent):
QGraphicsItem.__init__(self, parent)
self._handleItems = []
self._rect = QRectF(0, 0, 0, 0)
if self.parentItem():
self._rect = self.parentItem().rect()
for flag in (self.TopLeft, self.Top, self.TopRight, self.Right,
self.BottomRight, self.Bottom, self.BottomLeft, self.Left):
handle = HandleItem(flag, self)
handle.setCursor(self.handleCursors[flag])
self._handleItems.append(handle)
self.updateHandleItemPositions()
def boundingRect(self):
if self.parentItem():
return self._rect
else:
return QRectF(0, 0, 0, 0)
def paint(self, painter, option, widget):
painter.setRenderHint(QPainter.Antialiasing)
painter.setPen(QPen(QColor(127, 127, 127), 2.0, Qt.DashLine))
painter.drawRect(self._rect)
def doResize(self):
self.parentItem().setRect(self._rect)
self.updateHandleItemPositions()
def updateHandleItemPositions(self):
for item in self._handleItems:
item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False)
if item.positionflags() == self.TopLeft:
item.setPos(self._rect.topLeft())
elif item.positionflags() == self.Top:
item.setPos(self._rect.left() + self._rect.width() / 2 - 1,
self._rect.top())
elif item.positionflags() == self.TopRight:
item.setPos(self._rect.topRight())
elif item.positionflags() == self.Right:
item.setPos(self._rect.right(),
self._rect.top() + self._rect.height() / 2 - 1)
elif item.positionflags() == self.BottomRight:
item.setPos(self._rect.bottomRight())
elif item.positionflags() == self.Bottom:
item.setPos(self._rect.left() + self._rect.width() / 2 - 1,
self._rect.bottom())
elif item.positionflags() == self.BottomLeft:
item.setPos(self._rect.bottomLeft())
elif item.positionflags() == self.Left:
item.setPos(self._rect.left(),
self._rect.top() + self._rect.height() / 2 - 1)
item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
def setTop(self, v):
self._rect.setTop(v)
self.doResize()
def setRight(self, v):
self._rect.setRight(v)
self.doResize()
def setBottom(self, v):
self._rect.setBottom(v)
self.doResize()
def setLeft(self, v):
self._rect.setLeft(v)
self.doResize()
def setTopLeft(self, v):
self._rect.setTopLeft(v)
self.doResize()
def setTopRight(self, v):
self._rect.setTopRight(v)
self.doResize()
def setBottomRight(self, v):
self._rect.setBottomRight(v)
self.doResize()
def setBottomLeft(self, v):
self._rect.setBottomLeft(v)
self.doResize()
class CropItem(QGraphicsPathItem):
def __init__(self, parent):
QGraphicsPathItem.__init__(self, parent)
self.extern_rect = parent.boundingRect()
self.intern_rect = QRectF(0, 0, self.extern_rect.width()/2, self.extern_rect.height()/2)
self.intern_rect.moveCenter(self.extern_rect.center())
self.setBrush(QColor(10, 100, 100, 100))
self.setPen(QPen(Qt.NoPen))
SizeGripItem(self)
self.create_path()
def create_path(self):
self._path = QPainterPath()
self._path.addRect(self.extern_rect)
self._path.moveTo(self.intern_rect.topLeft())
self._path.addRect(self.intern_rect)
self.setPath(self._path)
def rect(self):
return self.intern_rect
def setRect(self, rect):
self._intern = rect
self.create_path()
if __name__ == '__main__':
app = QApplication(sys.argv)
view = QGraphicsView()
scene = QGraphicsScene()
view.setScene(scene)
pixmapItem = scene.addPixmap(QPixmap("Lenna.png"))
cropItem = CropItem(pixmapItem)
view.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
view.show()
view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
view.setFixedSize(view.size())
sys.exit(app.exec_())