【问题标题】:QWidgets Leaving Artifacts Of Previous PaintQWidgets 留下以前油漆的伪影
【发布时间】:2014-11-21 09:34:54
【问题描述】:

我有一个应用程序可以交互式地在屏幕上移动从 QWidget 派生的对象。有时,当我将焦点切换到另一个应用程序时,它们会留下先前几何图形的伪影,但会一直留在屏幕上。

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

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

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

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

【问题讨论】:

  • 如果您尝试在此行之后调用update() 怎么办:setGeometry(geo);
  • 这绝对有帮助。谢谢你。我还有一个小神器。我在上面添加了剩余神器的图片。
  • 我找到了解决方案。解决方案是在更改子对象的几何形状后更新() parentWidget。 (这对我来说很有意义,因为孩子的几何形状是相对于 parentWidget,而不是孩子本身。我现在认为这很可能不是一个错误,但如果有一个很好的参考,我也想更好地理解渲染内部可用,甚至是几句智慧的话。)

标签: qt qwidget qpainter


【解决方案1】:

我找到了似乎是一个解决方案。解决方案是 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();
}

【讨论】:

    【解决方案2】:

    这更像是一个评论而不是一个答案,但由于 cmets 有大小和其他限制,我认为这些信息可能对其他人有价值,所以我将其包含在此处。

    我又开始认为这可能是一个错误。我刚刚使用了文档中的 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

    【讨论】:

      猜你喜欢
      • 2012-09-10
      • 2013-02-12
      • 1970-01-01
      • 1970-01-01
      • 2016-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多