LINUX.ORG.RU

qt4 проблема отзывчивости интерфейса при использовании repaint()

 


0

1

Ситуацию попробую описать словами, чтобы не постить портянки кода. Есть тяжело нагруженный thread, который примерно 50 раз в секунду формирует объект QImage и посылает сигнал на соответствующий слот в GUI потоке. Задача GUI потока рисовать этот QImage в главном окне. Если из этого слота вызывать update() для окна, то все нормально за исключением того что кадры пропадают и «кино» идет рывками. Если же вызываю repaint() то кино идет плавно, но интерфейс практически блокируется, и ждать реакции на нажатие кнопки можно минуту. Рисую через DrawImage в обработчике PaintEvent. В чем причина или как сделать кошерно, не теряя кадры и отзывчивость интерфейса?

ИМХО... но не видя код...
24 кадров будет достаточно. :)
То что тормозит, это нормально, грузишь основной тред.
Попробуй разные setGraphicsSystem - скорее всего с raster будет быстрее. И оптимизируй картинку при DrawImage, то есть выруби альясинги, убери маштабирование итд.

zJes ★★
()

ничего не знаю, у меня 25 кадров в секунду при помощи update, тоже QImage прилетают - ничего не теряется, всё плавно

выключи билинейную фильтрацию масштабирования если используешь

я подозреваю что у тебя что-то копится, как собака которая догнала свой хвост - такой эффект :)

I-Love-Microsoft ★★★★★
()

Наконец появился повод прочитать про разметку постов )).Весь код наверное нет смысла постить.


QImage *pImage;

void VideoThread::run()
{
   //... 
  forever {
        mutex.lock();
        cond_ADCREADY.wait(&mutex); //жду готовности данных от ацп
        //QPixmap image;
        mutex.unlock();

        //  ... формирую QImage
 
        frame++;
        emit ShowVideo(); //отправляю сигнал GUI потоку
    }
}


MainForm::MainForm(QWidget *parent)
{
    setupUi(this);
    setWindowTitle("Video");

    // ...

    //соединяю синал со слотом
    connect(&thread, SIGNAL(ShowVideo()), this, SLOT(ShowVideo()));

   // ...
}

void MainForm::ShowVideo()
{
   //repaint(); плавное кино тормоза интерфейса. 
   update();  //отзывчивый интерфейс кино рывками
}

void MainForm::paintEvent(QPaintEvent * /* event */)
{
    QPainter painter(this);
    if (!pImage->isNull()) {

        QRectF target(LeftImageMargin, 0.0, 1024, 1024);
        QRectF source(0.0, 0.0, 512.0, 512.0);

        painter.drawImage(target, *pImage, source);
    }
}

За совет покрутить setGraphicsSystem спасибо завтра на работе опробую.
FreeLiver ★★★
() автор топика
Ответ на: комментарий от FreeLiver

pImage у тебя общие данные для двух потоков? Где локи в paintEvent тогда?

anonymous
()
Ответ на: комментарий от FreeLiver
const cv::Mat& Worker::newFrame(const cv::Mat &frame) {
    {
        QMutexLocker locker(&imageLock);
        image = frame;
    }
    emit updateFrame();
    return frame;
}

void CvDeclarativeItem::updateFrame() {
    Q_D(CvDeclarativeItem);
    auto &image = d->image;

    auto cols = image.cols;
    auto rows = image.rows;

    {
        QMutexLocker locker(&d->worker->imageLock);
        cv::swap(d->worker->image, image);
    }

    if (cols != image.cols || rows != image.rows) {
        setSize(QSizeF(image.cols, image.rows));
    }

    update();
}

У меня так было сделано, не претендую на идеальность и работоспособность. У меня точно не было 50 кадров в секунду, хотя бы потому что cv стафф занимал больше времени.

anonymous
()
Ответ на: комментарий от anonymous

Ну и paintEvent для полноты:

void CvDeclarativeItem::paint(QPainter *p, const QStyleOptionGraphicsItem*, QWidget*) {
    Q_D(const CvDeclarativeItem);

    auto &mat = d->image;
    QImage img(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
    p->drawImage(boundingRect(), img.rgbSwapped());
}

anonymous
()

Тэг забыл поставить. Завтра попробую еще показывть каждый 2-3 кадр. Как может вывод битмапа 50 раз в секунду так нагрузить GUI thread?

FreeLiver ★★★
() автор топика

Примерный алгоритм для 25 кадров в секунду (если отрисовка займет большее время, чем 1/25 секунды, понятное дело будет тормозить):

1. frameDuration = 1000 / 25
2. timeStamp1 =  QDateTime::currentDateTime()
3. Производишь немедленную отрисовку кадра через вызов repaint()
4. timeStamp2 = QDateTime::currentDateTime()
5. delay = frameDuration - (timeStamp2 - timeStamp1)
6. QTimer::singleShot(delay)
7. переходишь к пункту 1

m0rph ★★★★★
()

QImage is designed and optimized for I/O, and for direct pixel access and manipulation, while QPixmap is designed and optimized for showing images on screen.

может QPixmap попробовать?

shty ★★★★★
()

Можно попробовать передать виджет с картинкой в отдельный поток. А кнопочки пускай будут в основном.

quiet_readonly ★★★★
()
Ответ на: комментарий от quiet_readonly

Можно попробовать передать виджет с картинкой в отдельный поток.

Каким образом?

anonymous
()

Всем спасибо, особенно zJes. Отключил масштабирование с 512 до 1024 при выводе картинки проблема ушла. Сейчас сделаю формирование QImage размером 1024х1024 сразу. А я-то наивный думал что вывод битмапа с масштабированием аппаратно умели еще видеокарты 90-х. По-подводу замечаний о локах - вы наблюдательны, но здесь из-за специфики задачи они реально не нужны, а плавность пострадает.

FreeLiver ★★★
() автор топика
Ответ на: комментарий от FreeLiver

битмапа с масштабированием аппаратно умели еще видеокарты 90-х

Именно по этой причине рекомендую вместо текущей системы с отображением QImage на стандартных виджетах воспользоваться QGLWidget. Нарисовать квадратик с текстурой на весь экран не сложно, но в нагрузку получите кучу разных плюшек и нормальную скорость работы.

trex6 ★★★★★
()
Ответ на: комментарий от trex6

Ты про это?

In 4.8 it is now possible to use QPainter in a separate thread to render to a QGLWidget, QGLPixelBuffer and QGLFrameBufferObject assuming you are using the OpenGL [ES] 2.0 paint engine. It’s important to note that QGLWidget does not get moved to a secondary thread (it’s a QWidget afterall).

anonymous
()
29 ноября 2012 г.
Ответ на: комментарий от FreeLiver

Делаю такой софт: http://code.google.com/p/mmarker/

Картинку показываю пользователю из QPixmap, т.к. QPimap оптимизирован для вывода на экран из готового буфера. Картинка в QPixmap попадает из QImage, где перед этим отрисовывается. Раз мне нужно картинку много двигать, часто перерисовывать одно и то же, то QPixmap подходит лучше всего, судя по его описанию на Qt-сайтах.

kiverattes ★☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.