QWidgets留下以前的油漆工件

时间:2014-11-21 09:34:55

标签: qt qwidget qpainter

我有一个应用程序,可以在屏幕上交互式地移动从QWidget派生的对象。有时候,当我将焦点切换到另一个应用程序时,它们会留下以前几何图形的工件,这些工件会被清理干净,但在此之前会保留在屏幕上。

以下是我在交互式重新调整窗口小部件时看到的工件的一些示例:

artifacts

我能够创建一个相当简单的测试用例,自动复制一个类似的问题(尽管只留下一行厚的工件)。在此示例中,在几何体的每个水平“翻转”之后始终保留单个垂直线伪影:

#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();
}

我是否缺少某种同步或这是一个错误? (我开始研究事件队列和后备存储算法,但是一旦我将焦点从测试应用程序更改为Qt Creator,所以我可以单步执行,工件就会消失。这使得跟踪有点棘手。此外,它是双缓冲的这一事实使得很难准确地看到项目何时被呈现到临时缓冲区,因为没有视觉反馈。)

添加后添加:

update();

(并在下面的评论中提到),有显着的改进,但其他相同的代码,仍然产生了一个神器,这里显示红色箭头所指向的红色圆圈:

enter image description here

2 个答案:

答案 0 :(得分:0)

我发现了什么似乎是一个解决方案。解决方案是update()(由vhancho建议),但专门用于在更改其几何体后更新对象的parentWidget。

(这对我来说很有意义,因为孩子的几何学是相对于parentWidget,而不是孩子本身。当更新几何topLeft时,我还有另一个类似的混乱情节,当然,它总是在坐标中相比之下,drawRect将位于调用它的对象小部件的坐标中。)

以下是更正的测试用例:

#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);

    parentWidget()->update();   //this line removes all artifacts from this test case

}



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();
}

答案 1 :(得分:0)

这更多是评论而不是答案,但由于评论有大小和其他限制,我认为这些信息可能对其他人有价值,我会在此处加入。

我回想起这可能是一个错误。我刚刚使用了docs中的QRubberBand示例模式,如果我快速旋转橡皮筋的原点,我会看到相同类型的工件。以下是我从文档中创建的示例的完整代码:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QRubberBand>
class Widget : public QWidget
{ Q_OBJECT void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); public: Widget(QWidget *parent = 0); ~Widget(); private: QRubberBand rubberBand; QPoint origin; }; #endif // WIDGET_H #include <QMouseEvent> Widget::Widget(QWidget *parent) : QWidget(parent) , rubberBand(QRubberBand::Rectangle, this) { } Widget::~Widget() { }
void Widget::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
//if (!rubberBand)
// rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand.setGeometry(QRect(origin, QSize()));
rubberBand.show();
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand.setGeometry(QRect(origin, event->pos()).normalized());
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand.hide();
// determine selection, for example using QRect::intersects()
// and QRect::contains().
}
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}

同样,这可以通过在几何体修改之后添加update()来解决,但由于这不是文档化模式示例的一部分,我认为它不被认为是必要的。这是合适的期望吗?

以下是解决工件问题的修改:

void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand.setGeometry(QRect(origin, event->pos()).normalized());
update();
}

这也是一个错误:

https://bugreports.qt-project.org/browse/QTBUG-42827