使用 QtConcurrent 加载 Pixmap 并绘制它

Using QtConcurrent to load a Pixmap and paint it

本文关键字:绘制 Pixmap QtConcurrent 加载 使用      更新时间:2023-10-16

>我正在尝试创建一个Tile渲染程序。下面是一些基本代码。

页眉

class Tile: public QGraphicsItem
{
public:
Tile(void);
~Tile(void);
QGraphicsPixmapItem *tileItem;
void update(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
 protected:
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget);
};

.CPP:

.Constructor etc
.
.
void Tile::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
    if(tileItem==NULL)
    {
        qDebug()<<"Loading Pixmap";
        QPixmap p("c:\qt\tile\tile0-0.png");
        tileItem=new QGraphicsPixmapItem;
        tileItem->setPixmap(p); 
    }
    tileItem->paint(painter,option,widget);
}

我正在尝试制作一个应用程序,该应用程序将大图像的磁贴粘贴到QGraphicsScene上。但是一次加载所有图块非常耗时,并且会占用大量内存。所以我正在亚类化 QGraphicsItem 并覆盖油漆。类中的 paint 方法仅在 QGraphicsView 中进入视图时调用。因此,通过在油漆中加载我的瓷砖,我基本上可以创建一个应用程序,仅在瓷砖进入视野时才加载它们。到目前为止,这很多工作都在起作用。

为了使用户体验更好,我使用QtConcurrent 尝试在单独的线程中加载瓷砖。这就是我所做的更改。

.CPP

connect(&watcher,SIGNAL(finished()),this,SLOT(updateSceneSlot()));
void Tile::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
    if(tileItem==NULL)
    {   
        TilePainter=painter;
        TileOption=option;
        TileWidget=widget;
        qDebug()<<"Paint Thread id "<< QThread::currentThread();
        future=QtConcurrent::run(LoadTilePixmap,this);
        watcher.setFuture(future);
    }
    else
        tileItem->paint(painter, option, widget);
}    

加载图皮图函数:

void LoadTilePixmap(Tile *temp,QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
qDebug()<<"Loading Pixmap";
QPixmap p("c:\qt\tile\tile0-0.png");
temp->tileItem=new QGraphicsPixmapItem;
temp->tileItem->setPixmap(p);
qDebug()<<"Loaded Pixmap";
}

void Tile::updateSceneSlot()
{
    qDebug()<<"updateSceneSlot Thread id "<< QThread::currentThread();
    tileItem->paint(TilePainter, TileOption, TileWidget);
}

这段代码应该可以工作,但一旦调用 paint,它就会在运行时崩溃。添加断点后,我将问题缩小到导致崩溃temp->tileItem->paint(painter,option,widget);

我得到的输出是

Loading Pixmap 
Almost Loaded Pixmap 
First-chance exception at 0x6526174a (QtGuid4.dll) in Visualizer.exe: 0xC0000005: Access violation reading location 0xc88bffe1.
Unhandled exception at 0x6526174a (QtGuid4.dll) in Visualizer.exe: 0xC0000005: Access violation reading location 0xc88bffe1.

谁能帮我,让我知道为什么最后一行/油漆方法崩溃。我该如何解决它?

编辑代码以更新更改

只有主(也称为GUI)线程可以在屏幕上绘制。我相信,来自 LoadTilePixmap() 函数的以下行,您在单独的线程中运行,尝试在屏幕上绘制像素图项目的内容。

temp->tileItem->paint(painter,option,widget);

在线程中,您应该只加载和准备图像,当线程完成后,向主线程发出图像已准备就绪的信号,并从主线程进行绘制。

从您发布的代码中不清楚,但是您是否在Tile的构造函数中将tileItem初始化为NULL?如果没有,那将是你所看到的崩溃的可能解释。

我通过避免绘画而是使用更新功能来解决这个问题。从我读过的任何文档中,更新都间接安排了油漆调用。所以最后我的代码看起来像这样

void LoadTilePixmap(Tile *temp)
{
        QPixmap p("c:\qt\tile\tile0-0.png");
        temp->tileItem=new QGraphicsPixmapItem;
        temp->tileItem->setPixmap(p);
        temp->update(0,0,511,511);
}

这将导致我的重载 paint 函数第二次被调用,但这次 if 条件为 false,它会进入其他绘制。不完全是最佳解决方案,但它目前有效。