Qt 1.6 | Enginio Manual | Enginio QML Examples - Image Gallery | image-gallery.qml Example File

Qt 1.6.3 Reference Documentation

# image-gallery.qml Example File

image-gallery/image-gallery.qml

      /****************************************************************************
      **
      ** Copyright (C) 2015 The Qt Company Ltd.
      ** Contact: http://www.qt.io/licensing/
      **
      ** This file is part of the examples of the Qt Toolkit.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** You may use this file under the terms of the BSD license as follows:
      **
      ** "Redistribution and use in source and binary forms, with or without
      ** modification, are permitted provided that the following conditions are
      ** met:
      **   * Redistributions of source code must retain the above copyright
      **     notice, this list of conditions and the following disclaimer.
      **   * Redistributions in binary form must reproduce the above copyright
      **     notice, this list of conditions and the following disclaimer in
      **     the documentation and/or other materials provided with the
      **     distribution.
      **   * Neither the name of The Qt Company Ltd nor the names of its
      **     contributors may be used to endorse or promote products derived
      **     from this software without specific prior written permission.
      **
      **
      ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
      **
      ** $QT_END_LICENSE$
      **
      ****************************************************************************/

      import QtQuick 2.0
      import Enginio 1.0

      import QtQuick.Dialogs 1.0
      import QtQuick.Controls 1.0
      import QtQuick.Layouts 1.0

      /*
       * Enginio image gallery example.
       *
       * Main window contains list of enginioModel on the backend and button for uploading
       * new enginioModel. Image list contains image thumbnail (generated by Enginio
       * backend) and some image metadata. Clicking list items downloads image file
       * and displays it in dialog window. Clicking the red x deletes enginioModel from
       * backend.
       *
       * In the backend enginioModel are represented as objects of type "objects.image". These
       * objects contain a property "file" which is a reference to the actual binary file.
       */

      Item {
          id: main
          width: 460
          height: 640

          BackendHelper{
              id: backendHelper
          }

          property var imagesUrl: new Object
          Rectangle {
              id: root
              anchors.fill: parent
              opacity: 1 - backendHelper.opacity
              color: "#f4f4f4"

              // Enginio client specifies the backend to be used
              EnginioClient {
                  id: client
                  backendId: backendHelper.backendId
                  onError: console.log("Enginio error: " + reply.errorCode + ": " + reply.errorString)
              }

              EnginioModel {
                  id: enginioModel
                  client: client
                  query: { // query for all objects of type "objects.image" and include not null references to files
                      "objectType": "objects.image",
                      "include": {"file": {}},
                      "query" : { "file": { "$ne": null } }
                  }
              }

              // Delegate for displaying individual rows of the model
              Component {
                  id: imageListDelegate

                  BorderImage {
                      height: 120
                      width: parent.width
                      border.top: 4
                      border.bottom: 4
                      source: hitbox.pressed ? "qrc:images/delegate_pressed.png" : "qrc:images/delegate.png"

                      Image {
                          id: shadow
                          anchors.top: parent.bottom
                          width: parent.width
                          visible: !hitbox.pressed
                          source: "qrc:images/shadow.png"
                      }

                      Image {
                          id: image
                          x: 10
                          width: 100
                          height: 100
                          anchors.verticalCenter: parent.verticalCenter
                          opacity: image.status == Image.Ready ? 1 : 0
                          Behavior on opacity { NumberAnimation { duration: 100 } }
                          Component.onCompleted: {
                              if (id in imagesUrl) {
                                  image.source = imagesUrl[id]
                              } else {
                                  var data = { "id": file.id,
                                      "variant": "thumbnail"}
                                  var reply = client.downloadUrl(data)
                                  reply.finished.connect(function() {
                                      imagesUrl[id] = reply.data.expiringUrl
                                      if (image && reply.data.expiringUrl) // It may be deleted as it is delegate
                                          image.source = reply.data.expiringUrl
                                  })
                              }
                          }
                      }
                      Rectangle {
                          color: "transparent"
                          anchors.fill: image
                          border.color: "#aaa"
                          Rectangle {
                              id: progressBar
                              property real value:  image.progress
                              anchors.bottom: parent.bottom
                              width: image.width * value
                              height: 4
                              color: "#49f"
                              opacity: image.status != Image.Ready ? 1 : 0
                              Behavior on opacity {NumberAnimation {duration: 100}}
                          }
                      }

                      Column {
                          anchors.left: image.right
                          anchors.right: deleteIcon.left
                          anchors.margins: 12
                          y: 10
                          Text {
                              height: 33
                              width: parent.width
                              verticalAlignment: Text.AlignVCenter
                              font.pixelSize: height * 0.5
                              text: name ? name : ""
                              elide: Text.ElideRight
                          }
                          Text {
                              height: 33
                              width: parent.width
                              verticalAlignment: Text.AlignVCenter
                              font.pixelSize: height * 0.5
                              text: sizeStringFromFile(file)
                              elide:Text.ElideRight
                              color: "#555"
                          }
                          Text {
                              height: 33
                              width: parent.width
                              verticalAlignment: Text.AlignVCenter
                              font.pixelSize: height * 0.5
                              text: timeStringFromFile(file)
                              elide:Text.ElideRight
                              color: "#555"
                          }
                      }

                      // Clicking list item opens full size image in separate dialog
                      MouseArea {
                          id: hitbox
                          anchors.fill: parent
                          onClicked: {
                              imageDialog.fileId = file.id;
                              imageDialog.visible = true
                              root.state = "view"
                          }
                      }

                      // Delete button
                      Image {
                          id: deleteIcon
                          anchors.right: parent.right
                          anchors.verticalCenter: parent.verticalCenter
                          anchors.rightMargin: 18
                          source: removeMouseArea.pressed ?"qrc:icons/delete_icon_pressed.png" : "qrc:icons/delete_icon.png"
                          MouseArea {
                              id: removeMouseArea
                              anchors.fill: parent
                              anchors.margins: -10
                              onClicked: enginioModel.remove(index)
                          }
                      }
                  }
              }

              // A simple layout:
              // a listview and a line edit with button to add to the list
              Rectangle {
                  id: header
                  anchors.top: parent.top
                  width: parent.width
                  height: 70
                  color: "white"

                  Row {
                      id: logo
                      anchors.centerIn: parent
                      anchors.horizontalCenterOffset: -4
                      spacing: 6
                      Image {
                          source: "qrc:images/enginio.png"
                          width:160 ; height: 60
                          fillMode:Image.PreserveAspectFit
                      }
                      Text {
                          text: "Gallery"
                          anchors.verticalCenter: parent.verticalCenter
                          anchors.verticalCenterOffset: -3
                          font.bold: true
                          font.pixelSize: 46
                          color: "#555"
                      }
                  }
                  Rectangle {
                      width: parent.width ; height: 1
                      anchors.bottom: parent.bottom
                      color: "#bbb"
                  }
              }

              Row {
                  id: listLayout

                  Behavior on x {NumberAnimation{ duration: 400 ; easing.type: "InOutCubic"}}
                  anchors.top: header.bottom
                  anchors.bottom: footer.top

                  ListView {
                      id: imageListView
                      model: enginioModel // get the data from EnginioModel
                      delegate: imageListDelegate
                      clip: true
                      width: root.width
                      height: parent.height
                      // Animations
                      add: Transition { NumberAnimation { properties: "y"; from: root.height; duration: 250 } }
                      removeDisplaced: Transition { NumberAnimation { properties: "y"; duration: 150 } }
                      remove: Transition { NumberAnimation { property: "opacity"; to: 0; duration: 150 } }
                  }

                  // Dialog for showing full size image
                  Rectangle {
                      id: imageDialog
                      width: root.width
                      height: parent.height
                      property string fileId
                      color: "#333"

                      onFileIdChanged: {
                          image.source = ""
                          // Download the full image, not the thumbnail
                          var data = { "id": fileId }
                          var reply = client.downloadUrl(data)
                          reply.finished.connect(function() {
                              image.source = reply.data.expiringUrl
                          })
                      }
                      Label {
                          id: label
                          text: "Loading ..."
                          font.pixelSize: 28
                          color: "white"
                          anchors.centerIn: parent
                          visible: image.status != Image.Ready
                      }
                      Rectangle {
                          property real value: image.progress
                          anchors.bottom: parent.bottom
                          width: parent.width * value
                          height: 4
                          color: "#49f"
                          Behavior on opacity {NumberAnimation {duration: 200}}
                          opacity: image.status !== Image.Ready ? 1 : 0
                      }
                      Image {
                          id: image
                          anchors.fill: parent
                          anchors.margins: 10
                          smooth: true
                          cache: false
                          fillMode: Image.PreserveAspectFit
                          Behavior on opacity { NumberAnimation { duration: 100 } }
                          opacity: image.status === Image.Ready ? 1 : 0
                      }
                      MouseArea {
                          anchors.fill: parent
                          onClicked: root.state = ""
                      }
                  }
              }

              BorderImage {
                  id: footer

                  width: parent.width
                  anchors.bottom: parent.bottom
                  source: addMouseArea.pressed ? "qrc:images/delegate_pressed.png" : "qrc:images/delegate.png"
                  border.left: 5; border.top: 5
                  border.right: 5; border.bottom: 5

                  Rectangle {
                      y: -1 ; height: 1
                      width: parent.width
                      color: "#bbb"
                  }
                  Rectangle {
                      y: 0 ; height: 1
                      width: parent.width
                      color: addMouseArea.pressed ? "transparent" : "white"
                  }

                  Text {
                      text: "Click to upload..."
                      font.bold: true
                      font.pixelSize: 28
                      color: "#444"
                      anchors.centerIn: parent
                  }

                  Item {
                      id: addButton
                      width: 40 ; height: 40
                      anchors.margins: 20
                      anchors.right: parent.right
                      anchors.verticalCenter: parent.verticalCenter
                      Image {
                          id: removeIcon
                          source: addMouseArea.pressed ? "qrc:icons/add_icon_pressed.png" : "qrc:icons/add_icon.png"
                          anchors.centerIn: parent
                      }
                  }

                  MouseArea {
                      id: addMouseArea
                      anchors.fill: parent
                      onClicked: fileDialog.visible = true;
                  }
                  Rectangle {
                      id: progressBar
                      property real value:0
                      anchors.bottom: parent.bottom
                      width: parent.width * value
                      height: 4
                      color: "#49f"
                      Behavior on opacity {NumberAnimation {duration: 100}}
                  }
              }

              // File dialog for selecting image file from local file system
              FileDialog {
                  id: fileDialog
                  title: "Select image file to upload"
                  nameFilters: [ "Image files (*.png *.jpg *.jpeg)", "All files (*)" ]

                  onSelectionAccepted: {
                      var pathParts = fileUrl.toString().split("/");
                      var fileName = pathParts[pathParts.length - 1];
                      var fileObject = {
                          objectType: "objects.image",
                          name: fileName,
                          localPath: fileUrl.toString()
                      }
                      var reply = client.create(fileObject);
                      reply.finished.connect(function() {
                          var uploadData = {
                              file: { fileName: fileName },
                              targetFileProperty: {
                                  objectType: "objects.image",
                                  id: reply.data.id,
                                  propertyName: "file"
                              },
                          };

                          imagesUrl[reply.data.id] = reply.data.localPath

                          var uploadReply = client.uploadFile(uploadData, fileUrl)
                          progressBar.opacity = 1
                          uploadReply.progress.connect(function(progress, total) {
                              progressBar.value = progress/total
                          })
                          uploadReply.finished.connect(function() {
                              var tmp = enginioModel.query; enginioModel.query = null; enginioModel.query = tmp;
                              progressBar.opacity = 0
                          })
                      })
                  }
              }

              states: [
                  State {
                      name: "view"
                      PropertyChanges {
                          target: listLayout
                          x: -root.width
                      }
                  }
              ]
          }
          function sizeStringFromFile(fileData) {
              var str = [];
              if (fileData && fileData.fileSize) {
                  str.push("Size: ");
                  str.push(fileData.fileSize);
                  str.push(" bytes");
              }
              return str.join("");
          }

          function doubleDigitNumber(number) {
              if (number < 10)
                  return "0" + number;
              return number;
          }

          function timeStringFromFile(fileData) {
              var str = [];
              if (fileData && fileData.createdAt) {
                  var date = new Date(fileData.createdAt);
                  if (date) {
                      str.push("Uploaded: ");
                      str.push(date.toDateString());
                      str.push(" ");
                      str.push(doubleDigitNumber(date.getHours()));
                      str.push(":");
                      str.push(doubleDigitNumber(date.getMinutes()));
                  }
              }
              return str.join("");
          }
      }