[SOLVED] Render hidden QQuickPaintedItem
-
Is it possible to create a canvas (e.g pixmap?) in form of a QQuickPaintedItem and render normal QML Items like
Labelonto it and extract everything as QImage while being hidden from the user?Performace is not a priority.
My use case:
I mux multiple videosources (via OpenCV) together and want to use QML Widgets like
Labelas overlay. Then I want to extract the "view" as some sort of image and encode it again as video. The problem is when the view exceeds the size of my window, thengrabToImage()only clips the visible part.Preferably I would like to create the pixmap with the composition (muxed videos and
Label) at the "to be exported size" hidden from the user and then create a resized view if I would want to render on the screen, otherwise it would be exported at the usual size.My current solution:
I read the videos into
QImages, mux them together viaQPainterin aQQuickPaintedItem, expose this asImageViewerQMLand render everything in anItem. This is exposed to themain.qmlthrough the filename.main.qmlApplicationWindow { id: rootWindow width: Screen.width height: Screen.height visible: true Content { visible: true }content.qmlItem { id: renderer anchors.centerIn: parent anchors.fill: parent ImageViewerQML { id: imageviewer anchors.centerIn: parent } GridView { id: fpsTextExportView cellHeight: fpsTextExportView.height / 5 cellWidth: fpsTextExportView.width anchors { fill: imageviewer; } model: DelegateModel { model: fpsOptionsModel delegate: fpsTextExportDelegate } } Component { id: fpsTextExportDelegate Item { Label { x: 20 y: 15 text: model.displayedText font: displayedTextFont color: model.color visible: { model.displayedTextEnabled && model.fpsOptionsEnabled } } } } -
I found a the following solution.
I simply use the
QImageas my rendering space and export that image. Drawing is still done with aQPainterand theQQuickPaintedItemis only used to display the image for the user.QML Items like a
Labelare drawn viaQPainteronto theQImagesurface viaQImage image; QPainter painter(&image); painter.setPen("#FFFFFF"); painter.setFont(QFont("Helvetica", 15)); int x = 20; int y = 20; painter.drawText(x,y, "Hello World"); painter.done();For more generic use I provide a
paint(QPainter & painter, int x, int y, QSize resolution)interface in C++ for each "rendered object" as alternative compared to aQ_OBJECTwhere the rendering is provided by Qt.This looks like this:
class Renderer : public QObject { ... Q_SLOT void processImage(const QImage image) { _image = image.copy(); QPainter painter; painter.begin(&_image); _draw_fps_text(painter); ... painter.end(); } Q_SLOT void setFPSOptions(const QList<FPSOptions> fpsOptionsList) { _fps_options_list = fpsOptionsList; } void _draw_fps_text(QPainter & painter) { int video_count = _get_video_count(); int image_width = _image.size().width(); int image_height = _image.size().height(); for(int i = 0; i < _fps_options_list.size(); ++i) { int x_padding = static_cast<int>(image_width / 28); int x_step = static_cast<int>(image_width / video_count); int y_step = static_cast<int>(image_height / 15); int x = x_padding + x_step * i; // width int y = y_step; // height _fps_options_list[i].paint(painter, x, y, image.size()); } }My rendering pipeline changed from
VideoCapture -> (frame) -> Processing -> (QImage, data) -> QML Viewer -> (Grab QImage from View) -> Exporter -> (IO)to
VideoCapture -> (frame) -> Processing -> (QImage, data) -> Renderer -> (QImage) -> Exporter -> (QImage) + (IO) -> QML ViewerThe
Exportercurrently pipes theQImageto the QML Viewer as it's currently impossible to let a QML Object run in another thread. The optimal solution would be to duplicate the output of theRendererto bothExporterandQML Viewer, see this thread.