X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/graphlib.git/blobdiff_plain/a7346f12e37f808491a76b2465de8ae8cd951d51..86bb6428e4aeab4b28f5b88809dd642c7c53fb36:/DrawingWindow.cpp?ds=sidebyside diff --git a/DrawingWindow.cpp b/DrawingWindow.cpp index c68b7b8..0cd3633 100644 --- a/DrawingWindow.cpp +++ b/DrawingWindow.cpp @@ -1,10 +1,111 @@ +/* + * Copyright (c) 2007, Arnaud Giersch + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include "DrawingWindow.h" #include #include -#include #include #include +/*! \class DrawingWindow + * \brief Fenêtre de dessin. + * + * \author Arnaud Giersch + * \date novembre 2007 + * + * Cette classe décrit un widget Qt permettant d'écrire des + * applications graphiques simples. Pour cela, il faut définir une + * fonction de dessin. Cette fonction ne retourne rien et prend comme + * unique paramètre une référence vers un objet de class + * DrawingWindow. + * + * La fonction devra ensuite être passée en paramètre pour les + * constructeurs de la classe, ainsi que les dimension requises pour + * la fenêtre graphique. Le programme est ensuite compilé comme + * n'importe quel programme Qt. + * + * Concrètement, la fonction sera exécutée dans un nouveau thread, + * tandis que le thread principal s'occupera de la gestion des + * évènements et du rendu dans la fenêtre. + * + * NB. Pour toutes les méthodes de dessin, le coin en haut à gauche + * de la fenêtre a les coordonnées (0, 0). Le coin en bas à droite de + * la fenêtre a les coordonnées (largeur - 1, hauteur - 1), si la + * fenêtre est de dimension largeur × hauteur. + * + * Un appui sur la touche provoque la fermeture de la fenêtre. + * Comme pour la plupart des applications, il est également possible + * de fermer la fenêtre via le gestionnaire de fenêtres. + * + * Il est possible, dans une application, d'ouvrir plusieurs fenêtres, + * avec des fonctions de dessin éventuellement différentes. + * L'application se terminera normalement lorsque la dernière fenêtre + * sera fermée. + */ + +/*! \example hello.cpp + * + * Voir le code source à la fin de la page. Pour compiler et exécuter + * ce programme, il faut : + * + * 1. Créer le fichier \c hello.pro + * + * Pour simplifier, ce fichier contient la liste des fichiers sources + * composant le programme. + * + * \include hello.pro + * + * 2. Créer le fichier \c Makefile avec la commande : + * + * \verbatim qmake-qt4 hello.pro \endverbatim + * ou tout simplement : + * \verbatim qmake-qt4 \endverbatim + * + * 3. Compiler le programme avec la commande : + * + * \verbatim make hello \endverbatim + * ou tout simplement : + * \verbatim make \endverbatim + * + * 4. Exécuter le programme avec la commande : + * + * \verbatim ./exemple \endverbatim + * + * Code source de l'exemple + */ + +/*! \example exemple.cpp + * + * Un exemple un peu plus sophistiqué. + */ + +//! Classe de thread. class DrawingThread: public QThread { public: DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f); @@ -21,34 +122,130 @@ private: friend class DrawingWindow; }; +enum UserEvents { + SyncRequest = QEvent::User, //!< Demande de synchronisation. + CloseRequest, //!< Demande de fermeture de la fenêtre. + DrawTextRequest, //!< Demande d'écriture de texte. +}; + +//! Demande de synchronisation. +class SyncRequestEvent: public QEvent { +public: + SyncRequestEvent(): QEvent(static_cast(SyncRequest)) + { } +}; + +//! Demande de fermeture de fenêtre. +class CloseRequestEvent: public QEvent { +public: + CloseRequestEvent(): QEvent(static_cast(CloseRequest)) + { } +}; + +//! Demande de tracé de texte. +class DrawTextEvent: public QEvent { +public: + const int x; + const int y; + const char *text; + const int flags; + DrawTextEvent(int x_, int y_, const char* text_, int flags_) + : QEvent(static_cast(DrawTextRequest)) + , x(x_), y(y_), text(text_), flags(flags_) + { } +}; + //--- DrawingWindow ---------------------------------------------------- -DrawingWindow::DrawingWindow(ThreadFunction f, int w, int h) +/*! \file DrawingWindow.h + * \brief Classe DrawingWindow. + */ + +/*! \typedef DrawingWindow::ThreadFunction + * \brief Type de la fonction de dessin, passée en paramètre de construction. + */ +/*! \var DrawingWindow::DEFAULT_WIDTH + * \brief Largeur par défaut de la fenêtre. + */ +/*! \var DrawingWindow::DEFAULT_HEIGHT + * \brief Hauteur par défaut de la fenêtre. + */ +/*! \var DrawingWindow::width + * \brief Largeur de la fenêtre. + */ +/*! \var DrawingWindow::height + * \brief Hauteur de la fenêtre. + */ +/*! \var DrawingWindow::paintInterval + * \brief Intervalle de temps entre deux rendus (ms). + */ + +//! Constructeur. +/*! + * Construit une nouvelle fenêtre de dessin avec les dimensions + * passées en paramètres. La fonction fun sera exécutée dans un + * nouveau thread. + * + * \param fun fonction de dessin + * \param width_ largeur de la fenêtre + * \param height_ hauteur de la fenêtre + * + * \see QWidget + */ +DrawingWindow::DrawingWindow(ThreadFunction fun, int width_, int height_) : QWidget() - , width(w) - , height(h) -{ - initialize(f); -} - + , width(width_) + , height(height_) +{ + initialize(fun); +} + +//! Constructeur. +/*! + * Construit un nouveau widget de dessin avec les dimensions passées + * en paramètres. La fonction fun sera exécutée dans un nouveau + * thread. + * + * \param parent widget parent + * \param fun fonction de dessin + * \param width_ largeur de la fenêtre + * \param height_ hauteur de la fenêtre + * + * \see QWidget + */ DrawingWindow::DrawingWindow(QWidget *parent, - ThreadFunction f, int w, int h) + ThreadFunction fun, int width_, int height_) : QWidget(parent) - , width(w) - , height(h) -{ - initialize(f); -} - + , width(width_) + , height(height_) +{ + initialize(fun); +} + +//! Constructeur. +/*! + * Construit un nouveau widget de dessin avec les dimensions passées + * en paramètres. La fonction fun sera exécutée dans un nouveau + * thread. + * + * \param parent widget parent + * \param flags flags passés au constructeur de QWidget + * \param fun fonction de dessin + * \param width_ largeur de la fenêtre + * \param height_ hauteur de la fenêtre + * + * \see QWidget + */ DrawingWindow::DrawingWindow(QWidget *parent, Qt::WindowFlags flags, - ThreadFunction f, int w, int h) + ThreadFunction fun, int width_, int height_) : QWidget(parent, flags) - , width(w) - , height(h) + , width(width_) + , height(height_) { - initialize(f); + initialize(fun); } +//! Destructeur. DrawingWindow::~DrawingWindow() { delete thread; @@ -56,36 +253,121 @@ DrawingWindow::~DrawingWindow() delete image; } +//! Change la couleur de dessin. +/*! + * La couleur est un entier, tel que retourné par getPointColor. + * Normalement de la forme #00RRGGBB. + * + * \param color couleur + * + * \see setColor(const char *), setColor(float, float, float), + * setBgColor(unsigned int), + * getPointColor + */ +void DrawingWindow::setColor(unsigned int color) +{ + setColor(QColor::fromRgb(color)); +} + +//! Change la couleur de dessin. +/*! + * Le nom de couleur est de la forme "black", "white", "red", "blue", ... + * + * \param name nom de couleur + * + * \see setColor(unsigned int), setColor(float, float, float), + * setBgColor(const char *) + * \see http://www.w3.org/TR/SVG/types.html#ColorKeywords + */ +void DrawingWindow::setColor(const char *name) +{ + setColor(QColor(name)); +} + +//! Change la couleur de dessin. +/*! + * Les composantes de rouge, vert et bleu de la couleur doivent être + * compris entre 0 et 1. Si le trois composantes sont à 0, on obtient + * du noir; si les trois composantes sont à 1, on obtient du blanc. + * + * \param red composante de rouge + * \param green composante de vert + * \param blue composante de bleu + * + * \see setColor(unsigned int), setColor(const char *), + * setBgColor(float, float, float) + */ void DrawingWindow::setColor(float red, float green, float blue) { - fgColor.setRgbF(red, green, blue); - applyColor(); + setColor(QColor::fromRgbF(red, green, blue)); } -void DrawingWindow::setColor(const char *name) +//! Change la couleur de fond. +/*! + * \param color couleur + * + * \see setBgColor(const char *), setBgColor(float, float, float), + * setColor(unsigned int), + * getPointColor, + * clearGraph + */ +void DrawingWindow::setBgColor(unsigned int color) { - fgColor.setNamedColor(name); - applyColor(); + setBgColor(QColor::fromRgb(color)); } -void DrawingWindow::setBgColor(float red, float green, float blue) +//! Change la couleur de fond. +/*! + * \param name nom de couleur + * + * \see setBgColor(unsigned int), setBgColor(float, float, float), + * setColor(const char *), + * clearGraph + * \see http://www.w3.org/TR/SVG/types.html#ColorKeywords + */ +void DrawingWindow::setBgColor(const char *name) { - bgColor.setRgbF(red, green, blue); + setBgColor(QColor(name)); } -void DrawingWindow::setBgColor(const char *name) +//! Change la couleur de fond. +/*! + * \param red composante de rouge + * \param green composante de vert + * \param blue composante de bleu + * + * \see setBgColor(unsigned int), setBgColor(const char *), + * setColor(float, float, float), + * clearGraph + */ +void DrawingWindow::setBgColor(float red, float green, float blue) { - bgColor.setNamedColor(name); + setBgColor(QColor::fromRgbF(red, green, blue)); } +//! Efface la fenêtre. +/*! + * La fenêtre est effacée avec la couleur de fond courante. + * + * \see setBgColor + */ void DrawingWindow::clearGraph() { safeLock(imageMutex); - painter->fillRect(image->rect(), bgColor); + painter->fillRect(image->rect(), getBgColor()); dirty(); safeUnlock(imageMutex); } +//! Dessine un point. +/*! + * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de + * dessin courante. + * + * \param x, y coordonnées du point + * + * \see setColor + */ void DrawingWindow::drawPoint(int x, int y) { safeLock(imageMutex); @@ -94,6 +376,16 @@ void DrawingWindow::drawPoint(int x, int y) safeUnlock(imageMutex); } +//! Dessine un segment. +/*! + * Dessine un segement de droite entre les coordonnées (x1, y1) et + * (x2, y2), avec la couleur de dessin courante. + * + * \param x1, y1 coordonnées d'une extrémité du segment + * \param x2, y2 coordonnées de l'autre extrémité du segment + * + * \see setColor + */ void DrawingWindow::drawLine(int x1, int y1, int x2, int y2) { safeLock(imageMutex); @@ -102,6 +394,17 @@ void DrawingWindow::drawLine(int x1, int y1, int x2, int y2) safeUnlock(imageMutex); } +//! Dessine un rectangle. +/*! + * Dessine le rectangle parallèle aux axes et défini par les + * coordonnées de deux sommets opposés (x1, y1) et (x2, y2). Utilise + * la couleur de dessin courante. + * + * \param x1, y1 coordonnées d'un sommet du rectangle + * \param x2, y2 coordonnées du sommet opposé du rectangle + * + * \see fillRect, setColor + */ void DrawingWindow::drawRect(int x1, int y1, int x2, int y2) { QRect r; @@ -114,13 +417,34 @@ void DrawingWindow::drawRect(int x1, int y1, int x2, int y2) safeUnlock(imageMutex); } +//! Dessine un rectangle plein. +/*! + * Dessine le rectangle plein parallèle aux axes et défini par les + * coordonnées de deux sommets opposés (x1, y1) et (x2, y2). Utilise + * la couleur de dessin courante. + * + * \param x1, y1 coordonnées d'un sommet du rectangle + * \param x2, y2 coordonnées du sommet opposé du rectangle + * + * \see drawRect, setColor + */ void DrawingWindow::fillRect(int x1, int y1, int x2, int y2) { - painter->setBrush(fgColor); + painter->setBrush(getColor()); drawRect(x1, y1, x2, y2); painter->setBrush(Qt::NoBrush); } +//! Dessine un cercle. +/*! + * Dessine un cercle de centre (x, y) et de rayon r. Utilise la + * couleur de dessin courante. + * + * \param x, y coordonnées du centre du cercle + * \param r rayon du cercle + * + * \see fillCircle, setColor + */ void DrawingWindow::drawCircle(int x, int y, int r) { QRect rect; @@ -132,23 +456,95 @@ void DrawingWindow::drawCircle(int x, int y, int r) safeUnlock(imageMutex); } +//! Dessine un disque. +/*! + * Dessine un disque (cercle plein) de centre (x, y) et de rayon r. + * Utilise la couleur de dessin courante. + * + * \param x, y coordonnées du centre du disque + * \param r rayon du disque + * + * \see drawCircle, setColor + */ void DrawingWindow::fillCircle(int x, int y, int r) { - painter->setBrush(fgColor); + painter->setBrush(getColor()); drawCircle(x, y, r); painter->setBrush(Qt::NoBrush); } -void DrawingWindow::drawText(int x, int y, const char *text) +//! Écrit du texte. +/*! + * Écrit le texte text, aux coordonnées (x, y) et avec les paramètres + * d'alignement flags. Le texte est écrit avec la couleur de dessin + * courante. Les flags sont une combinaison (ou binaire) de + * Qt::AlignLeft, Qt::AligneRight, Qt::AlignHCenter, Qt::AlignTop, + * Qt::AlignBottom, Qt::AlignVCenter, Qt::AlignCenter. Par défaut, le + * texte est aligné en haut à gauche. + * + * \param x, y coordonnées du texte + * \param text texte à écrire + * \param flags paramètres d'alignement + * + * \see drawTextBg, setColor + * \see QPainter::drawText + */ +void DrawingWindow::drawText(int x, int y, const char *text, int flags) { - QRect r(image->rect()); - r.moveTo(x, y); - safeLock(imageMutex); - painter->drawText(r, 0, text, &r); - dirty(r); - safeUnlock(imageMutex); + safeLock(syncMutex); + if (!terminateThread) { + qApp->postEvent(this, new DrawTextEvent(x, y, text, flags)); + syncCondition.wait(&syncMutex); + } + safeUnlock(syncMutex); } +//! Écrit du texte sur fond coloré. +/*! + * Écrit du texte comme drawText, mais l'arrière-plan est coloré avec + * la couleur de fond courante. + * + * \param x, y coordonnées du texte + * \param text texte à écrire + * \param flags paramètres d'alignement + * + * \see drawText, setColor, setColorBg + */ +void DrawingWindow::drawTextBg(int x, int y, const char *text, int flags) +{ + painter->setBackgroundMode(Qt::OpaqueMode); + drawText(x, y, text, flags); + painter->setBackgroundMode(Qt::TransparentMode); +} + + +//! Retourne la couleur d'un pixel. +/*! + * Retourne la couleur du pixel de coordonnées (x, y). La valeur + * retournée peut servir de paramètres à setColor(unsigned int) ou + * setBgColor(unsigned int). + * + * \param x, y coordonnées du pixel + * \return couleur du pixel + * + * \see setColor(unsigned int), setBgColor(unsigned int) + */ +unsigned int DrawingWindow::getPointColor(int x, int y) +{ + return image->pixel(x, y); +} + +//! Synchronise le contenu de la fenêtre. +/*! + * Pour des raisons d'efficacités, le résultat des fonctions de dessin + * n'est pas affiché immédiatement. L'appel à sync permet de + * synchroniser le contenu de la fenêtre. Autrement dit, cela bloque + * l'exécution du programme jusqu'à ce que le contenu de la fenêtre + * soit à jour. + * + * \param time durée maximale de l'attente + * \return true si la fenêtre a pu être synchronisée + */ bool DrawingWindow::sync(unsigned long time) { bool synced; @@ -156,33 +552,49 @@ bool DrawingWindow::sync(unsigned long time) if (terminateThread) { synced = false; } else { - qApp->postEvent(this, new QEvent(QEvent::User)); + qApp->postEvent(this, new SyncRequestEvent()); synced = syncCondition.wait(&syncMutex, time); } safeUnlock(syncMutex); return synced; } +//! Ferme la fenêtre graphique. void DrawingWindow::closeGraph() { - qApp->postEvent(this, new QEvent(QEvent::Type(QEvent::User + 1))); + qApp->postEvent(this, new CloseRequestEvent()); } +//! Suspend l'exécution pendant un certain temps. +/*! + * \param secs temps d'attente en seconde + */ void DrawingWindow::sleep(unsigned long secs) { DrawingThread::sleep(secs); } +//! Suspend l'exécution pendant un certain temps. +/*! + * \param msecs temps d'attente en millisecondes + */ void DrawingWindow::msleep(unsigned long msecs) { DrawingThread::msleep(msecs); } +//! Suspend l'exécution pendant un certain temps. +/*! + * \param usecs temps d'attente en microsecondes + */ void DrawingWindow::usleep(unsigned long usecs) { DrawingThread::usleep(usecs); } +/*! + * \see QWidget + */ void DrawingWindow::closeEvent(QCloseEvent *ev) { timer.stop(); @@ -200,30 +612,28 @@ void DrawingWindow::closeEvent(QCloseEvent *ev) thread->wait(); } +/*! + * \see QWidget + */ void DrawingWindow::customEvent(QEvent *ev) { switch ((int )ev->type()) { - case QEvent::User: - mayUpdate(); - qApp->sendPostedEvents(this, QEvent::UpdateLater); - qApp->sendPostedEvents(this, QEvent::UpdateRequest); - qApp->sendPostedEvents(this, QEvent::Paint); - qApp->processEvents(QEventLoop::ExcludeUserInputEvents | - QEventLoop::ExcludeSocketNotifiers | - QEventLoop::DeferredDeletion | - QEventLoop::X11ExcludeTimers); - qApp->flush(); - qApp->syncX(); - syncMutex.lock(); - syncCondition.wakeAll(); - syncMutex.unlock(); + case SyncRequest: + realSync(); break; - case QEvent::User + 1: + case CloseRequest: close(); break; + case DrawTextRequest: + DrawTextEvent* tev = dynamic_cast(ev); + realDrawText(tev->x, tev->y, tev->text, tev->flags); + break; } } +/*! + * \see QWidget + */ void DrawingWindow::keyPressEvent(QKeyEvent *ev) { bool accept = true; @@ -239,6 +649,9 @@ void DrawingWindow::keyPressEvent(QKeyEvent *ev) ev->accept(); } +/*! + * \see QWidget + */ void DrawingWindow::paintEvent(QPaintEvent *ev) { QPainter widgetPainter(this); @@ -249,13 +662,21 @@ void DrawingWindow::paintEvent(QPaintEvent *ev) widgetPainter.drawImage(rect, imageCopy, rect); } +/*! + * \see QWidget + */ void DrawingWindow::showEvent(QShowEvent *ev) { + QWidget::showEvent(ev); + qApp->flush(); + qApp->syncX(); timer.start(paintInterval, this); thread->start_once(QThread::IdlePriority); - QWidget::showEvent(ev); } +/*! + * \see QWidget + */ void DrawingWindow::timerEvent(QTimerEvent *ev) { if (ev->timerId() == timer.timerId()) { @@ -268,13 +689,19 @@ void DrawingWindow::timerEvent(QTimerEvent *ev) //--- DrawingWindow (private methods) ---------------------------------- -void DrawingWindow::initialize(DrawingWindow::ThreadFunction f) +//! Fonction d'initialisation. +/*! + * Fonction appelée par les différents constructeurs. + * + * \param fun fonction de dessin + */ +void DrawingWindow::initialize(DrawingWindow::ThreadFunction fun) { terminateThread = false; lockCount = 0; image = new QImage(width, height, QImage::Format_RGB32); painter = new QPainter(image); - thread = new DrawingThread(*this, f); + thread = new DrawingThread(*this, fun); setFocusPolicy(Qt::StrongFocus); setFixedSize(image->size()); @@ -288,14 +715,57 @@ void DrawingWindow::initialize(DrawingWindow::ThreadFunction f) dirtyFlag = false; } +//! Change la couleur de dessin. +/*! + * \param color couleur + */ inline -void DrawingWindow::applyColor() +void DrawingWindow::setColor(const QColor& color) { QPen pen(painter->pen()); - pen.setColor(fgColor); + pen.setColor(color); painter->setPen(pen); } +//! Change la couleur de fond. +/*! + * \param color couleur + */ +inline +void DrawingWindow::setBgColor(const QColor& color) +{ + painter->setBackground(color); +} + +//! Retourne la couleur de dessin courante. +/*! + * \return couleur de dessin courante + */ +inline +QColor DrawingWindow::getColor() +{ + return painter->pen().color(); +} + +//! Retourne la couleur de fond courante. +/*! + * \return couleur de fond courante + */ +inline +QColor DrawingWindow::getBgColor() +{ + return painter->background().color(); +} + +//! Verrouille un mutex. +/*! + * S'assure que le thread courant ne peut pas être terminé s'il + * détient un mutex. Pendant de safeUnlock. + * + * \param mutex le mutex à verrouiller + * + * \see safeUnlock + */ inline void DrawingWindow::safeLock(QMutex &mutex) { @@ -304,6 +774,15 @@ void DrawingWindow::safeLock(QMutex &mutex) mutex.lock(); } +//! Déverrouille un mutex. +/*! + * S'assure que le thread courant ne peut pas être terminé s'il + * détient un mutex. Pendant de safeLock. + * + * \param mutex le mutex à déverrouiller + * + * \see safeLock + */ inline void DrawingWindow::safeUnlock(QMutex &mutex) { @@ -312,6 +791,7 @@ void DrawingWindow::safeUnlock(QMutex &mutex) thread->setTerminationEnabled(true); } +//! Marque l'image entière comme non à jour. inline void DrawingWindow::dirty() { @@ -319,12 +799,24 @@ void DrawingWindow::dirty() dirtyRect = image->rect(); } +//! Marque un point de l'image comme non à jour. +/*! + * \param x, y coordonnées du point + */ inline void DrawingWindow::dirty(int x, int y) { dirty(QRect(x, y, 1, 1)); } +//! Marque une zone de l'image comme non à jour. +/*! + * La zone est définie par un rectangle dont les coordonnées de deux + * sommets oppposés sont données. + * + * \param x1, y1 coordonnées d'un sommet du rectangle + * \param x2, y2 coordonnées du sommet opposé du rectangle + */ inline void DrawingWindow::dirty(int x1, int y1, int x2, int y2) { @@ -333,6 +825,10 @@ void DrawingWindow::dirty(int x1, int y1, int x2, int y2) dirty(r.normalized()); } +//! Marque une zone de l'image comme non à jour. +/*! + * \param rect rectangle délimitant la zone + */ void DrawingWindow::dirty(const QRect &rect) { if (dirtyFlag) { @@ -343,6 +839,11 @@ void DrawingWindow::dirty(const QRect &rect) } } +//! Génère un update si besoin. +/*! + * Génère une demande de mise à jour de la fenêtre (appel à update) + * s'il y en a besoin. + */ void DrawingWindow::mayUpdate() { imageMutex.lock(); @@ -354,8 +855,77 @@ void DrawingWindow::mayUpdate() update(rect); } +//! Fonction bas-niveau pour sync. +/*! + * Fonction de synchronisation dans le thread principal. + * + * \see sync, customEvent + */ +void DrawingWindow::realSync() +{ + mayUpdate(); + qApp->sendPostedEvents(this, QEvent::UpdateLater); + qApp->sendPostedEvents(this, QEvent::UpdateRequest); + qApp->sendPostedEvents(this, QEvent::Paint); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents | + QEventLoop::ExcludeSocketNotifiers | + QEventLoop::DeferredDeletion | + QEventLoop::X11ExcludeTimers); + qApp->flush(); + qApp->syncX(); + syncMutex.lock(); + syncCondition.wakeAll(); + syncMutex.unlock(); +} + +//! Fonction bas-niveau pour drawText. +/*! + * Le rendu de texte doit être fait dans le thread principal. D'où + * les manipulations tordues et la synchronisation qui s'en suit. + * + * \param x, y, text, flags cf. drawText + * + * \see drawText, customEvent + */ +void DrawingWindow::realDrawText(int x, int y, const char *text, int flags) +{ + QRect r(image->rect()); + switch (flags & Qt::AlignHorizontal_Mask) { + case Qt::AlignRight: + r.setRight(x); + break; + case Qt::AlignHCenter: + if (x < width / 2) + r.setLeft(2 * x - width + 1); + else + r.setRight(2 * x); + break; + default: + r.setLeft(x); + } + switch (flags & Qt::AlignVertical_Mask) { + case Qt::AlignBottom: + r.setBottom(y); + break; + case Qt::AlignVCenter: + if (y < height / 2) + r.setTop(2 * y - height + 1); + else + r.setBottom(2 * y); + break; + default: + r.setTop(y); + } + syncMutex.lock(); + painter->drawText(r, flags, text, &r); + dirty(r); + syncCondition.wakeAll(); + syncMutex.unlock(); +} + //--- DrawingThread ---------------------------------------------------- +//! Constructeur. DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f) : drawingWindow(w) , threadFunction(f) @@ -363,6 +933,7 @@ DrawingThread::DrawingThread(DrawingWindow &w, DrawingWindow::ThreadFunction f) { } +//! Démarre le thread si ce n'a pas encore été fait. void DrawingThread::start_once(Priority priority) { if (!started_once) { @@ -371,6 +942,7 @@ void DrawingThread::start_once(Priority priority) } } +//! La vraie fonction pour le thread. void DrawingThread::run() { threadFunction(drawingWindow);