I. L'article original

Qt Quarterly est une revue trimestrielle électronique proposée par Qt à destination des développeurs et utilisateurs de Qt. Vous pouvez trouver les versions originales.

Nokia, Qt, Qt Quarterly et leurs logos sont des marques déposées de Nokia Corporation en Finlande et/ou dans les autres pays. Les autres marques déposées sont détenues par leurs propriétaires respectifs.

Cet article est la traduction de l'article Back in Form with QFormLayout de Jasmin Blanchette paru dans la Qt Quarterly Issue 25.

Cet article est une traduction de l'un des tutoriels en anglais écrits par Nokia Corporation and/or its subsidiary(-ies), inclus dans la documentation de Qt. Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia.

II. Mise en route

L'existence de QFormLayout débuta dans Qtopia (Qtopia était la version mobile de Qt il y a quelques années), lorsque sa raison d'être était de supporter les rangées de lignes pour accueillir les petits écrans de la plupart des téléphones portables. Cependant, le besoin des formulaires à deux colonnes est aussi tombé sur les ordinateurs de bureau, surtout quand les plateformes et les « look-and-feel » sont différents.

Voici un exemple des boîtes de dialogue sous Windows et Mac OS X vers lesquelles on veut s'approcher.

Image non disponible
Boîte de dialogue Windows.
Image non disponible
Boîte de dialogue Mac OS X.

Pour les développeurs, QFormLayout est une bonne nouvelle pour une autre raison. Certains écrivent du code comme celui-ci :

 
Sélectionnez
    nameLabel = new QLabel(tr("&Name:"));
    nameLabel->setBuddy(nameLineEdit);
    
    emailLabel = new QLabel(tr("&Name:"));
    emailLabel->setBuddy(emailLineEdit);
    
    ageLabel = new QLabel(tr("&Name:"));
    ageLabel->setBuddy(ageSpinBox);
    
    QGridLayout *gridLayout = new QGridLayout;
    gridLayout->addWidget(nameLabel, 0, 0);
    gridLayout->addWidget(nameLineEdit, 0, 1);
    gridLayout->addWidget(emailLabel, 1, 0);
    gridLayout->addWidget(emailLineEdit, 1, 1);
    gridLayout->addWidget(ageLabel, 2, 0);
    gridLayout->addWidget(ageSpinBox, 2, 1);
    setLayout(gridLayout);

Au lieu de ces quatorze lignes de codes (plus trois lignes vides), il est possible d'écrire les cinq lignes suivantes :

 
Sélectionnez
    QFormLayout *formLayout = new QFormLayout;
    formLayout->addRow(tr("&Name:"), nameLineEdit);
    formLayout->addRow(tr("&Email:"), emailLineEdit);
    formLayout->addRow(tr("&Age:"), ageSpinBox);
    setLayout(formLayout);

Dans cette version exploitant QFormLayout, chaque appel à addRow() crée un QLabel à côté des champs et paramètre son voisin.

Conceptuellement, un QFormLayout est composé de lignes, créées en utilisant addRow() ou insertRow(). Chacune se compose d'un libellé et d'un champ, les deux étant des QWidget. Si une chaîne QString est passée pour un libellé, un QLabel est créé, comme dans l'exemple précédent. Il est aussi possible d'ajouter des éléments qui contiennent déjà deux widgets en appelant addRow(QWidget~*).

QFormLayout dispose des propriétés suivantes pour personnaliser le « look and feel » :
  • alignement du libellé : détermine si les libellés dans la colonne de gauche sont alignés à gauche ou à droite. Windows et GNOME alignent les libellés à gauche ; Mac OS X et KDE, à droite ;
  • politique de la ligne. QFormLayout supporte trois types de politiques : « emboîter les longues lignes », « emboîter toutes les lignes » et « ne pas emboîter les lignes ». L'option « ne pas emboîter les lignes » (par défaut pour toutes les plateformes) permet d'indiquer que les libellés sont toujours disposés à gauche des champs d'édition ;
  • politique d'agrandissement des lignes : sur Windows, le widget représentant le champ s'agrandit pour prendre l'espace disponible, alors que, sous Mac OS X, ils tendent de rester à une taille fixe ;
  • alignement du formulaire : sur Mac OS X, le formulaire doit être centré horizontalement dans la fenêtre si un espace suffisant est disponible.

Pour toutes ses propriétés, QFormLayout interroge QStyle pour obtenir les valeurs par défaut (on peut les modifier au besoin).

III. Exemple : formulaire de rapport de bogue

Au travers d'un exemple complet, on montrera comment utiliser QFormLayout pour placer ces widgets. La boîte de dialogue est un formulaire de rapport de bogue affiché ci-dessous sur quatre plateformes différentes :

Image non disponible
Formulaire de rapport de bogue sous Windows.
Image non disponible
Formulaire de rapport de bogue sous Mac OS X.
Image non disponible
Formulaire de rapport de bogue sous KDE.
Image non disponible
Formulaire de rapport de bogue sous GNOME.

Voici la déclaration de la classe :

 
Sélectionnez
class BugForm : public QDialog
{
        Q_OBJECT
    
    public:
        BugForm(QWidget *parent = 0);
    
    private slots:
        void reset();
    
    private:
        QLineEdit *nameEdit;
        QLineEdit *companyEdit;
        QLineEdit *phoneEdit;
        QLineEdit *emailEdit;
        QLineEdit *problemEdit;
        QTextEdit *summaryEdit;
        QComboBox *reproducibilityCombo;
        QDialogButtonBox *buttonBox;
};

Jusqu'ici, rien de spécial. Voici la première partie du constructeur :

 
Sélectionnez
BugForm::BugForm(QWidget *parent)
        : QDialog(parent)
    {
        nameEdit = new QLineEdit;
        companyEdit = new QLineEdit;
        phoneEdit = new QLineEdit;
        emailEdit = new QLineEdit;
        problemEdit = new QLineEdit;
        summaryEdit = new QTextEdit;
    
        reproducibilityCombo = new QComboBox;
        reproducibilityCombo->addItem(tr("Always"));
        reproducibilityCombo->addItem(tr("Sometimes"));
        reproducibilityCombo->addItem(tr("Rarely"));
    
        buttonBox = new QDialogButtonBox;
        buttonBox->addButton(tr("Submit Bug Report"),
                             QDialogButtonBox::AcceptRole);
        buttonBox->addButton(tr("Don't Submit"),
                             QDialogButtonBox::RejectRole);
        buttonBox->addButton(QDialogButtonBox::Reset);
    
        connect(buttonBox, SIGNAL(accepted()),
                this, SLOT(accept()));
        connect(buttonBox, SIGNAL(rejected()),
                this, SLOT(reject()));
        connect(buttonBox->button(QDialogButtonBox::Reset),
                SIGNAL(clicked()), this, SLOT(reset()));

Le code suivant crée plusieurs widgets d'édition pour le formulaire avec une QDialogButtonBox avec trois boutons.

 
Sélectionnez
QFormLayout *layout = new QFormLayout;
        layout->addRow(tr("&Name:"), nameEdit);
        layout->addRow(tr("&Company:"), companyEdit);
        layout->addRow(tr("&Phone:"), phoneEdit);
        layout->addRow(tr("&Email:"), emailEdit);
        layout->addRow(tr("Problem &Title:"), problemEdit);
        layout->addRow(tr("&Summary Information:"),
                       summaryEdit);
        layout->addRow(tr("&Reproducibility:"),
                       reproducibilityCombo);
    
        QVBoxLayout *mainLayout = new QVBoxLayout;
        mainLayout->addLayout(layout);
        mainLayout->addWidget(buttonBox);
        setLayout(mainLayout);
    
        setWindowTitle(tr("Report Bug"));
    }

Une QFormLayout est ensuite instanciée et on la peuple avec les lignes, une par widget d'édition. Ensuite, on insère le QFormLayout dans une QVBoxLayout qui contient aussi la QDialogButtonBox.

Finalement, le bouton " Reset " est connecté sur le slot reset(), qui met à zéro tous les champs d'édition.

Le formulaire de rapport de bogue présenté ci-dessus peut aussi être conçu avec Qt Designer, en glissant une QFormLayout sur le formulaire et ensuite les widgets enfants sur celui-ci, comme on pourrait le faire avec une QGridLayout.

Image non disponible

Pour porter des fichiers .ui existants avec QFormLayout, il faut simplement modifier le fichier .ui et remplacer QGridLayout par QFormLayout, puisque que les deux ont le même format de gestion.

VII. Conclusion

Qt est excellent pour apporter les mécanismes fondamentaux nécessaires pour développer des applications multiplateformes performantes. Comme cet article l'a montré, il est possible d'apporter une API de haut niveau qui encapsule les spécificités de chaque plateforme. Un exemple est QDialogButtonBox, introduit dans Qt 4.2 avec un haut niveau d'abstraction pour une QHBoxLayout contenant des boutons, prenant place dans l'ordre indiqué par les lignes directrices de la plateforme. QFormLayout suit cette direction et on peut espérer qu'à l'avenir plus de classes permettant d'améliorer les performances et la lisibilité du code verront le jour.

J'adresse ici de chaleureux remerciements à beaucoup de monde, en particulier à Thibaut Cuvelier et Claude Leloup pour leur relecture et leurs conseils avisés.

Au nom de toute l'équipe Qt, j'aimerais adresser le plus grand remerciement à Nokia pour nous avoir autorisés à traduire cet article !