Qt: glClear does not work with overpainting on OpenGL - c++

Problem
When QPainter is created after glClear the latter has no effect.
Description
I use Qt 5.7.1. I get same results with gcc on Linux and vc++ on Windows.
I have the following in a widget derived from QGLWidget:
void CanvasWidget::initializeGL()
{
qglClearColor(m_backgroundColor);
}
V1:
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
QPainter painter(this);
painter.drawLine(0, 0, 1000, 1000);
}
Produces:
V2:
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
}
Produces:
What I want is:
Which can be done with a hack:
void CanvasWidget::paintGL()
{
QPainter painter(this);
qglClearColor(m_backgroundColor);
glClear(GL_COLOR_BUFFER_BIT);
painter.drawLine(0, 0, 1000, 1000);
}
Question
What is going on? Why can't glClean and QPainter work together? Why can't I get it with V1?
Minimal Reproducible Example
main.cpp
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainwindow;
mainwindow.show();
return app.exec();
}
MainWindow.h
#pragma once
#include "CanvasWidget.h"
#include <QMainWindow>
#include <memory>
class MainWindow : public QMainWindow
{
public:
explicit MainWindow(QWidget *parent = 0);
MainWindow(const MainWindow &) = delete;
MainWindow & operator= (const MainWindow &) = delete;
virtual ~MainWindow() = default;
private:
std::unique_ptr<CanvasWidget> m_canvasWidget;
};
MainWindow.cpp
#include "MainWindow.h"
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, m_canvasWidget(new CanvasWidget(parent))
{
setCentralWidget(m_canvasWidget.get());
}
CanvasWidget.h
#pragma once
#include <QGLWidget>
class CanvasWidget : public QGLWidget
{
Q_OBJECT
public:
CanvasWidget(QWidget* parent = 0, const QGLWidget* shareWidget = 0, Qt::WindowFlags f = 0);
private:
virtual void initializeGL() override;
virtual void paintGL() override;
private:
QColor m_backgroundColor;
};
CanvasWidget.cpp
#include "CanvasWidget.h"
#include <QMessageBox>
#include <QWheelEvent>
CanvasWidget::CanvasWidget(
QWidget* parent /*= 0*/,
const QGLWidget* shareWidget /*= 0*/,
Qt::WindowFlags f /*= 0 */)
: QGLWidget(parent, shareWidget, f)
, m_backgroundColor(0, 93, 196)
{}
void CanvasWidget::initializeGL()
{
qglClearColor(m_backgroundColor);
}
void CanvasWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
QPainter painter(this);
painter.drawLine(0, 0, 1000, 1000);
}

Adding setAutoFillBackground(false) in CanvasWidget constructor solves the problem.
All the credit goes to #G.M.

In Qt tutorial about overpainting OpenGL with QPainter it is stated:
When overpainting 2D content onto 3D content, we need to use a
QPainter and make OpenGL calls to achieve the desired effect. Since
QPainter itself uses OpenGL calls when used on a QGLWidget subclass,
we need to preserve the state of various OpenGL stacks when we perform
our own calls
So, looking at your code in V1 I suppose that QPainter sets its own clear color state, and if you don't set your glClearColor explicitly, you will get what QPainter object has set.
And I would also suggest you to use a tool like RenderDoc or gDebugger to trace GL commands of your app to see exactly what happens under the hood.

Related

QT Widget only showing up when using SetFixedSize(...)

I've created a custom Widget: in the designer and promoted a QWidget to a TreeItem (my own). But when I run the code, I can't see the widget. The only way to see it is to use setFixedSize(100, 100). Even resize and setGeometrydon't work.
I want to be able to see my custom widget without giving it a fixed size.
TreeItem.h
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QWidget>
#include <QRect>
class TreeItem : public QWidget
{
Q_OBJECT
public:
explicit TreeItem(QRect rect, QWidget *parent = nullptr);
virtual void paintEvent(QPaintEvent*);
signals:
public slots:
protected:
QRect m_rect;
QFont m_font;
};
#endif // TREEITEM_H
TreeItem.cpp
#include "treeitem.h"
TreeItem::TreeItem(QRect rect, QWidget *parent): QWidget(parent)
{
m_rect = rect;
m_font = QFont("Arrial", 20);
setFixedSize(100, 100); // doesn't show up without this line
}
void TreeItem::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.drawLine(0, 0, 100, 100);
}

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

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.
You only need one painter. The second one wasn't activated, and you don't need it anyway.
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.
Don't call update() from paintEvent(): it's nonsense (literally).
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.
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.
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();
}
This error can happen too when the QPixmap used to create the QPainter(QPixmap) is invalid (if there is no file at such path).
Be sure your QPixmap is correct before painting on it.

qt openGL - draw triangle

So my goal is to (first) draw a triangle with openGL.
my questions:
1) How/when do both of my functions get called? I see that only one gets called. i.e. void MyGLWidget::paintGL. I am confused because as you can see I never call this function, it gets called automatically.I added a widget on my ui which I promoted to MyGLWidget. But when/why/how does it get (not) called?
my code:
myglwidget.cpp
#include "myglwidget.h"
#include <QtWidgets>
#include <QtOpenGL>
#include <GL/glu.h>
MyGLWidget::MyGLWidget(QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
}
void MyGLWidget::initializeGL()
{
glClearColor(1,1,0,1);
qDebug("init"); //<-------never gets printed
}
void MyGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
qDebug("painting"); //<---- does get printed
glColor3f(1,0,0);
glBegin(GL_TRIANGLES);
glVertex3f(-0.5,-0.5,0);
glVertex3f(0.5,-0.5,0);
glVertex3f(0.0,0.5,0);
glEnd();
}
myglwidget.h
#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H
#include <QGLWidget>
class MyGLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit MyGLWidget(QWidget *parent = 0);
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
private:
};
#endif // MYGLWIDGET_H
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.setWindowTitle("OpenGL with Qt DAO");
w.show();
return a.exec();
}
I do not see a class called Widget made by you which is connected to the class MyGlWidget. Maybe I am wrong but shouldn't you have to make a instance of MyGlWidget (Call it's constructor instead of Widget?)
Both functions are called internally by the QGLWidget super class. See QT Docs
In the documentation you also see that these virtual functions are protected. In your code they are public. So you have to change that in order to make it work.

Why QPixmap::scaled() does not work?

I'm writing simple application by using QPixmap and QPainter. In my programm I need to load some images and resize them. I used QPixmap::scaled(), but images does not scaled. What I did wrong?
This is my code:
chesstile.cpp
#include "chesstile.h"
ChessTile::ChessTile(QWidget *parent) :
QLabel(parent)
{
}
void ChessTile::paintEvent(QPaintEvent *)
{
const QString &fileName = "images/white_king.png";
QPixmap bgPixmap(fileName);
bgPixmap.scaled(QSize(64, 64));
QPainter painter(this);
painter.drawPixmap(0, 0, bgPixmap);
}
chesstile.h
#ifndef CHESSTILE_H
#define CHESSTILE_H
#include <QLabel>
#include <QString>
#include <QPainter>
#include <QPixmap>
#include <QSize>
class ChessTile : public QLabel
{
Q_OBJECT
public:
ChessTile(QString fileName,
QString tileColor,
QWidget *parent = 0);
void paintEvent(QPaintEvent *);
private:
signals:
public slots:
};
#endif // CHESSTILE_H
You'll notice from the docs that the QPixmap::scaled member function is const - i.e. it doesn't change the object itself.
The scaled object is returned by that method, it doesn't change the original pixmap.
Try something like:
QPixmap bgPixmap(fileName);
QPixmap scaled = bgPixmap.scaled(QSize(64, 64));
QPainter painter(this);
painter.drawPixmap(0, 0, scaled)

QT drawing without erasing widget

I have a QWidget-derived class. In the constructor I say:
setPalette(QPalette(QColor(250,250,200)));
setAutoFillBackground(true);
Then in my widget's paintEvent() I say:
QPainter painter(this);
painter.drawRect(1,2,3,4);
There is also an updateNow() slot...which just calls update(). How can I make sure my palette doesn't get erased after that update call?
I don't have any problems with the following:
#include <QApplication>
#include <QWidget>
#include <QPalette>
#include <QPaintEvent>
#include <QPainter>
class Test : public QWidget
{
public:
Test()
{
setPalette(QPalette(QColor(250, 250, 200)));
setAutoFillBackground(true);
}
protected:
virtual void paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.drawRect(10, 20, 30, 40);
}
virtual void mousePressEvent(QMouseEvent*)
{
update();
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Test myTest;
myTest.show();
return app.exec();
}
The rectangle draws, and stays after I click, which triggers update. What are you seeing?