Pyside/PyQt - Attach QWidget to QGraphicsItem


#1

Has anyone been able to attach an standard QWidget to a QGraphicsItem, say a QSpinBox to modify a float value that the QGraphicsItem is representing?
I know it is possible to add a QWidget to a QGraphicsScene but I want to attach it to an Item in my Scene, rather than the scene itself.

Thanks muchly in advance.


#2

I’m not sure QGraphicsItem supports that directly unless you paint it in yourself. I think what you’re looking for if you want to display widgets is an inheritor called QGraphicsWidget. Then you can just add it as a child of your custom QGraphicsItem. http://doc.qt.io/qt-5/qgraphicswidget.html


#3

Thanks for the reply Claudio,

There doesn’t seem to be any method on the QGraphicsWidget to add a standard widget as a child.
My current plan/work around is to add a standard widget to the scene, overlaying it on top of the QGraphicsItem/Widget and then use a custom moving signal to keep the standard widget aligned to the Graphics widget if/when it is moved.


#4

Here’s a proof of concept, but probably not the most efficient. You need to use a special layout reserved for GraphicsWidget items. I inherited in this example, but the documentation proposes composition if you’re simply adding widgets to a scene. More importantly is that widgets need to be added to the scene despite all other requirements. Note that I parented the QGraphicsWidget to a QGraphicsRectItem and it works as intended.

import sys
from Qt import QtWidgets, QtCore


class TestGraphicsWidget(QtWidgets.QGraphicsWidget):
    """

    """
    comboBoxIndexChanged = QtCore.Signal(str)
    lineEditTextChanged = QtCore.Signal(str)

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

        combo = QtWidgets.QComboBox()
        combo.addItems([str(i) for i in range(5)])
        combo.currentIndexChanged[unicode].connect(self.comboBoxIndexChanged)

        line = QtWidgets.QLineEdit()
        line.textChanged.connect(self.lineEditTextChanged)

        self.lineEdit = scene.addWidget(line)
        self.comboBox = scene.addWidget(combo)


        layout = QtWidgets.QGraphicsLinearLayout()
        layout.addItem(self.lineEdit)
        layout.addItem(self.comboBox)

        self.setLayout(layout)


class Window(QtWidgets.QDialog):
    """

    """
    def __init__(self, parent=None):
        super(Window, self).__init__(parent=parent)
        self.scene = QtWidgets.QGraphicsScene()
        self.view = QtWidgets.QGraphicsView(self.scene)

        self.pushButtonAdd = QtWidgets.QPushButton('Add')

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.view)
        layout.addWidget(self.pushButtonAdd)

        self.setLayout(layout)

        self.pushButtonAdd.pressed.connect(self.addTestItem)

    @QtCore.Slot(object)
    def printOutput(self, text):
        print(text)

    @QtCore.Slot()
    def addTestItem(self):
        """

        :return:
        """
        item = QtWidgets.QGraphicsRectItem()
        item.setRect(QtCore.QRectF(0, 0, 200, 40))
        w = TestGraphicsWidget(self.scene)
        w.setParentItem(item)
        w.comboBoxIndexChanged.connect(self.printOutput)
        w.lineEditTextChanged.connect(self.printOutput)
        self.scene.addItem(item)


if __name__ in ('__main__', '__builtin__'):

    app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
    app.setStyle('plastique')

    dialog = Window(None)
    dialog.show()
    sys.exit(app.exec_())

#5

Thank you sir, you are a hero!
I will review this and post back with anything else I figure out.