Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Vložení a načtení obrázků do MySQL databáze - Qt

29.6.2018

MySQL je jednou z nejrozšířenějších serverových databází. Postupně si ukážeme jak načíst a zobrazit obrázky uložené jako BLOB data v různých typech aplikací (ve smyslu použití té které knihovny pro uživatelské rozhraní a přístup k databázi). Konkrétně půjde, alespoň prozatím o tyto:

  • Qt Widgets

  • WinForms v C#

  • C++ a „čísté“ Windows API

Ve všech případech budeme vycházet z jedné existující databáze s tabulkou, obsahující sloupce:

  • id (číslo - autoincrement)

  • nazev - text

  • obrazek - longblob

  • velikost – číslo (int)

V dnešním článku začneme aplikací využívající knihovnu Qt. Ukázkové příklady kódu budou z testovací aplikace obsahující na hlavním a jediném okně QListWidget ve kterém budou načteny ID jednotlivých záznamů (řádků tabulky) a při změně výběru seznamu načteme obrázek a zajistíme jeho zobrazení (a samozřejmě překreslování podle potřeby) na okně aplikace.

V aplikaci postavené na knihovně Qt využijeme třídu QSqlDatabase a související třídy pro podporu SQL databází.

Databázi si otevřeme do globál proměnné nebo členské proměnné vhodné třídy (bude typu QSqlDatabase).

db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("127.0.0.1");
db.setDatabaseName("nazev_db");
db.setUserName("user_db");
db.setPassword("heslo_db");
bool ok = db.open();
if (!ok)
    terminate();

Přidání obrázku ze souboru provedeme jeho načtením do objektu QByteArray který pak použijeme jako parametr instance třídy QSqlQuery jak je vidět v následujícím kódu který je volaný na výběr příslušné položky menu.

void okno_hlavni::on_pridat() noexcept
{
        QString soubor = QFileDialog::getOpenFileName(0,
                QString::fromStdWString(L"Vyber obrázek"), tr("r:\\fotografie"), tr("Images (*.png *.tiff *.tif *.jpg)"));
        QSqlQuery q = QSqlQuery(_databaze->db);
        QFile file(soubor);
        if (!file.open(QIODevice::ReadOnly))
                return;
        QByteArray inByteArray = file.readAll();
        q.prepare("insert into obrazky (nazev, obrazek, velikost) values (:nazev, :imageData, :velikost)");
        q.bindValue(":nazev", soubor);
        q.bindValue(":imageData", inByteArray);
        q.bindValue(":velikost", inByteArray.length());
        if (!q.exec())
        {
                QSqlError err = q.lastError();
                this->ui.edit_nazev->setText(err.driverText() + err.databaseText());
        }
        naplnit_seznam();
}

Fukce která naplní seznam (QListWidget) vypadá takto:

void okno_hlavni::naplnit_seznam() noexcept
{
        ui.list_seznam->clear();
        QSqlQuery q = QSqlQuery("select id, nazev from obrazky", _databaze->db);
        while (q.next())
        {
                ui.list_seznam->addItem(q.value(0).toString());
        }
}

Přidání obrázku tedy máme hotové, nyní zbývá při změně výběru načít aktuální obrázek do objektu QPixmap a zajistit jeho zobrazení a překreslování podle potřeby.

Funkci zmena_vyberu si připojíme k události currentItemChanged v konstruktoru třídy okna aplikace:

        connect(ui.list_seznam, &QListWidget::currentItemChanged, this, &okno_hlavni::zmena_vyberu);

Funkce pak vypadá takto?

void okno_hlavni::zmena_vyberu(QListWidgetItem *current, QListWidgetItem *previous) noexcept
{
        if (nullptr == current)
                return;
        QSqlQuery q = QSqlQuery("select * from obrazky where id = " + current->text(), _databaze->db);
        if (!q.next())
                return;
        this->ui.edit_nazev->setText(q.value(1).toString());
        this->pixmap.loadFromData(q.value(2).toByteArray());
        this->repaint();
}

Jak je zřejmé hned po načtení vyvoláme bezprostřední překreslení okna, o následná vyvolání se již stará systém, my musíme pouze napsat vlastní metodu paintEvent v naší třídě okna (odvozené od QMainWindow):

void okno_hlavni::paintEvent(QPaintEvent* q)
{
        QPainter p(this);
        if (this->pixmap.isNull())
                return;
        p.drawPixmap(300, 50, this->pixmap);
}