フォトサーフェス

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

フォトサーフェスではFolderListModelFolderDialog を備えたRepeater を使用して、ユーザーが選択したフォルダから画像にアクセスする方法と、Qt Quick 入力ハンドラを使用して、同じアイテム内でのドラッグ、回転、ピンチ操作によるズームを処理する方法を紹介します。

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

例の実行

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

メインウィンドウの作成

Photo Surfaceアプリのメインウィンドウを作成するために、Window QMLタイプをルートアイテムとして使用します。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アプリケーションも参照してください

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.