-
Bonjour et bienvenu sur devnet,
La première règle enfreinte: ne jamais accéder à des éléments de la GUI depuis un autre thread que celui où la QApplication a été créée.
Ensuite, par curiosité, pourquoi éliminer d'une application Qt des classes comme QTcpServer et QTcpSocket ou encore QThread, pour finalement réimplémenter leur fonctionnalité sur chaque plateforme ?
-
Bonjour SGaist,
Merci de me répondre.
En fait ça m'est t'arrivé de travailler dans des entreprises (une banque par exemple), où il ne travaille pas avec des Frameworks en général et Qt en particulier. c'est pour cette raison que j'ai choisi pthread. en général il font du développement pour de l'embarqué donc on ne peut même pas utiliser thread du c++11(bibliothèque standard).
Et pour ce qui est l'accès à des éléments de la GUI depuis un autre thread que celui où la QApplication a été créée, c'est que je n'ai pas vraiment le choix. parce que pthread n'accepte pas les fonction membres d'une classe.
Merci de tes éclaircissements. -
Il faut savoir séparer les choses. Appliquer les règles du développement sur système embarqué à une application desktop n'est pas la meilleure des idées. L'inverse est tout autant vrai.
Ce n'est pas parce que pthread n'est pas capable de faire une chose qu'il faut utiliser une alternative qui est explicitement interdite. Dans ce cas précis, il faut utiliser QMetaObject::invokeMethod.
Ceci étant, comme dit précédemment, c'est ajouter une grosse couche de complexité pour un bénéfice de 0.
-
Bonjour SGaïste,
En fait tout ce que j'essaye de faire c'est d'utiliser des thread indépendamment de Qt.
je suis en train de tester une nouvelle méthode avec thread de la bibliothèque standard du c++11, je te tiendrai au courant dès que je l'aurai peaufiner. et merci encore . -
Alors il faut garder de manière bien séparée la partie GUI et la partie thread.
-
tu veux dire que je dois mettre les fonctions utilisées dans les thread dans une classe à part ?
-
En principe, l'encapsulation est la meilleure méthode.
Un exemple avec QThread est Mandelbrot. Tout le processing se fait dans le thread, les données sont ensuite envoyées au thread principal pour affichage.
-
Bonjour à tous,
J’ai changé la structure de mes classes, pour faire simple j’ai mis tout ce qui concerne les sockets dans un header sous forme de fonctions libres, du coté serveur je n’ai rien changé pour l’instatant.Fichier header.h : ce fichier contient les inclusions communes et quelques define #ifndef HEADER_H #define HEADER_H // On inclut les fichiers standards #include <iostream> #include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <thread> #include <mutex> //Les Fichiers Qt #include <QPlainTextEdit> #include <QPushButton> #define PORT 10000 #if defined (WIN32) //Si nous sommes sous WINDOWS #include <winsock2.h> #include <windows.h> //On peut remarquer que le type socklen_t qui existe sous Linux, n'est pas défini sous Windows. Ce type sert à stocker la taille d'une structures de type sockaddr_in. Ça n'est rien d'autre qu'un entier mais il nous évitera des problèmes éventuels de compilation sous Linux par la suite. Il va donc falloir le définir nous même à l'aide du mot clef typedef comme il suit typedef int socklen_t; #elif defined (linux) //Si nous sommes sous LINUX #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> // Define, qui nous serviront par la suite #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket(s) close(s) // De même typedef int SOCKET; typedef struct sockaddr_in SOCKADDR_IN; typedef struct sockaddr SOCKADDR; #endif struct Shared { int data; pthread_mutex_t mut; pthread_cond_t synchro; }; struct ThreadData { QPlainTextEdit *strucPlainText; QPushButton *strucBtnOn; QPushButton *strucBtnOff; struct Shared *psh; }; struct MsgData { char nom[256]; char message[256]; }; #endif // HEADER_H
Maintenant voici les fichiers Du côté client
Fichier fctclient.h
#ifndef FCTCLIENT_H #define FCTCLIENT_H #include "../SocketGraphicServer/header.h" #include <QDebug> #include <QPlainTextEdit> #include <QString> #include <QMessageBox> /* Socket et contexte d'adressage du client */ SOCKET csock; SOCKADDR_IN csin; socklen_t crecsize; int sock_err; QPlainTextEdit *m_plainTextEdit ; struct ThreadData m_threadDataClient; bool m_graphic; struct MsgData m_msgDataClient; bool boolConnection; void Initialisation(struct ThreadData &myThreadData); bool FctConnection(); void FctDeconnection(); void* FctReceiveMsg(void *arg); #endif // FCTCLIENT_H
Fichier fctclient.cpp
#include "fctclient.h" void Initialisation(struct ThreadData &myThreadData) { m_graphic = true; m_threadDataClient = myThreadData; #if defined (WIN32) WSADATA WSAData; int erreur = WSAStartup(MAKEWORD(2,2), &WSAData); #elif int erreur = 0; #endif if(!erreur) { /* Création de la socket */ csock = socket(AF_INET, SOCK_STREAM, 0); printf("La socket client est %d\n", csock); /* Configuration de la connexion */ csin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //127.0.0.1"); "192.168.0.12" csin.sin_family = AF_INET; csin.sin_port = htons(PORT); } } bool FctConnection() { m_threadDataClient.strucPlainText->appendPlainText(QString("je suis la connection")); bool returnVal = false; //Si on n'arrive pas à se connecter. if(connect(csock, (SOCKADDR *)&csin, sizeof(csin)) == SOCKET_ERROR) { //printf("Connexion à %s sur le port %d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); // Attente pendant laquelle le client se connecte if(!m_graphic) printf("Impossible de se connecter."); else m_threadDataClient.strucPlainText->appendPlainText(QString("Impossible de se connecter.")); returnVal = false; } //Si on arrive à se connecter else { if(!m_graphic) printf("Connexion à %s sur le port %d\n", inet_ntoa(csin.sin_addr), htons(csin.sin_port)); else { m_threadDataClient.strucPlainText->appendPlainText(QString("Connexion à %1 sur le port %2.").arg(inet_ntoa(csin.sin_addr)).arg(htons(csin.sin_port))); } returnVal = true; boolConnection = true; } return returnVal; } void FctDeconnection() { /* On ferme la socket précédemment ouverte */ closesocket(csock); //QMessageBox::information(nullptr, "csock", QString::number(csock)); #if defined (WIN32) WSACleanup(); #endif boolConnection = false; m_threadDataClient.strucPlainText->appendPlainText(QString("Déconnexion")); } void* FctReceiveMsg(void *arg) { qDebug() << "j'attends un message avant le while" ; //m_msgDataClient = (MsgData*)arg; while(boolConnection) { if(recv(csock, (char*)&m_msgDataClient, sizeof(m_msgDataClient), 0) != SOCKET_ERROR) { std::string nom = m_msgDataClient.nom; std::string message = m_msgDataClient.message; qDebug() << QString::fromStdString(nom); qDebug() << QString::fromStdString(message); pthread_mutex_lock(&m_threadDataClient.psh->mut); pthread_cond_signal(&m_threadDataClient.psh->synchro); pthread_mutex_unlock(&m_threadDataClient.psh->mut); } } }
Fichier mainwindowclient.h
#ifndef MAINWINDOWCLIENT_H #define MAINWINDOWCLIENT_H #include <QApplication> #include <QMainWindow> //#include "threadFunctions.h" #include "fctclient.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindowClient; } QT_END_NAMESPACE class MainWindowClient : public QMainWindow { Q_OBJECT public: MainWindowClient(QWidget *parent = nullptr); ~MainWindowClient(); void FctConnectionsObjets(); void FctRcvMsg(); void* FctWriteMsg(); public slots: void SlotRcvMsg(); void SlotSetMessage(MsgData&); void SlotConnection(); void SlotDeconnexion(); private: Ui::MainWindowClient *ui; //ClassClient myClassClient; //On déclare les thread //std::thread threadRcvMsg; pthread_t threadRcvMsg; pthread_t threadWriteMsg; //Les structures struct Shared m_shared; struct ThreadData m_threadDataClient; }; #endif // MAINWINDOWCLIENT_H
Le fichier mainwindowclient.cpp
#include "mainwindowclient.h" #include "ui_mainwindowclient.h" MainWindowClient::MainWindowClient(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindowClient) { ui->setupUi(this); qDebug() << "entrée du constructeur"; m_shared = { .data = 0, .mut = PTHREAD_MUTEX_INITIALIZER, .synchro = PTHREAD_COND_INITIALIZER, }; m_threadDataClient = { .strucPlainText = ui->plainTextEditRapport, .strucBtnOn = ui->btnConDecon, .strucBtnOff = ui->btnDeconnecter, .psh = &m_shared, }; //myClassClient = ClassClient(m_threadDataClient); FctConnectionsObjets(); } MainWindowClient::~MainWindowClient() { delete ui; } void MainWindowClient::FctConnectionsObjets() { QObject::connect(ui->btnConDecon, SIGNAL(clicked()), this, SLOT(SlotConnection())); QObject::connect(ui->btnDeconnecter, SIGNAL(clicked()), this, SLOT(SlotDeconnexion())); //QObject::connect(this, SIGNAL(m_signa1(MsgData&)), this, SLOT(SlotEtMessage(MsgData&))); } void MainWindowClient::SlotConnection() { //myClassClient = ClassClient(m_threadDataClient); if(FctConnection()) { ui->btnConDecon->setText("&Déconnecter"); SlotRcvMsg(); } } void MainWindowClient::SlotRcvMsg() { int ret1 = pthread_create(&threadRcvMsg, NULL, FctReceiveMsg, nullptr); // int ret2 = pthread_create(&threadWriteMsg, NULL, FctWriteMsg, nullptr); int ret2 = pthread_create(&threadWriteMsg, NULL, [](void *ptr){static_cast<MainWindowClient*>(ptr)->FctWriteMsg(); return (void*)nullptr;}, nullptr); //int ret = pthread_create(&threadRcvMsg, NULL, ClassClient::FctReceiveMsg, nullptr); //threadRcvMsg = std::thread((FctReceiveMsg)); if(ret1 != 0) { ui->plainTextEditRapport->appendPlainText("Erreur : erreur de création de pthread_create() de \"threadRcvMsg\"."); } else { ui->plainTextEditRapport->appendPlainText("Succés : Création de pthread \"threadRcvMsg\" réussie"); } if(ret2 != 0) { ui->plainTextEditRapport->appendPlainText("Erreur : erreur de création de pthread_create() de \"threadWriteMsg\"."); } else { ui->plainTextEditRapport->appendPlainText("Succés : Création de pthread \"threadWriteMsg\" réussie"); } } void MainWindowClient::SlotSetMessage(MsgData&) { ui->plainTextEditRapport->appendPlainText("je suis set message"); } void* MainWindowClient::FctWriteMsg() { pthread_mutex_lock(&m_threadDataClient.psh->mut); pthread_cond_wait(&m_threadDataClient.psh->synchro, &m_threadDataClient.psh->mut); ui->plainTextEditRapport->appendPlainText("je suis FctWriteMsg message"); pthread_mutex_unlock(&m_threadDataClient.psh->mut); } void MainWindowClient::SlotDeconnexion() { //threadRcvMsg.detach(); ui->btnConDecon->setText("&Connecter"); //myClassClient.FctDeconnection(); FctDeconnection(); }
Fichier main.cpp cote client
#include "mainwindowclient.h" #include <QApplication> #include <QLocale> #include <QTranslator> int main(int argc, char *argv[]) { QApplication a(argc, argv); QTranslator translator; const QStringList uiLanguages = QLocale::system().uiLanguages(); for (const QString &locale : uiLanguages) { const QString baseName = "SocketGraphicClient_" + QLocale(locale).name(); if (translator.load(":/i18n/" + baseName)) { a.installTranslator(&translator); break; } } MainWindowClient w; w.show(); return a.exec(); }
Et voici les erreurs maintenant. Il y en a 33 mais j’en ai mis que 3. Je ne comprends pas d’où vient cette erreur de redéfinition de variables
:-1: erreur : debug/main.o:D:\Fichiers_applications\C++\Projets_QtCreator\Test\Revis_gnrle\Travaux_pratiques\SocketGraphic\build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug\SocketGraphicClient/../../SocketGraphicRoot/SocketGraphicClient/fctclient.h:13: multiple definition of `csock'; debug/fctclient.o:D:\Fichiers_applications\C++\Projets_QtCreator\Test\Revis_gnrle\Travaux_pratiques\SocketGraphic\build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug\SocketGraphicClient/../../SocketGraphicRoot/SocketGraphicClient/fctclient.h:13: first defined here :-1: erreur : debug/main.o:D:\Fichiers_applications\C++\Projets_QtCreator\Test\Revis_gnrle\Travaux_pratiques\SocketGraphic\build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug\SocketGraphicClient/../../SocketGraphicRoot/SocketGraphicClient/fctclient.h:14: multiple definition of `csin'; debug/fctclient.o:D:\Fichiers_applications\C++\Projets_QtCreator\Test\Revis_gnrle\Travaux_pratiques\SocketGraphic\build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug\SocketGraphicClient/../../SocketGraphicRoot/SocketGraphicClient/fctclient.h:14: first defined here :-1: erreur : debug/main.o:D:\Fichiers_applications\C++\Projets_QtCreator\Test\Revis_gnrle\Travaux_pratiques\SocketGraphic\build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug\SocketGraphicClient/../../SocketGraphicRoot/SocketGraphicClient/fctclient.h:15: multiple definition of `crecsize'; debug/fctclient.o:D:\Fichiers_applications\C++\Projets_QtCreator\Test\Revis_gnrle\Travaux_pratiques\SocketGraphic\build-SocketGraphicRoot-Desktop_Qt_6_3_1_MinGW_64_bit-Debug\SocketGraphicClient/../../SocketGraphicRoot/SocketGraphicClient/fctclient.h:15: first defined here
Merci d’avance de votre aide
-
@Mourad2021 said in Problème avec pthread et QPlainTextEdit:
fctclient.h
Cet
tefctclient.h
nepeurpeut pas avoir#include "fctclient.h"
plus qu'une seule fois dans un fichier.cpp
. Vous l'avez dansfctclient.cpp
et dansmainwindowclient.h
->main.cpp
. -
Pourquoi avoir créé des variables statiques pour tous ces éléments ?
-
@JonB said in Problème avec pthread et QPlainTextEdit:
Cette fctclient.h ne peur pas avoir
Scary words :)
Comme l'a noté @JonB , le fichier est inclus plusieurs fois et comme :
/* Socket et contexte d'adressage du client */
SOCKET csock;
SOCKADDR_IN csin;
socklen_t crecsize;
int sock_err;sont déclarées dans le .h, elles sont ainsi définies plusieurs fois.
-
Merci à tous de me répondre, ça m'aide beaucoup pour ne pas perdre du temps.
JonB en fait je l'utilise mainwindowclient dont :#include "fctclient.h" dans mainwindowclient.h
et dans fctclient.cpp ce qui est normal pour définir les fonctions
par contre
mpergand ça marche le fait de déplacer la déclaration des variables dans le fctclient.cpp
SGaist : elles ne sont pas déclarées statiques.
en tout cas merci à tous pour votre aide.
je vous tiendrai au courant de la suite. -
@Mourad2021 said in Problème avec pthread et QPlainTextEdit:
ça marche le fait de déplacer la déclaration des variables dans le fctclient.cpp
Il existe en C un mot magique: extern