This demo uses Qt Quick components to create a simple Flickr client. It shows a selection of the images that have most recently been added to Flickr. The chief purpose of the demo is to show how to use Qt Quick components to create a user interface that responds to orientation changes.
It also demonstrates the use of the following Qt Quick components: Window, Page, PageStack, Button, ProgressBar, TextField, Toolbar, ToolBarLayout, ToolButton, Slider, ScrollDecorator and StatusBar.
Source: Browse here
The Flickr demo is almost completely written in QML. Qt Quick components are used to create the UI and a simple C++ wrapper collects the QML files and image files of the application into a single binary using the Qt resource system. The main.cpp sets the network proxy for the QApplication that runs the demo. Note that if FULLSCREEN is defined, then the demo will be compiled to run in fullscreen mode. This is required when compiling the application for Symbian devices.
The code in main.cpp sets the main QML file of the application to be Splash.qml which implements a splash screen. While the splash screen is showing, the rest of the QML code is loaded with a QML Loader. This loads the file flickr.qml, which contains the QML root item of the demo, Window.
#include <QApplication> #include <QDeclarativeContext> #include <QDeclarativeEngine> #include <QDeclarativeView> #include <QNetworkProxy> #include <QUrl> int main(int argc, char **argv) { QApplication app(argc, argv); QUrl proxyUrl(qgetenv("http_proxy")); if (proxyUrl.isValid() && !proxyUrl.host().isEmpty()) { int proxyPort = (proxyUrl.port() > 0) ? proxyUrl.port() : 8080; QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyPort); QNetworkProxy::setApplicationProxy(proxy); } else { QNetworkProxyQuery npq(QUrl(QLatin1String("http://www.flickr.com"))); QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(npq); if (listOfProxies.size() > 0 && listOfProxies[0].type() != QNetworkProxy::NoProxy) QNetworkProxy::setApplicationProxy(listOfProxies[0]); } QDeclarativeView view; view.setSource(QUrl("qrc:/qml/Splash.qml")); #if defined(FULLSCREEN) view.window()->showFullScreen(); #else view.window()->show(); #endif QObject::connect(view.engine(), SIGNAL(quit()), &view, SLOT(close())); return app.exec(); }
The Window component is the root component of the application. It provides the means to react to orientation changes, as well as sizing the application.
The PageStack component manages a stack of Page components, which in turn implement the different views of the Flickr application. There are three Page components in the application:
All three Page components share a common ancestor, the FlickrPage component, which implements background graphics.
The ToolBar component shows the buttons for the different Pages. A Page component can specify the a set of tools that automatically show up in the ToolBar when the page is on the top of the stack of pages in the PageStack.
When the application starts the Component.onCompleted() handler in Window pushes the ThumbnailPage to the PageStack.
Window { id: window PageStack { id: pageStack anchors.fill: parent toolBar: toolBar onDepthChanged: searchBar.hideSearch(); } StatusBar { id: statusBar anchors { top: parent.top; left: parent.left; right: parent.right } opacity: largeImagePage.chromeOpacity } SearchBar { id: searchBar anchors.top: statusBar.bottom width: statusBar.width onSearchTagChanged: thumbnailPage.tags = searchTag } ToolBar { id: toolBar anchors { bottom: parent.bottom; left: parent.left; right: parent.right } opacity: largeImagePage.chromeOpacity } ThumbnailPage { id: thumbnailPage } LargeImagePage { id: largeImagePage } DetailsPage { id: detailsPage } Component.onCompleted: pageStack.push(thumbnailPage); }
When the Flickr application starts, it shows most recently added images from Flickr as thumbnails. The ThumbnailPage implements this view, it shows the thumbnails differently in portrait and landscape orientations.
import QtQuick 1.0 import Qt.labs.components.native 1.0 import "UIConstants.js" as UI FlickrPage { property alias tags: photoFeedModel.tags anchors { fill: parent; topMargin: statusBar.height; bottomMargin: toolBar.height } tools: ToolBarLayout { ToolButton { iconSource: "qrc:/qml/images/tb_back.svg" onClicked: Qt.quit(); } ToolButton { iconSource: "qrc:/qml/images/tb_reload.svg" onClicked: { if (searchBar.searching) searchBar.hideSearch(); photoFeedModel.reload(); } } ToolButton { iconSource: "qrc:/qml/images/tb_search.svg" onClicked: searchBar.searching ? searchBar.hideSearch() : searchBar.search(); } } PhotoFeedModel { id: photoFeedModel } GridView { property int thumbnailsInRow: 4 function cellWidth() { return Math.floor(width / thumbnailsInRow); } anchors { fill: parent; margins: UI.GRIDVIEW_MARGIN } cacheBuffer: 2 * height cellHeight: cellWidth cellWidth: cellWidth() delegate: Thumbnail {} model: photoFeedModel visible: !window.inPortrait onWidthChanged: { thumbnailsInRow = width / (UI.THUMBNAIL_WRAPPER_SIDE + UI.THUMBNAIL_SPACING); } } ListView { anchors { fill: parent; margins: UI.LISTVIEW_MARGIN } cacheBuffer: 2 * height delegate: ListDelegate {} model: photoFeedModel visible: window.inPortrait } }
The property window.inPortrait is used to control whether a list of thumbnails or a grid of thumbnails is shown. The GridView component's visibility is defined as !window.inPortrait and the ListView's visibility as window.inPortrait. The property is automatically set to match the current orientation.
The GridView and ListView use a common model, the PhotoFeedModel, which is a customized XmlListModel that fetches the thumbnail information from an online feed provided by Flickr. Sharing the PhotoFeedModel reduces code duplication and improves performance as the shared model fetches the thumbnail information only once for both views.
The tools property of the ThumbnailPage contains a ToolBarLayout with three ToolButton components. The ToolBarLayout automatically positions the ToolButtons from left to right with appropriate spacings in between, automatically adjusting to different orientations.
The leftmost button quits the application, the center button reloads the thumbnails and the button on the right brings out the search bar, which is implemented in the component SearchBar.
Image of the ThumbnailPage in portrait orientation:
Image of the ThumbnailPage showing a grid of thumbnails in landscape orientation:
Image of the ThumbnailPage with the SearchBar visible:
The Flickr demo will show the a larger version of an image when the user clicks or taps on the thumbnail. This action is handled in the delegates of ListView and GridView, both calling the photoClicked() function in Thumbnail, which pushes the LargeImagePage to the PageStack with the function PageStack.push().
The LargeImagePage shows a larger version of an image that is flickable and zoomable.
The LargeImagePage shows a ProgressBar while loading an image. Once the image is loaded, ProgressBar will be hidden and the actual image is shown.
ProgressBar { anchors.centerIn: parent minimumValue: 1 maximumValue: 100 value: image.progress * 100 visible: image.status != Image.Ready Text { text: Math.floor(parent.value) + " %" anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.top anchors.bottomMargin: UI.PROGRESSBAR_LABEL_BOTTOM_MARGIN font.bold: true color: UI.PROGRESSBAR_LABEL_COLOR } }
The Slider component is used to zoom the image. Dragging the Slider scales the image creating a zoom feature.
Slider { id: slider maximumValue: 1 stepSize: (maximumValue - minimumValue) / 100 opacity: UI.SLIDER_OPACITY anchors { bottom: parent.bottom bottomMargin: UI.SLIDER_BOTTOM_MARGIN left: parent.left leftMargin: UI.SLIDER_SIDE_MARGIN right: parent.right rightMargin: UI.SLIDER_SIDE_MARGIN } } Binding { target: image; property: "scale"; value: slider.value; when: slider.visible }
Once the zoom level of the image is larger than the screen size, scroll decorators are shown. ScrollDecorator indicates the part of the image that is shown while flicking. The flickableItem property of ScrollDecorator is bound to the id flickable, which identifies the Flickable element that wraps the image.
ScrollDecorator { flickableItem: flickable }
The following image depicts the LargeImagePage component in action.
The status bar and toolbar are automatically hidden in the LargeImagePage, tapping or clicking on the image will bring both back to the screen for 2 seconds. The rightmost toolbar button brings forth a view that shows detailed information about the image, the button on the left shows the ThumbnailPage. The code in LargeImagePage.qml assigns a ToolBarLayout with the two buttons to the tools property of LargeImagePage
tools: ToolBarLayout { ToolButton { iconSource: "qrc:/qml/images/tb_back.svg" onClicked: pageStack.pop(); } ToolButton { iconSource: "qrc:/qml/images/tb_info.svg" checked: false onClicked: pageStack.replace(detailsPage); } }
Note the use of the PageStack.replace() function, pressing on the rightmost button will not increase the stack depth in PageStack, it will instead replace LargeImagePage with a DetailsPage object with the id detailsPage, which is defined in flickr.qml. The LargeImagePage object will not be destroyed even if it is not in the PageStack as it is defined within the Window element in flickr.qml.
The Details page, implemented in the file DetailsPage.qml, shows detailed information of the image.
The button on the left makes the application to go back to the ThumbnailPage, the button on the right will show the large version of the image by replacing itself with the LargeImagePage on the PageStack with PageStack.replace().