QGraphicsLinearLayout不能正确调整大小以更改内容

时间:2020-06-15 09:59:21

标签: python pyside2 qgraphicsview qgraphicsitem qgraphicswidget

我正在尝试创建一个QGraphicsWidget,其中QGraphicsLinearLayout包含更多的布局和小部件,同时由于用户交互而添加和删除内容。最初,布局并没有以几何方式分布它们的项目,而只是将它们彼此紧挨着放置。我通过在两个项目之间添加layout.addStretch()来解决此问题。这是我为小部件创建布局的方式:

class NodeInstance(QGraphicsItem):
    def __init__(self):
        super(NodeInstance, self).__init__()

        # main widget
        self.body_widget = QGraphicsWidget(self)
        self.body_layout = QGraphicsLinearLayout(Qt.Horizontal)
        self.body_layout.setSpacing(30)

        #       inputs
        self.inputs_layout = self.get_ports_layout(num_ports=5)  # returns QGraphicsLinearLayout
        self.body_layout.addItem(self.inputs_layout)
        self.body_layout.setAlignment(self.inputs_layout, Qt.AlignLeft | Qt.AlignVCenter)

        self.body_layout.addStretch()  # add space in between

        #       outputs
        self.outputs_layout = self.get_ports_layout(num_ports=2)  # returns QGraphicsLinearLayout
        self.body_layout.addItem(self.outputs_layout)
        self.body_layout.setAlignment(self.outputs_layout, Qt.AlignRight | Qt.AlignVCenter)

        self.body_widget.setLayout(self.body_layout)

    def get_ports_layout(self, num_ports):
        """Creating vertical layout with PortInstances and stretches in between them."""
        layout = QGraphicsLinearLayout(Qt.Vertical)
        layout.setSpacing(3)
        for i in range(num_ports):
            inp = PortInstance()  # a horizontal QGraphicsLinearLayout containing two QGraphicsWidgets
            layout.addItem(inp)
            if i != num_ports-1:
                layout.addStretch()
        return layout

窗口小部件只是QGraphicsItem NodeInstance的一部分。

通过左键单击,我添加了一个输入:

def mousePressEvent(self, event):
    if event.button() == Qt.LeftButton:
        # add new input
        self.inputs_layout.addStretch()
        self.inputs_layout.addItem(PortInstance())

        self.body_layout.invalidate()

有效,布局会相应调整大小。 右键单击,删除第一个:

    elif event.button() == Qt.RightButton:
        inp: PortInstance = self.inputs_layout.itemAt(0)

        # remove input's contents from scene (for some reason, it doesn't happen automatically, even when deleting)
        self.scene().removeItem(inp.pin)
        self.scene().removeItem(inp.label)

        self.inputs_layout.removeAt(0)

        self.body_layout.invalidate()

这里有两个问题:

  1. 布局不会再次调整其大小,它应该变得更小,它保持不变
  2. 添加的拉伸仍然以某种方式存在,我无法访问它们或将其从布局中删除。剩余的输入未正确分配,并且经常通过添加和删除输入,顶部的空白区域(我假设是拉伸)会变大。

因此,我希望主窗口小部件及其布局在可能的情况下缩小(也没有为它设置SizePolicy的运气),并正确分配其内容的位置,而没有任何未使用的空白区域。

我不知道还能尝试什么,而且在文档中找不到任何提示。 我正在使用PySide2 5.15.0和Python 3.8.3。

完整代码

import sys

from PySide2.QtWidgets import QMainWindow, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem, \
    QGraphicsLinearLayout, QGraphicsLayoutItem, QGraphicsWidget
from PySide2.QtGui import QPainter, QColor, QPen, QFont, QFontMetrics
from PySide2.QtCore import QRectF, Qt, QPointF, QSizeF


class NodeInstance(QGraphicsItem):
    def __init__(self):
        super(NodeInstance, self).__init__()

        # main widget
        self.body_widget = QGraphicsWidget(self)
        self.body_layout = QGraphicsLinearLayout(Qt.Horizontal)
        self.body_layout.setSpacing(30)

        #       inputs
        self.inputs_layout = self.get_ports_layout(num_ports=5)  # returns QGraphicsLinearLayout
        self.body_layout.addItem(self.inputs_layout)
        self.body_layout.setAlignment(self.inputs_layout, Qt.AlignLeft | Qt.AlignVCenter)

        self.body_layout.addStretch()  # add space in between

        #       outputs
        self.outputs_layout = self.get_ports_layout(num_ports=2)  # returns QGraphicsLinearLayout
        self.body_layout.addItem(self.outputs_layout)
        self.body_layout.setAlignment(self.outputs_layout, Qt.AlignRight | Qt.AlignVCenter)

        self.body_widget.setLayout(self.body_layout)

    def get_ports_layout(self, num_ports):
        """Creating vertical layout with PortInstances and stretches in between them."""
        layout = QGraphicsLinearLayout(Qt.Vertical)
        layout.setSpacing(3)
        for i in range(num_ports):
            inp = PortInstance()  # a horizontal QGraphicsLinearLayout containing two QGraphicsWidgets
            layout.addItem(inp)
            if i != num_ports-1:
                layout.addStretch()
        return layout

    def boundingRect(self):
        return self.body_layout.geometry().toRect()

    def get_header_rect(self):
        header_height = self.get_header_height()
        header_width = self.get_width()
        height = self.get_height()

        return QRectF(-header_width/2, -height/2, header_width, header_height)

    def paint(self, painter, option, widget=None):
        painter.setBrush(QColor('yellow'))
        painter.setPen(QPen(QColor(0, 0, 0), 3))
        painter.drawRoundedRect(self.boundingRect(), 20, 20)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            # add new input
            self.inputs_layout.addStretch()
            self.inputs_layout.addItem(PortInstance())

            self.body_layout.invalidate()
        elif event.button() == Qt.RightButton:
            inp: PortInstance = self.inputs_layout.itemAt(0)

            # remove input's contents from scene (for some reason, it doesn't happen automatically, even when deleting)
            self.scene().removeItem(inp.pin)
            self.scene().removeItem(inp.label)

            self.inputs_layout.removeAt(0)

            self.body_layout.invalidate()


class PortInstance(QGraphicsLinearLayout):
    def __init__(self):
        super(PortInstance, self).__init__(Qt.Horizontal)

        self.pin = Pin()  # QGraphicsWidget
        self.label = Label()  # QGraphicsWidget

        self.addItem(self.pin)
        self.addItem(self.label)

        self.setAlignment(self.pin, Qt.AlignCenter)
        self.setAlignment(self.label, Qt.AlignCenter)
        self.setSpacing(10)


class Pin(QGraphicsWidget):
    def __init__(self):
        super(Pin, self).__init__()
        self.setGraphicsItem(self)

        self.width = 30
        self.height = 30

    def boundingRect(self):
        return QRectF(QPointF(0, 0), self.geometry().size())

    def setGeometry(self, rect):
        self.prepareGeometryChange()
        QGraphicsLayoutItem.setGeometry(self, rect)
        self.setPos(rect.topLeft())

    def sizeHint(self, which, constraint=...):
        return QSizeF(self.width, self.height)

    def paint(self, painter, option, widget=None):
        painter.setBrush(QColor(0, 0, 255, 150))
        painter.setPen(Qt.NoPen)
        painter.drawEllipse(self.boundingRect())


class Label(QGraphicsWidget):
    def __init__(self):
        super(Label, self).__init__()
        self.setGraphicsItem(self)

        self.text = 'label'
        self.font = QFont('Arial', 15)
        f_m = QFontMetrics(self.font)
        self.width = f_m.width(self.text)
        self.height = f_m.height()

    def boundingRect(self):
        return QRectF(QPointF(0, 0), self.geometry().size())

    def setGeometry(self, rect):
        self.prepareGeometryChange()
        QGraphicsLayoutItem.setGeometry(self, rect)
        self.setPos(rect.topLeft())

    def sizeHint(self, which, constraint=...):
        return QSizeF(self.width, self.height)

    def paint(self, painter, option, widget=None):
        painter.setFont(self.font)
        painter.drawText(self.boundingRect(), self.text)


class MyView(QGraphicsView):
    def __init__(self):
        super(MyView, self).__init__()

        scene = QGraphicsScene(self)
        scene.setSceneRect(0, 0, self.width(), self.height())

        self.setScene(scene)

        ni = NodeInstance()
        self.scene().addItem(ni)
        ni.setPos(150, 80)


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setCentralWidget(MyView())


if __name__ == '__main__':
    app = QApplication(sys.argv)

    mw = MainWindow()
    mw.show()

    sys.exit(app.exec_())

0 个答案:

没有答案