psykhi
psykhi

Reputation: 2981

How to grab a qwidget /render it in a pixmap everytime it needs to be repainted?

I have a QWidget that I don't want to show on screen. Instead I want to get a pixmap of the widget every time it is repainted, in order to send it to another part of the application I'm working on. The API provides two ways of doing this: QPixmap::grabWidget and QWidget::render

The thing is that both these methods will fire a new paintEvent of the widget, creating a recursive repainting of the widget, so they should not be used inside the paintEvent method.

So how could I get a pixmap reprensenting the widget everytime it is repainted?

Upvotes: 4

Views: 5219

Answers (3)

NoobNoob
NoobNoob

Reputation: 104

I know that this question is old, but I want to add some clarity on the solution, posting some actual working code for future reference.

sourceWidget is the widget that we want to render (aka capture the content as image)

sourceFrame is the parent of sourceWidget

renderLabel is the target in which we want to render the sourceWidget

This is the .h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFrame>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MyEventFilter : public QObject {

Q_OBJECT

public:
    MyEventFilter(QObject* parent = nullptr);

protected:
    bool eventFilter(QObject* obj, QEvent* event) override;
};

class MainWindow : public QMainWindow {

    Q_OBJECT

public:
    MainWindow(QWidget* parent = nullptr);
    ~MainWindow();

     QFrame* sourceFrame;
     QWidget* sourceWidget;
     QLabel* renderLabel;

private:
    Ui::MainWindow* ui;

    MyEventFilter* myEventFilter;
};
#endif // MAINWINDOW_H

This is the .cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QDebug>
#include <QPaintEvent>

MyEventFilter::MyEventFilter(QObject *parent) : QObject(parent) {

     Q_UNUSED(parent);
}

bool MyEventFilter::eventFilter(QObject* obj, QEvent* event) {

    if (event->type() == QEvent::Paint) {
        QPaintEvent* paintEvent = static_cast<QPaintEvent*>(event);
        qDebug() << "Paint Event on" << obj << parent();

        if (qobject_cast<MainWindow*>(parent())) {
        //QImage image = qobject_cast<MainWindow*>(parent())->sourceWidget->grab().toImage();
        QPixmap pixmap = qobject_cast<MainWindow*>(parent())->sourceWidget->grab();
        qobject_cast<MainWindow*>(parent())->renderLabel->setPixmap(pixmap);
    }

    Q_UNUSED(paintEvent);
    return true;

} else {

        // standard event processing
        return QObject::eventFilter(obj, event);
    }
}

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {

    ui->setupUi(this);

    sourceFrame = ui->sourceFrame;
    sourceWidget = ui->sourceWidget;
    renderLabel = ui->renderLabel;

    myEventFilter = nullptr;
    myEventFilter = new MyEventFilter(this);
    sourceFrame->installEventFilter(myEventFilter);
}

MainWindow::~MainWindow() {

    delete ui;
}

Upvotes: 0

Dmitry Sazonov
Dmitry Sazonov

Reputation: 8994

As another way - you can do next check:

void paintEvent(QPaintEvent*)
{
  QPainter painter(this);
  // If painter redirection was turned on, the painter will *not* paint on `this`!
  // Painting code
  //...
  if ( this == qobject_cast< QWidget * >( painter.device() ) )
  {
    // Do grabbing
  }
}

paintEvent is called both for painting on widget and on grabbing. painter->device() will return not null, but instance of QWidget * object only when current painting is on widget. In case of drawing on QPixmap - it will return QPixmap *. So you need to call grabbing code only when painting is performed only on real widget.

Upvotes: 5

psykhi
psykhi

Reputation: 2981

Well I found a solution. I took all the content of the widget that I wanted to grab, and placed it inside a Frame. Then I grabbed the Frame. It's not really elegant but it works.

Upvotes: 0

Related Questions