使用QtConcurrent加载Pixmap并绘制它

时间:2011-05-10 13:46:45

标签: c++ multithreading qt qpainter qtconcurrent

我正在尝试创建一个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并重写油漆。 QGraphicsItem类中的paint方法仅在它进入QGraphicsView内的视图时被调用。因此,通过在tile中加载我的tile,我基本上可以创建一个仅在它们进入视图时加载tile的应用程序。 到目前为止,这一点很有用。

为了让用户体验更好,我使用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);

}    

LoadTilePixmap函数:

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.

任何人都可以帮助我,让我知道为什么lastline / paint方法崩溃了。我该如何解决?

编辑代码以更新更改

3 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

我通过避免绘画而不是使用更新功能解决了这个问题。从我读过的任何文档更新间接调度绘制调用。 所以最后我的代码看起来像这个

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);
}

这将导致我的第二次调用我的重载绘制函数,但这次if条件为false并且它进入其中绘制的else。这不是一个最佳解决方案,但现在可以使用。