使用 QtConcurrent 加载 Pixmap 并绘制它
我正在尝试创建一个平铺渲染程序。这是一些基本代码。
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
只有主(也称为 GUI)线程可以在屏幕上绘图。
我相信 LoadTilePixmap() 函数中的以下行(您在单独的线程中运行)尝试在屏幕上绘制像素图项的内容。
在线程中,您应该只加载并准备图像,当线程完成时,向主线程发出信号,表明图像已准备好,并从主线程进行绘图。
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.
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.
从您发布的代码中尚不清楚,但是您是否在
Tile
的构造函数中将tileItem
初始化为 NULL?如果不是,这可能是您所看到的崩溃的可能解释。It's not clear from the code you posted, but are you initializing
tileItem
to be NULL in the constructor ofTile
? If not, that would be a possible explanation for the crash you are seeing.我通过避免绘制并使用更新功能解决了这个问题。从我读过的任何文档中,更新都间接安排了绘制调用。
所以最后我的代码看起来像这样
这将导致我的重载绘制函数被第二次调用,但这次 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
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.