Qt中的实时像素绘制

5
我的应用程序显示一张长的科学纵向滚动图片(1024 x 99999999... px),它是由一系列 QPixmap 1024 x 128 块组成。这使我可以通过从表中选择所需的块来以最小的 CPU 成本滚动图片: block_id = y_coord/128。同时,QPixmap 是快速屏幕输出的首选“像素容器”。
但现在我的应用程序有一串新数据流,并需要将新数据添加到长图片的底部并进行显示。最小部分大小为:1024x1(一行)。此外,我希望尽可能快地显示每一行新数据(接近实时)。每 128 行新的数据将被“打包”到 QPixmap 中,但在收到足够的数据之前,我无法构建一个完整的块。
我应该考虑哪种方法来显示新数据?
这个视频给出了“添加新数据行”的想法,除了在我的情况下流是向上的:http://www.youtube.com/watch?v=Dy3zyQNK7jM

我猜你可以将新数据打包到一些临时像素图中,并在画面底部绘制它,然后以与现在相同的方式绘制其余部分(可能需要进行一些移动)。当你收到128行时,只需丢弃临时像素图并打包数据。 - Alex Telishev
3个回答

3

您可以直接修改 QPixmaps 的底部行并使用 update() 更新窗口(如果底部行在范围内)。

根据您的更新/重绘速度,您可能会发现使用 QImage 对于未完成的行更有效率。


2
更新 QPixmap 不便宜 :) - pavelkolodin
不过,像我之前提到的那样,更新 QImage 更便宜。你会在渲染速度上失去一些速度,但可能不会太多。你可以将条带设置为 102416 或其他尺寸,这应该会有所帮助。你测量过吗?* - spraff

1
在当代Qt中,使用光栅后端时,与QImage相比,QPixmap没有任何优势。所有内容都呈现到一个大的QImage后备缓冲区中,然后再将其复制到屏幕上。因此,只需使用QImage即可。
您可以拥有一个高度为128像素的QImage,但是您只绘制已经填充数据的部分。没有数据的部分要么不被绘制,要么悬挂在窗口的可见区域下方,从而有效地变成了不可见。

0

这里是我快速编写的一个示例。我不知道它是否最有效,但它展示了你正在考虑的基本思路:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QVector>
#include <QGraphicsScene>
#include <QTimerEvent>

#define TILE_HEIGHT 128
#define TILE_WIDTH 1024

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    QPixmap generateLine();
public slots:
    void timerEvent(QTimerEvent *);
private:
    QGraphicsView * m_view;
    QGraphicsScene * m_scene;
    QVector <QGraphicsPixmapItem *> m_tiles;
    QVector <QGraphicsPixmapItem *> m_lineBuffer;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

#include <QPixmap>
#include <QtGlobal>
#include <QDateTime>
#include <QTimer>
#include <QPaintEngine>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    this->setFixedWidth(TILE_WIDTH);
    this->setCentralWidget(m_view = new QGraphicsView());
    m_scene = new QGraphicsScene;
    m_view->setScene(m_scene);

    QPixmap p(TILE_WIDTH, TILE_HEIGHT);
    p.fill(Qt::black);
    m_tiles.append(new QGraphicsPixmapItem(p));
    m_tiles.last()->setPos(0,0);
    m_scene->addItem(m_tiles.last());

    qsrand(QDateTime::currentMSecsSinceEpoch());

    this->startTimer(0);
}

MainWindow::~MainWindow()
{

}

void MainWindow::timerEvent(QTimerEvent *)
{
    // if your generated data is on another thread, you may want to do some thread
    // synchronization with a Mutex and a Mutex Locker so you don't stomp on your
    // buffers

    // static bool busy = false;
    // static int skipCount = 0;
    // if(busy)
    // { 
    //    skipCount++;
    //     qDebug() << "Skipped Line count =" << skipCount;
    //     return;
    // }
    // busy = true;

    // grab a new line
    QPixmap linePix = generateLine();
    int y = m_tiles.size()*TILE_HEIGHT + m_lineBuffer.size()*1;

    // append it to the line buffer
    m_lineBuffer.append(new QGraphicsPixmapItem(linePix));

    // add it to the scene
    m_scene->addItem(m_lineBuffer.last());
    m_lineBuffer.last()->setPos(0, y);

    // scroll it into view
    m_view->ensureVisible(m_lineBuffer.last());

    if(m_lineBuffer.size() >= TILE_HEIGHT)
    {
        // when the line buffer is "full"
        // or ready to be made into a tile

        // compile all the qpixmaps into a single "tile"
        static QRectF source(0,0, TILE_WIDTH, 1);
        QPixmap tile(TILE_WIDTH, TILE_HEIGHT);
        QPainter painter;
        painter.begin(&tile);
        for(int i = 0; i < m_lineBuffer.size(); i++)
        {
            painter.drawPixmap(QRectF(0, i, TILE_WIDTH, 1),
                               m_lineBuffer.at(i)->pixmap(),
                                       source);
        }
        painter.end();

        // add it into the tiles list
        m_tiles.append(new QGraphicsPixmapItem(tile));

        // add it to the scene
        m_tiles.last()->setPos(0, (m_tiles.size() - 1)*TILE_HEIGHT);
        m_scene->addItem(m_tiles.last());

        // scroll it into view
        m_view->ensureVisible(m_tiles.last());

        // Clean up the line buffer
        foreach(QGraphicsPixmapItem * pi, m_lineBuffer)
        {
            m_scene->removeItem(pi);
            delete pi;
        }
        m_lineBuffer.clear();
    }
    // busy = false;
}

QPixmap MainWindow::generateLine()
{
    // create a random pixmap of TILE_WIDTH x 1
    static int img_width = TILE_WIDTH;
    QImage img(img_width,1, QImage::Format_RGB16);
    for(int i = 0; i< img_width; i++)
    {
        img.setPixel(i, 0, qrand()%65536);
    }
    return QPixmap::fromImage(img);
}

main.cpp

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.showMaximized();

    return a.exec();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接