PDA

View Full Version : Insert and read Image in QLable using QDataWidgetMapper



hsm13
6th November 2011, 23:50
I have simple master detail application using QDataWidgetMapper in master table; I added a blob in database and QLable to show the picture and button to select a picture

The button code


QString pix = QFileDialog::getOpenFileName(
this, tr("Open file"), "/MyDocument/", tr("PNG Images (*.jpg)") );

QImage image(pix);
ui->pix_label->setPixmap(QPixmap::fromImage(image));

and the classic mapper code


mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->setItemDelegate(new QSqlRelationalDelegate(this));
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
mapper->addMapping(ui->id,model->fieldIndex("id"));
mapper->addMapping(ui->fName,model->fieldIndex("fName"));
mapper->addMapping(ui->lName,model->fieldIndex("lName"));

Now, I tried to add this line but couldn't navigate pictures


mapper->addMapping(ui->picture_label,model->fieldIndex("picture"));


I need help to make that works, I'm a just beginner, so a detailed code is appreciated. Thanks in advance

ChrisW67
7th November 2011, 06:20
The QLabel does not have a property that set/returns its icon as a QByteArray. You need the image data as a QByteArray in order to insert/retrieve it from the blob database field. You will probably need to implement a QItemDelegate to handle the transfer of data to and from this widget. You would need to provide the QItemDelegate::setEditorData() and QItemDelegate::setModelData() functions and attach an instance of the delegate to your widget mapper.

You should also look at QPixmap::loadFromData(), QPixmap::save(), and QBuffer for manipulating the image data.

hsm13
7th November 2011, 07:10
Hum, it looks I need a lot. Is there a sample code ? Why it is so complicated in Qt ? And why there is no reference describe that case ?! I'm sorry

ChrisW67
7th November 2011, 09:50
Not really a lot. Something like this (compiles but untested):


class PicLoadingDelegate: public QItemDelegate
{
Q_OBJECT
public:
PicLoadingDelegate(QObject * parent = 0 ): QItemDelegate(parent) { }

void setEditorData( QWidget *editor, const QModelIndex &index ) const
{
QLabel *label = qobject_cast<QLabel *>(editor);
if (label) {
QByteArray imageData = index.data(Qt::EditRole).toByteArray();
QPixmap pixmap;
if (pixmap.loadFromData(imageData))
label->setPixmap(pixmap);
}
}

void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
QLabel *label = qobject_cast<QLabel *>(editor);
if (label) {
QBuffer buf;
buf.open(QIODevice::WriteOnly);
if (label->pixmap()->save(&buf))
model->setData(index, buf.data(), Qt::EditRole);
}
}
};

hsm13
9th November 2011, 06:11
wow that's great; Thank you so much.
Is PDF file is different than image ? and can I display it in hyperlink label to open it with outside application ? Many thanks again.

ChrisW67
9th November 2011, 06:49
Is PDF file is different than image ?
Yes.

and can I display it in hyperlink label to open it with outside application ?
Yes.

hsm13
9th November 2011, 11:23
ummm, sample code if found please

hsm13
10th November 2011, 06:17
Let's take a deep breath; In fact I found you post here

http://www.qtcentre.org/threads/44992-Upload-pdf-file-in-the-MySQL-database?highlight=pdf+blob

It's a bit different; so where I want some tricky help. sorry for any disturb.

cia.michele
8th January 2012, 13:08
Good Morning to all,
I've the same problem, but in a QSqlRelationalModel, so, how I should use this delegate?
I've got a table with severals column so, now I use a QSqlRelationalDelegate (I've also 3 QCombobox at 3 relations), so how can I switch from one delegate to other? Or I can subclass QSqlRelationalDelegate to obtain this? And your code affect only the QLabel behavior? The other columns will work as before?

Thanks for your time!

Michele

ChrisW67
8th January 2012, 21:58
The delegate code I posted is an example. It assumes that if the editor is a QLabel that the corresponding column in the model is a blob containing an image. It also shows how the current image in the label can be written back to the model. If the editor widget, this was for mapped widgets not a table view, was not a label then no data would be transferred in either direction.

Delegates are attached to a view, not model or table, either as a whole or to a column/ row of the the view (see QAbstractItemView::setItemDelegate(), setItemDelegateForColumn, setItemDelegateForRow). If you use one delegate for the whole view then your delegate usually needs to differentiate between columns using index.column() and do something different for each in the setEditorData() and setModelData() functions. If you use a delegate on a single column then the example is the sort of thing you need.

The default relational delegate should look after creating an populating combo box editors for established relationships and do the default for other columns. You can put separate delegates on columns if you wish or write a combined delegate by subclassing the relational delegate.

What are you trying to achieve?

cia.michele
9th January 2012, 08:03
I'm trying to achive the same effect needed by hsm 13 :) but I'm using a QSqlRelationalTableModel and QDataWidgetMapper, and I don't know how to use your example.
If I assign your delegate to the mapper [mapper->setItemDelegate(new PicLoadingDelegate(this));], I lose the link of other fields of my form.
I use QLabel to show the image stored into Blob file.

Thanks a lot

Michele

ChrisW67
9th January 2012, 22:17
You can only attach the delegate to the mapper as a whole so it will affect all the columns and you need to write the delegate to discriminate between columns accordingly. The related columns should be handed off to the superclass to get default combo box treatment. Something like:


class MyDelegate: public QSqlRelationalDelegate {
Q_OBJECT
public:
MyDelegate(QObject *p = 0): QSqlRelationalDelegate(p) { }
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
switch(index.column()) {
case 0: // do nothing special, simple editors
case 1:
QSqlRelationalDelegate::setEditorData(editor, index);
break;
case 2: // do something special for blob column 2
{
QLabel *label = qobject_cast<QLabel*>(editor);
Q_ASSERT(label);
// blob loading code
break;
}
case 3: // the related column gets default treatment
default:
QSqlRelationalDelegate::setEditorData(editor, index);
}
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
switch(index.column()) {
case 0: // do nothing special for column 0, 1
case 1:
QSqlRelationalDelegate::setEditorData(editor, index);
break;
case 2: // do something special for blob column 2
{
QLabel *label = qobject_cast<QLabel*>(editor);
Q_ASSERT(label);
// blob saving code
break;
}
case 3: // the related column gets default treatment
default: // so does anything we've missed
QSqlRelationalDelegate::setEditorData(editor, index);
}
}

};

cia.michele
10th January 2012, 08:17
Hello ChrisW67,
thanks a lot for your help. I've tried something like your code, and I've create my Delegate from your code, but, It seems don't work.
this is my code:


class PicLoadDelegate: public QSqlRelationalDelegate
{
Q_OBJECT

public:
PicLoadDelegate(QObject * parent = 0 ): QSqlRelationalDelegate(parent) { }
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
switch(index.column()) {
case 9: // do something special for blob column 2
{
QLabel *label = qobject_cast<QLabel*>(editor);
Q_ASSERT(label);
if (label) {
QByteArray imageData = index.data(Qt::EditRole).toByteArray();
QPixmap pixmap;
if (pixmap.loadFromData(imageData))
label->setPixmap(pixmap);
}
break;
}
default:
QSqlRelationalDelegate::setEditorData(editor, index);
}
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
{
switch(index.column()) {
case 9: // do something special for blob column 2
{
QLabel *label = qobject_cast<QLabel*>(editor);
Q_ASSERT(label);
if (label) {
QBuffer buf;
buf.open(QIODevice::WriteOnly);
if (label->pixmap()->save(&buf))
model->setData(index, buf.data(), Qt::EditRole);
}
break;
}
default: // so does anything we've missed
QSqlRelationalDelegate::setEditorData(editor, index);
}
}
};

What do you think is the mistake?

Thanks a lot for your time

Michele

ChrisW67
10th January 2012, 08:35
Which bit doesn't work? Doesn't compile? Doesn't update the QLabel mapped to column 9 (that's the tenth column)? Doesn't save a label image? Have you single stepped it? Is there valid data in the tenth column of the model?

cia.michele
10th January 2012, 09:28
You're right, I must explain better :)

It compile and run correctly (i tested with debug), but the image isn't stored or retrived. I load a picture in my QLabel's pixmap, and next send the model->submit() method. But into DB there is no image into BLOB field.

The DB is SQLITE, do you think I need to use base64 conversion?

Thanks a lot for your help

Michele

You're right, I must explain better :)

It compile and run correctly (i tested with debug), but the image isn't stored or retrived. I load a picture in my QLabel's pixmap, and next send the model->submit() method. But into DB there is no image into BLOB field.

The DB is SQLITE, do you think I need to use base64 conversion?

Thanks a lot for your help

Michele

Added after 47 minutes:

It's work!!
I forgot to specify the format into pixmap->save() method.
So, in setModelData method, now I have:


[...]
if (label->pixmap()->save(&buf,"PNG"))
model->setData(index, buf.data(), Qt::EditRole);
[...]

and it work fine!

Thank a lot for your help.

Michele

x_allaume
8th March 2020, 23:55
Hello everyone,

I'm kind of new to Qt, and I'm struggling with a similar problem as the one evoked by Michele :-) I'm also trying to synchronize a Qlabel pixmap with a blob in SQLITE3. The only difference I have are :
* My image blobs are contained in a separate table and I'm using a setRelation(1, QSqlRelation(images, "planteid", "imagedata") on the QSqlRelationalTableModel
* My images are in "JPG" format

Everything compiles and works perfectly thanks to your help, but I have a problem at the recording step. The pixmap QByteArray doesn't get sored in the model, neither synchronized in the database. I can save it as a file, which looks ok.
The only thing I can see is that the call to model->setData(index, buf.data(), Qt::EditRole) returns false. But the Qt documentation and researches I did aren't clear on this.

Do you have a hint, or an idea of way I could understand the problem further?

ChrisW67
12th March 2020, 04:03
If the setData() call returns false then it has failed. Never tried updating through a QSqlRelationalTableModel, so this is a best guess.

The docs talk about the value in setData() being the "index" of the record in the related table. I think they mean the primary key value of the entry in the related table. In the case of the situation of a completely new image, I think you would need to: Use a separate QSqlQuery to insert the binary data into the related table
Collect newId = lastInsertId() after the insert. The related table needs to have some sort of auto-populated key for that to work (INTEGER PRIMARY KEY in Sqlite). You could also manually manage the key column.
Call setData(index, newId, Qt::EditRole) on the QSqlRelationalTableModel