C++/QT : Utiliser la classe QSettings, pour gérer des thèmes

Partager cet article

Temps estimé pour la lecture de cet article : 30 min

Aujourd’hui nous allons parler d’une classe fort utile de QT à savoir la classe QSettings. Cette dernière va vous permettre par exemple d’enregistrer les préférences de l’utilisateur. Si vous vouliez proposer plusieurs thèmes pour votre interface et que vous voulez conserver ce que l’utilisateur désire, la classe QSettings est là solution à vos problèmes. Voici un exemple d’interface que l’on va réaliser à la fin de l’article :

Thème noir

Thème noir

Thème blanc

Thème blanc

Explications QSettings

Bon commencçons par mettre un lien vers la bible (la documentation, hein !). Donc comme je le disais précédemment la classe permet d’enregistrer des paramètres pour votre application. L’avantage c’est que la classe est multi-plateforme donc votre sauvegarde fonctionnera quel que soit le système.

Où trouver les sauvegardes

qt-qsettings-location

Pour conserver les données même après avoir éteint son PC, il faut bien que ces dernières soient persistantes. Voici donc l’endroit où l’on peut trouver, ces sauvegardes en sachant que cela varie selon le système :

  • Sous les systèmes Unix (gnu/linux par exemple), par défaut, on retrouve les fichiers dans le dossier $HOME/.config/OurApplication/ourData.conf
  • Sous un système comme Windows, les données sont enregistrées dans le registre directement (pratique quand on veut faire des changements à la main…). Par défaut : HKEY_CURRENT_USER\Software\OurApplication\ourData

Exemple choix d’un thème

Nous allons réaliser une petite application utilisant la classe QMainWindow de QT et nous allons donc utiliser la fameuse classe QSettings dont je vous ai tant parlé pour sauvegarder le thème choisi par l’utilisateur. Commencer donc par créer un nouveau projet, vous devriez avoir un fichier main.cpp, mainwindow.cpp (ou tout autre nom que vous lui avez donné) et mainwindow.h.

/* main.cpp */

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

Nous allons maintenant rentrer dans le vif du sujet, voici mon header une fois modifié :

/* mainwindows.h */
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QCloseEvent>

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);

protected :
    void closeEvent(QCloseEvent* event);

private:
    void setThemes(int theme = BLACK);
    void createMenu();

public slots:
    void changeCurrentTheme(int newTheme);

private:
    enum Themes { BLACK, WHITE };
    int currentTheme;

    QWidget *centralPanel;
    QPushButton *customButton;
    QLineEdit *input;

    QAction *quit;
    QAction *black;
    QAction *white;
};

#endif // MAINWINDOW_H

Alors regardons un peu tout ça, on créé une énumération qui va nous permettre d’associer un int à un nom de thème. Il y a également un int qui va permettre de conserver le thème couramment utilisé. On retrouve les widgets QPushButton et QLineEdit de nos captures ainsi que trois QActions qui nous permettent de récupérer les actions du menu.

Contenu du fichier main

Passons maintenant au fichier cpp correspondant :

/* mainwindows.cpp */
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QSettings>
#include <QMenuBar>
#include <QDebug>
#include <QSignalMapper>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
    QVBoxLayout *mainLayout = new QVBoxLayout;

    centralPanel = new QWidget;
    centralPanel->setLayout(mainLayout);

    customButton = new QPushButton("Valider");
    input = new QLineEdit();

    mainLayout->addWidget(input);
    mainLayout->addWidget(customButton);
    setCentralWidget(centralPanel);

    QSettings settings("QSettingsExample", "QSettingsExampleSettings");
    settings.beginGroup("general");
    currentTheme = settings.value("currentTheme", BLACK).value<int>();
    settings.endGroup();

    createMenu();
    setThemes(currentTheme);
}

void MainWindow::closeEvent(QCloseEvent* event){
    QSettings settings("QSettingsExample", "QSettingsExampleSettings");
    settings.clear();

    settings.beginGroup("general");
    settings.setValue("currentTheme", currentTheme);
    settings.endGroup();

    event->accept();
}

void MainWindow::setThemes(int theme) {
    if(theme == BLACK) {
        input->setStyleSheet("background: #484848; padding: 3px 5px; border: 1px solid #E8E8E8; color: white;");
        centralPanel->setStyleSheet("background: #121212; border: 0; padding: 0; margin : 0;");
        menuBar()->setStyleSheet("QMenuBar { background: #121212; color: #B5B5B5; } QMenu { background: black; } QMenuBar::item { background-color: transparent; }");
    } else {
        input->setStyleSheet("background: white; padding: 3px 5px; border: 1px solid #484848; color: black;");
        centralPanel->setStyleSheet("background: white; border: 0; padding: 0; margin : 0;");
        menuBar()->setStyleSheet("QMenuBar { background: white; color: black; } QMenu { background: white; color: black; } QMenuBar::item { background-color: transparent; }");
    }
    customButton->setStyleSheet("QPushButton { background: #25A2D8; color: white; border: none; padding: 8px 15px; margin: 0; } QPushButton:hover { background: #45B2E9; } ");
}


void MainWindow::createMenu() {
    QMenu* file = menuBar()->addMenu("Fichier");
    quit = file->addAction("Quitter", this, SLOT(close()));
    quit->setShortcut(Qt::CTRL + Qt::Key_Q);

    QMenu* themes = menuBar()->addMenu("Thèmes");
    white = themes->addAction("Blanc");
    black = themes->addAction("Noir");

    QSignalMapper *mapper = new QSignalMapper;
    mapper->setMapping(white, WHITE);
    mapper->setMapping(black, BLACK);

    connect(white, SIGNAL(triggered()), mapper, SLOT(map()));
    connect(black, SIGNAL(triggered()), mapper, SLOT(map()));
    connect(mapper, SIGNAL(mapped(int)), this, SLOT(changeCurrentTheme(int)));
}

void MainWindow::changeCurrentTheme(int newTheme) {
    currentTheme = newTheme;
    setThemes(currentTheme);
}

Je passe la création du menu qui est assez simple à réaliser sous QT. On va par contre s’attarder à cette portion de code de la fonction createMenu() :

QSignalMapper *mapper = new QSignalMapper;
mapper->setMapping(white, WHITE);
mapper->setMapping(black, BLACK);

connect(white, SIGNAL(triggered()), mapper, SLOT(map()));
connect(black, SIGNAL(triggered()), mapper, SLOT(map()));
connect(mapper, SIGNAL(mapped(int)), this, SLOT(changeCurrentTheme(int)));

Signal et événement QT

Le principe est simple par défaut, on peut associer un signal à un clic sur le menu, oui mais voila nous on aimerait également que lors du clic sur notre menu, on envoie un int correspond au thème cliqué. Comment faire ? Et bien la solution c’est d’utiliser la classe QSignalMapper.

On associe à notre QAction la variable que l’on désire grâce à la méthode setMapping(). Une fois l’association réalisée, on doit signaler au QAction qu’ils doivent envoyer un signal vers l’objet QSignalMapper, on lui associe alors la fonction map(). On peut alors utiliser le signal mapped() en précisant l’argument que l’on veut et enfin renvoyer le tout vers une fonction personnalisée.

Ensuite, notre fonction changeCurrentTheme() va récupérer dans la variable currentTheme, le thème sélectionné et on appelle la fonction setThemes afin d’appliquer le css correspondant.

Sauvegarder une variable grâce à la classe QSettings

qt-qsettings-save

Avec ce que l’on a vu précédemment, on peut changer de thème mais le thème courant n’est pas celui lancé par défaut, pour sauvegarder le thème sectionné c’est là que va intervenir la classe QSettings notamment dans deux fonctions, dans le constructeur de notre QMainWindow mais également dans la fonction closeEvent.

   QSettings settings("QSettingsExample", "QSettingsExampleSettings");
   settings.beginGroup("general");
   currentTheme = settings.value("currentTheme", BLACK).value<int>();
   settings.endGroup();
void MainWindow::closeEvent(QCloseEvent* event){
    QSettings settings("QSettingsExample", "QSettingsExampleSettings");
 
    settings.beginGroup("general");
    settings.setValue("currentTheme", currentTheme);
    settings.endGroup();
 
    event->accept();
}

Donc tout d’abord le constructeur, on initialise l’objet en précisant le nom du logiciel ainsi, que le nom du fichier de configuration, comme ceci :

   QSettings settings("QSettingsExample", "QSettingsExampleSettings");

Ensuite, on précise que l’on créé un repère, (ce que QT appelle un groupe) dans le fichier de configuration nommée « général », grâce à la fonction beginGroup, ensuite on charge la donnée enregistrée, le second argument étant la valeur par défaut, si aucune variable n’est présente dans le fichier, comme par exemple au premier lancement de l’application ;). Et on ferme le groupe et c’est tout !

Enfin, la fonction closeEvent est un peu particulière, elle permet tout simplement de détecter quand votre fenêtre va se fermer et de lancer une fonction juste avant la fermeture, c’est donc là que l’on va enregistrer nos données.

Comme tout à l’heure, on initialise l’objet, on créé notre groupe, et on y place la valeur de la variable currentTheme. Il faut également accepter l’événement de la fermeture de la fenêtre parce que sinon, l’utilisateur ne pourra plus la fermer :3, sinon that’s it, et oui QT c’est magique !

Conclusion

Voilà qui conclu notre article et vous aura donné, je l’espère un exemple pratique de la classe QSettings. Pour ceux qui ont la flemme de suivre le tutoriel et veulent juste télécharger l’exemple du début, une archive est bien sur disponible.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.