У меня есть приложение, которое должно рисовать попиксельно с заданной частотой кадров (имитируя старую машину).Одно предостережение состоит в том, что основной механизм машины работает в фоновом потоке, чтобы гарантировать, что пользовательский интерфейс остается отзывчивым и пригодным для использования во время моделирования.
В настоящее время я играю с использованием чего-то подобного:
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
, который, похоже, имеет интерфейс прямой записи пикселей.