【问题标题】:Draw QGraphicsWidget outside of parent在父级之外绘制 QGraphicsWidget
【发布时间】:2020-06-24 07:09:40
【问题描述】:

我正在开发节点编辑器,目前正在使用QGraphicsWidgetQGraphicsScene 创建自己的系统。我发现在我的Node 类中使用QGraphicsGridLayout 非常灵活,只需将文本/数字输入、标签等子组件添加到布局中即可。现在,我的问题是我想在输入旁边创建一个套接字,它应该看起来像一个可以连接边缘的小圆圈。目前看起来是这样的:

但是,我无法将套接字放置在节点之外的一半位置。我尝试在我的Node 类的paint() 方法中绘制它,但是当节点移动时它会从外部的部分留下痕迹,并且我失去了一些灵活性。实现以下外观的最佳方法是什么?

下面是我的Node 班级和我的Socket 班级。

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt, QRectF
from PyQt5.QtGui import QPainterPath, QBrush, QFont, QColor, QPalette
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsTextItem, QGraphicsWidget, QGraphicsGridLayout, QLabel, \
    QLineEdit, QSizePolicy

from src.gui.widgets.graphics.node_scene import NodeScene
from src.gui.widgets.graphics.socket import Socket, SOCKET_TYPE_FLOAT, SOCKET_TYPE_RGB


class Node(QGraphicsWidget):
    def __init__(self, scene: NodeScene, title: str = "", parent=None):
        super().__init__(parent)

        # define data properties
        self._input_sockets = []

        # define Node properties
        self._scene = scene
        self._width = 200
        self._height = 200
        self._rounding = 5
        self._padding = 8
        self._bg_color = QColor(80, 80, 100, 200)
        self._title_color = Qt.white
        self._title_font = QFont("Corbel", 8)
        self._title_font.setBold(True)
        self._title = title
        self._title_item = None

        # Define socket properties
        self._socket_label_font = QFont("Corbel", 7)
        self._socket_label_palette = QPalette()
        self._socket_label_palette.setColor(QPalette.Background, QColor(0, 0, 0, 0))
        self._socket_label_palette.setColor(QPalette.Foreground, self._title_color)

        # Define layout
        self._layout = QGraphicsGridLayout()
        self._layout.setColumnAlignment(0, Qt.AlignLeft)
        #self._layout.setContentsMargins(*[self._padding] * 4)
        self._layout.setHorizontalSpacing(2.0)
        self._layout.setRowSpacing(0, 16)
        self.setLayout(self._layout)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)

        # Try adding an input
        self.add_input(SOCKET_TYPE_FLOAT, "Test input")

        # Initialize title
        self._create_title()
        self.title = self._title

    @property
    def title(self):
        return self._title

    @title.setter
    def title(self, value):
        self._title = value
        self._title_item.setPlainText(self._title)

    def _create_title(self):
        self._title_item = QGraphicsTextItem(self)
        self._title_item.setDefaultTextColor(self._title_color)
        self._title_item.setFont(self._title_font)
        self._title_item.setPos(self._padding, 0)
        self._title_item.setTextWidth(self._width - self._padding)

    def boundingRect(self) -> QtCore.QRectF:
        return QRectF(0, 0, self._width, self._height).normalized()

    def paint(self, painter, option, widget=None):
        path_bg = QPainterPath()
        path_bg.addRoundedRect(0, 0, self._width, self._height, self._rounding, 1)
        painter.setPen(Qt.NoPen)  # Disables the border
        painter.setBrush(QBrush(self._bg_color))
        painter.drawPath(path_bg)

    def add_input(self, input_type: str, input_name: str, input_range=(0, 1)):
        if input_type == SOCKET_TYPE_FLOAT:
            socket = Socket()
            self._layout.addItem(Socket(), 1, 0)

        if input_type == SOCKET_TYPE_RGB:
            socket = Socket()
            self._layout.addItem(Socket(), 1, 0)

还有Socket

from PyQt5.QtCore import Qt, QRectF, QPoint
from PyQt5.QtGui import QPainterPath, QColor, QBrush
from PyQt5.QtWidgets import QGraphicsWidget

SOCKET_TYPE_FLOAT = "type_float"
SOCKET_TYPE_RGB = "type_rgb"


class Socket(QGraphicsWidget):

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

        self._circle_color = QColor(255, 130, 0, 255)
        self._bbox = QRectF(0, 0, 10, 10)
        self._current_edge = None

    def boundingRect(self):
        return self._bbox

    def paint(self, painter, option, widget=None):
        path = QPainterPath()
        path.addEllipse(self._bbox)
        painter.setPen(Qt.black)
        painter.setBrush(QBrush(self._circle_color))
        painter.drawPath(path)

【问题讨论】:

    标签: python layout pyqt pyqt5


    【解决方案1】:

    您需要为布局设置负边距,但您不能将 setContentsMargins() 与负值一起使用,因为如果发生这种情况,QGraphicsLayout 会自动将边距标准化为 0。

    我想出的解决方案是将布局子类化并覆盖 getContentsMargins() 以返回负边距:

    class NegativeMarginsLayout(QGraphicsGridLayout):
        def __init__(self, *margins):
            super().__init__()
            self._margins = margins
    
        def setContentsMargins(self, *margins):
            self._margins = margins
            self.invalidate()
            self.activate()
    
        def getContentsMargins(self):
            return self._margins
    
    class Node(QGraphicsWidget):
        def __init__(self, scene: QGraphicsScene, title: str = "", parent=None):
            # ...
            self._layout = NegativeMarginsLayout(-20, 0, 20, 0)
    

    请注意,使用 QGraphicsLayout 可能会给鼠标事件的碰撞函数带来问题,因此您可能应该覆盖 shape() 并返回 boundingRect 的 QPainterPath(可能会使用布局边距进行调整)。此外,您不需要创建 QPainterPath 来绘制圆角矩形的形状,只需使用 QPainter.drawRoundedRect

    【讨论】:

    • 完美运行!非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2019-01-09
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2015-02-24
    相关资源
    最近更新 更多