PDA

View Full Version : How do I display a picture on a QTableView cell?



danielperaza
4th April 2010, 00:34
:confused:Hi, it's me again :o !

I have a database table in which one of its columns stores the path to an image. I want to use a QSqlTableModel and a QTableView to show the records stored in that table, but I want to show the actual picture instead of its path. I'm a little confused by Qt's documentation on this issue.

Currently I'm trying to set the ItemDataRole to DecorationRole as it follows:



this->_model->setHeaderData(5, Qt::Horizontal, "Photo", Qt::DecorationRole);


But it doesn't work. Isn't any other way to do it without creating a custom delegate?

I looking at this example anyway, while this post gets replied:

http://doc.qt.nokia.com/4.6/itemviews-stardelegate.html

danielperaza
4th April 2010, 02:40
OK I followed the example above, and now I'm able to render the picture instead of its file path. Now I want to render it with a proper size, since it's being currently rendered at the size of its containing row. I want the row's height expands as needed to give the picture a proportional size (width is constrained).

borisbn
4th April 2010, 09:16
There is no need to use delegates, when you don't need to EDIT items. Your model must contain QAbstractItemModel::data (http://qt.nokia.com/doc/4.6/qabstractitemmodel.html#data) function. When view is asking to model a Qt::DecorationRole - return a QPixmap, when asking Qt::SizeHintRole - return QSize, that you counted for cell
see ItemDataRole (http://qt.nokia.com/doc/4.6/qt.html#ItemDataRole-enum)

danielperaza
4th April 2010, 16:36
So that means I must subclass a QSqlTableModel in order to reimplement the data() method, right?

borisbn
4th April 2010, 16:54
So that means I must subclass a QSqlTableModel in order to reimplement the data() method, right?
absolutely

danielperaza
5th April 2010, 03:42
Ok, I'm trying the following snippet but it doesn't seem to work (I receive Segmentation Fault):



QVariant PersonasModel::data(const QModelIndex &idx, int role) const
{
if(idx.column() == 5)
{
if(!QFile::exists(idx.data().toString()))
imgFile = ":/centinela/images/picture_unavailable.jpg";

QPixmap pixmap(idx.data().toString());

if(role == Qt::DecorationRole)
return pixmap;

if(role == Qt::SizeHintRole)
return pixmap.size();
return QSqlTableModel::data(idx, role);
}
return QSqlTableModel::data(idx, role);
}


If instead, I try the following, it does work, but I can't provide the SizeHint:



QVariant PersonasModel::data(const QModelIndex &idx, int role) const
{
if(idx.column() == 5 && role == Qt::DecorationRole)
{
return QPixmap(idx.data().toString());
}
return QSqlTableModel::data(idx, role);
}


Note that the fifth column of my model is the one storing the pictures. And I would like to be able to render a default picture, in case one is not available.

borisbn
5th April 2010, 05:17
The first variant seems to be good, unless unreferenced variable imgFile. Try to debug to see what code causes fault

danielperaza
5th April 2010, 05:58
Sorry, I mispelled the code that declared imgFile. But I already tried debugging and I'm sure that's not the problem. In my debugging session I noticed that idx.data() seems to fall into a infinite recursion. Somehow from this point on, a segmentation fault is produced. I used Qt Creator with GDB, but I couldn't spot the problem.

borisbn
5th April 2010, 06:15
it seems, that idx.data() calls your PersonasModel::data
try next:


QVariant PersonasModel::data(const QModelIndex &idx, int role) const
{
if ( idx.column() == 5 )
{
QString imgFile = QSqlTableModel::data( idx, Qt::DisplayRole );
if ( Qt::DisplayRole == role )
{
return QString();
}
if ( !QFile::exists( imgFile )
{
imgFile = ":/centinela/images/picture_unavailable.jpg";
}
QPixmap pixmap( imgFile );

if ( role == Qt::DecorationRole )
{
return pixmap;
}
if(role == Qt::SizeHintRole)
{
return pixmap.size();
}
}
return QSqlTableModel::data( idx, role );
}

faldzip
5th April 2010, 09:23
idx.data() calls your data implementation. And idx.column() is 5 so it goes to your if (column == 5) and there is a code asking for idx.data() and so on recursively. I suggest changing condition in if from column == 5 to (column == 5 && (role == Qt::DecorationRole || Qt::SizeHintRole)). In your first posted code and it should be fine.

danielperaza
5th April 2010, 21:25
Ok, thanks a lot for your suggestion. I'll try it out and see if it works.

danielperaza
5th April 2010, 21:49
I tried this:



QVariant PersonasModel::data(const QModelIndex &idx, int role) const
{
if(idx.column() == 5 && (role == Qt::DecorationRole || role == Qt::SizeHintRole/* || role == Qt::DisplayRole*/))
{
QString imgFile = idx.data().toString();
if(!QFile::exists(imgFile))
imgFile = ":/centinela/images/picture_unavailable.jpg";

QPixmap pixmap(idx.data().toString());

if(role == Qt::DecorationRole)
return pixmap;

if(role == Qt::SizeHintRole)
return pixmap.size();

/*if(role == Qt::DisplayRole)
return "";*/
}
return QSqlTableModel::data(idx, role);
}



It renders the picture, but rows aren't resized to the proper height (should I call QTableView::resizeRowsToContents() ?) and also there is a more important problem: it also renders the picture's file path besides the image; I don't want this to happen. I only want to show the picture. The commented code is what I tried to do in order to fix it, but causes the program to crash again.

borisbn
5th April 2010, 22:04
your program crashes, because you should write

QString imgFile = QSqlTableModel::data(idx, Qt::DisplayRole);
instead of

QString imgFile = idx.data().toString();
try to exactly repeat my previous code...

QVariant PersonasModel::data(const QModelIndex &idx, int role) const
{
if ( idx.column() == 5 )
{
QString imgFile = QSqlTableModel::data( idx, Qt::DisplayRole );
if ( Qt::DisplayRole == role )
{
return QString();
}
if ( !QFile::exists( imgFile )
{
imgFile = ":/centinela/images/picture_unavailable.jpg";
}
QPixmap pixmap( imgFile );
if ( role == Qt::DecorationRole )
{
return pixmap;
}
if(role == Qt::SizeHintRole)
{
return pixmap.size();
}
}
return QSqlTableModel::data( idx, role );
}


should I call QTableView::resizeRowsToContents() ?
honestly, I don't know. try ...

danielperaza
5th April 2010, 22:26
Worked great!, but there are still two little problems: 1) default picture is never rendered, 2) rows never acquire the required height, not even calling QTableView::resizeRowsToContents() nor without calling it.

borisbn
6th April 2010, 05:51
1)
Are you sure, that resource you use is available ?
try


QLabel * label = new QLabel( this );
label->setPixmap( QPixmap( ":/centinela/images/picture_unavailable.jpg" ) );

maybe your resource named :/centinela/images/picture_unavailable without .jpg ?


2)
try to comment out


if(role == Qt::SizeHintRole)
{
return pixmap.size();
}

and call resizeRowsToContents

danielperaza
7th April 2010, 15:30
I was spelling the "image" part of my resource's path in english, but declaring it in spanish ("imagenes"), so that solves the first problem. However, I took into account your second advice, and nothing happened.

danielperaza
9th April 2010, 23:04
Please, does any body have any idea?