Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
6a4f56ea680aa6078de204e716fd5ff85a458d17
[graphlib.git] / DrawingWindow.cpp
1 #include "DrawingWindow.h"
2 #include <QApplication>
3 #include <QPaintEvent>
4 #include <QThread>
5 #include <QTimerEvent>
6
7 /*! Classe de thread.
8  */
9 class DrawingThread: public QThread {
10 public:
11     DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f);
12     void start_once(Priority priority = InheritPriority);
13
14 protected:
15     void run();
16
17 private:
18     DrawingWindow &drawingWindow;
19     DrawingWindow::ThreadFunction threadFunction;
20     bool started_once;
21
22     friend class DrawingWindow;
23 };
24
25 enum UserEvents {
26     SyncRequest = QEvent::User, //!< Demande de synchronisation.
27     CloseRequest,               //!< Demande de fermeture de la fenêtre.
28     DrawTextRequest,            //!< Demande d'écriture de texte.
29 };
30
31 /*! Demande de synchronisation.
32  */
33 class SyncRequestEvent: public QEvent {
34 public:
35     SyncRequestEvent(): QEvent(static_cast<QEvent::Type>(SyncRequest))
36     { }
37 };
38
39 /*! Demande de fermeture de fenêtre.
40  */
41 class CloseRequestEvent: public QEvent {
42 public:
43     CloseRequestEvent(): QEvent(static_cast<QEvent::Type>(CloseRequest))
44     { }
45 };
46
47 /*! Demande de tracé de texte. 
48  */
49 class DrawTextEvent: public QEvent {
50 public:
51     const int x;
52     const int y;
53     const char *text;
54     const int flags;
55     DrawTextEvent(int x_, int y_, const char* text_, int flags_)
56         : QEvent(static_cast<QEvent::Type>(DrawTextRequest))
57         , x(x_), y(y_), text(text_), flags(flags_)
58     { }
59 };
60
61 //--- DrawingWindow ----------------------------------------------------
62
63 /*! Constructeur.
64  *
65  * Construit une nouvelle fenêtre de dessin avec les dimensions
66  * passées en paramètres.  La fonction fun sera exécutée dans un
67  * nouveau thread.
68  *
69  * \param fun           fonction de dessin
70  * \param width_        largeur de la fenêtre
71  * \param height_       hauteur de la fenêtre
72  *
73  * \see QWidget
74  */
75 DrawingWindow::DrawingWindow(ThreadFunction fun, int width_, int height_)
76     : QWidget()
77     , width(width_)
78     , height(height_)
79 {
80     initialize(fun);
81 }
82
83 /*! Constructeur.
84  *
85  * Construit un nouveau widget de dessin avec les dimensions passées
86  * en paramètres.  La fonction fun sera exécutée dans un nouveau
87  * thread.
88  *
89  * \param parent        widget parent
90  * \param fun           fonction de dessin
91  * \param width_        largeur de la fenêtre
92  * \param height_       hauteur de la fenêtre
93  *
94  * \see QWidget
95  */
96 DrawingWindow::DrawingWindow(QWidget *parent,
97                              ThreadFunction fun, int width_, int height_)
98     : QWidget(parent)
99     , width(width_)
100     , height(height_)
101 {
102     initialize(fun);
103 }
104
105 /*! Constructeur.
106  *
107  * Construit un nouveau widget de dessin avec les dimensions passées
108  * en paramètres.  La fonction fun sera exécutée dans un nouveau
109  * thread.
110  *
111  * \param parent        widget parent
112  * \param flags         flags passés au constructeur de QWidget
113  * \param fun           fonction de dessin
114  * \param width_        largeur de la fenêtre
115  * \param height_       hauteur de la fenêtre
116  *
117  * \see QWidget
118  */
119 DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags,
120                              ThreadFunction fun, int width_, int height_)
121     : QWidget(parent, flags)
122     , width(width_)
123     , height(height_)
124 {
125     initialize(fun);
126 }
127
128 /*! Destructeur.
129  */
130 DrawingWindow::~DrawingWindow()
131 {
132     delete thread;
133     delete painter;
134     delete image;
135 }
136
137 /*! Change la couleur de dessin.
138  *
139  * La couleur est un entier, tel que retourné par getPointColor.
140  * Normalement de la forme #00RRGGBB.
141  *
142  * \param color         couleur
143  *
144  * \see setColor(const char *), setColor(float, float, float),
145  *      setBgColor(unsigned int),
146  *      getPointColor
147  */
148 void DrawingWindow::setColor(unsigned int color)
149 {
150     setColor(QColor::fromRgb(color));
151 }
152
153 /*! Change la couleur de dessin.
154  *
155  * Le nom de couleur est de la forme "black", "white", "red", "blue", ...
156  *
157  * \param name          nom de couleur
158  *
159  * \see setColor(unsigned int), setColor(float, float, float),
160  *      setBgColor(const char *)
161  * \see http://www.w3.org/TR/SVG/types.html#ColorKeywords
162  */
163 void DrawingWindow::setColor(const char *name)
164 {
165     setColor(QColor(name));
166 }
167
168 /*! Change la couleur de dessin.
169  *
170  * Les composantes de rouge, vert et bleu de la couleur doivent être
171  * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
172  * du noir; si les trois composantes sont à 1, on obtient du blanc.
173  *
174  * \param red           composante de rouge
175  * \param green         composante de vert
176  * \param blue          composante de bleu
177  *
178  * \see setColor(unsigned int), setColor(const char *),
179  *      setBgColor(float, float, float)
180  */
181 void DrawingWindow::setColor(float red, float green, float blue)
182 {
183     setColor(QColor::fromRgbF(red, green, blue));
184 }
185
186 /*! Change la couleur de fond.
187  *
188  * \param color         couleur
189  *
190  * \see setBgColor(const char *), setBgColor(float, float, float),
191  *      setColor(unsigned int),
192  *      getPointColor,
193  *      clearGraph
194  */
195 void DrawingWindow::setBgColor(unsigned int color)
196 {
197     setBgColor(QColor::fromRgb(color));
198 }
199
200 /*! Change la couleur de fond.
201  *
202  * \param name          nom de couleur
203  *
204  * \see setBgColor(unsigned int), setBgColor(float, float, float),
205  *      setColor(const char *),
206  *      clearGraph
207  * \see http://www.w3.org/TR/SVG/types.html#ColorKeywords
208  */
209 void DrawingWindow::setBgColor(const char *name)
210 {
211     setBgColor(QColor(name));
212 }
213
214 /*! Change la couleur de fond.
215  *
216  * \param red           composante de rouge
217  * \param green         composante de vert
218  * \param blue          composante de bleu
219  *
220  * \see setBgColor(unsigned int), setBgColor(const char *),
221  *      setColor(float, float, float),
222  *      clearGraph
223  */
224 void DrawingWindow::setBgColor(float red, float green, float blue)
225 {
226     setBgColor(QColor::fromRgbF(red, green, blue));
227 }
228
229 /*! Efface la fenêtre.
230  *
231  * La fenêtre est effacée avec la couleur de fond courante.
232  *
233  * \see setBgColor
234  */
235 void DrawingWindow::clearGraph()
236 {
237     safeLock(imageMutex);
238     painter->fillRect(image->rect(), getBgColor());
239     dirty();
240     safeUnlock(imageMutex);
241 }
242
243 /*! Dessine un point.
244  *
245  * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
246  * dessin courante.
247  *
248  * \param x, y          coordonnées du point
249  *
250  * \see setColor
251  */
252 void DrawingWindow::drawPoint(int x, int y)
253 {
254     safeLock(imageMutex);
255     painter->drawPoint(x, y);
256     dirty(x, y);
257     safeUnlock(imageMutex);
258 }
259
260 /*! Dessine un segment.
261  *
262  * Dessine un segement de droite entre les coordonnées (x1, y1) et
263  * (x2, y2), avec la couleur de dessin courante.
264  *
265  * \param x1, y1        coordonnées d'une extrémité du segment
266  * \param x2, y2        coordonnées de l'autre extrémité du segment
267  *
268  * \see setColor
269  */
270 void DrawingWindow::drawLine(int x1, int y1, int x2, int y2)
271 {
272     safeLock(imageMutex);
273     painter->drawLine(x1, y1, x2, y2);
274     dirty(x1, y1, x2, y2);
275     safeUnlock(imageMutex);
276 }
277
278 /*! Dessine un rectangle.
279  *
280  * Dessine le rectangle parallèle aux axes et défini par les
281  * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
282  * la couleur de dessin courante.
283  *
284  * \param x1, y1        coordonnées d'un sommet du rectangle
285  * \param x2, y2        coordonnées du sommet opposé du rectangle
286  *
287  * \see fillRect, setColor
288  */
289 void DrawingWindow::drawRect(int x1, int y1, int x2, int y2)
290 {
291     QRect r;
292     r.setCoords(x1, y1, x2 - 1, y2 - 1);
293     r = r.normalized();
294     safeLock(imageMutex);
295     painter->drawRect(r);
296     r.adjust(0, 0, 1, 1);
297     dirty(r);
298     safeUnlock(imageMutex);
299 }
300
301 /*! Dessine un rectangle plein.
302  *
303  * Dessine le rectangle plein parallèle aux axes et défini par les
304  * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
305  * la couleur de dessin courante.
306  *
307  * \param x1, y1        coordonnées d'un sommet du rectangle
308  * \param x2, y2        coordonnées du sommet opposé du rectangle
309  *
310  * \see drawRect, setColor
311  */
312 void DrawingWindow::fillRect(int x1, int y1, int x2, int y2)
313 {
314     painter->setBrush(getColor());
315     drawRect(x1, y1, x2, y2);
316     painter->setBrush(Qt::NoBrush);
317 }
318
319 /*! Dessine un cercle.
320  *
321  * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
322  * couleur de dessin courante.
323  *
324  * \param x, y          coordonnées du centre du cercle
325  * \param r             rayon du cercle
326  *
327  * \see fillCircle, setColor
328  */
329 void DrawingWindow::drawCircle(int x, int y, int r)
330 {
331     QRect rect;
332     rect.setCoords(x - r, y - r, x + r - 1, y + r - 1);
333     safeLock(imageMutex);
334     painter->drawEllipse(rect);
335     rect.adjust(0, 0, 1, 1);
336     dirty(rect);
337     safeUnlock(imageMutex);
338 }
339
340 /*! Dessine un disque.
341  *
342  * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
343  * Utilise la couleur de dessin courante.
344  *
345  * \param x, y          coordonnées du centre du disque
346  * \param r             rayon du disque
347  *
348  * \see drawCircle, setColor
349  */
350 void DrawingWindow::fillCircle(int x, int y, int r)
351 {
352     painter->setBrush(getColor());
353     drawCircle(x, y, r);
354     painter->setBrush(Qt::NoBrush);
355 }
356
357 /*! Écrit du texte.
358  *
359  * Écrit le texte text, aux coordonnées (x, y) et avec les paramètres
360  * d'alignement flags.  Le texte est écrit avec la couleur de dessin
361  * courante.  Les flags sont une combinaison (ou binaire) de
362  * Qt::AlignLeft, Qt::AligneRight, Qt::AlignHCenter, Qt::AlignTop,
363  * Qt::AlignBottom, Qt::AlignVCenter, Qt::AlignCenter.  Par défaut, le
364  * texte est aligné en haut à gauche.
365  *
366  * \param x, y          coordonnées du texte
367  * \param text          texte à écrire
368  * \param flags         paramètres d'alignement
369  *
370  * \see drawTextBg, setColor
371  * \see QPainter::drawText
372  */
373 void DrawingWindow::drawText(int x, int y, const char *text, int flags)
374 {
375     safeLock(syncMutex);
376     if (!terminateThread) {
377         qApp->postEvent(this, new DrawTextEvent(x, y, text, flags));
378         syncCondition.wait(&syncMutex);
379     }
380     safeUnlock(syncMutex);
381 }
382
383 /*! Écrit du texte sur fond coloré.
384  *
385  * Écrit du texte comme drawText, mais l'arrière-plan est coloré avec
386  * la couleur de fond courante.
387  *
388  * \param x, y          coordonnées du texte
389  * \param text          texte à écrire
390  * \param flags         paramètres d'alignement
391  *
392  * \see drawText, setColor, setColorBg
393  */
394 void DrawingWindow::drawTextBg(int x, int y, const char *text, int flags)
395 {
396     painter->setBackgroundMode(Qt::OpaqueMode);
397     drawText(x, y, text, flags);
398     painter->setBackgroundMode(Qt::TransparentMode);
399 }
400
401
402 /*! Retourne la couleur d'un pixel.
403  *
404  * Retourne la couleur du pixel de coordonnées (x, y).  La valeur
405  * retournée peut servir de paramètres à setColor(unsigned int) ou
406  * setBgColor(unsigned int).
407  *
408  * \param x, y          coordonnées du pixel
409  * \return              couleur du pixel
410  *
411  * \see setColor(unsigned int), setBgColor(unsigned int)
412  */
413 unsigned int DrawingWindow::getPointColor(int x, int y)
414 {
415     return image->pixel(x, y);
416 }
417
418 /*! Synchronise le contenu de la fenêtre.
419  *
420  * Pour des raisons d'efficacités, le résultat des fonctions de dessin
421  * n'est pas affiché immédiatement.  L'appel à sync permet de
422  * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
423  * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
424  * soit à jour.
425  *
426  * \param time          durée maximale de l'attente
427  * \return              true si la fenêtre a pu être synchronisée
428  */
429 bool DrawingWindow::sync(unsigned long time)
430 {
431     bool synced;
432     safeLock(syncMutex);
433     if (terminateThread) {
434         synced = false;
435     } else {
436         qApp->postEvent(this, new SyncRequestEvent());
437         synced = syncCondition.wait(&syncMutex, time);
438     }
439     safeUnlock(syncMutex);
440     return synced;
441 }
442
443 /*! Ferme la fenêtre graphique.
444  */
445 void DrawingWindow::closeGraph()
446 {
447     qApp->postEvent(this, new CloseRequestEvent());
448 }
449
450 /*! Suspend l'exécution pendant un certain temps.
451  *
452  * \param secs          temps d'attente en seconde
453  */
454 void DrawingWindow::sleep(unsigned long secs)
455 {
456     DrawingThread::sleep(secs);
457 }
458
459 /*! Suspend l'exécution pendant un certain temps.
460  *
461  * \param msecs          temps d'attente en millisecondes
462  */
463 void DrawingWindow::msleep(unsigned long msecs)
464 {
465     DrawingThread::msleep(msecs);
466 }
467
468 /*! Suspend l'exécution pendant un certain temps.
469  *
470  * \param usecs          temps d'attente en microsecondes
471  */
472 void DrawingWindow::usleep(unsigned long usecs)
473 {
474     DrawingThread::usleep(usecs);
475 }
476
477 /*! \see QWidget
478  */
479 void DrawingWindow::closeEvent(QCloseEvent *ev)
480 {
481     timer.stop();
482     thread->terminate();
483     syncMutex.lock();
484     terminateThread = true;     // this flag is needed for the case
485                                 // where the following wakeAll() call
486                                 // occurs between the
487                                 // setTerminationEnable(false) and the
488                                 // mutex lock in safeLock() called
489                                 // from sync()
490     syncCondition.wakeAll();
491     syncMutex.unlock();
492     QWidget::closeEvent(ev);
493     thread->wait();
494 }
495
496 /*! \see QWidget
497  */
498 void DrawingWindow::customEvent(QEvent *ev)
499 {
500     switch ((int )ev->type()) {
501     case SyncRequest:
502         realSync();
503         break;
504     case CloseRequest:
505         close();
506         break;
507     case DrawTextRequest:
508         DrawTextEvent* tev = dynamic_cast<DrawTextEvent *>(ev);
509         realDrawText(tev->x, tev->y, tev->text, tev->flags);
510         break;
511     }
512 }
513
514 /*! \see QWidget
515  */
516 void DrawingWindow::keyPressEvent(QKeyEvent *ev)
517 {
518     bool accept = true;
519     switch (ev->key()) {
520     case Qt::Key_Escape:
521         close();
522         break;
523     default:
524         accept = false;
525         break;
526     }
527     if (accept)
528         ev->accept();
529 }
530
531 /*! \see QWidget
532  */
533 void DrawingWindow::paintEvent(QPaintEvent *ev)
534 {
535     QPainter widgetPainter(this);
536     imageMutex.lock();
537     QImage imageCopy(*image);
538     imageMutex.unlock();
539     QRect rect = ev->rect();
540     widgetPainter.drawImage(rect, imageCopy, rect);
541 }
542
543 /*! \see QWidget
544  */
545 void DrawingWindow::showEvent(QShowEvent *ev)
546 {
547     QWidget::showEvent(ev);
548     qApp->flush();
549     qApp->syncX();
550     timer.start(paintInterval, this);
551     thread->start_once(QThread::IdlePriority);
552 }
553
554 /*! \see QWidget
555  */
556 void DrawingWindow::timerEvent(QTimerEvent *ev)
557 {
558     if (ev->timerId() == timer.timerId()) {
559         mayUpdate();
560         timer.start(paintInterval, this);
561     } else {
562         QWidget::timerEvent(ev);
563     }
564 }
565
566 //--- DrawingWindow (private methods) ----------------------------------
567
568 /*! Fonction d'initialisation.
569  *
570  * Fonction appelée par les différents constructeurs.
571  *
572  * \param fun           fonction de dessin
573  */
574 void DrawingWindow::initialize(DrawingWindow::ThreadFunction fun)
575 {
576     terminateThread = false;
577     lockCount = 0;
578     image = new QImage(width, height, QImage::Format_RGB32);
579     painter = new QPainter(image);
580     thread = new DrawingThread(*this, fun);
581
582     setFocusPolicy(Qt::StrongFocus);
583     setFixedSize(image->size());
584     setAttribute(Qt::WA_OpaquePaintEvent);
585     setFocus();
586
587     setColor("black");
588     setBgColor("white");
589     clearGraph();
590
591     dirtyFlag = false;
592 }
593
594 /*! Change la couleur de dessin.
595  *
596  * \param color                 couleur
597  */
598 inline
599 void DrawingWindow::setColor(const QColor& color)
600 {
601     QPen pen(painter->pen());
602     pen.setColor(color);
603     painter->setPen(pen);
604 }
605
606 /*! Change la couleur de fond.
607  *
608  * \param color                 couleur
609  */
610 inline
611 void DrawingWindow::setBgColor(const QColor& color)
612 {
613     painter->setBackground(color);
614 }
615
616 /*! Retourne la couleur de dessin courante.
617  *
618  * \return              couleur de dessin courante
619  */
620 inline
621 QColor DrawingWindow::getColor()
622 {
623     return painter->pen().color();
624 }
625
626 /*! Retourne la couleur de fond courante.
627  *
628  * \return              couleur de fond courante
629  */
630 inline
631 QColor DrawingWindow::getBgColor()
632 {
633     return painter->background().color();
634 }
635
636 /*! Verrouille un mutex.
637  *
638  * S'assure que le thread courant ne peut pas être terminé s'il
639  * détient un mutex.  Pendant de safeUnlock.
640  *
641  * \param mutex         le mutex à verrouiller
642  *
643  * \see safeUnlock
644  */
645 inline
646 void DrawingWindow::safeLock(QMutex &mutex)
647 {
648     if (lockCount++ == 0)
649         thread->setTerminationEnabled(false);
650     mutex.lock();
651 }
652
653 /*! Déverrouille un mutex.
654  *
655  * S'assure que le thread courant ne peut pas être terminé s'il
656  * détient un mutex.  Pendant de safeLock.
657  *
658  * \param mutex         le mutex à déverrouiller
659  *
660  * \see safeLock
661  */
662 inline
663 void DrawingWindow::safeUnlock(QMutex &mutex)
664 {
665     mutex.unlock();
666     if (--lockCount == 0)
667         thread->setTerminationEnabled(true);
668 }
669
670 /*! Marque l'image entière comme non à jour.
671  */
672 inline
673 void DrawingWindow::dirty()
674 {
675     dirtyFlag = true;
676     dirtyRect = image->rect();
677 }
678
679 /*! Marque un point de l'image comme non à jour.
680  *
681  * \param x, y          coordonnées du point
682  */
683 inline
684 void DrawingWindow::dirty(int x, int y)
685 {
686     dirty(QRect(x, y, 1, 1));
687 }
688
689 /*! Marque une zone de l'image comme non à jour.
690  *
691  * La zone est définie par un rectangle dont les coordonnées de deux
692  * sommets oppposés sont données.
693  *
694  * \param x1, y1        coordonnées d'un sommet du rectangle
695  * \param x2, y2        coordonnées du sommet opposé du rectangle
696  */
697 inline
698 void DrawingWindow::dirty(int x1, int y1, int x2, int y2)
699 {
700     QRect r;
701     r.setCoords(x1, y1, x2, y2);
702     dirty(r.normalized());
703 }
704
705 /*! Marque une zone de l'image comme non à jour.
706  *
707  * \param rect          rectangle délimitant la zone
708  */
709 void DrawingWindow::dirty(const QRect &rect)
710 {
711     if (dirtyFlag) {
712         dirtyRect |= rect;
713     } else {
714         dirtyFlag = true;
715         dirtyRect = rect;
716     }
717 }
718
719 /*! Génère un update si besoin.
720  *
721  * Génère une demande de mise à jour de la fenêtre (appel à update)
722  * s'il y en a besoin.
723  */
724 void DrawingWindow::mayUpdate()
725 {
726     imageMutex.lock();
727     bool dirty = dirtyFlag;
728     QRect rect = dirtyRect;
729     dirtyFlag = false;
730     imageMutex.unlock();
731     if (dirty)
732         update(rect);
733 }
734
735 /*! Fonction bas-niveau pour sync.
736  *
737  * Fonction de synchronisation dans le thread principal.
738  *
739  * \see sync, customEvent
740  */
741 void DrawingWindow::realSync()
742 {
743     mayUpdate();
744     qApp->sendPostedEvents(this, QEvent::UpdateLater);
745     qApp->sendPostedEvents(this, QEvent::UpdateRequest);
746     qApp->sendPostedEvents(this, QEvent::Paint);
747     qApp->processEvents(QEventLoop::ExcludeUserInputEvents |
748                         QEventLoop::ExcludeSocketNotifiers |
749                         QEventLoop::DeferredDeletion |
750                         QEventLoop::X11ExcludeTimers);
751     qApp->flush();
752     qApp->syncX();
753     syncMutex.lock();
754     syncCondition.wakeAll();
755     syncMutex.unlock();
756 }
757
758 /*! Fonction bas-niveau pour drawText.
759  *
760  * Le rendu de texte doit être fait dans le thread principal.  D'où
761  * les manipulations tordues et la synchronisation qui s'en suit.
762  *
763  * \param x, y, text, flags     cf. drawText
764  *
765  * \see drawText, customEvent
766  */
767 void DrawingWindow::realDrawText(int x, int y, const char *text, int flags)
768 {
769     QRect r(image->rect());
770     switch (flags & Qt::AlignHorizontal_Mask) {
771     case Qt::AlignRight:
772         r.setRight(x);
773         break;
774     case Qt::AlignHCenter:
775         if (x < width / 2)
776             r.setLeft(2 * x - width + 1);
777         else
778             r.setRight(2 * x);
779         break;
780     default:
781         r.setLeft(x);
782     }
783     switch (flags & Qt::AlignVertical_Mask) {
784     case Qt::AlignBottom:
785         r.setBottom(y);
786         break;
787     case Qt::AlignVCenter:
788         if (y < height / 2)
789             r.setTop(2 * y - height + 1);
790         else
791             r.setBottom(2 * y);
792         break;
793     default:
794         r.setTop(y);
795     }
796     syncMutex.lock();
797     painter->drawText(r, flags, text, &r);
798     dirty(r);
799     syncCondition.wakeAll();
800     syncMutex.unlock();
801 }
802
803 //--- DrawingThread ----------------------------------------------------
804
805 /*! Constructeur.
806  */
807 DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f)
808     : drawingWindow(w)
809     , threadFunction(f)
810     , started_once(false)
811 {
812 }
813
814 /*! Démarre le thread si ce n'a pas encore été fait.
815  */
816 void DrawingThread::start_once(Priority priority)
817 {
818     if (!started_once) {
819         started_once = true;
820         start(priority);
821     }
822 }
823
824 /*! La vraie fonction pour le thread.
825  */
826 void DrawingThread::run()
827 {
828     threadFunction(drawingWindow);
829 }