Qt/C++ - Урок 029. Изображение в базе данных в Qt – Сохранение и Восстановление

РуководствоQtBLOB, QPixmap, Qt, SQL, SQLite, база данных, изображение1226

Изображение в базе данных может быть сохранено в формате BLOB (англ. Binary Large Object - двоичный большой объект), то есть в формате массива двоичных данных. Формат BLOB также подходит для сохранения аудио и видео данных в базах данных.

Рассмотрим сохранение и восстановление изображения из базы данных на примере следующего приложения, в котором скриншот с экрана компьютера будет сохраняться в базе данных и выводиться в специальный QLabel , когда соответствующая запись будет выбрана в QTableView , который будет отображать все записи об изображения, хранящихся в базе данных.

Таким образом имеем таблицу в базе данных со следующими полями:

  • id (INTEGER) - id записи;
  • Name (VARCHAR(255)) - название хранящегося в базе данных файла;
  • Pic (BLOB) - поле для хранения изображений в базе данных.

Если не вдаваться подробно в урок, то самая ценная информация по данной теме в конце файла mainwindow.cpp в следующих методах:

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

Структура проекта

  • PicDataBase .pro - профайл проекта;
  • database.h - заголовочный файл бессменного класса обёртки для работы с базой данных;
  • database.cpp - файл исходных кодов класса-обёртки для работы с базой данных;
  • mainwindow.h - заголовочный файл основного окна приложения;
  • mainwindow.cpp -файл исходных кодов основного окна приложения;
  • mainwindow.ui - форма главного окна приложения;
  • main.cpp - основной файл исходных кодов приложения.

mainwindow.ui

В дизайнере  набрасываем следующую форму окна приложения.

PicDataBase.pro

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

main.cpp

Файл создан по умолчанию и не подвергается изменениям.

database.h

Для работы с базой данных использую уже привычный класс-обёртку DataBase .

ВНИМАНИЕ!!! - файл базы данных создается в папке C:/example , поэтому или поправьте метод DataBase::connectToDataBase() или создайте папку example на диске 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"               // Третья колонка

// Первая колонка содержит Autoincrement ID

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()
{
    /* В данном случае используется формирование сырого SQL-запроса
     * с последующим его выполнением.
     * */
    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)
{
    /* Запрос SQL формируется из QVariantList,
     * в который передаются данные для вставки в таблицу.
     * */
    QSqlQuery query;
    /* В начале SQL запрос формируется с ключами,
     * которые потом связываются методом bindValue
     * для подстановки данных из QVariantList
     * */
    query.prepare("INSERT INTO " TABLE " ( " TABLE_NAME ", "
                                             TABLE_PIC " ) "
                  "VALUES (:Name, :Pic)");
    query.bindValue(":Name",        data[0].toString());
    query.bindValue(":Pic",         data[1].toByteArray());

    // После чего выполняется запросом методом exec()
    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

Помимо привычных объектов DataBase и QSqlTableModel , а также методов настройки модели и представления, которые использовались в предыдущих статьях по QSqlRelationalTableModel и QSqlQueryModel , в данном файле присутствуют слоты для обработки нажатия по кнопке и клику по записи в таблице.

Первый слот выполняет создание скриншота экрана и добавление его в базу данных, а второй слот восстанавливает изображение из базы данных, беря его из объекта QSqlTableModel в QTableView и помещая его в QLabel в главном окне приложения.

#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:
    // Слот для записи скриншота в базу данных
    void on_screenButton_clicked();
    // Слот для получения изображения из базы данных
    void slotCurrentPic(QModelIndex index);

private:
    Ui::MainWindow *ui;
    /* В проекте используются объекты для взаимодействия с информацией в базе данных
     * и моделью представления таблицы базы данных
     * */
    DataBase        *db;
    QSqlTableModel  *model;

private:
    /* Также присутствуют два метода, которые формируют модель
     * и внешний вид TableView
     * */
    void setupModel(const QString &tableName, const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

Вся главная работа с базой данных и изображением производится в следующих методах:

  • 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);

    /* Устанавливаем названия колонок в таблице с сортировкой данных
     * */
    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);     // Устанавливаем модель на TableView
    ui->tableView->setColumnHidden(0, true);    // Скрываем колонку с id записей
    ui->tableView->setColumnHidden(2, true);    // Скрываем колонку с изображением
    // Разрешаем выделение строк
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    // Устанавливаем режим выделения лишь одной строки в таблице
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    // Устанавливаем размер колонок по содержимому
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);  // Запрещаем редактирование
    ui->tableView->horizontalHeader()->setStretchLastSection(true);     // Растягиваем последнюю колонку по всему tableView

    /* Подключаем сигнал об изменении выбора текущей строки в таблицу
     * к СЛОТу для установки изображения в picLabel
     * */
    connect(ui->tableView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
            this, SLOT(slotCurrentPic(QModelIndex)));

    model->select(); // Делаем выборку данных из таблицы
}

void MainWindow::on_screenButton_clicked()
{
    /* Делаем скриншот экрана и сохраняем его в объект QByteArray,
     * для этого ...
     * */
    QScreen *screen = QApplication::primaryScreen();    // Берём объект экрана
    QPixmap inPixmap = screen->grabWindow( 0 );         // Сохраняем его в изображение объекта QPixmap
    QByteArray inByteArray;                             // Создаём объект QByteArray для сохранения изображения
    QBuffer inBuffer( &inByteArray );                   // Сохранение изображения производим через буффер
    inBuffer.open( QIODevice::WriteOnly );              // Открываем буффер
    inPixmap.save( &inBuffer, "PNG" );                  // Записываем inPixmap в inByteArray

    // Записываем скриншот в базу данных
    db->insertIntoTable(QDateTime::currentDateTime().toString("dd.MM.yyyy_hh:mm:ss.png"), inByteArray);

    // Делаем выборку таблицы из Базы Данных
    model->select();
}

void MainWindow::slotCurrentPic(QModelIndex index)
{
    QPixmap outPixmap = QPixmap(); // Создаём QPixmap, который будет помещаться в picLabel
    /* Забираем данные об изображении из таблицы в качестве QByteArray
     * и помещаем их в QPixmap
     * */
    outPixmap.loadFromData(model->data(model->index(index.row(), 2)).toByteArray());
    // Устанавливаем изображение в picLabel
    ui->picLabel->setPixmap(outPixmap.scaled(400,300));
}

Итог

В результате получаем приложение, которое позволяет сохранять скриншоты экрана в базе данных и восстанавливать данное изображение, отображая его в QLabel окна приложения. Также комментарии по работе приложения Вы можете услышать в видеоуроке.

Видеоурок

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
Последние комментарии
  • EVILEG
  • 24 апреля 2017 г. 20:44

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

У меня пока мыслей на этот счёт нет ((

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

Пробовал играться с шарком, либо я криво смотрел, либо почему-то POST запросы на oauth.yandex.ru не летят, хотя должны постом лететь, я и исходники QOAuth2AuthorizationCodeFlow ковырял на пред...

  • EVILEG
  • 24 апреля 2017 г. 13:39

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

Возможно, стоит Wireshark`ом запросы посмотреть. В чём отличие идёт аякс запроса от запроса из библиотеки. Возможно, что не хватает какой-нибудь заголовочной информации.

Сейчас обсуждают на форуме
  • Arrow
  • 1 мая 2017 г. 1:00

Callback функции

Первый раз пытаюсь работать с Callback функциями. Помогите понять, что и где я не так делаю. Вот код: ReverseString.h #ifndef REVERSESTRING_H#define REVERSESTRING_H#includ...

  • EVILEG
  • 30 апреля 2017 г. 10:22

QMenu

Вам не кажется, что вы увлеклись со скриншотами? Добавляйте голый программный код в сообщения через специальный диалог. Это кнопка с двумя фигурными скобками. Иногда требуется повторить код у ...

  • CJIaBiK
  • 29 апреля 2017 г. 21:07

QPushButton

Спасибо

  • CJIaBiK
  • 29 апреля 2017 г. 17:57

QWebEngineView

спасибо помогло

  • EVILEG
  • 29 апреля 2017 г. 17:47

Ошибка

Такое случается, когда добавляете новые файлы, а объектный файл, в данном случае mainwindow.obj, не пересобирается как положено. Приходится чистить сборку.