Quand vous appelez une fenêtre modale avec Qt, Windows se charge de faire clignoter la bordure de cette dernière.
Par contre, quand elle n’a PAS de bordure (avec le flag Qt::FramelessWindowHint), rien ne se passe.
Résultat ?
L’utilisateur teste votre logiciel, essaie de faire autre chose après avoir ouvert une fenêtre modale en cliquant dans une autre fenêtre de votre application, et absolument rien ne se passe. Il pense qu’il y a un bug, ferme l’application et ne l’ouvre plus jamais parce qu’il a détesté sa première expérience.
Comment régler ce problème ?
J’ai passé énormément de temps pour une si petite chose, puisque personne n’en parle nulle part sur Internet, la documentation de Qt non plus d’ailleurs.
Voici comment j’ai fait.
Je ne dis pas que c’es la meilleure solution, mais c’est un début vers la bonne solution, mais plus important encore : elle fonctionne.
J’ai d’abord essayé d’installer un eventFilter dans la fenêtre modale, mais absolument aucun évènement n’est appellé quand on clique sur une autre fenêtre … bizarre …
Donc j’ai fait l’inverse : J’ai fait hériter toutes mes fenêtres d’une classe QMainWindow, qui capte l’évènement d’activation de fenêtre (QEvent::WindowActivate), pour ensuite vérifier si l’application a une fenêtre modale active (QApplication::activeModalWidget()). Si c’est le cas, je fais clignoter cette fenêtre modale avec un QTimer.
Oui je sais c’est pas très joli, mais je n’ai rien trouvé d’autre après des heures de tatonnement.
Par contre, pour faire les fenêtres modales, il faut faire attention pour que ce code fonctionne.
- D’abord, il ne faut pas qu’elle aille de parent, sans quoi le parent ne recevra pas d’évènement.
- Ensuite, exec() et setModal(true) ne fonctionneront pas, pas plus que setWindowModality(Qt::ApplicationModal). Vous devez utiliser setWindowModality(Qt::WindowModal), avec show()
- Pour finir, ne pas oublier que toutes vos fenêtre doivent hériter de MyMainWindow, et ne pas oublier d’installer un eventFilter, avec
installEventFilter(this);
dans le constructeur . Si vous l’avez déjà implémenté, n’oubliez pas de faire au moins:bool YourWindow::eventFilter(QObject *o, QEvent *e) { return MyMainWindow::eventFilter(o, e); }
Voici la classe en question :
MyMainWindow::MyMainWindow( QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), nFlash(0) { timer = new QTimer(); timer->setInterval(75); connect(timer, SIGNAL(timeout()), SLOT(flash())); } MyMainWindow::~MyMainWindow() { timer->stop(); delete timer; } void MyMainWindow::flashWindow() { if(nFlash == 0 || nFlash > 5) { nFlash = 1; timer->start(); } } void MyMainWindow::flash() { QWidget* pWidget = QApplication::activeModalWidget(); if (pWidget) { if(!(nFlash % 2)) { pWidget->setWindowOpacity(1); if(nFlash >= 9){ timer->stop(); nFlash = 0; return; } } else pWidget->setWindowOpacity(.85); nFlash++; } else { timer->stop(); nFlash = 0; } } bool MyMainWindow::eventFilter(QObject *o, QEvent *e) { if(e->type() == QEvent::WindowActivate) { QWidget* pWidget = QApplication::activeModalWidget(); if (pWidget) { pWidget->raise(); pWidget->activateWindow(); flashWindow(false); e->accept(); } } return QMainWindow::eventFilter(o, e); }class MyMainWindow : public QMainWindow { Q_OBJECT public: MyMainWindow(QWidget *parent=0, Qt::WindowFlags flags = 0); ~MyMainWindow(); void flashWindow(); private slots: void flash(); protected: bool eventFilter(QObject *obj, QEvent *event); QTimer *timer; int nFlash; };
Ensuite, il n'y a qu'àQuand vous appelez une fenêtre modale avec Qt, Windows se charge de faire clignoter la bordure de cette dernière.
Par contre, quand elle n'a PAS de bordure (avec le flag Qt::FramelessWindowHint), rien ne se passe.
Résultat ?
L'utilisateur teste votre logiciel, essaie de faire autre chose après avoir ouvert une fenêtre modale en cliquant dans une autre fenêtre de votre application, et absolument rien ne se passe. Il pense qu'il y a un bug, ferme l'application et ne l'ouvre plus jamais parce qu'il a détesté sa première expérience.
Comment régler ce problème ?
J'ai passé énormément de temps pour une si petite chose, puisque personne n'en parle nulle part sur Internet, la documentation de Qt non plus d'ailleurs.
Voici comment j'ai fait.
Je ne dis pas que c'es la meilleure solution, mais c'est un début vers la bonne solution, mais plus important encore : elle fonctionne.
J'ai d'abord essayé d'installer un eventFilter dans la fenêtre modale, mais absolument aucun évènement n'est appellé quand on clique sur une autre fenêtre ... bizarre ...
Donc j'ai fait l'inverse : J'ai fait hériter toutes mes fenêtres d'une classe QMainWindow, qui capte l'évènement d'activation de fenêtre (QEvent::WindowActivate), pour ensuite vérifier si l'application a une fenêtre modale active (QApplication::activeModalWidget()). Si c'est le cas, je fais clignoter cette fenêtre modale avec un QTimer.
Oui je sais c'est pas très joli, mais je n'ai rien trouvé d'autre après des heures de tatonnement.
Par contre, pour faire les fenêtres modales, il faut faire attention pour que ce code fonctionne.
- D'abord, il ne faut pas qu'elle aille de parent, sans quoi le parent ne recevra pas d'évènement.
- Ensuite, exec() et setModal(true) ne fonctionneront pas, pas plus que setWindowModality(Qt::ApplicationModal). Vous devez utiliser setWindowModality(Qt::WindowModal), avec show()
- Pour finir, ne pas oublier que toutes vos fenêtre doivent hériter de
Voici la classe en question :
#include "stdafx.h" #include "RCSWidget.h" #include #include MyMainWindow::MyMainWindow( QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), nFlash(0) { timer = new QTimer(); timer->setInterval(75); connect(timer, SIGNAL(timeout()), SLOT(flash())); } MyMainWindow::~MyMainWindow() { timer->stop(); delete timer; } void MyMainWindow::flashWindow(bool self) { if(nFlash == 0 || nFlash > 5 || self != mFlashSelf) { nFlash = 1; mFlashSelf = self; timer->start(); } } void MyMainWindow::flash() { QWidget* pWidget = (mFlashSelf ? this : QApplication::activeModalWidget()); if (pWidget) { if(!(nFlash % 2)) { pWidget->setWindowOpacity(1); if(nFlash >= 9){ timer->stop(); nFlash = 0; return; } } else pWidget->setWindowOpacity(.85); nFlash++; } else { timer->stop(); nFlash = 0; } } bool MyMainWindow::eventFilter(QObject *o, QEvent *e) { if(e->type() == QEvent::WindowActivate) { QWidget* pWidget = QApplication::activeModalWidget(); if (pWidget) { pWidget->raise(); pWidget->activateWindow(); flashWindow(false); e->accept(); } } return QMainWindow::eventFilter(o, e); }#include #include class RCSMainWindow : public QMainWindow { Q_OBJECT public: RCSMainWindow(QWidget *parent=0, Qt::WindowFlags flags = 0); ~RCSMainWindow(); void flashWindow(); private slots: void flash(); protected: bool eventFilter(QObject *obj, QEvent *event); QTimer *timer; int nFlash; };
Ensuite, il n'y a qu'à