Нарисуйте пиксельную графику в QWidget - PullRequest
3 голосов
/ 09 декабря 2010

У меня есть приложение, которое должно рисовать попиксельно с заданной частотой кадров (имитируя старую машину).Одно предостережение состоит в том, что основной механизм машины работает в фоновом потоке, чтобы гарантировать, что пользовательский интерфейс остается отзывчивым и пригодным для использования во время моделирования.

В настоящее время я играю с использованием чего-то подобного:

class QVideo : public QWidget {
public:
    QVideo(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), screen_image_(256, 240, QImage::Format_RGB32) {

    }

    void draw_frame(void *data) {
        // render data into screen_image_
    }

    void start_frame() {
        // do any pre-rendering prep work that needs to be done before
        // each frame
    }

    void end_frame() {
        update(); // force a paint event
    }

    void paintEvent(QPaintEvent *) {
        QPainter p(this);
        p.drawImage(rect(), screen_image_, screen_image_.rect());
    }

    QImage screen_image_;
};

Это в основном эффективно и на удивление не очень медленно.Тем не менее, есть проблема.Функция обновления планирует a paintEvent, она может не произойти сразу.Фактически, куча paintEvent может «объединиться» в соответствии с документацией Qt.

Отрицательный эффект, который я вижу, заключается в том, что после нескольких минут симуляции экран перестает обновляться (изображениекажется замороженным, хотя симуляция все еще выполняется), пока я не сделаю что-то, что вызывает обновление экрана, например переключение окна в развернутое окно и из него.

Я экспериментировал с использованием QTimer и другого подобного механизма, чтобыэффект от рендеринга в потоке GUI, так что я могу форсировать немедленные обновления, но это привело к неприемлемым проблемам с производительностью.

Есть ли лучший способ рисовать пиксели на виджете постоянно с фиксированным интервалом.Чистые решения Qt предпочтительны.

РЕДАКТИРОВАТЬ: Поскольку некоторые люди предпочитают иметь отношение, а не читать весь вопрос, я проясню проблему.Я не могу использовать QWidget::repaint, потому что он имеет ограничение в том, что он должен вызываться из того же потока, что и цикл обработки событий.В противном случае обновление не происходит, и вместо этого я получаю сообщения qDebug, такие как:

QPixmap: It is not safe to use pixmaps outside the GUI thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QWidget::repaint: Recursive repaint detected
QPainter::begin: A paint device can only be painted by one painter at a time.
QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent
QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent

РЕДАКТИРОВАТЬ: , чтобы продемонстрировать проблему, я создал этот простой пример кода:

QVideo.h

#include <QWidget>
#include <QPainter>

class QVideo : public QWidget {
    Q_OBJECT;
public:
    QVideo(QWidget *parent = 0, Qt::WindowFlags f = 0) : QWidget(parent, f), screen_image_(256, 240, QImage::Format_RGB32) {

    }

    void draw_frame(void *data) {
        // render data into screen_image_
        // I am using fill here, but in the real thing I am rendering
        // on a pixel by pixel basis
        screen_image_.fill(rand());
    }

    void start_frame() {
        // do any pre-rendering prep work that needs to be done before
        // each frame
    }

    void end_frame() {
        //update(); // force a paint event
        repaint();
    }

    void paintEvent(QPaintEvent *) {
        QPainter p(this);
        p.drawImage(rect(), screen_image_, screen_image_.rect());
    }

    QImage screen_image_;
};

main.cc:

#include <QApplication>
#include <QThread>
#include <cstdio>
#include "QVideo.h"


struct Thread : public QThread {

    Thread(QVideo *v) : v_(v) {
    }

    void run() {
        while(1) {
            v_->start_frame();
            v_->draw_frame(0); // contents doesn't matter for this example
            v_->end_frame();
            QThread::sleep(1);
        }
    }

    QVideo *v_;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QVideo w;
    w.show();

    Thread t(&w);
    t.start();

    return app.exec();
}

Я определенно готов исследовать варианты, которые не используют временную QImage для рендеринга.Это просто единственный класс в Qt, который, похоже, имеет интерфейс прямой записи пикселей.

Ответы [ 2 ]

2 голосов
/ 22 февраля 2012

Попробуйте отправить сигнал из потока в слот в виджете цикла событий, который вызывает repaint (), который затем будет выполнен сразу же. Я делаю что-то подобное в моей графической программе, которая выполняет основные вычисления в одном потоке, а затем сообщает виджету, когда пора перерисовать () данные.

1 голос
/ 10 декабря 2010

В подобных случаях я по-прежнему использовал QTimer, но вместо одного выполнял несколько шагов моделирования.Вы даже можете настроить программу на автоматическую настройку количества шагов моделирования, чтобы иметь возможность получать любые кадры в секунду, которые вам нравятся для обновления экрана.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...