Remove PhotoViewer example

Fixes: QTBUG-113593
Change-Id: Idafd79a140067fac9c9f27440f4f43453e2e2ddf
Pick-to: 6.5
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Kimmo Leppälä 2023-05-15 08:28:36 +03:00
parent 3c0d43145d
commit 117382f261
24 changed files with 0 additions and 1072 deletions

View File

@ -18,7 +18,6 @@ if(TARGET Qt6::Quick AND TARGET Qt6::QuickControls2)
endif()
if(TARGET Qt6::Quick AND TARGET Qt6::Network AND TARGET Qt6::QmlXmlListModel)
qt_internal_add_example(rssnews)
qt_internal_add_example(photoviewer)
endif()
if(TARGET Qt6::Quick AND TARGET Qt6::qsb AND TARGET Qt6::Pdf)
qt_internal_add_example(photosurface)

View File

@ -20,8 +20,5 @@ qtHaveModule(quick) {
qtHaveModule(xml) {
SUBDIRS += rssnews
}
qtHaveModule(quickcontrols2) {
SUBDIRS += photoviewer
}
}
}

View File

@ -1,70 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(photoviewer LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/demos/photoviewer")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml QmlXmlListModel Quick)
qt_add_executable(photoviewer
main.cpp
)
set_target_properties(photoviewer PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(photoviewer PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Qml
Qt6::Quick
Qt6::QmlXmlListModel
)
# Resources:
set(qmake_immediate_resource_files
"PhotoViewerCore/AlbumDelegate.qml"
"PhotoViewerCore/BusyIndicator.qml"
"PhotoViewerCore/Button.qml"
"PhotoViewerCore/EditableButton.qml"
"PhotoViewerCore/PhotoDelegate.qml"
"PhotoViewerCore/ProgressBar.qml"
"PhotoViewerCore/RssModel.qml"
"PhotoViewerCore/Tag.qml"
"PhotoViewerCore/images/box-shadow.png"
"PhotoViewerCore/images/busy.png"
"PhotoViewerCore/images/cardboard.png"
"PhotoViewerCore/script/script.mjs"
"main.qml"
)
qt_add_resources(photoviewer "qmake_immediate"
PREFIX
"/"
FILES
${qmake_immediate_resource_files}
)
if(lupdate_only)
target_sources(photoviewer PRIVATE
*.qml
PhotoViewerCore/*.qml
PhotoViewerCore/script/*.js
)
endif()
install(TARGETS photoviewer
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -1,125 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQml.XmlListModel
import QtQml.Models
Component {
id: albumDelegate
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
DelegateModel {
id: visualModel; delegate: PhotoDelegate { }
model: RssModel { id: rssModel; tags: tag }
}
BusyIndicator {
id: busyIndicator
anchors { centerIn: parent; verticalCenterOffset: -20 }
on: rssModel.status != XmlListModel.Ready
}
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 }
}
}
MouseArea {
anchors.fill: parent
onClicked: mainWindow.editMode ? photosModel.remove(index) : albumWrapper.state = 'inGrid'
}
Tag {
anchors { horizontalCenter: parent.horizontalCenter; bottom: parent.bottom; bottomMargin: 10 }
frontLabel: tag; backLabel: qsTr("Remove"); flipped: mainWindow.editMode
onTagChanged: function(tag) { rssModel.tags = tag }
onBackClicked: if (mainWindow.editMode) photosModel.remove(index);
}
states: [
State {
name: 'inGrid'
PropertyChanges { target: photosGridView; interactive: true }
PropertyChanges { target: albumsShade; opacity: 1 }
PropertyChanges { target: backButton; onClicked: albumWrapper.state = ''; y: 6 }
},
State {
name: 'fullscreen'; extend: 'inGrid'
PropertyChanges { target: photosGridView; interactive: false }
PropertyChanges { target: photosListView; interactive: true }
PropertyChanges { target: photosShade; opacity: 1 }
PropertyChanges { target: backButton; y: -backButton.height - 8 }
}
]
NumberAnimation {
id: onAddAmination
target: albumWrapper
properties: "scale"
from: 0.0
to: 1.0
easing.type: Easing.OutQuad
}
GridView.onAdd: onAddAmination
SequentialAnimation {
id: onRemoveAnimation
PropertyAction {
target: albumWrapper; property: "GridView.delayRemove"; value: true
}
NumberAnimation {
target: albumWrapper; property: "scale"; from: 1.0; to: 0.0; easing.type: Easing.OutQuad
}
PropertyAction {
target: albumWrapper; property: "GridView.delayRemove"; value: false
}
}
GridView.onRemove: onRemoveAnimation
transitions: [
Transition {
from: '*'; to: 'inGrid'
SequentialAnimation {
NumberAnimation { properties: 'opacity'; duration: 250 }
PauseAnimation { duration: 350 }
NumberAnimation { target: backButton; properties: "y"; duration: 200; easing.type: Easing.OutQuad }
}
},
Transition {
from: 'inGrid'; to: '*'
NumberAnimation { properties: "y,opacity"; easing.type: Easing.OutQuad; duration: 300 }
}
]
}
}
}

View File

@ -1,12 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
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 }
}

View File

@ -1,34 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
Item {
id: container
property alias label: labelText.text
property color tint: "transparent"
signal clicked
width: labelText.width + 70 ; height: labelText.height + 18
BorderImage {
anchors { fill: container; 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
}
Image { anchors.fill: parent; source: "images/cardboard.png"; antialiasing: true }
Rectangle {
anchors.fill: container; color: container.tint; visible: container.tint != ""
opacity: 0.25
}
Text { id: labelText; font.pixelSize: 15; anchors.centerIn: parent }
MouseArea {
anchors { fill: parent; leftMargin: -20; topMargin: -20; rightMargin: -20; bottomMargin: -20 }
onClicked: container.clicked()
}
}

View File

@ -1,47 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
Item {
id: container
property string label
signal clicked
width: textInput.width + 70 ; height: textInput.height + 18
BorderImage {
anchors { fill: container; 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
}
Image { anchors.fill: parent; source: "images/cardboard.png"; antialiasing: true }
TextInput {
id: textInput; text: label; font.pixelSize: 15; anchors.centerIn: parent
Keys.onReturnPressed: {
container.label = textInput.text
container.focus = true
}
Keys.onEnterPressed: {
container.label = textInput.text
container.focus = true
}
Keys.onEscapePressed: {
textInput.text = container.label
container.focus = true
}
}
Rectangle {
anchors.fill: container; border.color: "steelblue"; border.width: 4
color: "transparent"; visible: textInput.focus; antialiasing: true
}
MouseArea {
anchors { fill: parent; leftMargin: -20; topMargin: -20; rightMargin: -20; bottomMargin: -20 }
onClicked: { textInput.forceActiveFocus(); Qt.inputMethod.show(); }
}
}

View File

@ -1,151 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import "script/script.mjs" as Script
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 }
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
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
}
Rectangle {
id: placeHolder
property int w: 400
property int h: 400
property double s: Script.calculateScale(w, h, photoWrapper.width)
color: 'white'; anchors.centerIn: parent; antialiasing: true
width: w * s; height: h * s; visible: originalImage.status != Image.Ready
Rectangle {
color: "#878787"; antialiasing: true
anchors { fill: parent; topMargin: 3; bottomMargin: 3; leftMargin: 3; rightMargin: 3 }
}
}
Rectangle {
id: border; color: 'white'; anchors.centerIn: parent; antialiasing: true
width: originalImage.paintedWidth + 6; height: originalImage.paintedHeight + 6
visible: !placeHolder.visible
}
BusyIndicator { anchors.centerIn: parent; on: originalImage.status != Image.Ready }
Image {
id: originalImage; antialiasing: true;
source: link; cache: false
fillMode: Image.PreserveAspectFit; width: photoWrapper.width; height: photoWrapper.height
}
Image {
id: hqImage; antialiasing: true; source: ""; visible: false; cache: false
fillMode: Image.PreserveAspectFit; width: photoWrapper.width; height: photoWrapper.height
}
Binding {
target: mainWindow; property: "downloadProgress"; value: hqImage.progress
when: listItem.ListView.isCurrentItem
}
Binding {
target: mainWindow; property: "imageLoading"
value: (hqImage.status == Image.Loading) ? 1 : 0; when: listItem.ListView.isCurrentItem
}
MouseArea {
width: originalImage.paintedWidth; height: originalImage.paintedHeight; anchors.centerIn: originalImage
onClicked: {
if (albumWrapper.state == 'inGrid') {
gridItem.GridView.view.currentIndex = index;
albumWrapper.state = 'fullscreen'
} else {
gridItem.GridView.view.currentIndex = index;
albumWrapper.state = 'inGrid'
}
}
}
states: [
State {
name: 'stacked'; when: albumWrapper.state == ''
ParentChange { target: photoWrapper; parent: stackItem; x: 10; y: 10 }
PropertyChanges { target: photoWrapper; opacity: stackItem.PathView.onPath ? 1.0 : 0.0 }
},
State {
name: 'inGrid'; when: albumWrapper.state == 'inGrid'
ParentChange { target: photoWrapper; parent: gridItem; x: 10; y: 10; rotation: photoWrapper.randomAngle2 }
},
State {
name: 'fullscreen'; when: albumWrapper.state == 'fullscreen'
ParentChange {
target: photoWrapper; parent: listItem; x: 0; y: 0; rotation: 0
width: mainWindow.width; height: mainWindow.height
}
PropertyChanges { target: border; opacity: 0 }
PropertyChanges { target: hqImage; source: listItem.ListView.isCurrentItem ? link : ""; visible: true }
}
]
transitions: [
Transition {
from: 'stacked'; to: 'inGrid'
SequentialAnimation {
PauseAnimation { duration: 10 * index }
ParentAnimation {
target: photoWrapper; via: foreground
NumberAnimation {
target: photoWrapper; properties: 'x,y,rotation,opacity'; duration: 600; easing.type: 'OutQuart'
}
}
}
},
Transition {
from: 'inGrid'; to: 'stacked'
ParentAnimation {
target: photoWrapper; via: foreground
NumberAnimation { properties: 'x,y,rotation,opacity'; duration: 600; easing.type: 'OutQuart' }
}
},
Transition {
from: 'inGrid'; to: 'fullscreen'
SequentialAnimation {
PauseAnimation { duration: gridItem.GridView.isCurrentItem ? 0 : 600 }
ParentAnimation {
target: photoWrapper; via: foreground
NumberAnimation {
targets: [ photoWrapper, border ]
properties: 'x,y,width,height,opacity,rotation'
duration: gridItem.GridView.isCurrentItem ? 600 : 1; easing.type: 'OutQuart'
}
}
}
},
Transition {
from: 'fullscreen'; to: 'inGrid'
ParentAnimation {
target: photoWrapper; via: foreground
NumberAnimation {
targets: [ photoWrapper, border ]
properties: 'x,y,width,height,rotation,opacity'
duration: gridItem.GridView.isCurrentItem ? 600 : 1; easing.type: 'OutQuart'
}
}
}
]
}
}
}

View File

@ -1,19 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
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
}
}

View File

@ -1,17 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQml.XmlListModel
XmlListModel {
property string tags : ""
function encodeTags(x) { return encodeURIComponent(x.replace(' ',',')); }
source: "http://api.flickr.com/services/feeds/photos_public.gne?"+(tags ? "tags="+encodeTags(tags)+"&" : "")
query: "/feed/entry"
XmlListModelRole { name: "title"; elementName: "title"; attributeName: "" }
XmlListModelRole { name: "link"; elementName: "link"; attributeName: "href" }
}

View File

@ -1,53 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
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)
front: EditableButton {
id: frontButton; rotation: flipable.randomAngle
anchors { centerIn: parent; verticalCenterOffset: -20 }
onClicked: flipable.frontClicked()
onLabelChanged: flipable.tagChanged(label)
}
back: Button {
id: backButton; tint: "red"; rotation: flipable.randomAngle
anchors { centerIn: parent; verticalCenterOffset: -20 }
onClicked: flipable.backClicked()
}
transform: Rotation {
origin.x: flipable.width / 2; origin.y: flipable.height / 2
axis.x: 0; axis.y: 1; axis.z: 0
angle: flipable.angle
}
states: State {
name: "back"; when: flipable.flipped
PropertyChanges { target: flipable; angle: 180 }
}
transitions: Transition {
ParallelAnimation {
NumberAnimation { properties: "angle"; duration: 400 }
SequentialAnimation {
NumberAnimation { target: flipable; property: "scale"; to: 0.8; duration: 200 }
NumberAnimation { target: flipable; property: "scale"; to: 1.0; duration: 200 }
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -1,15 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
export function calculateScale(width, height, cellSize) {
var widthScale = (cellSize * 1.0) / width
var heightScale = (cellSize * 1.0) / height
var scale = 0
if (widthScale <= heightScale) {
scale = widthScale;
} else if (heightScale < widthScale) {
scale = heightScale;
}
return scale;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,303 +0,0 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\title Qt Quick Demo - Photo Viewer
\ingroup qtquickdemos
\example demos/photoviewer
\brief A QML photo viewer that uses \l XmlListModel and \l XmlListModelRole
to download Flickr feeds, and \l Package to display the photos in different
views.
\meta {tag} {demo,quick,quickcontrols,touch}
\image qtquick-demo-photoviewer-small.png
\e{Photo Viewer} demonstrates the following \l{Qt Quick} features:
\list
\li Using custom QML types.
\li Using Qt Quick Controls to create an application window.
\li Using the \l Package type with a \l DelegateModel to provide
delegates with a shared context to multiple views.
\li Using XML list models to download Flickr feeds.
\li Using the \l Flipable type to create labels with different text on
the front and back.
\li Using the \l PathView, \l Path, \l PathAttribute, and \l PathLine
types to lay out photos on a path.
\li Providing feedback to users while data is loading.
\li Localizing applications.
\endlist
\include examples-run.qdocinc
\section1 Using Custom Types
In the Photo Viewer app, we use the following custom types that are each
defined in a separate .qml file:
\list
\li \c AlbumDelegate.qml
\li \c BusyIndicator.qml
\li \c Button.qml
\li \c EditableButton.qml
\li \c PhotoDelegate.qml
\li \c ProgressBar.qml
\li \c RssModel.qml
\li \c Tag.qml
\endlist
To use the custom types, we add an import statement to the main QML file,
\c main.qml, that imports the folder called \c PhotoViewerCore where the
types are located:
\quotefromfile demos/photoviewer/main.qml
\skipto PhotoViewerCore
\printuntil "
\section1 Creating the Main Window
In \c main.qml, we use the \l ApplicationWindow Qt Quick Control to create the
app main window:
\quotefromfile demos/photoviewer/main.qml
\skipto ApplicationWindow
\printuntil visible
We use a ListModel type with \l ListElement types to display photo albums:
\skipto ListModel
\printuntil Prague
\printuntil }
List elements are defined like other QML types except that they contain a
collection of \e 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 \c tag role to specify the photos to download.
A \l DelegateModel type is used together with the \l Package type to provide
delegates to multiple views. The \c model property holds the model providing
data for the delegate model and the \c delegate property specifies the
template defining each item instantiated by a view:
\printuntil DelegateModel
We use a \l GridView type to lay out the albums as a grid:
\printuntil }
The \c model property references the package name \c album that we specify
in \c AlbumDelegate.qml. We use the \l Package type to allow the photos
to move between different views. The \l Package contains the named items
\c browser, \c fullscreen, and \c album:
\quotefromfile demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml
\skipto Package
\printuntil albumWrapper
The named items are used as the delegates by the views that reference the
special \l {DelegateModel::parts} property to select the model that provides
the chosen delegate.
We use a \l ListView type to lay out albums in other views:
\quotefromfile demos/photoviewer/main.qml
\skipto ListView
\printuntil }
\skipto ListView
\printuntil }
\section1 Displaying Photos
We use the \c PhotoDelegate custom type that is specified in
\c PhotoDelegate.qml to display photos. We use a \l Package type to lay
out the photos either in a stack, list, or a grid:
\quotefromfile demos/photoviewer/PhotoViewerCore/PhotoDelegate.qml
\skipto Package
\printuntil gridItem
The photos are rotated at random angles by using the \c Math.random()
JavaScript method:
\printuntil stackItem
We use a \l BorderImage type to create borders for the images:
\printuntil border.left
\printuntil }
\section1 Downloading Flickr Feeds
In \c AlbumDelegate.qml, we use the \l DelegateModel to provide the
\c PhotoDelegate delegate to the \c RssModel model:
\quotefromfile demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml
\skipto DelegateModel
\printuntil RssModel
\printuntil }
In \c RssModel.qml, we use the \l XmlListModel type as a data source for
\l Package objects to download photos from the selected feeds:
\quotefromfile demos/photoviewer/PhotoViewerCore/RssModel.qml
\skipto XmlListModel
\printuntil encodeTags
We use the \c tags custom property to specify which photos to download. The
\c encodeTags custom function uses the \c encodeURIComponent JavaScript
method to ensure that the requests to the server are correctly formatted.
We use the \c source property to fetch photos that have the specified tags
attached from public Flickr feeds:
\printuntil query
The \c query property specifies that the \l XmlListModel generates a model
item for each feed entry.
We use the \l XmlListModelRole type to specify the model item attributes.
Each model item has the \c title and \c link attributes that match the
values of the corresponding feed entry:
\printuntil link
\section1 Creating Flipable Labels
When users select the \b 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 \b Remove.
In \c AlbumDelegate.qml, we use the \c Tag custom type to specify the
text to display on the front and back sides of album labels:
\quotefromfile demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml
\skipto Tag
\printuntil onBackClicked
\printuntil }
The \c onTagChanged signal handler is used to change the tag based on
which the model is populated. The \c onBackClicked signal handler is used to
remove the album.
In \c Tag.qml, we use a \l Flipable type with custom properties and
signals to create the labels:
\quotefromfile demos/photoviewer/PhotoViewerCore/Tag.qml
\skipto Flipable
\printuntil tagChanged
The \c front property holds the \c EditableButton custom type that enables
users to edit the label text:
\printuntil onLabelChanged
\printuntil }
The \c back property holds the \c Button custom type that is used to remove
the album:
\printuntil onClicked
\printuntil }
\section1 Laying Out Photos on a Path
In \c AlbumDelegate.qml, we use a \l PathView type to lay out the photos
provided by the \c visualModel.parts.stack model on a path that has the
form of a stack:
\quotefromfile demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml
\skipto PathView
\printuntil 0.0
\printuntil }
\printuntil }
The \c path property holds the \l Path type that defines the path used by
the \l PathView. The \l PathAttribute types are used to set a range of
\c 0 to \c 9999 for the \c z attribute. This way, the path creates a stack
of album photos. Because each \c PhotoDelegate is slightly rotated at a
random angle, this results in a realistic-looking stack of photos.
\section1 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 \c AlbumDelegate.qml, we use the \c BusyIndicator custom type and the
\c on custom property to display a rotating image while the Flickr feed is
being loaded:
\quotefromfile demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml
\skipto BusyIndicator
\printuntil rssModel
\printuntil }
In \c PhotoDelegate.qml, we use them to indicate activity while a photo is
being loaded:
\quotefromfile demos/photoviewer/PhotoViewerCore/PhotoDelegate.qml
\skipto BusyIndicator
\printuntil }
We define the \c BusyIndicator type in \c BusyIndicator.qml. We use an
\l [QML] {Image} type to display an image and apply a \l NumberAnimation to
its \c rotation property to rotate the image in an infinite loop:
\quotefromfile demos/photoviewer/PhotoViewerCore/BusyIndicator.qml
\skipto Image
\printuntil }
\printuntil }
In your apps, you can also use the \l BusyIndicator type from the
\l {Qt Quick Controls} module.
In \c main.qml, we use the \c ProgressBar custom type to indicate progress
while a high quality version of a photo is being opened on full screen:
\quotefromfile demos/photoviewer/main.qml
\skipto ProgressBar
\printuntil }
We define the \c ProgressBar type in \c ProgressBar.qml. We use a
\l Rectangle type to create the progress bar and apply a \l NumberAnimation
to its \c opacity property to change the color of the bar from black to
white as data loading proceeds:
\quotefromfile demos/photoviewer/PhotoViewerCore/ProgressBar.qml
\skipto Item
\printuntil /^\}/
In your apps, you can also use the \l ProgressBar type from the
\l {Qt Quick Controls} module.
\section1 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 \l Column type in \c main.qml to position buttons for adding and
editing albums and exiting the application:
\quotefromfile demos/photoviewer/main.qml
\skipto Column
\printuntil quit()
\printuntil }
\printuntil }
We use the \l[QML]{Qt::}{qsTr()} command to mark the button labels
translatable.
We use the \c lupdate tool to generate the translation source files and
the \c lrelease tool to convert the translated strings to the QM files used
by the application at runtime. These files are stored in the \c i18n
directory.
To make the application aware of the translations, we add code to the
\c main() function in the \c {main.cpp} file. The code creates a
\l QTranslator object, loads a translation according to the current locale
at runtime, and installs the translator object into the application:
\quotefromfile demos/photoviewer/main.cpp
\skipto main
\printuntil app.installTranslator
\sa {QML Applications}
*/

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>AlbumDelegate</name>
<message>
<location filename="../PhotoViewerCore/AlbumDelegate.qml" line="111"/>
<source>Remove</source>
<translation>Entfernen</translation>
</message>
</context>
<context>
<name>main</name>
<message>
<location filename="../main.qml" line="93"/>
<source>Add</source>
<translation>Zufügen</translation>
</message>
<message>
<location filename="../main.qml" line="102"/>
<source>Edit</source>
<translation>Bearbeiten</translation>
</message>
<message>
<location filename="../main.qml" line="107"/>
<source>Quit</source>
<translation>Verlassen</translation>
</message>
<message>
<location filename="../main.qml" line="122"/>
<source>Back</source>
<translation>Zurück</translation>
</message>
</context>
</TS>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en">
</TS>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>AlbumDelegate</name>
<message>
<location filename="../PhotoViewerCore/AlbumDelegate.qml" line="111"/>
<source>Remove</source>
<translation>Supprimer</translation>
</message>
</context>
<context>
<name>main</name>
<message>
<location filename="../main.qml" line="93"/>
<source>Add</source>
<translation>Ajouter</translation>
</message>
<message>
<location filename="../main.qml" line="102"/>
<source>Edit</source>
<translation>Éditer</translation>
</message>
<message>
<location filename="../main.qml" line="107"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../main.qml" line="122"/>
<source>Back</source>
<translation>Retour</translation>
</message>
</context>
</TS>

View File

@ -1,21 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QTranslator>
#include <QDebug>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QTranslator qtTranslator;
if (qtTranslator.load(QLocale(), "qml", "_", ":/i18n/"))
app.installTranslator(&qtTranslator);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}

View File

@ -1,91 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Controls
import "PhotoViewerCore"
ApplicationWindow {
id: mainWindow
visible: true
Rectangle {
focus: true
Keys.onBackPressed: {
event.accepted = true
backButton.clicked()
}
}
property real downloadProgress: 0
property bool imageLoading: false
property bool editMode: false
width: 800; height: 480; color: "#d5d6d8"
ListModel {
id: photosModel
ListElement { tag: "Flowers" }
ListElement { tag: "Wildlife" }
ListElement { tag: "Prague" }
}
DelegateModel { id: albumVisualModel; model: photosModel; delegate: AlbumDelegate {} }
GridView {
id: albumView; width: parent.width; height: parent.height; cellWidth: 210; cellHeight: 220
model: albumVisualModel.parts.album; visible: albumsShade.opacity != 1.0
}
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
}
}
Rectangle {
id: albumsShade; color: mainWindow.color
width: parent.width; height: parent.height; opacity: 0.0
}
ListView { anchors.fill: parent; model: albumVisualModel.parts.browser; interactive: false }
Button {
id: backButton
label: qsTr("Back")
rotation: 3
x: parent.width - backButton.width - 6
y: -backButton.height - 8
visible: Qt.platform.os !== "android"
}
Rectangle { id: photosShade; color: 'black'; width: parent.width; height: parent.height; opacity: 0; visible: opacity != 0.0 }
ListView { anchors.fill: parent; model: albumVisualModel.parts.fullscreen; interactive: false }
Item { id: foreground; anchors.fill: parent }
ProgressBar {
progress: mainWindow.downloadProgress; width: parent.width; height: 4
anchors.bottom: parent.bottom; opacity: mainWindow.imageLoading; visible: opacity != 0.0
}
}

View File

@ -1,35 +0,0 @@
TEMPLATE = app
QT += qml quick
CONFIG += lrelease embed_translations
SOURCES += main.cpp
lupdate_only {
SOURCES = *.qml \
PhotoViewerCore/*.qml \
PhotoViewerCore/script/*.js
}
TRANSLATIONS += i18n/qml_fr.ts \
i18n/qml_de.ts
EXTRA_TRANSLATIONS += i18n/qml_en.ts
RESOURCES += main.qml \
PhotoViewerCore/AlbumDelegate.qml \
PhotoViewerCore/BusyIndicator.qml \
PhotoViewerCore/Button.qml \
PhotoViewerCore/EditableButton.qml \
PhotoViewerCore/PhotoDelegate.qml \
PhotoViewerCore/ProgressBar.qml \
PhotoViewerCore/RssModel.qml \
PhotoViewerCore/Tag.qml \
PhotoViewerCore/images/box-shadow.png \
PhotoViewerCore/images/busy.png \
PhotoViewerCore/images/cardboard.png \
PhotoViewerCore/script/script.mjs \
target.path = $$[QT_INSTALL_EXAMPLES]/demos/photoviewer
INSTALLS += target

View File

@ -68,7 +68,6 @@ tst_examples::tst_examples()
#ifdef QT_NO_XMLPATTERNS
excludedDirs << "demos/twitter";
excludedDirs << "demos/flickr";
excludedDirs << "demos/photoviewer";
excludedFiles << "snippets/qml/xmlrole.qml";
excludedFiles << "particles/itemparticle/particleview.qml";
excludedFiles << "views/visualdatamodel/slideshow.qml";