TP : JEditor, un simple éditeur de fichier texte

Avatar de Monsieur X
Description du chapitre et des ses objectifs :

Pour commencer à nous familiariser avec Qt, je vous propose de réaliser un simple éditeur de texte.

Image de Partie

Accéder directement à une des parties du cours :

Cahier des charges

Ce lecteur possédera 3 menus :

  • Fichier
    • Nouveau
    • Ouvrir
    • Enregistrer
    • Enregistrer Sous
    • Quitter
  • Édition
    • Couper
    • Copier
    • Coller
  • Aide
    • A propos de JEditor
    • A propos de Qt Jambi

On pourra donc créer, ouvrir, enregistrer et enregistrer sous un fichier texte.
En plus d'avoir ces menus dans la barre de menu, on ajoutera une toolbar contenant les plus utiles.

La solution

Pour avoir une barre de menu, il nous faudra hériter de la classe QMainWindow de Qt.
En effet, avec Qt, lorsque l'on veut créer un widget, on hérite de la classe en Qt et ensuite on ajoute des méthodes d'actions spécifiques.
Par exemple les méthodes new(), open(), save(), saveas(), quit().

N'oubliez pas la documentation de Qt Jambi.
C'est votre source de renseignement par excellence. Dedans, il y a absolument tout.

Vous pouvez aussi la trouver dans votre installation de Qt Jambi en local.

Vous le savez peut-être, Qt Jambi, nous propose un outil très intéressant que l'on appelle QtDesigner.
Malgrès les apparences, cet outil est difficile à prendre en main.
En le lançant, on s'aperçoit très vite que l'on peut faire des choses très jolies.
Mais c'est un faux semblant car ensuite, vous n'arriverez pas à l'exploiter correctement dans vos programmes.

Pour bien comprendre la manière dont les choses se présentent, on ne va pas utiliser QtDesigner dans un premier temps.
Voici le code commenté de l'application :
Vous pouvez télécharger le pack d'image.

Code java - Numéro de ligne
  1. /*
  2.  * JEditor.java
  3.  * Version : 2
  4.  *
  5.  * Created on 2 novembre 2007
  6.  * Modified on 13 novembre 2007
  7.  */
  8.  
  9. /* Import des classes espace de nomage nécessaires */
  10. import com.trolltech.qt.QVariant;
  11.  
  12. /* Importation des éléments du GUI */
  13. import com.trolltech.qt.gui.QApplication;
  14. import com.trolltech.qt.gui.QMainWindow;
  15. import com.trolltech.qt.gui.QTextEdit;
  16. import com.trolltech.qt.gui.QMenu;
  17. import com.trolltech.qt.gui.QToolBar;
  18. import com.trolltech.qt.gui.QAction;
  19. import com.trolltech.qt.gui.QMenuBar;
  20. import com.trolltech.qt.gui.QFileDialog;
  21. import com.trolltech.qt.gui.QCloseEvent;
  22. import com.trolltech.qt.gui.QMessageBox;
  23. import com.trolltech.qt.gui.QIcon;
  24. import com.trolltech.qt.gui.QKeySequence;
  25. import com.trolltech.qt.gui.QCursor;
  26.  
  27. /* Importation des éléments de gestions */
  28. import com.trolltech.qt.core.QFile;
  29. import com.trolltech.qt.core.QTextStream;
  30. import com.trolltech.qt.core.Qt;
  31.  
  32.  
  33. /* Définition de l'application et de ses attributs */
  34. public class JEditor extends QMainWindow {
  35.     private String curFile;         // Fichier actuellement ouvert
  36.     private QTextEdit textEdit;     // Widget permettant l'affichage et la modification du texte
  37.     private QMenu fileMenu;         // Menu Fichier
  38.     private QMenu editMenu;         // Menu Edition
  39.     private QMenu helpMenu;         // Menu Aide
  40.     private QToolBar fileToolBar;   // Barre d'outil Fichier
  41.     private QToolBar editToolBar;   // Barre d'outil Edition
  42.     private QAction newAct;         // Action Nouveau
  43.     private QAction openAct;        // Action Ouvrir
  44.     private QAction saveAct;        // Action Enregistrer
  45.     private QAction saveAsAct;      // Action Enregistrer Sous
  46.     private QAction exitAct;        // Action Quitter
  47.     private QAction cutAct;         // Action Couper
  48.     private QAction copyAct;        // Action Copier
  49.     private QAction pasteAct;       // Action Coller
  50.     private QAction aboutAct;       // Action A Propos de JEditor
  51.     private QAction aboutQtAct;     // Action A propos de Qt Jambi
  52.     private String rsrcPath = "classpath:images"; // Répertoire des images
  53.    
  54.     /* Définition du constructeur */
  55.     public JEditor()
  56.     {
  57.         QMenuBar menuBar = new QMenuBar();                // On crée la barre de menu
  58.         textEdit = new QTextEdit(this);
  59.         setMenuBar(menuBar);                              // On ajoute la barre de menu à notre Application
  60.         setCentralWidget(textEdit);                       // On ajoute la zone de Texte
  61.         /* On lance les méthodes de création des différents attributs de notre fenêtre */
  62.         try {
  63.               createActions();
  64.           } catch (Exception e) {
  65.               e.printStackTrace();
  66.           }
  67.           createMenus();
  68.           createToolBars();
  69.           createStatusBar();
  70.           /* On lance la méthode documentWasModified lorsque le contenu du texte est modifié */
  71.           textEdit.document().contentsChanged.connect(this, "documentWasModified()");
  72.           /* Le fichier de départ est un fichier vide */
  73.           setCurrentFile("");
  74.       }
  75.      
  76.       /* On redéfinit la méthode lorsque l'on clique sur le bouton fermer de la fenêtre pour demander à l'utilisateur
  77.          s'il veut enregistrer avant de quitter */
  78.       public void closeEvent(QCloseEvent event)
  79.       {
  80.           if (maybeSave()) {
  81.               event.accept();
  82.           } else {
  83.               event.ignore();
  84.           }
  85.       }
  86.      
  87.       /* Lors de l'ouverture d'un nouveau fichier, on doit fermer le précédent, on avertit donc l'utilisateur.
  88.          On efface le contenu du widget textEdit et on met à 0 la valeur du nom de fichier */
  89.       public void newFile()
  90.       {
  91.           if (maybeSave()) {
  92.               textEdit.clear();
  93.               setCurrentFile("");
  94.           }
  95.       }
  96.      
  97.       /* Méthode d'ouverture du fichier */
  98.       public void open()
  99.       {
  100.           if (maybeSave()) {
  101.               String fileName = QFileDialog.getOpenFileName(this);
  102.               if (fileName.length() != 0)
  103.                   loadFile(fileName);
  104.           }
  105.       }
  106.      
  107.       /* On enregistre les modifications effectuées dans le fichier */
  108.       public boolean save()
  109.       {
  110.           if (curFile.length() == 0) {
  111.               return saveAs();
  112.           } else {
  113.               return saveFile(curFile);
  114.           }
  115.       }
  116.      
  117.       /* On enregistre les modifications dans un nouveau fichier */
  118.       public boolean saveAs()
  119.       {
  120.           String fileName = QFileDialog.getSaveFileName(this);
  121.           if (fileName.length() == 0)
  122.               return false;
  123.  
  124.           return saveFile(fileName);
  125.       }
  126.      
  127.       /* Fonction A propos de JEditor */
  128.       public void about()
  129.       {
  130.           QMessageBox.about(this,
  131.                             tr("A Propos de JEditor"),
  132.                             tr("<b>JEditor<b> est une application d'exemple"+
  133.                               "pour faire une démonstration de l'utilisation de Qt Jambi"));
  134.       }
  135.      
  136.       /* Méthode qui définit que le document a été modifié pour savoir si on a besoin de le sauvegarder ou pas */
  137.       public void documentWasModified()
  138.       {
  139.           setWindowModified(textEdit.document().isModified());
  140.       }
  141.      
  142.       /* Création des actions des menus et des toolbars */
  143.       private void createActions()
  144.       {
  145.           /* Actions du menu Fichier */
  146.           newAct = new QAction(new QIcon(rsrcPath + "/new.png"), tr("&amp;Nouveau"), this);
  147.           newAct.setShortcut(new QKeySequence(tr("Ctrl+N")));
  148.           newAct.setStatusTip(tr("Nouveau fichier"));
  149.           newAct.triggered.connect(this, "newFile()");
  150.  
  151.           openAct = new QAction(new QIcon(rsrcPath + "/open.png"), tr("&amp;Ouvrir..."), this);
  152.           openAct.setShortcut(tr("Ctrl+O"));
  153.           openAct.setStatusTip(tr("Ouvrir un fichier"));
  154.           openAct.triggered.connect(this, "open()");
  155.  
  156.           saveAct = new QAction(new QIcon(rsrcPath + "/save.png"), tr("&amp;Enregistrer..."), this);
  157.           saveAct.setShortcut(tr("Ctrl+S"));
  158.           saveAct.setStatusTip(tr("Enregistrer le fichier"));
  159.           saveAct.triggered.connect(this, "save()");
  160.  
  161.           saveAsAct = new QAction(new QIcon(rsrcPath + "/save_as.png"), tr("Enregistrer Sous..."), this);
  162.           saveAsAct.setStatusTip(tr("Enregistrer le fichier sous ..."));
  163.           saveAsAct.triggered.connect(this, "saveAs()");
  164.  
  165.           exitAct = new QAction(tr("Quitter"), this);
  166.           exitAct.setStatusTip(tr("Quitter l'application"));
  167.           exitAct.triggered.connect(QApplication.instance(), "quit()");
  168.  
  169.           /* Actions du Menu Edition */
  170.           cutAct = new QAction(new QIcon(rsrcPath + "/cut.png"), tr("Cou&amp;per"), this);
  171.           cutAct.setShortcut(new QKeySequence(tr("Ctrl+X")));
  172.           cutAct.setStatusTip(tr("Couper la séléction"));
  173.           cutAct.triggered.connect(textEdit, "cut()");
  174.  
  175.           copyAct = new QAction(new QIcon(rsrcPath + "/copy.png"), tr("&amp;Copier..."), this);
  176.           copyAct.setShortcut(tr("Ctrl+C"));
  177.           copyAct.setStatusTip(tr("Copier la séléction"));
  178.           copyAct.triggered.connect(textEdit, "copy()");
  179.  
  180.           pasteAct = new QAction(new QIcon(rsrcPath + "/paste.png"), tr("Co&amp;ller..."), this);
  181.           pasteAct.setShortcut(tr("Ctrl+V"));
  182.           pasteAct.setStatusTip(tr("Coller le texte précédement couper ou copier"));
  183.           pasteAct.triggered.connect(textEdit, "paste()");
  184.          
  185.           /* Action du menu Aide */
  186.           aboutAct = new QAction(new QIcon(rsrcPath + "/about.png"), tr("A Propos de &amp;JEditor"), this);
  187.           aboutAct.setStatusTip(tr("A Propos de JEditor"));
  188.           aboutAct.triggered.connect(this, "about()");
  189.          
  190.           aboutQtAct = new QAction(new QIcon(rsrcPath + "/qt.png"), tr("A Propos de &amp;Qt"), this);
  191.           aboutQtAct.setStatusTip(tr("Show the Qt library's About box"));
  192.           aboutQtAct.triggered.connect(QApplication.instance(), "aboutQt()");
  193.          
  194.           cutAct.setEnabled(false);
  195.           copyAct.setEnabled(false);
  196.           textEdit.copyAvailable.connect(cutAct, "setEnabled(boolean)");
  197.           textEdit.copyAvailable.connect(copyAct, "setEnabled(boolean)");
  198.       }
  199.      
  200.       /* Création des Menus */
  201.       private void createMenus()
  202.       {
  203.           /* Menu fichier */
  204.           fileMenu = menuBar().addMenu(tr("&amp;Fichier"));
  205.           fileMenu.addAction(newAct);
  206.           fileMenu.addAction(openAct);
  207.           fileMenu.addAction(saveAct);
  208.           fileMenu.addAction(saveAsAct);
  209.           fileMenu.addSeparator();
  210.           fileMenu.addAction(exitAct);
  211.  
  212.           /* Menu Edition */
  213.           editMenu = menuBar().addMenu(tr("&amp;Edition"));
  214.           editMenu.addAction(cutAct);
  215.           editMenu.addAction(copyAct);
  216.           editMenu.addAction(pasteAct);
  217.  
  218.           menuBar().addSeparator();
  219.  
  220.           /* Menu Aide */
  221.           helpMenu = menuBar().addMenu(tr("&amp;Aide"));
  222.           helpMenu.addAction(aboutAct);
  223.           helpMenu.addAction(aboutQtAct);
  224.       }
  225.      
  226.       /* Création de la barre de menu */
  227.       private void createToolBars()
  228.       {
  229.           fileToolBar = addToolBar(tr("Fichier"));
  230.           fileToolBar.addAction(newAct);
  231.           fileToolBar.addAction(openAct);
  232.           fileToolBar.addAction(saveAct);
  233.  
  234.           editToolBar = addToolBar(tr("Edition"));
  235.           editToolBar.addAction(cutAct);
  236.           editToolBar.addAction(copyAct);
  237.           editToolBar.addAction(pasteAct);
  238.       }
  239.      
  240.       /* Création de la Barre de Status */
  241.       private void createStatusBar()
  242.       {
  243.           statusBar().showMessage(tr("Prêt"));
  244.       }
  245.      
  246.       /* Test si le fichier doit être sauvegarder ou non */
  247.       private boolean maybeSave()
  248.       {
  249.           if (textEdit.document().isModified()) {
  250.               QMessageBox.StandardButton ret = QMessageBox.warning(this, tr("JEditor"),
  251.                                                                     tr("Le document a été modifié.\n" +
  252.                                                                       "Voulez-vous sauvegarder les modifications ?"),
  253.                                                                     new QMessageBox.StandardButtons(QMessageBox.StandardButton.Ok,
  254.                                                                                                     QMessageBox.StandardButton.Discard,
  255.                                                                                                     QMessageBox.StandardButton.Cancel));
  256.               if (ret == QMessageBox.StandardButton.Ok) {
  257.                   return save();
  258.               } else if (ret == QMessageBox.StandardButton.Cancel) {
  259.                   return false;
  260.               }
  261.           }
  262.           return true;
  263.       }
  264.      
  265.       /* Charger un fichier */
  266.       public void loadFile(String fileName)
  267.       {
  268.           QFile file = new QFile(fileName);
  269.           if (!file.open(new QFile.OpenMode(QFile.OpenModeFlag.ReadOnly, QFile.OpenModeFlag.Text))) {
  270.               QMessageBox.warning(this, tr("JEditor"), String.format(tr("Impossible de lire le fichier %1$s:\n%2$s."), fileName, file.errorString()));
  271.               return;
  272.           }
  273.  
  274.           QTextStream in = new QTextStream(file);
  275.           QApplication.setOverrideCursor(new QCursor(Qt.CursorShape.WaitCursor));
  276.           textEdit.setPlainText(in.readAll());
  277.           QApplication.restoreOverrideCursor();
  278.  
  279.           setCurrentFile(fileName);
  280.           statusBar().showMessage(tr("Fichier Charg"), 2000);
  281.       }
  282.      
  283.       /* Sauvegarde du fichier */
  284.       public boolean saveFile(String fileName)
  285.       {
  286.           QFile file = new QFile(fileName);
  287.           if (!file.open(new QFile.OpenMode(QFile.OpenModeFlag.WriteOnly, QFile.OpenModeFlag.Text))) {
  288.               QMessageBox.warning(this, tr("JEditor"), String.format(tr("Impossible d'écrire dans le fichier %1$s:\n%2$s."), fileName, file.errorString()));
  289.               return false;
  290.           }
  291.  
  292.           QTextStream out = new QTextStream(file);
  293.           QApplication.setOverrideCursor(new QCursor(Qt.CursorShape.WaitCursor));
  294.           out.writeString(textEdit.toPlainText());
  295.           QApplication.restoreOverrideCursor();
  296.  
  297.           setCurrentFile(fileName);
  298.           statusBar().showMessage(tr("Fichier sauvegard"), 2000);
  299.           file.close();
  300.           return true;
  301.       }
  302.      
  303.       /* On enregistre le nom du fichier ouvert comme nom du fichier courant */
  304.       public void setCurrentFile(String fileName)
  305.       {
  306.           curFile = fileName;
  307.           textEdit.document().setModified(false);
  308.           setWindowModified(false);
  309.  
  310.           String shownName;
  311.           if (curFile.length() == 0)
  312.               shownName = tr("sans_titre.txt");
  313.           else
  314.               shownName = curFile;
  315.  
  316.           setWindowTitle(String.format(tr("%1$s[*] - %2$s"), shownName, tr("JEditor")));
  317.       }
  318.      
  319.       /* Lancement de l'application */
  320.       public static void main(String[] args) {
  321.             QApplication.initialize(args);
  322.  
  323.             JEditor application = new JEditor();
  324.             application.show();
  325.  
  326.             QApplication.exec();
  327.         }
  328. }


Voilà le programme commenté.
Je pense que le programmeur chevronné que vous êtes comprendra assez facilement cet exemple.
On peut très facilement ajouter une fonction CTRL+Z.
C'est un petit exemple mais il vous montre déjà pas mal de petites choses.
L'étape suivante serait de pouvoir ouvrir plusieurs fichiers en même temps. (Petite piste : Les threads et l'implémentation de Clonable)
Vous pouvez noter aussi le déplacement géré par Qt des barres de menus, l'agrandissement de la fenêtre aussi.


Voilà, vous avez créé votre première application multi plates-formes en Java avec Qt Jambi.
Vous pouvez avoir différents rendus en utilisant cette commande :

Code console - Numéro de ligne
  1. java JEditor -style=motif
  2. java JEditor -style=plastique
  3. java JEditor -style=windows
  4. java JEditor -style=cde


Pour voir l'application finie, allez ici.

Sommaire - Chapitre suivant

Nos rédacteurs et membres sont pour la plupart ouverts à des remarques constructives et servir à alerter le rédacteur du cours, des fautes éventuelles ou de propositions et nouvelles perspectives de cours etc ...
Pour ce faire cliquez ici

Postez vous aussi un commentaire à cette partie via le lien que voici