IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

FAQ Qt 5 et erreurs courantes

FAQ Qt 5 et erreurs courantesConsultez toutes les FAQ

Nombre d'auteurs : 1, nombre de questions : 16, dernière mise à jour : 6 février 2022 

 
OuvrirSommaireDivers
précédentsommairesuivant
 
 

Certaines fonctions membres des classes Qt telles que QTabWidget::currentWidget(), QTableView:::indexWidget(), QTableWidget::cellWidget(), QTreeWidget::currentItem(), QMdiArea::activeSubWindow() ou QObject::sender() dans un slot vous renvoient respectivement le type QWidget* pour les trois premières, QTreeWidgetItem*, QMdiSubWindow* et QObject* correspondant à la classe de base, là où vous attendriez votre propre classe qui en hérite (directement ou indirectement). Vous avez alors besoin de downcaster le pointeur retourné afin de récupérer votre instance de classe et utiliser les services qu'elle fournit.

Qt possède pour ce faire la fonction libre qobject_cast dont l'utilisation est la même que ses homologues :

 
Sélectionnez
ou auto C++11VotreClasse * pointeur = qobject_cast<VotreClasse *>(/* … */);

N'oubliez pas de vérifier si la conversion a réussi ou non :

 
Sélectionnez
if (pointeur) {
    // Vous pouvez utiliser le pointeur
} else {
    // Erreur dans la conversion, peut-être :
    //  - un mauvais type dérivé demandé ?
    //  - le pointeur retourné par la fonction est déjà nul ?
}

Ou selon le cas avec une assertion :

 
Sélectionnez
Q_ASSERT(pointeur);
// Utilisation

Exemples :

 
Sélectionnez
auto monWidget = qobject_cast<MaClasseWidget *>( tabWidget.currentWidget() );
auto monWidget = qobject_cast<MaClasseWidget *>( tableView.indexWidget( tableView.indexAt({i,j}) ) );
auto monWidget = qobject_cast<MaClasseWidget *>( tableWidget.cellWidget(i,j) );
auto monItem = qobject_cast<MonTreeWidgetItem *>( treeWidget.currentItem() );
auto maSubWindow = qobject_cast<MaSubWindow *>( mdiArea.activeSubWindow() );
auto monObjet = qobject_cast<MaClasse *>( sender() );
Mis à jour le 16 novembre 2018  par Winjerome

Cette erreur est souvent commise par les débutants qui suivent le cours d'OpenClassrooms.

Au lieu d'utiliser les variables membres monBouton et monLayout dans le constructeur de votre classe, vous déclarez de nouvelles variables locales indépendantes. Ces variables locales ont beau porter le même nom que les membres de votre classe déclarés dans le .h, elles n'ont strictement aucun lien avec et leur modification n'affecte aucunement les membres (et inversement), elles sont utilisées à leur place jusqu'à la fin du constructeur, et en sortant :

  • les éléments graphiques gérés par ces variables locales sont malgré tout correctement intégrés à votre interface (connexions, affichage…), donnant l'illusion que cette partie du code fait les choses correctement ;
  • les membres de la classe restent non initialisés.

Ainsi, plus tard lorsque dans la fonction_membre (généralement un slot connecté à un évènement) vous tentez d'accéder à vos objets graphiques à l'aide (cette fois) des membres non initialisés, vous obtenez un comportement indéfiniQu'est-ce qu'un « comportement indéfini » (Undefined Behaviour / « UB ») ? qui dans le cas présent se manifeste souvent par un crash.

 
Sélectionnez
class Fenetre : public QWidget {
    QPushButton * monBouton;
    QVBoxLayout * monLayout;

public:
    Fenetre();
    void fonction_membre();
};
 
Sélectionnez
Fenetre::Fenetre() {
    Partie en trop qui sert à déclarer une nouvelle variable.
Supprimez-la, et vous utiliserez bien le membre de la classe.
QVBoxLayout *
monLayout = new QVBoxLayout{this}; // Nouvelle variable locale ! Idem.QPushButton * monBouton = new QPushButton{"Hello"}; // Nouvelle variable locale ! monLayout->addWidget(monBouton); } // Les pointeurs membres 'monBouton' et 'monLayout' eux, n'ont pas été initialisés !!! // … mais on les utilise ici : void Fenetre::fonction_membre() { monBouton->setText("Hello World!"); // boom ! monLayout->addWidget( new QPushButton{"Bonjour le monde !", this} ); // boom ! }

Analogie toute simple : pour modifier la valeur d'une variable int a précédemment déclarée, vous n'ajoutez pas int devant mais utilisez a seul :

 
Sélectionnez
int a = 0;
int b = 10;
 
Sélectionnez
int a = 10 * b;
 
Sélectionnez
a = 10 * b;

C'est la même chose ici : QPushButton * monBouton et QVBoxLayout * monLayout ont déjà été déclarés dans le .h.

Sur GCC/Clang, l'option -Wshadow permet de vous prévenir de cette erreur :

 
Sélectionnez
(GCC)   fenetre.cpp:<ligne>:19: warning: declaration of 'monLayout' shadows a member of 'Fenetre' [-Wshadow]
(CLang) fenetre.cpp:<ligne>:19: warning: declaration shadows a field of 'Fenetre' [-Wshadow]
    QVBoxLayout * monLayout = new QVBoxLayout{this};    // Nouvelle variable locale !
                  ^
fenetre.hpp:<ligne>:19: note: shadowed declaration is here
    QVBoxLayout * monLayout;
                  ^

Sur Visual Studio (depuis la version 2015), l'option /W4 (au minimum) vous donnera :

 
Sélectionnez
fenetre.cpp(<ligne>) warning C4458: declaration of 'monLayout' hides class member
fenetre.hpp(<ligne>) note: see declaration of 'Fenetre::monLayout'

(Idem pour monBouton.)

Cette erreur peut facilement être évitée en initialisant vos membres à l'endroit fait pour cela : la liste d'initialisation[Erreur courante] La « liste d'initialisation », ou comment initialiser correctement ses membres et la classe de base ?.

Mis à jour le 30 décembre 2018  par Winjerome

Pour ce faire, vous avez à votre disposition deux classes, que vous pouvez combiner ou non selon votre besoin :

Considérons l'arborescence suivante :

Image non disponible

avec « dossier » un répertoire quelconque situé sur votre ordinateur ayant pour chemin(1) QString const path {"chemin/vers/dossier"};. Les différents codes ci-après vous donneront les sorties suivantes(2) :

Description Code Sortie
Tous les éléments (chemin complet) directs du dossier.
 
Sélectionnez
QDirIterator it {path};
while (it.hasNext()) {
    qDebug() << it.next();
}
 
Sélectionnez
"…/dossier/."
"…/dossier/.."
"…/dossier/fichier.txt"
"…/dossier/image-1.png"
"…/dossier/image-2.png"
"…/dossier/image-3.png"
"…/dossier/sous-dossier"
"…/dossier/sous-dossier-1"
"…/dossier/sous-dossier-2"
"…/dossier/sous-dossier-3"
Tous les éléments (noms seuls) directs du dossier.
 
Sélectionnez
QDirIterator it {path};
while (it.hasNext()) {
    it.next();  // Voir avertissement de fin 
    qDebug() << it.fileName();
}

Ou

 
Sélectionnez
QDir dir {path};
for (QString const & item : dir.entryList()) {
    qDebug() << item;
}
 
Sélectionnez
"."
".."
"fichier.txt"
"image-1.png"
"image-2.png"
"image-3.png"
"sous-dossier"
"sous-dossier-1"
"sous-dossier-2"
"sous-dossier-3"
Tous les éléments directs du dossier, mais aussi ceux des sous-dossiers, sous-sous-dossiers, etc.
 
Sélectionnez
QDirIterator it {path, QDirIterator::Subdirectories};
while (it.hasNext()) {
    qDebug() << it.next();
}
 
Sélectionnez
"…/dossier/."
"…/dossier/.."
"…/dossier/fichier.txt"
"…/dossier/image-1.png"
"…/dossier/image-2.png"
"…/dossier/image-3.png"
"…/dossier/sous-dossier"
"…/dossier/sous-dossier/."
"…/dossier/sous-dossier/.."
"…/dossier/sous-dossier/sous-sous-dossier"
"…/dossier/sous-dossier/sous-sous-dossier/."
"…/dossier/sous-dossier/sous-sous-dossier/.."
"…/dossier/sous-dossier/sous-sous-dossier/fichier.txt"
"…/dossier/sous-dossier-1"
"…/dossier/sous-dossier-1/."
"…/dossier/sous-dossier-1/.."
"…/dossier/sous-dossier-1/fichier1.txt"
"…/dossier/sous-dossier-1/fichier2.txt"
"…/dossier/sous-dossier-2"
"…/dossier/sous-dossier-2/."
"…/dossier/sous-dossier-2/.."
"…/dossier/sous-dossier-2/fichier1.txt"
"…/dossier/sous-dossier-2/fichier2.txt"
"…/dossier/sous-dossier-3"
"…/dossier/sous-dossier-3/."
"…/dossier/sous-dossier-3/.."
"…/dossier/sous-dossier-3/fichier1.txt"
"…/dossier/sous-dossier-3/fichier2.txt"
Seuls les dossiers sont listés.
 
Sélectionnez
QDirIterator it {path, QDir::Dirs,
                       QDirIterator::Subdirectories};
while (it.hasNext()) {
    qDebug() << it.next();
}
 
Sélectionnez
"…/dossier/."
"…/dossier/.."
"…/dossier/sous-dossier"
"…/dossier/sous-dossier/."
"…/dossier/sous-dossier/.."
"…/dossier/sous-dossier/sous-sous-dossier"
"…/dossier/sous-dossier/sous-sous-dossier/."
"…/dossier/sous-dossier/sous-sous-dossier/.."
"…/dossier/sous-dossier-1"
"…/dossier/sous-dossier-1/."
"…/dossier/sous-dossier-1/.."
"…/dossier/sous-dossier-2"
"…/dossier/sous-dossier-2/."
"…/dossier/sous-dossier-2/.."
"…/dossier/sous-dossier-3"
"…/dossier/sous-dossier-3/."
"…/dossier/sous-dossier-3/.."
Les dossiers courants « . » (Dot) et parents « .. » (DotDot) sont filtrés.
 
Sélectionnez
QDirIterator it {path, QDir::Dirs | QDir::NoDotAndDotDot,
                       QDirIterator::Subdirectories};
while (it.hasNext()) {
    qDebug() << it.next();
}
 
Sélectionnez
"…/dossier/sous-dossier"
"…/dossier/sous-dossier/sous-sous-dossier"
"…/dossier/sous-dossier-1"
"…/dossier/sous-dossier-2"
"…/dossier/sous-dossier-3"
Seuls les fichiers sont listés.
 
Sélectionnez
QDirIterator it {path, QDir::Files, 
                       QDirIterator::Subdirectories};
while (it.hasNext()) {
    qDebug() << it.next();
}
 
Sélectionnez
"…/dossier/fichier.txt"
"…/dossier/image-1.png"
"…/dossier/image-2.png"
"…/dossier/image-3.png"
"…/dossier/sous-dossier/sous-sous-dossier/fichier.txt"
"…/dossier/sous-dossier-1/fichier1.txt"
"…/dossier/sous-dossier-1/fichier2.txt"
"…/dossier/sous-dossier-2/fichier1.txt"
"…/dossier/sous-dossier-2/fichier2.txt"
"…/dossier/sous-dossier-3/fichier1.txt"
"…/dossier/sous-dossier-3/fichier2.txt"
Les fichiers sont filtrés par leur nom.
 
Sélectionnez
QDirIterator it {path, {"fichier*.txt"}, 
                       QDir::Files,
                       QDirIterator::Subdirectories};
while (it.hasNext()) {
    qDebug() << it.next();
}
 
Sélectionnez
"…/dossier/fichier.txt"
"…/dossier/sous-dossier/sous-sous-dossier/fichier.txt"
"…/dossier/sous-dossier-1/fichier1.txt"
"…/dossier/sous-dossier-1/fichier2.txt"
"…/dossier/sous-dossier-2/fichier1.txt"
"…/dossier/sous-dossier-2/fichier2.txt"
"…/dossier/sous-dossier-3/fichier1.txt"
"…/dossier/sous-dossier-3/fichier2.txt"
Les fichiers sont filtrés par leur nom et triés selon un leur taille.
 
Sélectionnez
auto imagesList = QDir{path}.entryInfoList(
                               {"image*.png"},
                               QDir::Files,
                               QDir::Size | QDir::Reversed);
for (QFileInfo const & item : imagesList) {
    qDebug() << item.absoluteFilePath() << item.size();
}
 
Sélectionnez
"…/dossier/image-2.png" 6241
"…/dossier/image-1.png" 6249
"…/dossier/image-3.png" 6257

Vous pourrez trouver dans la documentation QDir::SortFlag, QDir::Filter et QDirIterator::IteratorFlag d'autres filtres disponibles venant compléter QDir::Size/Reversed, QDir::Files/Dirs et QDirIterator::Subdirectories utilisés ici.

Lorque vous utilisez QDirIterator, prenez soin d'exécuter en premier lieu la fonction membre QDirIterator::next() afin de vous placer sur le bon élément, avant d'éventuellement utiliser une autre fonction de la classe QDirIterator permettant de récupérer des informations supplémentaires sur cet élément.

Un QDirIterator venant d'être construit ne désigne aucun élément ; il faut d'abord exécuter it.next() pour qu'il se positionne sur le tout premier élément (sous réserve que it.hasNext() soit true et que cet élément existe bien, ou cela n'aura aucun effet).

Notez qu'une ligne telle que qDebug() << it.next() << " - " << it.filePath(); ne garantit pas l'ordre d'exécution des deux instructions.


Rappel : sous Windows n'oubliez pas de doubler le backslash \ qui sépare chacun des dossiers.
Ex. QString const path {"C:\\Users\\Jerome\\dossier"};
Rappel : les dossiers « . » et « .. » représentent respectivement les dossiers courant et parent.
Ex. dossier/. et dossier/sous-dossier/.. représentent tous les deux dossier.
Note : pour alléger la lecture, le chemin menant à ce dossier principal est représenté ici par « … » dans la colonne de sortie.

Créé le 13 décembre 2017  par Winjerome

Avant tout, vous devez distinguer le dossier où se situe l'exécutable et le dossier de travail « working directory ». En effet, le dossier de travail à partir duquel est lancé l'exécutable n'est pas forcément le dossier où se situe ce dernier.

  • Lorsque vous double-cliquez dessus pour le lancer, ou déplacez des fichiers dessus pour les avoir en entrée argv, alors c'est le même ;
  • En ligne de commande si vous vous situez dans un dossier différent pour lancer votre programme, alors le dossier de travail est le dossier dans lequel vous vous trouvez ;
  • Depuis un EDI, le dossier depuis lequel il est lancé est configurable. Par défaut dans Qt Creator, il s'agit du dossier build-[…]-(Debug/Release).

Malheureusement pour vous, si vous lisez ce sujet, c'est que vous avez supposé que ces dossiers étaient les mêmes. Or comme vous venez de le voir, ce n'est pas forcément le cas. Vous devez donc en tenir compte lorsqu'en particulier vous fournissez des chemins relatifs vers vos fichiers, car ceux-ci seront résolus à partir du dossier de travail et non celui où se situe l'exécutable, et ne mèneront donc pas au même dossier selon les cas présentés. Par conséquent, vos fichiers ne seront pas trouvés, ou écrits au mauvais endroit.

Afin de résoudre ce problème de chemin, vous avez diverses solutions.

  • Pour des fichiers de type « ressource » telles que des icônes affichées dans votre application, il est préférable de se tourner vers le système de ressources Qt. Votre exécutable intègrera dès lors les fichiers en son sein et vous n'aurez plus à vous soucier du chemin.

Les fichiers ressource ne sont pas modifiables.

Une solution utilisant la fonction QDir::setCurrent() qui modifie de dossier de travail n'est pas idéale : si un utilisateur passe un chemin relatif à son dossier de travail, votre application ne sera plus en mesure de trouver le fichier ou dossier ciblé.

Durant le développement, afin de placer vos fichiers .dll au bon endroit, il vous est possible de configurer dans Qt Creator le « Working directory » dans les propriétés de votre projet : Image non disponible ⇒ « Run ».

À voir également


À l'aide de QDir::filePath().

Créé le 3 juin 2021  par Winjerome

Rappel

Lorsque vous déclarez une variable dite « locale » sous la forme QWidget widget; au sein d'une fonction ou d'un quelconque bloc {}, celle-ci est détruite dès que l'on en sort. Ainsi le widget suivant :

 
Sélectionnez
void fonction() {
    QWidget widget;
    // ...
    widget.show();
} // widget de suite détruit ici

sera détruit avant même qu'il ait le temps d'être affiché, ou aura à peine été affiché qu'il disparaîtra immédiatement. Et il en sera de même pour tout objet déclaré de la sorte qui aura éventuellement été inséré dedans ou ailleurs. Le widget détruisant ses enfants avec lui, vous risquez même une double destruction selon l'ordre de leur déclaration.

Vous vous posez peut-être la question de la fonction main() où vous avez sûrement utilisé de telles variables locales sans aucun problème…

Ceci est dû au fait que la fonction QApplication::exec() (le return app.exec(); situé à la fin) est bloquante : elle retourne sa valeur seulement lorsque vous fermez votre (dernier) widget. Vous restez donc dans la fonction main() tout le long de l'exécution.

Ouverture d'une boîte de dialogue modale

L'affichage d'une boîte de dialogue modale s'effectuant à l'aide de sa fonction membre exec() (tout aussi bloquante), ceci entre parfaitement dans le cas d'utilisation d'une variable locale. Vous avez juste à la définir en lui passant d'éventuels paramètres par le biais de son constructeur, appeler sa fonction membre exec(), et récupérer les données entrées par l'utilisateur avec une ou plusieurs fonction(s) membre(s) que vous aurez ajouté dans votre classe héritant de QDialog :

 
Sélectionnez
void fonction() {
    MyDialog dialog{/* éventuels paramètres */};
    if (dialog.exec() == QDialog::Accepted) {
        // Récupération des données rentrées…
        // Par exemple avec une fonction membre getData() que vous auriez créé :
        auto data = dialog.getData();
        // …
    }
}

Je vois beaucoup de gens qui utilisent à la place de MyDialog dialog{/* ... */}; un pointeur alloué comme ceci :

 
Sélectionnez
MyDialog * dialog = new MyDialog{this};

et habitués avec Qt à ne pas avoir à faire de delete, négligent de faire un delete dialog; à la fin.

C'est incorrect, et ceci malgré la présence du this ! (Et je ne parle même pas du cas où il n'y est pas…)

Le parent (dans la majorité des cas votre fenêtre principale) ne détruit ses enfants que lorsqu'il est lui-même détruit. Il faudra alors attendre que l'utilisateur ferme l'application pour effectivement détruire les instances hide() jusque-là. Je dis bien « les » car autant de fois ce code sera exécuté, autant de nouvelles instances de MyDialog seront créées, utiliseront inutilement des ressources, et attendront patiemment d'être détruites.

L'utilisation de pointeur n'est ici pas justifiée. L'existence des instances des MyDialog n'a de sens qu'au sein de la fonction. Limitez donc naturellement leur durée de vie à l'intérieur de cette dernière en utilisant une variable « locale » qui est automatiquement détruite en sortant du scope.

Autre ouverture d'une fenêtre non modale

Une fenêtre non modale devra au contraire rester ouverte une fois l'exécution de la fonction qui l'a créée terminée. Pour ce faire, vous devez utiliser un pointeur et l'opérateur new qui permettra d'étendre sa durée de vie jusqu'au delete correspondant :

 
Sélectionnez
void fonction() {
    QWidget * widget = new QWidget{/*...*/};
    // ...
    widget->show();
}

Se pose alors la question de l'emplacement de ce delete… qui va être plus ou moins lié au comportement que vous souhaitez.

  • La nouvelle fenêtre est-elle complètement indépendante de la première ou se ferme-t-elle en même temps que celle-ci ?
  • Si cette fonction est exécutée plusieurs fois, est-ce qu'une nouvelle fenêtre est ouverte à chaque fois ou est-ce que je garde la même ?
  • Que se passe-t-il si la seconde fenêtre est fermée ? Si la première est fermée ? Comment gérer l'éventuel échange de données entre les deux en conséquence ?

Heureusement, Qt vous offre des outils permettant de gérer ces cas :

  • le mécanisme parent/enfant : les enfants sont automatiquement libérés par le parent lorsque ce dernier est détruit ;
  • l'attribut Qt::WA_DeleteOnClose qui permet au widget de s'autodelete lorsqu'il est fermé (évènement closeEvent) ;
  • les pointeurs intelligents QScopedPointer/std::unique_ptr qui, détruits, vont automatiquement delete le pointeur qu'ils gèrent ;
  • les pointeurs intelligents QSharedPointer/std::shared_ptr qui permettent de partager la responsabilité du pointeur entre plusieurs instances ; le dernier détruit étant chargé de delete le pointeur ;
  • QPointer qui permet de vérifer si un objet (dérivant exclusivement de QObject) a été détruit ou non.

À voir également

Mis à jour le 20 novembre 2018  par Winjerome

Sur les forums C++ et particulièrement Qt, je vois passer beaucoup de fichiers d'entête qui contiennent de nombreux #include qui n'ont rien à faire là.

Ces fichiers d'entête n'ont pas pour but d'inclure les dépendances dont vous aurez besoin dans les fichiers source qui les incluent, ce sont les fichiers sources eux-mêmes qui doivent s'occuper de les inclure, car ce sont eux seuls qui en ont réellement besoin.
Les seuls fichiers à inclure dans les fichiers d'entête sont ceux définissant les types utilisés en son sein-même. Et encore, comme indiqué dans ce sujetLe problème d'inclusion circulaire / la « forward declaration », si les bonnes conditions sont réunies, vous pouvez vous contenter de forward declarations.

Mais en quoi est-ce important ?

Lorsque vous incluez tous ces fichiers dans une entête, le compilateur va, lors de la compilation de chaque unité de compilation qui l'inclut, avoir à tous les parcourir inutilement et mettra donc du temps supplémentaire. Alors que placés dans le fichier source, seule l'unité de compilation concernée les aurait parcourus.

Et ce temps additionnel bien que sans doute négligeable sur votre petit projet, ne le sera certainement pas sur un gros projet.

Cette bonne pratique vous évitera également le problème souvent croisé d'inclusion circulaireLe problème d'inclusion circulaire / la « forward declaration ».

Prenons l'exemple suivant(1) :

.h
Sélectionnez
#ifndef MAFENETRE_H
#define MAFENETRE_H
 
#include <QApplication>
#include <QWidget>
#include <QtWidgets>
#include <QPushButton>
#include <QLCDNumber>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "MyLCD.h"
#include "Video.h"
 
class MaFenetre : public QWidget
{
    Q_OBJECT
 
public:
 
    MaFenetre();
 
public slots:
 
    void createbutton();
 
private:
 
    QPushButton *m_minusbutton;
    QPushButton *m_plusbutton;
    QPushButton *m_addbuttonListener;
    QHBoxLayout *m_toplayout;
    QVBoxLayout *m_principallayout;
    QVBoxLayout *m_layout;
    MyLCD *m_lcd;
    Video *m_video;
    int count;
};
 
#endif // MAFENETRE_H
.cpp
Sélectionnez
#include "MaFenetre.h"
#include <iostream>
 
 
using namespace std;
MaFenetre::MaFenetre() : QWidget()
{
    // Construction des boutons
 
    m_minusbutton = new QPushButton("-", this);
    m_lcd = new MyLCD();
    m_plusbutton = new QPushButton("+", this);
    m_addbuttonListener = new QPushButton("add", this);
 
    // Création des layouts
 
    // Layout menu
    m_toplayout = new QHBoxLayout;
    m_toplayout->addWidget(m_minusbutton);
    m_toplayout->addWidget(m_lcd);
    m_toplayout->addWidget(m_plusbutton);
    m_toplayout->addWidget(m_addbuttonListener);
 
    // Layout bouton "video"
 
    m_layout = new QVBoxLayout;
 
    // Layout principal
 
    m_principallayout = new QVBoxLayout;
    m_principallayout->addLayout(m_toplayout);
    m_principallayout->addLayout(m_layout);
 
    setLayout(m_principallayout);
 
    QWidget::connect(m_minusbutton, SIGNAL(clicked()), m_lcd, SLOT(decrement()));
    QWidget::connect(m_plusbutton, SIGNAL(clicked()), m_lcd, SLOT(increment()));
    QWidget::connect(m_addbuttonListener, SIGNAL(clicked()), this, SLOT(createbutton()));
}
 
void MaFenetre::createbutton()
{
    for(int i = count; i < count + m_lcd->value(); i++){
        Video *m_video = new Video(count);
        m_layout->addWidget(m_video);
    }
    count += m_lcd->value();
}
  • #include <QtWidgets> : ce fichier, à ne pas confondre avec <QWidget>, inclut TOUTES les entêtes des classes qui composent le module Qt Widgets. Autant dire que son inclusion et très pénalisante et complètement inutile ;
  • #include <QApplication> : aucun élément qu'il définit n'est utilisé, que ce soit dans le .h ou dans le .cpp. Tout simplement inutile (d'autant que le point précédent l'incluait déjà) ;
  • #include <iostream> dans le fichier source (ainsi que using namespace std;) : idem son contenu n'est pas utilisé, son inclusion est inutile ;
  • #include <QLCDNumber> : aucune utilisation ici non plus, sa place est plutôt dans le fichier MyLCD.h ou le fichier source selon le contenu.

Nous arrivons maintenant à la partie intéressante. En voyant les membres :

 
Sélectionnez
    QPushButton *m_minusbutton;
    QPushButton *m_plusbutton;
    QPushButton *m_addbuttonListener;
    QHBoxLayout *m_toplayout;
    QVBoxLayout *m_principallayout;
    QVBoxLayout *m_layout;
    MyLCD *m_lcd;
    Video *m_video;

vous pourriez vous dire que nous avons besoin d'inclure les éléments suivants :

 
Sélectionnez
#include <QPushButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "MyLCD.h"
#include "Video.h"

Mais aucun de ces fichiers n'a réellement besoin d'être inclus. Vu que nos membres sont des pointeurs (à nouveau, je vous renvoie vers ce sujetLe problème d'inclusion circulaire / la « forward declaration »), nous pouvons économiser ces inclusions et seulement forward-déclarer ces classes dans le .h ; puis inclure ces fichiers dans le .cpp où là seulement on aura besoin de leur définition :

.h
Sélectionnez
class QPushButton;
class QHBoxLayout;
class QVBoxLayout;
class MyLCD;
class Video;
.cpp
Sélectionnez
#include <QPushButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "MyLCD.h"
#include "Video.h"

Pour finir, le seul fichier nécessaire, car la classe ici définie en hérite : #include <QWidget>.

Les deux fichiers deviennent alors :

.h
Sélectionnez
#ifndef MAFENETRE_H
#define MAFENETRE_H
 
#include <QWidget>
 
class QPushButton;
class QHBoxLayout;
class QVBoxLayout;
class MyLCD;
class Video;

class MaFenetre : public QWidget
{
    // définition de la classe
};
 
#endif // MAFENETRE_H
.cpp
Sélectionnez
#include "MaFenetre.h"
#include <QPushButton>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "MyLCD.h"
#include "Video.h"
 
// reste du code

Notez que les fichiers tels que :

 
Sélectionnez
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

sont des entêtes du langage C, pas du C++. En C++ on utilise ceux avec un « c » devant et sans extension :

 
Sélectionnez
#include <cstdio>
#include <cstdlib>
#include <cmath>

(Les deux premiers étant souvent inclus inutilement en C++.)

À voir également



Créé le 13 décembre 2017  par Winjerome

Il n'existe actuellement aucun signal relatif à cet évènement. Cependant, il existe la fonction membre virtuelle (protected:) suivante :

 
Sélectionnez
void QWidget::closeEvent(QCloseEvent *event)

que vous pouvez redéfinir pour faire ce que vous souhaitez, notamment :

  • confirmer la fermeture et effectuer une éventuelle action le cas échéant :

     
    Sélectionnez
    class VotreWidget : public LeWidgetDontVousHeritez {
    protected:
        void closeEvent(QCloseEvent *event) override {
            if (QMessageBox::question(this, "Fermeture", "Voulez-vous vraiment fermer cette fenêtre ?") == QMessageBox::Yes) {
                // votre action
                event->accept(); // on accepte la fermeture, le widget sera bien fermé
            } else {
                event->ignore(); // on refuse la fermeture, le widget reste affiché
            }
        }
    };
  • émettre un signal que vous pourrez connecter (en ajoutant au besoin les éléments du point précédent) :

     
    Sélectionnez
    class VotreWidget : public LeWidgetDontVousHeritez {
        Q_OBJECT
    signals:
        void closed();
    protected:
        void closeEvent(QCloseEvent *event) override {
            emit closed(); // on émet le signal closed()
            event->accept();
        }
    };
     
    Sélectionnez
    VotreWidget *widget = new VotreWidget(/* … */);
    connect(widget, &VotreWidget::closed, /* … */);

    Rappel : un signal ne doit pas être implémenté. Seule sa déclaration doit figurer dans le .h.

Vous pourriez être tenté d'utiliser le signal void destroyed(QObject *) de la classe QObject, mais attention :

  • ce signal est émis dans le destructeur de QObject. Autrement dit le destructeur du widget a déjà été appelé, vous ne manipulez plus qu'un QObject ;
  • un widget qui est close() (par le code ou la croix rouge) n'est détruit que si vous avez passé le flag Qt::WA_DeleteOnClose. Dans le cas contraire, il est juste caché (hide()), l'objet existe toujours et ce signal n'est pas émis ;
  • à ce niveau vous ne pouvez plus éviter la fermeture du widget, contrairement à closeEvent().
Créé le 27 juin 2018  par Winjerome
précédentsommairesuivant
 
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2020 Winjerome. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.