Tag Archives: Qt5

Créer un WebService en C++ avec Qt

Un petit article pour évoquer la possibilité de faire un webservice, en C++/Qt.

Mon besoin est simple, je veux fournir une interface web pour réaliser des lancer de dés avec le système de lancement de dés DiceParser, issu de Rolisteam.

DiceParser est écrit en C++ avec Qt5. A partir de ce constat, je pouvais soit faire un serveur web en C++, soit faire un site web en python/php/whatever pour lancer une commande système et utiliser dice, le client en ligne de commande de DiceParser.

La deuxième solution est  la plus facile mais clairement un peu «sale» et niveau sécurité ce n’est pas idéal. Le «challenge» se trouve dans la première méthode.
Je n’avais pas envie de réinventer la roue donc j’ai cherché une solution technique pour satisfaire mon besoin. Un composant C++/Qt qui permet de créer un serveur http avec possibilité d’être notifier à chaque requête. J’ai trouvé un composant qui réalise cela.

Le dépôt git du composant : https://github.com/azadkuh/qhttp.git

Implémentation du serveur

  m_server = new qhttp::server::QHttpServer(this);
    m_server->listen( // listening on 0.0.0.0:8080
            QHostAddress::Any, port,
            [=](qhttp::server::QHttpRequest* req, qhttp::server::QHttpResponse* res)
            {
                req->collectData(1024);

		// Ici mettre le code d'analyse de la requête

        });
    if ( !m_server->isListening() ) {
            qDebug() << "failed to listen";

        }
    else
    {
        qDebug()<< "Server is On!!";
    }
Instancier le serveur web

Nous créons une instance du serveur, nous démarrons l’écoute sur le port (80 par défaut pour le protocole http).
L’écoute est réalisé par une fonction lambda, cela n’a rien d’obligatoire, mais c’est plus minimaliste.

Notre fonction est notifiée à la réception d’une requête http.

Récupérer les informations d’une requête

// Récupère les données
req->collectData(1024);

// Récupère l'url (les données du GET)
QString getArg = req->url().toString();

// Découpage des arguments
getArg=getArg.replace("/?","");
QStringList args = getArg.split('&');
QHash<QString,QString> m_hashArgs;
for( auto argument : args)
{
    QStringList keyValue = argument.split('=');
    if(keyValue.size()==2)
    {
        m_hashArgs.insert(keyValue[0],keyValue[1]);
    }
}

// recherche dans la table des arguments la présence de fonction.
if(m_hashArgs.contains("cmd"))
{
	// répondre à la requête reconnue.
}
Découpage

Ici, nous analysons la requête pour identifier les actions à réaliser. Nous cherchons le paramètre cmd, pour trouver la commander à identifier. Le travail préalable est des créer une tableau associatif (dans une QHash) pour conserver les paramètres et leur valeur.

Une fois la commande de dés identifiés, il faut l’exécuter.

Exécuter la commande et répondre


                    QString result = startDiceParsing(QUrl::fromPercentEncoding(m_hashArgs["cmd"].toLocal8Bit()));
                    

                    res->setStatusCode(qhttp::ESTATUS_OK);
                    res->addHeader("Access-Control-Allow-Origin", "*");
                    res->addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
                    res->addHeader("Access-Control-Allow-Headers", "x-requested-with");


                    QString html("<!doctype html>\n"
                             "<html>\n"
                             "<head>\n"
                             "  <meta charset=\"utf-8\">\n"
                             "  <title>Rolisteam Dice System Webservice</title>\n"
                             "  <style>.dice {color:#FF0000;font-weight: bold;}</style>"
                             "</head>\n"
                             "<body>\n"
                             "%1\n"
                             "</body>\n"
                             "</html>\n");

res->end(html.arg(result).toLocal8Bit());

La première action est de convertir la commande pour la transformer en données lisibles. Elle est actuellement encodé en mode pourcent. Qt fournit une méthode statique pour faire la conversion. Le resultat de cette conversion est ensuite envoyé à une fonction qui exécute la commande et donne le résultat.

Il faut ensuite générer la réponse. Le premier truc à définir est le code réponse du protocole http. Le module qhttp propose des raccourcis pour cela.

Ensuite, la réponse doit contenir 3 paramètres dans les headers afin d’être accessible par des frameworks Ajax/javascript/Web 2.0 (vous ressentez mon dédain envers ces techno ?).
Après la définition des headers, il faut créer le code html (ou autre) de la réponse.

Créer une page web cliente du service

<!DOCTYPE html>                                                                                               
<html>      
<head>                       
<meta charset="utf-8"> 
<meta name="Author" content="Renaud GUEZENNEC"/>                                                            
  <title>Rolisteam Dice System Webservice</title>  
  <style>.dice {color:#FF0000;font-weight: bold;}</style>                                                     
  <script type="text/javascript" src="js/jquery-3.1.1.min.js" ></script>            
  <script type="text/javascript">  
    function displayResult() {                                                                                                         
      var request = $.ajax({   
        method: "get",    
        url: "http://127.0.0.1:8085/",  
        dataType: "html",  
        async: false,
        data: {cmd: $('#cmd').val()} 
      });                
      request.done(function(data){  
        var str ="<p>" 
        str = str.concat(data,"</p>");                                                                        
        $('.diceresult').prepend(str);    
      }); 
    }   
  </script>  
</head>       
<body>    
<form method="POST" action=""/> 
<input id="cmd" name="cmd"/>  
<input id="roll" name="roll" type="button" onclick="javascript:displayResult(); return 0;" value="Roll"/>
</form>                
<a href="index.html">Clear</a> 
<div class="diceresult">
</div>      
</body> 
</html>
Page cliente

Le travail ici est assez simple. Il faut créer un formulaire pour permettre à l’utilisateur de saisir la commande de dés. J’ai utilisé JQuery comme framework javascript pour récupérer la commande et envoyer la requête de mon webservice. Quand la requête s’est bien passée, le résultat s’affiche dans la page sans recharger la page.

Le lien vers le code source: https://github.com/Rolisteam/DiceParser/tree/master/webserver
Le webservice fait partie de projet DiceParser: https://github.com/Rolisteam/DiceParser

Distribuer une application Qt

Bonjour,

Si vous suivez ce blog, vous savez que je suis développeur C++/Qt et que je contribue beaucoup à Rolisteam (www.rolisteam.org). C’est un logiciel libre pour faire du jeu de rôle. Il vise un public assez large. Il est donc multi-plateforme. Il fonctionne sous GNU/Linux, windows et Mac Os. Je suis un linuxien convaincu mais je développe sur les autres systèmes pour Rolisteam.

L’aspect développement n’est pas vraiment un problème, grâce à Qt. Cela marche grosso modo bien. La chose devient plus périlleuse quand il s’agit de distribuer le logiciel.

Dans mon activité professionnelle, j’ai constaté,  qu’il est assez rare qu’un développeur connaisse les solutions pour distribuer facilement un logiciel. Je vais essayer ici d’expliquer comment faire. Du moins, comment je fais dans rolisteam et si vous avez d’autres astuces. N’hésitez pas à en faire part dans les commentaires.

Prédicat de base:

Vous avez un logiciel écrit en C++/Qt, il est géré par un fichier .pro. Certains systèmes de build propose des systèmes identiques, voir plus puissant mais plus complexe (e.g: cmake). Je me contente d’évoquer les outils Qt.

Pour GNU/Linux :

Pour ma part, j’ai opté pour une distribution source et la création de package Ubuntu. Grâce à Launchpad, il est possible de créer un dépôt pour son projet et de soumettre des versions. Il faut l’admettre, c’est très pratique. Il existe probablement des services pour faire cela pour d’autres distributions.

Des contributeurs ont créé des package pour d’autres distributions (fedora…). Pour arch-linux, j’ai crée un script de compilation (à tester).

L’ensemble des outils utilisées ne sont pas des outils Qt. Cela n’est donc pas le propos de cet article.  Je me suis mis de côté la tâche de jeter un coup d’œil à appimage. Cela pourrait être une solution intéressante.

Pour Windows:

Trouver tous les éléments

Une application a des dépendances. Sous windows, il est courant d’installer l’exécutable et ses dépendances dans un même dossier. Il est parfois difficile d’identifier les dépendances. Rassurez-vous un outil magique a été créée. Il s’agit de “windeployqt.exe“. C’est un outil distribué avec Qt. Son but est de copier toutes les dépendances Qt d’un binaire dans un dossier.

windeployqt  /chemin/vers/mon/executable.exe
Commande Windeployqt

Cette commande va lire le binaire et va copier l’ensemble des dépendances identifiés à côté du binaire.

Par soucis de clarté, je préfère utiliser un répertoire propre et distinct du répertoire de compilation.

windeployqt --dir /chemin/vers/un/dossier/propre /chemin/vers/mon/application.exe
Dossier séparé

L’ajout du l’argument –dir permet de définir ce «dossier de destination».

Une autre difficulté entre en jeu quand votre application utilise du QML. En effet, il convient de rajouter un argument à la commande afin de permettre à l’outil d’aller lire votre code QML pour trouver ses dépendances.

windeployqt --qmldir /chemin/vers/le/dossier/des/qml --dir /dossier/propre application.exe
Distributer du QML

Et après ?

Une fois cette étape terminée, vous aurez dans votre dossier de destination l’ensemble des dépendances Qt (et QML) de votre projets.

Je vous mets un exemple pour un logiciel utilisant Qt5 et un peu de QML le tout fut compilé avec Visual Studio.

├── accessible/
├── audio/
├── bearer/
├── designer/
├── Enginio.dll
├── iconengines/
├── icudt52.dll
├── icuin52.dll
├── icuuc52.dll
├── imageformats
├── mediaservice
├── msvcp120.dll
├── msvcr120.dll
├── platforms/
│ └── qwindows.dll
├── playlistformats/
├── position/
├── printsupport/
├── qml1tooling/
├── qmltooling/
├── Qt5Core.dll
├── Qt5Declarative.dll
├── Qt5Gui.dll
├── Qt5Network.dll
├── Qt5OpenGL.dll
├── Qt5Qml.dll
├── Qt5Quick.dll
├── Qt5WebKit.dll
├── Qt5Widgets.dll
├── Qt5Xml.dll
├── Qt5XmlPatterns.dll
├── sensorgestures/
├── sensors/
├── sqldrivers/
├── unins000.dat
└── unins000.exe

Si votre projet utilise d’autres bibliothèques (zlib …), il convient de rajouter les .dll correspondantes dans le dossier. La dernière étape est d’inclure le .exe de votre application.
En double-clickant sur le .exe, il devrait se lancer. Si ce n’est pas le cas, une dll est probablement manquante.
Cette procédure est à faire après un changement de version de Qt. Il faut vous assurer que les dll embarquées soient de la bonne version.

A ce stade, il ne reste qu’à mettre en place l’outil de publication de votre application.

 

Publication

Pour cette étape, il y a principalement deux méthodes:

La méthode de l’archive Zip, il suffit d’archiver le dossier de destination dans une archive .zip. Ce fichier peut être extrait sur n’importe quel windows, et cela fonctionnera. Il faudra créer à la main les raccourcis et il n’y pas d’autre moyen pour dés-installer le logiciel que de supprimer le dossier extrait à la main.

L’autre méthode est d’utiliser un outil qui va créer un installeur. Les plus connus sont Inno setup et NSIS.
J’utilise pour ma part: InstallForge (http://installforge.net), il est graphique et simple.
Qu’importe l’outil choisi, il faudra lui dire d’installer tous les fichiers de votre dossier de destination (en respectant la hiérarchie des fichiers) chez vos utilisateurs. Ces outils permettent de créer automatiquement des scripts de désinstallation, ils créent également les raccourcis sur le bureau et la barre de démarrage. Ils permettent aussi d’afficher du contenu à l’utilisateur (licence, logo du logiciel) et cela lui permet de prendre des décisions, au cours de l’installation (si besoin).

Pour finir, après cette étape, vous aurez un installeur soit en mode .zip, soit un .exe. Il ne reste plus qu’à mettre cet installeur accessible à vos utilisateurs et c’est bon.

Pensez à le tester quand même avant de faire des annonces publiques. On sait jamais !

 

Pour Mac OS:

Pour MacOs, il existe un outil similaire qui s’appelle: macqtdeploy.
Il utilise les mêmes paramètres que la version windows, la seule différence réside dans l’utilisation d’un .app (en lieu et place du .exe).

macdeploy application.app -qmldir=/User/dossier/vers/code/qml 
sur MacOs X

Bien sûr, cela peut être intégrer dans QtCreator comme étape de déployement.
Sur MacOs, il est possible de demander à Qt de compiler une application dans un bundle (c’est d’ailleurs la solution par défaut). Un bundle est un dossier se terminant par «.app». Dans l’environnement graphique de MacOs, un double click lance le logiciel qu’il contient. Il est également possible, d’explorer le contenu du bundle en ligne de commande (cela se comporte comme un dossier) ou par un click droit: «afficher le contenu du packet» en graphique.

Dans tous les cas, l’utilisation de la commande va venir placer l’ensemble des dépendances Qt dans le bundle. Pour être précis, les bibliothèques sont placées dans le dossier Contents/Frameworks du bundle. Les modules QML sont dans Resources/qml.

A l’instar de windows, il faut mettre également d’éventuelles autres bibliothèques dans le bundle. C’est une étape un peu technique. Pour rolisteam, j’utilise zlib, voilà le programme bash qui me permet de faire l’ajout et de bien définir les chemins.

#!/bin/sh

############################################
# Rolisteam 
# Script to deploy libz into rolisteam.
#
###########################################

echo 'Display dependancies'
otool -L rolisteam.app/Contents/MacOS/rolisteam



echo 'Create directory'
mkdir rolisteam.app/Contents/Frameworks/libz.framework/

echo 'Copy lib binary'
cp /usr/lib/libz.1.dylib rolisteam.app/Contents/Frameworks/libz.framework/

echo 'Rewrite path to the lib'
install_name_tool -change /usr/lib/libz.1.dylib @executable_path/../Frameworks/libz.framework/libz.1.dylib rolisteam.app/Contents/MacOS/rolisteam
Ajouter une dépendances à un bundle

En gros, il y a la copie du fichier en lui même puis, la modification de l’exécutable pour lui dire d’aller chercher zlib dans le bundle et non sur mon poste. Cette étape est très importante car sinon, votre bundle ne sera pas portable sur une autre machine.

Et après ?

Le fichier .app complet suffit pour être installé sur une autre machine. Cependant, il est de coutume de distribuer un logiciel par un dmg. Cela permet de le compresser pour faciliter sa distribution et surtout MacOs permet de rendre graphique l’installation par un dmg.

Pour réaliser cela, j’utilise un petit utilitaire: node-appdmg. Il suffit d’un petit fichier de configuration en json pour définir les paramètres.

{
  "title": "Rolisteam  - Mac Os X",
  "icon": "rolisteam.icns",
  "background": "1500-rolisteam.png",
  "icon-size": 80,
  "contents": [
    { "x": 448, "y": 344, "type": "link", "path": "/Applications" },
    { "x": 192, "y": 344, "type": "file", "path": "rolisteam.app" }
  ]
}
Configuration node-appdmg pour rolisteam

Vous lancez l’utilitaire avec le fichier de configuration et le nom de votre dmg pour obtenir votre dmg tout beau, tout propre.

node-appdmg dmg.json monapplication.dmg

Il ne reste qu’à distribuer votre dmg.

Conclusion

Nous venons de voir les solutions et les outils mis en place pour rolisteam.
Si vous avez des questions sur les outils,  ou si vous voulez en proposer d’autres, les commentaires sont là pour ça.

L5R présentation

Retours d’expérience d’une présentation QML

Pour préparer ma conférence à Pas Sage En Seine [FR], j’ai décidé de faire une présentation QML.
Cela offre un bien meilleur contrôle, bien plus de possibilité graphique (animations, intégration de code, 3D…) et de même de l’interactivité.
Bien sûr, une présentation QML demande plus de temps à faire. Le but de l’article est ici d’expliquer les astuces que j’ai mises en place. Espérant gagner du temps la prochaine fois.

Le contenu de la présentation QML:

Comme vous pouvez le voir, la présentation est une application C++ qui affiche du QML. Cette méthode permet de faciliter le contrôle de la partie QML. Je m’en sers pour enregistrer des captures d’écran de la présentation afin d’en faire un PDF. C’est le meilleur pour distribuer la présentation ou dans le cas ou pour éviter l’effet démo.
Tout en haut, il y a les classes C++, les fichiers de management du projet et le fichier main.qml. Dans le répertoire «pages», vous y trouverez les différentes slides de la présentation. Dans rsrc, il y a l’ensemble des images nécessaire à la présentation.

├── cpphighlighter.cpp
├── cpphighlighter.h 
├── deployment.pri 
├── LICENSE 
├── main.cpp 
├── main.qml 
├── pages 
│ ├── 01_intro.qml 
│ ├── 02_presentation.qml 
│ ├── 03_jdr_et_rolisteam.qml 
│ ├── 043_Exemple_code_1.qml 
│ ├── 04_jdr_avantages_pb.qml 
│ ├── 05_avantage_jdr_virtuel.qml 
│ ├── 06_fonctionnalites_rolisteam.qml 
│ ├── 07_rolisteam_debut.qml 
│ ├── 08_Rolistik_a_Rolisteam.qml 
│ ├── 10_frise_chronologique.qml 
│ ├── 11_son_usage.qml 
│ ├── 12_son_fonctionnement.qml 
│ ├── 13_dice_parser.qml 
│ ├── 14_themes_audio_3_pistes.qml 
│ ├── 15_nouveaute_1_8.qml 
│ ├── 16_projet_avenir.qml 
│ ├── 17_reussites.qml 
│ ├── 18_les_lecons.qml 
│ ├── 19_objectif_rolisteam_libre.qml 
│ ├── 20_FAQ.qml 
├── pasSageEnSeine.pro 
├── pasSageEnSeine.pro.user 
├── qmlcontroler.cpp 
├── qmlcontroler.h 
├── qmlcontroler.ui 
├── qml.qrc 
├── README.md 
├── rsrc 
│ ├── all.png 
│ ├── cc.png 
│ └── chat.png

L’application C++

Il peut être utile de connaître l’état de la présentation QML, et de pouvoir lire des notes sur chaque volet. Pour faire cela, j’ai écris une petite application C++.

le fichier main.cpp

Le premier objectif de l’application est d’afficher la présentation QML. Il faut donc charger le QML et établir des moyens de communication entre le monde QML et le C++. Comme vous pouvez le voir, je passe en paramètre la taille de la présentation en paramètre du QML. Ce dernier élément n’est pas obligatoire car par défaut j’utilise la taille de l’écran comme référence.

#include <QApplication>
#include <QQmlApplicationEngine>
#include "qmlcontroler.h"
#include <QQmlContext>
#include <QQuickTextDocument>

#include "cpphighlighter.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;

    engine.rootContext()->setContextProperty("ScreenW",1280);
    engine.rootContext()->setContextProperty("ScreenH",720);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    QmlControler ctr;
    ctr.setEngine(&engine);

    return app.exec();
}
main.cpp

Dans ce projet, la classe QmlControler est l’objet (fenêtre) C++ qui affiche l’aperçu de la présentation et les commentaires/notes.

Regardons en détails cet élément:

La fenêtre d’aperçu

C’est une simple fenêtre. Elle est découpée en deux parties.  Celle de gauche affiche dans un QLabel l’aperçu de la présentation QML. La partie de droite affiche les notes et commentaires sur la page courante dans un QTextArea.

void QmlControler::currentPageHasChanged(int i)
{
    m_currentScreen = i;
    QImage img = m_window->grabWindow();

    if(img.isNull())
        return;

    static int count = 0;


    img.save(tr("screens/%1_screen.png").arg(++count,3,10,QChar('0')),"png");
    qDebug() << "screen shot save" << count;

    m_ratioImage = (double)img.size().width()/img.size().height();
    m_ratioImageBis = (double)img.size().height()/img.size().width();

    m_label->setPixmap(QPixmap::fromImage(img));

    if((i+1>=0)&&(i+1<m_commentData.size()))
    {
        ui->textEdit->setHtml(m_commentData.at(i+1));
    }
    resizeLabel();
}
Current slide has changed

Quand la page courante change, la fenêtre C++ est informé par l’appel du slot: currentPageHasChanged alors l’application récupère une capture de la vue QML, l’affiche à l’écran (dans la partie gauche), affiche les notes à propos de la page courante et peut sauvegarder la capture dans un fichier.

Sauvegarder les captures dans des fichiers vous permet de créer un fichier PDF de votre présentation. Il s’agit là d’une solution de secours en cas de problème avec le matériel de la présentation ou une facilité pour distribuer votre présentation.

La commande qui va bien (sous linux) :

$ convert *.png mypresentation.pdf

 

L’application QML

Le système de chargement (Loader)

Par souci de clarté, je préfère avoir un fichier par page. Cela évite l’écueil d’un fichier QML long et complexe. L’application charge les pages en accord avec le modele de données principal dans le même ordre.  Pour faire cela, j’ai utilisé un ListModel. Une page est définie par un titre, un nom de fichier, un temps (non utilisé), et le nom de la page suivante.
Le fichier main.qml  affiche toutes les pages comme un «delegate» d’un pathview.  Toutes les pages sont chargés depuis le fichier de ressources de Qt (inclue dans le binaire grâce au système de ressources de Qt).

ListModel {
            id: panelModel
            ListElement {
                name: "Intro"
                path: "01_intro.qml"
                time: 1
                next: "Présentation de Rolisteam"
            }
First item of the model.

La donnée vraiment utile pour le chargement est bien sûr le nom du fichier.
Les autres sont là pour aider la rédaction ou la présentation.

Le loader fait son travail, voici les lignes importantes:

    PathView {
        id: view
        anchors.fill: parent
        model: panelModel
        highlightRangeMode:PathView.StrictlyEnforceRange
        snapMode: PathView.SnapOneItem
        delegate:  Loader {
             source: "pages/"+path
        }
Path View

Le temps était destiné à afficher un compte à rebours pour chaque slide mais je n’ai pas eu le temps de l’implémenter.

Il est important de mettre dans le main.qml tous les éléments que vous souhaitez avoir sur toutes les pages: le sommaire, l’indication de la page courante sur le total de pages, etc.

Ajouter un sommaire

Pour réaliser un sommaire, j’ai ajouté un ListView avec le modele suivant:

    ListView {
        id: listView1
        x: ScreenW*0.02
        y: ScreenH*0.3
        width: ScreenW/2
        height: ScreenH*0.2
        delegate: Item {
            width: ScreenW/2
            height: listView1.height/listView1.count
                Text {
                    color: view.currentIndex>=index ? "black" : "gray"
                    text: name
                    font.pointSize: ScreenH/48
                    anchors.verticalCenter: parent.verticalCenter
                    font.bold: true

                }
        }
        visible: view.currentIndex>0 ? true : false

        model: ListModel {
            ListElement {
                name: "Concepts"
                index:1
            }
            ListElement {
                name: "Chroniques"
                index:6
            }
            ListElement {
                name: "Logiciel"//système de build, code spécifique par OS.
                index:9
            }
            ListElement {
                name: "Bilan"
                index:15
            }
        }
    }
Table of contents in QML

Le champs index du modèle permet de connaître la page qui démarre cette section du sommaire.

Page suivante

Quand il y a beaucoup de pages il peut être intéressant de connaître la prochaine page afin de faire une petite transition vers elle. Dans mon cas, j’affiche dans le coin haut-droit le titre de la prochaine page. Ce fut assez utile pour les répétitions, je n’en ai presque pas eu besoin pendant la conférence.

    Text {
        anchors.top: parent.top
        anchors.right: parent.right
        text: panelModel.get(view.currentIndex).next+">"
    }
Next slide

L’autre possibilité est d’utiliser les notes dans la fenêtre C++.

Créer une Page de présentation QML

Chaque page est indépendante, elles peuvent être totalement différentes cependant pour des raisons de simplicité, elle utilise une conception identique. Dans mon cas, elles sont constituées d’une ListView avec un modèle. Chaque élément du modèle est un titre de section à évoquer pendant la présentation.

Les éléments ont un index. Cet index est contrôlé par le clavier (flèche bas pour augmenter l’index, haut pour réduire l’index).  Grâce à cet index, il est possible de cacher la liste et d’afficher une image, un schéma ou une vidéo.

Par exemple, dans ma présentation la fonctionnalité «Alias de dés» a pour index 10. Quand la index de la page devient 10, l’élément «Alias de dés» apparaît avec une petite animation. Puis l’index passe à 11, j’affiche une image illustrant la fonctionnalité des alias de dés. A 12, l’image disparaît et le texte revient.

Position et taille

Pour s’assurer de la lisibilité des éléments, je réalise l’ensemble des calculs en rapport à la taille de l’écran. Le positionnement est assuré par des ancres (anchors en qml).

    Image {
        id: image1
        anchors.left: parent.left
        anchors.top: parent.top
        anchors.leftMargin: ScreenW*0.04
        fillMode: Image.PreserveAspectFit
        source: "qrc:/rsrc/Rolisteam.svg"
        width: ScreenW*0.2
    }
Display the logo at the right position and size.

Autre piste

Il existe un module qui fournit des éléments QML pour faciliter la réalisation d’une présentation. Je ne l’ai pas utilisé mais il y a des choses intéressantes.

Module QML

Obtenir le code de mes présentations

Je vous invite à cloner ce code : https://github.com/obiwankennedy/pses

Autre exemple avec une image: https://github.com/obiwankennedy/l5rSummary

L5R présentation QML

Exemple de présentation QML