我正在尝试创建一个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()
这里有两个问题:
因此,我希望主窗口小部件及其布局在可能的情况下缩小(也没有为它设置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_())