【问题标题】:Draw pixel based graphics to a QWidget将基于像素的图形绘制到 QWidget
【发布时间】: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/….

标签: c++ qt4 pixel


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • QTimer 方法似乎可行,但我遇到了性能问题,我会重新考虑它。也许我忽略了一些东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多