- 1. Структура проекта
- 2. mainwindow.ui
- 3. PicDataBase.pro
- 4. main.cpp
- 5. база даних.h
- 6. database.cpp
- 7. mainwindow.h
- 8. mainwindow.cpp
- 9. Підсумок
- 10. Відеоурок
Зображення у базі даних може бути збережено у форматі BLOB (англ. Binary Large Object - двійковий великий об'єкт), тобто у форматі масиву двійкових даних. Формат BLOB також підходить для збереження аудіо та відео даних у базах даних.
Розглянемо збереження та відновлення зображення з бази даних на прикладі наступного додатку, в якому скріншот з екрана комп'ютера буде зберігатися в базі даних і виводитися в спеціальний QLabel , коли відповідний запис буде обраний QTableView , який відображатиме всі записи про зображення, що зберігаються у базі даних.
Таким чином, маємо таблицю в базі даних з наступними полями:
- id (INTEGER) - ідентифікатор запису;
- Name (VARCHAR(255)) - назва файлу, що зберігається в базі даних;
- Pic (BLOB) - поле для зберігання зображень у базі даних.
Якщо не вдаватися докладно в урок, то найцінніша інформація на цю тему в кінці файлу mainwindow.cpp у наступних методах:
- MainWindow::on_screenButton_clicked()
- MainWindow::slotCurrentPic(індекс QModelIndex)
Структура проекта
- PicDataBase .pro - профайл проекту;
- database.h - заголовний файл беззмінного класу обгортки для роботи з базою даних;
- database.cpp - файл вихідних кодів класу-обгортки для роботи з базою даних;
- mainwindow.h - заголовний файл основного вікна програми;
- mainwindow.cpp -файл вихідних кодів основного вікна програми;
- mainwindow.ui - форма головного вікна програми;
- main.cpp - основний файл вихідних кодів програми.
mainwindow.ui
У дизайнері накидаємо наступну форму вікна програми.
PicDataBase.pro
У профайлі програми обов'язково підключаємо модуль sql для роботи з базами даних.
main.cpp
Файл створено за замовчуванням і не змінюється.
база даних.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)
#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.
Мистика какая-то, решил перепроверить еще раз все работает (в замешательстве если честно), вот код:
Может база не открылась в прошлый раз. Либо пересобрали проект. хз, если честно ))