I have an application that interactively moves objects derived from QWidget around the screen. Sometimes, they leave artifacts of the previous geometries which get cleaned up when I switch focus to another application, but stay on the screen until then.
Here are some examples of the artifacts I see when re-sizing a widget interactively:
I was able to create a fairly simple test case that replicates a similar issue automatically (although only leaving one-line thick artifacts). In this example, a single vertical line artifact is consistently left after each horizontal "flip" of the geometry:
#include <QApplication>
#include <QDialog>
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>
#include <QWidget>
class TestWidget : public QWidget
{
Q_OBJECT
public:
explicit TestWidget(QWidget *parent = 0);
private:
void paintEvent(QPaintEvent *e);
QRect thisRect();
void timerEvent(QTimerEvent *t);
};
TestWidget::TestWidget(QWidget *parent) :
QWidget(parent)
{
setGeometry(100,100, 100,100);
startTimer(5);
}
QRect TestWidget::thisRect()
{
return QRect(QPoint(),geometry().size());
}
void TestWidget::timerEvent(QTimerEvent *t)
{
QRect geo = geometry();
static bool growUp = false;
static bool flipUp = false;
static bool flipOver = false;
static bool growOver = false;
static int delta = 1;
static int delta2 = 1;
static int tick = 0;
static int enDelta = 1;
tick++;
if(flipUp)
geo.adjust(0,enDelta * delta,0,0);
else
geo.adjust(0,0,0,enDelta * delta);
if(tick%3==0)
enDelta = 0;
else
enDelta = 1;
if(geo.height()>100)
{
if(growUp)
delta = 1;
else
delta = -1;
growUp = !growUp;
}
if(geo.height() == 0)
flipUp = !flipUp;
if(flipOver)
geo.adjust(delta2 ,0, 0,0);
else
geo.adjust(0,0,delta2 ,0);
if(geo.width()>100)
{
if(growOver)
delta2 = 1;
else
delta2 = -1;
growOver = !growOver;
}
if(geo.height() == 0)
flipOver = !flipOver;
setGeometry(geo);
}
void TestWidget::paintEvent(QPaintEvent *e)
{
QBrush b(QColor(55,200,55,190));
QPainter p(this);
p.setBrush(b);
//p.drawRect(QRect(QPoint,this->geometry().size()));
//p.drawRect(QRect(QPoint,geometry().size()));
if(thisRect().width() != 0 && thisRect().height() != 0)
{
p.drawRect(thisRect());
qDebug() << "painted rect is " << thisRect();
qDebug() << "painted geo is " << geometry();
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//MainWindow w;
//w.show();
QDialog d;
d.show();
int i;
TestWidget *tw;
for(i=0; i<1; i++) //2000; i++)
{
tw = new TestWidget(&d);
tw->show();
}
return a.exec();
}
Is there some sort of synchronization I am missing or is this a bug? (I started to look into the event queue and backing store algorithms, but as soon as I change focus from the test application to Qt Creator so I can single step, the artifacts go away. This makes it a little tricky to trace. Also, the fact that it is double buffered makes it harder to see exactly when items are rendered to the staging buffer, since there is no visual feedback as it happens.)
After adding adding an:
update();
(and as mentioned in the comment below), there were significant improvements, but the otherwise identical code, still produced an artifact which is shown here in the red circle to which the red arrow is pointing:
I found what appears to be a solution. The solution was to update() (as suggested by vhancho), but specifically to update the parentWidget of the object after altering its geometry.
(This makes sense to me since a child's geometry is relative to the parentWidget, not the child itself. I also had another similar episode of confusion when updating the geometry topLeft, which, of course, will always be in the coordinates of the parent widget. drawRect, by contrast, will be in the coordinates of the object widget in which it is called.)
Here is the corrected test case: