Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt : 5.10.0 - Qt Creator : 4.5.0 - Qt Installer : 3.0.2 - JOM : 1.1.2 - Qt Build suite : 1.7.0 - VS Qt 5 : 2.0.0

Les préférences d'une appli : un QSettings en singleton ?

Bonjour !

Je continue à abuser de demandes de conseil. Quand on a des cadors en C++ pas loin, il faut en profiter ! ;)

Le titre résume pas mal ma question. Concrètement c'est pour une appli particulière, Nem-Nem, un jeu de dés développé il y a plusieurs années que je viens de passer en Qt5. J'en profite pour nettoyer certains modules (les rendre plus propres).

Les préférences de l'appli, très nombreuses et de types variés (QSize, QColor, QPoint, numériques, chaînes, booléens mais aussi historique des scores...) sont stockées dans un QSettings lu au démarrage de l'appli, écrit à la fin, et modifié dans un écran dédié.

De nombreuses classes dépendent de ces options (les dés, la piste, la feuille de score, les sons...) et n'y accèdent pas elles-mêmes, mais disposent de SLOTs genre "setColor". Ça ne me plaît pas et j'ai l'occasion de changer.

Donc j'ai pensé à une classe Preferences héritant de QSettings et sous forme de singleton, lue et écrite en temps réel sur le disque (pas de souci de performance), dans laquelle les objets iraient piocher leurs caractéristiques quand ils en ont besoin.

Qu'en pensez-vous ? Comment faites-vous ?

Merci !!

PS il me semble avoir déjà discuté de cela, ici ou ailleurs, mais il y a assez longtemps... :8
«1

Réponses

  • Tu n'as pas forcement besoin d'un singleton (ie assurer l’unicité ET l’accès global), ni de l’héritage (tu ne souhaites pas modifier le comportement de QSettings), mais uniquement accéder a QSettings a plusieurs endroits. Le DP Service Locator est probablement plus adapté. Cf http://gameprogrammingpatterns.com/service-locator.html
  • June 2015 modifié
    Dans mes softs, j'ai une classe PreferencesManager qui utilise QSettings et cela sert à stocker tous les types (Grâce à QVariant). Il utilise QSettings mais n'hérite pas de QSettings.

    //le .h
    https://github.com/obiwankennedy/rolisteam/blob/master/src/preferences/preferencesmanager.h

    //le .cpp
    https://github.com/obiwankennedy/rolisteam/blob/master/src/preferences/preferencesmanager.cpp


    Apres pour l'utiliser, je recupère l'instance dans une classe utilisatrice:

    m_preferences = PreferencesManager::getInstance();
    puis, je demande au systeme la valeur d'un élement:

    m_color = m_preferences->value("BackGroundColor",QColor(GRAY_SCALE,GRAY_SCALE,GRAY_SCALE)).value<QColor>();
    Si la valeur est définie, la valeur est enregistrée est retournée. Sinon, c'est la valeur par défaut.

    Quand l'utilateur modifie la valeur (par la fenetre de préferences ou autres) on enregristre la valeur:

    m_preferences->registerValue("BackGroundColor",color,true);
    On peut enregistrer une valeur avec la clé (1er parametre), la valeur (2eme param) et le 3eme parametre c'est un bool pour savoir si on ecrase la valeur si elle est déjà enregistrer).

    Bien sûr, le systeme sauvegarde toutes les valeurs enregistrer dans le fichier settings à la fermeture de l'application.

    Après, j'ai ajouté un système de listener qui permet de s'inscrire et d'etre notifier quand une valeur change. C'est truc utile pour synchroniser mes préférences et le reste de mon application.
    Quand l'utilisateur de rolisteam change la position ou l'image de fond de l'application, c'est automatiquement appliqué par l'application.

    J'ai fait le choix de faire une fenetre de préferences statique dans le sens qu'elle n'est pas généré automatiquement car je voulais garder la main sur l'affichage. Puis, les préferences ont été de plus en plus conséquentes. Au début, j'en avais trop peu pour que cela soit intéressant.
    Cela serait une voie d'amélioration intéressante.

    En tout cas, je suis très content de ce systeme. Cela marche bien. J'ai fais une présentation assez légère. J'invite à regarder les fichiers main.cpp et mainwindow.cpp pour voir comment mon préférences manager s'initialise et se sauvegarde.

    https://github.com/obiwankennedy/rolisteam/blob/master/src/main.cpp
    https://github.com/obiwankennedy/rolisteam/blob/master/src/mainwindow.cpp
  • Je vais étudier avec intérêt cette notion de service.

    Je ne suis pas obligé effectivement de dériver de QSettings, je peux simplement avoir un membre de ce type.
    On peut aussi considérer que QSettings est une classe permettant d'enregistrer, conserver et restituer des informations d'une session de l'appli à une autre, et que je la dérive pour lui ajouter des fonctions d'enregistrement et de restitution de données propres à l'appli (setDieColor, messagesOnBoard par exemple). Non ?
  • obiwankennedy ton système me rappelle des choses. Il me semble bien que c'est avec toi que j'en avais parlé. :)
  • June 2015 modifié
    service locator ou singleton c'est quand meme très proches. Cela permet d'avoir un element minimaliste qui gère l'unicité (tout le coté static) et un service qui n'a pas à géré toutes les regles d'unicité et d'acces.

    Cela passe par une méthode static pour retourner une instance d'un service static. C'est tout a fait envisagable de modifier mon service preferencesManager et le mettre dans un locator.

    J'ai beaucoup amélioré le truc avec la notion de listener sur mon systeme. Cela permet des trucs tres tres sympa.
  • Si j'ai bien compris les grandes lignes de "service locator", les classes fournies par ce service ont au moins une contrainte : il faut obligatoirement que leur constructeur soit private.

    Je m'explique. Une classe a besoin du service audio, le locator lui donne un Audio*. Parfait. Mais forcément la classe cliente a besoin de manipuler ce service, donc un
    #include "audio.h"
    est nécessaire.
    Mais avec l'include, un new Audio devient possible... ce qui n'est pas souhaitable... donc le constructeur de Audio doit être private.
  • Plus personne ne moufte... J'ai dit une connerie ?
  • @obiwankennedy A tout hazard, tu aurais encore cette classe ?
    Je commence à comprendre ce qu'est un singleton et j'ai de gros problèmes avec mes QSettings ...
  • Un singleton n'est pas utile pour gérer les QSettings.
    QSettings est une sorte de singleton déguisé.

    Le pattern singleton a eu son heure de gloire il y a quelques années, mais en pratique, il y a peu de cas ou ce pattern se justifie vraiment.

    Crée un nouveau topic en explicitant ton problème.
  • 18 Apr modifié
    Bien sûr, je l’utilise régulièrement dans rolisteam et d’autres projets.

    Tu trouveras le code ici:
    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferencesmanager.h
    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferencesmanager.cpp

    En gros, tu récupères une instance par:
    PreferencesManager* m_preferences = PreferencesManager::getInstance();
    Tu enregistres une valeur par: m_preferences->registerValue(QString key,QVariant value);
    Tu récupères la valeur par un : m_preferences->value(QString key,QVariant defaultValue);


    Au lancement de ton programme, tu assures d’avoir appeler : m_preferences->readSettings(QSettings & settings);
    et à la fermeture: m_preferences->writeSettings(QSettings & settings);
    Cela permet de sauvegarder les préférences dans un QSettings pour les garder la prochaine fois que tu utilises le logiciel.

    Après j’ai ajouté un système de listener qui permet de prévenir dynamiquement un élément qui doit être notifier au changement de valeur. Cela permet de prendre en compte les changements sans redémarrer l’application.

    Le code du listener:
    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferenceslistener.h
    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferenceslistener.cpp

    On s’enregistre comme cela:
    m_preferences->registerListener("HeartBeatStatus",this);
    m_preferences->registerListener("HbFrequency",this);

    Le 1er parametre est la clé dont tu surveilles la valeur.
    Le 2ème «this» est une classe implémentant la classe abstraite: PreferencesListener
    La notification se fait par: preferencesHasChanged avec en argument la clé qui a changé.




    Un exemple simple d’utilisation:
    https://github.com/Rolisteam/rolisteam/blob/master/src/network/heartbeatsender.cpp
    https://github.com/Rolisteam/rolisteam/blob/master/src/network/heartbeatsender.h
    Cette classe permet d’envoyer un message «signe de vie» sur le réseau toutes les N secondes. L’utilisateur peut définir depuis la fenêtre des préférences d’activer ou pas la fonctionnalité et il peut définir la période du message.


    Si tu veux voir ma fenêtre de préférences:

    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferencesdialog.cpp
    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferencesdialog.h
    https://github.com/Rolisteam/rolisteam/blob/master/src/preferences/preferencesdialogbox.ui
  • //qRegisterMetaTypeStreamOperators("CleverURI");
    //qRegisterMetaTypeStreamOperators("CleverUriList");
    blog.rolisteam.org/file/0001-fix-compilation-issue-under-Qt5.5.patch
    #include "data/cleveruri.h"
    Afterward, the type can be streamed using QMetaType::load() and QMetaType::save(). These functions are used when streaming a QVariant.
    C'est pour enregistrer des liens Html dans des QVariant ?
    Je ne suis pas sûr de comprendre (mais je crois que cela n'a aucune importance pour moi).

    /////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////
    QSettings n'accepte qu'une seule clé, c'est bien cela ? (fausse question).

    readSettings(QSettings & settings); /// writeSettings(QSettings & settings);
    Que ce passe t'il si les deux QSettings ci dessus se rapporte au même fichier ?
    1) J'écris les valeurs par default dans la QMap.
    2) Je lis et importe les settings d'un fichier. Ces données écrasent les valeurs par default dans la QMap.
    Puis
    m_preferences->writeSettings(QSettings & settings);
    3)Les valeurs sont écrasées "dans" le fichier.

    Par contre, les valeurs supprimées ne disparaissent jamais du fichier, non ?
    (C'est vraiment juste pour comprendre. de mon coté je commençais toujours l'écriture par settings.clear();). j'ai l'impression qu'une clé supprimée au second démarrage ne sera jamais supprimée du fichier ini. Tu recherches certainement ce comportement.
    /////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////
    La seule et vraie question :

    De mon coté, je me suis très souvent retrouvé avec des clés ou des valeurs écrites à moitié dans le fichier Ini (je n'ai jamais trouvé la raison).
    Du coup, a chaque utilisation, je faisais un settings.sync(); et j'essayais de tester toutes les erreurs possibles. De ton coté, tu ne les cherchent même pas. C'est que tu n'as jamais eu d'erreur ?
    if (settings.status() == QSettings::NoError && settings.isWritable() && settings.format() != QSettings::InvalidFormat)
    T'es tu déjà retrouvé avec ce type de problème ?
    (J'écrivais dans mon QSettings lors de la destruction de mon programme et ce dernier plantait et plante encore beaucoup).
    /////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////
    Sinon, c'est quoi ceci : #include "preferences/rolisteamtheme.h". :\">
    Un model appliqué à tout ton programme ?
    Tu y définis tes boutons et tout ca ?
    Je vais fouiller ton site ;)

    Merci beaucoup @obiwankennedy !
    Je vais regarder ton preferenceslistener.
  • 23 Apr modifié
    En fait, si tu veux enregistrer des types perso avec QSettings tu dois définir les opérateurs << et >> sur des QDataStream.
    CleverUri est une classe qui gère une uri (chemin vers un fichier) en gardant son type : image, plan, scénario…
    Mes fichiers récents par exemple son des CleverUri pas de simple chemin.
    Il faut dire à Qt que les opérateurs existent avec la méthode: qRegisterMetaTypeStreamOperators

    Je ne pense pas que cela te soit utile.

    ________________

    Tu peux créer un groupe de valeur avec QSettings.
    Par exemple, les valeurs liées aux préférences peuvent être dans un groupe "préférences".

    1/ Après, il est important que cela soit les mêmes QSettings. Pour lire, les préférences de la derniere fois ou le logiciel a été lancé.

    2/ Elle n’ecrase pas la valeur de la QMap, la map est vide.

    3/ Je ne dis pas écrasée mais enregistrées.

    Après si tu as besoin de valeur variable dans une session de ton application mais que ces valeurs ne soient pas enregistrées à la fin. Tu peux modifier la classe pour gérer cela.
    Un petit bool à passer et une nouvelle et le stockage des parametre temp à créer. Rien de mortel.

    Il faut voir que j’utilise QSettings mais on pourrait modifier la classe pour lire/écrire depuis autres choses:
    Json, base de données…

    Cela n’a aucun impact sur le système de préférences. PreferencesManager s’inspire du fonctionnement de QSettings mais il n’a pas le même but.
    _________

    Pour faire disparaitre les valeurs, j’ai une option de reset du fichier de préférences ou l’on prend que les paramètres par défauts.
    Cela permet de tester le soft propre. Comme un nouvel utilisateur peut le tester.
    ________


    J’ai jamais eu de problème après j’applique à la lettre la littérature sur QSettings.
    Le sync est inutile quand ton objet est détruit.
    Donc, je n’ai jamais eu de problème.

    __________

    "preferences/rolisteamtheme.h" c’est une classe pour theme rolisteam. Cela peut contenir une palette de couleur pour le style ou du code CSS. Tu peux installer rolisteam sur ta bécane, et regarder dans la page de préférences. Il y a 3 thèmes par défaut. (J’en cherche d’autres).



  • 25 Apr modifié
    On s’enregistre comme cela:
    m_preferences->registerListener("HeartBeatStatus",this);
    m_preferences->registerListener("HbFrequency",this);
    Ca s'appelle du Key Value Observing
    C'est un concept intéressant mais assez lourd à gérer.
    Je réfléchissais à un moyen de faire ce genre de choses dans Qt sans que cela soit trop lourd dingue.

    J'ai déjà un système de notification basé sur des CustomEvents.
    En ajoutant une simple classe ValueChangedEvent j'arrive à un résultat assez similaire.
    Ce n'est pas du Key Value Observing, mais ça y ressemble en termes de fonctionalités.

    Je vous propose un projet exemple.
    A partir du menu, on peut ouvrir deux dialogues de Prefs,
    - un premier utilisant une notification globale envoyée lorsqu'on clique sur les boutons OK ou Apply
    - un deuxième utilisant une notification par valeur, permettant une mise à jour dynamique de la fenêtre de Document.

    Un aspect que je juge très important avec mon système de notification, c'est qu'il élimine les dépendances entre observeur et observé. Les fenêtres s'enregistrent pour recevoir les notifs des dialogues sans les connaître, c'est l'EventCenter qui sert d'intermédiaire.

  • 26 Apr modifié
    Pas possible de joindre un fichier avec QWebEngine !
    Ca marchait, mais ça marche plus, quelle bouse le WebEngine, c'est de plus en plus bugué, désespérant X(

    [EDIT]
    Nouvelle version dispo ici:
    PrefsDialog2
  • Oui, le pattern observer est celui utilisé et adapté pour passer une key. Listener est un autre nom pris du Java.

    Dans rolisteam, il y a également un systeme basé sur des customEvents non pour les préférences mais pour la reception de données via socket.
    C'est utilisé pour les tchat. J'en avais parler avec un mec de chez Nokia (à l'époque ou Qt leur appartenait). C'était un contributeur Qt assez important. Il m'avait déconseillé d'utiliser des customs Events.
    Surtout si la classe d'event contient des données importantes.

    De plus, le système d'event ne marche qu'avec des QObject ou dérivés. Quand on a une classe maison qui n'en est pas. Il faut soit qu'un QObject consomme l'event et transfère l'info à la classe en question.
    Soit, il faut rendre la classe une sous classe de QObject.



  • Bonjour.

    J'ai fait une bêtise ... mais laquelle ?
    J'ai regardé comment, dans rolisteam, tu faisais pour te servir de ta classe "preferencesmanager" @obiwankennedy.
    Du coup, j'ai compris que peut-être mon problème venait du fait que j'initialisais pas assez tôt cette classe :
    int main(int argc, char *argv[])
    {
    ....
    QApplication application(argc, argv);
    Preferences* preferences = Preferences::getInstance();
    preferences->readSettings();
    .....
    }
    ou bien que je la détruisais trop tôt :
    void Dispatch::fermetureDuProgramme(QCloseEvent* closeEvent)
    {
    preferences = Preferences::getInstance();
    preferences->writeSettings();
    QApplication::quit();
    }
    Après, comme je n'avais toujours pas confiance dans la classe QSettings, je l'ai tout simplement virée :
    bool Preferences::readSettings()
    {
    if (QFile(value("cheminDuFichierIni").toString()).exists())
    {
    qDebug()<<"QFile Settings exist";
    QFile file(value("cheminDuFichierIni").toString());
    if (file.open(QIODevice::ReadWrite))
    {
    QByteArray byteArray = file.readAll();
    QDataStream dataStream(&byteArray, QIODevice::ReadOnly);
    dataStream >> (*m_optionDictionary);
    file.close();
    return true;
    }
    }
    }
    Normalement, jusque ici je suis dans les clous.
    Je pense que la premiere erreur est ici (le programme plante à la cloture) :
    class Preferences
    {
    public:
    QVariantMap* retourneMap();
    }

    QVariantMap* Preferences::retourneMap()
    {
    return m_optionDictionary;
    }
    Je crois qu'il faut que la fonction QVariantMap* retourneMap(); soit static ? Mais du coup, il faut quel opérateur pour return m_optionDictionary; ???
    Je comprends pas du tout.

    Pour la seconde erreur, c'est encore pire :
    Je me retrouve avec 53: avertissements dont le plus signifiant est certainement : "QT_HAS_BUILTIN" redefined.

    J'ai essayé de trouver la ligne qui provoque cela mais ce n'est pas évident.
    Du coup j'ai viré toutes les classes de mon programme et normalement l'erreur se situe bien dans celle-ci : (J'ai l'impression que c'est le singleton).
    #ifndef PREFERENCES_H
    #define PREFERENCES_H

    #include <QVariant>
    #include <QString>
    #include <QMap>
    #include <QDir>
    #include <QDebug>
    #include <QProcess>
    #include <QApplication>
    #include <QDateTime>
    #include <QStorageInfo>
    #include <QDataStream>

    #include "windef.h"
    #include "windows.h"

    class Preferences
    {
    public:

    static Preferences* getInstance();
    ~Preferences();
    bool registerValue(QString key,QVariant value, bool overwrite = true);
    const QVariant value(QString key);
    bool readSettings();
    bool writeSettings();
    bool contains(QString key);
    bool remove(QString key);
    //QVariantMap* retourneMap();

    private:

    Preferences();

    private:

    QVariantMap* m_optionDictionary;
    static Preferences* m_singleton;
    };
    #endif // PREFERENCES_H
    #ifndef PREFERENCES_CPP
    #define PREFERENCES_CPP

    #include "preferences.h"

    Preferences::Preferences()
    : m_optionDictionary(NULL)
    {

    }
    Preferences::~Preferences()
    {
    //delete m_singleton;
    delete m_optionDictionary;
    }

    Preferences* Preferences::m_singleton = NULL;

    Preferences* Preferences::getInstance()
    {
    if(m_singleton == NULL)
    {
    m_singleton = new Preferences;
    }

    return m_singleton;
    }
    bool Preferences::registerValue(QString key,QVariant value, bool overwrite)
    {
    if((overwrite)||(!m_optionDictionary->contains(key)))
    {
    QVariant oldValue;
    if(m_optionDictionary->contains(key))
    {
    oldValue = m_optionDictionary->value(key);
    }
    m_optionDictionary->insert(key,value);
    if(oldValue != value)
    {
    }
    return true;
    }
    else
    return false;
    }
    const QVariant Preferences::value(QString key)
    {
    if(m_optionDictionary->contains(key))
    {
    return m_optionDictionary->value(key);
    }
    else
    {
    QVariant defaultValue;
    return defaultValue;
    }
    }
    /*QVariantMap* Preferences::retourneMap()
    {
    return m_optionDictionary;
    }*/
    bool Preferences::remove(QString key)
    {
    return m_optionDictionary->remove(key);
    }
    bool Preferences::contains(QString key)
    {
    return m_optionDictionary->contains(key);
    }
    bool Preferences::readSettings()
    {
    QString cheminDuFichierIni = value("cheminDuFichierIni",QApplication::applicationDirPath() + "/PsyENCle/Utilisateur/Variables/Variables.ini").toString();
    if (QFile(value("cheminDuFichierIni").toString()).exists())
    {
    QFile file(value("cheminDuFichierIni").toString());
    if (file.open(QIODevice::ReadWrite))
    {
    QByteArray byteArray = file.readAll();
    QDataStream dataStream(&byteArray, QIODevice::ReadOnly);
    dataStream >> (*m_optionDictionary);

    /*qDebug()<<"QFile Settings open";
    QByteArray byteArray = file.readAll();
    QJsonParseError * error = new QJsonParseError();
    QJsonDocument jsonDocument = QJsonDocument::fromJson(byteArray,error);
    if(error != 0)
    qDebug()<<error->errorString();
    if(jsonDocument.isNull())
    qDebug()<<"jsonDocument is NULL";
    QJsonObject jsonObject = jsonDocument.object();
    (*m_optionDictionary) = jsonObject.toVariantMap();*/
    file.close();
    return true;
    }
    else
    {
    qDebug()<<file.errorString();
    return false;
    }
    }
    else
    {
    qDebug()<<"ReadSettings(): Le fichier Ini n'éxiste pas.";
    return false;
    }
    }
    bool Preferences::writeSettings()
    {
    QString cheminDuFichierIni = value("cheminDuFichierIni").toString().remove("/Variables.ini");
    if (QDir(cheminDuFichierIni).exists())
    {
    QFile file(value("cheminDuFichierIni").toString());
    if (file.open(QIODevice::ReadWrite))
    {
    QByteArray byteArray;
    QDataStream dataStream(&byteArray, QIODevice::WriteOnly);
    dataStream << (*m_optionDictionary);
    file.write(byteArray);
    file.close();
    return true;
    }
    else
    {
    qDebug()<<file.errorString();
    return false;
    }
    }
    else
    {
    qDebug()<<"WriteSettings(): Le fichier Ini n'éxiste pas.";
    return false;
    }
    }
    #endif // PREFERENCES_CPP
    Après 3 jours passé dessus, je vois que je suis parfaitement incapable de résoudre ça ...
    Le compilo et le Dégoguer ne servent à rien ... au mieux, j'ai une flèche qui pointe sur ce qui pourrait être une adresse mémoire mais ...

    Je suppose que pour vous c'est évident ?
  • 28 Apr modifié
    peut-être mon problème venait du fait que j'initialisais pas assez tôt cette classe
    Sans objet avec un singleton.
    ou bien que je la détruisais trop tôt :
    Sans objet avec un singleton.
    comme je n'avais toujours pas confiance dans la classe QSettings
    QSettings marche très bien, si ce n'est pas le cas c'est que tu dois faire n'importe quoi ...

    Preferences::Preferences()
    : m_optionDictionary(NULL)
    Ton dico, il est initialisé où ?
    En fait la bonne question est: pourquoi utiliser un pointeur ?

    Tu as regardé l'exemple que j'ai posté plus haut ?
  • Tu as regardé l'exemple que j'ai posté plus haut ?
    Oui, j'ai commencé.
    Mieux c'est programmé et plus j'ai de difficultés à comprendre :D
    Pour l'instant j'en suis à comprendre la doc de QCustomEvent et à lire ce que tu as écris.
    Tu insères des classes dans des QList, tu crée tes propres iterateurs ... c'est chaud pour moi ;)
    QSettings marche très bien, si ce n'est le cas c'est que tu dois faire n'importe quoi ...
    Très certainement ! je n'ai aucune prétention, bien au contraire.
    J'ai reste avec un doute que QSettings soit synchrone mais je n'ai pas fait de test sur le sujet.
    De toute façon, je m'en suis débarrassé.

    Le dictionnaire était bien initialisé.
    J'ai viré beaucoup de lignes pour faciliter la lecture dont celle-ci par mégarde.
    Preferences::Preferences()
    : m_optionDictionary(NULL)
    {
    m_optionDictionary = new QVariantMap;
    }
    Si j'ai bien fait gaffe, le programme plante à la destruction de cette fonction :
    QVariantMap* Preferences::retourneMap()
    {
    return m_optionDictionary;
    }
    Je crois qu'elle devrait être static ? Enfin, je ne sais pas mais y'a un truc ...
    class Preferences
    {
    public:

    static Preferences* getInstance();
    ~Preferences();
    //QVariantMap* retourneMap();
    }
    Je vois des trucs comme ca dans ton programme :
    const QVariant &ValueChangedEvent::value()
    {
    return vValue;
    }
    Je vais essayer.

    Pour "QT_HAS_BUILTIN" redefined, je ne vois pas du tout.
    Le compilo me sort n'importe quoi avec des Warnings sur des classes que je n'utilise pas.
    Le programme compile et fonctionne, mais QtCreator plante lorsque je le ferme.
    Je vais compiler les sources de d'obiwankennedy pour voir ... sur google, je ne trouve que des trucs en chinois, cela ne semble pas commun.
  • 28 Apr modifié
    Oui, c'est ca.

    Pour la premiere erreur je pense qu'il fallait écrire :
    const QVariantMap & retourneMap();
    const QVariantMap &Preferences::retourneMap()
    {
    return (*m_optionDictionary);
    }
    C:\Users\Seven\Desktop\Programmations\build-PsyENCle-QT_Static-Release\release\PsyENCle.exe s'est terminé avec le code 0
    Entre les singletons, les truc const static ou je ne sais quoi ... j'en perds mon latin.

    Je regarde pour la seconde erreur en compilant le code d'obiwankennedy.
  • Si j'ai bien fait gaffe, le programme plante à la destruction de cette fonction :
    QVariantMap* Preferences::retourneMap()
    {
    return m_optionDictionary;
    }
    Je ne comprends ce que tu entends par destruction de fontion.
    Pour l'instant j'en suis à comprendre la doc de QCustomEvent et à lire ce que tu as écris.
    Ca c'est pour les notifs.
    Regarde plutôt la classe AppPrefs. C'est un QSetting avec une gestion des valeurs par défaut.
  • 28 Apr modifié
    Désolé si je n'utilise pas le bon vocabulaire.
    A la cloture du programme, j'avais un message du genre "Le programme s'est terminé subitement".
    Comme le debogueur ne me donnait aucune information, j'ai commencé à virer toutes les classes les unes après les autres. J'ai observé que la classe qui produisait cette erreur était Preferences.
    Puis j'ai procédé par elimination dans cette classe en supprimant les fonctions les unes après les autres.
    Du coup, j'ai trouvé que la simple présence de cette fonction alors que le programme compilait et s'exécutait correctement provoque une erreur "Le programme s'est terminé subitement".

    C'est pour cela que j'ai marqué à la destruction de QVariantMap* retourneMap(); le programme plante.
    J'avais essayé pas mal de combinaisons pour résoudre le problème sans y parvenir. En relisant ton programme, j'ai trouvé un truc qui semble convenir :
    const QVariantMap & retourneMap();
    const QVariantMap &Preferences::retourneMap()
    {
    return (*m_optionDictionary);
    }
    Comme je ne comprends plus rien à rien, je procède par essais erreurs ...

    Je reviens quand j'ai compilé rolisteam pour la seconde erreur "QT_HAS_BUILTIN" redefined.


  • 28 Apr modifié
    Je vois des trucs comme ca dans ton programme :
    const QVariant &ValueChangedEvent::value()
    {
    return vValue;
    }
    Si une méthode retourne un objet, il est mieux de retourner une référence pour éviter la copie.

    Si j'ai une méthode:
    QVariant & value()

    si je fais:
    QVariant v=value() // j'ai une copie
    mais:
    QVariant& v=value() // pas de copie

    Dans ce cas il vaux mieux mettre const, sinon, l'objet original peut être modifié.
    Une référence n'est qu'un pointeur déguisé.
  • Ah, sur ca je suis parfaitement ok.
    Le truc, c'est que le compilo ne me laisse pas écrire ce que je veux (parce que cela ne respecte pas la norme, ok). Il me dit que je suis dans une fonction static blablabla. Je n'avais jamais vu d'opérateur comme ça (* m_optionDictionary) (si c'est un opérateur).
    En fait, ta classe ValueChangedEvent correspond certainement à ce que je dois faire. C'est d'ailleurs pis poil ce que je veux : lorsqu'une valeur change, tu la renvois.
    Je vais étudier tout cela (j'ai trop de choses à apprendre ...).
  • 28 Apr modifié
    A la cloture du programme, j'avais un message du genre "Le programme s'est terminé subitement".
    Comme le debogueur ne me donnait aucune information,
    En mode Debug, tu n'as pas de stack trace dans le volet Debugger quand ça plante ?

    Mais je répète: pourquoi utiliser un pointeur pour m_optionDictionary
  • 28 Apr modifié
    Je me suis encore trompé : L'erreur provenait d'une autre classe.
    Je me suis loupé à cause des includes en cascade.
    Le problème venait soit des defines soit parce que le parent n'était pas signalé.
    Du coup, QtCreator ne plante plus ... Est-ce normal que ce dernier plante simplement parce qu'une classe est mal écrite ?

    ....

    stack trace ... Je cherche ce que c'est.
  • stack trace : la pile des appels : une fenetre magique qui va te rendre de sacres services !!!
  • 28 Apr modifié
    Vous parlez de clang static analyser ?
    Ce dernier m'a sortie un paquet d'erreurs que j'ai toutes corrigées.
    Mais l'erreur initiale est réapparut (Elle n'y est plus qu'a la premiere compilation après avoir nettoyé le projet) :
    C:\Qt\Qt5.7.0\5.7\mingw53_32\include\QtCore\qendian.h:53: avertissement : "QT_HAS_BUILTIN" redefined
    # define QT_HAS_BUILTIN(x) __has_builtin(x)
    ^
    QtCreator plante de-nouveau à la fermeture.
    J'ai viré toutes les classes en faisant des /* */
    Seul main.cpp devrait compiler et ce dernier est quasi vide ...

    Si vous parlez de la fenêtre Stack (depuis le menu Fenêtre// Vues // Stack), celle ci est vide aussi.
    Voici ce que je vois quand je fais planter mon programme :

    Par exemple, si je fais
        double * n;
    while(1) n = new double;
    Je vois des trucs comme ca :
    0x473538                   55                    push   %ebp
    0x473539 <+0x0001> 89 e5 mov %esp,%ebp
    0x47353b <+0x0003> 53 push %ebx
    0x47353c <+0x0004> 83 ec 34 sub $0x34,%esp
    Des fois, il m'indique une ligne particulière, souvent même.
    dans ce cas précis, stack me dit :
    1 ntdll!RtlFormatCurrentUserKeyPath 0x772cb292
    2 ntdll!RtlFormatCurrentUserKeyPath 0x772cb79a
    3 ntdll!RtlFormatCurrentUserKeyPath 0x772cb732
    4 ntdll!RtlFormatCurrentUserKeyPath 0x772cb69d
    5 ntdll!RtlImageNtHeader 0x772c3cce
    6 ntdll!RtlpNtEnumerateSubKey 0x77360d1b
    7 ntdll!RtlUlonglongByteSwap 0x7731ae1e
    8 ?? 0x17900000
    9 ntdll!RtlImageNtHeader 0x772c3cce
    10 msvcrt!malloc 0x75fb9d45
    11 pthread_rel_time_in_ms 0x64b42c85
    12 ?? 0x75a0cead
    13 WinMain *16 qtmain_win.cpp 123 0x473610
    14 main 0x47d1dd
    Effectivement, cela m'aide à trouver le problème (le main) et ca facilite beaucoup la tâche.
    Mais aucune info pour QT_HAS_BUILTIN(x)

    Bon, je crois que j'ai plus qu'a repartir d'un projet vide et ajouter les classes une par une ????

  • 28 Apr modifié
    Cela ressemble à cela :
    https://codereview.qt-project.org/#/c/161929/
    https://testresults.qt.io/coin/integration/qt/qtbase/tasks/1466535612.thrift_bin

    Ils semblent dire que cela vient d'un bug sur MinGW 64, mais moi je suis sous 32

    https://codereview.qt-project.org/#/c/161929/5/src/corelib/global/qendian.h
    Ici, c'est différent.

    Il faut que je modifie QEndian ?
    #ifndef QENDIAN_H
    #define QENDIAN_H

    #include <QtCore/qglobal.h>

    // include stdlib.h and hope that it defines __GLIBC__ for glibc-based systems
    #include <stdlib.h>
    #include <string.h>

    QT_BEGIN_NAMESPACE

    <b><i><u>#ifdef __has_builtin
    # define QT_HAS_BUILTIN(x) __has_builtin(x)
    #else
    # define QT_HAS_BUILTIN(x) 0
    #endif</u></i></b>

    /*
    * ENDIAN FUNCTIONS
    */
    Je ne comprends rien ... Je n'utilise ni #include QtCore/qglobal.h ni #include stdlib.h ni #include string.h et encore moins #include qendian.h ... Il faut que je fasse le tour de tous mes includes pour voir qui utilise qendian ?
    Non, je vais complètement laisser tomber, c'est encore plus simple ;)
    Après tout, ca ne plante plus donc ... autant en terminer. Je crois comprendre que ce n'est pas de faute alors c'est parfait.

    Merci beaucoup pour le temps pris !!!!
  • problème résolu, il fallait que je vire les namespace (si cela s'appelle comme cela).

    @mpergand

    J'ai fait un petit test ce matin.
    Tu trouveras certainement des fautes qui expliquent tout.
    Cependant, si je fais ceci :
    void testQSettings::writeSettings()
    {
    QSettings settings01("qsettingsTest", "qsettingsTest");
    QSettings settings02("AfondDeProcesseur.ini",QSettings::IniFormat);
    QSettings settings03("HKEY_CURRENT_USER\\AfondDeProcesseur\\AfondDeProcesseur\\AfondDeProcesseur",QSettings::NativeFormat);
    QSettings settings04(QSettings::IniFormat, QSettings::UserScope,"AfondDeProcesseur", "AfondDeProcesseur");
    settings01.beginGroup("AfondDeProcesseur");
    settings02.beginGroup("AfondDeProcesseur");
    settings03.beginGroup("AfondDeProcesseur");
    settings04.beginGroup("AfondDeProcesseur");
    settings01.clear();
    settings02.clear();
    settings03.clear();
    settings04.clear();
    for(int i = 0 ; i < 100000 ; i ++)
    {
    settings01.setValue(QString::number(i),QString::number(i));
    settings02.setValue(QString::number(i),QString::number(i));
    settings03.setValue(QString::number(i),QString::number(i));
    settings04.setValue(QString::number(i),QString::number(i));
    }
    settings01.endGroup();
    settings02.endGroup();
    settings03.endGroup();
    settings04.endGroup();
    }
    void testQSettings::readSettings()
    {
    QSettings settings01("qsettingsTest", "qsettingsTest");
    QSettings settings02("AfondDeProcesseur.ini",QSettings::IniFormat);
    QSettings settings03("HKEY_CURRENT_USER\\AfondDeProcesseur\\AfondDeProcesseur\\AfondDeProcesseur",QSettings::NativeFormat);
    QSettings settings04(QSettings::IniFormat, QSettings::UserScope,"AfondDeProcesseur", "AfondDeProcesseur");
    int cible = 0;
    foreach(const QString& key,settings01.allKeys())
    {
    //valeurDontLInscriptionEstEffective.append("Valeur cible : " + QString::number(cible) + " " + key + " " + settings01.value(key).toString());
    //cible++;
    qDebug()<<settings01.value(key).toString() << "//" << settings02.value(key).toString() << "//" << settings03.value(key).toString() << "//" << settings04.value(key).toString();
    //QApplication::processEvents();
    }
    settings01.clear();
    settings02.clear();
    settings03.clear();
    settings04.clear();
    qDebug()<<"Fin du traitement";
    }
    QSettings 2 et 4 restent désespérément vide.
    Les données ne sont jamais écrite dans les fichiers textes :

    "0" // "" // "0" // ""
    "1" // "" // "1" // ""
    "10" // "" // "10" // ""
    "100" // "" // "100" // ""
    "1000" // "" // "1000" // ""
    "10000" // "" // "10000" // ""
    "10001" // "" // "10001" // ""
    "10002" // "" // "10002" // ""
    "10003" // "" // "10003" // ""
    ....
    En quoi je me trompe ?
  • 29 Apr modifié
    Windows n'ecrit que quand il le temps de faire, ok.
    Le processus est bloquant : je me trompe.
    Par contre l'application peut planter au moment ou Windows écrit dans le fichier.
    Dans ce cas, les données ne sont pas écrite.
    Fin de tous les mystères. Aucun problème avec Qt.
Connectez-vous ou Inscrivez-vous pour répondre.