Evgenii Legotckoi
Evgenii LegotckoiNov. 25, 2015, 8:17 a.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 20, 2017, 5:46 p.m.

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

t
  • May 20, 2017, 5:53 p.m.

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

Evgenii Legotckoi
  • May 20, 2017, 11:54 p.m.

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

Evgenii Legotckoi
  • May 20, 2017, 11:57 p.m.

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

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

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

Evgenii Legotckoi
  • May 21, 2017, 12: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 21, 2017, 6:33 p.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, 2:19 a.m.

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

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

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

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

То есть

QPixmap inixmap(fileName);
F
  • Jan. 21, 2018, 2:29 p.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. 21, 2018, 2:31 p.m.

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

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

F
  • Jan. 21, 2018, 2:32 p.m.

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

Evgenii Legotckoi
  • Jan. 21, 2018, 3:42 p.m.

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

s
  • Feb. 22, 2018, 11:42 a.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. 22, 2018, 5:22 p.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. 25, 2018, 7:55 p.m.

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

Evgenii Legotckoi
  • Feb. 26, 2018, 2:13 a.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, 4:42 a.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, 5:08 a.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, 7:09 a.m.

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

s
  • March 2, 2018, 8:29 a.m.

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

Evgenii Legotckoi
  • March 3, 2018, 6:45 a.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, 7:02 a.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, 9:39 a.m.

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

Evgenii Legotckoi
  • March 5, 2018, 3:55 p.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, 3:56 a.m.

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

s
  • March 7, 2018, 4:58 a.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, 6:03 a.m.

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

model->select();
s
  • March 20, 2018, 5:42 a.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 20, 2018, 5 p.m.

Hello!

It can be anything. In fact, you missed rightly path to your database, I think.
s
  • April 12, 2018, 4:57 a.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, 11:27 a.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, 1:44 p.m.

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

Comments

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

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 22, 2024, 11:51 a.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiOct. 31, 2024, 2:37 p.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 8:19 a.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 7:51 a.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 11:02 a.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 24, 2024, 3:11 p.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 6:04 a.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 3:49 a.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 9:10 a.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks