Auf dieser Seite

Foto-Oberfläche

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

Eine Fotosammlung

„Photo Surface“ veranschaulicht, wie man mit einem „ Repeater “, einem „ FolderListModel “ und einem „ FolderDialog “ auf Bilder aus einem vom Benutzer ausgewählten Ordner zugreift. Das Beispiel zeigt außerdem, wie man das Ziehen, Drehen und Zoomen per Pinch-Geste innerhalb desselben Elements mithilfe von „Qt Quick “-Eingabehandlern handhabt.

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

Ausführen des Beispiels

Sie können das Beispiel wie folgt ausführen:

Erstellen des Hauptfensters

Um das Hauptfenster für die App „Photo Surface“ zu erstellen, verwenden Sie den QML-Typ „ Window “ als Stammelement. Dieser richtet das Fenster automatisch für die Verwendung mit Qt Quick grafische 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

Auf den Inhalt eines Ordners zugreifen

Verwenden Sie den QML-Typ „ Repeater “ zusammen mit der „ FolderListModel “, um die in einem Ordner befindlichen GIF-, JPG- und PNG-Bilder anzuzeigen (wobei „main.cpp“ die Liste der unterstützten Bildformate möglicherweise erweitert):

        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 Benutzern, 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 Typ „FolderDialog“ zu verwenden, fügen Sie die folgende Importanweisung hinzu:

import QtQuick.Dialogs

Die Funktion „ folderDialog.open() “ öffnet den Dateidialog, sobald die erste Diashow beendet ist, sofern kein Ordnerpfad als Befehlszeilenargument angegeben wurde:

        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 den Ordnerdialog auch durch Klicken auf das entsprechende Symbol öffnen. Hier wird das Symbol durch einen „Image“-QML-Typ dargestellt. Innerhalb des „Image“-Typs ruft ein „ TapHandler “ mit dem Signal-Handler „ 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()
        }
    }

Anzeigen von Bildern auf der „Photo“-Oberfläche

Ein „ Rectangle “ wird als Delegat für ein „ Repeater “ verwendet, um für jedes Bild, das „ FolderListModel “ im ausgewählten Ordner findet, einen Rahmen bereitzustellen. JavaScript-Methoden von „ Math() “ platzieren die Rahmen zufällig auf der Fotooberfläche, drehen sie in zufälligen Winkeln und skalieren die Bilder. Die Rahmenfarbe zeigt den Interaktionsstatus 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
                }

Verarbeitung von Zieh- und Pinch-Gesten sowie der Maus

Ein ` DragHandler ` und ein ` PinchHandler ` in jedem Fotorahmen verarbeiten Zieh-, Zoom- und Drehgesten:

                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 der „ PinchHandler “ innerhalb des „Rectangle“ deklariert ist, wird die Eigenschaft „ PinchHandler.target “ implizit so festgelegt, dass Pinch-Gesten das „Rectangle“ 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 auf einem Touchscreen oder einem Multi-Touch-Touchpad gleichermaßen gut. Durch die Transformation des Rahmens wird auch dessen Inhalt (das Bild) transformiert.

Die Eigenschaft „ DragHandler.target “ ist ebenfalls implizit festgelegt, sodass Sie ein Foto mit einem Finger auf einem Touchscreen oder Touchpad oder mit einer Maus ziehen können. Im Signal-Handler „ onActiveChanged “ des „ DragHandler “ wird der ausgewählte Fotorahmen nach vorne geholt, indem der Wert seiner Eigenschaft „ z “ erhöht wird (während die gemeinsam genutzte Eigenschaft „ highestZ “ den größten bisher verwendeten Wert für „ z “ enthält). Wenn das Ziehen endet, beginnt eine Animation, die den Fotorahmen noch eine Weile in derselben Richtung weiterbewegt, bis er langsamer wird und zum Stillstand kommt. Wenn Sie ein Foto über den Rand der Oberfläche hinaus „schleudern“, erweitert sich die Oberfläche, um die neue Position aufzunehmen. Sie können sich frei bewegen, um verschiedene Bereiche der Oberfläche über den „ ScrollView “ zu betrachten, der den „Repeater“ und alle von ihm gefüllten Fotorahmen enthält.

Da Sie zwei Fotos mit zwei Fingern über deren „DragHandler“ ziehen und zudem einen „ PinchHandler “ mit zwei Fingern zusammenziehen können und sich Fotosammlungen dazu neigen, sich übereinander zu stapeln, müssen Sie „ grabPermissions “ so anpassen, dass der „ PinchHandler “ Vorrang hat: Wenn die Zusammenzugsgeste beginnt, verhindert er, dass die „DragHandler“ die Berührungspunkte wieder übernehmen.

Um das Beispiel auf Computern ohne Touch-Geräte interaktiver zu gestalten, fügen Sie den „ HoverHandler “ hinzu, von dem die oben genannte „border.color“ abhängt, sowie zwei „ WheelHandlers “. Mit dem einen können Sie die Strg-Taste gedrückt halten und das Mausrad verwenden, um das Foto um den Mauszeiger herum 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. Beide heben das Foto auf dieselbe Weise an wie der oben genannte ` 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
                }

Quelldateien

Beispielprojekt auf 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.