在调用函数500次时获取Pixmap是一个空像素图

时间:2016-12-22 05:20:28

标签: qt memory-leaks qpixmap qevent qeventloop

我在qt标签中显示图像。以下是我的代码:

$(document).ready(function() { 

     var dlg=$('#dialog').dialog({
        title: "jQuery Dialog",
        resizable: true,
        autoOpen:false,
        modal: true,
        hide: 'fade',
        width:350,
        height:275
     });


     $('#btn').click(function(e) {
         e.preventDefault();
         dlg.load('test3.php', function(){
             dlg.dialog('open');
         });
      }); 
});

点击按钮它会向服务器发送请求,并显示从服务器收到的不同图像。如果不超过500次,它工作正常。如果超过该值,则显示此错误

void MyClass::onPushButtonClicked(QString myurl)
{
    this->setCursor(Qt::WaitCursor);
    ui.qtImageLabel->clear();
    qDebug()<<QTime::currentTime()<<"MyClass: onPushButtonClicked";
    QNetworkAccessManager *qnam_push_button_clicked_show_image;
    QNetworkReply *reply;
    QNetworkRequest request;
    request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
    QUrl url(myurl);
    request.setUrl(url);
    qnam_push_button_clicked_show_image = new QNetworkAccessManager(this);
    if(qnam_push_button_clicked_show_image)
    {
        QObject::connect(qnam_push_button_clicked_show_image, SIGNAL(finished(QNetworkReply*)),
                         this, SLOT(onPushButtonClickedRequestCompleted(QNetworkReply*)));
        reply = qnam_push_button_clicked_show_image->post(request, url.encodedQuery());
        QEventLoop loop;
        QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
        loop.exec();
    }
}

void MyClass::onPushButtonClickedRequestCompleted(QNetworkReply *reply)
{
    qDebug()<<QTime::currentTime()<<"MyClass: onPushButtonClickedRequestCompleted request completed";
    if (reply->error() != QNetworkReply::NoError)
    {
        qDebug() << "Error in" << reply->url() << ":" << reply->errorString();
        this->setCursor(Qt::ArrowCursor);
        return;
    }
    QByteArray data = reply->readAll();
    QPixmap pixmap;
    pixmap.loadFromData(data);
    int width;
    int height;
    //application size can be changed
    QRect rec = QApplication::desktop()->screenGeometry();
    height = rec.height();
    width = rec.width();
    qDebug()<<QTime::currentTime()<<width<<","<<height;
    QSize *size = new QSize(width,height);
    if(size)
    {
        QPixmap scaledPixmap = pixmap.scaled(*size);
        ui.qtImageLabel->setPixmap(scaledPixmap);
    }
    if(size)
    {
        delete size;
        size = NULL;
    }
    data.clear();
    this->setCursor(Qt::ArrowCursor);
    reply->deleteLater();
    return;
}

并且它不显示图像。然后,如果有人再次发送图像请求,则会显示以下错误: Qt捕获了一个从事件处理程序抛出的异常。投掷 Qt不支持来自事件处理程序的异常。你必须 重新实现QPixmap::scaled: Pixmap is a null pixmap 并捕获所有例外情况。

我没有得到上面代码中的错误。有人可以告诉我如何解决这个问题吗?

1 个答案:

答案 0 :(得分:0)

明显的泄漏是qnam_push_button_clicked_show_image = new QNetworkAccessManager(this);,它在任何地方都没有平衡删除。 QNAM通常应该创建一次,然后在应用程序的生命周期中重用,而不是为单个请求创建。因此,通过在类成员中转换qnam_push_button_clicked_show_image(与ui相同),您将修复泄漏并提高代码效率。

那就是说,我认为这不是导致你的QPixmap错误的原因。如果您在X11上运行此代码,则QPixmap由X Pixmap资源支持,该资源受各种因素(软件和硬件)的限制。即使从您的代码中没有明显的泄漏,也可能是重复分配大型像素映射会缓慢分割由X管理的内存池,直到它无法为缩放的像素图分配足够大的块然后触发错误。或者它可能是图形堆栈中某处的驱动程序错误。如果在开始破坏之前更改缩放大小增加或减少限制,您是否尝试过?如果是这样,切换到QImage可能有助于减轻对X的压力。

除此之外,代码可以使用一些清理,尤其是多余的QEventLoop用法。我猜这是一种阻止按钮被多次点击直到新图像被加载的方法,但我更倾向于在图像下载时使用button.setEnabled(false)来实现它,因为嵌套的事件循环结合了网络事件是无数重入问题的一个因素,很难调试崩溃/错误。

我也很困惑为什么在堆上分配size,特别是当它被删除之后,这些if (size)真的很混乱,因为它们可以理解为if (size->isValid())而他们真正的意思是if (size != nullptr),这几乎可以保证,因为在该线上获得OOM的机会是无穷小的。 (如果你最终耗尽内存,我的猜测可能会发生在上面的readAll()或loadFromData()调用中。)

ps:祝你再次按下该按钮500次以检查是否有助于修复泄漏;)