Blocage du récepteur
Montre comment utiliser l'API synchrone de QSerialPort dans un thread non-GUI.
Blocking Receiver montre comment créer une application pour une interface série en utilisant l'API synchrone de QSerialPort dans un thread non-GUI.

QSerialPort L'API Blocking Receiver prend en charge deux approches de programmation générales :
- L'approche 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 XML. QSerialPort émet un signal lorsque l'opération est terminée. Par exemple, QSerialPort::write() revient immédiatement. Lorsque les données sont envoyées au port série, QSerialPort émet bytesWritten().
- L'approche synchrone (bloquante). Dans les applications non-GUI et multithread, les fonctions
waitFor...()peuvent être appelées (c'est-à-dire QSerialPort::waitForReadyRead()) pour suspendre le thread appelant jusqu'à ce que l'opération soit terminée.
Dans cet exemple, l'approche synchrone est démontrée. L'exemple du terminal illustre l'approche asynchrone.
Le but de cet exemple est de démontrer un modèle que vous pouvez utiliser pour simplifier votre code de programmation série, sans perdre la réactivité de votre interface utilisateur. L'utilisation de l'API de programmation série bloquante de Qt conduit souvent à un code plus simple, mais en raison de son comportement bloquant, elle ne devrait être utilisée que dans des threads non GUI pour éviter que l'interface utilisateur ne se fige. Mais contrairement à ce que beaucoup pensent, l'utilisation de threads avec QThread n'ajoute pas nécessairement une complexité ingérable à votre application.
Cette application est un récepteur, qui démontre le travail associé à l'application de l'expéditeur Exemple de blocage de l'expéditeur.
L'application Receiver reçoit la demande de l'application Sender via le port série et lui envoie une réponse.
Nous commencerons par la classe ReceiverThread, qui gère le code de programmation série.
class ReceiverThread : public QThread { Q_OBJECT public: explicit ReceiverThread(QObject *parent = nullptr); ~ReceiverThread(); void startReceiver(const QString &portName, int waitTimeout, const QString &response); signals: void request(const QString &s); void error(const QString &s); void timeout(const QString &s); private: void run() override; QString m_portName; QString m_response; int m_waitTimeout = 0; QMutex m_mutex; bool m_quit = false; };
ReceiverThread est une sous-classe de QThread qui fournit une API pour recevoir les demandes de l'expéditeur et dispose de signaux pour fournir des réponses et signaler les erreurs.
Vous devez appeler startReceiver() pour démarrer l'application Receiver. Cette méthode transfère au ReceiverThread les paramètres souhaités pour configurer et démarrer l'interface série. Lorsque le ReceiverThread reçoit une requête de l'émetteur, il émet le signal request(). En cas d'erreur, les signaux error() ou timeout() sont émis.
Il est important de noter que startReceiver() est appelé à partir du thread principal de l'interface graphique, mais que les données de réponse et les autres paramètres sont accessibles à partir du thread de ReceiverThread. Les membres des données de ReceiverThread sont lus et écrits simultanément par différents threads, il est donc conseillé d'utiliser QMutex pour synchroniser l'accès.
void ReceiverThread::startReceiver(const QString &portName, int waitTimeout, const QString &response) { const QMutexLocker locker(&m_mutex); m_portName = portName; m_waitTimeout = waitTimeout; m_response = response; if (!isRunning()) start(); }
La fonction startReceiver() stocke le nom du port série, le délai d'attente et les données de réponse, et QMutexLocker verrouille le mutex pour protéger ces données. Nous démarrons ensuite le thread, à moins qu'il ne soit déjà en cours d'exécution. QWaitCondition::wakeOne La fonction run() sera abordée ultérieurement.
void ReceiverThread::run() { bool currentPortNameChanged = false; m_mutex.lock(); QString currentPortName; if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } int currentWaitTimeout = m_waitTimeout; QString currentRespone = m_response; m_mutex.unlock();
Dans la fonction run(), commencez par acquérir le verrou du mutex, récupérez le nom du port série, le délai d'attente et les données de réponse à partir des données membres, puis relâchez le verrou. La méthode startReceiver() ne doit en aucun cas être appelée en même temps qu'un processus récupérant ces données. QString est réentrant mais pas sûr pour les threads, et il n'est pas recommandé de lire le nom du port série à partir d'un démarrage, d'un appel et d'un timeout ou des données de réponse d'un autre processus. ReceiverThread ne peut gérer qu'un seul démarrage à la fois.
L'objet QSerialPort est construit sur la pile dans la fonction run() avant l'entrée dans la boucle :
QSerialPort serial; while (!m_quit) {
Cela nous permet de créer un objet une fois, tout en exécutant la boucle, et signifie également que toutes les méthodes de l'objet seront exécutées dans le contexte du thread run().
Dans la boucle, vérifiez si le nom du port série pour le démarrage en cours a changé ou non. Si c'est le cas, rouvrez et reconfigurez le port série.
if (currentPortName.isEmpty()) { emit error(tr("Port not set")); return; } else 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; } } if (serial.waitForReadyRead(currentWaitTimeout)) {
La boucle continuera à attendre les données de la demande :
// read request QByteArray requestData = serial.readAll(); while (serial.waitForReadyRead(10)) requestData += serial.readAll();
Warning : La méthode waitForReadyRead() doit être utilisée avant chaque appel read() pour l'approche bloquante, car elle traite toutes les routines d'E/S au lieu de la boucle Qt event-loop.
Le signal timeout() est émis si une erreur survient lors de la lecture des données.
} else { emit timeout(tr("Wait read request timeout %1") .arg(QTime::currentTime().toString())); }
Après une lecture réussie, essayez d'envoyer une réponse et attendez la fin du transfert :
// write response const QByteArray responseData = currentRespone.toUtf8(); serial.write(responseData); if (serial.waitForBytesWritten(m_waitTimeout)) { const QString request = QString::fromUtf8(requestData); emit this->request(request);
Attention : La méthode waitForBytesWritten() doit être utilisée après chaque appel à write() pour l'approche bloquante, car elle traite toutes les routines d'E/S au lieu de la boucle événementielle de Qt.
Le signal timeout() est émis si une erreur se produit lors de l'écriture des données.
} else { emit timeout(tr("Wait write response timeout %1") .arg(QTime::currentTime().toString())); }
Une fois l'écriture réussie, le signal request() contenant les données reçues de l'application Sender est émis :
emit this->request(request);
Ensuite, le thread passe à la lecture des paramètres actuels de l'interface série, car ils peuvent déjà avoir été mis à jour, et exécute la boucle depuis le début.
m_mutex.lock(); if (currentPortName != m_portName) { currentPortName = m_portName; currentPortNameChanged = true; } else { currentPortNameChanged = false; } currentWaitTimeout = m_waitTimeout; currentRespone = m_response; 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 à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutorial : Construire et exécuter.
Voir aussi Terminal série et Bloquer l'émetteur.
© 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.