Meego Wiki
Views

QML/Sample App Using CPP Models

From MeeGo wiki
Revision as of 21:57, 20 May 2011 by Dlawlor (Talk | contribs)
Jump to: navigation, search

Contents

Overview

This tutorial will demonstrate a simple (but non-trivial) app which uses

  • Meego UX Component widgets
  • C++ models
  • QML List Views
  • QML Components

This tutorial assumes you have the MeeGo SDK installed on Linux
See: http://wiki.meego.com/SDK/Docs/1.2/MeeGo_SDK_1.2_Preview

Also assumed: you have a a chroot / Xephyr environment set up.

This tutorial will use meego-ux-components objects like:

  • Book Menus -
  • Buttons
  • AppPage

For other uses of Meego UX Components, and another tutorial of Book Menus, See:

Other QML Tutorials:

Follow these steps on a chroot / Xephyr system

Start Qt Creator

qtcreator &>/dev/null &

Create a new project

... screen shots ...

The auto-generated QML File looks like this:

import QtQuick 1.0

Rectangle {
    width: 360
    height: 360
    Text {
        text: "Hello World"
        anchors.centerIn: parent
    }
    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }
}

Turn off Shadow Build

Build->Build All

Run

(CTRL-R or Green "Play" Button)

The running app looks like this:

<screen shot>

Clicking it closes the app (Qt.quit())


Add code to main.qml

Replace the contents of main.qml with this:

The last 2 Component statements are commented out so the app will run as is before creating more files.

import Qt 4.7
import MeeGo.Components 0.1

Window {
    id: window

    bookMenuModel: [ qsTr("Button Demo"), qsTr("Book 2") ]
    bookMenuPayload: [ gallery, book2 ]
    bookMenuTitle: qsTr("Book Menu")

    Component.onCompleted: switchBook( gallery )

    //Component { id: gallery; MainPage {} }
    //Component { id: book2; Book2 {} }
}

Test / Run

Run the app and see the Book Menu. If you click on one of the menu items, you'll notice an error because of the commented out Component statements.

Add code for the Book Page example

Adding the Book2 Component

(from the meego-ux-widgetgallery example)

  • (right click on the QML folder in the Projects view and Add New... / QML File / "Book2.qml")
  • Replace the generated file Book2.qml with this:

/*
 * Copyright 2011 Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * LGPL, version 2.1.  The full text of the LGPL Licence is at
 * http://www.gnu.org/licenses/lgpl.html
 */

/* This file contains relativy empty pages and is meant to demonstrate the
   book/page concept */

import Qt 4.7

PageDummy {
    id: pageDummy

    innerText: qsTr("book 2, page 1")
    rectColor: "lightblue"
    showButton: true
    buttonLabel: qsTr("Page 2")
    pageTitle: qsTr("Dummy book 2")

    onClicked: { addPage( page2 ) }

    Component{
        id: page2;

        PageDummy {
            id: pageDummy2

            innerText: qsTr("book 2, page 2")
            rectColor: "lightgreen"
            showButton: true
            buttonLabel: qsTr("Page 3")
            pageTitle: qsTr("Dummy book 2")

            onClicked: { addPage( page3 )
            }
        }
    }

    Component{
        id: page3;

        PageDummy {
            id: pageDummy3

            innerText: qsTr("book 2, page 3")
            rectColor: "orange"
            showButton: true
            buttonLabel: qsTr("Page 4")
            pageTitle: qsTr("Dummy book 2")

            onClicked: { addPage( page4 )
            }
        }
    }

    Component{
        id: page4;

        PageDummy {
            id: pageDummy4

            innerText: qsTr("book 2, page 4")
            rectColor: "darkgrey"
            pageTitle: qsTr("Dummy book 2")
        }
    }
}

Add PageDummy.qml

The file Book2.qml references PageDummy.qml, so create it the same way as Book2.qml

/*
 * Copyright 2011 Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * LGPL, version 2.1.  The full text of the LGPL Licence is at
 * http://www.gnu.org/licenses/lgpl.html
 */

/* This file is just meant as a dummy to quickly create pages for
   demonstrating the book/page concept. */
import Qt 4.7
import MeeGo.Components 0.1

AppPage {
    id: pageDummy

    property alias innerText: rectText.text //text shown in the rect
    property alias rectColor: innerRect.color   //color of the rect in the middle
    property alias showButton: nextButton.visible   //nextButton visible?
    property alias buttonLabel: nextButton.text    //nextButton label
    property string bookTitle: "book title" //shown in the title bar

    signal clicked()

    anchors.fill:  parent

    Rectangle { z: -1; anchors.fill: parent; color: "grey" } //background

    Rectangle {
        id: innerRect

        anchors.fill: parent
        anchors.margins:  50
        color: "lightgreen"

        Text {
            id: rectText

            text: "dummy page"
            anchors.centerIn: parent
            font.pixelSize: 40
            color: "black"
        }
    }

    Button {
        id: nextButton

        visible: false
        text: "next page >"
        anchors.right:  innerRect.right
        anchors.bottom:  innerRect.bottom
        anchors.margins: 10
        onClicked: { pageDummy.clicked() }
    }
}

You will see the use of the meego-ux-components "Button" and "AppPage" components in PageDummy.qml

Now, replace the two commented out Component statements in main.qml with:

    Component {
        id: gallery;
        AppPage {id: dummy2}
    }

    // Dummy page until MainPage.qml is defined
    AppPage { id: gallery; }
    //Component { id: gallery; MainPage {} }

    Component { id: book2; Book2 {} }


Since the MainPage.qml file isn't created yet, we use a dummy AppPage placeholder temporarily to prevent a run-time error when invoking the menu.

Run

You can now click on the Book2 menu and see a new page.

Add code for seeing Custom List Views in the MainPage

We will use a C++ QAbstractListModel to repersent out list.

mymodel.h

Create the file mymodel.h by right-clicking on the simple-app project name and "Add New..." a C++ Header File

Paste this into mymodel.h

#ifndef MYMODEL_H
#define MYMODEL_H

#include <QAbstractListModel>
#include <QColor>

struct Data {
    Data( const char* name, const QString& flag, double population )
        : name(name), flag(flag), population(population) {}
    QString name;
    QString flag;
    double population;
};

const int FlagRole = Qt::UserRole + 1;
const int PopulationRole = Qt::UserRole + 2;
class MyModel : public QAbstractListModel
{
public:
    MyModel();
    int rowCount( const QModelIndex& ) const;
    QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;

    QList< Data > m_data;
};

#endif // MYMODEL_H

Create the mymodel.cpp file with this content:


#include "mymodel.h"
#include <QByteArray>
MyModel::MyModel()
{
    m_data
        << Data("Denmark", "qrc:images/denmark.jpg", 5.4)
        << Data("Sweden", "qrc:images/sweden.jpg", 9.3)
        << Data("Iceland", "qrc:images/iceland.jpg", 3.2)
        << Data("Norway", "qrc:images/norway.jpg", 4.8)
        << Data("Finland", "qrc:images/finland.jpg", 5.3);

    // By default the DisplayRole is mapped to the propery "display"
    QHash<int, QByteArray> mapping =roleNames();
    mapping.insert( FlagRole, "flag");
    mapping.insert( PopulationRole, "population");
    setRoleNames( mapping );
}

int MyModel::rowCount( const QModelIndex& ) const
{
    return m_data.count();
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if ( !index.isValid() )
        return QVariant();

    Data data = m_data[index.row()];
    if ( role == Qt::DisplayRole )
        return data.name;
    else if ( role == FlagRole )
        return data.flag;
    else if ( role == PopulationRole )
        return data.population;
    else
        return QVariant();
}

Modify main.cpp

Modify main.cpp by replacing its contents with this:

#include <QtGui/QApplication>
#include <QDeclarativeEngine>
#include <QDeclarativeContext>
#include "qmlapplicationviewer.h"
#include "mymodel.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyModel model;


    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/simple-app/main.qml"));
    viewer.showExpanded();

    // Register the model "myModel" with QML
    QDeclarativeContext *context = viewer.engine()->rootContext();
    context->setContextProperty("myModel", &model);

    return app.exec();
}


Add the file MainPage.qml

A Component called MainPage.qml which will display row of Buttons that act like Tabs in a QTabView.

The area below the buttons are filled with one of two mini-apps (List View sample, and mini file browser)

/*
 * Copyright 2011 Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * LGPL, version 2.1.  The full text of the LGPL Licence is at
 * http://www.gnu.org/licenses/lgpl.html
 */

/* The MainPage lets the user switch between different contents which
   show the widgets available in these components. */

//import Qt 4.7
import QtQuick 1.0
import MeeGo.Components 0.1

AppPage {
    id: mainPage

    // This will be the first app / content displayed in the Button Demo page
    state: "flags"

    pageTitle: qsTr("Button Demo")


    // Demo an Action Menu......
    // Comment the block below to see a new Action Menu

//    actionMenuModel: [ qsTr("Landscape"),
//                       qsTr("Portrait"),
//                       qsTr("Inv. Landscape"),
//                       qsTr("Inv. Portrait") ]
//    actionMenuPayload: [  1, 2, 3, 4 ]
//    actionMenuTitle: qsTr("Action Menu")

//    onActionMenuTriggered: {

//        if( selectedItem == 1) {
//            window.orientation = 1
//        } else if( selectedItem == 2) {
//            window.orientation = 2
//        } else if( selectedItem == 3) {
//            window.orientation = 3
//        } else if( selectedItem == 4) {
//            window.orientation = 0
//        }
//    }



    Item {
        id: contentButtons


        // Some Debug output to stderr ...
        onVisibleChanged: {
            console.log("xxxxxxxxxxxxxx 1 Parent wid ", parent.width);
            console.log("xxxxxxxxxxxxxx 2 Wid        ", width);
        }
        onWidthChanged: {
            console.log("wwwwwwwwwwwwww 1 Parent wid ", parent.width);
            console.log("wwwwwwwwwwwwww 2 Wid        ", width);
        }


        property int buttonWidth: parent.width * 0.2;
        property int buttonHeight: 60;
        property int buttonMargins: 2;

        property string activeButtonImage: "image://themedimage/widgets/common/button/button-default"
        property string buttonImage: "image://themedimage/widgets/common/button/button"
        property string buttonImagePressed: "image://themedimage/widgets/common/button/button-default-pressed"

        //width: 2 * buttonWidth + 3 * buttonMargins
        height: buttonHeight
        anchors.top: parent.top
        anchors.topMargin:  10
        anchors.horizontalCenter: parent.horizontalCenter


        Button {
            id: flagsButton
            active: true
            width:  parent.buttonWidth; height: parent.buttonHeight

            // Since there are only two buttons, the right of this button is near the center.
            anchors { margins: parent.buttonMargins; right: parent.horizontalCenter;}
            text: qsTr("Flags")

            onClicked: {
                mainPage.state = "flags"
                active = true
                browserButton.active = false
            }
        }


        Button {
            id: browserButton

            width:  parent.buttonWidth; height: parent.buttonHeight

            // This button is to the right of the flags button
            anchors { margins: parent.buttonMargins; left: flagsButton.right }
            text: qsTr("File Browser")

            onClicked: {
                mainPage.state = "browser"
                active = true
                flagsButton.active = false
            }
        }
    }

    // This item holds the contents of the selected button and fills the rest of the window
    Item {
        id: contentSpace

        anchors { top: contentButtons.bottom; bottom: parent.bottom; left: parent.left; right: parent.right }
    }


    // One of these two components will fill the contentSpace Item above.
    FlagContent    { id: flagContent;    anchors.fill: contentSpace; anchors.top: contentButtons.bottom }

    //Placeholder until BrowserContent.qml is present
    Rectangle      { id: browserContent; anchors.fill: contentSpace; anchors.top: contentButtons.bottom; color: "red"; }
    //BrowserContent { id: browserContent; anchors.fill: contentSpace }


    Rectangle { z: -1; anchors.fill: parent; color: "grey" } //background


    states:  [
        State {
            name: "flags"
            PropertyChanges { target: flagContent; visible: true }
            PropertyChanges { target: browserContent; visible: false }
        },
        State {
            name: "browser"
            PropertyChanges { target: flagContent; visible: false }
            PropertyChanges { target: browserContent; visible: true }
        }
    ]
}

Add the File FlagContent.qml

This shows an example of using a QML ListView - which uses a delegate for a row to be displayed

The model statement references a C++ model called "myModel" defined in mymodel.{h,cpp}

import QtQuick 1.0

    ListView {
        clip: true
        model: myModel
        anchors.top: parent.bottom
        anchors.fill: parent
        delegate: Rectangle {
            color: Qt.rgba(0.9,0.9,0.9)
            height: childrenRect.height
            width: parent.width
            Image {
                id: image
                source: flag
                width: 64
                height: 64
                fillMode: Image.PreserveAspectFit

                anchors { left:parent.left; leftMargin:30}
            }
            Text {
                text: display + "\n" +"population: " + population + " mill."
                anchors { left:image.right; verticalCenter: image.verticalCenter; leftMargin: 5 }
            }
        }
    }

Modify main.qml

Remove this:

    AppPage { id: gallery; }

Uncomment this line:

Component { id: gallery; MainPage {} }

Copy Resource Files

Copy the images and .qrc file from the solution directory:

Add this to your simple-app.pro RESOURCES += resources.qrc

Rebuild and Run

Since we added new .cpp and .h files, it would be a good idea to Build->Run Qmake; Build->Rebuild All

Run...

Adding code for the File Browser

To see another mini-app for the "Browser" button, copy these files to your project from the solution directory: Media:Qml-button-ex-rename-to-tgz.jpg

Move these files to your project's qml directory (sub directory of project)
BrowserContent.qml
FileSystemView.qml
ImageViewer.qml
PathDisplay.qml

Move these files to your project's top-level directory
dirmodel.cpp
dirmodel.h
main.cpp (new code added to register a Directory Model with QML named "_model" 

Modify your project's .pro file by adding

  • dirmodel.h to HEADERS
  • dirmodel.cpp to SOURCES

Replace these two lines in MainPage.qml:

We were using a generic red rectangle to hold a space for the BrowserContent Component, we can now use it.

    Rectangle      { id: browserContent; anchors.fill: contentSpace; anchors.top: contentButtons.bottom; color: "red"; }
    //BrowserContent { id: browserContent; anchors.fill: contentSpace }

With...

    BrowserContent { id: browserContent; anchors.fill: contentSpace }

Rebuild All

Run

Clicking on the Browser button should allow you to browse the file system. If you click on a jpg or png, the image will be previewed.

Personal tools