0

我创建了一个自定义 QGraphicsWidget ,它能够在场景中调整小部件的大小。我还可以将预定义的小部件(例如按钮、标签等)添加到我的自定义小部件中。我现在有两个问题。label第一个是小部件在插入新的或小部件时不会更改大小(重新调整),LineEdit因此新插入的小部件不会超出自定义小部件边框。

当我尝试将 的 更改为 0 以外的值时遇到第二个问题setContentMarginsQGraphicsLayout例如QGraphicsLayout.setContentMargins(1, 1, 1, 20)会延迟小部件中的光标LineEdit

这是图像。

在此处输入图像描述

(拖动灰色三角形改变大小)

import sys

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

class Container(QtWidgets.QWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.setStyleSheet('Container{background:transparent;}')


class GraphicsFrame(QtWidgets.QGraphicsWidget):

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

        x, y, h, w = args
        rect = QRectF(x, y, h, w)
        self.setGeometry(rect)

        self.setMinimumSize(150, 150)
        self.setMaximumSize(400, 800)

        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, True)

        self.mousePressPos = None
        self.mousePressRect = None
        self.handleSelected = None

        self.polygon = QtGui.QPolygon([
            QtCore.QPoint(int(self.rect().width()-10), int(self.rect().height()-20)),
            QtCore.QPoint(int(self.rect().width()-10), int(self.rect().height()-10)),
            QtCore.QPoint(int(self.rect().width()-20), int(self.rect().height()-10))
        ])

        graphic_layout = QtWidgets.QGraphicsLinearLayout(Qt.Vertical, self)
        graphic_layout.setContentsMargins(0, 0, 0, 20)  # changing this will cause the second problem

        self.container = Container()

        proxyWidget = QtWidgets.QGraphicsProxyWidget(self)
        proxyWidget.setWidget(self.container)

        graphic_layout.addItem(proxyWidget)

        self.contentLayout = QtWidgets.QFormLayout()
        self.contentLayout.setContentsMargins(10, 10, 20, 20)
        self.contentLayout.setSpacing(5)

        self.container.layout.addLayout(self.contentLayout)
        self.options = []

    def addOption(self, color=Qt.white, lbl=None, widget=None):
        self.insertOption(-1, lbl, widget, color)

    def insertOption(self, index, lbl, widget, color=Qt.white):
        if index < 0:
            index = self.contentLayout.count()
        self.contentLayout.addRow(lbl, widget)

        self.options.insert(index, (widget, color))


    def update_polygon(self):
        self.polygon = QtGui.QPolygon([
            QtCore.QPoint(int(self.rect().width() - 10), int(self.rect().height() - 20)),
            QtCore.QPoint(int(self.rect().width() - 10), int(self.rect().height() - 10)),
            QtCore.QPoint(int(self.rect().width() - 20), int(self.rect().height() - 10))
        ])

    def hoverMoveEvent(self, event):
        if self.polygon.containsPoint(event.pos().toPoint(), Qt.OddEvenFill):
            self.setCursor(Qt.SizeFDiagCursor)

        else:
            self.unsetCursor()

        super(GraphicsFrame, self).hoverMoveEvent(event)


    def mousePressEvent(self, event):

        self.handleSelected = self.polygon.containsPoint(event.pos().toPoint(), Qt.OddEvenFill)

        if self.handleSelected:
            self.mousePressPos = event.pos()
            self.mousePressRect = self.boundingRect()

        super(GraphicsFrame, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):

        if self.handleSelected:
            self.Resize(event.pos())

        else:
            super(GraphicsFrame, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):

        super(GraphicsFrame, self).mouseReleaseEvent(event)
        self.handleSelected = False
        self.mousePressPos = None
        self.mousePressRect = None
        self.update()

    def paint(self, painter, option, widget):

        painter.save()

        painter.setBrush(QBrush(QColor(37, 181, 247)))
        pen = QPen(Qt.white)
        pen.setWidth(2)

        if self.isSelected():
            pen.setColor(Qt.yellow)

        painter.setPen(pen)
        painter.drawRoundedRect(self.rect(), 4, 4)

        painter.setPen(QtCore.Qt.white)
        painter.setBrush(QtCore.Qt.gray)
        painter.drawPolygon(self.polygon)

        super().paint(painter, option, widget)

        painter.restore()

    def Resize(self, mousePos):
        """
        Perform shape interactive resize.
        """
        if self.handleSelected:
            self.prepareGeometryChange()
            width, height = self.geometry().width()+(mousePos.x()-self.mousePressPos.x()),\
                            self.geometry().height()+(mousePos.y()-self.mousePressPos.y())

            self.setGeometry(QRectF(self.geometry().x(), self.geometry().y(), width, height))
            self.contentLayout.setGeometry(QtCore.QRect(0, 30, width-10, height-20))

            self.mousePressPos = mousePos
            self.update_polygon()
            self.updateGeometry()


def main():

    app = QApplication(sys.argv)

    grview = QGraphicsView()
    scene = QGraphicsScene()
    grview.setViewportUpdateMode(grview.FullViewportUpdate)
    scene.addPixmap(QPixmap('01.png'))
    grview.setScene(scene)

    item = GraphicsFrame(0, 0, 300, 150)
    scene.addItem(item)

    item.addOption(Qt.green, lbl=QtWidgets.QLabel('I am a label'), widget=QtWidgets.QLineEdit())
    item.addOption(lbl=QtWidgets.QLabel('why'), widget=QtWidgets.QLineEdit())
    item.addOption(lbl=QtWidgets.QLabel('How'), widget=QtWidgets.QLineEdit())
    item.addOption(lbl=QtWidgets.QLabel('Nooo.'), widget=QtWidgets.QLineEdit())
    item.addOption(lbl=QtWidgets.QLabel('Nooo.'), widget=QtWidgets.QLineEdit())
    item.addOption(lbl=QtWidgets.QLabel('Nooo.'), widget=QtWidgets.QLineEdit())

    item2 = GraphicsFrame(50, 50, 300, 150)
    scene.addItem(item2)

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


if __name__ == '__main__':
    main()
4

1 回答 1

1

正如已经不止一次向您建议的那样,如果您仅使用它来嵌入 QGraphicsProxyWidget,则使用带有 QGraphicsLayout 的 QGraphicsWidget 并不是一个好主意,因为除非您真的知道自己在更改几何图形时,否则您肯定会面临意外行为。重新做。

然后,prepareGeometryChange对于updateGeometryQGraphicsWidget 来说完全没有必要,并且使用项目几何形状调整小部件的大小是绝对错误的,原因有两个:首先,由图形布局来管理内容大小,然后你使用场景坐标,因为你'正在使用缩放,这些坐标将不正确,因为它们应该在小部件的坐标中进行转换。

由于场景矩形不断变化,使用 QSizeGrip 是不可行的(我不得不说,如果与内容的交互式调整大小一起完成,这并不总是一个好主意),你可以使用一个简单的 QGraphicsPathItem ,并使用它作为调整大小的参考,这比连续移动多边形并绘制它要简单得多。

class SizeGrip(QtWidgets.QGraphicsPathItem):
    def __init__(self, parent):
        super().__init__(parent)
        path = QtGui.QPainterPath()
        path.moveTo(0, 10)
        path.lineTo(10, 10)
        path.lineTo(10, 0)
        path.closeSubpath()
        self.setPath(path)
        self.setPen(QtGui.QPen(Qt.white))
        self.setBrush(QtGui.QBrush(Qt.white))
        self.setCursor(Qt.SizeFDiagCursor)


class GraphicsFrame(QtWidgets.QGraphicsItem):

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

        x, y, w, h = args
        self.setPos(x, y)

        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, True)

        self.container = Container()

        self.proxy = QtWidgets.QGraphicsProxyWidget(self)
        self.proxy.setWidget(self.container)

        self.proxy.setMinimumSize(150, 150)
        self.proxy.setMaximumSize(400, 800)
        self.proxy.resize(w, h)

        self.contentLayout = QtWidgets.QFormLayout()
        self.contentLayout.setContentsMargins(10, 10, 20, 20)
        self.contentLayout.setSpacing(5)

        self.container.layout.addLayout(self.contentLayout)
        self.options = []

        self.sizeGrip = SizeGrip(self)
        self.mousePressPos = None

        self.proxy.geometryChanged.connect(self.resized)
        self.resized()

    def addOption(self, color=Qt.white, lbl=None, widget=None):
        self.insertOption(-1, lbl, widget, color)

    def insertOption(self, index, lbl, widget, color=Qt.white):
        if index < 0:
            index = self.contentLayout.count()
        self.contentLayout.addRow(lbl, widget)
        self.options.insert(index, (widget, color))

    def mousePressEvent(self, event):
        gripShape = self.sizeGrip.shape().translated(self.sizeGrip.pos())
        if event.button() == Qt.LeftButton and gripShape.contains(event.pos()):
            self.mousePressPos = event.pos()
        else:
            super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self.mousePressPos:
            delta = event.pos() - self.mousePressPos
            geo = self.proxy.geometry()
            bottomRight = geo.bottomRight()
            geo.setBottomRight(bottomRight + delta)
            self.proxy.setGeometry(geo)
            diff = self.proxy.geometry().bottomRight() - bottomRight
            if diff.x():
                self.mousePressPos.setX(event.pos().x())
            if diff.y():
                self.mousePressPos.setY(event.pos().y())
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self.mousePressPos = None
        super().mouseReleaseEvent(event)

    def resized(self):
        rect = self.boundingRect()
        self.sizeGrip.setPos(rect.bottomRight() + QtCore.QPointF(-20, -20))

    def boundingRect(self):
        return self.proxy.boundingRect().adjusted(-11, -11, 11, 11)

    def paint(self, painter, option, widget):
        painter.save()
        painter.setBrush(QBrush(QColor(37, 181, 247)))
        painter.drawRoundedRect(self.boundingRect().adjusted(0, 0, -.5, -.5), 4, 4)
        painter.restore()

请注意,在显示视图fitInView() 之前使用不是一个好主意,尤其是在使用代理小部件和布局的情况下。

于 2020-12-29T16:07:36.017 回答