照片表面
这是一个用于触摸设备的 QML 应用程序,它使用带有FolderListModel 的 Repeater 来访问文件夹中的内容,并使用PinchHandler 来处理获取内容上的捏合手势。

Photo Surface演示了如何使用带有FolderListModel 和FolderDialog 的Repeater 访问用户所选文件夹中的图片。该示例还展示了如何使用Qt Quick 输入处理程序在同一项目中处理拖动、旋转和捏合缩放。
所有应用程序代码都包含在一个 QML 文件photosurface.qml 中。内联 JavaScript 代码可在照片表面放置、旋转和缩放图片。
运行示例
要从 Qt Creator,打开Welcome 模式,然后从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行。
创建主窗口
要为 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.folderlistmodel
通过 FolderDialog,用户可以选择包含图像的文件夹:
FolderDialog { id: folderDialog title: qsTr("Choose a folder with some images") onAccepted: folderModel.folder = selectedFolder }
要使用 FolderDialog 类型,请添加以下导入语句:
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() }
用户也可单击文件夹对话框图标打开它。在这里,一个图像QML 类型会显示该图标。在图像类型中,带有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() } }
在照片表面显示图像
Rectangle 用作Repeater 的委托,为FolderListModel 在选定文件夹中找到的每张图片提供一个边框。JavaScriptMath() 方法会在照片表面随机放置边框、以随机角度旋转边框并缩放图片。边框颜色表示交互状态:
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 是在矩形内部声明的,因此PinchHandler.target 属性是隐式设置的,因此捏合手势可操作矩形。旋转属性指定相框可以旋转到所有角度,缩放属性指定相框可以在0.1 和10 之间缩放。捏合手势在触摸屏或多点触控触摸板上同样有效。变换框架可变换其内容(图像)。
DragHandler.target 属性也是隐式设置,因此您可以用一根手指在触摸屏或触摸板上拖动照片,也可以用鼠标拖动照片。在DragHandler 的onActiveChanged 信号处理程序中,选定的相框会通过增加其z 属性的值上升到顶部(而共享的highestZ 属性则保存着迄今为止使用过的最大z 值)。拖动结束后,动画会开始让相框沿同一方向移动一小会儿,然后放慢速度并停止。如果将照片 "甩 "过曲面的边缘,曲面就会展开以容纳新的位置。您可以通过包含中继器及其填充的所有相框的ScrollView 移动查看曲面的不同部分。
由于您可以通过 DragHandlers 用两根手指拖动两张照片,也可以用两根手指捏一张PinchHandler ,而照片集往往会堆叠在一起,因此您需要调整grabPermissions ,使PinchHandler 具有优先权:当捏的手势开始时,它不允许 DragHandlers 再次接管触摸点抓取。
为了使示例在没有触摸设备的电脑上更具交互性,请添加上述 border.color 所依赖的HoverHandler 以及两个WheelHandlers 。其中一个允许你按住 Ctrl 键并使用鼠标滚轮围绕鼠标光标旋转照片;另一个允许你按住 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 }
源文件
另请参阅 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.