Search code examples
pythonpyqtpyqt5qapplication

Run an application and quit it after rendering on PyQt5


I would like to render SVG files with PyQt5.

The simplest way to do that is to use QSvgGenerator applied on a QPainter object.

However, for some reason I have to render text in the output SVG file. To do that, having a QApplication running is compulsory because some components initialized during the execution of QApplication are needed. Otherwise, QPainter.drawText() methods end in a SEGFAULT.

I'm now able to generate text in my SVG file by creating a QSvgWidget object with handles the painting through the paintEvent method.

If I just run the application with the exec_ method, everything works fine. However I'm only interested in generating the SVG, so I don't want to be forced to close the main window with my mouse (I'd like to run my program on a headless server). Here is my base code:

app = QApplication(sys.argv)
drawer = MyDrawerClass()
drawer.show()
app.exec_()

and MyDrawerClass inheritates from QSvgWidget and implements printEvent method which is successfully called when executing the app.

So my question is: Is there a way to run the app a headless way and quit it after everything is rendered? I read a few things on QTimer but I can't find any example which suits my usage.


Solution

  • I assume you're referring to the approach used in your previous question.

    Some painter capabilities require that a QGuiApplication is constructed before using them, and drawing text is amongst them, since it depends on the system's own GUI management (default fonts, DPI, etc). Usually one would use a standard QApplication, but, as reported in the documentation:

    For "non-QWidget based Qt applications, use QGuiApplication instead, as it does not depend on the QtWidgets library".

    This will make the creation of a QGuiApplication faster and lighter.

    Unfortunately, a running display is mandatory for Q[Gui]Applications, so you will not be able to run it on a headless server, unless at least a minimal virtual X server is active. If you can manage to do that, the following example should work fine.

    def createImage(width=400, height=400):
        rect = QRect(0, 0, width, height)
        generator = QSvgGenerator()
        generator.setFileName("test.svg")
        generator.setSize(rect.size())
        generator.setViewBox(rect)
        painter = QPainter(generator)
        painter.fillRect(rect, Qt.black)
        textRect = QRect(0, 0, 200, 200)
        textRect.moveCenter(rect.center())
        painter.setPen(Qt.white)
        painter.setBrush(Qt.darkGray)
        painter.drawRect(textRect)
        painter.drawText(textRect, Qt.AlignCenter, 'test')
        painter.end()
    
    if __name__ == "__main__":
        app = QGuiApplication(sys.argv)
        createImage()
    

    Since there's no need for an event loop (because there's no GUI interaction), you don't have to actually exec_(), and the program will automatically exit as soon as the paint function is returned.