【发布时间】:2012-02-22 09:34:05
【问题描述】:
我有一个应用程序需要以指定的帧速率逐个像素地绘制(模拟旧机器)。需要注意的是,主机引擎在后台线程中运行,以确保 UI 在模拟期间保持响应和可用。
目前,我正在玩弄这样的东西:
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_;
};
这主要是有效的,而且速度不是很慢。但是,有一个问题。更新功能 schedules paintEvent,它可能不会立即发生。事实上,根据 Qt 文档,一堆paintEvent 可能会“组合”起来。
我看到的负面影响是,经过几分钟的模拟,屏幕停止更新(虽然模拟仍在运行,但图像似乎冻结了),直到我做了一些强制屏幕更新的事情,例如切换窗口的最大化。
我已经尝试使用QTimer 和其他类似机制来产生在 GUI 线程中呈现的效果,以便我可以强制立即更新,但这会导致不可接受的性能问题。
有没有更好的方法以固定的间隔不断地将像素绘制到小部件上。首选纯 Qt 解决方案。
编辑:由于some people 选择有态度而不是阅读整个问题,我会澄清这个问题。我不能使用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 中唯一一个似乎有直接像素写入接口的类。
【问题讨论】:
-
您可以使用排队的信号槽连接来确保从 GUI 线程调用 update()。
-
@Noah:首先,你是一个有态度的人。我需要做的是绘制像素,这就是我谈到的原因所以如果我正在做的事情有替代解决方案,我很感兴趣。我使用的代码没有使用
QPixmap,所以很明显它是在Qt内部使用的。鉴于我的例子,你的建议似乎不可行。我将尝试制作一个简单的示例来证明这一点。 -
@Noah:你的建议似乎不起作用,如果你能拿出一个示范,它在给定的例子中确实有效,我很乐意接受你的回答。
-
@ChrisV - 重绘()。 OP 需要重绘()。它也是一个插槽,就像它在文档中所说的那样。
-
你看到这个答案了吗? stackoverflow.com/questions/1508151/….