Catégorie : Programmation     Tags : ,      

    Retrouvez cet article dans : Linux Magazine 79

    Voici comment utiliser la bibliothèque multiplateforme wxWidgets sur les systèmes Unix/Linux en C++

    1. Présentation wxWidgets

    xWidgets anciennement nommée wxWindows est une bibliothèque multiplateforme de haut-niveau. Développée au départ par Julian Smart dès 1992 à " Artificial Intelligence Applications Institute ", elle s’est enrichie de plusieurs versions passant par l’utilisation de X11, Motif ou GTK+ pour les systèmes UNIX, MS Windows ou MAC OS (liste non exhaustive).
    Elle est réalisée sur la licence LGPL permettant une utilisation libre ou commerciale sans avoir besoin de fournir les sources des applications. La particularité principale par rapport aux bibliothèques similaires telles Qt ou java (au sens langage+plate-forme) est d’utiliser au maximum les contrôles natifs du système sur lequel elle est installée. Une comparaison rapide par rapport aux bibliothèques existantes s’impose donc.

    2. Comparaison avec d’autres bibliothèques

    2.1 Qt de Trolltech

    Choisi pour le projet KDE, il s’agit d’un excellent choix (déjà présenté dans le magazine), multiplateforme, gratuit pour une utilisation non commerciale sur quasiment tous les systèmes, avec un support technique pour la version payante.
    Cependant Trolltech n’utilise pas les objets graphiques natifs (look’n’feel) des plate-formes, il utilise ses propres objets (sauf sur Mac ?) et se distingue donc des autres applications du système, avec l’avantage d’avoir le même comportement partout. De plus, sa conception est plus conforme à l’esprit objet que ne l’est wxWidgets et un environnement RAD (Rapid Application Development) de qualité (Qt designer) existe. Mais un pré compilateur spécial (moc) est nécessaire pour compiler les programmes. Site : www.trolltech.com

    2.2 FLTK

    Il s’agit d’un projet gratuit pour utilisation commerciale, uniquement axé sur des objets graphiques non natifs (comme Qt). Il y a un concepteur d’interfaces prévu (FLUID). Le code généré est de faible taille avec une API (Application Programmable Interface) de bonne facture, mais il faut utiliser des bibliothèques annexes pour les parties autres que le graphisme (qui possèdent d’ailleurs des objets assez élaborés). Site : www.fltk.org

    2.3 Fox-toolkit

    Il s’agit d’un projet similaire à wxWidgets mais n’utilisant que des objets graphiques non natifs. Plus élaboré que FLTK il ressemble à wxWidgets (ou Qt) en étant moins complet sur certains points, en revanche, il est a priori plus rapide que wxWidgets ou Qt. Il possède un RAD gratuit, mais il est nécessaire d’utiliser des bibliothèques annexes pour les parties autres que le graphisme. Site : www.fox-toolkit.org

    2.4 Ultimate++

    Projet similaire à wxWidgets mais uniquement sur les systèmes Linux et MS Windows. La comparaison est assez difficile à faire et celle fournie sur le site n’est pas correcte. A tester donc. Site : http://upp.sourceforge.net

    2.5 Gtk+

    Il s’agit d’une bibliothèque très connue écrite en C, mais dont le portage n’est pas complet sur tous les systèmes. De plus, les objets ne sont pas natifs. Une utilisation en C++ est possible grâce à Gtkmm ou VDK. Cependant, il faut recourir à des bibliothèques annexes pour les parties autres que le graphisme. Site : www.gtk.org

    2.6 Java

    Il s’agit d’un produit (une plate-forme) gratuit de Sun permettant d’avoir un fonctionnement multiplateforme sans recompilation. Il faut en revanche que la machine virtuelle java (JVM) soit installée sur le système. Le look’n’feel n’est pas respectée (en général) et les programmes demandent des ressources plus importantes pour fonctionner avec une vitesse inférieure à celle d’un programme en C++. Cependant, la gestion mémoire n’étant plus du ressort du programmeur (plus de pointeurs), cela peut simplifier les choses, d’autant que le langage est moins complexe sur certains points que le C++.

    2.7 wxWidgets

    Lorsque l’on cherche une bibliothèque permettant une programmation avec des objets graphiques, une utilisation du réseau, des flux... il y a 3 choix possibles : Qt, wxWidgets et java.
    Si on rajoute : gratuité des applications commerciales, un produit portable, une bonne documentation, un grand choix de langage (Perl, Python, Lua, C#...), un produit mature avec une grande communauté, un look’n’feel du système, alors wxWidget apparaît comme étant la seule bibliothèque à offrir ces caractéristiques. On pourra reprocher : l’utilisation de macros et non des templates en C++ (mais dans l’avenir un passage pur à la STL est prévu), ainsi qu’une programmation moins orientée objet que Qt ou java, mais est-ce vraiment un si gros défaut ?
    Comme il s’agit d’une sur-couche (une façade) de l’API du système, il existe des différences (minimes) entre les versions avec une légère perte des possibilités de l’API d’origine. Mais une émulation des objets graphiques est réalisée lorsqu’un objet n’existe pas.
    De plus, de nombreux programmes de conception d’interfaces existent, mais aucun n’offre une utilisation aussi puissante et complète qu’un Kylix ou Qt designer sous Linux (mais cet inconvénient à tendance à s’estomper si on sépare comme il se doit les données des interfaces).
    Cependant n’ayant pas essayé wxDesigner je ne puis le comparer à Qt designer. Il existe des environnements développés avec wxWidgets : Code::Blocks (IDE), wxFormbuilder, wxRapid, wxGlade qui laissent entrevoir à l’avenir des solutions du type RAD. Site : www.wxwidgets.org

    2.8 Conclusion des comparaisons

    Je ne suis pas en train de dire qu’il s’agit de la meilleure bibliothèque, mais elle permet de faire de nombreux projets sans avoir besoin de rajouter d’autres bibliothèques annexes et elle conserve l’aspect natif du système pour les objets graphiques.
    Du fait de son âge, plus de 13 ans d’existence, il y a une forte communauté prête à répondre aux nombreuses questions des utilisateurs.
    Cependant, toutes les bibliothèques citées plus haut méritent qu’on s’y attarde avant de faire un choix, ce qui ne fut pas facile dans mon cas : VDK, Gtkmm, Qt, java ?
    La gratuité, l’utilisation par de nombreuses sociétés et les réponses rapides de la communauté wxWidgets ont fait pencher la balance. Donc merci à toute cette équipe pour ce magnifique travail.

    3. Installation et utilisation

    3.1 Installation

    La version stable actuelle est la 2.6.2 et existe en un seul exemplaire pour les différents systèmes sauf pour les systèmes UNIX où il y a 3 versions (wxX11, wxMotif et wxGTK).
    On notera le développement d’une version spéciale wxUniversal qui n’utilise pas les objets natifs (comme le fait Qt), mais émule les différents objets.
    La première étape est de télécharger sur le site le fichier : je prendrais comme exemple wxGTK avec comme fichier wxGTK-2.6.2.tar.gz.
    Dans le cas où votre version ne serait pas la bonne, il faut utiliser le fichier wxAll contenant toutes les versions en un seul fichier. Il sélectionnera alors de lui-même la version la plus adaptée lors de l’exécution du script ./configure. L’installation s’obtient alors par les lignes de commande suivantes :

    tar -xzf wxGTK-2.6.2.tar.gz
    cd wxGTK-2.6.2
    mkdir wxGTK
    cd wxGTK
    .././configure
    make
    su
    make install
    ldconfig

    Pour tester si tout fonctionne faire wx-config --version

    3.2 Compilation des programmes

    Il suffit simplement de taper : g++ mon_prog.cpp -o mon_prog `wx-config --libs --cppflags` pour obtenir un fichier exécutable mon_prog. Sur la distribution (une Mandriva) que j’utilise, il y a un souci sur l’emplacement des bibliothèques.
    Au moment de l’exécution et du chargement dynamique, le système ne les retrouve pas. Pour corriger cela, utilisez strace mon_prog pour voir où le système attend les bibliothèques.
    Par défaut, wxWidgets installe les librairies dans le répertoire /usr/local/lib. Il suffit alors de faire un lien entre le répertoire attendu et celui de /usr/local/lib pour qu’il les trouve (ou simplement de changer le PATH).

    4. Premier programme

    4.1 Programme permettant d’afficher une fenêtre

    Classique parmi les classiques, voici une fenêtre " Hello World ", premier programme (en anglais désolé) fourni par tous les langages et toutes les bibliothèques (wxWidgets n’échappant pas à la règle). Il s’agit du programme le plus simple pour afficher une fenêtre.

    #include <wx/wx.h>
    // l’application
    class MyApp : public wxApp
    {
      public:
    // équivalent du main ou winmain
       virtual bool OnInit();
    };
    
    IMPLEMENT_APP(MyApp)
    
    // entrée de l’application
    bool MyApp::OnInit()
    { // création d’une fenêtre
      wxFrame *frame = new wxFrame((wxFrame *)NULL,
           -1, wxT("Hello World"),
           wxPoint(50,50),wxSize(450,340) );
    // on affiche la fenêtre
      frame->Show(true);
    // on la place en premier plan
      SetTopWindow(frame);
    // on indique que la boucle d’événements peut continuer
      return true;
    }

    /img-articles/lm/79/cc-art-widgets/fig-1.jpg

    On a un fonctionnement par défaut d’une fenêtre. Si on clique sur la croix, on ferme la fenêtre, on peut l’agrandir à la dimension voulue. Toutes ces actions génèrent des événements qui sont traités par la fenêtre.
    Il est possible de modifier ce fonctionnement en ajoutant à l’objet une table d’événements, c’est-à-dire un contrôleur d’événements propre qui se placera devant le contrôleur par défaut de l’objet.

    4.2 Améliorations du programme

    4.2.1 Changer la fermeture normale de la fenêtre

    On souhaiterait fermer la fenêtre sur la troisième tentative, pour cela il suffit de créer une nouvelle classe dérivée de wxFrame possédant un gestionnaire d’événements pour l’événement associé à la fermeture de la fenêtre. On remplace simplement dans le programme précédent wxFrame par MyFrame.

    class MyFrame : public wxFrame {
     public:
      MyFrame(wxWindow* f,int n,wxString t,
        wxPoint p,wxSize s) :
      wxFrame(f,n,t,p,s) {}
    // méthode associée à la fermeture
      void OnClose(wxCloseEvent& event);
    // déclaration d’une table d’événements
      DECLARE_EVENT_TABLE()
    };
    
    void MyFrame::OnClose(wxCloseEvent& event) {
     static int n=0;
     n++;
    // on détruit la fenêtre (une autre solution existe)
     if (n>=3) Destroy();
    }
    // contrôleur d’événements au-dessus de wxFrame
    BEGIN_EVENT_TABLE(MyFrame,wxFrame)
      EVT_CLOSE(MyFrame::OnClose)
    END_EVENT_TABLE()

    /img-articles/lm/79/cc-art-widgets/fig-2.jpg

    On récupère l’événement de fermeture du type wxCloseEvent et on compte le nombre de tentatives de l’utilisateur. Une fois le nombre de 3 ou plus atteint, on ferme la fenêtre en utilisant la méthode Destroy(). La fermeture de la dernière fenêtre entraîne alors par défaut la fermeture de l’application. La macro BEGIN_EVENT_TABLE indique que le contrôleur d’événements fait partie de MyFrame et est au-dessus du contrôleur de wxFrame.
    On désire cette fois-ci demander à l’utilisateur s’il est sûr de fermer la fenêtre, car il peut s’agir d’une action involontaire de sa part. Pour cela, on va ouvrir une boîte de dialogue avant la fermeture pour demander son accord. Ensuite, plutôt que de détruire la fenêtre par la méthode précédente, on va simplement poursuivre le fonctionnement normal d’une fenêtre en laissant l’événement se propager sur le contrôleur de l’objet de base en l’occurrence ici wxFrame (si l’utilisateur clique sur Oui).

    void MyFrame::OnClose(wxCloseEvent& event)
    {
     wxMessageDialog box((wxWindow*)NULL,
        wxT(“Sur de quitter ?”),
        wxEmptyString,wxYES_NO );
     if (box.ShowModal()==wxID_YES) event.Skip();
    }

    Pour laisser l’événement se poursuivre, il suffit d’utiliser la méthode Skip().

    4.2.2 Ajout de menus

    On va ici ajouter des menus et traiter la sélection. Les événements envoyés sont du type wxCommandEvent. Cet événement a ceci de particulier qu’il s’agit du seul événement capable de se propager d’une fenêtre enfant vers la fenêtre parent. Cela permet donc de définir des menus, des barres d’outils dans une fenêtre fille et de profiter du traitement dans la fenêtre mère. On a alors le code suivant :

    class MyFrame : public wxFrame {
     public:
      MyFrame(wxWindow* f,int n,
        wxString t,wxPoint p,wxSize s);
    // Traitement de la fermeture de la fenêtre
      void OnClose(wxCloseEvent& event);
    // On sélectionne le menu Nouveau
      void MenuNouveau(wxCommandEvent& event);
    // on veut quitter par le menu
      void MenuQuitter(wxCommandEvent& event);
    // menu information de la fenêtre
      void MenuAide(wxCommandEvent& event);
      DECLARE_EVENT_TABLE()
      enum {
    // numéro associé à chaque menu
        id_nouveau,id_quit,id_aide
      };
    };
    // table d’événements associée
    BEGIN_EVENT_TABLE(MyFrame,wxFrame)
      EVT_CLOSE(MyFrame::OnClose)
      EVT_MENU(id_nouveau,MyFrame::MenuNouveau)
      EVT_MENU(id_quit,MyFrame::MenuQuitter)
      EVT_MENU(id_aide,MyFrame::MenuAide)
    END_EVENT_TABLE()

    /img-articles/lm/79/cc-art-widgets/fig-3.jpg

    Le traitement et la sélection des événements se font grâce aux numéros associés à chaque menu, on peut si on le désire traiter tous les menus dans une seule fonction. Pour cela, il faut utiliser le numéro wxID_ANY ou -1.
    Le & devant le F de Fichier permet d’avoir un raccourci au clavier en utilisant [ALT]+[F]. Ensuite, il suffit de taper sur la lettre N pour avoir le menu Nouveau.

     MyFrame::MyFrame(wxWindow* f,int n,
       wxString t,wxPoint p,wxSize s) :
        wxFrame(f,n,t,p,s,
        wxSTAY_ON_TOP|wxDEFAULT_FRAME_STYLE )
    {
    // une barre de menu
      wxMenuBar* menubar=new wxMenuBar();
    // le premier menu
      wxMenu* menu_1 = new wxMenu();
      menu_1->Append(id_nouveau, wxT(“&Nouveau”),
         wxT(“Une nouvelle fenetre”),
         wxITEM_NORMAL);
      menu_1->Append(id_quit, wxT(“&Quit”),
         wxT(“Quitter la fenetre”), wxITEM_NORMAL);
    // ajout du premier menuà la barre
      menubar->Append(menu_1, wxT("&File"));
    // le deuxième menu
      wxMenu* menu_2 = new wxMenu();
      menu_2->Append(id_aide, wxT("&A propos"),
         wxT("A propos de la fenetre"),
         wxITEM_NORMAL);
      menubar->Append(menu_2, wxT("&Aide"));
    // on affecte la barre de menu à la fenêtre
      SetMenuBar(menubar);
    }

    La première chaîne associée est le nom du menu, la deuxième est une information pour l’utilisateur. Cependant, cette information n’est pas visible sur cette version de la fenêtre car celle-ci ne contient pas de ligne d’informations. On pourra noter que l’aide ne s’affiche pas par défaut avec des " ballons " mais dans la barre de statut.

    4.2.3 Ajout d’une ligne d’information

    Cette ligne permet d’afficher les informations sur les objets sélectionnés ou pouvant être sélectionnés par un passage de la souris. Pour ajouter cette ligne, il suffit de rajouter dans le constructeur de la fenêtre (donc MyFrame) le code ci-dessous :

    // 1 seule zone de texte
    wxStatusBar* statusbar=CreateStatusBar(1);
    // texte dans la 1ere zone=>0
    statusbar->SetStatusText(
      wxT(“Aide de la fenetre”),0
    );

    /img-articles/lm/79/cc-art-widgets/fig-4.jpg

    On a alors une information des menus lorsque la souris passe dessus. Cependant, on peut aussi rajouter des " tuyaux " (les tips) pour la fenêtre. Par défaut, une fois celle-ci fermée, on a le nom de la fenêtre comme ballon soit le " Hello World ".
    On peut aussi utiliser la propagation des événements wxCommandEvent en mettant les menus et barres d’outils dans des fenêtres filles séparées.
    Le traitement ne se faisant pas dans celles-ci, une propagation automatique aura lieu, permettant une exécution dans les fenêtres mères. Cela permet d’obtenir de façon aisée plusieurs présentations pour une même application.
    Pour avoir un " tuyau " lorsque la fenêtre est ouverte, il suffit de rajouter :

     // dans le constructeur de la fenêtre.
    SetToolTip(wxT("Une fenetre"));

    4.2.4 Ajout d’une barre d’outils

    Il est souvent plus intéressant et plus rapide d’utiliser une barre d’outils avec des images représentant les actions à effectuer telles " disquette " pour sauvegarder, " dossier " pour ouvrir un fichier...
    Pour cela, il convient de rajouter une barre d’outils avec les mêmes numéros associés que les menus, car les événements sont les mêmes. Le code à ajouter dans le constructeur est le suivant :

    // création de la barre d’outils
    wxToolBar *toolbar=new wxToolBar((wxWindow*)this,-1);
    toolbar->AddTool(id_nouveau, wxT("Nouveau"),
      wxBitmap(wxT("nouveau.png"),
      wxBITMAP_TYPE_ANY), wxNullBitmap,
      wxITEM_NORMAL, wxT(“Nouvelle fenetre”),
    // on ajoute un outil avecl’image associée
      wxT("Ouvrir une nouvelle fenetre"));
    toolbar->AddTool(id_aide, wxT("A propos"),
      wxBitmap(wxT("a_propos.png"),
      wxBITMAP_TYPE_ANY), wxNullBitmap,
      wxITEM_NORMAL, wxT("A propos"),
      wxT("Une aide"));
    toolbar->AddTool(id_quit, wxT("Quit"),
      wxBitmap(wxT("quit.png"),
      wxBITMAP_TYPE_ANY),
      wxNullBitmap, wxITEM_NORMAL,
      wxT(“Au revoir”),
      wxT(“Vous quittez deja ?”));
    // on fixe la barre d’outils dans la fenêtre
    SetToolBar(toolbar);
    // on "réalise" cette barre d’outils
    // pour qu’elle puisse s’afficher
    toolbar->Realize();

    /img-articles/lm/79/cc-art-widgets/fig-5.jpg

    On a ici un objet qui permet d’avoir des informations ballon comme le permettent tous les objets dérivant du type wxWindow.
    Le programme permet d’avoir une même réponse que celle obtenue avec les menus. On traite le même type d’événements.
    Par contre, si on ajoute les lignes précédentes dans le constructeur, une erreur va se produire à l’exécution, car il faut pouvoir lire les images du type PNG (ou d’autres types).
    Pour résoudre ce problème le plus simple est d’ajouter ::wxInitAllImageHandlers(); dans la méthode OnInit de l’application afin de pouvoir lire tous les types d’images gérées.

    5. Conclusion

    Cette première partie vous présente l’application de base réalisée avec wxWidgets en C++.
    Pour le moment il n’y a aucun objet affiché dans la fenêtre, pas de liste de choix, pas de zone de texte, simplement une fenêtre vide.
    Le second article présentera la méthode pour réaliser un économiseur d’écran en faisant onduler celui-ci. Il décrira la façon de dessiner sur une fenêtre, la gestion des timers ainsi que les événements liés à l’affichage.

    Posté par (La rédaction) | Signature : Olivier Corrio | Article paru dans Creative Commons License

    Laissez une réponse

    Vous devez avoir ouvert une session pour écrire un commentaire.