Нарисуйте QGraphicsWidget вне родительского - PullRequest
0 голосов
/ 12 марта 2020

Я работаю над редактором узлов и в настоящее время создаю свою собственную систему, используя QGraphicsWidget и QGraphicsScene. Мне очень удобно использовать QGraphicsGridLayout в моем классе Node и просто добавлять подкомпоненты, такие как ввод текста / чисел, метки и т. Д. c в макет. Теперь моя проблема в том, что я хотел бы создать сокет прямо рядом с входом, который должен выглядеть как маленький круг, где можно соединить ребро. В настоящее время это выглядит так:

Current look of my system

Однако я не могу заставить сокет быть размещенным на полпути вне Узла. Я попытался нарисовать его в методе paint() моего класса Node, но затем он оставляет следы от части, которая находится снаружи, когда узел перемещается, и я теряю некоторую гибкость. Каков наилучший способ получить следующий вид?

Image of node editor

Ниже приведен мой класс 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)

1 Ответ

1 голос
/ 12 марта 2020

Вам нужно установить отрицательные поля для макета, но вы не можете использовать 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)

layout outside margins

Обратите внимание, что использование QGraphicsLayout может создать проблемы с функциями столкновения для событий мыши, поэтому вам, вероятно, следует переопределить shape() и вернуть QPainterPath of boundingRect (возможно, скорректированный с помощью полей макета). Кроме того, вам не нужно создавать QPainterPath для рисования формы скругленного прямоугольника, просто используйте QPainter.drawRoundedRect.

...