Auf dieser Seite

Foto-Oberfläche

Eine QML-App für Touch-Geräte, die einen Repeater mit einem FolderListModel verwendet, um auf Inhalte in einem Ordner zuzugreifen, und ein PinchHandler, um Pinch-Gesten auf den abgerufenen Inhalten zu verarbeiten.

Eine Sammlung von Fotos

Photo Surface demonstriert, wie ein Repeater mit einem FolderListModel und einem FolderDialog verwendet wird, um auf Bilder aus einem vom Benutzer ausgewählten Ordner zuzugreifen. Das Beispiel zeigt auch, wie das Ziehen, Drehen und Zoomen innerhalb desselben Elements mithilfe von Qt Quick Input Handlers gehandhabt werden kann.

Der gesamte Anwendungscode ist in einer QML-Datei enthalten, photosurface.qml. Inline-JavaScript-Code platziert, dreht und skaliert Bilder auf der Fotooberfläche.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorauszuführen, öffnen Sie den Modus Welcome und wählen Sie das Beispiel aus Examples. Weitere Informationen finden Sie unter Qt Creator: Tutorial: Erstellen und Ausführen.

Erstellen des Hauptfensters

Um das Hauptfenster für die Photo Surface-App zu erstellen, verwenden Sie den QML-Typ Window als Stammelement. Er richtet das Fenster automatisch für die Verwendung mit Qt Quick grafischen Typen:

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

Zugriff auf den Inhalt von Ordnern

Verwenden Sie einen Repeater QML-Typ zusammen mit FolderListModel, um die GIF-, JPG- und PNG-Bilder in einem Ordner anzuzeigen (obwohl main.cpp die Liste der unterstützten Bildtypen erweitern kann):

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

Importieren Sie den Typ FolderListModel:

import Qt.labs.folderlistmodel

Ein FolderDialog ermöglicht es dem Benutzer, den Ordner auszuwählen, der die Bilder enthält:

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

Um den FolderDialog-Typ zu verwenden, fügen Sie die folgende Import-Anweisung hinzu:

import QtQuick.Dialogs

Die Funktion folderDialog.open() öffnet den Dateidialog, wenn die anfängliche Diashow beendet ist, es sei denn, es wurde ein Ordnerpfad als Befehlszeilenargument angegeben:

        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()
            }

Benutzer können auch auf das Symbol des Ordnerdialogs klicken, um ihn zu öffnen. Hier zeigt ein Image QML-Typ das Symbol an. Innerhalb des Typs Image ruft ein TapHandler mit dem Signalhandler onTapped die Funktion folderDialog.open() auf:

    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()
        }
    }

Anzeige von Bildern auf der Fotooberfläche

Ein Rectangle wird als Delegierter für ein Repeater verwendet, um einen Rahmen für jedes Bild bereitzustellen, das FolderListModel im ausgewählten Ordner findet. Die JavaScript-Methoden Math() platzieren die Rahmen zufällig auf der Fotooberfläche, drehen sie in zufälligen Winkeln und skalieren die Bilder. Die Farbe des Rahmens zeigt den Zustand der Interaktion an:

            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
                }

Handhabung von Zieh- und Kneifgesten und der Maus

Ein DragHandler und ein PinchHandler in jedem Fotorahmen dienen zum Ziehen, Zoomen und Drehen:

                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)
                    }
                }

Da PinchHandler innerhalb des Rechtecks deklariert ist, ist die Eigenschaft PinchHandler.target implizit so eingestellt, dass Pinch-Gesten das Rechteck manipulieren. Die Rotationseigenschaften legen fest, dass die Rahmen in alle Winkel gedreht werden können, und die Skalierungseigenschaften legen fest, dass sie zwischen 0.1 und 10 skaliert werden können. Die Pinch-Geste funktioniert auch auf einem Touchscreen oder einem Multi-Touch-Touchpad. Durch das Transformieren des Rahmens wird sein Inhalt (das Bild) transformiert.

Die Eigenschaft DragHandler.target ist ebenfalls implizit festgelegt, so dass Sie ein Foto mit einem Finger auf einem Touchscreen oder Touchpad oder mit einer Maus ziehen können. Im Signalhandler onActiveChanged von DragHandler wird der ausgewählte Fotorahmen nach oben gezogen, indem der Wert der Eigenschaft z erhöht wird (während die gemeinsame Eigenschaft highestZ den größten bisher verwendeten Wert z enthält). Nach Beendigung des Ziehens beginnt eine Animation, die das Bild eine Weile in dieselbe Richtung bewegt, dann langsamer wird und schließlich zum Stillstand kommt. Wenn Sie ein Foto über den Rand der Oberfläche hinaus "schleudern", dehnt sich die Oberfläche aus, um die neue Position aufzunehmen. Sie können sich bewegen, um verschiedene Teile der Oberfläche über die ScrollView zu betrachten, die den Repeater und alle von ihm befüllten Fotorahmen enthält.

Da Sie zwei Fotos mit zwei Fingern über ihre DragHandler ziehen können und Sie auch ein PinchHandler mit zwei Fingern festhalten können und sich Fotosammlungen übereinander stapeln, müssen Sie grabPermissions so anpassen, dass PinchHandler Vorrang hat: Wenn die Geste des Festhaltens beginnt, lässt sie nicht zu, dass die DragHandler die Touchpoint-Griffe wieder übernehmen.

Um das Beispiel auf Computern ohne Touch-Geräte interaktiver zu machen, fügen Sie die HoverHandler, von der die obige border.color abhängt, und zwei WheelHandlers hinzu. Mit dem einen können Sie die Strg-Taste gedrückt halten und das Mausrad verwenden, um das Foto um den Mauszeiger zu drehen; mit dem anderen können Sie die Umschalttaste gedrückt halten und das Mausrad verwenden, um die Position unter dem Mauszeiger zu vergrößern oder zu verkleinern. In beiden Fällen wird das Foto auf die gleiche Weise angehoben, wie es auf DragHandler der Fall ist:

                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
                }

Quelldateien

Beispielprojekt @ code.qt.io

Siehe auch QML-Anwendungen und Qt Quick Beispiele und 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.