Introduction
J'ai eu l'occasion de développer successivement avec Qt et avec les MFC. J'aimerqi partager mon expérience à propos des différences entre les deux boites à outils.Je ne suis pas un auteur professionnel. Cet article n'est donc pas aussi bien écrit que ce que vous pourrez trouver sur des sites web professionels ou dans des magazines. Il s'agit simplement de mon expérience, que je partage avec mes propres mots.
Cet article ne prétend pas être objectif. Je ne prétend pas détailler toutes les qualités et les défauts de Qt ou des MFC. Cet article est juste le reflet de mes impressions.
L'article est écrit d'un point de vue pragmatique: mon chef me donne les spécifications des applications qu'il veut et je les développe. J'en ai développées certaines avec Qt, et d'autres avec les MFC.
Les MFC (Microsoft Foundation Class) sont une boite à outils graphique pour le système Windows. C'est une surcouche plus ou moins objet d'une partie de l'API Win32 développée par Microsoft. L'API est tantot en C, tantot en C++, et en général un mélange pas très clair.
Qt est une boite à outils graphique C++ démarrée en 1994 par Trolltech ( www.trolltech.com). Qt fonctionne sous toutes les versions de Windows, sous MacOs X, sous tous les Unix et sur des équipements embarqués comme le Sharp Zaurus. C'est une bibliothèque totalement orientée objet.
Le modèle document-vue
La programmation en MFC impose l'emploi du Document-Vue et des templates (il est très difficile de ne pas les utiliser, mais rien ne garantit qu'ils soient bien utilisé). Les templates sont cependant figés et il est très difficile de faire quelque chose de non prévu au départ (par exemple splitter une zone pour y afficher deux vues ayant des documents différents, etc...). Un autre problème est que c'est souvent le template qui crée les vues et il n'est pas forcément prévu d'y accéder ensuite. Tout doit être fait par le document, ce qui peut parfois poser quelques problèmes.
Qt n'impose pas de modèle de développement (mais il ne les interdit pas non plus). Il est ainsi possible d'utiliser un modèle document-vue lorsque cela s'avère judicieux, et ceci sans problème d'implémentation.
Pseudo-objet contre pur object
La différence fondamentale entre les MFC et Qt est leur conception.
Les MFC sont une sorte de surcouche objet permettant d'accéder à l'API windows qui est en C. L'architecture n'est pas très objet. Il est nécessaire dans plein de situations de passer une structure 'C' avec une quinzaine de champs (dont seulement un ou deux vous intéressent), appeler des méthodes dont certains paramètres sont obsolètes, etc...
Et il y a plein de trucs sans aucune logique. Par exemple, si on créé en MFC un objet graphique il
ne sera en réalité pas créé tant que la méthode Create() n'aura pas été appelée. Pour les
dialogues, il faut attendre OnInitDialog(), pour les vues OnInitialUpdate(), etc. Ainsi, si on
créé un objet (manuellement) et qu'ensuite on appelle certaines de ses méthodes, le
programme pourra planter !!
Prenons l'exemple d'un dialogue où on a un champ d'edition Cedit, l'appel
depuis le dialogue de la méthode GetWindowText() sur ce champ plantera tant
qu'on ne sera pas dans le DoModal() (soit avant soit après !!). On s'étonne
qu'ensuite le C++ ait la réputation de planter bizarrement.
Qt est tout le contraire. C'est basé sur une architecture purement objet, réellement pensée (par des gens ayant eu l'expérience d'autres bibliothèques comme les MFC, Java, Motif...). Il en résulte une bibliothèque extrêmement cohérente à tous points de vue (nommage, objets, héritage...), complète (classes proposées, méthodes des classes) tout en restant simple d'utilisation (arguments et valeurs retournées des méthodes sont logiques...).
Sous Qt, si vous avez un champ d'édition (QlineEdit), vous le créer avec l'opérateur new, comme n'importe quelle classe et vous pouvez appeler TOUTES ses méthodes, n'importe quand à partir du moment où l'objet existe. Manipuler cet objet revient exactement à manipuler l'objet affiché. Il n'y a pas de trucs à connaitre, ca fonctionne de la façon la plus simple qu'on peut imaginer.
La boucle de messages
Les MFC sont construits autour d'une boucle d'évenements (appelés aussi messages). Toute action se rapporte à la réception et à l'émission de messages. Il existe des milliers de messages renvoyés par Windows au programme... Malheureusement, il n'est du tout évident de savoir lequel utiliser, quelles infos il contient, etc... Certains sont redondants, certains ne sont pas toujours émis, etc... la documentation est d'ailleurs assez déficiente à ce sujet (pas facile de savoir quel objet émet quel message, ni quand). Il est aussi parfois difficile de savoir qui recevra un message émis et qui ne le recevra pas, etc... Certaines fonctionnalités qui pourraient être exécutées par appel de méthodes sont remplacées par des envois de messages, ce qui ne facilite en rien le debuggage ni la relecture du code.
Sous Qt, il existe un système de callback basé sur l'envoi de signaux et leur réception dans des slots qui permet de communiquer entre des classes. Ce système est très souple (on dit explicitement à l'objet d'écouter un signal donné). Il est aussi plus simple car le nombre de signaux différents pouvant être émis par une classe est restreint (rarement plus de 4-5). Ce système est assez proche des 'listener' de Java, mais bien plus léger à utiliser en pratique.
Création graphique
Les MFC ne gèrent pas les réarrangements (layout) dans les fenêtres : cela pose d'énormes problèmes si on veut qu'une fenêtre soit de taille variable. Il faut repositionner soi-même tous les contrôles (cela explique pourquoi tant de dialogues sous Windows ont une taille fixe). Le problème devient plus grave quand on doit traduire une application, car beaucoup de langues ont des expressions plus longues que l'anglais. Vous devez retravailler vos fenêtres pour chaque langue.
L'éditeur de ressources de Visual Studio est fortement limité : il permet de placer des contrôles à l'écran, et c'est à peu près tout. Quelques propriétés sont disponibles via l'onglet 'properties' et le 'class wizard' permet de créer des variables et méthodes relativement facilement. Il faut cependant remarquer qu'il serait quasiment impossible de le faire à la main (gérer la boucle de message à la main, les DDX, IMPLEMENT_DYNCREATE, etc...).
Sous Qt, tout peut être codé à la main, parce que le code est simple. Pour créer un bouton, il suffit de faire:
button = new QPushButton( "texte de mon bouton", MaFenetre );
Pour appeler une methode 'action()' si le bouton est clické, écrivez :
connect( button, SIGNAL( clicked() ), SLOT( action() ) );
Qt a un système d'arrangement automatique qui est tellement simple à utiliser que c'est une perte de temps que de vouloir s'en passer.
Il existe de plus un outil, 'Qt Designer', qui permet une programmation proche d'un RAD (vous pouvez associer facilement du code aux actions, l'éditeur de properties est complet, associer des signaux et des slots, etc...). Il gère le réarrangement automatique et génère du code lisible et compréhensible. Le code est généré dans un fichier séparé, de sorte que vous pouvez modifier votre interface alors que vous l'utiliser déjà dans votre code.
Qt Designer permet de faire des choses qui sont pratiquement impossibles en MFC. Essayez par exemple d'afficher une listview avec des champs préremplis en MFC, ou de faire un 'tab-control' avec différentes vues suivant les onglets...
La documentation
La documentation est un aspect primordial à considérer lorsqu'on veut utiliser une bibliothèque graphique riche en fonctionnalités. Celle de Visual, MSDN semble pléthorique, tient sur 10 CDROM. On y trouve des articles pour faire toutes sorte de choses. Cependant, elle laisse l'impression que ce qui est documenté provient d'une mauvaise architecture. La navigation à l'intérieur est de piètre qualité : impossible d'accéder facilement depuis une classe a ses classes mères ou filles, présentations des méthodes sans la signature, accès difficiles aux méthodes héritées, etc. Par ailleurs, en recherchant de l'aide sur un mot-clé, on tombe sur l'aide de tous les langages sur ce mot-clé: Java, Visual Basic, C++, InterDev, ...
La documentation de Qt est excellente. Le mieux est que vous la regardiez vous-meme: doc.trolltech.com
Elle est à la fois copieuse et compète, puisqu'elle couvre tout Qt, et pourtant, elle ne prend que 18 Mo. Toutes les classes et toutes les méthodes sont documentées correctement, avec une foule de details et des exemples. Il est facile de naviguer d'une classe à une autre, en utilisant soit la version html, soit l'outil fourni par Trolltech (Qt Assistant). La documentation comprend bien sur un tutoriel et des exemples illustrants des utilisations typiques. Il existe aussi une FAQ et une mailing-list, dont les archives sont accessibles via le web ou un newsgroup. Si vous possedez une licence, vous pouvez contacter le support qui répond dans la journée.
Le fait que la conception objet de Qt soit bien pensée est un facteur expliquant la clarté de l'aide, un des objectifs de Trolltech étant La documentation doit être tellement bonne qu'il ne doit pas y avoir besoin de faire du support.
L'Unicode
Sous Qt, toutes les Qstring sont nativement en unicode. A noter qu'il existe des méthodes
permettant de convertir les Qstring en char * (lorsque cela est possible, bien sur). Du code Qt
est donc automatiquement unicode et il n'y a donc aucun problème.
En MFC, ce n'est pas le cas. Tout d'abord il faut compiler/linker avec des options spéciales (changer le point d'entrée de l'exécutable, etc...). Ensuite, il faut rajouter des _T autour de toutes les chaînes (simple et double quote), etc... Il ne faut plus utiliser de char, mais des TCHAR, toutes les fonctions C de manipulation de chaîne sont remplacées par d'autres (autre nom) etc... De plus un programme compilé avec les options 'unicode' ne fonctionnera pas avec une DLL non compilée avec ces options.
Remarque : techniquement la grande différence vient de la conception de la
classe Cstring en MFC à comparer à celle de la class Qstring. Cstring est
basiquement un char * auquel on a rajoute quelques méthodes. Cela
a l'avantage que partout ou on a le droit d'utiliser des char *,
on peut utiliser une Cstring. Ca paraît bien a première vue mais cela entraîne
une série impressionnante d'inconvénients, en particulier parce qu'on peut
alors modifier directement le char * sans réellement se soucier
de la classe (dangereux) et on a vu les problèmes posés lorsqu'on est en
unicode.
La Qstring au contraire stocke en interne un char *, en
effet et même une version unicode de la chaîne, ce qui présente l'inconvénient
d'être plus lourd en effet. Il résulte cependant une puissance lors de
l'utilisation, d'où l'absence de problèmes lors de l'unicode par exemple. Les
QString possêdent par ailleurs des fonctionnalités bien pratique, comme la
copie paresseuse (économie de mémoire et de temps) C'est un exemple typique
d'une conception objet correcte de la part de Qt et d'une bidouille tirée du C
pour les MFC.
L'internationalisation
Il est possible d'internationaliser un programme MFC. Il 'suffit' de mettre toutes les chaînes
que l'on veut voir traduites dans une String Table, (et mettre des LoadString( identifieur )
dans le code, ce qui n'est quand même pas pratique si on veut la modifier rapidement ou
connaître simplement le texte). Ensuite il faut transformer les ressources en DLL, traduire les
chaînes de la string table (en utilisant Visual) dans la langue désirée, traduire aussi les
ressources graphiques (qui ont du texte qui ne se trouve pas dans la String Table) et demander
au programme d'utiliser cette DLL.
Sous Qt, il faut rajouter la macro " tr " devant les chaînes que l'on veut traduire. Ensuite il suffit d'utiliser un programme fourni qui permet d'extraire ces chaînes et surtout les affiche dans une interface permettant la traduction aisée (accès à un dictionnaire, rappel du contexte d'utilisation, gestion des modifications des phrases à traduire, etc...). Il est à noter que c'est une interface à destination d'un traducteur ce qui est bien différent d'une édition de DLL sous Visual.
Cet éditeur permet aussi de traduire les raccourcis clavier (pratique). Il est sous licence GPL (et donc gratuit, améliorable...). Le fichier sur lequel travaille l'éditeur est en xml, les spécifications et le code étant à disposition de tout le monde, c'est une garantie de plus de ne pas être bridé ni limité par Qt en cas de changement de format de leur part, etc...
Les problèmes des ressources
Une partie du développement doit être faite via les " ressources ". Il est indispensable de les utiliser pour de nombreux cas de développement en MFC.
- Il est quasiment impossible de développer avec un autre outil que Visual Studio
- L'éditeur de ressources est limité en terme de fonctionnalités (voire par exemple l'éditeur de dialog (tout n'est pas possible), les propriétés (certaines choses sont paramétrables via les différents onglets des propriétés, mais pas forcément tout)). Pour les toolbar, c'est forcement une image qui concatène les différentes icônes de bouton... Il faut mettre certaines chaînes dans la String Table (par exemple le nom de l'application, auquel cas il faut savoir qu'il faut séparer par des '\n' différents champs qui ont une signification particulière (mais allez la retrouver rapidement).
- Les ressources correspondent a un certain nombre de #define dans le fichier 'Resource.h' (suivi d'une valeur inférieure à 32768). Outre le fait que ce n'est pas propre, cela peut poser des problèmes en cas d'effacement ou de renommage de ressources. Ou des conflits si plusieurs DLL utilisent des fichiers 'resource.h' avec les mêmes nom de ressources et des valeurs différentes.
- Lorsqu'on utilise une DLL qui a ses propres ressources, voire qui utilise aussi d'autres DLL, il y a des risques que le programme confonde les ressources des différentes DLL et du programme (si les #define correspondent aux mêmes valeurs). Il faut alors réserver des plages qui s'excluent mutuellement mais on ne contrôle pas forcément toutes les DLL.
Sous Qt, il n'existe pas de système de ressources, ce qui résout tous les problèmes cités précédemment.
Prix
Quand vous avez acheté Visual Studio, les MFC viennent avec gratuitement.
Qt est gratuit et libre pour sa version Unix (disponible sous licence GPL). Une version gratuite est disponible sous Windows pour utilisation non commerciale. En revanche, pour du développement commercial classique, à sources fermées, vous devez acheter une licence. Cette licence est valable pour une plate-forme, pour un seul développeur, à vie. Elle inclut le support pendant une année. Il n'y a pas de coût de redistribution associé. Le prix est assez cher pour une petite société: 1500 $ (avec des réductions pour l'achat de plusieurs licences).
Cependant, il faut noter que ce prix, c'est moins de la moitié du coùt d'un programmeur pendant un mois. Pour le même développement en Qt et en MFC, on gagne bien plus qu'un demi mois en terme de vitesse de développement et de richesse de l'application résultante. C'est un investissement qui vaut le coup.
Distribution
Pour distribuer votre application MFC, vous dépendez de la présence de la DLL MFC42.dll sur le système d'exploitation cible. Mais pour le même nom, MFC42.dll, il y a en fait trois versions de cette DLL. Il est donc nécessaire de vérifier que l'utilisateur a la version correcte, et sinon, de la mettre à jour. Le mise à jour va changer le comportement de tout plein d'applications sur son système. C'est plutot inconfortable. Que dire si ses applications ne marchent plus après la mise à jour ?
Qt nomme ses DLL explicitement (Qt-mt303.dll). Ainsi, il n'y a aucun risque de modifier le comportement d'une application dépendant d'une autre version de Qt quand on en installe une nouvelle. C'est plus rassurant.
Autres avantages de Qt
Qt n'est pas un concurrent de MFC. C'est un toolkit C++ à part entière. Il fournit ainsi de nombreuses fonctionnalités non présentes dans les MFC, ou bien présentes mais pénibles à utiliser.- Contrôles :
Qt est en premier lieu une bibliothèque graphique. Elle fournit donc un ensemble de widgets
(ou 'contrôles' en langage Windows) complet (labels, champs de saisie...) et variés. Certains sont
très perfectionnés, comme le QlistView qui permet de gérer des arbres à
plusieurs colonnes (avec icône, avec des cases à cocher, etc...)
- XML : Qt fourni des classes permettant de manipuler des fichiers XML (SAX2 et DOM).
Encore une fois, simple d'utilisation, sans bug, complet.
- Expressions Régulières : Qt permet l'analyse des Expressions Régulières . Cela va bien au delà de l'analyse des ' ?' et
'*'. Il est complet, accepte différents types d'ER (perl, etc...). Cela permet de parser des
fichiers ou des lignes aisément ou encore de valider des champs de saisies (masques de saisie
par exemple).
- Multi-plateforme : Qt 3.0 est multi-plateforme : Windows (tout type), Unix (tout type), MacOS. Cela signifie
qu'il suffit juste de recompiler le code sous la bonne plateforme pour générer un exécutable,
sans modifier une seule ligne.
- Classes génériques : Qt n'est pas une simple bibliothèque graphique. Elle fournit aussi des classes génériques
permettant de gérer les map, les list, les files, etc... Elles sont souvent très riches en
fonctionnalités (beaucoup plus que les MFC ou même que la STL) et du coup très pratiques.
Concernant la STL, il est à noter que QT est compatible aussi bien avec ses propres
classes que celles de la STL.
- Gestion de la mémoire : Qt est riche en fonctionnalités facilitant la
gestion de la mémoire. Les objets sont automatiquement détruits quand leur
parent est détruit, la copie et le partage des objets est assisté par un
système de comptage de référence qui fonctionne de façon automatique, ...
- Gestion du réseau : Qt fournit des classes facilitant la programmation
réseau: socket, dns, ftp, http, ...
- Gestion de bases de données : Qt fournit des classes pour une interaction
sans soucis avec des bases de données : connections à divers bases, requètes
SQL, contrôles reflétant automatiquement l'état des données, ...
- Gestion de l'OpenGL: Qt fournit des classes pour s'intégrer sans soucis avec
des bibliothèques OpenGL (3d).
- Canvas : Qt fournit des classes pour une gestion optimisées d'objets à deux
dimensions se déplaçant rapidement (sprites).
- Styles : Il est possible d'adapter compètement le rendu de tous les contrôles Qt. Cela permet à Qt d'emuler tous les styles des toolkits disponibles : Motif, MFC, NextStep, ...
Et Codejock ?
Les nombreux inconvénients des MFC ont fait qu'il existe un certain nombre de surcouches des MFC qui permettent de les utiliser plus facilement ou mieux. J'ai ainsi utiliser la bibliothèque CodeJock et la question peut se poser de comparer MFC+Codejock à Qt. Voici quelques points à noter :
- Codejock est une surcouche des MFC qui sont une surcouche de l'API windows.
Rajouter des surcouches pour masquer les défauts est rarement une bonne solution. La
plupart des défauts précités perdureront donc (templates pour le document vue,
messages, ressources, unicode, internationalisation...).
- Les classes fournies par codejock permettent souvent une utilisation plus aisée des
classes MFC (méthodes plus simples ou en plus, fonctionnalités supplémentaires). Il
est ainsi presque possible de créer une vue avec des tabulations facilement alors que
cela relevait de la mission impossible en MFC. Cela dit cette bibliothèque est très
restreinte, ne fournissant que quelques classes et non pas un jeu complet de classes (on
est a mi-chemin entre la surcouche et le paquet de rustines).
- La bibliothèque est de piètre qualité : il subsiste de nombreux bugs, certains sont
même rajoutés. Ces derniers 6 mois, il y a eu 3 releases (1.9.2.1, 1.9.2.2, 1.9.3.0)
chacune corrigeant une cinquantaine de bugs dont plusieurs majeurs. En fait cette
bibliothèque est loin d'être stable ni testée (elle n'a pas une qualité professionnelle, les
utilisateurs jouant le rôle d'alpha-testeurs (ce n'est pas le cas avec Qt)). Il est à noter
que l'API évolue entre ces différentes releases et qu'il faut donc parfois changer son
propre code si on change de version. C'est encore le signe d'une conception
hasardeuse. Concernant Qt, cela s'est produit que 2 fois depuis 1994 : le passage de Qt
1.x à Qt 2.x et le passage de Qt 2.x à 3.x.
- La lecture du code (malheureusement indispensable dans le cas de codejock pour
comprendre certains fonctionnements étranges) révèle un monceau d'horreurs. En
particulier, des méthodes de plus de 500 lignes (avec du code redondant qui aurait pu
être factorisé, des 'return' en plein milieu...). Très peu de commentaires en général et
des bidouilles à gogo. De nombreux attributs sont aussi déclarés en public, ce qui n'est pas bon a mon avis.
- La documentation est très sommaire, voire vide dans certains cas (la méthode est bien
présente dans la documentation mais il n'y a pas d'explications sur son rôle). Il semble
bien que ce ne soit pas une priorité pour eux.
- Comparativement à Qt, je ne pense pas qu'il existe des fonctionnalités qui seraient présentes dans Codejock et non dans Qt, hormis l'éditeur héxadécimal qui cependant est relativement simple à faire en Qt (des exemples existent d'ailleurs) et est bourré de bugs.
Conclusion
La conclusion qui ressort de mon expérience est évidente : Qt est bien mieux que les MFC, tant en terme de rapidité de développement que de fonctionnalités. Qt permet de faire des programmes de meilleure qualité, avec moins de peine.
Certains se sont plaint que cet article était biaisé vers Qt et n'était pas assez objectif vis à vis des MFC. Il s'agit simplement de notre expérience: programmer avec Qt était simple et rapide, alors que plein de problèmes sont apparus avec les MFC. Si les MFC ont des points positifs, nous ne les avons pas encore rencontré, en dehors du fait d'etre livré gratuitement avec Visual Studio. Le fait que Microsoft ai décidé de les laisser complètement tomber en faveur de .NET vient corroborer notre point de vue.
Nous sommes bien évidemment avide de toute remarque supplémentaire: pour des suggestions, des améliorations, des remarques ou des critiques, contactez-nous!.
Je souhaite inclure des citations de personne ayant utilisé Qt et les MFC. Si vous êtes dans ce cas, écrivez-nous!
Liens
D'autres personne ont écrit (en anglais) des comparaisons avec Qt:
- From Gtk to PyQt, par Philippe Fremy: une analyse du même programme, écrit en Gtk, Qt et PyQt.
- Qt vs Java whitepaper, by Mathias Kalle Dallheimer.
- Guillaume Laurent explique pourquoi Qt est mieux que gtkmm
- Gui Framework
Copyright Philippe Fremy 2002 Last modification : $Date: 2005/07/02 17:45:27 $ - $Author: philippe $