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

C++ 将基于像素的图形绘制到QWidget,c++,qt4,pixel,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) { }

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

编辑:既然选择了一种态度而不是阅读整个问题,我将澄清这个问题。我不能使用
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_;
};
#包括
#包括
QVideo类:公共QWidget{
Q_对象;
公众:
QVideo(QWidget*parent=0,Qt::WindowFlags f=0):QWidget(parent,f),屏幕图像(256,240,QImage::Format_RGB32){
}
空心画框(空心*数据){
//将数据渲染到屏幕图像中_
//我在这里使用fill,但在真实的事物中我正在渲染
//逐像素
屏幕图像填充(rand());
}
无效开始帧(){
//执行渲染前需要完成的任何预渲染准备工作
//每帧
}
无效结束帧(){
//update();//强制绘制事件
重新油漆();
}
无效paintEvent(QPaintEvent*){
油漆工p(本);
p、 drawImage(rect(),screen_image,screen_image_.rect());
}
QImage屏幕u图像u;
};
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();
}
#包括
#包括
#包括
#包括“QVideo.h”
结构线程:公共QThread{
线程(QVideo*v):v_v(v){
}
无效运行(){
而(1){
v_u->开始帧();
v_u->draw_frame(0);//对于本例,内容并不重要
v_u->end_frame();
QThread::sleep(1);
}
}
QVideo*v_2;;
};
int main(int argc,char*argv[]){
QApplication应用程序(argc、argv);
qw;
w、 show();
螺纹t&w;
t、 start();
返回app.exec();
}

我绝对愿意探索不使用临时
QImage
渲染的选项。这只是
Qt
中唯一一个似乎具有直接像素写入接口的类。

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

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

您可以使用排队的信号槽连接来确保从GUI线程调用update()。@Noah:首先,您是以一种态度开始的人。我需要做的是画像素,这就是为什么我要说的,所以如果有一个替代的解决方案,我所做的,我感兴趣。我使用的代码没有使用
QPixmap
,因此很明显,它是在Qt内部使用的。以我为例,你的建议似乎行不通。我将试着给出一个简单的例子来说明这一点。@Noah:你的建议似乎不起作用,如果你能给出一个例子来说明它在哪里起作用,我很乐意接受你的答案。@ChrisV-repaint()。OP需要重新绘制()。它也是一个插槽,就像文档中说的。你看到这个答案了吗。QTimer方法似乎是可行的,但我在这方面遇到了性能问题,我将重新讨论它。也许我忽略了什么。