mercredi 30 novembre 2011

Changer l'orientation de l'écran "Portrait" / "Paysage" en QML

Aujourd'hui je vais vous montrer comment forcer un smartphone à basculer d'un mode d'orientation à un autre. Cela est très utile par exemple pour afficher un lecteur video en pleine page en mode paysage et revenir en mode portrait lorsque la vidéo est terminée.

Dans la mesure où il n'existe pas à l'heure actuelle d'éléments QML permettant de basculer d'un mode d'orientation à un autre, je propose d'exploiter le code Qt existant généré par le Wizard Qt Quick. En effet vous avez sans doute remarqué que le wizard générait une classe Qt dénommée QmlApplicationViewer. Cette classe dispose d'une méthode appropriée "setOrientation" dont l'implémentation consiste à jouer sur les attributs du QWidget pour changer l'orientation.

C'est exactement ce dont nous avons besoin : il faut donc rendre cette méthode "appelable" ("invokable" en anglais) depuis QML ; ensuite nous aurons qu'à déclarer une instance d'objet QmlApplicationViewer dans le contexte déclaratif QML.

Commençons par éditer le fichier qmlapplicationviewer.h afin de modifier la signature de la méthode "setOrientation" :

class QmlApplicationViewer : public QDeclarativeView
{
    Q_OBJECT

 
       Q_ENUMS(ScreenOrientation)


public:
    enum ScreenOrientation {
        ScreenOrientationLockPortrait,
        ScreenOrientationLockLandscape,
        ScreenOrientationAuto
    };

    explicit QmlApplicationViewer(QWidget *parent = 0);
    virtual ~QmlApplicationViewer();

    static QmlApplicationViewer *create();

    void setMainQmlFile(const QString &file);
    void addImportPath(const QString &path);

    
        // Note that this will only have an effect on Symbian and Fremantle.
    Q_INVOKABLE void setOrientation(ScreenOrientation orientation);


    void showExpanded();

private:
    explicit QmlApplicationViewer(QDeclarativeView *view, QWidget *parent);
    class QmlApplicationViewerPrivate *d;
};

QApplication *createApplication(int &argc, char **argv);

Vous remarquerez églalement que j'ai déclaré le type énuméré "ScreenOrientation"  comme exportable via le mot clef Q_ENUMS. Cela permettra d'utiliser cette enumération depuis QML comme paramètre lors de l'invocation de la méthode setOrientation.

Ensuite il faut éditer le contenu de la fonction main() afin :
  • de déclarer l'objet QmlViewerApplication dans le contexte QML
  • de déclarer le type énuméré "ScreenOrientation"
// Instantiation de l'objet QmlApplicationViewer
QmlApplicationViewer viewer;

viewer.setOrientation(QmlApplicationViewer::ScreenOrientationLockPortrait);


// Récupération du contexte déclaratif QML
QDeclarativeContext* context = viewer.rootContext();

// Exposition de l'objet QmlApplicationViewer afin de le rendre accessible en QML 
context->setContextProperty("appviewer", &viewer);

// Enregistrement du type QmlApplicationViewer afin d'utiliser ce type en QML

qmlRegisterType<QmlApplicationViewer>("AppViewer", 1, 0, "QmlApplicationViewer");


Et c'est tout pour la partie Qt C++. Maintenant il s'agit d'utiliser la méthode QmlApplicationViewer::setOrientation depuis QML. J'ai donc créé une interface QML toute simple comme l'atteste ces deux captures d'écran :


Mode portrait

Mode paysage

Vous avez sans doute compris le fonctionnement : le bouton permet de changer le mode d'orientation. Pour cela j'ai défini un type QML "Bouton" comme suit :

import QtQuick 1.0


Rectangle {

    property alias text: messageText.text
    property alias posY: textBox.y

    signal pressed
    property color onButtonPressed: "grey"
    property color onButtonReleased: "lightgrey"

    id: textBox
    color: "lightgrey"
    width: messageText.width+20; height: messageText.height+10
    border.width: 1
    border.color: "black"
    radius: 10
    smooth: true
    anchors.horizontalCenter: parent.horizontalCenter



    Text {
        id: messageText
        text: "Default"
        anchors.centerIn: parent
        font.pointSize: 12; font.bold: true; font.family: "Helvetica"
    }



    MouseArea {
        anchors.fill: parent
        onPressed: parent.color = parent.onButtonPressed
        onReleased: parent.color = parent.onButtonReleased
        onClicked: textBox.pressed()
    }
 }

Et j'ai créé une page principale utilisant ce bouton :

import QtQuick 1.0
import AppViewer 1.0


Rectangle {
    width: 360
    height: 640

    gradient:
        Gradient {
            GradientStop {
                position: 0
                color: "#ccc094"
            }

            GradientStop {
                position: 0.8
                color: "#777057"
            }

            GradientStop {
                position: 1
                color: "#000000"
            }
        }

    Button
    {
        id: myButton
        text: ""
        posY:100

        onPressed: {
            if(state == "portrait")
            {
                appviewer.setOrientation(QmlApplicationViewer.ScreenOrientationLockLandscape)
                state = "landscape"
            }
            else if(state == "landscape")
            {
                appviewer.setOrientation(QmlApplicationViewer.ScreenOrientationLockPortrait)
                state = "portrait"
            }
        }

        state: "portrait"
        states:[
            State{
                name: "portrait"
                PropertyChanges {
                    target: myButton
                    text: "Switch to landscape"
                }
            },
            State{
                name: "landscape"
                PropertyChanges {
                    target: myButton
                    text: "Switch to portrait"
                }
            }
        ]
    }
}

Les étapes importantes du listing ci-dessus sont les suivantes :
  • Importer le module "AppViewer" afin de pouvoir accéder à l'objet du même nom
  • Déclaration d'un bouton "Button" avec deux états : l'un gérant l'affichage en mode portrait, l'autre gérant l'affichage en mode paysage
  • Appeler la méthode appviewer.setOrientation lorsque l'utilisateur clique sur le bouton avec un paramètre dont la valeur dépend de l'état courant.
Et voilà. Le code source est diponible ici

Aucun commentaire:

Enregistrer un commentaire