Site : tvaira.free.fr

Mise en oeuvre d’un port série sous Qt

Présentation Qt

Qt est une bibliothèque logicielle orientée objet développée en C++ par Qt Development Frameworks, filiale de Digia.

Voir la présentation de Qt.

Le port série RS-232

RS-232 est une norme standardisant une voie de communication de type série sur trois fils minimum. Disponible sur presque tous les PC depuis 1981 jusqu’au milieu des années 2000, il est communément appelé le « port série ».

Sur les systèmes d’exploitation MS-DOS et Windows, les ports RS-232 sont désignés par les noms COM1, COM2, etc. Cela leur a valu le surnom de « ports COM », encore utilisé de nos jours.

On utilise maintenant des adaptateurs USB/RS-232 car les PC ne disposent plus d’interfaces physiques RS-232. Cela revient à exploiter logiciellement un port série virtuel. Ces périphériques utilisent en réalité une transmission série avec un convertisseur USB <–> RS-232 (les circuits les plus répandus sont pl2303, FTDI FT232, …). Certains adaptateurs ajoutent un circuit MAX232 pour mettre en forme des signaux conformes au standard RS-232. La prise en charge du périphérique est assurée par le système d’exploitation via un pilote de périphérique (driver).

Lire le cours sur la transmission série.

Le port série virtuel

Un port série virtuel est une solution logicielle qui émule un port série standard.

Cela permet généralement :

  • d’augmenter le nombre de ports série (sans installation de matériel supplémentaire mais dans les limites des ressources disponibles)
  • de partager les données en provenance d’un périphérique entre plusieurs applications
  • de raccorder un périphérique série standard (RS232, …) sur un port USB avec un adaptateur (manque ou absence de ports série physiques disponibles)

De nombreux périphériques USB sont “vus” comme des ports séries virtuels par le système (XBee, Bluetooh, GPS, …).

Pour établir une communication effective via un port série physique (RS-232) ou virtuel, il est nécessaire de définir le protocole utilisé : notamment, le débit de la transmission (en bits/s), le codage utilisé, le découpage en trame, etc. La norme RS-232 (couche Physique) laisse ces points libres, mais en pratique on utilise souvent des trames d’un caractère ainsi constituées :

  • 1 bit de départ (START)
  • 7 à 8 bit de données
  • 1 bit de parité optionnel (aucune, paire ou impaire)
  • 1 ou plusieurs bits d’arrêt (STOP)

Remarque : De nombreux périphériques séries expriment encore leur vistesse de transmission en bauds. Le baud est l’unité de mesure du nombre de symboles transmissibles par seconde. Dans le cas d’un signal modulé utilisé dans le domaine des télécommunications, le baud est l’unité de mesure de la rapidité de modulation. Le terme “baud” provient du patronyme d’Émile Baudot, l’inventeur du code Baudot utilisé en télégraphie. Il ne faut donc pas confondre le baud avec le bit par seconde (bit/s) car il est souvent possible de transmettre plusieurs bits par symbole. Si on transmet un bit par symbole alors le baud et le bit par seconde (bit/s) sont équivalents.

Exemple : calcul du temps de transmission d’une trame de requête de 8 octets en considérant les paramètres suivants de la liaison série :

  • Vitesse : 9600 bits/s,
  • Longueur des données : 8 bits,
  • Parité : Paire,
  • Nombre de bits de stop : 1

Taille de la trame (en bits) pour transmettre un octet : 1 (START) + 8 (DATA) + 1 (PARITÉ) + 1 (STOP) = 11 bits

Taille de la trame (en bits) pour transmettre une requête : 8 octets x 11 bits = 88 bits

Temps de transmission d’un octet : (1 x 11) / 9600 = 0,00114583 s = 1,14583 ms

Temps de transmission d’une requête : (8 x 11) / 9600 = 0,00917 s = 9,167 ms

Prise en charge matérielle d’un port série

Gestion du port série sous Qt

Suivant votre version de Qt :

  • Qt 4 : Malheureusement, la version 4 de Qt ne fournit pas de classes pour gérer un port série nativement. On va donc devoir utiliser une bibliothèque logicielle externe à Qt : la classe QextSerialPort disponible ici.

  • Qt 5 : le problème n’existe plus en Qt5 car on dispose des classes QSerialPort et QSerialPortInfo avec le module serialport.

$ sudo apt-get install libqt5serialport5 libqt5serialport5-dev

Il faudra ensuite ajouter le module serialport au fichier de projet .pro :

QT += serialport

Exemple Qt 4

On crée un nouveau répertoire et on se déplace à l’intérieur :

$ mkdir mo-qextserialport-1

$ cd mo-qextserialport-1/

On télécharge l’archive :

$ wget https://github.com/qextserialport/qextserialport/archive/master.zip

Remarque : vous pouvez utiliser aussi cette archive qextserialport-1.2rc.zip.

On décompresse l’archive téléchargée :

$ unzip master.zip
$ rm -f master.zip

$ mv qextserialport-master qextserialport

On crée un nouveau fichier de projet :

$ vim mo-qextserialport-1.pro
include(qextserialport/src/qextserialport.pri)

TEMPLATE = app
TARGET = 
DEPENDPATH += .
INCLUDEPATH += .

OBJECTS_DIR = ./tmp
MOC_DIR = ./tmp
DESTDIR = ./bin

# Input
SOURCES += main.cpp

On crée maintenant un programme de test du port série :

$ vim main.cpp
#include "qextserialport.h"
#include <QDebug>

#define PORT "/dev/ttyUSB0"

int main()
{
    QextSerialPort *port;
    
    // instanciation du port en mode asynchrone -> QextSerialPort::Polling
    port = new QextSerialPort(QLatin1String(PORT), QextSerialPort::Polling);
    
    // TODO : paramètrer le port (débit, ...)
    
    // ouverture du port en lecture/écriture
    port->open(QIODevice::ReadWrite);
    qDebug("<debug> etat ouverture port : %d", port->isOpen());
    
    // TODO : réceptionner et/ou envoyer des données
    
    // fermeture du port
    port->close();
    qDebug("<debug> etat ouverture port : %d", port->isOpen());
    
    delete port;
    
    return 0;
}

On fabrique et on exécute le programme de test :

$ qmake

$ make

$ ./bin/mo-qextserialport-1
<debug> etat ouverture port : 1
<debug> etat ouverture port : 0

Le paramétrage du port série se fait avec les méthodes suivantes :

  • setBaudRate()
  • setParity()
  • setDataBits()
  • setStopBits()

Et pour les opération de lecture et d’écriture, on utilisera par exemple les méthodes read() et write().

Documentation : QextSerialPort.

Exemple Qt 5

On crée un nouveau répertoire et on se déplace à l’intérieur :

$ mkdir mo-qserialport-1

$ cd mo-qserialport-1/

On crée un nouveau fichier de projet :

$ vim mo-qserialport-1.pro

QT += serialport

TEMPLATE = app
TARGET = 

# Input
SOURCES += main.cpp

On crée maintenant un programme de test du port série :

$ vim main.cpp
#include <QSerialPort>
#include <QDebug>

#define PORT "/dev/ttyUSB0"

int main()
{
    QSerialPort *port;
    
    // instanciation du port 
    port = new QSerialPort(QLatin1String(PORT));
    
    // TODO : paramètrer le port (débit, ...)
    
    // ouverture du port
    port->open(QIODevice::ReadWrite);
    qDebug("<debug> etat ouverture port : %d", port->isOpen());
    
    // TODO : réceptionner et/ou envoyer des données
    
    // fermeture du port
    port->close();
    qDebug("<debug> etat ouverture port : %d", port->isOpen());
    
    delete port;
    
    return 0;
}

On fabrique et on exécute le programme de test du port série :

$ qmake

$ make

$ ./bin/mo-qserialport-1
<debug> etat ouverture port : 1
<debug> etat ouverture port : 0

Le paramétrage du port série se fait avec les méthodes suivantes :

  • setBaudRate()
  • setParity()
  • setDataBits()
  • setStopBits()

Et pour les opération de lecture et d’écriture, on utilisera par exemple les méthodes read() et write().

Documentation : QSerialPort.

Communication

De nombreux périphériques utilisent un protocole de communication de type ASCII pour échanger des trames. Il existe des protocoles propriétaires et/ou standardisés. Certains sont très répandus : NMEA 0183 (GPS, station météo, …), commandes AT (modem, XBee , Bluetooth, …).

Cela revient :

  • à réceptionner et/ou envoyer des trames : on utilisera les services de la classe QextSerialPort (Qt4) ou QSerialPort (Qt5)
  • à décoder ces trames (vérification, extraction des données, …) : on utilisera les services de la classe QString de Qt.

Remarque : Transmettre des données en ASCII permet de s’affanchir du problème de la réprensation des données en mémoire (voir little endian et big endian sur wikipedia).

Dans une communication, on distinguera trois types de trame :

  • les trames périodiques
  • les trames apériodiques (envoyées sur un évènement ou un changement d’état)
  • les trames de requête et de réponse

La gestion de la communication va ensuite dépendre de l’architecture de votre application. Si votre application dispose d’une IHM, vous ne pourrez pas effectuer d’appels bloquants à partir du thread GUI (Graphical User Interface), qui est le fil d’exécution principal, sinon l’application risquerait de se “figer”. Le thread GUI est responsable de l’affichage et des interactions avec l’utilisateur et surtout c’est le seul thread qui doit modifier l’affichage.

En résumé, cela revient :

  • soit à gérer le port en mode synchrone à partir du thread GUI en utilisant les signaux readyRead() pour la réception et bytesWritten() pour l’émission
  • soit à gérer le port en mode asynchrone à partir d’un autre thread qui communiquera par signal/slot avec le thread GUI

Configuration du port

  • En Qt 4 :
// instanciation du port en mode synchrone -> QextSerialPort::EventDriven
QextSerialPort *port = new QextSerialPort(portName, QextSerialPort::EventDriven);

// configuration
port->setBaudRate(BAUD9600);
port->setDataBits(DATA_8);
port->setParity(PAR_NONE);
port->setStopBits(STOP_1);
port->setFlowControl(FLOW_OFF);
//port->setTimeout(500);

// ouverture
port->open(QIODevice::ReadWrite);

// ...

// fermeture
port->close();
  • En Qt 5 :
// instanciation du port
QSerialPort *port = new QSerialPort(portName);

// configuration
port->setBaudRate(QSerialPort::Baud9600);
port->setDataBits(QSerialPort::Data8);
port->setParity(QSerialPort::NoParity);
port->setStopBits(QSerialPort::OneStop);
port->setFlowControl(QSerialPort::NoFlowControl);

// ouverture
port->open(QIODevice::ReadWrite);

// ...

// fermeture
port->close();

Émission de trames

Remarque : L’émission est généralement plus simple à mettre en oeuvre que la réception.

L’émission de trames périodiques peut se faire sous Qt avec un QTimer (i.e. un minuteur ou temporisateur). Le principe est le suivant :

// instancie un objet QTimer
QTimer *timer = new QTimer(this);

// connecte le signal timeout() au slot envoyer()
connect(timer, SIGNAL(timeout()), this, SLOT(envoyer()));

// démarre le timer (la période s'expime en millisecondes)
timer->start(1000); // toutes les secondes

// ...

// arrête le timer
if(timer->isActive())
{
    timer->stop();
}

À l’expiration du timer le signal timeout()sera émis et le slot envoyer() sera exécuté. Celui-ci devra fabriquer une trame et l’émettre sur le port.

Les fonctions d’écriture sont les suivantes :

  • qint64 write(const char * data, qint64 maxSize)
  • qint64 write(const char * data)
  • qint64 write(const QByteArray & byteArray)

Pour les trames apériodiques ou de requête, il suffit d’émettre la trame en utilisant un objet port de type QextSerialPort (Qt4) ou QSerialPort (Qt5) :

int MaClasse::emettre(const QString &trame)
{
    int nombresOctets = -1;

    if (port == NULL || !port->isOpen())
    {
        return -1;
    }

    nombresOctets = port->write(trame.toLatin1());

    return nombresOctets;
}

Remarque : Il faudra faire attention à la gestion des tampons (buffers) internes. On rappelle que le traitement fait par un ordinateur est beaucoup plus rapide que l’émission d’une trame (nanosecondes vs millisecondes !). Pour de grosses quantités de données, il faudra se synchroniser avec l’émission sur le périphérique pour éviter la saturation des tampons (buffers) internes.

Pour aller plus loin, on dispose :

  • du signal bytesWritten(qint64 bytes) : ce signal est émis chaque fois bytes octets de données ont été écrits sur le périphérique.
  • de la méthode waitForBytesWritten(int msecs) : elle attend jusqu’à ce que le signal bytesWritten() ait été émis ou que msecs millisecondes soient passées. Il est déconseillé de l’utiliser à partir du thread GUI.
  • de la méthode setTimeout(long millisec) : elle définit les délais d’attente d’écriture (et aussi de lecture) pour le port en millisec millisecondes. C’est un délai d’expiration par caractère individuel et non pour l’opération entière.

Évidemment, il faudra aussi s’intéresser au contrôle de flux. Le contrôle de flux, dans un réseau informatique, représente un asservissement du débit binaire des données transmises de l’émetteur vers le récepteur. Le stop and wait est la forme la plus simple de contrôle de flux. En communication série asynchrone RS232, deux modes de contrôle de flux sont proposés : en hardware via les lignes RTS/CTS (Ready To Send/Clear To Send) ou sous contrôle logiciel via les caractères ASCII XON/XOFF. Le contrôle hardware en RS232 nécessite 5 fils (Rx, Tx, Gnd, RTS, CTS) et le contrôle logiciel n’en nécessite que 3 (Rx, Tx, Gnd).

Sans l’utilisation d’une boucle d’évènements, il est possible de gérer le port série en mode bloquant (cf. thread) en utilisant la méthode :

  • waitForBytesWritten() bloque les appels jusqu’à ce que les données ait été écrites sur le port série.

Remarque : Cette méthode expire après ms millisecondes passé en argument (timeout). Le délai d’attente par défaut est 30000 millisecondes. Si msecs est égal à -1, la méthode n’aura pas de timeout.

Réception de trames

Les fonctions de lecture sont très nombreuses :

  • qint64 read(char * data, qint64 maxSize)
  • QByteArray read(qint64 maxSize)
  • QByteArray readAll()
  • qint64 readLine(char * data, qint64 maxSize)
  • QByteArray readLine(qint64 maxSize = 0)

La classe QextSerialPort (Qt4) ou QSerialPort (Qt5) fournit le signal readyRead(). Ce signal est émis une fois que de nouvelles données sont disponibles pour la lecture à partir du périphérique.

Il faut donc tout d’abord le connecter à un slot de réception :

void MaClasse::ouvrirPort(const QString &portName)
{
    port = new QextSerialPort(portName, QextSerialPort::EventDriven); // Qt 4
    // configuration du port ...

    if (port->open(QIODevice::ReadWrite) == true)
    {
        // on connecte le signal readyRead() au slot recevoir()
        connect(port, SIGNAL(readyRead()), this, SLOT(recevoir()));
    }
}

Puis, le principe de réception de trame à partir d’un thread GUI sera le suivant :

void MaClasse::recevoir()
{
    QByteArray donnees;

    while (port->bytesAvailable())
    {
        donnees += port->readAll();
        usleep(100000); // cf. timeout
    }

    QString trameRecue = QString(donnees.data()); 
    
    // ....
}

Sans l’utilisation d’une boucle d’évènements, il est possible de gérer le port série en mode bloquant (cf. thread) en utilisant la méthode :

  • waitForReadyRead() bloque les appels jusqu’à ce que de nouvelles données soient disponibles pour la lecture.

Remarque : Cette méthode expire après ms millisecondes passé en argument (timeout). Le délai d’attente par défaut est 30000 millisecondes. Si msecs est égal à -1, la méthode n’aura pas de timeout.

Détection des ports disponibles

En Qt5, vous disposez de la classe QSerialPortInfo pour faire cela. La classe QSerialPortInfo fournit des informations sur les ports série existants sur votre machine.

On utilise la méthode statique QSerialPortInfo::availablePorts() pour générer une liste d’objets QSerialPortInfo. Chaque objet QSerialPortInfo de cette liste représente un port série unique et on peut récupérer le nom du port (portName()), l’emplacement du système (systemLocation()), la description (description()) et le fabricant (manufacturer()). Cet objet peut également être utilisé comme paramètre d’entrée pour le constructeur ou la méthode setPort() de la classe QSerialPort.

Exemple qui génère une QStringList des ports disponibles pouvant être utilisée dans un QComboBox :

QList<QSerialPortInfo> listePorts;
QStringList listePortsDisponibles;
    
listePorts = QSerialPortInfo::availablePorts();
for (int i=0; i < listePorts.size();i++)
{
  QSerialPortInfo info = listePorts.at(i);
  //if(info.portName().contains("ACM") || info.portName().contains("USB"))
  {
    if(!info.manufacturer().isEmpty())
        listePortsDisponibles << info.manufacturer() + " (" + info.portName() + ")";
    else
        listePortsDisponibles << info.portName();
  }
}
qDebug() << Q_FUNC_INFO << "listePortsDisponibles" << listePortsDisponibles;

Lien : Enumerator Example

Décodage de trames

La classe QString contient de nombreuses méthodes pour manipuler des chaînes de caractère. Il vous faudra consulter très souvent sa documentation : doc.qt.io/qt-4.8/qstring.html.

Remarque : la documentation de Qt est très riche et très bien faite !

Pour le traitement d’une trame, les méthodes les plus utilisées sont :

  • startsWith() et endsWith() pour vérifier les délimiteurs de début de trame (par exemple le dollar $) et de fin de trame (par exemple la séquence “\r\n”)
  • contains() ou count() pour détecter (ou compter) la présence de certains caractères (le plus souvent des délimiteurs comme la virgule ,, le point-virgule ; ou l’étoile *)
  • section() ou split() pour le découpage à partir de délimiteurs (comme la virgule ,, le point-virgule ; …) ou mid() pour une extraction sans délimiteur
  • replace() pour le remplacement et remove() pour la suppression de caractères
  • indexOf() pour obtenir la position d’un caractère

Voici quelques exemples d’utilisation de la classe QString dans le cadre d’un traitement d’une trame NMEA 0183 :

#include <QDebug>
#include <QString>

int main(int argc, char *argv[])
{
    QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000*0E";
    // Faire des essais :
    //QString phrase = "";
    //QString phrase = "GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000*0E";
    //QString phrase = "$GPAAM,A,A,0.10,N,WPTNME*32";
    //QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000";
    
    QString checksum;
    const QString debutTrame = "$";
    const QString typeTrame = "GPGGA";
    const QString debutChecksum = "*";
    
    // phrase vide ?
    if(phrase.length() != 0)
    {
        // est-ce une phrase NMEA 0183 ?
        if(phrase.startsWith(debutTrame))
        {    
            // est-ce la bonne phrase ?
            if(phrase.startsWith(debutTrame + typeTrame))
            {
                // y-a-t-il un checksum ?
                if(phrase.contains(debutChecksum))
                {
                    checksum = phrase.section(debutChecksum, 1, 1);
                    qDebug() << "checksum : 0x" << checksum;
                }
                else
                    qDebug() << "Attention : il n'y a pas de checksum dans cette phrase !";
            }
            else
                qDebug() << "Erreur : ce n'est pas une trame GGA !";
        }
        else
            qDebug() << "Erreur : ce n'est pas une trame NMEA 0183 !";
    }
    else
        qDebug() << "Erreur : phrase vide !";
   
    return 0;
}

Généralement, les protocoles intègrent dans la trame un processus de détection d’erreurs de transmission appelée somme de contrôle (checksum), cf. ci-dessous.

Une fois la trame vérifiée et découpée, il faudra sans doute assurer la conversion vers des données numériques (int, double, …) :

#include <QDebug>
#include <QString>

int main(int argc, char *argv[])
{
    /* Exemple de base */
    int i = 2;
    double d = 3.14;
    
    // Du numérique -> chaîne de caractères
    QString entier = QString::number(i); // int -> QString
    QString reel = QString::number(d); // double -> QString
    //QString reel = QString::number(d, 'f', 1); // double -> QString (avec un chiffre après la virgule)
    
    qDebug() << "L'entier i : " << entier;
    qDebug() << "Le réel d : " << reel;
    
    // De chaîne de caractères -> numérique
    entier = "100";
    reel = "2.71828";
    i = entier.toInt(); // QString -> int
    d = reel.toDouble(); // QString -> double
    
    qDebug() << "L'entier i : " << i;
    qDebug() << "Le réel d : " << d;
    
    /* Exemple appliqué */
    QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000*0E";
    QString horodatage;
    unsigned int heure, minute;
    double seconde;
    
    // découpe la trame avec le délimiteur ',' et récupère le deuxième champ
    horodatage = phrase.section(',', 1, 1); 
    // découpe une chaine à partir d'une position et un nombre de caractères
    heure = horodatage.mid(0, 2).toInt();
    minute = horodatage.mid(2, 2).toInt();
    seconde = horodatage.mid(4, 2).toDouble();
    
    qDebug() << "Horodatage : " << horodatage;
    
    horodatage = QString::number(heure) + " h " + QString::number(minute) 
                 + " " + QString::number(seconde) + " s";
    
    qDebug() << "Horodatage : " << horodatage;
   
    return 0;
}

Remarque : on peut aussi utiliser la méthode arg() (voir ci-dessous).

Un dernier exemple qui montre le calcul du checksum pour une trame NMEA 0183 :

#include <QDebug>
#include <QString>

unsigned char Simulation::calculerChecksum(QString data)
{
   unsigned char checksum = 0;
   
   // remarque : le dollar n'entre pas dans le calcul du checksum
   for(int i=1;i<data.length() && data.at(i).toAscii() != '*';i++)
   {
      checksum ^= data.at(i).toAscii(); // XOR
   }
   //qDebug("(checksum) 0x%02X\n", checksum);
      
   return checksum;
}

int main(int argc, char *argv[])
{
    QString phrase = "$GPGGA,064036.289,4836.5375,N,00740.9373,E,1,04,3.2,200.2,M,,,,0000";
    
    checksum = calculerChecksum(phrase);
    
    // formate le checksum en ASCII sous la forme hexadécimale
    strChecksum = QString("%1").arg(checksum, 2, 16, QLatin1Char('0'));
    
    phrase += QString("*"); // ajoute le délimiteur de checksum
    phrase += strChecksum.toUpper(); // ajoute le checksum converti en majuscule
    phrase += QString("\r\n"); // ajoute le délimiteur de fin

    // ...
        
    return 0;
}

Thread d’arrière-plan

Il est possible de confier la gestion du port à un thread d’arrière-plan.

QSerialPort fournit les méthodes suivantes pour gérer le port série en mode bloquant :

  • waitForReadyRead() bloque les appels jusqu’à ce que de nouvelles données soient disponibles pour la lecture.
  • waitForBytesWritten() bloque les appels jusqu’à ce que les données ait été écrites sur le port série.

Remarque : Ces deux méthodes expirent après ms millisecondes passé en argument (timeout). Le délai d’attente par défaut est 30000 millisecondes. Si msecs est égal à -1, les méthodes n’auront pas de timeout.

Qt fournit la classe QThread pour la création et manipulation d’un thread. Cette classe possède une méthode run() qui contiendra le code exécuté par le thread. Cette classe émet le signal started() lorsqu’un thread est lancé et finished() lorsque le thread est terminé. Il y a plusieurs approches possibles dans l’utilisation de QThread, en voici deux :

  • on dérive une classe de QThread et on implémente la fonction run() qui contiendra le code du thread.
#include <QThread>
class Communication : public QThread
{
    Q_OBJECT
public:
    Communication(QObject * parent = 0);
    void run(); // le code du thread
private:

signals:

public slots:
};

// le code du thread
void Communication::run()
{
    QextSerialPort *port = new QextSerialPort(portName, QextSerialPort::Polling);
    
    while(isRunning())
    {
        // ...
    }
}

Communication *communication = new Communication;

// on démarre le thread
communication->start();  // cela exécutera dans un thread la méthode run() de la classe Communication
  • pour exécuter du code dans un nouveau thread, on instancie directement un QThread et on assigne les objets héritant de QObject à ce thread en utilisant la fonction moveToThread(). On connecte le started() à un slot de cet objet pour exécuter le code du thread.
Acquisition *acquisition = new Acquisition;
QThread *threadAcquisition = new QThread;
acquisition->moveToThread(threadAcquisition);

QObject::connect(threadAcquisition, SIGNAL(started()), acquisition, SLOT(main()));
QObject::connect(threadAcquisition, SIGNAL(finished()), acquisition, SLOT(terminer()));

// on démarre le thread
threadAcquisition->start(); // cela exécutera dans un thread la méthode main() de la classe Acquisition

// ...

Lire les Threads sous Qt.

Retour au sommaire