Journal Squarez, le retour

Posté par . Licence CC by-sa
Tags :
28
7
fév.
2014

Bonjour journal,
Il y a plusieurs vendredis je t'ai présenté un petit jeu pour navigateur dans ce journal, depuis il a légèrement évolué.

Finalement je n'ai pas suivi la liste que j'avais donnée pour les améliorations, j'ai tout de même créé quelques graphismes, réécrit la majorité du code de l'interface et optimisé quelque peu les performances.

Suivant les conseils dans les commentaires, le jeu a été publié sur le marketplace Firefox, Mozilla m'a fourni un Geeksphone Keon pour le porter, ce qui m'a permis de voir que les performances étaient déplorables et m'a fait travailler des heures pour que ça soit acceptable.

Maintenant parlons des nouveautés, je viens de terminer une version pour jolla qui utilise donc Qt/qml. Le jeu sera publié d'ici quelques jours au moins sur le magasin officiel.
C'est mon premier développement en Qt, et le code est commun avec la version pour navigateur. Je vais donner mes impressions en tant que développeur C++ qui découvre ces outils.
* Qt n'est pas du C++, pour utiliser les fonctionnalités qui le rendent intéressant il est très intrusif (des macros partout, la préférence pour les pointeurs, des contraintes sur la gestion des threads)
* Qt montre ses origines historiques et n'aime vraiment pas la stl (bibliothèque standard C++ qui permet de gérer vecteurs, chaînes de caractères, entrées/sorties)
* La séparation modèle/vue offerte par qml est vraiment bien pensée, la partie de logique n'a pas à faire de suppositions sur l'affichage qui en sera fait. La partie affichage réagit à des événements, présente les informations fournies par le modèle et utilise les interfaces prévues pour le modifier.
* Qml est vraiment bien pensé, je ne lui reproche que le positionnement des objets qui est souvent hasardeux, plusieurs modèles sont proposés pour places les éléments et c'est difficile au moins au début de les utiliser correctement.

Par rapport à la version pour navigateur, qu'est-ce que c'est fluide ! Sur le téléphone même les effets de particules ne ralentissent pas les animations.

Enfin, quelques commentaires sur les autres outils que j'ai utilisé.
La version html utilise emscripten pour générer le javascript. Le projet est excellent, il fait exactement ce qu'on lui demande. Mes souhaits pour qu'il soit encore meilleur sont une amélioration de la documentation (pour les bindings c++ je suis souvent allé voir les sources des templates directement), une meilleure intégration dans les outils de compilation : je n'ai pas trouvé comment compiler à la fois une version javascript et du code natif, et un compilateur plus rapide (mais j'ai vu que c'est en cours).
Aucun navigateur ne m'a donné des informations utiles sur le temps consommé pour le dessin des pages. Comme le jeu utilise des éléments html pour représenter les éléments du jeu, j'ai dû trouver moi-même ce qui est rapide et ce qui ne l'est pas.
Qtcreator a lui au contraire des outils de mesure de performance très intéressants, on peut savoir à quelle vitesse tournent les animations, quelles sont les fonctions qui prennent du temps à être traitées, quels signaux sont émis, ce qui ne m'a pas été vraiment utile étant donné que je n'ai pas eu de problèmes de performance.

Pour ceux qui veulent juste perdre du temps sur le jeu lui-même, c'est ici.

  • # Ah tiens

    Posté par (page perso) . Évalué à 6. Dernière modification le 07/02/14 à 14:24.

    Qml est vraiment bien pensé, je ne lui reproche que le positionnement des objets qui est souvent hasardeux, plusieurs modèles sont proposés pour places les éléments et c'est difficile au moins au début de les utiliser correctement.

    "Hasardeux" ?

    Je trouve au contraire que le système de positionnement est une grande force de QML car il est d'une souplesse Jean-Claude Van Dammienne.
    On peut utiliser du positionnement absolu, relatif (par rapport à d'autres composants), ou bien du layouting plus "classique" (en colonne, en grille, etc).
    Après, si tu veux dire qu'on est un peu perdu au début, je peux te rejoindre là dessus, mais je ne connais aucun autre environnement qui me permette de positionner mes éléments aussi facilement que je le fais en QML.

    • [^] # Re: Ah tiens

      Posté par . Évalué à 1.

      Tu as raison, c'est surtout une impression qui m'est restée parce que j'étais perdu au début. Comme il y a beaucoup de choix sur la méthode pour positionner un élément, Internet ne donne pas toujours la bonne réponse au problème qu'on se pose et c'est pas simple de trouver l'outil correct.

      Ceci dit je ne sait toujours pas comment faire une simple table avec du qml : mon modèle me donne plusieurs valeurs pour chaque index et je veux simplement que les colonnes puissent se redimensionner en fonction du contenu.

      • [^] # Re: Ah tiens

        Posté par (page perso) . Évalué à 2.

        Ceci dit je ne sait toujours pas comment faire une simple table avec du qml : mon modèle me donne plusieurs valeurs pour chaque index et je veux simplement que les colonnes puissent se redimensionner en fonction du contenu.

        Une table en QML, c'est classiquement le ListView.
        Et au niveau des largeurs automatiques, ce que tu cherches à faire ressemble à ce qui est décrit sur cette page :
        http://qt-project.org/wiki/How_to_create_columns_in_a_QML_ListView

        • [^] # Re: Ah tiens

          Posté par (page perso) . Évalué à 3.

          Cela dit, vu que je suis un peu rouillé en QML ces temps-ci, j'ai oublié de te dire que si tu as la possibilité d'utiliser QtQuick.Controls 1.1, tu as le TableView qui formalise un peu plus le concept de tableau.
          Mais attention, contrairement au listview qui est "à poil", ce composant utilise par défaut le thème natif de la plateforme cible, mais tu peux le customiser aux petits oignons.

        • [^] # Re: Ah tiens

          Posté par . Évalué à 1.

          C'est effectivement une solution qui va marcher dans la majorité des cas.
          Je trouve que c'est tout de même un hack, ça ne gère pas facilement la possibilité d'avoir des contenus différents par colonne, un contenu plus complexe qui dépend de propriétés (comme mettre des éléments en gras ou des icônes en fonction de la valeur dans le modèle).

          Il y avait un fil de commentaires sur la dépêche sur LibreOffice à propos du dénigrement de code ancien, voici exactement un exemple de comment il apparaît. J'utiliserai probablement ce javascript ou une adaptation pour faire mes tables, il sera complexifié pour tenir compte de cas particuliers, d'ici quelques temps qml aura un support natif des tables et tout le monde trouvera que mon code sera monstrueux.

          • [^] # Re: Ah tiens

            Posté par (page perso) . Évalué à 2.

            Tu es surtout confronté (comme moi à l'époque) au fait que les composants de base de QtQuick sont relativement primitifs et qu'on se retrouve à réinventer la roue lorsqu'on souhaite revenir à iso-fonctionnalités avec les applications plus « traditionnelles ».

  • # Des exemples !

    Posté par (page perso) . Évalué à 3.

    En tant que développeur, je serai curieux de connaitre les exemples concrets sur la non-intégration STL, l'utilisation de pointeurs ou la gestion des threads. Il me semblait que beaucoup de chemin avait été fait dans ce sens.

    • [^] # Re: Des exemples !

      Posté par . Évalué à 4.

      Les cas que j'ai eu le temps de voir:

      Le qml ne peut pas afficher des std::string, il faut utiliser des QString.

      L'interface graphique doit être mise à jour dans le thread principal, lorsque l'on utilise des std::thread (C++11), le système de signaux ne peut pas savoir qu'il doit transférer le signal vers un autre thread et il y a toutes les chances qu'en cascade l'interface soit touchée par le mauvais thread. Si on veut utiliser des std::thread, on doit renoncer aux signaux.

      Un QObject n'est pas copiable/déplaçable, si on veut avoir un membre qui soit un QObject on en revient souvent à avoir des (auto-)pointeurs vers le QObject. C'est peut-être un mauvais usage de ma part, je n'ai pas cherché beaucoup plus loin.
      À quelques endroits les pointeurs sont préférés à des références, comme pour la création d'un QVariant, nécessaire pour exposer un modèle via un QAbstractListModel par exemple.

      Il y a clairement une duplication de fonctionnalités au niveau des conteneurs, je ne vois pas ce qu'apportent les QList et semblables.

      Peu d'objets sont utilisables dans un stream, ou alors il faut utiliser un header que je ne connais pas. Un QString ne peut pas être affiché sur la sortie standard avec std::cout << monQString;

      L'intégration qui existe permet la création de QString à partir de std::string et inversement, l'utilisation de lambdas, std::function et pointeurs sur méthode pour la connexion de signaux, et tous les problèmes que je n'ai pas eu parce qu'ils ont été corrigés !

      • [^] # Re: Des exemples !

        Posté par (page perso) . Évalué à 3.

        C'est intéressant.

        (Note: je ne fais plus que du PyQt depuis longtemps, j'ai perdu de vue Qt/C++ depuis la version 3).

        De fait, utiliser Qt fonctionne bien avec une cohérence globale au niveau de ses types, de ses conteneurs et autres. Il arrive bien à échanger des données avec des conteneurs et types externes mais c'est plus une notion d'import/export que d'interopérabilité. Ca ne m'étonne pas que le mélange profond des deux au sein d'un même programme se passe pas bien.

        Donc ça force un style particuliers de programmation, avec des QThread, des QString, des QObject et des QList. Quand j'ai commencé, j'ai trouvé que ce style "forcé" était de bonne qualité et donc ça m'a plutôt plu. Mais je conçois que ça puisse poser des problèmes.

        Lorsque Qt est arrivé en 1994, c'était vraiment une révolution. On était même pas sur de trouver une STL fonctionnelle sur toutes les plate-formes, et à côté, on avait Qt qui fournissait bien plus que la STL, avec une meilleure documentation et une interface plus abordable.

        Depuis, la STL a rattrapé une partie de son retard et devient relativement utilisable (bien qu'un peu lourdingue à mon goût), surtout avec du boost.

        Donc Qt peut être vu comme une solution non standard. Je râlais de la même façon quand je faisais des MFC et que je n'arrivais pas à faire marcher mes std::string et que Microsoft m'obligeais à utiliser des CString (les string en MFC). J'aurai jamais pensé que l'argument se retournerai contre mon toolkit adoré un jour.

        std::cout << monQString;

        Je pense qu'il y a de bonnes raisons à ça. Au hasard, je dirai que comme la STL ne spécifie pas réellement d'encoding par défaut, suivant les options de compilations que tu passes à ton programme, il faut que les chaînes soit encodées en UTF8, UTF16 ou UTF32, voire même latin1.

        Donc Qt t'oblige à faire le choix toi-même, en convertissant soit en UTF8 avec tu toUtf8(), soit en en encoding "locale" avec toLocal8bit().

        Et si tu veux afficher cette même chaîne sur un terminal, l'encoding final à choisir est assez complexe : sous Windows, il faut probablement la convertir un cp1252 ou en UTF8 suivant la version de Windows et de la console, sous Linux UTF8 a l'air de s'en sortir pas trop mal. Je doute honnètement que cout gère ce genre de subtilité, donc au final, ton code sera probablement pas portable.

        Demande à Victor Stinner le cauchemar que c'est de faire marcher l'unicode correctement sous Python.

      • [^] # Re: Des exemples !

        Posté par (page perso) . Évalué à 3.

        lorsque l'on utilise des std::thread (C++11), le système de signaux ne peut pas savoir qu'il doit transférer le signal vers un autre thread et il y a toutes les chances qu'en cascade l'interface soit touchée par le mauvais thread. Si on veut utiliser des std::thread, on doit renoncer aux signaux.

        Tu as essayé ? Je viens d'essayer et ça marche très bien. Qt s'intègre parfaitement avec std::thread et pas de problème pour faire passer des signaux et slots entre en std::thread et un QThread. Si lo'bject est créé dans le std::thread, ses slots seront executé dans ce thread là.

        Un QObject n'est pas copiable/déplaçable,

        Oui, un QObject ne peux pas être copié car il est supposé être hérité. En général, ces classes ne sont pas copiable. Tu peux toujours avoir un constructeur de copie dans ta classe héritière, mais je ne pense pas que ça ait du sens.

        pointeurs sont préférés à des références, comme pour la création d'un QVariant,

        Ah bon? QVariant::fromValue n'a pas besoin de pointeur. Tu n'es pas sensé le constructeur interne qui prends un pointeur.

        Il y a clairement une duplication de fonctionnalités au niveau des conteneurs, je ne vois pas ce qu'apportent les QList et semblables.

        Oui c'est vrai. L'apport est une API unifiée et plus utilisable pour les utilisateur de Qt. Mais c'est vrai que c'est dupliqué.
        Aussi, Qt ne veux pas avoir de type de la STL dans son ABI car Qt conserve une ABI binarie qui est mieux que celle de la standard library. Ainsi, Qt compilé avec libc++ de clang est compatible binaire avec une application qui utilise libstdcpp de gcc, et inversément.

        Un QString ne peut pas être affiché sur la sortie standard avec std::cout << monQString;

        Il faut écrire l'opérateur soit même. C'est vrai que Qt devrait supporter ça. Tu peux toujours envoyer un patch :-)

  • # Peut ton utiliser QML pour generer une web-app ?

    Posté par . Évalué à 2.

    Je suis dev c++, travaillant sur des projets vraiment loiiiin du web. J'ai deja fait du QT, j'avais beaucoup aime.

    Est-il possible d'utiliser la suite Qt/QML pour ecrire une webapp, avec emscripten pour convertir C++ en javascript, et QML qui genererait HTML/CSS ?

    C'est peut etre une question totalement stupide hein…

  • # Pourquoi c'est si lourd dans un navigateur ?

    Posté par . Évalué à 7.

    Certes, mon PC fixe perso accuse 12 ans (P4 à 2.4GHz), mais je peux encore faire tourner des applis et des jeux 3D bien plus gourmands que ça. Là, ma pauvre machine peine à afficher 64 carrés de couleur. Sous Firefox, le CPU est utilisé à 30% quand il ne se passe rien, 100% quand ça bouge. Sous Chrome, 100% en permanence. Même comportement sur un laptop de 2008 (bon, pas tout jeune quand même). Il faut que je sorte mon quad core du boulot pour espérer voir les animations s'animer (ça consomme tout de même un core à 80%).

    À quoi cela est-ce dû ? Qt via emscripten, c'est pas encore ça ?

    C'est dommage, car j'apprécie ce genre de casse-tête. En tout cas, félicitation pour avoir un même code C++ qui fonctionne à la fois en natif et dans un navigateur, ça m'impressionne.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.