| | auteur : Matthieu Brucher |
Dans un code Qt4, on verra souvent un new sans delete associé.
En fait, si une nouvelle instance de QObject est crée et qu'on lui spécifie un
parent - le premier argument du constructeur -, c'est ce parent qui sera chargé
de la destruction du fils.
La majorité des classes de Qt4 hérite plus ou moins directement de QObject, mais
attention, ce n'est pas le cas de toutes. L'indicateur est la possibilité de passer
un objet parent au constructeur.
|
lien : La classe QObject
|
| | auteur : Matthieu Brucher |
Les signaux et slots sont l'implémentation par Trolltech du pattern observer.
Ce pattern permet de prévenir les classes voulant observer un évènement.
Pour utiliser ce système, il faut hériter de QObject, soit directement,
soit à l'aide d'un des objets dérivés de QObject. Ensuite, il faut définir la
macro Q_OBJECT qui servira à qmake puis à moc pour générer le code
nécessaire à l'utilisation de ces fonctions.
Pour déclarer des signaux, il suffit d'indiquer :
signal:
void monSignal();
|
pour que moc génère le code qui va bien et pour que le signal puisse
être utilisé. On appellera un signal par :
Ces signaux peuvent être connectés à d'autres signaux ou à des slots.
Ces slots sont des fonctions du programmeur qui seront appelées dès que possible.
public slots:
void monSlot();
|
Une fois les slots définis, il suffit de connecter les signaux et les
slots entre eux. Comme il s'agit de connexion directe, il n'est pas
possible de connecter un signal sans paramètre à un slot avec un paramètre.
Pour cela, il faut utiliser entre-temps QSignalMapper.
connect(this, SIGNAL(monSignal(), somethingElse, SLOT(monSlot()));;
|
On constatera aussi que signaux et slots ne peuvent pas retourner de
valeur, pour l'instant.
|
lien : Les signaux et slots
lien : Tutoriel sur les signaux et slots de Qt 4
|
| | auteur : Nykoo |
Dans la question "
Les signaux et autres slots" nous avons vu comment s'interface une
connexion entre signaux et slots simples, c'est à dire sans transmission de valeur.
Or, les signaux et slots ont la capacité de se transmettre des données par le biais
de leurs arguments.
Prenons l'exemple de la classe QLineEdit de Qt.
Son signal textChanged(const QString& ) permet de récupérer
le texte présent dans le QLineEdit au moment de l'émission du signal.
De même, son slot setText(const QString& ) permet de redéfinir
le texte contenu dans le QLineEdit à l'aide d'un objet QString.
Rappel: Il est possible de connecter un signal avec un slot, mais on peut
également connecter un signal à un autre signal.
Pour créer une connexion signal/slot (ou signal/signal) qui permette la transmission de valeurs,
il suffit d'écrire la signature complète de chaque signal/slot dans la fonction connect().
Autrement dit, il faut indiquer le nom des signaux/slots en question, ainsi que
les types des arguments qu'ils prennent en paramètre :
| Exemple de signature complète |
monSignal(QString,int)
monSlot(QString,int)
connect(objet1,SIGNAL(monSignal(QString,int)),objet2,SLOT(monSlot(QString,int)))
|
Attention, car il faut prendre certaines précautions. Dans une fonction
connect(), les types des arguments des deux fonctions doivent être
compatibles et placés dans le même ordre. Des arguments sont compatibles
si le même objet est en jeux. Ex: const QString& est compatible avec QString. Par contre QString* et QString& ne le sont pas. Voici un exemple de connexion avec 2 types compatibles.
| connect(this,SIGNAL(monSignal(QString,int)),somethingElse,SLOT(monSlot(const QString&,int)));
|
Cependant, le signal ou le slot qui est situé dans la partie droite de la fonction connect() peut avoir un nombre d'arguments inférieur ou égal à celui du signal situé à gauche. Par exemple la connexion suivante:
| connect(this,SIGNAL(monSignal(QString,int)),somethingElse,SLOT(monSlot(QString)))
|
est valide. L'argument int sera simplement ignoré.
|
| | auteur : Nykoo |
Cette question nécessite d'avoir compris la création de signaux/slots simples
(sans arguments) décrite dans la question
"Les signaux et autres slots"
Pour créer un signal ou un slot avec des arguments il suffit d'ajouter les noms
des types dans le prototype:
|
class myClass : public xxx
{
Q_OBJECT
...
signals:
void monSignal(type1,type2)
public slots:
void monSlot(type1,type2);
...
}
|
Ainsi, pour émettre son signal il suffit d'utiliser le mot clef emit.
| Emmission du signal void monSignal(nom_type) |
void fonction()
{
nom_type t;
emit monSignal(t);
}
|
La connexion se fera avec un signal ou un slot qui prend un argument de type nom_type.
|
| | auteur : mac&cheese |
Sous Qt, les connexions entre les signaux et slots peuvent être mis en
place soit manuellement, soit automatiquement, en utilisant la capacité
qu'a QMetaObject d'établir des liens entre ces derniers.
Cette partie ne concerne pas les connexions manuelles (traitées à cette
adresse),
en revanche elle traite de la méthode automatique.
Bien qu'il soit plutôt aisé d'implémenter un slot et de le connecter dans le
constructeur, nous pouvons tout aussi bien utiliser l'outil d'auto-connexion
de QMetaObject pour connecter le signal clicked() de myButton à un slot dans notre classe.
Bien qu'il soit plutôt aisé d'implémenter un slot et de le connecter dans le
constructeur, nous pouvons tout aussi bien utiliser l'outil d'auto-connexion de
QMetaObject pour connecter les signals et les slot clicked() :
| QMetaObject::connectSlotsByName(QObject *object);
|
Cette fonction du QMetaObject de Qt connecte automatiquement tous slots qui
respectent la convention on_Nomobjet_Nomsignal() au signal
Nomsignal() correspondant de l'object Nomobjet. Deux conditions
sont à remplir:
- L'objet NomObject doit être un enfant de l'objet passé en paramètre à
la méthode connectSlotByName().
- Les objets (parents et enfants) doivent être nommés avec la fonction
QString.
| exemple |
class MainWindows : public QWidget
{
Q_OBJECT
public :
MainWindows(QWidget * parent = 0);
public slots :
void on_myButton_clicked();
};
MainWindows::MainWindows(QWidget * parent):QWidget(parent)
{
...
QPushButton * pButton = new QPushButton("essai auto connect");
pButton->setObjectName("myButton");
...
QMetaObject::connectSlotsByName(this);
}
|
Attention: supposons, cette fois-ci, que nous créons notre fenêtre via le Qt Designer et non plus en ligne de code; nous possédons alors un fichier supplémentaire,
à savoir, mainwindows.ui.
Lors de la compilation, l'outil uic de Qt, se charge de générer le code de la fonction setupUi() de notre fenêtre (mainwindows.ui).
Le code généré utilise alors de la même façon :
Il est donc possible d'utiliser de la même façon, les auto-connexions sur une GUI codée à la main que sur une GUI créée à l'aide de Qt Designer (comment utiliser les *.ui)
REMARQUE
: Si deux enfants ont le même nom, l'auto-connect ne se fera que sur le premier enfant trouvé.
REMARQUE2
: Si QMetaObject::connectSlotsByName est appelé plusieurs fois, les connections générées seront multiples.
Lors de l'utilisation d'un *.ui, ne pas oublier que la fonction setupUi appel l'autoconnection
Voici deux exemples à télécharger :
|
téléchargement : Utilisation de autoconnect sans *.ui
téléchargement : Utilisation de autoconnect avec *.ui
|
| | auteur : kinji1 |
Qt propose de nombreuses classes avec de nombreux signaux déjà définis. Cependant
il peut parfois être intéressant de rajouter de l'information à ces signaux afin
de les paramétrer selon l'objet émetteur. On pourrait ainsi vouloir que différents
boutons réalisant une action commune, par exemple ouvrir une page web lorsque l'on
clique dessus, soient connecté à un même slot auquel on précise l'URL à utiliser.
Seulement le signal clicked() de la classe QPushButton ne transmet aucun paramètre,
ce qui ne permet pas de spécifier l'URL.
Une première solution consiste alors à connecter le signal clicked()
de chaque bouton à un slot différent qui se contentera d'appeler la fonction
d'ouverture de la page web avec l'url correspondante. Cependant, comme il est
nécessaire de créer un slot différent par bouton, cela rallonge inutilement la
taille du code, surtout lorsqu'il y a un grand nombre de boutons.
Une autre solution est d'utiliser la classe QSignalMapper. Dans notre exemple,
celle-ci va s'occuper d'appeler le slot ouvrant la page web avec un paramètre
configuré pour chacun des boutons (l'URL de la page). Nous avons donc d'un côté
les signaux clicked() des différents QPushButton, et de l'autre un slot
openUrl(const QString& url) et le QSignalMapper au milieu pour faire les correspondances.
Tout d'abord il faut créer un objet QSignalMapper, puis connecter les signaux
des boutons à son slot map(). On définit alors, pour chacun des boutons, le
paramètre à utiliser pour l'appel à openUrl via setMapping().
mapper = new QSignalMapper();
connect(bouton1, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(bouton1, "http://url1");
connect(bouton2, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(bouton2, "http://url2");
|
Enfin, il suffit de connecter le signal mapped de QSignalMapper à notre slot final.
Ainsi quand un bouton émettra son signal, le slot openUrl sera utilisé avec le paramètre
correspondant au bouton.
connect(mapper, SIGNAL(mapped(const QString &)), this, SLOT(openUrl(const QString &)));
|
Note: QSignalMapper ne se limite pas à des paramètres de type QString. Il
est également possible d'utiliser des entiers ou encore des QWidget* et des QObject*.
Il faut dans ce cas utiliser le signal mapped correspondant au type que l'on veut transmettre.
|
| | auteur : Matthieu Brucher |
Cette erreur se produit lorsque la partie QObject d'une classe n'a pas été ajoutée à l'édition des liens.
Lors de l'utilisation de la macro Q_OBJECT, on définit un certain nombre de méthodes et de variables statiques. Ces méthodes et ces variables sont implémentées dans un fichier généré automatiquement par qmake à l'aide de l'outil moc. Vous pouvez naturellement créer ce fichier manuellement et l'ajouter pour compilation et édition des liens.
|
lien : La classe QObject
lien : Les signaux et slots
|
| | auteur : Yan Verdavaine |
Qt fournit la classe QProcess. Cette classe permet de contrôler l'exécution d'une
application dans un nouveau process. Il est ainsi possible de contrôler :
- les variables d'environnements par la méthode setEnvironement()
- le répertoire d'exécution par setWorkingDirectory()
- de lancer l'application par la fonction start
La fonction start qui crée un process enfant (sera fermé si le process parent est
fermé) avec pour paramètres :
- path de l'exécutable
- liste des paramètres d'entrée de l'exe
- un mode d'ouverture pour interagir avec l'application par le biais
des entrées/sorties standards
Cette classe possède d'autres fonctions pour lancer une application :
-
QProcess::execute : équivalent à start. Bloquante jusqu'à la fin
de l'exécution de l'application lancée
-
QProcess::startDetached : permet de lancer une application dans un
process indépendant. Peut donner le pid du process créé
Il est bon de remarquer que ces fonctions:
- ont des paramètres similaires à ceux de start :
- path de l'exécutable
- liste des paramètres d'entrée de l'exe
- ne donne pas de moyen pour interagir avec le process par le biais des
entrées/sorties standards
- ces deux fonctions sont des fonctions static et peuvent être appelées
sans création d'un QProcess. Dans ce cas les variables d'environnement et le
répertoire d'exécution sera le même que ceux de l'application courante.
| Exemple utilisant QProcess::startDetached |
#include <QtGui>
class MyQPushButton : public QPushButton
{
public :
MyQPushButton(const QString & text,const QString & exe, QWidget * parent = 0)
: QPushButton (text,parent),
m_exe(exe)
{
resize(75, 30);
setFont(QFont("Times", 18, QFont::Bold));
};
virtual void mouseReleaseEvent ( QMouseEvent * event )
{
QProcess::startDetached (m_exe);
}
private :
QString m_exe;
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QWidget w;
QVBoxLayout vl(&w);
MyQPushButton bouton1("Ouvrir DVP / Qt","cmd /c start http://qt.developpez.com/",&w);
MyQPushButton bouton2("Ouvrir notepad","notepad",&w);
MyQPushButton bouton3("Ouvrir invite de commande","cmd" ,&w);
vl.addWidget(&bouton1);
vl.addWidget(&bouton2);
vl.addWidget(&bouton3);
w.show();
return app.exec();
}
|
|
| | auteur : Yan Verdavaine |
QProcess permet de lancer des applications externes ( voir ici).
Elle permet surtout d'interagir avec l'application à la manière d'un pipe au travers des
entrées/sorties standards de l'application.
Ainsi le troisième paramètre de la fonction start permet de spécifier quel type d'interaction on
veut utiliser (par défaut, les deux types sont activés) :
- mode read : récupération de la sortie standard (stdout) et d'erreur(stderr)
- mode write : écriture sur l'entrée standard de l'application (stdin)
QProcess utilise les méthodes définies par QIODevice pour faire ces interactions.
Pour la récuperation des sorties, il faut faire attention au channel que l'on est en train de lire :
De plus Qprocess fournit deux signaux intéressants, qui indiquent si des données
venant d'une des sorties sont arrivées :
-
readyReadStandardOutput () : des données provenant de la sortie
standards sont prêtes à être traitées
-
readyReadStandardError () : des données provenant de la sortie d'erreur
sont prêtes à être traitées
Il est aussi possible de faire ces interactions à l'aide de fichiers :
-
setStandardErrorFile : fichier où l'on récupère la sortie d'erreur
de l'application lancée
-
setStandardOutput : fichier ou l'on récupère la sortie standard de
l'application lancée
-
setStandardInputFile : fichier contenant les donné à envoyer sur
l'entrée standard de l'application lancée
Attention : sous windows, un programme lisant l'entrée standard (stdin) en parallèle
de son ihm, peut se bloquer lorsqu'il est exécuté en process enfant. Ce bug est
normalement corrigé sous vista.
|
téléchargement : TestQProcess_zip
|
| | auteurs : Yan Verdavaine, IrmatDen |
Qt fournit la classe QDesktopServices et en particulier la fonction static QDesktopServices::openUrl, qui permet d'interagir avec les applications associées par défaut. Elle peut être étendue si nécessaire pour ajouter ses propres handlers pour un schéma donné.
|
#include <QtGui>
class MyQPushButton : public QPushButton
{
public :
MyQPushButton(const QString & text,const QString & url, QWidget * parent = 0)
: QPushButton (text,parent) , m_url(url)
{
resize(75, 30);
setFont(QFont("Times", 18, QFont::Bold));
};
virtual void mouseReleaseEvent ( QMouseEvent * event )
{
QDesktopServices::openUrl(m_url);
}
private :
QString m_url;
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QWidget w;
MyQPushButton bouton1(" Ouvrir DVP / Qt","http://qt.developpez.com",&w);
MyQPushButton bouton2("Ouvrir fichier txt","c:/test.txt",&w);
MyQPushButton bouton3("Envoyer mail","mailto:qt@dvp.com?subject=test envoie mail" ,&w);
QVBoxLayout vl(&w);
vl.addWidget(&bouton1);
vl.addWidget(&bouton2);
vl.addWidget(&bouton3);
w.show();
return app.exec();
}
|
|
| | auteur : IrmatDen |
Qt fournit la classe QLibrary permettant de charger de façon multi-plateformes une bibliothèque dynamique, ainsi que d'en récupérer des pointeurs vers les fonctions exportées.
Après avoir créé un objet de ce type, il faut spécifier le nom de la bibliothèque dynamique à associer sans préciser l'extension (ce n'est plus multi-plateformes sinon ). Il suffit ensuite d'appeler resolve() en fournissant le nom du symbole à trouver. Il est retourné en tant que void*, donc un cast sera bien évidemment nécessaire. Sont aussi fournies des fonctions statiques évitant l'instanciation dans le cas où l'on ne voudrait récupérer qu'un symbole.
Par exemple, admettons qu'une fonction d'une bibliothèque permette de compter le nombre de lettres dans un mot :
typedef int (*StringLength)(char*);
QLibrary lib("string_util");
StringLength strLength = (StringLength)lib.resolve("strlen");
if(strLength)
strLength("QLibrary");
|
|
| | auteur : Yan Verdavaine |
Une grosse partie de Qt est basé sur le COW. Il permet ainsi d'utiliser ce pattern.
Pour cela, il faut créer une classe héritant de QSharedData est possédant un constructeur,
un constructeur par recopie et un destructeur public. Cette classe sera l'objet
interne qui sera partagé. Elle possède un compteur de référence thread safe et ne doit
pas être directement accédé. Sa vie sera gérée par d'autres classes.
Pour accéder à une instance de cette classe, deux choix sont possible :
-
QSharedDataPointer: permet de partager implicitement un QSharedData.
L'objet interne est partagé en lecture. L'accès à l'objet en écriture va générer
une recopie de l'objet.
-
QExpliciteSharedDataPointer : permet de partager explicitement un
QSharedData. L'objet interne est partagé en lecture/écriture. L'objet interne sera
recopié uniquement sur demande.
Ces deux classes sont des pointeurs intelligents
spécialisé sur la manipulation des pointeurs sur QSharedData. Ils implémentent
donc la sémantique des pointeurs avec des accès const (lecture) et non const (écriture).
Elles détruiront le QSharedData une fois son compteur à zéro.
Ces pointeurs intelligents possèdent deux fonctions qu'il est utile de connaitre :
-
Detach() : Si le compteur de référence est > 1, le QSharedData sera copié
-
Reset() : Initialise à null le pointeur intelligent
Remarque : ces classes sont thread safe
|
lien : Comment Qt optimise t il les copies ?
lien : Faq C++ : pointeurs intelligents
lien : Tutoriel : Pointeurs intelligents
|
| | auteur : François Jaffré |
Les timers sont gérés avec Qt à l'aide de la classe QTimer. Cette classe est
relativement simple à utiliser grâce aux méthodes suivantes :
- int interval () const -> Permet de connaitre l'intervalle de temps (en ms) entre chaque déclanchement du timer.
- bool isActive () const -> Permet de connaitre si le timer est activé ou pas.
- bool isSingleShot () const -> Permet de connaitresi le timer est en déclanchement unique ou pas.
- void setInterval ( int msec ) -> Permet de paramétrer l'intervalle (en ms) entre chaques déclanchements du timer
- void setSingleShot ( bool singleShot ) -> Permet de mettre le timer en mode déclanchement unique
- void start() -> Permet de démarrer le timer
- void stop() -> Permet d'arrêter le timer
| Création d'un chronomètre simple précis à la seconde | 
#include <QApplication>
#include <QLCDNumber>
#include <QPushButton>
#include <QTimer>
#include <QGridLayout>
class TimerChrono : public QWidget
{
Q_OBJECT
private :
QPushButton* m_Bouton_StartStop;
QPushButton* m_Bouton_Reset;
QLCDNumber* m_LCD;
int m_Timer_value;
QTimer* m_timer;
bool validStart;
public :
TimerChrono()
{
this->m_LCD = new QLCDNumber(5, this);
this->m_Bouton_StartStop = new QPushButton("Start",this);
this->m_Bouton_Reset = new QPushButton("Reset",this);
QGridLayout *layout = new QGridLayout();
layout->addWidget(m_LCD, 0, 0);
layout->addWidget(m_Bouton_StartStop, 2,0);
layout->addWidget(m_Bouton_Reset, 2,1);
this->setLayout(layout);
this->m_Timer_value=0;
this->m_timer = new QTimer(this);
connect(this->m_timer, SIGNAL(timeout()), this, SLOT(update()));
this->m_timer->setInterval(1000);
this->validStart=true;
connect(this->m_Bouton_StartStop, SIGNAL(clicked(bool)), this, SLOT(click_StartStop(bool)));
connect(this->m_Bouton_Reset, SIGNAL(clicked(bool)), this, SLOT(click_Reset(bool)));
resize(200, 150);
}
private slots:
void update()
{
m_Timer_value++;
m_LCD->display(m_Timer_value);
}
void click_Reset(bool valid)
{
this->m_timer->stop();
m_Timer_value=0;
m_LCD->display(m_Timer_value);
this->validStart=true;
m_Bouton_StartStop->setText("Start");
}
void click_StartStop(bool valid)
{
if(validStart == true)
{
m_Bouton_StartStop->setText("Stop");
validStart = false;
this->m_timer->start();
}
else
{
m_Bouton_StartStop->setText("Start");
validStart = true;
this->m_timer->stop();
}
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TimerChrono w;
w.show();
return a.exec();
}
|
Remarque 1 : Si vous souhaitez utiliser un timer en mode "single shot" QTimer possède une methode static QTimer::singleShot() qui peut vous être très utile.
Remarque 2 : Un timer n'est jamais très précis et est fortement dépendant de l'OS. Si vous souhaitez être précis à la milliseconde près ce n'est pas la solution à utiliser.
|
lien : QTimer
lien : QTimer::SingleShot
|
| | auteur : François Jaffré |
La classe QTime permet de récupéré l'heure du PC facilement à l'aide de la méthode static suivantes :
QTime time = QTime::currentTime();
|
|
lien : QTime
|
| | auteur : François Jaffré |
QTime fournit la méthode elapsed() qui permet de connaitre le temps écoulé (en ms)
entre l'exécution de la méthode start() ou restart() et l'appel à la fonction elapsed().
QTime time;
time.start();
ma_fonction();
int millisecondes = time.elapsed();
|
Remarque : Si vous voulez vraiment mesurer précisément votre code et faire
du benchmarking de votre application alors il est préférable de vous tournez vers la
QTestLib qui est faite pour ça.
|
lien : QTime
lien : QTestLib
|
| | auteur : Louis du Verdier |
Dans les années 1980 à 1990 (aucune information ne précise la date exacte), la fonction main() ne renvoyait rien, elle était donc de type void.
Plus récemment, des améliorations de cette fonction ont introduit les arguments qui sont en fait dans la norme actuelle de mettre "int argc" et "char *argv[]".
Ces arguments sont complémentaires : "char *argv[]" représente un tableau de pointeurs de taille donnée par "int argc". Sur la plupart des systèmes d'exploitation,
le tout sert par exemple à retrouver le chemin du programme lancé (avec argv[0]), ou encore par exemple à retrouver un fichier par lequel s'est ouvert le programme
(argc serait donc supérieur à 1).
Quel rapport avec Qt ? Et bien la bibliothèque permet de récupérer les arguments à l'aide de la fonction QCoreApplication::arguments.
La récupération sert par exemple à récupérer le chemin d'un éventuel fichier ouvert par clic sur l'icône de celui-ci.
QStringList args = QCoreApplication::arguments();
QString nom_fichier = args[1];
|
Note : Lors de la déclaration de la fonction main, il faut mettre les arguments pour que ce qui est donné marche, c'est à dire utiliser un début de code comme celui-ci pour la fonction main :
int main (int argc, char *argv[])
{
QApplication app(argc,argv)
}
|
Remarque : En règle générale, le premier élément correspond au nom de l'exécutable. Suivant la plateforme et la mode d'exécution, cet élément peut aussi contenir le chemin relatif ou le chemin absolu de l'exécutable concaténé avec son nom.
Voici un exemple permettant de clore :
const QStringList args = QCoreApplication::arguments();
if(args.count() > 1)
{
QString nom_fichier = args[1];
lireFichier(nom_fichier, this);
}
|
Attention : Sous mac, le double-clic sur un fichier ne sera pas renseigné dans les arguments, contrairement à Windows et Linux. Pour cela il faut utiliser QFileOpenEvent.
|
lien : QFileOpenEvent
|
Consultez les autres F.A.Q's
Les sources présentés sur cette page sont libres de droits,
et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs.
Copyright ©2006
Developpez LLC. Tous droits réservés Developpez LLC.
Aucune reproduction, même partielle, ne peut être faite de ce site et de
l'ensemble de son contenu : textes, documents et images sans l'autorisation
expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à 3 ans
de prison et jusqu'à 300 000 E de dommages et intérêts.
Cette page est déposée à la SACD.
|