将基于像素的图形绘制到QWidget

时间:2010-12-09 20:55:01

标签: c++ qt4 pixel

我有一个应用程序需要以指定的帧速率逐个像素地绘制(模拟旧机器)。需要注意的是,主机引擎在后台线程中运行,以确保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_;
};

这大部分都是有效的,而且非常慢。但是,有一个问题。更新功能安排一个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中唯一一个似乎有直接像素写入界面的类。

2 个答案:

答案 0 :(得分:2)

尝试从线程向信号循环小部件中的一个插槽发出一个信号,该小部件调用repaint(),然后立即执行。我在我的绘图程序中做了类似的事情,它在一个线程中执行主要计算,然后告诉窗口小部件什么时候重新绘制()数据。

答案 1 :(得分:1)

在类似的情况下,我所做的仍然是使用QTimer,但是做了几个模拟步骤而不是一个。您甚至可以让程序自动调整模拟步骤的数量,以便能够为屏幕更新每秒获得任何帧数。