Bonjour tout le monde,
J’essaie de réaliser un chat multicast en C++ à l’aide du framework Qt. J’ai commencé par la partie serveur. Je me suis basé sur un cours trouvé sur internet, que j’ai réadapté pour qu’il soit adapté à Qt 6. Je ne sais pas de quelle version il s’agit, mais le programme trouvé sur internet utilise directement :
#include <QApplication>
#include <QPushButton>
// ou encore
connect(client, ... // Sans le Qobject
Je sais que ça ne marche plus en tout cas avec Qt 6, ce qui m’a obligé à faire des modifications après le copier-coller. Je pense que mon problème vient de mes modifs, mais je ne vois pas où.
Mes fichiers sont en fin du post pour la lisibilté. Tout ça compile et s’éxécute. Cependant, dans la "sortie de l’application" de Qt Creator, j’ai le message "qt.core.qobject.connect: QObject::connect: No such slot QWidget::nouvelleConnexion()" (aucun problème dans la "sortie du compilateur"). Le problème semble venir du fichier "fenetre_serveur.cpp" à la ligne 28.
//...
QObject::connect(serveur, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
//...
Je ne vois pas pourquoi il me dit que ce slot n’existe pas alors que je l’ai bien écrit dans le .cpp et le .h. Pour éviter les erreurs de nom, je les ai copier-coller là où il faut à partir de cette ligne justement.
Pourtant il me semble bien avoir compris les paramètres de connect :
- serveur : car j’atend un signal du widget serveur;
- SIGNAL(newConnection()) : le signal du widget serveur qui m’intéresse;
- this : Parceque je veux activer un slot de la fenêtre où je suis (parent de serveur);
- SLOT(nouvelleConnexion()) : C’est le slot que je veux activer, qui est un slot ajouté.
Je me serai trompé sur l’argument this ? je devrais mettre quoi alors ? :/ Je me dit que c’est this le problème car apparemment il cherche le slot "QWidget::nouvelleConnexion" et pas "fenetre_serveur::nouvelleConnexion". j’ai essayé de l’écrire directement dans la ligne 28, mais alors l’erreur est la même mais avec "QWidget::fenetre_serveur::nouvelleConnexion".
Je vois une autre possibilité, c’est la ligne 12 de mon *.h :
//...
class fenetre_serveur : public QWidget {
//Q_OBJECT J'arrive pas à voir pourquoi mais ça compile pas si je laisse ça...
//...
Je ne comprend pas pourquoi, mais si je laisse la macro cpp Q_OBJECT
, je ne peux pas compiler, et je ne comprend pas pourquoi car dans le cours que j’ai on me demande de la mettre, dans le programme trouvé sur internet elle est également présente.
De mon côté, je vais temporairement ignorer le problème, faire le client, puis voir ce qu’il se passe… Avez-vous des pistes pour m’aider ?
Merci par avance
FICHIERS COMPLETS
Les commentaires entre /**/ Sont des modifications que je veux apporter plus tard, elles ne sont pas importantes pour l’instant.
fenetre_serveur.cpp :
#include "fenetre_serveur.h"
// Constructeur par défaut
fenetre_serveur::fenetre_serveur() : QWidget()
{
// Texte d'information du serveur
etatServeur = new QLabel(this);
// Parametres du bouton
boutonQuitter = new QPushButton("Quitter", this);
QObject::connect(boutonQuitter, SIGNAL(clicked()), qApp, SLOT(quit()));
// Disposition
layout = new QVBoxLayout(this); //Pour ne pas s'embêter avec les ->setGeometry
layout->addWidget(etatServeur);
layout->addWidget(boutonQuitter);
setWindowTitle("Fenetre du serveur");
// Demarrage du serveur
/* Songer a mettre ceci hors du constructeur plus tard : J'aimerai pouvoir faire plusieurs serveurs en simultanné */
serveur = new QTcpServer(this);
// Attente de la connexion de n'importe qui sur le port 50885.
if( !serveur->listen(QHostAddress::Any, 50885)){ /* Si possible laisser l'utilisateur choisir son port ou un port automatique*/
etatServeur->setText("Le serveur n'a pas pu demarrer : <br />" + serveur->errorString());
}
else{
etatServeur->setText("Le serveur a démarré sur le port <strong>" + QString::number(serveur->serverPort()) + "</strong>.<br /> Des clients peuvent maintenant se connecter");
QObject::connect(serveur, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
}
}
void fenetre_serveur::nouvelleConnexion()
{
envoyerATous("Un nouveau client vient de se connecter"); // Annonce
// Nouveau client
QTcpSocket *nouveauClient = serveur->nextPendingConnection();
clients << nouveauClient;
// Signaux
QObject::connect(nouveauClient, SIGNAL(readyRead()), this, SLOT(donneesRecues())); // signal readyRead introuvable dans la doc de QTcpSocket
QObject::connect(nouveauClient, SIGNAL(disconnected()), this, SLOT(deconnexionClient())); //idem
}
void fenetre_serveur::donneesRecues()
{
// Recherche du QTcpSoccket du client qui envoi
QTcpSocket *socket = qobject_cast< QTcpSocket *>(sender());
if(socket == 0){ return; }
// Recuperation du message
QDataStream in(socket); /*rendre les if plus propres */
if(tailleMessage == 0){ // Si on a pas déjà la taille du message
if(socket->bytesAvailable() < (int)sizeof(quint16)){ // Si on a pas au moins un int, alors on a pas encore reçu le message en entier
return;
}
in >> tailleMessage; // Si on au moins un entier, alors on a la taille du message
}
if(socket->bytesAvailable() < tailleMessage) {return;} // Si on a pas encore le message entier, on attend
QString message;
in >> message; // On vide entièrement in dans message;
envoyerATous(message); // On l'envoi à tout le monde;
tailleMessage = 0; // On se rend prêt à recevoir un nouveau message;
}
void fenetre_serveur::envoyerATous(const QString& message)
{
QByteArray paquet; // Le paquet a envoyer est un vecteur d'octets
QDataStream out(&paquet, QIODevice::WriteOnly); // Simplement pour pouvoir ecrire dans le paquet avec <<. C'est du sucre syntaxique
out << (quint16) 0; // Réservation de l'emplacement pour la taille du message
out << message; // On place le message a proprement parler
/* Ajouter un traitement du message, pour mettre le pseudo de qui envoi, l'heure... */
out.device()->seek(0); // On se repalce au début du paquet
out << (quint16) (paquet.size() - sizeof(quint16)); // La taille du message compte pas dans la taille du message...
// Envoi
for(int i = 0; i < clients.size(); i++)
{
clients[i]->write(paquet);
}
}
void fenetre_serveur::deconnexionClient()
{
envoyerATous("Un client vient de se déconnecter");
// Qui ?
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
if( socket == 0) { return; }
clients.removeOne(socket); // On retire le client
socket->deleteLater();
/* Mettre des else !!! */
}
fenetre_serveur.h
#ifndef FENETRE_SERVEUR_H
#define FENETRE_SERVEUR_H
// fichiers par défaut
#include <QWidget>
#include <QtNetwork>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>
class fenetre_serveur : public QWidget {
//Q_OBJECT J'arrive pas à voir pourquoi mais ça compile pas si je laisse ça...
public:
fenetre_serveur();
void envoyerATous(const QString& message); /*renommer en envoyer*/
private slots:
void nouvelleConnexion(); /*renommer en client_connexion*/
void donneesRecues(); /*renommer en recevoir*/
void deconnexionClient(); /*renommer en client_deconnexion*/
private:
// Elements de l'interface
QLabel *etatServeur;
QPushButton *boutonQuitter;
QVBoxLayout *layout;
// Elements de réseau
QTcpServer *serveur;
QList<QTcpSocket *> clients;
quint16 tailleMessage = 0;
};
#endif // FENETRE_SERVEUR_H
(Je ne crois pas que mon main soit utile donc je le met pas pour des raisons de place)