使用 QtConcurrent 加载 Pixmap 并绘制它

发布于 2024-11-06 18:17:26 字数 2702 浏览 0 评论 0原文

我正在尝试创建一个平铺渲染程序。这是一些基本代码。

Header

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内部时才会被调用。因此,通过在油漆中加载我的图块,我基本上可以创建一个仅在图块进入视图时才加载图块的应用程序。 到目前为止,这些都是有效的。

为了让用户体验更好,我使用 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 方法崩溃。我该如何修复它?

编辑代码以更新更改

I'm trying to create a Tile rendering program. Heres some basic code.

Header

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

I'm trying to make a application that will paste tiles of a big image onto a QGraphicsScene. But loading all the tiles at once is time consuming and takes up a lot of memory. So I'm subclassing QGraphicsItem and overriding paint. The paint method in the QGraphicsItem class is only called when a it comes into view inside the QGraphicsView. So by loading up my tile inside paint, I can basically create an application that loads tiles only when they come into view.
This much is working so far.

To make the user experience better I'm use QtConcurrent to try an load the tiles up in a seperate thread.
SO here's the changes I've made.

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 function:

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

This code should work but it keeps crashing at runtime as soon as paint gets called. After adding breakpoints I narrowed the problem down to temp->tileItem->paint(painter,option,widget); which causes the crash.

The output that I get is

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.

Could anyone help me and let me know why the lastline/paint method crashes. How can I fix it?

EDITED CODE TO UPDATE CHANGES

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雨落星ぅ辰 2024-11-13 18:17:26

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

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

在线程中,您应该只加载并准备图像,当线程完成时,向主线程发出信号,表明图像已准备好,并从主线程进行绘图。

Only the main(also called GUI) thread can draw on the screen.
The following line from the LoadTilePixmap() function, which you run in a separate thread, I believe, tries to paint the content of your pixmap item on the screen.

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

In the thread you should just load and prepare the image and when the thread is done, signal to the main thread that the image is ready and do the drawing from the main thread.

待天淡蓝洁白时 2024-11-13 18:17:26

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

It's not clear from the code you posted, but are you initializing tileItem to be NULL in the constructor of Tile? If not, that would be a possible explanation for the crash you are seeing.

笑饮青盏花 2024-11-13 18:17:26

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

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 进行绘制。不完全是最佳解决方案,但目前有效。

I solved the issue by avoiding paint and instead using the update function. From whatever documentation I've read update indirectly schedules a paint call.
So in the end my code looks like this

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

This will cause my overloaded paint function to get called for a second time but this time the if condition is false and it goes into else which paints. Not exactly an optimum solution but it works for now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文