Evgenii Legotckoi
Evgenii LegotckoiNov. 25, 2015, 7:17 p.m.

Qt/C++ - Lesson 029. Picture in database in Qt – Saving and Loading

Images in the database can be stored in BLOB format ( Binary Large Object ), that is an array of binary data format. BLOB format is also suitable for storing audio and video data in databases.

Consider the saving and loading of the image from the database as an example of the next application that screenshot of your computer screen will be saved in the database and displayed in a special a QLabel , when the corresponding entry is selected in a QTableView , which will display all the records of the images stored in the database .

Thus we have a database table with the following fields:

  • id (INTEGER) - id;
  • Name (VARCHAR(255)) - the name stored in the database file data;
  • Pic (BLOB) - a field for storing images in the database.

If you do not go into detail in the lesson, the most valuable information on this topic at the end of mainwindow.cpp file in the following methods:

  • MainWindow::on_screenButton_clicked()
  • MainWindow::slotCurrentPic(QModelIndex index)

Project structure

  • PicDataBase.pro - the profile of the project;
  • database.h - header file wrapper class for working with the database;
  • database.cpp - file source wrapper class codes for the work with the database;
  • mainwindow.h - header file of the main application window;
  • mainwindow.cpp - file source code of the main application window;
  • mainwindow.ui - form the main application window;
  • main.cpp - the main file of the application source code.

mainwindow.ui

In the designer's apply this form of the application window.

PicDataBase.pro

In profile, the application is mandatory connect sql module for working with databases.

main.cpp

The file is created by default and is not subject to change.

database.h

To work with a database using already familiar class-wrapper DataBase.

ATTENTION!!! - The database file is created in the folder C:/example , so the correct method or DataBase::connectToDataBase() or create an example folder on drive C.

#ifndef DATABASE_H
#define DATABASE_H

#include <QObject>
#include <QSql>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDatabase>
#include <QFile>
#include <QDate>
#include <QDebug>

#define DATABASE_HOSTNAME   "ScreenDataBase"
#define DATABASE_NAME       "Screen.db"

#define TABLE                   "ScreenTable"       
#define TABLE_NAME              "Name"              
#define TABLE_PIC               "Pic"               

class DataBase : public QObject
{
    Q_OBJECT
public:
    explicit DataBase(QObject *parent = 0);
    ~DataBase();
    void connectToDataBase();

private:
    QSqlDatabase    db;

private:
    bool openDataBase();        
    bool restoreDataBase();     
    void closeDataBase();       
    bool createTable();        

public slots:
    bool insertIntoTable(const QVariantList &data);     
    bool insertIntoTable(const QString &name, const QByteArray &pic);
};

#endif // DATABASE_H

database.cpp

#include "database.h"

DataBase::DataBase(QObject *parent) : QObject(parent)
{

}

DataBase::~DataBase()
{

}

void DataBase::connectToDataBase()
{
    if(!QFile("C:/example/" DATABASE_NAME).exists()){
        this->restoreDataBase();
    } else {
        this->openDataBase();
    }
}

bool DataBase::restoreDataBase()
{
    if(this->openDataBase()){
        return (this->createTable()) ? true : false;
    } else {
        qDebug() << "Не удалось восстановить базу данных";
        return false;
    }
    return false;
}

bool DataBase::openDataBase()
{
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setHostName(DATABASE_HOSTNAME);
    db.setDatabaseName("C:/example/" DATABASE_NAME);
    if(db.open()){
        return true;
    } else {
        return false;
    }
}

void DataBase::closeDataBase()
{
    db.close();
}

bool DataBase::createTable()
{
    QSqlQuery query;
    if(!query.exec( "CREATE TABLE " TABLE " ("
                            "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                            TABLE_NAME     " VARCHAR(255)    NOT NULL,"
                            TABLE_PIC      " BLOB            NOT NULL"
                        " )"
                    )){
        qDebug() << "DataBase: error of create " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

bool DataBase::insertIntoTable(const QVariantList &data)
{
    QSqlQuery query;
    query.prepare("INSERT INTO " TABLE " ( " TABLE_NAME ", "
                                             TABLE_PIC " ) "
                  "VALUES (:Name, :Pic)");
    query.bindValue(":Name",        data[0].toString());
    query.bindValue(":Pic",         data[1].toByteArray());

    if(!query.exec()){
        qDebug() << "error insert into " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

bool DataBase::insertIntoTable(const QString &name, const QByteArray &pic)
{
    QVariantList data;
    data.append(name);
    data.append(pic);

    if(insertIntoTable(data))
        return true;
    else
        return false;
}

mainwindow.h

In addition to the usual facilities of DataBase and QSqlTableModel , and model tuning techniques and concepts that were used in previous articles for QSqlRelationalTableModel and QSqlQueryModel , in this file there are slots to handle clicking on the button and click on the record in the table.

The first slot carries creating screenshot of the screen and add it to the database, and the second slot reconstructs the image from the database, taking it from QSqlTableModel object in QTableView and placing it in QLabel in the main application window.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSqlTableModel>
#include <QModelIndex>

#include "database.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    // Slot to record screenshots into the database
    void on_screenButton_clicked();
    // The slot for the getting of image from the database
    void slotCurrentPic(QModelIndex index);

private:
    Ui::MainWindow *ui;
    DataBase        *db;
    QSqlTableModel  *model;

private:
    void setupModel(const QString &tableName, const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

All main work with the database, and the image is made in the following techniques:

  • MainWindow::on_screenButton_clicked()
  • MainWindow::slotCurrentPic(QModelIndex index)
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QApplication>
#include <QBuffer>
#include <QScreen>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    db = new DataBase();
    db->connectToDataBase();

    this->setupModel(TABLE,
                     QStringList() << trUtf8("id")
                                   << trUtf8("Имя изображения")
                                   << trUtf8("изображение")
                     );

    this->createUI();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setupModel(const QString &tableName, const QStringList &headers)
{
    model = new QSqlTableModel(this);
    model->setTable(tableName);

    /* Set the columns names in a table with sorted data
     * */
    for(int i = 0, j = 0; i < model->columnCount(); i++, j++){
        model->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
}

void MainWindow::createUI()
{
    ui->tableView->setModel(model);     
    ui->tableView->setColumnHidden(0, true);    // Hide the column id Records
    ui->tableView->setColumnHidden(2, true);    // Hide the column with image
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);  // editing is not allowed
    ui->tableView->horizontalHeader()->setStretchLastSection(true);     // Stretch the last column of around tableView

    /* Connect the signal to change the selection of the current row in the table 
     * to the slot to set the image picLabel
     * */
    connect(ui->tableView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
            this, SLOT(slotCurrentPic(QModelIndex)));

    model->select(); 
}

void MainWindow::on_screenButton_clicked()
{
    /* Make a screenshot of the screen and save it as QByteArray object
     * */
    QScreen *screen = QApplication::primaryScreen();    // Take a screen object
    QPixmap inPixmap = screen->grabWindow( 0 );         // Keeping it in the image of the object QPixmap
    QByteArray inByteArray;                             // Create QByteArray object to save the image
    QBuffer inBuffer( &inByteArray );                   // Saving images produced through the buffer
    inBuffer.open( QIODevice::WriteOnly );              // Open buffer
    inPixmap.save( &inBuffer, "PNG" );                  // Write inPixmap in inByteArray

    // Write a screenshot of the database
    db->insertIntoTable(QDateTime::currentDateTime().toString("dd.MM.yyyy_hh:mm:ss.png"), inByteArray);

    model->select();
}

void MainWindow::slotCurrentPic(QModelIndex index)
{
    QPixmap outPixmap = QPixmap(); // Create QPixmap, which will be placed in picLabel
    /* Taking the image data from the table as QByteArray and put them in QPixmap
     * */
    outPixmap.loadFromData(model->data(model->index(index.row(), 2)).toByteArray());
    ui->picLabel->setPixmap(outPixmap.scaled(400,300));
}

Result

The result is an application that allows you to save screen screenshots in the database and restore this image by displaying it in QLabel window.

Video

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

t
  • May 21, 2017, 3:46 a.m.

Здравствуйте, при сборке выдаёт две такие ошибки C:\untitled1\mainwindow.cpp:99: ошибка: C2039: picLabel: ­Ґ пў«пҐвбп з«Ґ­®¬ "Ui::MainWindow" C:\untitled1\mainwindow.cpp:99: ошибка: C2227: ўла ¦Ґ­ЁҐ б«Ґў  ®в "->setPixmap" ¤®«¦­® гЄ §лў вм ­  вЁЇ Є« бб , бвагЄвгал Ё«Ё ®ЎкҐ¤Ё­Ґ­Ёп «ЁЎ® ­  г­ЁўҐаб «м­л© вЁЇ Как это исправить, помогите пожалуйста

t
  • May 21, 2017, 3:53 a.m.

И ещё не могли бы подсказать что нужно изменить в коде чтобы по нажатию на кнопку в БД загружался не скриншот, а выбранные и уже существующие изображения?
Заранее спасибо!

Evgenii Legotckoi
  • May 21, 2017, 9:54 a.m.

Интерфейс приложения сделать через графический дизайнер так, как сделано в данной статье. Убедиться, что существует QLabel в интерфейсе, с таким же названием, как в этой статье.

Evgenii Legotckoi
  • May 21, 2017, 9:57 a.m.

Создать QPixmap из файла, указав путь к файлу.

QPixmap pixmap("/path/to/file.png");
t
  • May 21, 2017, 4:46 p.m.

Спасибо большое! Но если я правильно понял, то для каждого файла нужно будет описывать каждый раз путь? А можно сделать как-то так чтобы можно было выбирать их?

Evgenii Legotckoi
  • May 21, 2017, 10:34 p.m.

Используйте QFileDialog класс. У него есть статические методы, которые позволяют открыть диалог, в котором можно выбрать либо один файл, либо несколько файлов.

Например, чтобы забрать имя одного файла:

QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                "/home",
                                                tr("Images (*.png *.xpm *.jpg)"));

Или нескольких файлов:

QStringList files = QFileDialog::getOpenFileNames(
                        this,
                        "Select one or more files to open",
                        "/home",
                        "Images (*.png *.xpm *.jpg)");

Ну и закидываете это всё в QPixmap, ну а дальше уже как по накатанной.

t
  • May 22, 2017, 4:33 a.m.
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "/home", tr("Images (*.png *.xpm *.jpg)")); 
QPixmap inixmap = fileName; // Сохраняем его в изображение объекта QPixmap; 
QByteArray inByteArray; // Создаём объект QByteArray для сохранения изображения QBuffer 
inBuffer( &inByteArray ); 
inBuffer.open( QIODevice::WriteOnly ); // Открываем буффер 
inixmap.save( &inBuffer, "*.png *.xpm *.jpg" ); 
db->insertIntoTable(QDateTime::currentDateTime().toString("DSC02442.png"), inByteArray); 
model->select();

Так не верно?

Evgenii Legotckoi
  • May 23, 2017, 12:19 p.m.

Нет. не верно. Ошибка вот в этой строке:

QPixmap inixmap = fileName; // Сохраняем его в изображение объекта QPixmap; 

Выше я показывал, как что нужно путь передавать в качестве аргумента

QPixmap pixmap("/path/to/file.png");

То есть

QPixmap inixmap(fileName);
F
  • Jan. 22, 2018, 1:29 a.m.

Здравствуйте, также реализовывал приложение и понадобилось сохранить картинку. Но я брал напрямую через буфер

QPixmap pix(clipboard->pixmap());

            QBuffer inBuffer( &picture);
            inBuffer.open( QIODevice::WriteOnly );
            pix.save( &inBuffer, "PNG" );

Picture имеет тип QByteArray как и у Вас. При проверке наличии данных через консоль
qDebug() << "Здесь картинка из буфера:" << picture;
Содержимое отображается


Но при вставке данных в таблицу возникает ошибка

QSqlError("1", "Unable to execute statement", "unrecognized token: \"\u001A\"")


qDebug() << "That's pictire in binary code" <<picture;
str = "Insert into commands_" + profile_name +" (name, seq, pic) values (" + name + "," + content + ", " +picture+" );";
qDebug() << str;
if (query.exec(str))
{
qDebug() << "OK";
}

В таблице создано 1 поле для картинки qr = "CREATE TABLE commands_" +p[1] + " (name text NOT NULL, seq text NOT NULL, pic BLOB NOT NULL);";
F
  • Jan. 22, 2018, 1:31 a.m.

также вот вывод  текста самого запроса

"Insert into commands_one (name, seq, pic) values ('bnbvn','\n1. bvnb\n2. bvnb', �PNG\r\n\u001A\n );"

F
  • Jan. 22, 2018, 1:32 a.m.

Перед этим приложение работало отлично, ошибка связана именно с добавлением данного поля

Evgenii Legotckoi
  • Jan. 22, 2018, 2:42 a.m.

Добрый день!
Подготовьте изображение к вставке в базу данных через bind, как сделано в методе insertIntoTable, в данном примере.
Больше похоже на то, что вылетает неожиданный символа при попытке вставки в базу данных, bind может нивелировать эту проблему.

s
  • Feb. 22, 2018, 10:42 p.m.

Hi, could you please show how to delete file from image Blob?  also if the same image exist in Blob then don't over write..

Evgenii Legotckoi
  • Feb. 23, 2018, 4:22 a.m.

Hello.

You can use QSqlQuery for delete record with blob image. But you need know id of this record.

For example
bool DataBase::removeRecord(const int id)
{
    QSqlQuery query;
    query.prepare("DELETE FROM " TABLE " WHERE id= :ID ;");
    query.bindValue(":ID", id);
    if(!query.exec()){
        qDebug() << "error delete row " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}
For check of existing row you can use select sql request in QSqlQuery
s
  • Feb. 26, 2018, 6:55 a.m.

thanks, but Id should be the same the one as i select the image in tree view.

Evgenii Legotckoi
  • Feb. 26, 2018, 1:13 p.m.

Yes. But, if you use QSqlTableModel for TreeView, that you have id of image, I think. Or You should create hidden column with Id of image.

s
  • Feb. 26, 2018, 3:42 p.m.

Thanks, It would be really kool, if you fix the code, i am trying but as i am new i dont understand.

my problem is:  i want to delete the selected image from tree view.  and second problem is, i want to add an image, if that image is already exists. it should not shows into data base also not appear into tree view .. here are the files https://drive.google.com/file/d/1z7GG48GYkk4A3lsDKF9P6OqxHQ5ye_Fb/view
Evgenii Legotckoi
  • Feb. 26, 2018, 4:08 p.m.

I will see to these sources, but i have a little free time during the week usually. I will not promise anything.

s
  • Feb. 26, 2018, 6:09 p.m.

Thanks, i will be very glad :)  no problem take your time.

s
  • March 2, 2018, 7:29 p.m.

Hello Evileg if you could please make it in this weekend, i would be very glad :)

Evgenii Legotckoi
  • March 3, 2018, 5:45 p.m.

You have model for Picture List and you use id in first column. It means, you can use this model for getting ID of this picture.

model->data(model->index(selectedRow, 0)).toInt();
You need just to get this selectedRow , when you click in the table, this table can to invoke signal clicked(const QModelIndex & index ). You can get row from index. Via index.row()
s
  • March 5, 2018, 6:02 p.m.

Thanks for the help, but i still dont get that. i tried to complie it but i got errors. i want to share you my project file once again. here is my Email: soz7557@thi.de due to  privacy if you please send me msg i will send you the files on email. Thanks

s
  • March 5, 2018, 8:39 p.m.

Here i upload my code : https://stackoverflow.com/questions/49112880/qtableview-delete-selected-file-from-sql-database

Evgenii Legotckoi
  • March 6, 2018, 2:55 a.m.

1) You need create variable for storing of ID in MainWindow

int m_selectedId {0};
2) store this variable
void MainWindow::slotCurrentPic(QModelIndex index)
{
    m_selectedId = model->data(model->index(index.row(), 0)).toInt();
}

3) Delete ID
void MainWindow::on_Delete_Button_clicked(QModelIndex index)
{
    db->removeRecord(id);
}

And please, ask your next questions on the forum
s
  • March 6, 2018, 2:56 p.m.

Thanks but Now i have an error again id is not decleared parameter

s
  • March 7, 2018, 3:58 p.m.

My question is now, i am able to delete the images, but my Table view is not updating untill if dont do other function in my Gui

void MainWindow::on_Delete_Button_clicked()
{  

QMessageBox::StandardButton reply;
     reply = QMessageBox::question(this, "Delete", "Do you really want to delete?",
                                   QMessageBox::Yes|QMessageBox::No);
     if (reply == QMessageBox::Yes) {
       qDebug() << "Yes was clicked";
       QSqlRecord record;
           int i = ui->tableView->currentIndex().row(); 
           record = model->record(i);
           db->removeRecord(record.value("id").toInt(false));
           ui->tableView->reset();
        }
    else {
       qDebug() << "Yes was *not* clicked";

  }
}

 

Evgenii Legotckoi
  • March 7, 2018, 5:03 p.m.

I think, you should to select one more your model.

model->select();
s
  • March 20, 2018, 3:42 p.m.

Hello EVILEG,  i am having new problem. with data base,  please help to solve this issue


Data Base error

QSqlDatabasePrivate::database: unable to open database: " out of memory Error opening database "

Path work directory: "AIC_tool_DB/"

QFSFileEngine::open: No file name specified

QSqlQuery::prepare: database not open

error insert info ScreenTable

"No query Unable to fetch row"

Evgenii Legotckoi
  • March 21, 2018, 3 a.m.

Hello!

It can be anything. In fact, you missed rightly path to your database, I think.
s
  • April 12, 2018, 2:57 p.m.

Hello Evileg,  i hope you are doing great.  i want to add a Search function in this data base.  i have a Lind Edit. and i want to write  22.01.2018_18:00 and it shows me that picture.   please help how will the code for that

s
  • Oct. 6, 2019, 9:27 p.m.
  • (edited)

Добрый день Евгений. Спасибо за пример, все понятно. Попытался сделать по аналогии сохранение в базе MySQL заготовок отчетов excel, но MySQL ругается на нарушение в строке запроса. Я подозреваю, что видимо я не правильно читаю файл отчета. Я его читаю в масиив QByteArray и уже его пытаюсь передать через bindValue. Если не трудно ткните куда посмотреть реализацию. (Делал такое на VC++ 6.0, там работает без проблем, но хочу перейти на QT).
Update.
Мистика какая-то, решил перепроверить еще раз все работает (в замешательстве если честно), вот код:

QString m_file_name = QFileDialog::getOpenFileName(this, "Open file...", ".", "*.*");

    QFile qfile(m_file_name);

    QByteArray inByte;

    QBuffer inBuffer(&inByte);

    if(qfile.open(QIODevice::ReadOnly)){

        inByte = qfile.readAll();

        QVariant data(inByte);

        QString m_sql = "INSERT INTO file_data (file_data) VALUES(:data)";

        QSqlQuery query;

        query.prepare(m_sql);

        query.bindValue(":data", data);

        if(!query.exec()){
            QMessageBox::warning(nullptr, "error", query.lastError().text());
        }

        qfile.close();
    }    
Evgenii Legotckoi
  • Oct. 6, 2019, 11:44 p.m.

Может база не открылась в прошлый раз. Либо пересобрали проект. хз, если честно ))

Comments

Only authorized users can post comments.
Please, Log in or Sign up
B

C++ - Test 002. Constants

  • Result:16points,
  • Rating points-10
B

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
FL

C++ - Test 006. Enumerations

  • Result:80points,
  • Rating points4
Last comments
k
kmssrFeb. 8, 2024, 3:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 7:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 5:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 6:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
AC
Alexandru CodreanuJan. 19, 2024, 8:57 a.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCTDec. 27, 2023, 5:57 a.m.
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
ДмитрийJan. 10, 2024, 1:18 a.m.
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii LegotckoiDec. 12, 2023, 3:48 a.m.
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Follow us in social networks