"QPainter::drawRects: Painter not active " error C++/QT

N.D. picture N.D. · Aug 5, 2016 · Viewed 16.6k times · Source

I'm a beginner at Qt and c++ and I wanted to see how to use a QPainter and events in Qt but I got stuck because of an error message during the execution, my original code:

the main.cpp

#include "customwidget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QScopedPointer<QWidget> widget(new customWidget());
    widget->resize(240, 120);
    widget->show();

    return a.exec();
}

and the header:

#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H

#include <QWidget>
#include <QMouseEvent>
#include <QPoint>
#include <QPainter>

class customWidget : public QWidget
{
    Q_OBJECT
public:
    explicit customWidget(QWidget *parent = 0);
    void paintEvent(QPaintEvent *);
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);

private:
    QPoint m_mousePos;
    QRect m_r2;
signals:

    void needToRepaint();
public slots:
};

#endif // CUSTOMWIDGET_H

and the .cpp:

#include "customwidget.h"

customWidget::customWidget(QWidget *parent) : QWidget(parent)
{
    QRect m_r2;
    QPoint m_mousePos;

    QObject::connect(this, SIGNAL(needToRepaint()), this, SLOT(repaint()));
}

void customWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

// ############ First Rectangle ****************************************

    QRect r1 = rect().adjusted(10, 10, -10, -10);
    painter.setPen(QColor("#FFFFFF"));
    painter.drawRect(r1);

// ############ Seconde Rectangle ****************************************

    QRect r2(QPoint(0, 0), QSize(100, 100));


    m_r2.moveCenter(m_mousePos);

    QPainter painter2;
    QPen pen;
    painter2.setPen(QColor("#000000"));
    pen.setWidth(3);
    painter2.setPen(pen);
    painter2.drawRect(m_r2);
    update();



}

void customWidget::mouseMoveEvent(QMouseEvent *event)
{

    m_mousePos = event->pos();

    emit needToRepaint();
}

I tried to search it on the web and saw that it's because the QPainter isn't located in the paintEvent but it's not the case in my code, thanks for your help.

Answer

  1. You only need one painter. The second one wasn't activated, and you don't need it anyway.

  2. Don't ever call repaint() unless you somehow absolutely need the painting to be done before repaint() returns (that's what happens!). If you keep the event loop running properly, you won't ever need that.

  3. Don't call update() from paintEvent(): it's nonsense (literally).

  4. When you wish to repaint the widget, call update(): it schedules an update from the event loop. Multiple outstanding updates are coalesced to keep the event loop functional and prevent event storms.

  5. Let the compiler generate even more memory management code for you. You've done the first step by using smart pointers - that's good. Now do the second one: hold the instance of CustomWidget by value. It doesn't have to be explicitly dynamically allocated. C++ is not C, you can leverage values.

  6. In a simple test case, you don't want three files. Your code should fit in as few lines as possible, in a single main.cpp. If you need to moc the file due to Q_OBJECT macros, add #include "main.moc" at the end, and re-run qmake on the project to take notice of it.

This is how such a test case should look, after fixing the problems. Remember: it's a test case, not a 100kLOC project. You don't need nor want the meager 35 lines of code spread across three files. Moreover, by spreading out the code you're making it harder for yourself to comprehend.

Even in big projects, unless you can show significant build time improvements if doing the contrary, you can have plenty of small classes implemented Java-style completely in the header files. That's about the only Java-style-anything that belongs in C++.

// https://github.com/KubaO/stackoverflown/tree/master/questions/simple-paint-38796140
#include <QtWidgets>

class CustomWidget : public QWidget
{
   QPoint m_mousePos;
public:
   explicit CustomWidget(QWidget *parent = nullptr) : QWidget{parent} {}
   void paintEvent(QPaintEvent *) override;
   void mouseMoveEvent(QMouseEvent *event) override {
      m_mousePos = event->pos();
      update();
   }
};

void CustomWidget::paintEvent(QPaintEvent *)
{
   QPainter painter(this);

   auto r1 = rect().adjusted(10, 10, -10, -10);
   painter.setPen(Qt::white);
   painter.drawRect(r1);

   auto r2 = QRect{QPoint(0, 0), QSize(100, 100)};
   r2.moveCenter(m_mousePos);
   painter.setPen(QPen{Qt::black, 3, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin});
   painter.drawRect(r2);
}

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   CustomWidget w;
   w.show();
   return app.exec();
}