Frame accurate drawing (bufferswap) - c++

How to bufferswap between two textures, to achieve drawing on the next VBL?
Currently I'm using two QPixmap which I set to a QLabel. This results in a unstable delay between 18ms and 45ms measured against parallelport, using a 85Hz CRT monitor (no buffers there which add any delays).
My goal would be to issue a flip command which leads to bufferswap on the next VBL (Vertical Blanking Interrupt).
What I have tried now:
I setup a full screen window in the main.cpp (different problem: can't open a window at runtime) and initialize there two QPixmaps.
With a signal created by a timer I trigger the change:
myVisualStimuli = new VisualStimuli(0);
//Start Timer for Stimulus Presenation
StimulusThread = new QThread(this);
StimulusTimer = new QTimer(0);
StimulusTimer->setInterval(500);
StimulusTimer->moveToThread(StimulusThread);
myVisualStimuli->moveToThread(StimulusThread);
myVisualStimuli->connect(StimulusTimer, SIGNAL(timeout()), SLOT(SetStimulus()), Qt::DirectConnection);
this->connect(StimulusTimer, SIGNAL(timeout()), SLOT(SetStimulus()), Qt::DirectConnection);
// Make sure the timer gets started from m_thread.
StimulusTimer->connect(StimulusThread, SIGNAL(started()), SLOT(start()));
StimulusThread->start();
And the Drawing: Header / Code
#ifndef VISUALSTIMULI_H
#define VISUALSTIMULI_H
#include <QtCore>
#include <QObject>
#include <QThread>
#include <QLabel>
class VisualStimuli: public QThread
{
Q_OBJECT
public:
explicit VisualStimuli(QObject *parent = 0);
int Setup();
bool StimulusToggle = true;
QRect *screenres;
QLabel* StimulusWindow;
QPixmap *pix1;
QPixmap *pix2;
public slots:
void SetStimulus();
private:
protected:
void run();
};
#endif // VISUALSTIMULI_H
#include "visualstimuli.h"
VisualStimuli::VisualStimuli(QObject *parent) : QThread(parent)
{
}
int VisualStimuli::Setup()
{
return 0;
}
void VisualStimuli::SetStimulus()
{
start();
}
void VisualStimuli::run()
{
if(StimulusToggle){
this->StimulusWindow->setPixmap(*pix1);
StimulusToggle=false;
}else{
this->StimulusWindow->setPixmap(*pix2);
StimulusToggle=true;
}
}
Due to Hardware I'm bound to Windows 10 64Bit, so I 'don’t mind to use any platform dependent code/library for the stimulus presentation.

Related

Qt/C++ How can I disconnect a QProgressDialog::canceled signal to its QProgressDialog::cancel slot?

I have a QProgressDialog and I would like to override its cancel() slot to change its behavior.
Instead of closing the dialog, I would like to do some other operations and then close the dialog after a QThread to finish before closing the dialog.
I tried to disconnect the canceled/cancel signal/slot couples and the reconnect with the new behavior but it does not seem to change much.
As as soon as I click on the cancel button, the progress dialog gets closed first and then my lambda get executed anyway.
Qobject::disconnect(m_progressdialog, &QProgressDialog::canceled, m_progressdialog, &QProgressDialog::cancel);
Qobject::connect(m_progressdialog, &QProgressDialog::canceled, [](){
// continue displaying the dialog as an occupation bar
m_progressdialog->setValue(0);
// do some other things
// a lot of code
// ...
// only later close the dialog
m_progressdialog->close();
});
Is there a way to do this correctly?
I don't know your whole code, but according to the documentation, the idea is more or less the same you're saying: a slot to connect the signal QProgressDialog::canceled().
The following code is just an example but it's working. In this case, instead of using the own Qt property wasCanceled, it is used a boolean to control when to stop and cancel the QProgressDialog.
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
class QProgressDialog;
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
~Dialog();
private slots:
void on_pushButton_clicked();
void my_custom_cancel();
private:
Ui::Dialog *ui;
QProgressDialog *progress;
int numTasks = 100000;
bool canceled = false;
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QProgressDialog>
#include <QThread>
#include <QDebug>
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Dialog)
{
progress = new QProgressDialog("Task in progress...", "Cancel", 0, numTasks);
connect(progress, SIGNAL(canceled()), this, SLOT(my_custom_cancel()));
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_clicked()
{
progress->open();
for (int i = 0; i < numTasks; i++) {
progress->setValue(i);
QThread::usleep(100);
if (canceled)
break;
}
progress->setValue(numTasks);
}
void Dialog::my_custom_cancel()
{
qDebug() << "do something";
canceled = true;
}

Qt3D Crashing when removing frame graph

I'm trying to create an application using Qt3D that where I can create multiple view windows on the same scene. I started with the code from the Qt3DWindow and the Simple C++ Example and started moving things around. What I figured is that each view window would define its own frame graph (just using a simple QForwardRenderer for now) and camera and then I would add each window's frame graph to the main frame graph in my scene.
Everything appears to be working fine as I create multiple windows, but when I close the windows and start removing frame graphs, the application crashes. It's crashing on a background thread somewhere down in the Qt3DCore or Qt3DRender module and I can't get to the source code. As I understand it I should be able to modify the frame graph dynamically at run time, but is that not thread safe? Are you expected to wholesale replace one frame graph with another as opposed to modifying the active frame graph like I'm doing?
--- Edit ---
I did a little more testing and if I delay destroying the QWindow (i.e., the surface that it's trying to render to) a bit after removing its frame graph from the parent frame graph, I don't get the crash. I do however get some warnings on the console that say:
Qt3D.Renderer.Backend: bool __cdecl Qt3DRender::Render::GraphicsContext::makeCurrent(class QSurface *) makeCurrent failed
My guess is it's a threading issue, that the backend is still trying to use the QSurface to render to after it has been destroyed on the main thread. I don't really like my solution (I just used a single shot timer to delay destroying the window by 1 second), but it's better than crashing.
RenderWindow.h
#ifndef RENDERWINDOW_H
#define RENDERWINDOW_H
#include <QWindow>
#include <Qt3DCore>
#include <Qt3DRender>
#include <Qt3DInput>
#include <Qt3DExtras/QForwardRenderer>
class RenderWindow : public QWindow
{
public:
RenderWindow(QScreen* screen = nullptr);
~RenderWindow();
Qt3DRender::QCamera* camera() const;
Qt3DRender::QFrameGraphNode* frameGraph() const;
protected:
void resizeEvent(QResizeEvent *) override;
private:
// Rendering
Qt3DRender::QFrameGraphNode* mpFrameGraph;
Qt3DRender::QCamera* mpCamera;
static bool msFormatDefined;
};
#endif // RENDERWINDOW_H
RenderWindow.cpp
#include "renderwindow.h"
#include <QDebug>
bool RenderWindow::msFormatDefined = false;
namespace
{
// Different clear colors so that it's obvious each window is using a
// different camera and frame graph.
static QColor sClearColors[] = {
Qt::darkBlue,
Qt::blue,
Qt::darkCyan,
Qt::cyan
};
static int sViewCount = 0;
}
RenderWindow::RenderWindow(QScreen* screen)
: QWindow(screen)
, mpFrameGraph(nullptr)
, mpCamera(new Qt3DRender::QCamera)
{
setSurfaceType(QWindow::OpenGLSurface);
// Set the default surface format once
if (!msFormatDefined)
{
QSurfaceFormat format;
format.setVersion(4, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setDepthBufferSize(24);
format.setSamples(4);
format.setStencilBufferSize(8);
setFormat(format);
QSurfaceFormat::setDefaultFormat(format);
msFormatDefined = true;
}
// Camera
mpCamera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
mpCamera->setPosition(QVector3D(0, 0, 40.0f));
mpCamera->setViewCenter(QVector3D(0, 0, 0));
// Frame Graph (using forward renderer for now)
Qt3DExtras::QForwardRenderer* renderer = new Qt3DExtras::QForwardRenderer;
renderer->setCamera(mpCamera);
renderer->setSurface(this);
renderer->setClearColor(sClearColors[sViewCount++ % 4]);
mpFrameGraph = renderer;
}
RenderWindow::~RenderWindow()
{
qDebug() << "start ~RenderWindow";
// Unparent objects. Probably not necessary but it makes me feel
// good inside.
mpFrameGraph->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
mpCamera->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
delete mpFrameGraph;
delete mpCamera;
qDebug() << "end ~RenderWindow";
}
Qt3DRender::QCamera* RenderWindow::camera() const
{
return mpCamera;
}
Qt3DRender::QFrameGraphNode* RenderWindow::frameGraph() const
{
return mpFrameGraph;
}
void RenderWindow::resizeEvent(QResizeEvent *)
{
mpCamera->setAspectRatio((float)width()/(float)height());
}
Scene.h
#ifndef SCENE_H
#define SCENE_H
#include <Qt3DCore/QEntity>
#include <Qt3DInput/QInputAspect>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QRenderAspect>
#include <Qt3DRender/QRenderSettings>
class RenderWindow;
class Scene
{
public:
Scene();
~Scene();
Qt3DCore::QEntityPtr rootNode() const;
void addView(RenderWindow* window);
private:
void setupScene();
private:
Qt3DCore::QEntityPtr mpRoot;
// Frame Graph
Qt3DRender::QFrameGraphNode* mpFrameGraph;
Qt3DRender::QRenderSettings* mpRenderSettings;
// Aspects
Qt3DCore::QAspectEngine* mpEngine;
Qt3DRender::QRenderAspect* mpRenderAspect;
Qt3DInput::QInputAspect* mpInputAspect;
};
#endif // SCENE_H
Scene.cpp
#include "scene.h"
#include <QDebug>
#include <QPropertyAnimation>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QCylinderMesh>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QTorusMesh>
#include "orbittransformcontroller.h"
#include "RenderWindow.h"
Scene::Scene()
: mpRoot(nullptr)
, mpFrameGraph(new Qt3DRender::QFrameGraphNode)
, mpRenderSettings(new Qt3DRender::QRenderSettings)
, mpEngine(new Qt3DCore::QAspectEngine)
, mpRenderAspect(new Qt3DRender::QRenderAspect)
, mpInputAspect(new Qt3DInput::QInputAspect)
{
mpEngine->registerAspect(mpRenderAspect);
mpRenderSettings->setActiveFrameGraph(mpFrameGraph);
setupScene();
mpRoot->addComponent(mpRenderSettings);
mpEngine->setRootEntity(mpRoot);
}
Scene::~Scene()
{
qDebug() << "start ~Scene";
mpEngine->setRootEntity(Qt3DCore::QEntityPtr());
mpRoot.clear();
delete mpEngine;
// mpRenderSettings and mpFrameGraph are children of the
// root node and are automatically destroyed when it is.
qDebug() << "end ~Scene";
}
Qt3DCore::QEntityPtr Scene::rootNode() const
{
return mpRoot;
}
void Scene::addView(RenderWindow* window)
{
// Add the window's frame graph to the main frame graph
if (window->frameGraph())
{
window->frameGraph()->setParent(mpFrameGraph);
}
}
void Scene::setupScene()
{
mpRoot.reset(new Qt3DCore::QEntity);
Qt3DCore::QEntity* entity = new Qt3DCore::QEntity;
entity->setParent(mpRoot.data());
// Create the material
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(entity);
material->setAmbient(Qt::black);
material->setDiffuse(QColor(196, 196, 32));
material->setSpecular(Qt::white);
// Torrus
Qt3DCore::QEntity *torusEntity = new Qt3DCore::QEntity(entity);
Qt3DExtras::QTorusMesh *torusMesh = new Qt3DExtras::QTorusMesh;
torusMesh->setRadius(5);
torusMesh->setMinorRadius(1);
torusMesh->setRings(100);
torusMesh->setSlices(20);
Qt3DCore::QTransform *torusTransform = new Qt3DCore::QTransform;
torusTransform->setScale3D(QVector3D(1.5, 1, 0.5));
torusTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), -45.0f));
torusEntity->addComponent(torusMesh);
torusEntity->addComponent(torusTransform);
torusEntity->addComponent(material);
// Sphere
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(entity);
Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh;
sphereMesh->setRadius(3);
Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform;
/*OrbitTransformController *controller = new OrbitTransformController(sphereTransform);
controller->setTarget(sphereTransform);
controller->setRadius(20.0f);
QPropertyAnimation *sphereRotateTransformAnimation = new QPropertyAnimation(sphereTransform);
sphereRotateTransformAnimation->setTargetObject(controller);
sphereRotateTransformAnimation->setPropertyName("angle");
sphereRotateTransformAnimation->setStartValue(QVariant::fromValue(0));
sphereRotateTransformAnimation->setEndValue(QVariant::fromValue(360));
sphereRotateTransformAnimation->setDuration(10000);
sphereRotateTransformAnimation->setLoopCount(-1);
sphereRotateTransformAnimation->start();*/
sphereEntity->addComponent(sphereMesh);
sphereEntity->addComponent(sphereTransform);
sphereEntity->addComponent(material);
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "scene.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void createWindow();
private:
Ui::MainWindow *ui;
Scene* scene;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include "ui_mainwindow.h"
#include "renderwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
scene(new Scene())
{
ui->setupUi(this);
connect(ui->createButton, &QPushButton::clicked, this, &MainWindow::createWindow);
}
MainWindow::~MainWindow()
{
qDebug() << "~MainWindow";
delete scene;
delete ui;
}
void MainWindow::createWindow()
{
RenderWindow* window = new RenderWindow();
scene->addView(window);
window->resize(640, 480);
window->show();
QVector3D pos[] = {
QVector3D(0, 0, 40),
QVector3D(0, 25, -30),
QVector3D(-20, -20, -20),
QVector3D(40, 0, 0)
};
static int count = 0;
window->camera()->setPosition(pos[count++%4]);
window->camera()->setViewCenter(QVector3D(0, 0, 0));
// Delete the window when it is closed.
connect(window, &QWindow::visibilityChanged, this, [=](bool on)
{
if (!on)
window->deleteLater();
});
}
I've thoroughly tested your example and draw the same conclusions. When you destroy the window too quickly, the application crashes, probably because Qt3D still tries to issue some OpenGL commands to the underlying QSurface. I think this is a bug that should be reported.
A 'cleaner' work-around of this problem could be to track the generated 3d windows in the main window. You could maintain a list of pointers to all windows that where generated (and probably closed by the user at the some point). The windows are finally destroyed in the destructor of the main window.
I had exactly the same problem. I was creating a class derived from Qt3DWindow in a dialog box so the user could preview the effects of the choices made, and the program crashed when the dialog exited. In fact on Windows this crash causes the debugger and Qt Creator to crash too!
I tried working around this in a variety of ways and some helped because it turns out that it is a threading issue that was fixed on the 23rd October:
https://github.com/qt/qt3d/commit/3314694004b825263c9b9f2d69bd85da806ccbbc
The fix is now to apply the patch, and recompile Qt. 5.11.3 (or perhaps 5.12) will be out quite soon I expect but this bug is a killer if you are using Qt3D in dialogs.

C++ Timer difficulties

I'm having issues implementing timing. What i want to do is run this:
//relay 1 power cycle
void MainWindow::relay1PCycle()
{
relay1High();
while (true){
QTimer::singleShot(750, this, SLOT(relay1Low()));
QTimer::singleShot(750, this, SLOT(relay1High()));
}
}
as while a button is pressed.
//power cycle button
void MainWindow::on_pushButton_3_toggled(bool checked)
{
if (checked){
//run relay1PCycle as a thread.
}
else{
//Terminate relay1PCycle thread.
}
}
however, so far everything i've tried so far has been a failure.
What's the correct way to go about this?
Thanks
The task they want to implement is not blocking, so it is not necessary to use threads, only a timer with an adequate logic. To make it simple we will assign that task to the next class:
relaycontroller.h
#ifndef RELAYCONTROLLER_H
#define RELAYCONTROLLER_H
#include <QObject>
#include <QTimer>
#include <QDebug>
class RelayController : public QObject
{
Q_OBJECT
public:
explicit RelayController(QObject *parent = 0):QObject(parent){
timer = new QTimer(this);
state = false;
timer->setInterval(750);
//old style
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
//new style
//connect(timer, &QTimer::timeout, this, &RelayController::onTimeout);
}
void start(){
qDebug()<<"timer";
timer->start();
}
void stop(){
timer->stop();
}
void relayLow(){
//code here: relay off
qDebug()<<"low";
}
void relayHigh(){
//code here: relay on
qDebug()<<"high";
}
private slots:
void onTimeout(){
if(state)
relayHigh();
else
relayLow();
state = !state;
}
private:
QTimer *timer;
bool state;
};
#endif // RELAYCONTROLLER_H
Then an object is created and used in the slot pressed:
mainwindow.h
private:
RelayController *controller;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
controller = new RelayController(this);
ui->pushButton_3->setCheckable(true);
}
void MainWindow::on_pushButton_toggled(bool checked)
{
if(checked)
controller->start();
else
controller->stop();
}

Trigger different event on QTimer reset

I am building a little program that shows traffic light images using QTimer. So i setup my timer and everything works good. But I cant figure out, how can I get the Robot Lights to ->show() and ->hide() each time the timer interval is reached. I can have this all wrong, im still learning so please advise.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void timer();
QTimer *mytimer;
private:
Ui::MainWindow *ui;
int timerValue;
private slots:
void showGreen();
void showYellow();
void showRed();
void on_startButton_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->green->setVisible(false);
ui->yellow->setVisible(false);
ui->red->setVisible(false);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timer(){
ui->green->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showYellow()));
mytimer->start(timerValue = (ui->spinBox->value())*1000);
}
void MainWindow::showYellow(){
ui->yellow->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showRed()));
mytimer->start(timerValue);
}
void MainWindow::showRed(){
ui->red->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showGreen));
mytimer->start(timerValue);
}
void MainWindow::showGreen(){
ui->green->setVisible(true);
mytimer = new QTimer(this);
connect(mytimer,SIGNAL(timeout()),this,SLOT(showYellow()));
mytimer->start(timerValue);
}
void MainWindow::on_startButton_clicked()
{
timer();
ui->startButton->setEnabled(false);
}
I Also disable the Start button when clicked so the timer cannot run twice.
So On the main Window UI I basicly Have a Spinbox that takes the input of the user which is the time that I pass that to Qtimer, and then I have the 3 images with lights red green yellow that has to show and hide in the intervals . So I created almost something like a manual loop. Qtimer starts and shows Green, then goes to ShowYellow Slot and then showRed slot, so now, its suppose to go to the Green slot and then to yellow, but it doesnt go to Green again.
Can someone tell me why not.
Read the comments:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
...
mytimer = new QTimer(this); // create QTimer once
}
void MainWindow::timer() {
timerValue = ui->spinBox->value() * 1000;
showGreen(); // reuse showGreen()
}
void MainWindow::showYellow() {
// this is how toggling can be done
ui->yellow->setVisible(!ui->yellow->isVisible());
mytimer->disconnect(); // disconnect before making a new connection
connect(mytimer ,SIGNAL(timeout()), this, SLOT(showRed()));
mytimer->start(timerValue);
}
void MainWindow::showRed() {
ui->red->setVisible(!ui->red->isVisible());
mytimer->disconnect();
connect(mytimer ,SIGNAL(timeout()), this, SLOT(showGreen()));
mytimer->start(timerValue);
}
void MainWindow::showGreen() {
ui->green->setVisible(!ui->green->isVisible());
mytimer->disconnect();
connect(mytimer ,SIGNAL(timeout()), this, SLOT(showYellow()));
mytimer->start(timerValue);
}
Perhaps most simply:
void MainWindow::showHide(){
ui->green->setVisible(!ui->green->isVisible());
}
The show() and hide() member functions of QWidget are equivalent to setVisible(true) and setVisible(false), respectively.
A problem you may run into later, if you press the start button more than once, is that you spawn a new interval timer every time, leading to quicker blinking with every press of the button. If this is not what you want to happen, you'll have to control the generation of new timers.

Fading away widget in QT C++

I want to make a simple QWidget which is a simple rectangle fade away. The main problem is that the paint event paint at the same place every time in fact making the effect opposite, it make the colour stronger. Is there any way of achieving this functionality? Could you maybe provide some simple example?
My code:
`
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void paintEvent(QPaintEvent*);
~Widget();
private:
Ui::Widget *ui;
int alfa;
int i;
QTimer time;
};
#endif // WIDGET_H
`
and the cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QColor>
#include <QPainter>
#include <QBrush>
#include <QTimer>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent,Qt::Popup | Qt::FramelessWindowHint),
ui(new Ui::Widget),
time()
{
ui->setupUi(this);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_PaintOnScreen);
time.setInterval(500);
time.start();
connect(&time,SIGNAL(timeout()),this,SLOT(update()));
alfa=100;
i=0;
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
int rectBase = height();
QColor c(255,0,255);
alfa=alfa-(i*10);
c.setAlpha(alfa);
qDebug()<<c.alpha();
i++;
painter.setBrush(QBrush(c));
painter.drawRect(0, 0, width(),height());
}
Widget::~Widget()
{
delete ui;
}
You shouldn't rely on QWidget::paintEvent() to change your alpha level, since it can be called less or more than you want (multiple update() calls may result in only one paintEvent() and paintEvent() may be called when you don't expect it).
So a more reliable way to get to the result, is have a separate slot where you decrease the alpha level and then call update(). Your class definition might look like this:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget( QWidget * inParent );
private:
void paintEvent(QPaintEvent *);
private slots:
void animate();
private:
QTimer * mTimer;
int mAlpha;
};
And the declaration:
Widget::Widget( QWidget * inParent )
:
QWidget( inParent ),
mTimer( new QTimer( this ) ),
mAlpha( 255 )
{
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_PaintOnScreen);
mTimer->setInterval( 40 );
mTimer->setSingleShot( false );
connect( mTimer, SIGNAL(timeout()), this, SLOT(animate()) );
mTimer->start();
}
void
Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setBrush( QColor(255,0,255,mAlpha ) );
painter.drawRect(rect());
}
void
Widget::animate()
{
if ( mAlpha > 0 )
{
mAlpha -= 3;
}
else
{
mTimer->stop();
}
update();
}
Notice that I did decrease the interval of the timer. You only called an update() every half a second. That typically does not result in a smooth animation.
I get a warning with this code on Kubuntu Linux, under Qt5:
QWidget::paintEngine: should no longer be called
The originating line is in qWarning("QWidget::paintEngine: Should no longer be called");, in src/widgets/kernel/qwidget_qpa.cpp and discussed a bit in this ticket:
https://qt.gitorious.org/qt/qtbase-harmattan/commit/3037525
You can get the warning to stop by removing the setAttribute(Qt::WA_PaintOnScreen);, so I did that. After taking that line out, it works for me--although your subtraction model is strange. You are modifying the alpha as well as changing the subtraction value on each iteration; you probably didn't intend both. So either change it to:
QColor c (255, 0, 255);
alfa = alfa - 10;
if (alfa >= 0) {
c.setAlpha(alfa);
} else {
time.stop();
}
...or:
QColor c(255,0,255);
if (alfa - i * 10 >= 0) {
c.setAlpha(alfa - i * 10);
i++;
} else {
time.stop();
}
Etc. (See also #PrisonMonkeys note on your timer not necessarily being the only source of update() calls.) Regarding getting these warnings to be more vocal so you don't miss them, you might look at The Essential Noisy Debug Hook For Qt, which I should update.
If with the change, an alpha blended window doesn't work on your platform at all, you should mention explicitly what your circumstance is...as it is working for me.