FAQ Qt 5 et erreurs courantes

FAQ Qt 5 et erreurs courantesConsultez toutes les FAQ
Nombre d'auteurs : 1, nombre de questions : 16, dernière mise à jour : 6 février 2022

Lorsque vous connectez un signal à un slot, les paramètres d'entrée du slot sont obtenus à partir de ceux émis par le signal. Par exemple depuis le signal
QLineEdit
::
textChanged(const
QString
&
text) vers un slot MonWidget::
onTextChanged(const
QString
&
text)
connect
(monLineEdit, &
QLineEdit
::
textChanged,
this
, &
MonWidget::
onTextChanged);
// ou ancienne forme :
connect
(monLineEdit, SIGNAL
(textChanged(const
QString
&
)),
this
, SLOT
(onTextChanged(const
QString
&
)));
Lorsque le signal est émis avec la ligne emit
textChanged(/* le nouveau texte */
); au sein de la classe QLineEdit
, notre slot onTextChanged() est exécuté avec en entrée le nouveau texte.
Autant il est possible de connecter un signal avec un nombre de paramètres supérieur ou égal au slot : le slot recevant les paramètres correspondants, autant l'inverse n'est pas possible. Si par exemple vous voulez suite à l'appui sur un certain bouton (signal QPushButton
::
clicked() sans paramètre) exécuter ce même slot MonWidget::
onTextChanged(const
QString
&
text) possédant un paramètre, le signal sera incapable de fournir ce paramètre et vous obtiendrez une erreur (à la compilation pour la première forme « error: static assertion failed: Signal and slot arguments are not compatible. » ou durant l'exécution pour l'ancienne « QObject::connect: Incompatible sender/receiver arguments »).
Lorsque vous connaissez à l'avance le paramètre à passer au moment où vous effectuez la connexion, ceci ne peut PAS se faire en remplaçant la partie const
QString
&
par votre string :
connect
(monLineEdit, SIGNAL
(textChanged(const
QString
&
)),
this
, SLOT
(onTextChanged("Texte"
)));
ou
connect
(monLineEdit, SIGNAL
(textChanged(const
QString
&
)),
label, SLOT
(setText("Texte"
)));
remplacer ainsi un type par une variable ou expression n'a pas grand sens.
Il vous faut :
-
soit utiliser la classe
(C++03) comme indiqué dans ce sujet ou ce cours, ou plus simple une fonction lambda (depuis Qt 5, C++11) :QSignalMapper
C++03 Qt 5, C++11 SélectionnezQSignalMapper
*
signalMapper=
new
QSignalMapper
(this
);connect
(button,SIGNAL
(clicked()), signalMapper,SLOT
(map()));connect
(signalMapper,SIGNAL
(mapped(QString
const
&
)),this
,SLOT
(onTextChanged(QString
const
&
)));connect
(signalMapper,SIGNAL
(mapped(QString
const
&
)), label,SLOT
(setText(QString
const
&
))); signalMapper->
setMapping(button,"Texte"
);Sélectionnezconnect
(button,&
QPushButton
::
clicked,this
, [this
](){
onTextChanged("Texte"
);}
);Sélectionnezconnect
(button,&
QPushButton
::
clicked, label, [this
](){
label->
setText("Texte"
);}
);(Nous devons capturer
this
, pour sa fonction membre (implicitementthis
->
)onTextChanged et son membre (implicitementthis
->
)label afin de pouvoir les utiliser au sein de la fonction lambda.)Notez qu'il est préférable, notamment si les objets
this
et label peuvent être détruits avant le bouton, d'ajouter le 3e paramètre de contexte ou d'utiliser par dessus la classe QPointer et avant de les utiliser dans la fonction lambda, de vérifier s'ils n'ont pas été détruits entre la capture et l'exécution (cf. la note de fin). - soit connecter un slot intermédiaire sans paramètre dans lequel on retrouvera la ligne label
->
setText("Texte"
).
Lorsque vous capturez une référence, un pointeur nu ou this
dans une fonction lambda, prêtez une attention particulière à leur durée de vie par rapport au sender. Si l'objet est détruit et qu'un nouveau signal est émis, leur utilisation au sein de la fonction lambda entraînera un comportement indéfiniQu'est-ce qu'un « comportement indéfini » (Undefined Behaviour / « UB ») ? (cf. dangling pointer/referenceQu'est-ce qu'un « dangling pointer », une « dangling reference » ?). En effet, une connexion normale est automatiquement détruite si le sender ou le receiver sont détruits, mais lors d'une connexion sans contexte avec une fonction lambda, seule la destruction du sender supprime la connexion.
Pour palier à cela, vous pouvez comme montré ci-dessus ajouter un contexte grâce à cette surcharge de la fonction connect() qui entraînera la suppression de la connexion lors de la destruction l'objet. Je vous conseille aussi fortement d'utiliser la classe QPointer qui permet d'observer à la manière d'un pointeur intelligent la durée de vie d'un objet dont la classe hérite de QObject.
Vous pouvez consulter à ce sujet l'item 31 d'Effective Modern C++ de Scott Meyers ainsi que le billet Use carefully lambda function with Qt's connections de Guillaume Belz.
À voir également
Lorsque votre signal ou slot possède plusieurs surcharges, l'écriture &
VotreClasse::
votreSlot devient insuffisante. Votre compilateur n'est pas capable de déterminer la surcharge que vous souhaitez utiliser, et vous donne l'erreur :
Il vous faut effectuer un cast à l'aide de static_cast
qui explicite la signature de la fonction sous la forme :
static_cast
(void
(VotreClasse::
*
)(types des paramètres)(&
VotreClasse::
votreSlot))
Voici un exemple pour la fonction membre currentIndexChanged() de la classe QComboBox
qui possède les deux surcharges suivantes :
QComboBox
*
comboBox =
new
QComboBox
;
// utilisation de : void currentIndexChanged(int index)
connect
(comboBox, static_cast
<
void
(QComboBox
::
*
)(int
)>
(&
QComboBox
::
currentIndexChanged),
/* ... */
);
// utilisation de : void currentIndexChanged(const QString & text)
connect
(comboBox, static_cast
<
void
(QComboBox
::
*
)(const
QString
&
)>
(&
QComboBox
::
currentIndexChanged),
/* ... */
);
Pour alléger (un peu) la syntaxe, il existe depuis la version Qt 5.7 la variable template qOverload() (ainsi que qConstOverload() et qNonConstOverload() pour des surcharges constantes et non constantes) :
class
VotreClasse {
void
fonctionSurchargee(); // (1)
void
fonctionSurchargee(QString
); // (2)
void
fonctionSurchargee(int
, QString
); // (3)
void
fonctionSurchargee(int
, QString
) const
; // (4)
}
;
Ces variables template nécessitant C++14, vous avez en C++11 les classes équivalentes :
C++14 | C++11 |
---|---|
Sélectionnez
|
Sélectionnez
|
L'exemple précédent devient alors :
// utilisation de : void currentIndexChanged(int index)
connect
(comboBox, qOverload
<
int
>
(&
QComboBox
::
currentIndexChanged),
/* ... */
);
// utilisation de : void currentIndexChanged(const QString & text)
connect
(comboBox, qOverload
<
const
QString
&>
(&
QComboBox
::
currentIndexChanged),
/* ... */
);
À voir également