C'est parti !
Le but de cette partie va être de réaliser un utilitaire de téléchargement : l'utilisateur rentre une URL, appuie sur un bouton, voit une barre de téléchargement qui défile et hop, le fichier arrive !
Nous allons tout d'abord nous mettre d'accord sur le projet de départ, j'ai réalisé une petite interface très simple :
Vous remarquerez le titre du projet très original

.
Voici donc les fichiers de départ :
main.cpp
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12 | #include <QApplication>
#include "FenPrincipale.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
FenPrincipale fenetre;
fenetre.show();
return app.exec();
}
|
FenPrincipale.h
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | #ifndef HEADER_FENPRINCIPALE
#define HEADER_FENPRINCIPALE
#include <QtGui>
class FenPrincipale : public QWidget
{
Q_OBJECT
public:
FenPrincipale();
private slots:
private:
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
QLabel *label;
QLineEdit *urlEdit;
QPushButton *demarrerTelechargement;
QProgressBar *progression;
bool erreurTrouvee = false; //Variable qui nous permet de savoir s'il y a eu une erreur ou non.
};
#endif
|
FenPrincipale.cpp
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #include "FenPrincipale.h"
FenPrincipale::FenPrincipale()
{
//On donne une taille par défaut à la fenêtre
resize(228, 112);
setWindowTitle("zDownloader");
//On définit les layouts
verticalLayout = new QVBoxLayout(this);
horizontalLayout = new QHBoxLayout();
label = new QLabel(this);
label->setText("URL :");
horizontalLayout->addWidget(label);
//Le lineEdit qui contient l'URL entrée par le visiteur
urlEdit = new QLineEdit(this);
horizontalLayout->addWidget(urlEdit);
verticalLayout->addLayout(horizontalLayout);
demarrerTelechargement = new QPushButton(this);
demarrerTelechargement->setText("Télécharger");
verticalLayout->addWidget(demarrerTelechargement);
progression = new QProgressBar(this);
progression->setValue(0);
verticalLayout->addWidget(progression);
}
|
On attaque les choses sérieuses, que diable

!
Bon, tout d'abord, on sait que tout va se passer lors du clic sur le bouton.
Nous allons donc faire un slot
telechargement() que nous allons connecter à notre signal de clic !
Ajoutons donc cette ligne dans le constructeur :
Code : C++ | connect(demarrerTelechargement, SIGNAL(clicked()), this, SLOT(telechargement()));
|
Je vous laisse définir ce slot
telechargement() qui renvoie un simple type void.
Tant qu'on est là, on va tout de suite se protéger des petits malins qui vont appuyer plusieurs fois sur le bouton téléchargement alors qu'il est en cours... tout simplement en désactivant le bouton une fois qu'il est enfoncé en ajoutant ceci dans le slot telechargement() :
Code : C++ | demarrerTelechargement->setEnabled(false);
|
Attaquons nous aux choses sérieuses

:
Tout d'abord, nous devons créer une requête. Nous allons donc utiliser la classe QNetworkRequest pour cela.
La classe QNetworkRequest prend comme paramètre une constante QUrl, c'est à dire une Url.
Nous allons donc procéder de cette manière :
Code : C++ | const QUrl url = QUrl(urlEdit->text()); //On récupère l'URL entrée par l'utilisateur.
const QNetworkRequest requete(url); //On crée notre requête
|
Tout ceci bien évidemment dans le slot telechargement();
Nous devons à présent créer un QNetworkAccessManager. Dans votre code final, vous n'aurez pas l'impression que cette classe sert à grand chose, car elle apparait dans 1 ligne seulement. Mais c'est en fait elle qui gère toute la requête et l'accès au réseau !
C'est le QNetworkAccessManager qui possède la méthode get(), qui va nous permettre de télécharger le contenu de notre fameuse URL. Cette méthode renvoie un QNetworkReply, la classe qui va nous permettre de traiter toutes nos données, c'est la "réponse du serveur". Nous allons utiliser un pointeur pour pouvoir traiter les données sans problèmes de portée.
Récapitulation :
Code : C++ | QNetworkAccessManager *m = new QNetworkAccessManager; //On crée le QNetworkAccessManager qui va traiter la requête
/*Ensuite, on utilise la méthode get() pour télécharger le contenu de notre requête.
On récupère un pointeur de QNetworkReply.*/
QNetworkReply *r = m->get(requete);
|
A présent, il va falloir connecter 3 signaux de QNetworkReply pour pouvoir traiter :
- L'enregistrement dans un fichier (finished)
- La barre de progression (downloadProgress)
- La gestion des erreurs (error)
Nous allons donc créer ces trois slots et utiliser ces connect :
Code : C++ | connect(r, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(messageErreur(QNetworkReply::NetworkError)));
connect(r, SIGNAL(finished()), this, SLOT(enregistrer()));
connect(r, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(progressionTelechargement(qint64, qint64) ));
|
Le slot enregistrer()
Commençons par le plus important : enregistrer notre fichier.
Une fois que le slot est appelé, nous avons besoin de récupérer le contenu de notre QNetworkReply.
Qt intègre une fonction qui permet ainsi la récupération du "sender" :
Code : C++ | QNetworkReply *r = qobject_cast<QNetworkReply*>(sender());
|
Nous récupérons ainsi la fameuse réponse du serveur.
Tout d'abord, il faut ouvrir le fichier à enregistrer, puis mettre le contenu de la réponse du serveur en entier.
Procédons donc de cette manière :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | void FenPrincipale::enregistrer()
{
//On vérifie qu'il n'y a pas eu d'erreur.
if(!erreurTrouvee)
{
QNetworkReply *r = qobject_cast<QNetworkReply*>(sender()); //On récupère la réponse du serveur
QFile f("fichier.txt"); //On ouvre le fichier
if ( f.open(QIODevice::WriteOnly) )
{
f.write(r->readAll()); ////On lit la réponse du serveur que l'on met dans un fichier
f.close(); //On ferme le fichier
r->deleteLater(); //IMPORTANT : on emploie la fonction deleteLater() pour supprimer la réponse du serveur.
//Si vous ne le faites pas, vous risquez des fuites de mémoire ou autre.
//On indique que tout s'est bien passé
QMessageBox::information(this, "Fin de téléchargement", "Téléchargement terminé !");
}
close(); //On ferme le programme
}
}
|
Le slot progressionTelechargement()
A présent, quelque chose de bien plus simple, la barre de progression.
On a vu que le signal downloadProgress a deux paramètres qui sont tous deux des qint64.
Le premier correspond au nombre de bytes reçus et le second contient le nombre de bytes totaux.
Nous allons donc utiliser les méthodes setValue() et setRange() de notre barre de progression. Mais avant cela, une petite mise au point.
Beaucoup de gens pensent à tort que setValue() est la valeur en pourcentages de la barre de progression, ce qui est faux. Ou plutôt c'est partiellement faux.
Pour calculer le pourcentage, la barre de progression utilise deux valeurs : la valeur et le total, soit respectivement setValue() et setRange(). Pour calculer le pourcentage, il faut calculer le pourcentage de la valeur par rapport au total, ce qui revient à faire :

Pour obtenir une valeur en pourcentages.
Par exemple, si on a un téléchargement de 2000 octets et qu'on en a reçu 1000, on va calculer (1000/2000)*100 = 0,5*100 = 50. 50%, soit la moitié de notre téléchargement !
Mais alors, pourquoi les gens pensent que setValue() est la valeur en pourcentages ?
C'est tout simplement que le range par défaut d'une barre de progression est à 100, ce qui fait que la valeur en pourcentages est égale à (value/100)*100 = value, ce qui peut prêter à confusion, je vous l'accorde
Nous allons donc faire un slot basique qui prend en paramètres qint64 bytesRecus et qint64 bytesTotal.
Ah oui, aussi, il faut vérifier que le serveur envoie l'information bytesTotal, ce qui n'est pas toujours le cas ... Nous allons donc devoir vérifier que bytesTotal est différent de -1, la valeur reçue par QNetworkReply quand le serveur n'envoie pas cette information.
Code : C++ | void FenPrincipale::progressionTelechargement(qint64 bytesReceived, qint64 bytesTotal)
{
if (bytesTotal != -1)
{
progression->setRange(0, bytesTotal);
progression->setValue(bytesReceived);
}
}
|
Le slot messageErreur()
L'importantissime slot ! C'est lui qui va nous permettre de prévenir l'utilisateur en cas d'échec du téléchargement (fichier inconnu, connexion mauvaise, erreur de DNS...
Nous allons donc tout d'abord récupérer comme la dernière fois le sender() QNetworkReply.
Code : C++ | QNetworkReply *r = qobject_cast<QNetworkReply*>(sender());
|
Ensuite, nous affichons une erreur critique à l'utilisateur pour lui montrer qu'il s'est passé un petit problème ... et nous nous servons de la méthode errorString() pour afficher l'erreur, et enfin nous n'avons plus qu'a fermer la fenêtre :
Code : C++ | void FenPrincipale::messageErreur(QNetworkReply::NetworkError)
{
erreurTrouvee = true; //On indique qu'il y a eu une erreur au slot enregistrer
QNetworkReply *r = qobject_cast<QNetworkReply*>(sender());
QMessageBox::critical(this, "Erreur", "Erreur lors du chargement. Vérifiez votre connexion internet ou réessayez plus tard <br /><br /> Code de l'erreur : <br /><em>" + r->errorString() + "</em>");
close();
}
|
Et voilà, c'est fini !
Vous avez un magnifique zDownloader devant vos yeux ébahis !