Photo Surface
タッチデバイス向けのQMLアプリで、FolderListModel を備えたRepeaterを使用してフォルダ内のコンテンツにアクセスし、PinchHandler を用いて取得したコンテンツに対するピンチジェスチャーを処理します。

「Photo Surface」は、Repeater をFolderListModel およびFolderDialog と組み合わせて使用し、ユーザーが選択したフォルダ内の画像にアクセスする方法を示しています。また、この例では、Qt Quick のInput Handlerを使用して、同一のアイテム内でのドラッグ、回転、ピンチズームを処理する方法も示しています。
アプリのコードはすべて、1つのQMLファイル(photosurface.qml )にまとめられています。インラインのJavaScriptコードにより、画像がフォトサーフェス上に配置、回転、拡大縮小されます。
例の動作確認
この例は、以下の手順で実行できます。
- Qt Creator
Welcome モードを開き、Examples からこのサンプルを選択します。詳細については、Qt Creator の「チュートリアル:ビルドと実行」を参照してください。
- Qt Extension for Visual Studio Code
Command Palette から `Qt: Open Qt examples ` コマンドを実行し、リストからこの例を選択します。詳細については、Qt Extension for Visual Studio Code: チュートリアル: ビルドと実行を参照してください。
メインウィンドウの作成
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 と組み合わせて使用し、フォルダ内にある GIF、JPG、PNG 画像を表示します(ただし、main.cpp によってサポートされる画像形式のリストが拡張される可能性があります):
Repeater {
model: FolderListModel {
id: folderModel
objectName: "folderModel"
showDirs: false
nameFilters: root.imageNameFilters
}FolderListModel 型をインポートします:
import Qt.labs.folderlistmodelFolderDialogを使用すると、ユーザーは画像が格納されているフォルダを選択できます:
FolderDialog {
id: folderDialog
title: qsTr("Choose a folder with some images")
onAccepted: folderModel.folder = selectedFolder
}FolderDialog タイプを使用するには、次のインポート文を追加します:
import QtQuick.DialogsfolderDialog.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()
}
}Photoサーフェスへの画像の表示
Rectangle は、Repeater のデリゲートとして使用され、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
}ドラッグおよびピンチジェスチャー、およびマウスの処理
各写真フレーム内の `DragHandler ` および `PinchHandler ` が、ドラッグ、ピンチズーム、および回転を処理します:
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を操作できるようになります。rotationプロパティにより、フレームはあらゆる角度に回転可能となり、scaleプロパティにより、0.1 から10 の範囲で拡大縮小が可能となります。ピンチジェスチャーは、タッチスクリーンでもマルチタッチ対応のタッチパッドでも同様に正常に動作します。 フレームを変形させると、その内容(Image)も変形します。
DragHandler.target プロパティも暗黙的に設定されているため、タッチスクリーンやタッチパッドでは1本の指で、マウスでも写真をドラッグすることができます。DragHandler のonActiveChanged シグナルハンドラでは、選択された写真フレームのz プロパティの値を増やすことで、そのフレームを最前面に表示します(一方、共有プロパティであるhighestZ には、これまでに使用された最大のz 値が保持されます)。 ドラッグが終了すると、アニメーションが開始され、しばらくの間同じ方向に動き続け、徐々に減速して停止します。写真をサーフェスの端を越えて「フリック」すると、サーフェスは新しい位置に合わせて拡張されます。ScrollView (Repeaterおよびそこに配置されたすべての写真フレームを含む)を操作することで、サーフェスのさまざまな部分を閲覧できます。
2本の指を使ってDragHandlerを介して2枚の写真をドラッグしたり、2本の指でPinchHandler をピンチしたりできるほか、写真のコレクションは互いに重なり合う傾向があるため、grabPermissions を調整してPinchHandler に優先順位を持たせる必要があります。つまり、ピンチジェスチャーが開始された際、DragHandlerがタッチポイントのグリップを再び奪い取れないようにします。
タッチデバイスがないコンピュータでもこの例をよりインタラクティブにするには、上記の border.color に依存するHoverHandler と、2つのWheelHandlers を追加します。 1つは、Ctrlキーを押しながらマウスホイールを回すと、マウスカーソルの周囲で写真を回転させることができます。もう1つは、Shiftキーを押しながらマウスホイールを回すと、カーソルの位置を拡大または縮小できます。これら2つとも、前述の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
}ソースファイル
「QMLアプリケーション」および「Qt Quick の例とチュートリアル」も参照してください 。
© 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.