PDA

View Full Version : Awful flickering with QGridLayout on Mac OS X



agarny
6th July 2012, 17:15
Hi,

I am using a QGridLayout to show some widgets which I create on the fly, then delete, create others, etc. This all works fine, but on Mac OS X I get a really awful flickering (despite using setUpdatesEnabled()) while it's all fine on Windows and Linux.

I have created a small Qt project which reproduces the problem (see below). I would really appreciate if someone could tell me whether I missed something obvious or whether it's a 'bug' on Mac OS X.

Cheers, Alan.

Test.pro:

QT += core gui

TARGET = Test
TEMPLATE = app

SOURCES += main.cpp\
mainwindow.cpp

HEADERS += mainwindow.h
main.cpp:

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();

return a.exec();
}
mainwindow.cpp:

#include "mainwindow.h"

#include <QGridLayout>
#include <QPushButton>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
mWidget = new QWidget(this);

mLayout = new QGridLayout(mWidget);

mWidget->setLayout(mLayout);

populate();

setCentralWidget(mWidget);

this->resize(600, 300);
}

void MainWindow::populate()
{
setUpdatesEnabled(false);

for (int i = 0, iMax = mLayout->count(); i < iMax; ++i) {
QLayoutItem *item = mLayout->takeAt(0);

delete item->widget();
delete item;
}

for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) {
QPushButton *button = new QPushButton("Click me!", mWidget);

connect(button, SIGNAL(clicked()), this, SLOT(populate()));

mLayout->addWidget(button, i, j);
}

setUpdatesEnabled(true);
}
mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QGridLayout;

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);

private:
QWidget *mWidget;
QGridLayout *mLayout;

private Q_SLOTS:
void populate();
};

#endif

d_stranz
6th July 2012, 17:47
This is a little strange, because according to this code, your MainWindow isn't even visible yet at the time you call the populate() method - populate() is called during MainWindow's construction, prior to it being made visible via show(). You must mean that the flicker occurs when one of the buttons is clicked and the slot calls populate(), right?

My guess is that it is some issue related to deleting the widget whose clicked() signal is being handled during the execution of the slot. Possibly in Mac OS, the underlying graphics system is trying to update the button to reflect its new clicked state, and the rug has been yanked out from under it.

Try adding a button or menu item that is not going to be dynamically deleted and use that to invoke the populate slot, and see if the flicker problem still occurs. If it does, it might be due to some recursion or iteration in the layout size recalculation - turning off updates won't prevent events from being added to the queue, I don't think.

You could also try deleting the layout itself in the repopulate method and adding a whole new layout along with the contents.

agarny
6th July 2012, 17:56
This is a little strange, because according to this code, your MainWindow isn't even visible yet at the time you call the populate() method - populate() is called during MainWindow's construction, prior to it being made visible via show(). You must mean that the flicker occurs when one of the buttons is clicked and the slot calls populate(), right?Yes, sorry, the flicker does indeed occur when you click one of the buttons which will replace all existing buttons with new ones.


My guess is that it is some issue related to deleting the widget whose clicked() signal is being handled during the execution of the slot. Possibly in Mac OS, the underlying graphics system is trying to update the button to reflect its new clicked state, and the rug has been yanked out from under it.

Try adding a button or menu item that is not going to be dynamically deleted and use that to invoke the populate slot, and see if the flicker problem still occurs. If it does, it might be due to some recursion or iteration in the layout size recalculation - turning off updates won't prevent events from being added to the queue, I don't think.Well, the code I provided is just some example code and as it happens, in my application, I replace the contents of the grid layout as a result of selecting a menu item (among other things) and I still get the flickering.


You could also try deleting the layout itself in the repopulate method and adding a whole new layout along with the contents.Yes, I thought of that after posting my message. I guess I will have to give it a try and see how it goes.

agarny
8th July 2012, 02:39
Ok, I had a go at replacing the layout with another one, but no matter what I tried the flickering was still there. Then, I somehow thought about using a QStackedWidget object and add/remove a widget to/from to mimick what I was trying to achieve, and... well no more flickering! So, though I wish it didn't have to be so cumbersome, it at least works. Here is the changed code...

mainwindow.cpp:

#include "mainwindow.h"

#include <QGridLayout>
#include <QPushButton>
#include <QStackedWidget>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
mGridWidget(0),
mGridLayout(0)
{
mStackedWidget = new QStackedWidget(this);

populate();

setCentralWidget(mStackedWidget);

this->resize(600, 300);
}

void MainWindow::populate()
{
setUpdatesEnabled(false);

// Create a layout for our new widget and populate it

QWidget *newGridWidget = new QWidget(this);
QGridLayout *newGridLayout = new QGridLayout(newGridWidget);

static int counter = 0;

++counter;

for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j) {
QPushButton *button = new QPushButton(QString("Click me! [%1]").arg(QString::number(counter)), newGridWidget);

connect(button, SIGNAL(clicked()), this, SLOT(populate()));

newGridLayout->addWidget(button, i, j);
}

// Set newGridLaout as a layout for newGridWidget

newGridWidget->setLayout(newGridLayout);

// Add newGridWidget to mStackedWidget

mStackedWidget->addWidget(newGridWidget);

// Get rid of mGridWidget and mGridLayout (and of its contents)

if (mGridWidget) {
mStackedWidget->removeWidget(mGridWidget);

for (int i = 0, iMax = mGridLayout->count(); i < iMax; ++i) {
QLayoutItem *item = mGridLayout->takeAt(0);

delete item->widget();
delete item;
}

delete mGridLayout;
delete mGridWidget;
}

// Keep track of newGridWidget and newGridLayout

mGridWidget = newGridWidget;
mGridLayout = newGridLayout;

setUpdatesEnabled(true);
}

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QGridLayout;
class QStackedWidget;

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);

private:
QStackedWidget *mStackedWidget;

QWidget *mGridWidget;
QGridLayout *mGridLayout;

private Q_SLOTS:
void populate();
};

#endif