如何在QGraphicsScene中正确放置小部件

时间:2019-06-20 18:46:01

标签: python qgraphicsview qgraphicsscene pyside2

我正在尝试实现将放置在场景右侧的小部件(即席工具箱)。 它看起来应该像这样:

problem

在移动和缩放视图时,存在将其正确放置在此处并保持相同大小和位置的问题。

小部件本身是QComboBox。我试图将其作为QGraphicsWidget放置在场景中,将ZValue设置为高数值并将其设置为QGraphicsLinearLayout。不幸的是,我无法将其对齐到左侧,并且我觉得我做错了方法。

这是草图:

from PySide2 import QtWidgets, QtCore, QtGui
import sys

def hex2QColor(color):
    r = int(color[0:2], 16)
    g = int(color[2:4], 16)
    b = int(color[4:6], 16)
    return QtGui.QColor(r, g, b)

class rotateGroupBox(QtWidgets.QGroupBox):
    """Create content to fill the widget"""
    def __init__(self, parent=None):
        super(rotateGroupBox, self).__init__(parent)

        self.setTitle("Rotation")

        self.l_rotate = QtWidgets.QSpinBox()
        self.l_rotate.setRange(0, 360)

        self.r_rotate = QtWidgets.QSpinBox()
        self.r_rotate.setRange(0, 360)

        self.l_label = QtWidgets.QLabel("Left: ")
        self.r_label = QtWidgets.QLabel("Right: ")

        layout = QtWidgets.QVBoxLayout()

        l1 = QtWidgets.QHBoxLayout()
        l1.setContentsMargins(0,0,0,0)
        l1.addWidget(self.l_label)
        l1.addWidget(self.l_rotate)

        l2 = QtWidgets.QHBoxLayout()
        l2.setContentsMargins(0, 0, 0, 0)
        l2.addWidget(self.r_label)
        l2.addWidget(self.r_rotate)

        layout.addLayout(l1)
        layout.addLayout(l2)

        self.setLayout(layout)

class propertyBox(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(propertyBox, self).__init__(parent)

        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        self.backgroundColor = hex2QColor("efefef")
        self.foregroundColor = hex2QColor("333333")
        self.borderRadius = 10
        self.__mousePressPos = None
        self.__mouseMovePos = None

        self.draggable = True
        self.dragging_threshould = 5
        self.__mousePressPos = None
        self.__mouseMovePos = None

        gr1 = rotateGroupBox(self)
        gr2 = rotateGroupBox(self)
        gr3 = rotateGroupBox(self)
        gr4 = rotateGroupBox(self)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(gr1)
        layout.addWidget(gr2)
        layout.addWidget(gr3)
        layout.addWidget(gr4)
        self.setLayout(layout)

    def paintEvent(self, event):
        # get current window size
        s = self.size()
        qp = QtGui.QPainter()
        qp.begin(self)
        #qp.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
        qp.setPen(self.foregroundColor)
        qp.setBrush(self.backgroundColor)
        qp.drawRoundedRect(0, 0, s.width(), s.height(),
                           self.borderRadius, self.borderRadius)
        qp.end()

class graphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(graphicsView, self).__init__(parent)

    def wheelEvent(self, event):
        factor = 1.41 ** (-event.delta() / 240)
        self.scale(factor, factor)
        global RAW
        RAW = True

    def mousePressEvent(self, event):
        if event.button() & QtCore.Qt.RightButton:
            self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
        else:
            super(graphicsView, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() & QtCore.Qt.RightButton:
            self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
        else:
            super(graphicsView, self).mouseReleaseEvent(event)

app = QtWidgets.QApplication(sys.argv)

window = graphicsView()
window.resize(1000, 500)

scene = QtWidgets.QGraphicsScene()
window.setScene(scene)

box = scene.addWidget(propertyBox())

layout = QtWidgets.QGraphicsLinearLayout()
layout.setContentsMargins(0,0,0,0)
#layout.setAlignment(box, QtCore.Qt.AlignRight) # doesn't work
layout.addItem(box)

form = QtWidgets.QGraphicsWidget()
#form.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
form.setZValue(100000000)
form.setLayout(layout)
scene.addItem(form)

# Adding an item that should be overlapped by the property box
rect = QtWidgets.QGraphicsRectItem()
rect.setRect(0,0,200,200)
rect.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)
scene.addItem(rect)

window.show()
sys.exit(app.exec_())

1 个答案:

答案 0 :(得分:2)

一种可能的解决方案是使小部件成为viewport()的子级,而不是项的子级,因此场景的变换不会影响它。并在viewport()更改大小时调整位置:

from PySide2 import QtCore, QtGui, QtWidgets


class RotateGroupBox(QtWidgets.QGroupBox):
    """Create content to fill the widget"""

    def __init__(self, parent=None):
        super(RotateGroupBox, self).__init__(parent)

        self.setTitle("Rotation")

        self.l_rotate = QtWidgets.QSpinBox()
        self.l_rotate.setRange(0, 360)

        self.r_rotate = QtWidgets.QSpinBox()
        self.r_rotate.setRange(0, 360)

        self.l_label = QtWidgets.QLabel("Left: ")
        self.r_label = QtWidgets.QLabel("Right: ")

        layout = QtWidgets.QVBoxLayout(self)

        l1 = QtWidgets.QHBoxLayout()
        l1.setContentsMargins(0, 0, 0, 0)
        l1.addWidget(self.l_label)
        l1.addWidget(self.l_rotate)

        l2 = QtWidgets.QHBoxLayout()
        l2.setContentsMargins(0, 0, 0, 0)
        l2.addWidget(self.r_label)
        l2.addWidget(self.r_rotate)

        layout.addLayout(l1)
        layout.addLayout(l2)


class PropertyBox(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(PropertyBox, self).__init__(parent)

        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        self.backgroundColor = QtGui.QColor("salmon")
        self.foregroundColor = QtGui.QColor("red")
        self.borderRadius = 10

        gr1 = RotateGroupBox()
        gr2 = RotateGroupBox()
        gr3 = RotateGroupBox()
        gr4 = RotateGroupBox()

        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(gr1)
        layout.addWidget(gr2)
        layout.addWidget(gr3)
        layout.addWidget(gr4)

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(self.foregroundColor)
        painter.setBrush(self.backgroundColor)
        rect = QtCore.QRectF(
            QtCore.QPoint(),
            self.size() - 0.5 * painter.pen().width() * QtCore.QSize(1, 1),
        )
        painter.drawRoundedRect(rect, self.borderRadius, self.borderRadius)


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super(GraphicsView, self).__init__(parent)
        self.m_widgets = dict()

    def wheelEvent(self, event):
        factor = 1.41 ** (-event.delta() / 240)
        self.scale(factor, factor)

    def mousePressEvent(self, event):
        if event.button() & QtCore.Qt.RightButton:
            self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
        else:
            super(GraphicsView, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() & QtCore.Qt.RightButton:
            self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
        else:
            super(GraphicsView, self).mouseReleaseEvent(event)

    def addFixedWidget(self, widget, alignment):
        widget.setParent(self.viewport())
        self.m_widgets[widget] = alignment

    def showEvent(self, event):
        self._update_fixed_widgets()
        super(GraphicsView, self).showEvent(event)

    def resizeEvent(self, event):
        self._update_fixed_widgets()
        super(GraphicsView, self).resizeEvent(event)

    def _update_fixed_widgets(self):
        r = self.viewport().rect()
        for w, a in self.m_widgets.items():
            p = QtCore.QPoint()

            if a & QtCore.Qt.AlignHCenter:
                p.setX((r.width() - w.width()) / 2)
            elif a & QtCore.Qt.AlignRight:
                p.setX(r.width() - w.width())

            if a & QtCore.Qt.AlignVCenter:
                p.setY((r.height() - w.height()) / 2)
            elif a & QtCore.Qt.AlignBottom:
                p.setY(r.height() - w.height())
            w.move(p)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    scene = QtWidgets.QGraphicsScene()
    window = GraphicsView()
    window.resize(1000, 500)
    window.setScene(scene)
    window.addFixedWidget(
        PropertyBox(), QtCore.Qt.AlignRight | QtCore.Qt.AlignTop
    )
    rect = QtWidgets.QGraphicsRectItem()
    rect.setRect(0, 0, 200, 200)
    rect.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)
    scene.addItem(rect)
    window.show()
    sys.exit(app.exec_())

enter image description here