IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
FAQ Qt FAQ Qt Creator FAQ PyQt & PySide

FAQ Qt

FAQ QtConsultez toutes les FAQ

Nombre d'auteurs : 26, nombre de questions : 298, dernière mise à jour : 15 juin 2021 

 
OuvrirSommaireModulesQt Quick 1

Définir une propriété par défaut permet de déterminer la source de la parenté des éléments enfants. En C++, il est possible de définir une propriété par défaut grâce à la macro Q_CLASSINFO(Name, Value) avec pour Name "DefaultProperty" et Value une propriété de type QDeclarativeListProperty.

Voici un exemple commenté illustrant cela :

 
Sélectionnez
class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QDeclarativeListProperty<QObject> objects READ objects)
    Q_CLASSINFO("DefaultProperty", "objects")
 
public:
    MyClass() { }
 
    QDeclarativeListProperty<QObject> objects() {
        // On utilise le constructeur QDeclarativeListProperty::QDeclarativeListProperty
        // (QObject *object, void *data, AppendFunction append, CountFunction count = 0,
        // AtFunction at = 0, ClearFunction clear = 0) dans le but de spécifier des
        // fonctions permettant d'effectuer des opérations :
        return QDeclarativeListProperty<QObject>(this, 0, &MyClass::appendObjects,
            &MyClass::getObjectsCount, &MyClass::objectAt, &MyClass::clearObjects);
    }
 
private:
    static void appendObjects(QDeclarativeListProperty<QObject> *list, QObject *obj) {
        // Du fait que l'on a affaire à une fonction statique, on ne peut pas tout
        // simplement faire _objects.append(obj). Les opérations suivantes sont donc
        // nécessaires.
 
        // On récupère l'objet de MyClass depuis list avec un qobject_cast :
        MyClass *objects = qobject_cast<MyClass *>(list->object);
        // Si le pointeur objects n'est pas NULL :
        if (objects) {
            // On définit le parent du QObject* à ajouter à la liste :
            obj->setParent(objects);
            // Et on l'ajoute à la liste :
            objects->_objects.append(obj);
        }
    }
 
    static int getObjectsCount(QDeclarativeListProperty<QObject> *list) {
        // Le processus est identique à celui de la fonction appendObjects :
 
        // On récupère l'objet de MyClass depuis list avec un qobject_cast :
        MyClass *objects = qobject_cast<MyClass *>(list->object);
        // La valeur de retour, 0 si objects est NULL, sinon le nombre
        // d'objets dans la liste :
        return (objects) ? objects->_objects.count() : 0;
    }
 
    static QObject *objectAt(QDeclarativeListProperty<QObject> *list, int index) {
        // Le processus est identique à celui de la fonction appendObjects :
 
        // On récupère l'objet de MyClass depuis list avec un qobject_cast :
        MyClass *objects = qobject_cast<MyClass *>(list->object);
        // La valeur de retour, NULL si objects est NULL, sinon l'objet à
        // la position indiquée par index :
        return (objects) ? objects->_objects.at(index) : NULL;
    }
 
    static void clearObjects(QDeclarativeListProperty<QObject> *list) {
        // Le processus est identique à celui de la fonction appendObjects :
 
        // On récupère l'objet de MyClass depuis list avec un qobject_cast :
        MyClass *objects = qobject_cast<MyClass *>(list->object);
        // Si objects n'est pas NULL :
        if (objects)
            // On vide la liste :
            objects->_objects.clear();
    }
 
    QList<QObject *> _objects;
};

Dans QDeclarativeListProperty<QObject> objects(), on a utilisé :

 
Sélectionnez
QDeclarativeListProperty::QDeclarativeListProperty(QObject *object, void *data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0)

Par le biais de ce constructeur, on a défini des pointeurs sur fonctions permettant d'effectuer les opérations append, count, at et clear sur la liste objects.

Dans le main.cpp, on déclare le type MyObject avec qmlRegisterType() :

 
Sélectionnez
qmlRegisterType<MyClass>("MyClass", 1, 0, "MyClass");

Et enfin, on utilise la classe dans un fichier QML :

 
Sélectionnez
import QtQuick 1.0
import MyClass 1.0
 
Item {
    id: main
    width: 400
    height: 200
 
    Item { id: it }
 
    MyClass {
        Item { objectName: "something" }
        Item { objectName: "anotherthing" }
 
        Component.onCompleted: {
            console.log(objects[0].objectName)
        }
    }
}

Le log indique ceci :

 
Sélectionnez
something

Si l'on n'avait pas défini la propriété objects comme la propriété par défaut de la classe MyClass, on aurait dû écrire le code précédent comme ceci pour instaurer la parenté de manière identique :

 
Sélectionnez
import QtQuick 1.0
import MyClass 1.0
 
Item {
    id: main
    width: 400
    height: 200
 
    Item { id: it }
 
    MyClass {
        objects: [
            Item { objectName: "something" },
            Item { objectName: "anotherthing" }
        ]
 
        Component.onCompleted: {
            console.log(objects[0].objectName)
        }
    }
}
Créé le 7 mai 2012  par Louis du Verdier

Le fait d'utiliser l'attribut "default" dans la déclaration d'une propriété marque celle-ci comme étant la propriété par défaut de son type. C'est un facteur déterminant la source de la parenté des éléments enfants.

Par exemple :

 
Sélectionnez
// Fichier Compo.qml
 
import QtQuick 1.0
 
Item {
    id: item
    objectName: "item"
 
    default property alias content: stackItem.children
 
    Item {
        id: stackItem
        objectName: "stackItem"
        anchors.fill: parent
    }
}

Dans le fichier Compo.qml, on a spécifié une propriété par défaut sur un élément Item à l'Id stackItem. Voici un fichier main.qml qui utilise ce composant en lui insérant d'autres éléments :

 
Sélectionnez
// main.qml
 
import QtQuick 1.0
 
Item {
    id: main
    width: 400
    height: 200
 
    Compo {
        anchors.fill: parent
 
        Rectangle { id: rect }
        Item { id: it }
 
        Component.onCompleted: {
            console.log(rect.parent.objectName)
            console.log(it.parent.objectName)
        }
    }
}

La parenté est ici déterminée par la propriété par défaut. Avec celle-ci, on obtient les logs suivants :

 
Sélectionnez
stackItem
 stackItem

Sans, on obtient ceux-ci :

 
Sélectionnez
item
 item

Cela signifie que, dans le main.qml, les éléments définis à l'intérieur de l'élément Compo (c'est-à-dire, le Rectangle et l'Item) ont été ajoutés dans la liste "children" de l'élément stackItem, d'où la parenté.

Créé le 7 mai 2012  par Louis du Verdier

Il est assez fréquent que l'on ait besoin d'écrire des listes d'éléments pour pouvoir les manipuler par la suite. Une première approche envisageable est l'utilisation d'un ListModel dans le but d'y stocker les valeurs permettant de recréer par la suite les éléments :

 
Sélectionnez
ListModel {
    id: mymodel
    ListElement { contentText: "Quelque chose"; bold: true }
    ListElement { contentText: "Autre chose"; bold: false }
}
 
ListView {
    model: mymodel
    delegate : Text { text: contentText; font.bold: bold }
    anchors.fill: parent
}
 
Component.onCompleted: {
    console.log(mymodel.get(0).contentText);
}

Toutefois, cette solution peut souvent s'avérer lourde. Une deuxième solution est celle fournie par VisualItemModel :

 
Sélectionnez
Item {
    id: main
    width: 400
    height: 200
 
    VisualItemModel {
        id: mymodel
        Text { text: "Quelque chose"; font.bold: true }
        Text { text: "Autre chose"; font.bold: false }
    }
 
    ListView {
        anchors.fill: parent
        model: mymodel
    }
}

Une troisième solution est d'utiliser le type list. La syntaxe de déclaration d'une propriété de type list est la suivante :

 
Sélectionnez
property list<Type> listName

Un exemple d'utilisation :

 
Sélectionnez
Item {
    id: main
    width: 400
    height: 200
 
    property list<Text> mylist
 
    mylist: [
        Text { text: "Quelque chose"; font.bold: true },
        Text { text: "Autre chose"; font.bold: false }
    ]
 
    Repeater {
        // ...
        model: mylist
        delegate: // ce que l'on veut en utilisant mylist[index]
    }
 
    Component.onCompleted: {
        console.log(mylist[0].text);
    }
}

La solution de list<T> est assez pratique pour les manipulations ne mettant pas en jeu des éléments graphiques. L'utiliser pour afficher des éléments à l'écran n'est pas une bonne solution. Ainsi, pour définir une liste de chaines de caractères, utiliser list<string> sera très pratique. Pour définir une liste d'éléments Text, on préfèrera la plupart du temps les VisualItemModels.

Créé le 7 mai 2012  par Louis du Verdier

Pour définir une police à un élément textuel possédant la propriété font, il existe deux moyens : définir directement le nom d'une police installée sur le système avec font.family ou bien utiliser un élément FontLoader pour utiliser une police située à un emplacement donné, que ce soit en local ou sur Internet :

 
Sélectionnez
Item {
    id: main
    width: 400; height: 200
 
    Text {
        id: text
        font.family: "Times"
        text: "Texte"
    }
 
    FontLoader {
        id: symbolLoader
        name: "Symbol"
    }
 
    FontLoader {
        id: externalLoader
        source: "qrc:/fonts/font.ttf"
    }
 
    MouseArea {
        anchors.fill: parent
        onClicked: {
            switch (text.font.family) {
            case "Times":
                text.font.family = symbolLoader.name;
                break;
            case symbolLoader.name:
                text.font.family = externalLoader.name;
                break;
            case externalLoader.name:
                text.font.family = "Times";
                break;
            }
        }
    }
}

Il est également fréquent que l'on ait besoin de spécifier une police de remplacement, dans le cas où la police ne serait pas disponible. Pour cela, il suffit de spécifier son nom dans la propriété font.family de l'élément textuel :

 
Sélectionnez
Text {
    id: text
    font.family: "Times, Symbol, " + externalLoader.name
    text: "Police " + font.family
}
 
FontLoader {
    id: externalLoader
    source: "qrc:/fonts/font.ttf"
}

Dans l'exemple ci-dessus, la police Times sera utilisée. Si elle n'est pas disponible, ce sera la police Symbol. Si cette dernière n'est également pas disponible, ce sera la police chargée avec l'élément FontLoader.

Créé le 7 mai 2012  par Louis du Verdier

Lorsque l'on souhaite afficher une ressource volumineuse n'étant pas située en local (par exemple, une image), on aime assez pouvoir la mettre en cache pour éviter qu'il y ait lieu de multiples téléchargements ralentissant l'application.

Lorsque QML tente d'accéder à une ressource en provenance de l'extérieur, il utilise la classe QNetworkAccessManager. Par le biais de la fonction setNetworkAccessManagerFactory() de QDeclarativeEngine, qui prend pour argument une classe dérivée de QDeclarativeNetworkAccessManagerFactory, il est possible d'implémenter une "usine" pour créer des QNetworkAccessManager personnalisés afin de profiter d'un système de cache orchestré par une instance de QNetworkDiskCache, définie au QNetworkAccessManager grâce à sa méthode setCache().

Pour mettre en place un cache, il s'agit donc de créer une classe dérivée de QDeclarativeNetworkAccessManagerFactory et d'implémenter sa fonction create(), comme ceci :

 
Sélectionnez
class NetworkAccessManagerFactory : public QDeclarativeNetworkAccessManagerFactory
{
public:
    virtual QNetworkAccessManager *create(QObject *parent)
    {
        // Création d'une instance de QNetworkAccessManager :
        QNetworkAccessManager *manager = new QNetworkAccessManager(parent);
        // Création d'une instance de QNetworkDiskCache :
        QNetworkDiskCache *cache = new QNetworkDiskCache(manager);
        // L'emplacement où les fichiers de cache seront écrits :
        cache->setCacheDirectory(QDesktopServices::storageLocation(QDesktopServices::CacheLocation));
        // Le cache accepte jusqu'à 10Mo :
        cache->setMaximumCacheSize(10 * 1024 * 1024);
        // On définit le cache du manager à notre objet cache :
        manager->setCache(cache);
        // On retourne le manager prêt à l'usage :
        return manager;
    }
};

Ce code nécessite la présence de la ligne suivante dans le fichier de projet :

 
Sélectionnez
QT += network

Une fois l'usine préparée, il s'agit de faire en sorte que le QDeclarativeEngine l'utilise :

 
Sélectionnez
QDeclarativeView view;
view.engine()->setNetworkAccessManagerFactory(new NetworkAccessManagerFactory);
view.setSource(QUrl("qml/main.qml"));
view.show();

Et dans le fichier QML, la façon de coder reste inchangée :

 
Sélectionnez
import QtQuick 1.0
 
Item {
    id: main
    width: 400
    height: 200
 
    Image {
        anchors.centerIn: parent
        source: "http://www.example.com/image.png"
    }
}

Lors de la première exécution de l'application, l'image est chargée depuis l'extérieur. Lors des lancements suivants, elle ne charge plus l'image depuis l'url spécifiée mais depuis le cache.

Créé le 7 mai 2012  par Louis du Verdier

Dans QML, sur les périphériques Symbian, les événements des touches de volume ne sont pas délivrés par Keys.onVolumeDownPressed, Keys.onVolumeUpPressed ou Keys.onPressed. Une solution est d'utiliser la S60 Remote Control API. Voici un exemple de code pour le faire avec QML et C++.

On crée un nouvel élément QML pour délivrer l'événement voulu. L'API peut en réalité recevoir d'autres événements de touches multimédias. Voir le lien ci-dessus pour plus de détails.

MediakeyCaptureItem.h
Sélectionnez
#ifndef MediakeyCaptureItem_H
#define MediakeyCaptureItem_H
 
#include <QDeclarativeItem>
 
#ifdef Q_WS_S60
#include <remconcoreapitargetobserver.h>
#include <remconcoreapitarget.h>
#include <remconinterfaceselector.h>
 
class MediakeyCaptureItemPrivate;
class MediakeyCaptureItem : public QDeclarativeItem
{
    Q_OBJECT
public:
    MediakeyCaptureItem(QDeclarativeItem *parent = 0);
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
 
signals:
    void volumeDownPressed();
    void volumeUpPressed();
 
private:
    MediakeyCaptureItemPrivate *d_ptr;
private:    // Friend class definitions
    friend class MediakeyCaptureItemPrivate;
};
 
#endif // Q_WS_S60
#endif // MediakeyCaptureItem_H
MediakeyCaptureItem.cpp
Sélectionnez
#include "MediaKeyCaptureItem.h"
#ifdef Q_WS_S60
 
// A private class to access Symbian RemCon API
class MediakeyCaptureItemPrivate : public QObject, public MRemConCoreApiTargetObserver
{
public:
    MediakeyCaptureItemPrivate(MediakeyCaptureItem *parent);
    ~MediakeyCaptureItemPrivate();
    virtual void MrccatoCommand(TRemConCoreApiOperationId aOperationId,
                                            TRemConCoreApiButtonAction aButtonAct);
private:
    CRemConInterfaceSelector* iInterfaceSelector;
    CRemConCoreApiTarget*     iCoreTarget;
    MediakeyCaptureItem *d_ptr;
};
 
// Consructor
MediakeyCaptureItem::MediakeyCaptureItem(QDeclarativeItem *parent): QDeclarativeItem(parent)
{
    d_ptr = new MediakeyCaptureItemPrivate(this);
}
 
// The paint method
void MediakeyCaptureItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // This item has no visual
}
 
// Constructor
MediakeyCaptureItemPrivate::MediakeyCaptureItemPrivate(MediakeyCaptureItem *parent): d_ptr(parent)
{
    QT_TRAP_THROWING(iInterfaceSelector = CRemConInterfaceSelector::NewL());
    QT_TRAP_THROWING(iCoreTarget = CRemConCoreApiTarget::NewL(*iInterfaceSelector, *this));
    iInterfaceSelector->OpenTargetL();
}
 
// Destructor
MediakeyCaptureItemPrivate::~MediakeyCaptureItemPrivate(){
    delete iInterfaceSelector;
    delete  iCoreTarget;
}
 
// Callback when media keys are pressed
void MediakeyCaptureItemPrivate::MrccatoCommand(TRemConCoreApiOperationId aOperationId,
                                        TRemConCoreApiButtonAction aButtonAct)
{
    //TRequestStatus status;
    switch( aOperationId )
    {
    case ERemConCoreApiVolumeUp:
        emit d_ptr->volumeUpPressed();
        break;
    case ERemConCoreApiVolumeDown:
        emit d_ptr->volumeDownPressed();
        break;
    default:
        break;
    }
}
#endif // Q_WS_S60

Ensuite, il faut enregistrer dans le code C++ ce composant :

 
Sélectionnez
qmlRegisterType<MediakeyCaptureItem>("Mediakey", 1, 0, "MediakeyCapture");

Ensuite, il faut ajouter quelques lignes dans le .pro :

 
Sélectionnez
symbian {
    INCLUDEPATH += MW_LAYER_SYSTEMINCLUDE
    LIBS += -L\epoc32\release\armv5\lib -lremconcoreapi
    LIBS += -L\epoc32\release\armv5\lib -lremconinterfacebase
}

En QML, on appelle ce composant comme suit :

 
Sélectionnez
import Qt 4.7
import Mediakey 1.0
Item{
 
    ...
 
    MediakeyCapture{
        onVolumeDownPressed: console.log('VOLUME DOWN PRESSED ')
        onVolumeUpPressed: console.log('VOLUME UP PRESSED ')
    }
}
Créé le 7 mai 2012  par Thibaut Cuvelier

Voici un exemple d'utilisation de Qt Quick et des animations pour, après un clic de l'utilisateur, jouer une série d'événements retardés (comme ouvrir puis fermer une liste). On commence avec un cercle rouge. Quand l'utilisateur clique dessus, il s'anime en un rectangle et déclenche un timer qui, à terme, lance la transformation inverse, du rectangle au cercle.

 
Sélectionnez
import QtQuick 1.0
 
Rectangle {
    property int time: 800
    property int size: 300
    width: size; height: size; radius: size
    color: "red"
    Behavior on radius { NumberAnimation { duration: time } }
    Timer {
        id: reset
        interval: time;
        onTriggered: parent.radius = size
    }
 
    MouseArea {
        anchors.fill: parent
        onClicked: {
            parent.radius = 0;
            reset.start()
        }
    }
}

Si vous vouliez simplement avoir des événements qui se suivent directement, il aurait été possible d'utiliser une SequentialAnimation. Cet exemple, quant à lui, montre des délais arbitraires.

Créé le 7 mai 2012  par Thibaut Cuvelier

Lorsqu'une propriété d'un item est modifiée, un signal xxxChanged est émis, où xxx est le nom de la propriété. On peut donc s' y connecter par onXxxChanged (attention, la première lettre du nom de la propriété passe en majuscule).

 
Sélectionnez
import Qt 4.7
 
//Utilisation d'un rectangle en arrière-plan
Rectangle {
    width: 100; height: 100
 
    //Ajout d'une label
    Text { id: txt; anchors.centerIn : parent; font.pointSize: 24}
 
    //ajout de la  propriété value
    property int value : 0
 
    //code exécuté quand la propriété value change
    onValueChanged : txt.text = '<b>'+value+'</b>'
 
    //Timer qui incrémente la valeur de value toutes les 500 ms
    Timer {
        interval: 500; running: true; repeat: true
        onTriggered: value++
    }
}
Créé le 7 mai 2012  par Yan Verdavaine

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 © 2006 - 2017 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.