{"id":15,"date":"2009-09-02T13:41:22","date_gmt":"2009-09-02T13:41:22","guid":{"rendered":"http:\/\/liberty.fdn.fr\/?p=15"},"modified":"2025-08-17T20:13:44","modified_gmt":"2025-08-17T20:13:44","slug":"qt4-et-le-design-pattern-command","status":"publish","type":"post","link":"http:\/\/renaudguezennec.eu\/index.php\/2009\/09\/02\/qt4-et-le-design-pattern-command\/","title":{"rendered":"Qt4 et le design pattern Command"},"content":{"rendered":"<p>Bonjour,\u00a0 ce petit tutorial va tenter de vous expliquer comment r\u00e9aliser une op\u00e9ration longue tout en restant r\u00e9actif dans une application Qt4.<br \/>\nIl existe plusieurs moyens mais je ne vais en d\u00e9crire qu&#8217;un seul qui \u00e0 mon sens est le plus propre. Comme cas pratique, j&#8217;ai choisi d&#8217;impl\u00e9menter le Design patterns &#8220;Command&#8221;.<br \/>\nLa premi\u00e8re \u00e9tape utilise des thread.<\/p>\n<h2>Communication entre un thread et une application Qt<\/h2>\n<p>Pas de solution miracle, pour faire &#8220;deux choses \u00e0 la fois&#8221; dans une application, il faut passer par du multi-threading. Nous allons cr\u00e9er une classe thread qui h\u00e9rite de QThread. Cette classe sera le support du traitement long \u00e0 effectuer. Voyez \u00e7a comme une sorte encapsulation.<\/p>\n<p class=\"readability-styled\">\/\/.h de la classe<\/p>\n<pre class=\"code\">#ifndef THREAD_H\r\n#define THREAD_H\r\n#include &lt;QThread&gt;\r\n#include \"command.h\"\r\n<b>class<\/b> Thread : <b>public<\/b> QThread\r\n{\r\n    <b>Q_ObJECT<\/b>\r\n        COMMAND* mycommand;\r\n        bool undo;\r\n<b>public<\/b>:\r\n\r\n    Thread(COMMAND* mycommand,bool undo);\r\n\r\n    <b>protected<\/b>:\r\n        void run();\r\n};\r\n\r\n#endif <i>\/\/ THREAD_H<\/i>\r\n<\/pre>\n<p>Comme vous le voyez, c&#8217;est tr\u00e8s simple. Nous h\u00e9ritons de QThread, nous avons deux membres dans cette classe: un pointeur vers une instance de la classe COMMAND (utile pour l&#8217;impl\u00e9mentation du patterns du m\u00eame nom) et un bool\u00e9en qui nous permet de d\u00e9terminer le sens de l&#8217;action &#8220;annuler&#8221; (undo) ou &#8220;normal&#8221; (undo == false). bien entendu, il est possible d&#8217;avoir autant de param\u00e8tre que vous le souhaitez. Il ne faut pas oubli\u00e9 de red\u00e9finir la fonction run().<\/p>\n<p class=\"readability-styled\">\/\/ l&#8217;impl\u00e9mentation<\/p>\n<pre class=\"code\">#include \"thread.h\"\r\n\r\nThread::Thread(COMMAND* _mycommand,bool _undo)\r\n        : mycommand(_mycommand),undo(_undo)\r\n{\r\n\r\n}\r\nvoid Thread::run()\r\n{\r\n    <b>if<\/b>(undo)\r\n        mycommand-&gt;undo();\r\n    <b>else<\/b>\r\n        mycommand-&gt;doCommand();\r\n}\r\n<\/pre>\n<p>Dans le constructeur, je d\u00e9finis les donn\u00e9es membres. La fonction run ex\u00e9cute la commande en fonction du param\u00e8tre undo. Il faut savoir que tout le code appel\u00e9 dans run sera ex\u00e9cut\u00e9 dans un thread diff\u00e9rent.<br \/>\n\u00c0 ce stade, nous avons une classe Thread qui est pr\u00eate \u00e0 recevoir et ex\u00e9cuter une &#8220;command&#8221;.<\/p>\n<p>Nous allons voir maintenant comment cr\u00e9er un design pattern command.<\/p>\n<h2>Impl\u00e9menter le design pattern command<\/h2>\n<p>Ce pattern est tr\u00e8s utile pour g\u00e8rer l&#8217;annulation d&#8217;une action ou par exemple refaire la derni\u00e8re action. Il consiste \u00e0 cr\u00e9er une classe pour chaque action (du moins toutes les actions que vous voulez pouvoir modifier ou dans notre cas, ex\u00e9cuter dans un thread). Qt fournit des outils pour faire \u00e7a. Dans un cadre formateur, je pr\u00e9f\u00e8re l&#8217;impl\u00e9menter enti\u00e8rement.<br \/>\nIl faut tout d&#8217;abord \u00e9crire la classe abstraite qui d\u00e9finit une commande. Cela permettra \u00e0 notre classe thread de bien int\u00e9ragir avec la commande.<\/p>\n<pre class=\"code\">#ifndef COMMAND_H\r\n#define COMMAND_H\r\n#include &lt;QObject&gt;\r\n<b>class<\/b> COMMAND : <b>public<\/b> QObject\r\n{\r\n        <b>Q_ObJECT<\/b>\r\n\r\n<b>public<\/b>:\r\n    <b>virtual<\/b> void doCommand()=0;\r\n    <b>virtual<\/b> void undo()=0;\r\n<b>signals<\/b>:\r\n        void Maximum(int M);\r\n        void Minimum(int m);\r\n        void valueChanged(int v);\r\n        void done();\r\n};\r\n\r\n#endif <i>\/\/ COMMAND_H<\/i>\r\n<\/pre>\n<p>Il y a deux m\u00e9thodes abstraites pures: l&#8217;une pour faire la commande, l&#8217;autre pour l&#8217;annuler. J&#8217;ai ajout\u00e9 quelques signaux pour que la commande communique avec le thread principal pour informer l&#8217;utilisateur de l&#8217;avanc\u00e9e du la t\u00e2che en cours. Toutes les futures commandes de notre application doivent \u00eatre des sous-classes de COMMAND. Vous l&#8217;avez devin\u00e9, il faut impl\u00e9menter une commande, maintenant.<br \/>\nNotre commande sera vraiment basique, c&#8217;est une commande d&#8217;attente (so useless).<\/p>\n<p>\/\/wait.h<\/p>\n<pre class=\"code\">#ifndef WAIT_H\r\n#define WAIT_H\r\n#include \"command.h\"\r\n<b>class<\/b> WAIT : <b>public<\/b> COMMAND\r\n{\r\n<b>public<\/b>:\r\n    WAIT();\r\n\r\n    <b>virtual<\/b> void doCommand();\r\n    <b>virtual<\/b> void undo();\r\n\r\n};\r\n\r\n#endif <i>\/\/ WAIT_H<\/i>\r\n<\/pre>\n<p>Rien de particulier, juste la re-d\u00e9finition des fontions virtuelles pures. Voici leurs impl\u00e9mentations<\/p>\n<pre class=\"code\">#include \"wait.h\"\r\nWAIT::WAIT()\r\n{\r\n\r\n}\r\n\r\nvoid WAIT::doCommand()\r\n{\r\n\r\nint step = 10;\r\n        <b>emit<\/b> Maximum(100);\r\n        int i = 0;\r\n        int k = 0;\r\n        <b>emit<\/b> Minimum(i);\r\n    <b>for<\/b>(int j = 0 ; j&lt; 1000 ; j++)\r\n    {\r\n        sleep(0.5); <i>\/\/fake statement<\/i>\r\n        <b>if<\/b>(i&gt;=step)\r\n        {\r\n                <b>emit<\/b> valueChanged(++k);\r\n                i = 0;\r\n         }\r\n        i++;\r\n\r\n    }\r\n    <b>emit<\/b> valueChanged(++k);\r\n    <b>emit<\/b> done();\r\n}\r\n\r\nvoid WAIT::undo()\r\n{\r\n\r\n}\r\n<\/pre>\n<p>Dans notre &#8220;doCommand&#8221;, nous calculons le pas de la notification de l&#8217;application principale. Ici, j&#8217;ai arbitrairement choisi 10 mais dans un contexte utile, le pas est \u00e9gal \u00e0 la taille de vos donn\u00e9es \u00e0 traiter divis\u00e9 par le nombre de notification que vous voulez.<br \/>\nNous \u00e9mettons la valeur maximale. Initialisation des variables temporaires (i et k). On \u00e9met i (0). Nous faisons une bouble sur chaque \u00e9l\u00e9ment \u00e0 traiter, on fait le traitement et on calcule un peu pour savoir s&#8217;il faut ou pas pr\u00e9venir l&#8217;application principale.<br \/>\nIl est bon de ne pas pr\u00e9venir \u00e0 chaque fois, car si vous travaillez sur plusieurs milliers ou millions de donn\u00e9es le traitement des signaux ralentira un peu votre application. Comme exemple, imaginez que vous travaillez sur chaque pixel d&#8217;une grosse photo.<\/p>\n<p>Il ne reste plus qu&#8217;a \u00e9crire la fen\u00eatre principale qui affichera \u00e0 l&#8217;utisateur l&#8217;avanc\u00e9e de notre traitement.<\/p>\n<h2>QProgressBar et QThread.<\/h2>\n<pre class=\"code\">#ifndef MAINWINDOW_H\r\n#define MAINWINDOW_H\r\n\r\n#include &lt;QtGui\/QMainWindow&gt;\r\n#include &lt;QProgressBar&gt;\r\n#include &lt;QDockWidget&gt;\r\n#include \"thread.h\"\r\n<b>namespace<\/b> Ui\r\n{\r\n    <b>class<\/b> MainWindow;\r\n}\r\n\r\n<b>class<\/b> MainWindow : <b>public<\/b> QMainWindow\r\n{\r\n    <b>Q_OBJECT<\/b>\r\n\r\n<b>public<\/b>:\r\n    MainWindow(QWidget *parent = 0);\r\n    ~MainWindow();\r\n\r\n<b>private<\/b>:\r\n    Ui::MainWindow *ui;\r\n    QProgressBar *workinprogress;\r\n    QDockWidget *Progressdock;\r\n    Thread* myThread;\r\n};\r\n\r\n#endif <i>\/\/ MAINWINDOW_H<\/i>\r\n\r\n<\/pre>\n<p>Rien d&#8217;original, une mainwindow avec juste trois membres. Une QProgressBar qui affichera la progression.<br \/>\nUn DockWidget pour afficher la progesse bar, je ne l&#8217;ai pas impl\u00e9ment\u00e9 dans cet exemple mais il peut \u00eatre amusant d&#8217;afficher le dock quand une op\u00e9ration est en cours et la cacher quand c&#8217;est fini. Le dernier membre est une instance de notre classe Thread.<\/p>\n<pre class=\"code\">#include \"mainwindow.h\"\r\n#include \"ui_mainwindow.h\"\r\n#include \"wait.h\"\r\n\r\n\r\n\r\nMainWindow::MainWindow(QWidget *parent)\r\n    : QMainWindow(parent), ui(<b>new<\/b> Ui::MainWindow)\r\n{\r\n    ui-&gt;setupUi(<b>this<\/b>);\r\n    workinprogress = <b>new<\/b> QProgressBar;\r\n    Progressdock = <b>new<\/b> QDockWidget(tr(\"progress panel\"));\r\n    workinprogress-&gt;setValue(0);\r\n    Progressdock-&gt;setWidget(workinprogress);\r\n    Progressdock-&gt;setAllowedAreas(Qt::BottomDockWidgetArea);\r\n    addDockWidget(Qt::BottomDockWidgetArea,Progressdock);\r\n    WAIT* mywait = <b>new<\/b> WAIT();\r\n\r\n    myThread = <b>new<\/b> Thread(mywait,<b>false<\/b>);\r\n    <b>connect<\/b>(mywait,<b>SIGNAL<\/b>(Maximum(int)),workinprogress,<b>SLOT<\/b>(setMaximum(int)));\r\n    <b>connect<\/b>(mywait,<b>SIGNAL<\/b>(Minimum(int)),workinprogress,<b>SLOT<\/b>(setMinimum(int)));\r\n    <b>connect<\/b>(mywait,<b>SIGNAL<\/b>(valueChanged(int)),workinprogress,<b>SLOT<\/b>(setValue(int)));\r\n\r\n    <b>connect<\/b>(ui-&gt;pushButton,<b>SIGNAL<\/b>(clicked()),myThread,<b>SLOT<\/b>(start()));\r\n\r\n}\r\n\r\nMainWindow::~MainWindow()\r\n{\r\n    <b>delete<\/b> ui;\r\n}\r\n<\/pre>\n<p>Nous initialisons la QProgressbar et le QDockwidget. En suite, je param\u00e8tre un peu nos deux instances. Je cr\u00e9e alors une instance de WAIT et j&#8217;initialise le thread avec en param\u00e8tre l&#8217;adresse de notre commande.<br \/>\nJe connecte maintenant les signaux de la commande au slot de la QProgressBar.<br \/>\nFinalement, je connecte le clique sur &#8220;pushButton&#8221; sur le d\u00e9marrage du Thread.<br \/>\nAinsi, \u00e0 chaque clique, il demarrera la commande wait.<\/p>\n<h2>Aller plus loin<\/h2>\n<p>Il serait pr\u00e9f\u00e9rable de cr\u00e9er une instance de WAIT \u00e0 chaque clique et de l&#8217;ajouter dans une pile (dans la mainwindow). Cela est n\u00e9cessaire pour finir le design pattern command. En haut de la pile, se trouve la derni\u00e8re commande, si elle est annul\u00e9e alors il faut la d\u00e9piler et ex\u00e9cuter undo().<\/p>\n<p>Je proposerai en t\u00e9l\u00e9chargement ce petit exemple, d&#8217;ici quelques jours. En attendant, une petite capture d&#8217;\u00e9cran. <a title=\"Capture d'\u00e9cran Design Pattern Command, QThread et Qt.\" href=\"http:\/\/renaudguezennec.eu\/prog\/tutorial\/11\/Capture-3.png\" rel=\"lightbox\"> <img decoding=\"async\" class=\"title\" src=\"http:\/\/renaudguezennec.eu\/prog\/tutorial\/11\/Capture-3.png\" alt=\"Capture d'\u00e9cran Design Pattern Command, QThread et Qt.\" \/> <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bonjour,\u00a0 ce petit tutorial va tenter de vous expliquer comment r\u00e9aliser une op\u00e9ration longue tout en restant r\u00e9actif dans une application Qt4. Il existe plusieurs moyens mais je ne vais en d\u00e9crire qu&#8217;un seul qui \u00e0 mon sens est le plus propre. Comme cas pratique, j&#8217;ai choisi d&#8217;impl\u00e9menter le Design patterns &#8220;Command&#8221;. La premi\u00e8re \u00e9tape [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[80,23],"tags":[9,12,10,11],"class_list":["post-15","post","type-post","status-publish","format-standard","hentry","category-fr","category-tutorial","tag-c","tag-pattern","tag-qt","tag-qt4"],"_links":{"self":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/15","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/comments?post=15"}],"version-history":[{"count":1,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/15\/revisions"}],"predecessor-version":[{"id":16,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/15\/revisions\/16"}],"wp:attachment":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/media?parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/categories?post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/tags?post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}