사진 표면

FolderListModel 를 사용하여 폴더의 콘텐츠에 액세스하고 PinchHandler 를 사용하여 가져온 콘텐츠의 핀치 제스처를 처리하는 리피터를 사용하는 터치 디바이스용 QML 앱입니다.

Photo Surface는 RepeaterFolderListModelFolderDialog 를 사용하여 사용자가 선택한 폴더의 이미지에 액세스하는 방법과 Qt Quick 입력 핸들러를 사용하여 동일한 항목 내에서 드래그, 회전 및 핀치 확대/축소를 처리하는 방법을 보여줍니다.

모든 앱 코드는 하나의 QML 파일( photosurface.qml)에 포함되어 있습니다. 인라인 JavaScript 코드는 사진 표면에서 이미지를 배치, 회전 및 확대/축소하는 데 사용됩니다.

예제 실행하기

에서 예제를 실행하려면 Qt Creator에서 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 예제 빌드 및 실행하기를 참조하세요.

메인 창 만들기

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

사진 표면에 이미지 표시하기

Repeater 의 델리게이트로 Rectangle 을 사용하여 FolderListModel 이 선택한 폴더에서 찾은 각 이미지의 프레임을 제공합니다. JavaScript Math() 메서드를 사용하여 사진 표면에 프레임을 임의로 배치하고 임의의 각도로 회전하고 이미지의 크기를 조정합니다. 테두리 색상은 상호 작용 상태를 나타냅니다:

            delegate: Rectangle {
                required property date fileModified
                required property string fileName
                required property url fileUrl
                id: photoFrame
                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 은 직사각형 안에 선언되어 있으므로 PinchHandler.target 속성은 핀치 제스처가 직사각형을 조작하도록 암시적으로 설정되어 있습니다. 회전 속성은 프레임을 모든 각도로 회전할 수 있음을 지정하고, 크기 속성은 0.110 사이에서 크기를 조정할 수 있음을 지정합니다. 핀치 제스처는 터치스크린이나 멀티터치 터치패드에서도 동일하게 작동합니다. 프레임을 변형하면 해당 콘텐츠( 이미지)가 변형됩니다.

DragHandler.target 속성도 암시적으로 설정되어 있으므로 터치스크린이나 터치패드에서 한 손가락으로 또는 마우스로 사진을 드래그할 수 있습니다. DragHandleronActiveChanged 신호 핸들러에서는 선택한 사진 프레임의 z 속성 값을 높여서 맨 위로 올립니다(공유 highestZ 속성은 지금까지 사용된 값 중 가장 큰 z 값을 보유함). 드래그가 끝나면 잠시 동안 같은 방향으로 계속 움직이다가 속도가 느려지고 멈추는 애니메이션이 시작됩니다. 서페이스의 가장자리를 지나 사진을 '던지면' 서페이스가 새 위치에 맞게 확장됩니다. 리피터와 리피터가 채우는 모든 사진 프레임이 포함된 ScrollView 을 통해 이동하여 서페이스의 다른 부분을 볼 수 있습니다.

드래그핸들러를 통해 두 손가락으로 두 개의 사진을 드래그할 수 있고 두 손가락으로 PinchHandler 하나를 핀치할 수도 있으며 사진 모음이 서로 쌓이는 경향이 있으므로 grabPermissions 을 조정하여 PinchHandler 이 우선권을 갖도록 하여 핀치 제스처가 시작되면 드래그핸들러가 터치포인트 잡기를 다시 인수하지 못하도록 해야 합니다.

터치 디바이스가 없는 컴퓨터에서 예제를 더욱 인터랙티브하게 만들기 위해 위의 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
                }

예제 프로젝트 @ code.qt.io

QML 애플리케이션도참조하세요 .

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