Blocage de l'expéditeur
Montre comment utiliser l'API synchrone de QSerialPort dans un fil d'exécution.
Blocking S ender montre comment créer une application pour une interface série en utilisant l'API synchrone de QSerialPort dans un thread de travail.

QSerialPort Blocking Sender prend en charge deux alternatives de programmation :
- L'alternative asynchrone (non bloquante). Les opérations sont programmées et exécutées lorsque le contrôle revient dans la boucle d'événements de Qt. La classe QSerialPort émet un signal lorsque l'opération est terminée. Par exemple, la méthode write() revient immédiatement. Lorsque les données sont envoyées au port série, la classe QSerialPort émet le signal bytesWritten().
- L'alternative synchrone (bloquante). Dans les applications sans tête et multithread, la méthode wait peut être appelée (dans ce cas, waitForReadyRead()) pour suspendre le thread appelant jusqu'à ce que l'opération soit terminée.
Dans cet exemple, l'alternative synchrone est démontrée. L'exemple Terminal illustre l'alternative asynchrone.
L'objectif de cet exemple est de montrer comment simplifier votre code de programmation série sans perdre la réactivité de l'interface utilisateur. L'API de programmation série bloquante permet souvent de simplifier le code, mais elle ne doit être utilisée que dans des threads non GUI afin de préserver la réactivité de l'interface utilisateur.
Cette application est l'émetteur qui démontre le travail associé à l'application récepteur Exemple de récepteur bloquant.
L'application émettrice initie la demande de transfert via le port série vers l'application réceptrice et attend la réponse.
class SenderThread : public QThread { Q_OBJECT public: explicit SenderThread(QObject *parent = nullptr); ~SenderThread(); void transaction(const QString &portName, int waitTimeout, const QString &request); signals: void response(const QString &s); void error(const QString &s); void timeout(const QString &s); private: void run() override; QString m_portName; QString m_request; int m_waitTimeout = 0; QMutex m_mutex; QWaitCondition m_cond; bool m_quit = false; };
SenderThread est une sous-classe de QThread qui fournit une API pour la programmation des demandes au récepteur. Cette classe fournit des signaux pour répondre et signaler les erreurs. La méthode transaction() peut être appelée pour démarrer la nouvelle transaction de l'émetteur avec la demande souhaitée. Le résultat est fourni par le signal response(). En cas de problème, le signal error() ou timeout() est émis.
Notez que la méthode transaction() est appelée dans le thread principal, mais que la demande est fournie dans le thread SenderThread. Les membres de données du SenderThread sont lus et écrits simultanément dans différents threads, c'est pourquoi la classe QMutex est utilisée pour synchroniser l'accès.
void SenderThread::transaction(const QString &portName, int waitTimeout, const QString &request) { const QMutexLocker locker(&m_mutex); m_portName = portName; m_waitTimeout = waitTimeout; m_request = request; if (!isRunning()) start(); else m_cond.wakeOne(); }
La méthode transaction() stocke le nom du port série, le délai d'attente et les données de la demande. Le mutex peut être verrouillé avec QMutexLocker pour protéger ces données. Le thread peut alors être démarré, à moins qu'il ne soit déjà en cours d'exécution. La méthode wakeOne() est abordée plus loin.
void SenderThread::run() { bool currentPortNameChanged = false; m_mutex.lock(); QString currentPortName; if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } int currentWaitTimeout = m_waitTimeout; QString currentRequest = m_request; m_mutex.unlock();
Dans la fonction run(), la première étape consiste à verrouiller l'objet QMutex, puis à récupérer le nom du port série, le délai d'attente et les données de requête en utilisant les données membres. Une fois cette opération effectuée, le verrou QMutex est libéré.
La méthode transaction() ne doit en aucun cas être appelée en même temps qu'un processus récupérant les données. Remarque : bien que la classe QString soit réentrante, elle n'est pas sûre pour les threads. Il n'est donc pas recommandé de lire le nom du port série dans un thread de requête et de demander des données dans un autre thread. La classe SenderThread ne peut traiter qu'une seule demande à la fois.
L'objet QSerialPort est construit sur la pile dans la méthode run() avant d'entrer dans la boucle :
QSerialPort serial; if (currentPortName.isEmpty()) { emit error(tr("No port name specified")); return; } while (!m_quit) {
Cela permet de créer un objet pendant l'exécution de la boucle. Cela signifie également que toutes les méthodes de l'objet sont exécutées dans la portée de la méthode run().
Il est vérifié dans la boucle si le nom du port série de la transaction en cours a changé ou non. Si c'est le cas, le port série est rouvert et reconfiguré.
if (currentPortNameChanged) { serial.close(); serial.setPortName(currentPortName); if (!serial.open(QIODevice::ReadWrite)) { emit error(tr("Can't open %1, error code %2") .arg(m_portName).arg(serial.error())); return; } }
La boucle continue à demander des données, à écrire sur le port série et à attendre que toutes les données soient transférées.
// write request const QByteArray requestData = currentRequest.toUtf8(); serial.write(requestData); if (serial.waitForBytesWritten(m_waitTimeout)) {
Attention : Comme pour le transfert bloquant, la méthode waitForBytesWritten() doit être utilisée après chaque appel à la méthode write. Cela traitera toutes les routines d'E/S au lieu de la boucle d'événements de Qt.
Le signal timeout() est émis si une erreur de délai se produit lors du transfert de données.
} else { emit timeout(tr("Wait write request timeout %1") .arg(QTime::currentTime().toString())); }
Il y a une période d'attente pour la réponse après une demande réussie, puis elle est lue à nouveau.
// read response if (serial.waitForReadyRead(currentWaitTimeout)) { QByteArray responseData = serial.readAll(); while (serial.waitForReadyRead(10)) responseData += serial.readAll(); const QString response = QString::fromUtf8(responseData); emit this->response(response);
Attention : Comme pour l'alternative bloquante, la méthode waitForReadyRead() doit être utilisée avant chaque appel à read(). Cela traitera toutes les routines d'E/S au lieu de la boucle d'événements de Qt.
Le signal timeout() est émis si une erreur de délai se produit lors de la réception de données.
} else { emit timeout(tr("Wait read response timeout %1") .arg(QTime::currentTime().toString())); }
Lorsqu'une transaction est terminée avec succès, le signal response() contient les données reçues de l'application réceptrice :
emit this->response(response);
Ensuite, le thread se met en veille jusqu'à ce que la prochaine transaction apparaisse. Le thread lit les nouvelles données après s'être réveillé en utilisant les membres et exécute la boucle depuis le début.
m_mutex.lock(); m_cond.wait(&m_mutex); if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } else { currentPortNameChanged = false; } currentWaitTimeout = m_waitTimeout; currentRequest = m_request; m_mutex.unlock(); }
Exécution de l'exemple
Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple de Examples. Pour plus d'informations, voir Qt Creator: Tutorial : Construire et exécuter.
Voir aussi Terminal série et Récepteur de blocage.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.