У меня QGraphicsSvgItem на QGraphicsScene . Я пытаюсь изменить его размер с помощью элементов захвата (строки), которые расположены в ограничивающем прямоугольнике QGraphicsSvgItem . В файле test.svg
я установил для свойства векторного эффекта значение non-scaling-stroke.
когда я изменяю его размер, растягивая элемент захвата, он создает пустое пространство между ограничивающим прямоугольником и контуром изображения svg. Вот файл test.svg, см. Стиль последнего тега <g>
.
ниже код
import sys
from PyQt5.QtSvg import QGraphicsSvgItem, QSvgRenderer
from PyQt5.QtWidgets import QLineEdit, QGraphicsItem, QApplication, QMainWindow, QGraphicsScene, QGraphicsView, \
QGraphicsPathItem
from PyQt5.QtGui import QPen, QColor, QCursor, QPainterPath
from PyQt5.QtCore import Qt, QRectF, QPointF
class SizeGripItem(QGraphicsPathItem):
def __init__(self, annotation_item, index, direction=Qt.Horizontal, parent=None):
super(QGraphicsPathItem, self).__init__(parent=parent)
self.width = self.height = 0
if direction is Qt.Horizontal:
self.height = annotation_item.boundingRect().height()
else:
self.width = annotation_item.boundingRect().width()
path = QPainterPath()
path.addRect(QRectF(0, 0, self.width, self.height))
self.m_annotation_item = annotation_item
self._direction = direction
self.m_index = index
self.setPath(path)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setPen(QPen(QColor("black"), 2))
self.setBrush(QColor("red"))
self.setZValue(2)
@property
def direction(self):
return self._direction
def update_path(self):
if self._direction is Qt.Horizontal:
self.height = self.m_annotation_item.boundingRect().height()
else:
self.width = self.m_annotation_item.boundingRect().width()
path = QPainterPath()
path.addRect(QRectF(0, 0, self.width, self.height))
self.setPath(path)
def update_position(self):
"""updates grip items
"""
self.update_path()
pos = self.m_annotation_item.mapToScene(self.point(self.m_index))
x = self.m_annotation_item.boundingRect().x()
y = self.m_annotation_item.boundingRect().y()
pos.setX(pos.x() + x)
pos.setY(pos.y() + y)
self.setEnabled(False)
self.setPos(pos)
self.setEnabled(True)
def point(self, index):
"""
yields a list of positions of grip items in a node item
"""
x = self.m_annotation_item.boundingRect().width()
y = self.m_annotation_item.boundingRect().height()
if 0 <= index < 4:
return [
QPointF(0, 0),
QPointF(0, 0),
QPointF(0, y),
QPointF(x, 0)
][index]
def hoverEnterEvent(self, event):
if self._direction == Qt.Horizontal:
self.setCursor(QCursor(Qt.SizeHorCursor))
else:
self.setCursor(QCursor(Qt.SizeVerCursor))
super(SizeGripItem, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setCursor(QCursor(Qt.ArrowCursor))
super(SizeGripItem, self).hoverLeaveEvent(event)
def itemChange(self, change, value):
"""
Moves position of grip item on resize or reference circle's position change
"""
if change == QGraphicsItem.ItemPositionChange and self.isEnabled():
p = QPointF(self.pos())
if self.direction == Qt.Horizontal:
p.setX(value.x())
elif self.direction == Qt.Vertical:
p.setY(value.y())
self.m_annotation_item.resize(self.m_index, p)
return p
return super(SizeGripItem, self).itemChange(change, value)
class NodeItem(QGraphicsSvgItem):
def __init__(self, parent=None):
QGraphicsSvgItem.__init__(self, parent)
self.m_renderer = QSvgRenderer("test.svg")
self.setSharedRenderer(self.m_renderer)
self.rect = QRectF(0, 0, 150, 300)
self.setFlags(QGraphicsSvgItem.ItemIsMovable |
QGraphicsSvgItem.ItemIsSelectable |
QGraphicsSvgItem.ItemSendsGeometryChanges)
self.lineGripItems = []
self.sizeGripItems = []
def boundingRect(self):
return self.rect
def paint(self, painter, option, widget):
self.m_renderer.render(painter, self.boundingRect())
def resize(self, i, p):
"""Move grip item with changing rect of node item
"""
x = self.boundingRect().x()
y = self.boundingRect().y()
width = self.boundingRect().width()
height = self.boundingRect().height()
p_new = self.sizeGripItems[i].pos()
self.prepareGeometryChange()
if i == 0 or i == 1:
self.rect = QRectF(x + p.x() - p_new.x(), y + p.y() - p_new.y(), width - p.x() + p_new.x(),
height - p.y() + p_new.y())
if i == 2 or i == 3:
self.rect = QRectF(x, y, width + p.x() - p_new.x(), height + p.y() - p_new.y())
self.updateSizeGripItem([i])
def addGripItem(self):
"""adds grip items
"""
if self.scene() and not self.lineGripItems:
for i, (direction) in enumerate(
(
Qt.Vertical,
Qt.Horizontal,
Qt.Vertical,
Qt.Horizontal,
)
):
item = SizeGripItem(self, i, direction)
self.scene().addItem(item)
self.sizeGripItems.append(item)
def updateSizeGripItem(self, index_no_updates=None):
"""updates grip items
"""
index_no_updates = index_no_updates or []
for i, item in zip(range(len(self.sizeGripItems)), self.sizeGripItems):
if i not in index_no_updates:
item.update_position()
def itemChange(self, change, value):
"""Overloads and extends QGraphicsSvgItem to also update gripitem
"""
if change == QGraphicsItem.ItemPositionHasChanged:
self.updateSizeGripItem()
return
if change == QGraphicsItem.ItemSceneHasChanged:
self.addGripItem()
self.updateSizeGripItem()
return
return super(NodeItem, self).itemChange(change, value)
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(NodeItem())
sys.exit(app.exec_())
test.svg
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="29.681765mm"
height="50.584301mm"
viewBox="0 0 29.681765 50.584301"
version="1.1"
id="svg1553"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Column.svg">
<defs
id="defs1547" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="-53.758603"
inkscape:cy="95.455126"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1304"
inkscape:window-height="745"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata1550">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-144.63496,-116.2188)">
<g
id="use23287"
transform="matrix(1.9494464,0,0,1.7251648,-371.93598,227.29603)">
<g
id="g1584"
transform="matrix(0.68957937,0,0,-0.68957937,351.75302,3137.4373)">
<desc
id="desc1570">Column</desc>
<title
id="title1572">Column</title>
<path
d="m -125.4754,4606.3318 c 0.938,-3.094 5.438,-5.344 10.684,-5.344 5.25,0 9.746,2.25 10.687,5.344 v 31.129 c -0.941,3.094 -5.437,5.34 -10.687,5.34 -5.246,0 -9.746,-2.246 -10.684,-5.34 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;vector-effect:non-scaling-stroke"
id="path1574"
inkscape:connector-curvature="0" />
<g
id="g1578"
transform="matrix(1,0,0,-1,-164.4244,6574.5584)">
<path
d="m 38.949,1968.227 c 0.938,3.093 5.438,5.343 10.684,5.343 5.25,0 9.746,-2.25 10.687,-5.343 v -31.129 c -0.941,-3.094 -5.437,-5.34 -10.687,-5.34 -5.246,0 -9.746,2.246 -10.684,5.34 z m 0,0 H 60.32 M 38.949,1937.098 H 60.32"
style="vector-effect:non-scaling-stroke;stroke-width:0.70875001;fill:none;stroke:#000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1576"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
</svg>
в пи c i потоковое изображение элемента с помощью элемента захвата (показано вертикальной линией слева), затем между элементом захвата и границей изображения создается зазор.