4

I need to make a widget visible as a layout element for printing, for a QGIS plugin I am developing.

Which of the classes do I need to extend to do this?

After some research I think I begin to discern part of the procedure to achieve my goal:

  • I have to implement or extend the QgsLayoutItem class.
  • If I extend the QgsLayoutItem class, I must rewrite its draw() method in which I will define the way the widget will be drawn in the scene.
  • However, QgsLayoutItem has many derived classes: QgsLayoutItemPage, QgsLayoutItemMap, QgsLayoutItemPicture, QgsLayoutItemLabel, QgsLayoutItemLegend, QgsLayoutItemShape, QgsLayoutItemFrame and others.
2

1 Answer 1

4
+150

This is a partial answer which may not be exactly what you are looking for but perhaps a pointer to get you started. I think, in theory, the solution (or at least best avenue of research) is the QGraphicsProxyWidget class.

I made a few tests and achieved some, but not perfect results. The following code snippet adds a QWidget with child sub-widgets to a QgsPrintLayout object.

class TestProxyWidget(QGraphicsProxyWidget):
    def __init__(self):
        QGraphicsProxyWidget.__init__(self)
        self.w = CustomWidget()
        self.setWidget(self.w)
        
class CustomWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.layout = QHBoxLayout()
        self.lbl = QLabel('A Label:', self)
        self.le = QLineEdit(self)
        for w in self.children():
            self.layout.addWidget(w)
        self.setLayout(self.layout)
        
project = QgsProject.instance()
mgr = project.layoutManager()
layout = mgr.layoutByName('Test Layout')
proxy = TestProxyWidget()
layout.addItem(proxy)

The results are shown below, but as you can see- the size of the added widget is very large and I couldn't easily work out how to resize/scale the widget. Familiar methods like SetGeometry(), resize(), setMinimumSize(), setMaximumSize() etc don't work very well here. You can resize the parent widget, but the child widgets like labels, line edits, push buttons etc. don't scale properly. I'm sure there is a different approach to controlling the size but I'm not expert in this area as I haven't worked that much with Qt graphics classes and, unfortunately I don't much time at the moment to put into research.

enter image description here

If you do something like this example in the docs:

class CustomWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.layout = QHBoxLayout()
        self.lbl = QLabel('A Label:', self)
        self.cb = QComboBox(self)
        self.cb.addItems(['Large', 'Medium', 'Small'])
        self.pb = QPushButton('Click me', self)
        for w in self.children():
            self.layout.addWidget(w)
        self.setLayout(self.layout)

CW = CustomWidget()
scene = QGraphicsScene()
proxy = scene.addWidget(CW)
view = QGraphicsView(scene)
view.show()

The result looks like:

enter image description here

Perhaps this is more of a PyQt question (which might be better asked on Stack Overflow) or if you are lucky, a QGIS developer who knows a lot more about this than I do will give you a better answer here.

2
  • Thank you very much @Ben W, I will try your proposal, you have clarified many things. However, I will wait for an answer using PyQGIS. Although, with just PyQt it could work, it is possible that it will not be accepted as a plugin in the QGIS repository because it does not meet the API standards.
    – Luis Perez
    Oct 25, 2021 at 11:31
  • @Luis Perez, you are welcome. I'm not so sure what you mean by a PyQGIS answer. If you are developing plugins I'm sure you are aware that most of Qgis gui elements are built on underlying Qt library. That includes QgsLayout class which inherits from QGraphicsScene and all QgsLayoutItem classes which inherit from QGraphicsRectItem etc. Hence my remark that your question is probably more about PyQt than PyQGIS. Anyway, I wish you the best of luck.
    – Ben W
    Oct 25, 2021 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.