- 1. Структура проекта
- 2. mainwindow.ui
- 3. PicDataBase.pro
- 4. main.cpp
- 5. database.h
- 6. database.cpp
- 7. mainwindow.h
- 8. mainwindow.cpp
- 9. Итог
- 10. Видеоурок
Изображение в базе данных может быть сохранено в формате 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 окна приложения. Также комментарии по работе приложения Вы можете услышать в видеоуроке.
Здравствуйте, при сборке выдаёт две такие ошибки C:\untitled1\mainwindow.cpp:99: ошибка: C2039: picLabel: Ґ пў«пҐвбп з«Ґ®¬ "Ui::MainWindow" C:\untitled1\mainwindow.cpp:99: ошибка: C2227: ўла ¦ҐЁҐ б«Ґў ®в "->setPixmap" ¤®«¦® гЄ §лў вм вЁЇ Є« бб , бвагЄвгал Ё«Ё ®ЎкҐ¤ЁҐЁп «ЁЎ® гЁўҐаб «мл© вЁЇ Как это исправить, помогите пожалуйста
И ещё не могли бы подсказать что нужно изменить в коде чтобы по нажатию на кнопку в БД загружался не скриншот, а выбранные и уже существующие изображения?
Заранее спасибо!
Интерфейс приложения сделать через графический дизайнер так, как сделано в данной статье. Убедиться, что существует QLabel в интерфейсе, с таким же названием, как в этой статье.
Создать QPixmap из файла, указав путь к файлу.
Спасибо большое! Но если я правильно понял, то для каждого файла нужно будет описывать каждый раз путь? А можно сделать как-то так чтобы можно было выбирать их?
Используйте QFileDialog класс. У него есть статические методы, которые позволяют открыть диалог, в котором можно выбрать либо один файл, либо несколько файлов.
Например, чтобы забрать имя одного файла:
Или нескольких файлов:
Ну и закидываете это всё в QPixmap, ну а дальше уже как по накатанной.
Так не верно?
Нет. не верно. Ошибка вот в этой строке:
Выше я показывал, как что нужно путь передавать в качестве аргумента
То есть
Здравствуйте, также реализовывал приложение и понадобилось сохранить картинку. Но я брал напрямую через буфер
Но при вставке данных в таблицу возникает ошибка
QSqlError("1", "Unable to execute statement", "unrecognized token: \"\u001A\"")
также вот вывод текста самого запроса
"Insert into commands_one (name, seq, pic) values ('bnbvn','\n1. bvnb\n2. bvnb', �PNG\r\n\u001A\n );"
Перед этим приложение работало отлично, ошибка связана именно с добавлением данного поля
Добрый день!
Подготовьте изображение к вставке в базу данных через bind, как сделано в методе insertIntoTable, в данном примере.
Больше похоже на то, что вылетает неожиданный символа при попытке вставки в базу данных, bind может нивелировать эту проблему.
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..
Hello.
thanks, but Id should be the same the one as i select the image in tree view.
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.
Thanks, It would be really kool, if you fix the code, i am trying but as i am new i dont understand.
I will see to these sources, but i have a little free time during the week usually. I will not promise anything.
Thanks, i will be very glad :) no problem take your time.
Hello Evileg if you could please make it in this weekend, i would be very glad :)
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.
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
Here i upload my code : https://stackoverflow.com/questions/49112880/qtableview-delete-selected-file-from-sql-database
1) You need create variable for storing of ID in MainWindow
Thanks but Now i have an error again id is not decleared parameter
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
I think, you should to select one more your model.
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"
Hello!
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
Добрый день Евгений. Спасибо за пример, все понятно. Попытался сделать по аналогии сохранение в базе MySQL заготовок отчетов excel, но MySQL ругается на нарушение в строке запроса. Я подозреваю, что видимо я не правильно читаю файл отчета. Я его читаю в масиив QByteArray и уже его пытаюсь передать через bindValue. Если не трудно ткните куда посмотреть реализацию. (Делал такое на VC++ 6.0, там работает без проблем, но хочу перейти на QT).
Update.
Мистика какая-то, решил перепроверить еще раз все работает (в замешательстве если честно), вот код:
Может база не открылась в прошлый раз. Либо пересобрали проект. хз, если честно ))