Qt: QWidget::paintEngine: Should no longer be called
Asked Answered
C

4

9

I'm trying to make an app where you can draw with your finger on a canvas.
To achieve this, I'm subclassing QWidget as MFCanvas, registered the class in QML with
qmlRegisterType<>(), implementing the virtual paintEvent(); function, and
drawing on it with a QPainter inside the paintEvent(); function.

The Problem:
Upon construction, the QPainter throws this warning:

QWidget::paintEngine: Should no longer be called

Then, serveral other related warnings are thrown:

QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active

No wonder: the QPainter didn't draw anything...
Also, am i supposed to call paintEvent(); by myself?
Or should it be called every frame by QWidget, and i somehow messed it up?

I searched the web, but all posts i found had either no answer to them, or they where
using something else than QWidget.

My Code:

mfcanvas.cpp:

#include "mfcanvas.h"
#include <QDebug>
#include <QPainter>
#include <QVector2D>
#include <QList>

MFCanvas::MFCanvas(QWidget *parent) : QWidget(parent)
{
    paths = new QList<QList<QVector2D>*>();
    current = NULL;
    QWidget::resize(100, 100);
}

MFCanvas::~MFCanvas()
{
    delete paths;
}

void MFCanvas::paintEvent(QPaintEvent *)
{
    if(current!=NULL){
        if(current->length() > 1){
            QPainter painter(this);
            painter.setPen(Qt::black);
            for(int i = 1; i < current->length(); i++){
                painter.drawLine(current->at(i-1).x(), current->at(i-1).y(), current->at(i).x(), current->at(i).y());
            }
        }
    }
}

void MFCanvas::pressed(float x, float y)
{
    if(current==NULL){
        qDebug() << "null:"<<current;
        current = new QList<QVector2D>();
        current->append(QVector2D(x, y));
    }else{
        qDebug() << "current:"<<current;
    }
    paintEvent(NULL);
}

void MFCanvas::update(float x, float y)
{
    current->append(QVector2D(x, y));
}

void MFCanvas::resize(int w, int h)
{
    QWidget::resize(w, h);
}

main.cpp:

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QSurfaceFormat>
#include "creator.h"
#include "mfcanvas.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    qmlRegisterType<MFCanvas>("com.cpp.mfcanvas", 1, 0, "MFCanvas");

    QQmlApplicationEngine engine;

    QQmlComponent *component = new QQmlComponent(&engine);
    QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));

    Creator creator(component);
    QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)), &creator, SLOT(create(QQmlComponent::Status)));

    component->loadUrl(QUrl("qrc:///main.qml"));

    int rv;

    rv = app.exec();
    delete component;
    return rv;
}

creator.cpp:

#include "creator.h"
#include <QQuickWindow>
#include <QDebug>

Creator::Creator(QQmlComponent *component)
{
    this->component = component;
}

void Creator::create(QQmlComponent::Status status)
{
    if(status == QQmlComponent::Ready){
        QObject *topLevel = component->create();
        QQuickWindow::setDefaultAlphaBuffer(true);
        QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);

        QSurfaceFormat surfaceFormat = window->requestedFormat();
        window->setFormat(surfaceFormat);
        window->show();
    }
}

main.qml: (the important part)

import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import com.cpp.mfcanvas 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("MFCanvas")

    onSceneGraphInitialized: {
        drawMenu.visible = true;
        lineWidth.visible = true;
        colorMenu.visible = true;
        drawMenu.visible = false;
        lineWidth.visible = false;
        colorMenu.visible = false;
    }

    Rectangle {
        id: main
        anchors.fill: parent

        property real toolsH: 15
        property real iconW: 25

        property real menuH: 8
        property real menuW: 16

        property real dpi: (Screen.logicalPixelDensity == undefined ? 6 : Screen.logicalPixelDensity) * 1.5

        property color choosenColor: Qt.hsla(hue.value, saturation.value, luminance.value, 1)

        Text {
            anchors.centerIn: parent
            font.pointSize: 60
            text: "MFCanvas"
        }

        MFCanvas {
            id: canvas
            Component.onCompleted: {
                canvas.resize(main.width, main.height);
            }
        }
    //...
    }
}

Tell me if you need any additional information.
Thank you in advance! =)

Crypto answered 11/9, 2014 at 7:15 Comment(0)
C
0

I have found a simple solution myself:

Instead of deriving from QWidget, derive from QQuickPaintedItem. QQuickPaintedItem is a class that was made exactly for what i need: Painting on a QML-Element using a QPainter. Here is the Code (Narrowed down to the essential part):
mfcanvas.h:

class MFCanvas : public QQuickPaintedItem
{
    Q_OBJECT
public:
    explicit MFCanvas(QQuickItem *parent = 0);
    ~MFCanvas();

protected:
    void paint(QPainter *painter);

mfcanvas.cpp:

void MFCanvas::paint(QPainter *painter)
{
    painter->translate(-translation.x(), -translation.y());
    //...
}

As you can see, a simple paint() function is provided which hands over a pointer to a QPainter, ready to use. =)

Crypto answered 26/9, 2014 at 7:47 Comment(0)
A
9

This is nicely explained here:

https://forum.qt.io/topic/64693

In short: do not try to paint from the input event handler directly, but overload the paintEvent method in your widget instead and create the QPainter there. Use the input event exclusively to modify the internal data model and use QPainter in paintEvent to display it, on the output path.

Acetic answered 27/2, 2017 at 12:24 Comment(0)
L
3

In your mfcanvas.cpp, void MFCanvas::pressed(float x, float y) function, the line

paintEvent(NULL);

seems to be disturbing. Tried it in a similar code - I get the same error.

Proposed solution: using this->repaint() or this->update() instead of paintEvent(NULL) to repaint a widget seems to be more appropriate.

Possible explanation: looks like paintEvent() shouldn't be called this straightforward (like paintEvent() is called when paint() function is called). As far as I understand from the QPainter doc, the QPainter works together with the QPaintDevice and the QPaintEngine, these three form the basis for painting. The error QWidget::paintEngine: Should no longer be called puts it quite straight. The lines

QPainter::begin: Paint device returned engine == 0, type: 1 
QPainter::setPen: Painter not active

probably indicate that there's no QPaintEngine provided by this painter's QPaintDevice (like QPaintDevice::paintEngine). One can assume that this QPaintEngine is generated or otherwise called to existence by the paint device itself, for example, when the paint() function is called on a widget.

Lanfranc answered 23/9, 2014 at 22:20 Comment(0)
C
0

I have found a simple solution myself:

Instead of deriving from QWidget, derive from QQuickPaintedItem. QQuickPaintedItem is a class that was made exactly for what i need: Painting on a QML-Element using a QPainter. Here is the Code (Narrowed down to the essential part):
mfcanvas.h:

class MFCanvas : public QQuickPaintedItem
{
    Q_OBJECT
public:
    explicit MFCanvas(QQuickItem *parent = 0);
    ~MFCanvas();

protected:
    void paint(QPainter *painter);

mfcanvas.cpp:

void MFCanvas::paint(QPainter *painter)
{
    painter->translate(-translation.x(), -translation.y());
    //...
}

As you can see, a simple paint() function is provided which hands over a pointer to a QPainter, ready to use. =)

Crypto answered 26/9, 2014 at 7:47 Comment(0)
K
0

Maybe you shouldn't call "paintEvent()" actively, it will run automatically, you can also ignore this reminder. I have also encountered this, just delete "paintEvent();"

Knipe answered 21/7, 2024 at 6:0 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.