2016-12-22 16 views
0

Я показываю изображение в метке qt. Ниже мой код:Получение Pixmap является пустой pixmap при вызове функции 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; 
} 

При нажатии кнопки он отправляет запрос на сервер и будет показывать другое изображение, полученное от сервера. Он работает нормально, если он не превышает 500 раз. Если он превысил это, эта ошибка была показана

QPixmap::scaled: Pixmap is a null pixmap 

и не показывает изображения. Затем, если кто-то снова отправит запрос на изображение, тогда появится следующая ошибка: Qt поймал исключение, отправленное из обработчика события. Бросок исключений из обработчика событий не поддерживается в Qt. Вы должны повторить QApplication::notify() и поймать все исключения там.

Я не понимаю, что является ошибкой в ​​приведенном выше коде. Может кто-нибудь, пожалуйста, скажите мне, как это решить?

ответ

0

Очевидная утечка - qnam_push_button_clicked_show_image = new QNetworkAccessManager(this);, которая не имеет сбалансированного удаления в любом месте. QNAM обычно создаются один раз, а затем повторно используются для жизни приложения, а не создаются для одного запроса. Поэтому, превратив qnam_push_button_clicked_show_image в член класса (то же самое, что и ui), вы исправите как утечку, так и повысите эффективность кода.

Тем не менее, я не думаю, что это вызывает вашу ошибку QPixmap. Если вы используете этот код на X11, то QPixmap поддерживается ресурсом X Pixmap, который ограничен различными факторами (программным и аппаратным обеспечением). Несмотря на то, что из вашего кода нет очевидной утечки, может случиться так, что многократное выделение больших pixmaps медленно фрагментирует пул памяти, управляемый X, вплоть до того момента, когда он не может выделить блок, достаточно большой для масштабированной pixmap, а затем вызывает ошибку , Или это может быть ошибка драйвера где-то в графическом стеке. Пробовали ли вы, если изменение масштабного размера увеличивается или уменьшается предел, прежде чем он начнет ломаться? Если это так, переключиться на QImage может помочь уменьшить давление на X.

Кроме этого, код может использовать некоторую очистку, особенно это избыточное использование QEventLoop. Я предполагаю, что это способ предотвратить нажатие кнопки несколько раз, пока новое изображение не будет загружено, но я бы скорее реализовал это, используя button.setEnabled (false), пока изображение загружается, потому что вложенные циклы событий объединены с сетевыми событиями - это рецепт бесчисленных проблем с повторным подключением и трудно отлаживать сбои/ошибки.

Я тоже запутался о том, почему size выделяется в куче, особенно, когда он удален сразу после того, как, и эти if (size) действительно сбивает с толку, так как они могут быть поняты как if (size->isValid()) в то время как то, что они на самом деле имею в виду if (size != nullptr), который является довольно так как вероятность получения OOM на этой линии бесконечно мала. (если у вас в конечном итоге закончилась нехватка памяти, я предполагаю, что это, скорее всего, произойдет в вызовах readAll() или loadFromData() выше).

пс: удачи, нажав на эту кнопку еще 500 раз, чтобы проверить, если фиксировать утечку помогло;)

+0

ой, и есть еще одна небольшая утечка, когда вы возвращение рано из 'onPushButtonClickedRequestCompleted (QNetworkReply * ответ)' в случае ' ответ' имеет ошибку. Вы пропустите ответ, не называя 'reply-> deleteLater();' в этом случае. Обычно это первое, что я вызываю в таких слотах, что безопасно, так как ответ не будет фактически удален до выхода из слота, поэтому нет необходимости только называть его в конце. –