Using QAbstractItemDelegate to display a TGA from a database
I have the following code that should display any column that is a byte array as a 320x200 tga image. But the cells in the QTTableView are grey rather than the image. Also they don't size to 320x200, as the sizeHint() function is never called.
Any idea what I'm doing wrong?
Code:
#ifndef __IMAGE_DELEGATE_H_
#define __IMAGE_DELEGATE_H_
#include <QItemDelegate>
{
Q_OBJECT
public:
};
#endif
Code:
#include "SQLQueryItemDelegate.h"
#include <QPainter>
{
{
siImage.fromData((const uchar*) ba.data(), ba.size());
siPixmap.fromImage(siImage,Qt::AutoColor);
painter->drawPixmap(option.rect, siPixmap);
}
else
{
}
}
{
{
}
else
{
}
}
Thanks, everyone is very helpful here.
Re: Using QAbstractItemDelegate to display a TGA from a database
I figured it out. Maybe not all these steps are needed, but it works
Code:
buffer.setBuffer(&ba);
qir.setDevice(&buffer);
qir.read(&image);
painter->drawPixmap(option.rect, pixmap);
Re: Using QAbstractItemDelegate to display a TGA from a database
I found that it can't load the images fast enough in some cases. I drag the scrollbar to view through the rows, but the scrollbar lags behind the mouse cursor. Is there a way to defer paint events for say 100 milliseconds, so if you're just skipping by that row it doesn't need to draw?
Re: Using QAbstractItemDelegate to display a TGA from a database
You may paint to cell blank when you don't have the data, then store the index in an array and use a timer with setSingleShot(true) and the interval you want. Connect the timeout() signal to a slot where you call view->update(index) for all the indexes that need to be repainted.
Code:
class SQLQueryItemDelegate : public QStyledItemDelegate
{
...
signals:
void paintingDeferred(const QModelIndex& index);
...
};
class SQLQueryDeferredPainter
: public QObject{
Q_OBJECT
public:
protected slots:
void paintLater(const QModelIndex& index);
void paintPendingIndexes();
protected:
QModelIndexList m_indexes;
};
SQLQueryDeferredPainter
::SQLQueryDeferredPainter(QAbstractItemView *view, SQLQueryItemDelegate
*delegate,
QObject *parent
){
m_view = view;
connect(delegate, SIGNAL(paintingDeferred(const QModelIndex&)),
this, SLOT(paintLater(const QModelIndex&)));
m_timer.setSingleShot(true);
m_timer.setInterval(100);
connect(&m_timer, SIGNAL(timeout()),
this, SLOT(paintPendingIndexes()));
}
void SQLQueryDeferredPainter::paintLater(const QModelIndex& index)
{
m_indexes.append(index);
m_timer.start();
}
void SQLQueryDeferredPainter::paintPendingIndexes()
{
foreach(const QModelIndex& index, m_indexes)
m_view->update(index);
m_indexes.clear();
}
Re: Using QAbstractItemDelegate to display a TGA from a database
Don't load the images on every refresh. Load them once and then store in a pixmap cache or even subclass your model and make it return the images directly as the decoration role for its indexes.
Re: Using QAbstractItemDelegate to display a TGA from a database
I tried the deferred loading but it didn't look good while scrolling.
I'll load images in a thread. It will load images around the current view automatically. If you scroll, it will add images to load in a fixed-size buffer. That way, if you scroll fast, the buffer will overflow and you won't load images you won't see anyway.
Re: Using QAbstractItemDelegate to display a TGA from a database
Actually, using a cache increased the performance enough I can use it. Here's the code
Code:
#ifndef __BYTE_ARRAY_AS_IMAGE_ITEM_DELEGATE_H_
#define __BYTE_ARRAY_AS_IMAGE_ITEM_DELEGATE_H_
#include <QItemDelegate>
#include <QTimer>
class ModelIndicesDeferredPainter;
// If an item is a byte array, wait 100 milliseconds, then load it as an image if possible.
{
Q_OBJECT
public:
// This is needed because you can't emit events from paint() since paint() is const
void SetDeferredPainter(ModelIndicesDeferredPainter *df) {deferredPainter=df;}
signals:
void paintingDeferred(const QModelIndex& index);
protected:
ModelIndicesDeferredPainter *deferredPainter;
};
class ModelIndicesDeferredPainter
: public QObject{
Q_OBJECT
public:
bool paintLater(const QModelIndex& index);
protected slots:
void paintPendingIndexes();
protected:
QModelIndexList indicesToPaint;
// Don't immediately load images. This way, if an image immediately goes out of viewable area, it is not drawn
// This is needed so we know not to just call paintLater() again from ByteArrayAsImageItemDelegate
bool isPaintingLater;
};
#endif
Code:
#include "ByteArrayAsImageItemDelegate.h"
#include <QPainter>
#include <QBuffer>
#include <QImageReader>
#include <QAbstractItemView>
#include <QPixmapCache>
// From RakNet
#include "SuperFastHash.h"
{
{
unsigned int hash = SuperFastHash(ba.data(),ba.length());
// Safer collision code commented out, in case I need it
//hashKey.sprintf("%i%i%i",index.row(),index.column(),hash);
// Faster, in case images are duplicated
hashKey.sprintf("%i",hash);
bool paintLater;
if (foundPixmap==false)
{
paintLater=deferredPainter->paintLater(index);
if (paintLater==false)
{
// Load pixmap
buffer.setBuffer(&ba);
qir.setDevice(&buffer);
if (qir.read(&image))
{
pixmap
= QPixmap::fromImage(image
);
// Save to cache
}
else
{
// Not an image?
return;
}
}
}
if (foundPixmap==true || paintLater==false)
{
// Paint
QSize imageSize
= pixmap.
size();
float scaleWidth, scaleHeight;
float drawWidth, drawHeight;
scaleWidth = (float) imageSize.width() / (float)option.rect.width();
scaleHeight = (float)imageSize.height() / (float)option.rect.height();
// Downscale by the larger of the two proportions
if (scaleHeight > scaleWidth)
{
drawWidth=(float)imageSize.width()/scaleHeight;
drawHeight=(float)imageSize.height()/scaleHeight;
}
else
{
drawWidth=(float)imageSize.width()/scaleWidth;
drawHeight=(float)imageSize.height()/scaleWidth;
}
float centerX = (float)option.rect.left() + (float)option.rect.width()/2.0;
float centerY = (float)option.rect.top() + (float)option.rect.height()/2.0;
drawRect.setLeft(centerX-drawWidth/2.0);
drawRect.setRight(centerX+drawWidth/2.0);
drawRect.setTop(centerY-drawHeight/2.0);
drawRect.setBottom(centerY+drawHeight/2.0);
painter->drawPixmap(drawRect, pixmap);
}
}
else
{
}
}
{
storedItemView = view;
timer.setSingleShot(true);
timer.setInterval(100);
connect(&timer, SIGNAL(timeout()),
this, SLOT(paintPendingIndexes()));
isPaintingLater=false;
}
bool ModelIndicesDeferredPainter::paintLater(const QModelIndex& index)
{
if (isPaintingLater)
return false;
if (indicesToPaint.contains(index)==false)
{
indicesToPaint.append(index);
timer.start();
}
return true;
}
void ModelIndicesDeferredPainter::paintPendingIndexes()
{
// This flag is needed so ByteArrayAsImageItemDelegate::paint does not just call paintLater again()
isPaintingLater=true;
foreach(const QModelIndex& index, indicesToPaint)
storedItemView->update(index);
storedItemView->repaint();
isPaintingLater=false;
indicesToPaint.clear();
}
Re: Using QAbstractItemDelegate to display a TGA from a database
Why are you deferring anything? Not that I see what you are deferring...