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

Qt Quick Demo - Photo Viewer

A QML photo viewer that uses XmlListModel and XmlListModelRole custom QML types to download Flickr feeds, and Package to display the photos in different views.

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Qt Quick Demo - Photo Viewer▲

Image non disponible

Photo Viewer demonstrates the following https:// features:

  • Using custom QML types.

  • Using Qt Quick Controls 1 to create an application window.

  • Using the https:// type with a https:// to provide delegates with a shared context to multiple views.

  • Using custom types to download Flickr feeds.

  • Using the https:// type to create labels with different text on the front and back.

  • Using the https://, https://, https://, and https:// types to lay out photos on a path.

  • Providing feedback to users while data is loading.

  • Localizing applications.

Running the Example▲

To run the example from https://, open the Welcome mode and select the example from Examples. For more information, visit https://.

Using Custom Types▲

In the Photo Viewer app, we use the following custom types that are each defined in a separate .qml file:

  • AlbumDelegate.qml

  • BusyIndicator.qml

  • Button.qml

  • EditableButton.qml

  • PhotoDelegate.qml

  • ProgressBar.qml

  • RssModel.qml

  • Tag.qml

To use the custom types, we add an import statement to the main QML file, main.qml, that imports the folder called PhotoViewerCore where the types are located:

 
Sélectionnez
import "PhotoViewerCore"

We also use the following custom types that are defined in C++:

  • XmlListModel

  • XmlListModelRole

To register QML types from C++ we add the https:// macro to the https:// derived classes' definitions like this:

 
Sélectionnez
class XmlListModelRole : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString elementName READ elementName WRITE setElementName NOTIFY elementNameChanged)
    Q_PROPERTY(QString attributeName READ attributeName WRITE setAttributeName NOTIFY attributeNameChanged)
    QML_ELEMENT

In addition we need to add the following to the CMakeLists.txt file of the example:

 
Sélectionnez
set_target_properties(photoviewer PROPERTIES
    QT_QML_MODULE_VERSION 1.0
    QT_QML_MODULE_URI XmlListModel
)

qt6_qml_type_registration(photoviewer)

To build with qmake, we add CONFIG += qmltypes, QML_IMPORT_NAME, and QML_IMPORT_MAJOR_VERSION to the project file:

 
Sélectionnez
CONFIG += lrelease embed_translations qmltypes

QML_IMPORT_NAME = XmlListModel
QML_IMPORT_MAJOR_VERSION = 1

For more information on defining QML types from C++ see the https:// documentation.

To use the new types, we add an import statement that imports them to the QML files that use the types:

 
Sélectionnez
import XmlListModel

Creating the Main Window▲

In main.qml, we use the https:// Qt Quick Control to create the app main window:

 
Sélectionnez
ApplicationWindow {
    id: mainWindow

    visible: true

We use a https:// type with https:// types to display photo albums:

 
Sélectionnez
    ListModel {
        id: photosModel
        ListElement { tag: "Flowers" }
        ListElement { tag: "Wildlife" }
        ListElement { tag: "Prague" }
    }

List elements are defined like other QML types except that they contain a collection of role definitions instead of properties. Roles both define how the data is accessed and include the data itself. For each list element, we use the tag role to specify the photos to download.

A https:// type is used together with the https:// type to provide delegates to multiple views. The model property holds the model providing data for the delegate model and the delegate property specifies the template defining each item instantiated by a view:

 
Sélectionnez
    DelegateModel { id: albumVisualModel; model: photosModel; delegate: AlbumDelegate {} }

We use a https:// type to lay out the albums as a grid:

 
Sélectionnez
    GridView {
        id: albumView; width: parent.width; height: parent.height; cellWidth: 210; cellHeight: 220
        model: albumVisualModel.parts.album; visible: albumsShade.opacity != 1.0
    }

The model property references the package name album that we specify in AlbumDelegate.qml. We use the https:// type to allow the photos to move between different views. The https:// contains the named items browser, fullscreen, and album:

 
Sélectionnez
    Package {

        Item {
            Package.name: 'browser'
            GridView {
                id: photosGridView; model: visualModel.parts.grid; width: mainWindow.width; height: mainWindow.height - 21
                x: 0; y: 21; cellWidth: 160; cellHeight: 153; interactive: false
                onCurrentIndexChanged: photosListView.positionViewAtIndex(currentIndex, ListView.Contain)
            }
        }

        Item {
            Package.name: 'fullscreen'
            ListView {
                id: photosListView; model: visualModel.parts.list; orientation: Qt.Horizontal
                width: mainWindow.width; height: mainWindow.height; interactive: false
                onCurrentIndexChanged: photosGridView.positionViewAtIndex(currentIndex, GridView.Contain)
                highlightRangeMode: ListView.StrictlyEnforceRange; snapMode: ListView.SnapOneItem
            }
        }

        Item {
            Package.name: 'album'
            id: albumWrapper; width: 210; height: 220

The named items are used as the delegates by the views that reference the special https:// property to select the model that provides the chosen delegate.

We use a https:// type to lay out albums in other views:

 
Sélectionnez
    ListView { anchors.fill: parent; model: albumVisualModel.parts.browser; interactive: false }

    ListView { anchors.fill: parent; model: albumVisualModel.parts.fullscreen; interactive: false }

Displaying Photos▲

We use the PhotoDelegate custom type that is specified in PhotoDelegate.qml to display photos. We use a https:// type to lay out the photos either in a stack, list, or a grid:

 
Sélectionnez
Package {
    Item { id: stackItem; Package.name: 'stack'; width: 160; height: 153; z: stackItem.PathView.z }
    Item { id: listItem; Package.name: 'list'; width: mainWindow.width + 40; height: 153 }
    Item { id: gridItem; Package.name: 'grid'; width: 160; height: 153 }

The photos are rotated at random angles by using the Math.random() JavaScript method:

 
Sélectionnez
    Item {
        width: 160; height: 153

        Item {
            id: photoWrapper

            property double randomAngle: Math.random() * (2 * 6 + 1) - 6
            property double randomAngle2: Math.random() * (2 * 6 + 1) - 6

            x: 0; y: 0; width: 140; height: 133
            z: stackItem.PathView.z; rotation: photoWrapper.randomAngle

We use a https:// type to create borders for the images:

 
Sélectionnez
            BorderImage {
                anchors {
                    fill: originalImage.status == Image.Ready ? border : placeHolder
                    leftMargin: -6; topMargin: -6; rightMargin: -8; bottomMargin: -8
                }
                source: 'images/box-shadow.png'
                border.left: 10; border.top: 10; border.right: 10; border.bottom: 10
            }

Downloading Flickr Feeds▲

In AlbumDelegate.qml, we use the https:// to provide the PhotoDelegate delegate to the RssModel model:

 
Sélectionnez
            DelegateModel {
                id: visualModel; delegate: PhotoDelegate { }
                model: RssModel { id: rssModel; tags: tag }
            }

In RssModel.qml, we use the XmlListModel custom type as a data source for https:// objects to download photos from the selected feeds:

 
Sélectionnez
import XmlListModel

XmlListModel {
    property string tags : ""

    function encodeTags(x) { return encodeURIComponent(x.replace(' ',',')); }

We use the tags custom property to specify which photos to download. The encodeTags custom function uses the encodeURIComponent JavaScript method to ensure that the requests to the server are correctly formatted.

We use the source property to fetch photos that have the specified tags attached from public Flickr feeds:

 
Sélectionnez
    source: "http://api.flickr.com/services/feeds/photos_public.gne?"+(tags ? "tags="+encodeTags(tags)+"&" : "")

    query: "/feed/entry"

The query property specifies that the XmlListModel generates a model item for each feed entry.

We use the XmlListModelRole custom type to specify the model item attributes. Each model item has the title and link attributes that match the values of the corresponding feed entry:

 
Sélectionnez
    roles: [
        XmlListModelRole { elementName: "title"; attributeName: "" },
        XmlListModelRole { elementName: "link"; attributeName: "href" }

Creating Flipable Labels▲

When users select the Edit button, the album labels are flipped from their front side to their back side and the text on them changes from album name to Remove.

In AlbumDelegate.qml, we use the Tag custom type to specify the text to display on the front and back sides of album labels:

 
Sélectionnez
            Tag {
                anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: 10 }
                frontLabel: tag; backLabel: qsTr("Remove"); flipped: mainWindow.editMode
                onTagChanged: rssModel.tags = tag
                onBackClicked: if (mainWindow.editMode) photosModel.remove(index);
            }

The onTagChanged signal handler is used to change the tag based on which the model is populated. The onBackClicked signal handler is used to remove the album.

In Tag.qml, we use a https:// type with custom properties and signals to create the labels:

 
Sélectionnez
Flipable {
    id: flipable

    property alias frontLabel: frontButton.label
    property alias backLabel: backButton.label

    property int angle: 0
    property int randomAngle: Math.random() * (2 * 6 + 1) - 6
    property bool flipped: false

    signal frontClicked
    signal backClicked
    signal tagChanged(string tag)

The front property holds the EditableButton custom type that enables users to edit the label text:

 
Sélectionnez
    front: EditableButton {
        id: frontButton; rotation: flipable.randomAngle
        anchors { centerIn: parent; verticalCenterOffset: -20 }
        onClicked: flipable.frontClicked()
        onLabelChanged: flipable.tagChanged(label)
    }

The back property holds the Button custom type that is used to remove the album:

 
Sélectionnez
    back: Button {
        id: backButton; tint: "red"; rotation: flipable.randomAngle
        anchors { centerIn: parent; verticalCenterOffset: -20 }
        onClicked: flipable.backClicked()
    }

Laying Out Photos on a Path▲

In AlbumDelegate.qml, we use a https:// type to lay out the photos provided by the visualModel.parts.stack model on a path that has the form of a stack:

 
Sélectionnez
            PathView {
                id: photosPathView; model: visualModel.parts.stack; pathItemCount: 5
                visible: !busyIndicator.visible
                anchors.centerIn: parent; anchors.verticalCenterOffset: -30
                path: Path {
                    PathAttribute { name: 'z'; value: 9999.0 }
                    PathLine { x: 1; y: 1 }
                    PathAttribute { name: 'z'; value: 0.0 }
                }
            }

The path property holds the https:// type that defines the path used by the https://. The https:// types are used to set a range of 0 to 9999 for the z attribute. This way, the path creates a stack of album photos. Because each PhotoDelegate is slightly rotated at a random angle, this results in a realistic-looking stack of photos.

Providing Feedback to Users▲

We use a busy indicator and a progress bar to indicate activity while Flickr feeds and photos are being loaded.

In AlbumDelegate.qml, we use the BusyIndicator custom type and the on custom property to display a rotating image while the Flickr feed is being loaded:

 
Sélectionnez
            BusyIndicator {
                id: busyIndicator
                anchors { centerIn: parent; verticalCenterOffset: -20 }
                on: rssModel.status != XmlListModel.Ready
            }

In PhotoDelegate.qml, we use them to indicate activity while a photo is being loaded:

 
Sélectionnez
            BusyIndicator { anchors.centerIn: parent; on: originalImage.status != Image.Ready }

We define the BusyIndicator type in BusyIndicator.qml. We use an https:// type to display an image and apply a https:// to its rotation property to rotate the image in an infinite loop:

 
Sélectionnez
Image {
    id: container
    property bool on: false

    source: "images/busy.png"; visible: container.on
    NumberAnimation on rotation { running: container.on; from: 0; to: 360; loops: Animation.Infinite; duration: 1200 }
}

In your apps, you can also use the https:// type from the https:// module.

In main.qml, we use the ProgressBar custom type to indicate progress while a high quality version of a photo is being opened on full screen:

 
Sélectionnez
    ProgressBar {
        progress: mainWindow.downloadProgress; width: parent.width; height: 4
        anchors.bottom: parent.bottom; opacity: mainWindow.imageLoading; visible: opacity != 0.0
    }

We define the ProgressBar type in ProgressBar.qml. We use a https:// type to create the progress bar and apply a https:// to its opacity property to change the color of the bar from black to white as data loading proceeds:

 
Sélectionnez
Item {
    id: container

    property real progress: 0

    Behavior on opacity { NumberAnimation { duration: 600 } }

    Rectangle { anchors.fill: parent; color: "black"; opacity: 0.5 }

    Rectangle {
        id: fill; color: "white"; height: container.height
        width: container.width * container.progress
    }
}

In your apps, you can also use the https:// type from the https:// module.

Localizing Applications▲

The example application is translated into German and French. The translated strings are loaded at runtime according to the current locale.

We use a https:// type in main.qml to position buttons for adding and editing albums and exiting the application:

 
Sélectionnez
    Column {
        spacing: 20; anchors { bottom: parent.bottom; right: parent.right; rightMargin: 20; bottomMargin: 20 }
        Button {
            id: newButton; label: qsTr("Add"); rotation: 3
            anchors.horizontalCenter: parent.horizontalCenter
            onClicked: {
                mainWindow.editMode = false
                photosModel.append( { tag: "" } )
                albumView.positionViewAtIndex(albumView.count - 1, GridView.Contain)
            }
        }
        Button {
            id: deleteButton; label: qsTr("Edit"); rotation: -2;
            onClicked: mainWindow.editMode = !mainWindow.editMode
            anchors.horizontalCenter: parent.horizontalCenter
        }
        Button {
            id: quitButton; label: qsTr("Quit"); rotation: -2;
            onClicked: Qt.quit()
            anchors.horizontalCenter: parent.horizontalCenter
        }
    }

We use the https://() command to mark the button labels translatable.

We use the lupdate() tool to generate the translation source files and the lrelease() tool to convert the translated strings to the QM files used by the application at runtime. These files are stored in the i18n directory.

To make the application aware of the translations, we add code to the main() function in the main.cpp file. The code creates a https:// object, loads a translation according to the current locale at runtime, and installs the translator object into the application:

 
Sélectionnez
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QTranslator qtTranslator;
    qtTranslator.load(QLocale(), "qml", "_", ":/i18n/");
    app.installTranslator(&qtTranslator);

List of Files▲

Files:

List of Files▲

  • demos/photoviewer/CMakeLists.txt

  • demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml

  • demos/photoviewer/PhotoViewerCore/BusyIndicator.qml

  • demos/photoviewer/PhotoViewerCore/Button.qml

  • demos/photoviewer/PhotoViewerCore/EditableButton.qml

  • demos/photoviewer/PhotoViewerCore/PhotoDelegate.qml

  • demos/photoviewer/PhotoViewerCore/ProgressBar.qml

  • demos/photoviewer/PhotoViewerCore/RssModel.qml

  • demos/photoviewer/PhotoViewerCore/Tag.qml

  • demos/photoviewer/main.cpp

  • demos/photoviewer/main.qml

  • demos/photoviewer/photoviewer.pro

List of Files▲

Images:

List of Files▲
  • demos/photoviewer/PhotoViewerCore/images/box-shadow.png

  • demos/photoviewer/PhotoViewerCore/images/busy.png

  • demos/photoviewer/PhotoViewerCore/images/cardboard.png

See Also▲

See also https://

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Ce document est disponible sous la licence https:// telle que publiée par la Free Software Foundation.