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?
Related
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.
I would like share a string between two instances of QWidget.
In main.cpp, two objects are instantiated and shown like this:
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w1,w2; //Derived from QWidget
w1.show();
w2.show();
return a.exec();
}
I would introduce SharedState class:
// shared_state.h
#ifndef SHARED_STATE_HPP
#define SHARED_STATE_HPP
#include <QObject>
class SharedState : public QObject
{
Q_OBJECT
public:
SharedState(QString initialValue = "")
: currentValue(initialValue)
{}
QString getCurrentValue()
{
return currentValue;
}
public slots:
void setValue(QString newValue)
{
if(currentValue != newValue)
{
currentValue = newValue;
emit valueChanged(currentValue);
}
}
signals:
void valueChanged(QString);
private:
QString currentValue;
};
#endif // SHARED_STATE_HPP
Now I would provide reference to SharedState in Dialog's constructor,
// dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QWidget>
#include "shared_state.h"
namespace Ui {
class Dialog;
}
class Dialog : public QWidget
{
Q_OBJECT
public:
explicit Dialog(SharedState& state, QWidget *parent = 0);
~Dialog();
private slots:
void handleTextEdited(const QString&);
public slots:
void handleInternalStateChanged(QString);
private:
Ui::Dialog *ui;
SharedState& state;
};
#endif // DIALOG_H
You may have noticed that I have added two slots, one to handle the case when the text is manually edited and one when shared state will inform us that we are out of date.
Now in Dialog's constructor I had to set initial value to textEdit, and connect signals to slots.
// dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(SharedState& state, QWidget *parent) :
QWidget(parent),
ui(new Ui::Dialog),
state(state)
{
ui->setupUi(this);
ui->textEdit->setText(state.getCurrentValue());
QObject::connect(ui->textEdit, SIGNAL(textEdited(QString)),
this, SLOT(handleTextEdited(QString)));
QObject::connect(&state, SIGNAL(valueChanged(QString)),
this, SLOT(handleInternalStateChanged(QString)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::handleTextEdited(const QString& newText)
{
state.setValue(newText);
}
void Dialog::handleInternalStateChanged(QString newState)
{
ui->textEdit->setText(newState);
}
Now the change in the main function:
// main.cpp
#include "dialog.h"
#include "shared_state.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SharedState state("Initial Value");
Dialog w1(state), w2(state);
w1.show();
w2.show();
return a.exec();
}
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.
I'm a beginner in Qt when I run the following code I got these errors:
no void MainWindow::mousePressEvent(QMouseEvent *f) member function declared in class 'mainwindow'.
no void void MainWindow::paintEvent(QPaintEvent *e) member function declared in class 'mainwindow'.
The code is written in main.cpp file and I didn't write anything in mainwindow.cpp or mainwindow.h
The Qt code:
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QEvent>
#include <QMouseEvent>
#include <QPainter>
void MainWindow::mousePressEvent(QMouseEvent *f)
{
QPoint point=f->pos();
int y=1;
update();
}
void MainWindow::paintEvent(QPaintEvent *e)
{
int y;
QPoint point;
QPainter painter(this);
QPen linepen(Qt::red);
linepen.setCapStyle(Qt::RoundCap);
linepen.setWidth(30);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setPen(linepen);
if(y==1)
painter.drawPoint(point);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
In header should be:
protected:
void mousePressEvent(QMouseEvent *f);
void paintEvent(QPaintEvent *e);
And includes:
#include <QMouseEvent>
#include <QPaintEvent>
Also you should write your code in mainwindow.cpp(paintEvent, and another member functions). If you will have many classes, then your main.cpp can be very hard-readable.
i read other questions about the same issue but wasn't able to solve mine. When i run my code, the program starts, send QObject::connect: No such slot mywindow::changerLargeur(int) and then fails.
thanks you for your help
h file
#ifndef MYWINDOW_H
#define MYWINDOW_H
#include <QWidget>
#include <QSlider>
#include <QLCDNumber>
class mywindow : public QWidget
{
Q_OBJECT
public:
mywindow();
public slots:
void changerlargeur(int largeur);
private:
QSlider *glisseur;
QLCDNumber *afficheur;
};
#endif // MYWINDOW_H
cpp file
#include "mywindow.h"
mywindow::mywindow(): QWidget()
{
setFixedSize(200, 100);
glisseur = new QSlider(Qt::Horizontal,this);
glisseur->setRange(100,900);
glisseur->setGeometry(10, 60, 150, 20);
QObject::connect(glisseur, SIGNAL(valueChanged(int)), this, SLOT(changerLargeur(int)));
QObject::connect(glisseur, SIGNAL(valueChanged(int)), afficheur, SLOT(display(int)));
afficheur = new QLCDNumber(this);
afficheur->setSegmentStyle(QLCDNumber::Flat);
}
void mywindow::changerlargeur(intlargeur)
{
setFixedSize(largeur, 100);
}
main file
#include <QApplication>
#include "mywindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
mywindow fenetrep;
fenetrep.show();
return app.exec();
}
Like most other things in C++, Qt's signals and slots are case sensitive. So this won't work:
void changerlargeur(int largeur);
vs.
SLOT(changerLargeur(int))
You need to decide whether you want the L to be upper or lower case and stick with it.
Another issue is this:
QObject::connect(glisseur, SIGNAL(valueChanged(int)), afficheur, SLOT(display(int)));
afficheur = new QLCDNumber(this);
You can't connect to a slot on an object that doesn't exist yet.