Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre à l'essai
    Génération de classe modèle-vue avec les services autogénérés
    Bonjour,

    Je reviens une nouvelle fois ici pour découvrir un nouvel aspect de QxOrm et QxEntityEditor : les model/view. La petite particularité ici est que je désirerai les faire fonctionner avec les services. Y a t il une option cachée quelque part dans QxEntityEditor pour utiliser non pas la base de données directement, mais les services ?

    Pourrais tu m'indiquer un début de piste pour réaliser cela ?

  2. #2
    Expert confirmé


    Le module QxModelView attaque directement la base de données avec la classe qx::QxModel<T>.

    Il est tout à fait possible de créer une nouvelle classe héritant de qx::QxModel<T>, et qui utilise les services au lieu d'attaquer directement la base de données.
    Je vois bien une classe avec 2 paramètres template :
    - T : la classe persistante enregistrée dans le contexte QxOrm ;
    - S : la classe de services associée à la classe persistante.

    Dans cette nouvelle classe, on va surcharger toutes les méthodes préfixeés par qx (qui attaquent par défaut la BDD), pour leur dire d'utiliser les services.
    Si on nomme cette nouvelle classe ModelService, ça pourrait donner quelque chose comme ça :

    ModelService.h :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    template <class T, class S>
    class ModelService : public qx::QxModel<T>
    {
     
    public:
     
       ModelService(QObject * parent = 0) : qx::QxModel<T>(parent) { ; }
       ModelService(qx::IxModel * other, QObject * parent) : qx::QxModel<T>(other, parent) { ; }
       virtual ~ModelService() { ; }
     
       virtual QSqlError qxFetchAll(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL)
       {
          Q_UNUSED(pDatabase);
          clear();
     
          typedef typename qx::QxModel<T>::type_collection type_collection_tmp;
          boost::shared_ptr<type_collection_tmp> tmp;
          tmp.reset(new type_collection_tmp());
     
          S services;
          m_lastError = services.fetchAll(tmp, m_lstColumns, relation);
     
          beginInsertRows(QModelIndex(), 0, (tmp->count() - 1));
          m_model = (* tmp);
          endInsertRows();
          return m_lastError;
       }
     
       /*
          Faire la même chose pour les méthodes suivantes (il faut regarder l'implémentation dans qx::QxModel<T>, et modifier cette implémentation pour utiliser les services) :
          ---------------------------------------------------------------------------------------------------------------------------------------------------------------
          virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxCount(long & lCount, const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxFetchById(const QVariant & id, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxSaveRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxDeleteById(const QVariant & id, QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxDestroyById(const QVariant & id, QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
          virtual QSqlError qxExecuteQuery(qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL);
          virtual qx_bool qxExist(const QVariant & id, QSqlDatabase * pDatabase = NULL);
          virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList());
       */
     
    };


    Voilà, à présent il faut utiliser cette nouvelle classe ModelService<T, S> au lieu de qx::QxModel<T>, comme ceci par exemple (voir la FAQ pour plus de détails) :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // Create a model and fetch all data from database
    qx::IxModel * pModel = new ModelService<author, services::author_services>();
    pModel->qxFetchAll();
     
    // Associate the model to a QTableView and display it
    QTableView tableView;
    tableView.setModel(pModel);
    tableView.show();


    Remarque : toutes les méthodes que tu as surchargé sont accessibles en QML (avec le suffixe "_" sur chaque méthode).

    Autre remarque : Si tu veux aller plus loin et utiliser QxEntityEditor (pour gérer la notion de relations 1-n, n-1, n-n, 1-1), on peut automatiser l'utilisation de cette nouvelle classe, un peu comme on a fait dans le sujet sur les services : http://www.developpez.net/forums/d14...s-autogeneres/

    Aller dans le menu "Tools >> Export to C++ model/view project (settings)".
    Dans la section "C++ model/view template files", sélectionner l'option "Custom" (au lieu de "Default").
    On va juste copier-coller ce qu'il y a dans "Default", en remplaçant qx::QxModel<T> par ModelService<T, S>.
    Ce qui donne ceci :

    Header (.h) :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    @@COMMENT_FILE_CREATED_BY@@
     
    #ifndef @@INCLUDE_MACRO_GUARD@@
    #define @@INCLUDE_MACRO_GUARD@@
     
    #ifdef _QX_NO_PRECOMPILED_HEADER
    #ifndef Q_MOC_RUN
    #include "../include/@@PROJECT_NAME@@_precompiled_header.model_view.gen.h" // Need to include precompiled header for the generated moc file
    #endif // Q_MOC_RUN
    #endif // _QX_NO_PRECOMPILED_HEADER
     
    @@LIST_INCLUDE_DEPENDENCIES@@
     
    @@NAMESPACE_BEGIN@@
     
    typedef ModelService<@@FULL_CLASS_NAME@@, services::@@FULL_CLASS_NAME@@_services> @@CLASS_NAME@@_model_base_class;
    class @@CLASS_MACRO_EXPORT@@ @@CLASS_NAME@@_model : public @@CLASS_NAME@@_model_base_class
    {
     
       Q_OBJECT
     
    public:
     
       @@CLASS_NAME@@_model(QObject * parent = 0);
       @@CLASS_NAME@@_model(qx::IxModel * other, QObject * parent);
       virtual ~@@CLASS_NAME@@_model();
     
    @@LIST_RELATIONS_Q_INVOKABLE@@
    @@COMMENT_LIST_PROPERTIES_EXPOSED_BY_MODEL@@
     
    };
     
    @@NAMESPACE_END@@
     
    #endif // @@INCLUDE_MACRO_GUARD@@


    Source (.cpp) :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @@COMMENT_FILE_CREATED_BY@@
     
    #include "../include/@@PROJECT_NAME@@_precompiled_header.model_view.gen.h"
     
    @@LIST_INCLUDE_DEPENDENCIES_CPP@@
     
    #include <QxMemLeak.h>
     
    @@NAMESPACE_BEGIN@@
     
    @@CLASS_NAME@@_model::@@CLASS_NAME@@_model(QObject * parent /* = 0 */) : @@CLASS_NAME@@_model_base_class(parent) { ; }
     
    @@CLASS_NAME@@_model::@@CLASS_NAME@@_model(qx::IxModel * other, QObject * parent) : @@CLASS_NAME@@_model_base_class(other, parent) { ; }
     
    @@CLASS_NAME@@_model::~@@CLASS_NAME@@_model() { ; }
     
    @@IMPLEMENT_LIST_RELATIONS_Q_INVOKABLE@@
     
    @@NAMESPACE_END@@
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  3. #3
    Membre à l'essai
    Bonjour,

    Merci beaucoup pour votre réponse, cela m'a beaucoup aidé dans l'avancement de notre projet avec tortuga. Mise a part quelques includes qui manqué dans les fichiers générés, cette solution répond en tout point à notre problématique. Concernant les autres fonctions comme update, je reviendrai vers vous.

  4. #4
    Expert confirmé
    cette solution répond en tout point à notre problématique
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  5. #5
    Membre à l'essai
    Bonsoir,

    hier soir, j'avais un problème compilation au niveau de la variable tmp dans la fonction ModelClass::qxFetchAll. Pour corriger cette erreur, j'ai changé le type de la variable en boost::shared_ptr<qx::QxModel<T>::type_collection>. Est ce correct ? si oui, comment je fais pour faire la fonction ModelClass::qxInsert ? si non, pouvais m'aider a y voir plus clair ?

    merci encore.

  6. #6
    Expert confirmé
    Est ce correct ?
    Oui tu as raison, il y avait un problème de type (je n'avais pas essayé de compiler ).
    Pour respecter la norme C++ à 100% (et avoir du code compilable avec GCC par exemple), il faut même ajouter le mot-clé typename.
    Au final, la méthode qxFetchAll doit s'écrire comme ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    virtual QSqlError qxFetchAll(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL)
    {
       Q_UNUSED(pDatabase);
       clear();
     
       typedef typename qx::QxModel<T>::type_collection type_collection_tmp;
       boost::shared_ptr<type_collection_tmp> tmp;
       tmp.reset(new type_collection_tmp());
     
       S services;
       m_lastError = services.fetchAll(tmp, m_lstColumns, relation);
     
       beginInsertRows(QModelIndex(), 0, (tmp->count() - 1));
       m_model = (* tmp);
       endInsertRows();
       return m_lastError;
    }


    comment je fais pour faire la fonction ModelClass::qxInsert ?
    C'est exactement le même principe que pour qxFetchAll : regarde l'implémentation de la méthode de la classe de base dans le fichier QxModel.h du package QxOrm (qui attaque directement la BDD), puis modifie cette implémentation dans ta classe dérivée pour utiliser les services.

    Voici ce qu'on a dans le fichier QxModel.h (package QxOrm) :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL)
    {
       if (relation.count() == 0) { m_lastError = qx::dao::insert(m_model, database(pDatabase)); }
       else { m_lastError = qx::dao::insert_with_relation(relation, m_model, database(pDatabase)); }
       return m_lastError;
    }


    Dans ta classe dérivée, tu peux remplacer/surcharger par ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL)
    {
       Q_UNUSED(pDatabase);
       typedef typename qx::QxModel<T>::type_collection type_collection_tmp;
       boost::shared_ptr<type_collection_tmp> tmp;
       tmp.reset(new type_collection_tmp());
       (* tmp) = m_model;
     
       S services;
       m_lastError = services.insert(tmp, relation);
       return m_lastError;
    }
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  7. #7
    Membre à l'essai
    Bonsoir,

    votre solution marche nickel, mais le seul problème que je ne comprend pas comment utiliser la fonction pour ajouter un élément dans la base via le model/service.

    A l'heure actuelle , je fais ça au niveau de la gui :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
     _userModel->setModelValue(2 ,"login", _ui->lineEditLoginUser->text()); // souhaite créer un nouvel user avec comme login "_ui->lineEditLoginUser->text()"
     _userModel->qxInsert();


    est ce correct ?

    Pour la visualisation des données, je fais ça et cela marche bien :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    _ui->lineEditIdUser->setText(model->getModelValue(index.row(), "Uuid").toString());
        _ui->lineEditLoginUser->setText(model->getModelValue(index.row(), "login").toString());


    y t'il un autre moyen ?

  8. #8
    Expert confirmé
    votre solution marche nickel


    La prochaine fois, s'il-te-plait, créé un nouveau sujet sur le forum, car pour moi ta problématique de ce sujet est à présent résolu, à savoir :
    - tu disposes d'un modèle générique capable d'encapsuler toutes tes classes enregistrées dans le contexte QxOrm ;
    - ce modèle expose automatiquement toutes les propriétés définies dans la fonction qx::register_class<T> ;
    - ce modèle utilise la couche services (au lieu d'attaquer directement la BDD comme le propose par défaut qx::QxModel<T>).

    Maintenant, tu cherches à utiliser ce modèle dans des vues, c'est un autre sujet.

    est ce correct ? ... y t'il un autre moyen ?
    Oui c'est correct, mais tu perds un peu l'intérêt du concept model/view (notamment la synchronisation auto du modèle et de la vue).
    Concernant les vues, Qt propose ces classes :
    - QListView
    - QTreeView
    - QTableView
    - QDataWidgetMapper
    - Et bien évidemment QML !

    Pour ce que tu cherches à faire, QML semble plus naturel, l'exemple de la FAQ se rapproche : http://www.qxorm.com/qxorm_fr/faq.html#faq_300
    Sinon, avec les QWidgets, utilise la classe : QDataWidgetMapper.
    Il y a un très bon tutoriel ici : http://qt-quarterly.developpez.com/q...dance-donnees/
    Je te conseille de lire des docs et tutoriels sur le net, car maintenant c'est plus une problématique Qt que QxOrm

    Par exemple, avec 2 champs txtLogin et txtPassword, l'utilisation de la classe QDataWidgetMapper pourrait donner quelque chose comme ça :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    qx::IxModel * pModel = new ModelService<user, services::user_services>();
    pModel->qxFetchById(...);
     
    QHash<int, QByteArray> lstRoleNames = pModel->roleNames();
     
    QDataWidgetMapper * mapper = new QDataWidgetMapper(this);
    mapper->setModel(pModel);
    mapper->addMapping(txtLogin, lstRoleNames.key("login"));
    mapper->addMapping(txtPassword, lstRoleNames.key("password"));


    Remarque : j'ajouterai un accesseur dans la classe qx::IxModel pour récupérer le n° de colonne en fonction du nom de la propriété pour éviter de passer par la méthode roleNames() dans la prochaine version de QxOrm...
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  9. #9
    Expert confirmé
    Remarque : j'ajouterai un accesseur dans la classe qx::IxModel pour récupérer le n° de colonne en fonction du nom de la propriété pour éviter de passer par la méthode roleNames() dans la prochaine version de QxOrm...
    C'est fait dans cette BETA version : http://www.qxorm.com/version/QxOrm_1....1_BETA_15.zip
    Ce n'est plus nécessaire de passer par la méthode roleNames(), tu peux écrire à présent :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    qx::IxModel * pModel = new ModelService<user, services::user_services>();
    pModel->qxFetchById(...);
     
    QDataWidgetMapper * mapper = new QDataWidgetMapper(this);
    mapper->setModel(pModel);
    mapper->addMapping(txtLogin, pModel->getColumnIndex("login"));
    mapper->addMapping(txtPassword, pModel->getColumnIndex("password"));
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  10. #10
    Membre à l'essai
    Bonjour,

    pour les fichiers générés par les model_view, les relations sont plus bonne si je veux utiliser les services ?

    quelle solution s'offre à moi ?

  11. #11
    Expert confirmé
    pour les fichiers générés par les model_view, les relations sont plus bonne si je veux utiliser les services ?
    quelle solution s'offre à moi ?
    Le plugin de QxEntityEditor qui génère le code pour gérer la partie model/view n'est pas fait par défaut pour utiliser les services.
    Par contre, en utilisant les fonctionnalités de customisation de QxEntityEditor (soit par le moteur javascript, soit par le mécanisme de template), ça me semble possible de faire appel aux services pour valoriser les relations.

    Si tu regardes l'implémentation d'une méthode générée qui récupère une relation, tu verras une ligne de ce type :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);

    Il suffit de remplacer cette ligne par un appel à un service.
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  12. #12
    Expert confirmé
    Pour info, à partir de la version QxOrm 1.3.1 BETA 28 (http://www.qxorm.com/version/QxOrm_1....1_BETA_28.zip), une nouvelle classe est disponible : qx::QxModelService<T, S>.
    Tu peux donc à présent utiliser cette classe, au lieu de la classe ModelService que je t'ai fait créer dans mes 1ère réponses à ce topic.
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.

  13. #13
    Membre à l'essai
    Cette classe ne gère pas les relations généré par QxEntityEditor pour utiliser le service?

  14. #14
    Membre à l'essai
    Concernant l’implémentation de cette classe, j'ai un assert récurrent de la classe QAbstractItemModel.

    Lors que l'on fait une requête, par exemple qxFetchAll ou bien, qxFetchById, cette ligne remonte l'assert.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    this->beginInsertRows(QModelIndex(), 0, (tmp->count() - 1));


    Cette assert est tout a fait normal, car si il y a aucun résultat remonté, tmp->count() est égual a 0, donc tmp->count() - 1 est égal à -1,
    du coup le 2éme paramètre de beginInsertRows est plus grand que le 3éme, d'où cet assert.

    ---------------------------
    Microsoft Visual C++ Runtime Library
    ---------------------------
    Debug Error!

    Program: C:\Qt\Qt5.3.1\5.3\msvc2013\bin\Qt5Cored.dll
    Module: 5.3.1
    File: global\qglobal.cpp
    Line: 2127

    ASSERT: "last >= first" in file itemmodels\qabstractitemmodel.cpp, line 2602

    (Press Retry to debug the application)

    ---------------------------
    Abandonner Recommencer Ignorer
    ---------------------------
    Pouvez-vous corriger cette erreur s'il vous plait?

  15. #15
    Expert confirmé
    Cette assert est tout a fait normal, car si il y a aucun résultat remonté, tmp->count() est égual a 0, donc tmp->count() - 1 est égal à -1,
    du coup le 2éme paramètre de beginInsertRows est plus grand que le 3éme, d'où cet assert.
    C'est corrigé à partir de cette version :
    http://www.qxorm.com/version/QxOrm_1....1_BETA_29.zip

    EDIT : maintenant que QxOrm 1.3.1 & QxEntityEditor 1.1.7 sont sortis en version finale, tu peux utiliser la nouvelle option de QxEntityEditor 1.1.7 qui permet de générer des modèles basés sur la nouvelle classe qx::QxModelService<T, S> (menu Tools >> Export to C++ model/view project (settings)) :
    Le site de la bibliothèque QxOrm : bibliothèque C++ de gestion de données (Mapping Objet Relationnel ou ORM) basée sur les frameworks Qt et boost.
    QxEntityEditor : éditeur graphique pour la bibliothèque QxOrm (application multi-plateforme pour gérer graphiquement le modèle d'entités).

    Tutoriel : installer un environnement de développement avec QxOrm sous Windows.
    Tutoriel qxBlog : gestion de blogs en C++/Qt.
    Tutoriel qxClientServer : création d'un serveur d'applications en C++/Qt.