フォトサーフェス

リピータとFolderListModel を使ってフォルダ内のコンテンツにアクセスし、PinchHandler を使って取得したコンテンツのピンチジェスチャーを処理する、タッチデバイス用の QML アプリです。

Photo Surfaceは、FolderListModelFolderDialog を持つRepeater を使用して、ユーザーが選択したフォルダから画像にアクセスする方法と、Qt Quick Input Handlers を使用して、同じアイテム内でのドラッグ、回転、ピンチによるズームを処理する方法を示します。

すべてのアプリコードは、1つのQMLファイル(photosurface.qml )に含まれています。インラインJavaScriptコードは、写真表面上の画像を配置、回転、拡大縮小するために使用されます。

サンプルを実行する

Qt Creator からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Example を参照してください。

メインウィンドウの作成

Photo Surfaceアプリのメインウィンドウを作成するために、Window QMLタイプをルートアイテムとして使用します。これにより、Qt Quickグラフィカル タイプで使用するウィンドウが自動的に設定されます:

Window {
    id: root
    visible: true
    width: 1024; height: 600
    color: "black"
    title: Application.displayName + " : " + folderModel.folder
    property real defaultSize: 200
    property real surfaceViewportRatio: 1.5
    property var imageNameFilters: ["*.png", "*.jpg", "*.gif"] // overridden in main.cpp

フォルダコンテンツへのアクセス

Repeater QML型とFolderListModel QML型を使って、少なくともフォルダ内のGIF、JPG、PNG画像を表示します(ただし、main.cppはサポートする画像タイプのリストを拡張することがあります):

        Repeater {
            model: FolderListModel {
                id: folderModel
                objectName: "folderModel"
                showDirs: false
                nameFilters: root.imageNameFilters
            }

FolderListModel 型を使用するには、インポートする必要があります:

import Qt.labs.folderlistmodel

FolderDialog 、ユーザーが画像を含むフォルダを選択できるようにします:

    FolderDialog {
        id: folderDialog
        title: qsTr("Choose a folder with some images")
        onAccepted: folderModel.folder = selectedFolder
    }

FolderDialog タイプを使用するには、次の import ステートメントを追加します:

import QtQuick.Dialogs

フォルダパスがコマンドライン引数として与えられていない限り、最初のスライドショーが終了したときにファイルダイアログを開くためにfolderDialog.open() 関数を使用します:

        Component.onDestruction: {
            folderIcon.visible = true
            const lastArg = Application.arguments.slice(-1)[0]
            const standardPicturesLocations = StandardPaths.standardLocations(StandardPaths.PicturesLocation)
            const hasValidPicturesLocation = standardPicturesLocations.length > 0
            if (hasValidPicturesLocation)
                folderDialog.currentFolder = standardPicturesLocations[0]
            if (/.*hotosurface.*|--+/.test(lastArg)) {
                if (hasValidPicturesLocation)
                    folderModel.folder = standardPicturesLocations[0]
                else
                    folderDialog.open()
            }

ユーザーはフォルダダイアログのアイコンをクリックして開くこともできます。アイコンの表示にはImageQMLタイプを使用します。Imageタイプの内部では、onTapped シグナルハンドラを持つTapHandler を使い、folderDialog.open() 関数を呼び出しています:

    Image {
        id: folderIcon
        visible: false
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.margins: 10
        source: "resources/folder.png"

        TapHandler { onTapped: folderDialog.open() }

        HoverHandler { id: folderMouse }

        ToolTip.visible: folderMouse.hovered
        ToolTip.text: qsTr(`Open an image directory (${openShortcut.nativeText})`)
        ToolTip.delay: 1000

        Shortcut {
            id: openShortcut
            sequence: StandardKey.Open
            onActivated: folderDialog.open()
        }
    }

フォトサーフェスに画像を表示する

Repeater のデリゲートとしてRectangle を使用し、FolderListModel が選択されたフォルダ内で見つけた各画像のフレームを提供します。JavaScriptのMath() メソッドを使用して、写真サーフェス上にフレームをランダムに配置し、ランダムな角度で回転させ、画像を拡大縮小します。枠の色はインタラクションの状態を示している:

            delegate: Rectangle {
                required property date fileModified
                required property string fileName
                required property url fileUrl
                id: photoFrame
                objectName: "frame-" + fileName
                width: image.width * (1 + 0.10 * image.height / image.width)
                height: image.height * 1.10
                scale: root.defaultSize / Math.max(image.sourceSize.width, image.sourceSize.height)
                border.color: pinchHandler.active || dragHandler.active ? "darkturquoise"
                                                                        : mouse.hovered ? "darkseagreen"
                                                                                        : "saddlebrown"
                border.width: 3 / scale
                antialiasing: true
                Component.onCompleted: {
                    x = Math.random() * root.width - width / 2
                    y = Math.random() * root.height - height / 2
                    rotation = Math.random() * 13 - 6
                }

                Image {
                    id: image
                    anchors.centerIn: parent
                    fillMode: Image.PreserveAspectFit
                    source: photoFrame.fileUrl
                    antialiasing: true
                }

ドラッグとピンチのジェスチャーとマウスを扱う

ドラッグ、ピンチ・ズーム、回転を処理するために、各フォトフレームでDragHandlerPinchHandler を使用しています:

                PinchHandler {
                    id: pinchHandler
                    minimumRotation: -360
                    maximumRotation: 360
                    minimumScale: 0.1
                    maximumScale: 10
                    grabPermissions: PointerHandler.CanTakeOverFromAnything // and never gonna give it up
                    onActiveChanged: if (active) photoFrame.z = ++flick.highestZ
                }

                DragHandler {
                    id: dragHandler
                    onActiveChanged: {
                        if (active)
                            photoFrame.z = ++flick.highestZ
                        else
                            anim.restart(centroid.velocity)
                    }
                }

PinchHandler は Rectangle の内部で宣言されているため、PinchHandler.target プロパティが暗黙的に設定され、ピンチジェスチャーが Rectangle を操作できるようになっています。回転プロパティは、フレームがあらゆる角度に回転できることを指定し、スケールプロパティは、フレームが0.110 の間で拡大縮小できることを指定します。ピンチ・ジェスチャーは、タッチスクリーンやマルチタッチ・タッチパッドでも同様に機能します。フレームを変形すると、そのコンテンツ(イメージ)が変形します。

DragHandler.target プロパティも暗黙的に設定されているため、タッチスクリーンやタッチパッド上で、またはマウスを使って、指一本で写真をドラッグすることができます。DragHandler'のonActiveChanged シグナルハンドラでは、z プロパティの値を増加させることで、選択されたフォトフレームを一番上に上げます(一方、共有されたhighestZ プロパティには、これまでに使用された最大のz 値が保持されます)。ドラッグが終わると、しばらく同じ方向に動き続けるアニメーションを開始し、速度を落として停止します。サーフェスの端を越えて写真を「フライング」すると、サーフェスは新しい位置に合わせて拡大します。リピーターと、リピーターが入力するすべてのフォトフレームを含むScrollView 、サーフェスのさまざまな部分を見るために移動することができます。

DragHandlers を介して2本の指で2枚の写真をドラッグすることができ、2本の指で1つのPinchHandler をピンチすることもできます。また、写真のコレクションは互いに重なり合う傾向があるため、PinchHandler が優先されるようにgrabPermissions を調整する必要があります。ピンチジェスチャーが始まると、DragHandlers が再びタッチポイントのグラブを引き継ぐことを許可しません。

タッチデバイスを持たないコンピューターでもインタラクティブに操作できるように、上記のborder.colorが依存するHoverHandler 、2つのWheelHandlers 。もうひとつは、Shiftキーを押しながらマウス・ホイールを使って、カーソルのある位置を拡大・縮小するものだ。どちらも、上のDragHandler と同じように写真を上昇させることができます:

                HoverHandler { id: mouse }

                WheelHandler {
                    acceptedModifiers: Qt.ControlModifier
                    property: "rotation"
                    onActiveChanged: if (active) photoFrame.z = ++flick.highestZ
                }

                WheelHandler {
                    acceptedModifiers: Qt.ShiftModifier
                    property: "scale"
                    onActiveChanged: if (active) photoFrame.z = ++flick.highestZ
                }

プロジェクト例 @ code.qt.io

QMLアプリケーションも参照してください

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。