QWidgets 留下以前油漆的伪影
Posted
技术标签:
【中文标题】QWidgets 留下以前油漆的伪影【英文标题】:QWidgets Leaving Artifacts Of Previous Paint 【发布时间】: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,而不是孩子本身。我现在认为这很可能不是一个错误,但如果有一个很好的参考,我也想更好地理解渲染内部可用,甚至是几句智慧的话。)
【参考方案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
【讨论】:
以上是关于QWidgets 留下以前油漆的伪影的主要内容,如果未能解决你的问题,请参考以下文章
使用 Accelerate 缩放 Ycbcr (420f) 时的伪影