Search code examples
c++qtqpixmapqlabel

Save and restore QLabels with images in Qt


Currently I am using QLabels to display images on the GUI Application I am developing. It works by when the user clicks on the "Add Graphic" button, a new dynamic label is created and appears on the window. When the user double clicks the label, the file dialog opens and the user can select the image to display which works fine.

The requirement of the application is to restore the image when the application shuts down so that the user doesn't have to input the image again, which I am struggling to come up with a solution. I'm trying to use QBuffers and QSettings to restore the images but there is a line of code which just crashes the application.

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    readSettings();
    ui->setupUi(this);

    // Set up the window size
    this->setWindowTitle(QString::fromUtf8("Raspberry PI GUI v1.0"));

    this->resize(800, 400);

    // Add label Button
    button = new QPushButton("Add Graphic", this);
    button->setGeometry(QRect(QPoint(10, 20), QSize(200, 50)));
    button->show();
    QObject::connect(button, SIGNAL(pressed()), this, SLOT(input_label()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::input_label()
{
    Label *label = new Label(this);
    label->setText("New Graphic");
    label->show();
}

void MainWindow::writeSettings()
{
    QByteArray bArray;
    QBuffer buffer(&bArray);
    buffer.open(QIODevice::WriteOnly);
    this->label->pixmap()->save(&buffer, "PNG"); // This line of code is crashing everything

    QSettings settings("Save state", "GUIApp");
    settings.beginGroup("MainWindow");
    settings.setValue("image", bArray);
}

void MainWindow::readSettings()
{
     QSettings settings("Save state", "GUIApp");

     settings.beginGroup("MainWindow");
     QByteArray image = settings.value("image", QByteArray()).toByteArray();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    writeSettings();
    event->accept();
}

label.cpp

#include "label.h"

//---------------------------------------
// Deconstructor
//---------------------------------------
Label::~Label()
{
}

void Label::mousePressEvent(QMouseEvent *event)
{
    // Move the coordinates on the main window
    m_nMouseClick_X_Coordinate = event->x();
    m_nMouseClick_Y_Coordinate = event->y();
}

void Label::mouseMoveEvent(QMouseEvent *event)
{
    //-------------------------------------------------------------
    // Allow the user to drag the graphics on the Display
    //-------------------------------------------------------------
    move(event->globalX()-m_nMouseClick_X_Coordinate-m_pParentWidget->geometry().x(),

        event->globalY()-m_nMouseClick_Y_Coordinate-m_pParentWidget->geometry().y());
}

void Label::mouseDoubleClickEvent(QMouseEvent *event)
{
    //QByteArray bArray;
    //QBuffer buffer(&bArray);
    //buffer.open(QIODevice::WriteOnly);

    //--------------------------------
    // Open file dialog
    //--------------------------------
    QFileDialog dialog(this);
    dialog.setNameFilter(tr("Images(*.png, *.dxf, *.jpg"));
    dialog.setViewMode(QFileDialog::Detail);
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open Images"),
        "/home",
        tr("Image Files (*.png *.jpg *.bmp)"));

    if (!fileName.isEmpty())
    {
        QImage image(fileName);
        Label::setPixmap(fileName);
        Label::adjustSize();
    }
}

Solution

  • QLabel::pixmap returns a null pointer if no pixmap has been set. In the same way, you should check if your properties file has the image entry set, and only in that case restore the image.

    void MainWindow::writeSettings()
    {
        if (this->label->pixmap() != nullptr) {
            QByteArray bArray;
            QBuffer buffer(&bArray);
            buffer.open(QIODevice::WriteOnly);
            this->label->pixmap()->save(&buffer, "PNG");
    
            QSettings settings("Save state", "GUIApp");
            settings.beginGroup("MainWindow");
            settings.setValue("image", bArray);
        }
    }
    
    void MainWindow::readSettings()
    {
        QSettings settings("Save state", "GUIApp");
    
        settings.beginGroup("MainWindow");
        QByteArray image = settings.value("image").toByteArray();
        if (!image.isNull()) {
            QPixmap pixmap;
            if (pixmap.loadFromData(image)) {
                this->label->setPixmap(pixmap);
            }
        }
    }
    

    Update

    When you create the QLabel you are storing it in a local variable, but it is never assigned to this->label, thus you probably are accessing garbage when doing any this->label->... operation.

    void MainWindow::input_label()
    {
        Label *label = new Label(this);
        label->setText("New Graphic");
        label->show();
        this->label = label; // try with this
    }
    

    This is a workaround, if this works you must check the rest of logic since then you can be adding several QLabels, but only saving the last one... the same when restoring the state: this->label may not be created, so you must create it, as well as to initialize this->label to nullptr in the constructor.

    Several labels

    For several labels, I'd suggest having a QList<QLabel*> container instead of the single QLabel member, and saving all of them. When restoring, just programmatically insert a new label before restoring the image.

    class MainWindow ... {
      // ...
      QList<QLabel*> labels;
      // ...
    }
    
    void MainWindow::input_label()
    {
        QLabel *label = new QLabel(this);
        label->setText("New Graphic");
        label->show();
        this->labels.append(label);
    }
    
    void MainWindow::writeSettings()
    {
        int i = 1;
        Q_FOREACH(auto label, labels) { // save all existing labels with an image
          if (label->pixmap() != nullptr) {
            QByteArray bArray;
            QBuffer buffer(&bArray);
            buffer.open(QIODevice::WriteOnly);
            label->pixmap()->save(&buffer, "PNG");
    
            QSettings settings("Save state", "GUIApp");
            settings.beginGroup("MainWindow");
            settings.setValue(QString("image-%1").arg(i), bArray);
            ++i;
          }
        }
    }
    
    void MainWindow::readSettings()
    {
        QSettings settings("Save state", "GUIApp");
    
        settings.beginGroup("MainWindow");
        int i = 1;
        while (true) {
          QByteArray image = settings.value(QString("image-%1").arg(i)).toByteArray();
          if (!image.isNull()) {
            QPixmap pixmap;
            if (pixmap.loadFromData(image)) {
              input_label(); // add new label
              this->labels.back()->setPixmap(pixmap);
            }
          } else break; // no more images
          ++i;
        }
    }