Composing your Gallery app

Alright, we completed AlbumListWidgetAlbumWidget, and PictureWidget. If you remember correctly, AlbumListWidget and AlbumWidget are contained in a widget called GalleryWidget.

Let's take a look at the GalleryWidget.ui file:

This widget does not contain any standard Qt widgets but only our created widgets. Qt provides two ways to use your own widgets in the Qt designer:

  • Promoting widgets: This is the fastest and easiest way
  • Creating widget plugin for Qt designer: This is more powerful but more complex

In this chapter, we will use the first way, which consists of placing a generic QWidget as a placeholder and then promoting it to our custom widget class. You can follow these steps to add the albumListWidget and the albumWidget objects to the GalleryWidget.ui file from the Qt designer:

  1. Drag and drop a Widget from Containers to your form.
  2. Set the objectName (for example, albumListWidget) from the Property Editor.
  3. Select Promote to... from the widget contextual menu.
  4. Set the promoted class name (for example, AlbumWidget).
  5. Check that header file is correct (for example, AlbumWidget.h).
  6. Click on the Add button and then click on Promote.

If you fail your widget promotion, you can always reverse it with Demote to QWidget from the contextual menu.

There is nothing really exciting in the header and implementation of GalleryWidget. We only provide setters for the model and model selection of Album and Picture to forward them to albumListWidget and albumWidget. This class also relays the signal pictureActivated from albumWidget. Please check the full source code if needed.

This is the final part of this chapter. We will now analyze MainWindow. Nothing is done in MainWindow.ui because everything is handled in the code. This is MainWindow.h:

#include <QMainWindow> 
#include <QStackedWidget> 
 
namespace Ui { 
class MainWindow; 
} 
 
class GalleryWidget; 
class PictureWidget; 
 
class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 
 
public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 
 
public slots: 
    void displayGallery(); 
    void displayPicture(const QModelIndex& index); 
 
private: 
    Ui::MainWindow *ui; 
    GalleryWidget* mGalleryWidget; 
    PictureWidget* mPictureWidget; 
    QStackedWidget* mStackedWidget; 
}; 

The two slots, displayGallery() and displayPicture(), will be used to switch the display between the gallery (album list with the album and thumbnail) and the picture ( full-size). The QStackedWidget class can contain various widgets but display only one at a time.

Let's take a look to the beginning of the constructor in the MainWindow.cpp file:

ui->setupUi(this); 
 
AlbumModel* albumModel = new AlbumModel(this); 
QItemSelectionModel* albumSelectionModel = 
    new QItemSelectionModel(albumModel, this); 
mGalleryWidget->setAlbumModel(albumModel); 
mGalleryWidget->setAlbumSelectionModel(albumSelectionModel); 

First, we initialize the UI by calling ui->setupUi(). Then we create AlbumModel and its QItemSelectionModel. Finally, we call the setters of GalleryWidget that will dispatch them to the AlbumListWidget and AlbumWidget objects.

Continuing our analysis of this constructor:

PictureModel* pictureModel = new PictureModel(*albumModel, this); 
ThumbnailProxyModel* thumbnailModel = new ThumbnailProxyModel(this); 
thumbnailModel->setSourceModel(pictureModel); 
 
QItemSelectionModel* pictureSelectionModel = 
    new QItemSelectionModel(pictureModel, this); 
 
mGalleryWidget->setPictureModel(thumbnailModel); 
mGalleryWidget->setPictureSelectionModel(pictureSelectionModel); 
mPictureWidget->setModel(thumbnailModel); 
mPictureWidget->setSelectionModel(pictureSelectionModel); 

The behavior with Picture is close to the previous one with Album. But we also share ThumbnailProxyModel, which is initialized from PictureModel, and its QItemSelectionModel with PictureWidget.

The constructor now performs the signal/slot connections:

connect(mGalleryWidget, &GalleryWidget::pictureActivated, 
        this, &MainWindow::displayPicture); 
 
connect(mPictureWidget, &PictureWidget::backToGallery, 
        this, &MainWindow::displayGallery); 

Do you remember the pictureActivated() function? This signal is emitted when you double-click on a thumbnail in albumWidget. We can now connect it to our displayPicture slot, which will switch the display with the picture at its full size. Do not forget to also connect the backToGallery signal emitted when the user clicks on the backButton from PictureWidget. It will switch again to display the gallery.

The last part of the constructor is easy:

mStackedWidget->addWidget(mGalleryWidget); 
mStackedWidget->addWidget(mPictureWidget); 
displayGallery(); 
 
setCentralWidget(mStackedWidget); 

We add our two widgets, mGalleryWidget and mPictureWidget, to the mStackedWidget class. When the application starts, we want to display the gallery, so we call our own slot displayGallery(). Finally, we define mStackedWidget as the main window's central widget.

To finish this chapter, let's see what happens in these two magic slots that allows to switch the display when the user requests it:

void MainWindow::displayGallery() 
{ 
    mStackedWidget->setCurrentWidget(mGalleryWidget); 
} 
 
void MainWindow::displayPicture(const QModelIndex& /*index*/) 
{ 
    mStackedWidget->setCurrentWidget(mPictureWidget); 
} 

That seems ridiculously easy. We just request mStackedWidget to select the corresponding widget. As PictureWidget shares the same selection model with other views, we can even ignore the index variable.