このページでは

フォトサーフェス

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

写真集

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

すべてのアプリコードは、1つのQMLファイル(photosurface.qml )に含まれています。インラインJavaScriptコードは、フォトサーフェス上に画像を配置、回転、拡大縮小します。

例の実行

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

メインウィンドウの作成

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

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

フォルダ内の GIF、JPG、PNG 画像を表示するには、FolderListModel とともにRepeater QML タイプを使用します(ただし、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 シグナルハンドラを持つTapHandlerfolderDialog.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()
        }
    }

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

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

            delegate: Rectangle {
                id: photoFrame
                required property date fileModified
                required property string fileName
                required property url fileUrl
                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 。もう1つは、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 ApplicationsおよびQt Quick Examples and Tutorialsも参照して ください。

© 2026 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.