自定义2D图形

参考

https://www.pythonguis.com/tutorials/pyqt-qgraphics-vector-graphics/

https://doc.qt.io/qt-6/graphicsview.html

https://doc.qt.io/qt-6/qgraphicslinearlayout.html

https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QGraphicsItem.html#PySide6.QtWidgets.PySide6.QtWidgets.QGraphicsItem.paint https://doc.qt.io/qtforpython-5/PySide6/QtWidgets/QGraphicsItem.html#PySide6.QtWidgets.PySide6.QtWidgets.QGraphicsItem.paint

https://doc.qt.io/qt-6/qpainterpath.html

创建

from PySide6 import QtWidgets,QtGui
from PySide6.QtCore import Qt

app = QtWidgets.QApplication()

# Defining a scene rect of 400x200, with it's origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QtWidgets.QGraphicsScene(0, 0, 400, 200)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度


# Draw a rectangle item, setting the dimensions.
rect = QtWidgets.QGraphicsRectItem(0, 0, 200, 50)
rect.setBrush(brush)
rect.setPen(pen)
# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)
# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(rect)


ellipse = scene.addEllipse(0, 0, 100, 100, pen, QtGui.QBrush(Qt.blue))
ellipse.setPos(75, 30)
ellipse.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)


view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

自定义

from PySide6 import QtWidgets,QtGui
from PySide6.QtCore import Qt, QRectF



class SimpleItem(QtWidgets.QGraphicsItem):

    def boundingRect(self):
        penWidth = 1.0
        return QRectF(0 , 0,
                      40, 120)

    def paint(self, painter, option, widget):
        painter.setBrush(QtGui.QBrush(Qt.red))
        painter.drawRect(5, 0, 20, 10)
        painter.setBrush(QtGui.QBrush(Qt.green))
        painter.drawRect(5, 20, 20, 10)
        painter.drawRect(5, 40, 20, 10)
        painter.drawText(0, 75, '水箱')

app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 400, 200)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度


node = SimpleItem()
node.setPos(75, 80)
scene.addItem(node)
node.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)


line = scene.addLine(15, 10, 100, 10, QtGui.QPen(Qt.blue, 8))
line.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)

view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

resize

参考 https://stackoverflow.com/q/34429632


成功示例

下面的代码定义可调整大小的管道,并且有键盘微调位置功能

import sys

from PySide6.QtCore import Qt, QRectF, QPointF
from PySide6.QtGui import QBrush, QPainterPath, QPainter, QColor, QPen, QPixmap
from PySide6.QtWidgets import QGraphicsRectItem, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem


class GraphicsRectItem(QGraphicsRectItem):

    handleTopLeft = 1
    handleTopMiddle = 2
    handleTopRight = 3
    handleMiddleLeft = 4
    handleMiddleRight = 5
    handleBottomLeft = 6
    handleBottomMiddle = 7
    handleBottomRight = 8

    handleSize = +8.0
    handleSpace = -4.0

    KEYBOARD_MOVE_UNIT = 1

    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.selected_edge = None
        self.itemSelected = False

        self.minWH = 8 # 最小宽高限制,不能太窄

        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, True)
        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, event):
        """
        Executed when the mouse is pressed on the item.
        """
        self.handleSelected = self.handleAt(event.pos())
        if self.handleSelected:
            self.mousePressPos = event.pos()
            self.mousePressRect = self.boundingRect()

            
            rect = self.rect()
            if abs(rect.left() - self.mousePressPos.x()) < 5:
                self.selected_edge = 'left'
            elif abs(rect.right() - self.mousePressPos.x()) < 5:
                self.selected_edge = 'right'
            elif abs(rect.top() - self.mousePressPos.y()) < 5:
                self.selected_edge = 'top'
            elif abs(rect.bottom() - self.mousePressPos.y()) < 5:
                self.selected_edge = 'bottom'
            else:
                self.selected_edge = None



        super().mousePressEvent(event)

    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)

        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)

        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)

        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)

        elif self.handleSelected == self.handleMiddleRight:
            fromX = self.mousePressRect.right()
            toX = fromX + mousePos.x() - self.mousePressPos.x()
            diff.setX(toX - fromX)
            boundingRect.setRight(toX)
            rect.setRight(boundingRect.right() - offset)

        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)

        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)

        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)
        
        # 限制最小宽高
        if rect.width() < self.minWH:
            if self.selected_edge == 'left':
                rect.setLeft(rect.right() - self.minWH)
            else:
                rect.setRight(rect.left() + self.minWH)
        if rect.height() < self.minWH:
            if self.selected_edge == 'top':
                rect.setTop(rect.bottom() - self.minWH)
            else:
                rect.setBottom(rect.top() + self.minWH)


        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, 100)))
        painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine))
        painter.drawRect(self.rect())

        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(QBrush(QColor(255, 0, 0, 255)))
        painter.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))

        # 如果是选中状态, 画 resize 的8个点
        if self.itemSelected:
            for handle, rect in self.handles.items():
                if self.handleSelected is None or handle == self.handleSelected:
                    painter.drawEllipse(rect)

    def itemChange(self, change, value):
        # 被选中的状态变更
        if (change == QGraphicsItem.ItemSelectedChange):  
            self.itemSelected = value
                  
        return super().itemChange(change, value)

    def keyPressEvent(self, e):
        if not self.itemSelected :
            return

        if e.key() == Qt.Key_Right:
            self.setPos(self.x() + self.KEYBOARD_MOVE_UNIT, self.y())
        elif e.key() == Qt.Key_Left:
            self.setPos(self.x() - self.KEYBOARD_MOVE_UNIT, self.y())
        elif e.key() == Qt.Key_Down:
            self.setPos(self.x(), self.y() + self.KEYBOARD_MOVE_UNIT)
        elif e.key() == Qt.Key_Up:
            self.setPos(self.x(), self.y() - self.KEYBOARD_MOVE_UNIT)

def main():

    app = QApplication(sys.argv)

    grview = QGraphicsView()
    scene = QGraphicsScene()
    scene.setSceneRect(0, 0, 680, 459)

    grview.setScene(scene)

    item1 = GraphicsRectItem(0, 0, 8, 150)
    scene.addItem(item1)


    item2 = GraphicsRectItem(9, 9, 300, 8)
    scene.addItem(item2)


    grview.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
    grview.show()
    sys.exit(app.exec())

main()

未完成示例

下面代码尝试旋转功能的,还没有完工

from PySide6 import QtWidgets,QtGui
from PySide6.QtCore import Qt, QRectF



class LineItem(QtWidgets.QGraphicsItem):

    def __init__(self, *args):
        """
        Initialize the shape.
        """
     
        super().__init__(*args)
                
        self.startPosX, self.startPosY = 50, 0
        self.endPosX, self.endPosY = 50, 100

        self.radius =  16 # 圆弧半径
        self.length = 100

    def boundingRect(self):
        return QRectF(0 , 0,
                      max(self.startPosX, self.endPosX), 
                      max(self.startPosY, self.endPosY)
                      )

    def paint(self, painter, option, widget):
        painter.setBrush(QtGui.QBrush(Qt.red))
        painter.setPen(QtGui.QPen(Qt.gray, 2))
        painter.drawLine(self.startPosX, self.startPosY, self.endPosX, self.endPosY)

        # 画圆弧, 参考 https://www.byhy.net/py/qt/sn1/0139-custom1/
        
        len = ((self.radius**2)/2) **0.5  
        rectLen = len * 2
        topLeftX, topLeftY = (self.startPosX+self.endPosX)/2 - len, (self.startPosY+self.endPosY)/2 - len

        painter.drawArc(topLeftX, topLeftY, rectLen, rectLen, 0, 240 * 16)



app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 800, 600)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度


line = LineItem()
line.setPos(100, 100)
scene.addItem(line)


# line = scene.addLine(15, 10, 100, 10, QtGui.QPen(Qt.blue, 8))
# line.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)

view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

path

from PySide6 import QtWidgets,QtGui
from PySide6.QtCore import Qt


app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 400, 200)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度



path = QtGui.QPainterPath()
path.addRect(20, 20, 60, 60)
path.moveTo(0, 0)
path.cubicTo(99, 0, 50, 50, 99, 99)
# path.cubicTo(0, 99, 50, 50, 0, 0)

line = scene.addPath(path, QtGui.QPen(Qt.green, 1))
line.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)

view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

svg

简单示例

from PySide6 import QtWidgets,QtGui,QtSvgWidgets,QtSvg
from PySide6.QtCore import Qt, QRectF


app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 800, 600)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度


svgRenderer = QtSvg.QSvgRenderer('1.svg')
svg = QtSvgWidgets.QGraphicsSvgItem()
svg.setSharedRenderer(svgRenderer)
svg.setPos(100, 100)
scene.addItem(svg)
svg.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)


# line = scene.addLine(15, 10, 100, 10, QtGui.QPen(Qt.blue, 8))
# line.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsSelectable)

view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()



自定义+键盘移动功能

from PySide6 import QtWidgets,QtGui,QtSvgWidgets,QtSvg
from PySide6.QtCore import Qt, QRectF


SvgName2Renderer = {
    'tank' : '1.svg',
    'thrrot' : '2.svg',
}

for name,fn in SvgName2Renderer.items():
    SvgName2Renderer[name] = QtSvg.QSvgRenderer(fn)


class SvgItem(QtSvgWidgets.QGraphicsSvgItem):

    def __init__(self, *args):
        super().__init__(*args)

        self.itemSelected = False
        
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, True) # 必须要有这个,keyPressEvent 才会有效

    def itemChange(self, change, value):
        # 被选中的状态变更
        if (change == QtWidgets.QGraphicsItem.ItemSelectedChange):   
            self.itemSelected = value
                  
        return super().itemChange(change, value)

    def keyPressEvent(self, e):
        if not self.itemSelected :
            return

        if e.key() == Qt.Key_Right:
            self.setPos(self.x() + 1, self.y())
        elif e.key() == Qt.Key_Left:
            self.setPos(self.x() - 1, self.y())
        elif e.key() == Qt.Key_Down:
            self.setPos(self.x(), self.y() + 1)
        elif e.key() == Qt.Key_Up:
            self.setPos(self.x(), self.y() - 1)

app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 800, 600)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度

svg = SvgItem()
svg.setSharedRenderer(SvgName2Renderer['tank'])
svg.setPos(100, 100)
scene.addItem(svg)

svg.setSharedRenderer(SvgName2Renderer['thrrot'])
svg.setSharedRenderer(SvgName2Renderer['tank'])

view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

组合Item

QGraphicsItemGroup 实现

from PySide6 import QtWidgets,QtGui,QtSvgWidgets,QtSvg
from PySide6.QtCore import Qt, QRectF


SvgName2Renderer = {
    'tank' : '1.svg',
    'throt' : '2.svg',
}

for name,fn in SvgName2Renderer.items():
    SvgName2Renderer[name] = QtSvg.QSvgRenderer(fn)



class svgItem(QtWidgets.QGraphicsItemGroup):
    def __init__(self, svg, text, svgPos=(0,0), textPos=(60,20), textWidth=70, *args):
        super().__init__(*args)
        
        self.itemSelected = False

        self.svgItem = QtSvgWidgets.QGraphicsSvgItem()
        self.svgItem.setSharedRenderer(SvgName2Renderer[svg])
        self.svgItem.setPos(*svgPos) # 设置其在parent的显示位置

        self.textItem = QtWidgets.QGraphicsTextItem()
        self.textItem.setTextWidth(textWidth)
        self.textItem.setPlainText(text)
        self.textItem.setPos(*textPos) # 设置其在parent的显示位置

        self.addToGroup(self.svgItem)
        self.addToGroup(self.textItem)
    
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, True) # 必须要有这个,keyPressEvent 才会有效

    def itemChange(self, change, value):
        # 被选中的状态变更
        if (change == QtWidgets.QGraphicsItem.ItemSelectedChange):   
            self.itemSelected = value
                  
        return super().itemChange(change, value)
    
    def keyPressEvent(self, e):
        if not self.itemSelected :
            return

        if e.key() == Qt.Key_Right:
            self.setPos(self.x() + 1, self.y())
        elif e.key() == Qt.Key_Left:
            self.setPos(self.x() - 1, self.y())
        elif e.key() == Qt.Key_Down:
            self.setPos(self.x(), self.y() + 1)
        elif e.key() == Qt.Key_Up:
            self.setPos(self.x(), self.y() - 1)

app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 800, 600)


item = svgItem( 'throt', '主节气阀')
item.setPos(100, 100)
scene.addItem(item)


view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

setParentItem 实现

from PySide6 import QtWidgets,QtGui,QtSvgWidgets,QtSvg
from PySide6.QtCore import Qt, QRectF


SvgName2Renderer = {
    'tank' : '1.svg',
    'throt' : '2.svg',
}

for name,fn in SvgName2Renderer.items():
    SvgName2Renderer[name] = QtSvg.QSvgRenderer(fn)


class svgItem(QtWidgets.QGraphicsItemGroup):
    def __init__(self, svg, text, svgPos=(0,0), textPos=(60,20), textWidth=70, *args):
        super().__init__(*args)
        
        self.itemSelected = False

        self.svgItem = QtSvgWidgets.QGraphicsSvgItem()
        self.svgItem.setSharedRenderer(SvgName2Renderer[svg])
        self.svgItem.setPos(*svgPos) # 设置其在parent的显示位置

        self.textItem = QtWidgets.QGraphicsTextItem()
        self.textItem.setTextWidth(textWidth)
        self.textItem.setPlainText(text)
        self.textItem.setPos(*textPos) # 设置其在parent的显示位置

        self.addToGroup(self.svgItem)
        self.addToGroup(self.textItem)
    
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, True) # 必须要有这个,keyPressEvent 才会有效

    def itemChange(self, change, value):
        # 被选中的状态变更
        if (change == QtWidgets.QGraphicsItem.ItemSelectedChange):   
            self.itemSelected = value
                  
        return super().itemChange(change, value)
    
    def keyPressEvent(self, e):
        if not self.itemSelected :
            return

        if e.key() == Qt.Key_Right:
            self.setPos(self.x() + 1, self.y())
        elif e.key() == Qt.Key_Left:
            self.setPos(self.x() - 1, self.y())
        elif e.key() == Qt.Key_Down:
            self.setPos(self.x(), self.y() + 1)
        elif e.key() == Qt.Key_Up:
            self.setPos(self.x(), self.y() - 1)


class RoomItem(QtWidgets.QGraphicsItem):

    

    def boundingRect(self):
        penWidth = 1.0
        return QRectF(0 , 0,
                      300, 320)

    def paint(self, painter, option, widget):
        painter.setBrush(QtGui.QBrush(Qt.gray))
        painter.setPen(QtGui.QPen(Qt.cyan, 2))
        painter.drawRect(0, 0, 299, 319)
     
        painter.setPen(QtGui.QPen(Qt.blue, 2))
        painter.drawText(10, 0, '工作室')


app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 800, 600)


room = RoomItem()

item = svgItem( 'throt', '主节气阀')
item.setParentItem(room)
item.setPos(100, 100)

scene.addItem(room)


view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()

layout

from PySide6 import QtWidgets,QtGui
from PySide6.QtCore import Qt, QRectF


class Battary(QtWidgets.QGraphicsWidget):

    def boundingRect(self):
        penWidth = 1.0
        return QRectF(0 , 0,
                      40, 120)

    def paint(self, painter, option, widget):
        painter.setBrush(QtGui.QBrush(Qt.red))
        painter.drawRect(5, 0, 20, 10)
        painter.setBrush(QtGui.QBrush(Qt.green))
        painter.drawRect(5, 20, 20, 10)
        painter.drawRect(5, 40, 20, 10)
        painter.drawText(0, 75, '水箱')
        # painter.drawRoundedRect(-10, -10, 20, 20, 5, 5)

app = QtWidgets.QApplication()

scene = QtWidgets.QGraphicsScene(0, 0, 800, 600)

topW = QtWidgets.QGraphicsWidget()
scene.addItem(topW)

layout = QtWidgets.QGraphicsLinearLayout(Qt.Horizontal, topW)
layout.setSpacing(200)


# Define the brush (fill).
brush = QtGui.QBrush(Qt.red)
# Define the pen (line)
pen = QtGui.QPen(Qt.cyan, 1) # 颜色,宽度


node = Battary()
layout.addItem(node)

layout.addItem(Battary())


layout.addItem(Battary())

view = QtWidgets.QGraphicsView(scene)
view.show()
app.exec()