커피 머신

상태 기반 사용자 지정 사용자 인터페이스가 있는 Qt Quick 애플리케이션입니다.

커피 머신 예시 개요

이 앱은 제품 주문을 위한 일반적인 사용자 인터페이스를 구현하는 크로스 플랫폼, 다중 화면 크기 및 반응형 방식을 보여줍니다.

레시피 선택

커피 선택

커피 머신 애플리케이션을 사용하면 커피 종류를 선택하고 주문할 수 있는 해당 유형의 커피가 몇 잔 남았는지 표시할 수 있습니다.

커피 사용자 지정

레시피를 선택하면 앱에 커피 블렌드에 포함될 커피의 비율이 표시됩니다:

  • 추출 커피
  • 뜨거운 우유
  • 우유 거품
  • 설탕

이 비율은 슬라이더를 사용하여 수정할 수 있습니다.

추출 상태 표시하기

사용자가 컵이 머신에 들어갔음을 확인하면 추출이 시작됩니다.

추출이 시작되면 앱에 추출 과정이 애니메이션으로 표시됩니다.

커피가 준비되었습니다.

추출 과정이 끝나면 앱에 원하는 커피믹스가 담긴 커피잔이 몇 초 동안 표시된 후 시작 페이지로 돌아갑니다.

작동 방식

여기에서는 이러한 기능이 어떻게 구현되는지 설명합니다.

화면 크기 및 방향에 맞게 조정하기

애플리케이션 창 루트 객체에는 데스크톱 플랫폼에서 창 크기로 사용되는 높이와 너비에 대한 초기값이 있습니다. 기본값인 QWindow::AutomaticVisibility 은 모바일 플랫폼과 같이 창이 필요한 플랫폼에서 창이 최대화 또는 전체 화면으로 표시되도록 합니다.

ApplicationWindow {
    visible: true
    width: 1000
    height: 600
    title: qsTr("Coffee")

여기에서 ApplicationWindow 의 하위 객체는 ApplicationWindow 의 크기를 가져와 그에 따라 자체 크기를 결정합니다.

앱은 화면 방향의 변화에 쉽게 적응할 수 있도록 애플리케이션 페이지 구성 요소 전체에서 GridLayout 을 사용합니다.

이 애플리케이션은 StackView QML 유형을 사용하여 앱의 다양한 페이지를 표시합니다. 다른 페이지 구성 요소를 처리하는 StackViewApplicationFlowForm.ui.qml 에서 구현됩니다.

한 페이지에서 다음 페이지로 이동하면 ApplicationFlow.qml 에서 상태 변경이 트리거되며, 필요한 속성 변경은 PropertyChanges QML 유형에서 처리됩니다:

states: [
    State {
        name: "Home"
        PropertyChanges {
            target: toolbar
            backButton.opacity: 0
            backButton.enabled: false
            themeButton.opacity: 0
            themeButton.enabled: false
            logo.sourceSize.width: 70
            logo.sourceSize.height: 50
        }

이러한 상태 변경 중에 발생하는 애니메이션은 ApplicationFLowForm.ui.qmlStackView 컴포넌트 내부에서 TransitionPropertyAnimation 로 구현됩니다.

시작 페이지 구현하기

애플리케이션은 StackView 의 초기 항목으로 Home 페이지를 사용자에게 표시하는 것으로 시작됩니다:

StackView {
    id: stack
    anchors.top: parent.top
    anchors.bottom: parent.bottom
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.topMargin: parent.height / 20
    initialItem: Home {
        id: home
        visible: true
        state: applicationFlow.mode
    }
    pushEnter: Transition {
        PropertyAnimation {
            property: "x"
            from: stack.width
            to: 0
            duration: 400
        }
    }

Home 컴포넌트는 Item 컴포넌트를 상태 머신과 적절한 속성 별칭을 가진 루트 객체로 배치하고 그 뒤에 GridLayout 를 배치하여 구조화됩니다. 이와 동일한 종류의 구조화가 모든 앱 페이지 컴포넌트에 사용됩니다.

Home 페이지에는 Qt 로고가 위에 있는 커피 컵 이미지, 제목으로 Coffee Machine, 사용자가 기대할 수 있는 내용을 간략하게 설명하는 캡션, getStartedButton 버튼이 표시됩니다.

사용자는 getStartedButton 버튼을 누르면 onClicked 기능이 구현된 ApplicationFlow.qml 으로 이동할 수 있습니다:

home.getStartedbutton.onClicked: {
    applicationFlow.state = "Coffee-selection"
    stack.push(choosingCoffee)
}

이렇게 하면 ApplicationFlow.qml 에서 "커피 선택"으로 상태가 변경되고 choosingCoffee 컴포넌트가 Home 컴포넌트 위에 StackView 로 푸시됩니다.

커피 선택 구현하기

커피 선택 페이지 ChoosingCoffee.qml 가 표시되면 사용자는 선택할 수 있는 4가지 커피 옵션을 볼 수 있습니다. 이러한 옵션은 CoffeeCards 으로 표시되며 ChoosingCoffee.qmlGridLayout 안에 있습니다:

GridLayout {
    id: cards
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.top: parent.top
    rowSpacing: 20
    columnSpacing: 20
    CoffeeCard {
        id: cappuccino
        coffeeName: "Cappuccino"
        ingredients: "Milk, Espresso, Foam"
        time: 2
        cupsLeft: applicationFlow.cappuccinos
    }
    CoffeeCard {
        id: latte
        coffeeName: "Latte"
        ingredients: "Coffee, Foam"
        time: 3
        cupsLeft: applicationFlow.lattes
    }
    CoffeeCard {
        id: espresso
        coffeeName: "Espresso"
        ingredients: "Milk, Espresso"
        time: 2
        cupsLeft: applicationFlow.espressos
    }
    CoffeeCard {
        id: macchiato
        coffeeName: "Macchiato"
        ingredients: "Milk foam, Espresso"
        time: 4
        cupsLeft: applicationFlow.macchiatos
    }
}

CoffeeCard 의 구현은 CoffeeCard.qml 에 있습니다.

이러한 카드는 사용 가능한 화면 너비 및 높이 속성에 따라 그리드 또는 행 유형으로 표시될 수 있으며, ApplicationWindow 루트 객체는 상태 머신을 통해 ChoosingCoffee.qmlGridLayout 로 전달됩니다.

CoffeeCards 이름, 추출 시간, 재료 및 현재 사용 가능한 컵 수에 따라 다릅니다.

이 페이지에서 사용자는 화면 오른쪽 상단의 작은 태양 모양의 아이콘 버튼을 눌러 처음으로 애플리케이션 테마를 변경할 수도 있습니다. 테마 버튼을 누르면 ApplicationFlow.qml 에서 themeButton 함수가 호출됩니다:

function themeButton() {
    if (Colors.currentTheme == Colors.dark) {
        Colors.currentTheme = Colors.light
    } else {
        Colors.currentTheme = Colors.dark
    }
}

이 함수는 Colors.qmlcurrentTheme 속성을 변경하고 속성 바인딩에 따라 앱 전체에서 색상이 자동으로 변경됩니다. 앱에서 사용되는 모든 색상은 Colors.qml 에 있습니다.

테마가 밝은 테마로 전환되면 테마 변경 아이콘 버튼 아이콘이 반달 모양으로 바뀝니다.

커피 카드 중 하나를 누르면 CoffeeCard.qml 에서 AbstractButton 내부의 상태가 변경되고 NumberAnimation 에서 Transition 으로 이동합니다:

AbstractButton {
    width: parent.width - 2
    height: parent.height - 2
    anchors.horizontalCenter: parent.horizontalCenter
    anchors.verticalCenter: parent.verticalCenter
    id: button
    hoverEnabled: true
    checkable: true
    enabled: (cupsLeft != 0) ? true : false
    transitions: Transition {
        NumberAnimation {
            properties: "scale"
            duration: 50
            easing.type: Easing.InOutQuad
        }
    }

그러면 선택한 커피 카드의 크기가 작아지고 카드의 가장자리가 녹색으로 바뀌어 사용자에게 커피가 실제로 선택되었음을 알립니다.

모든 커피 카드 button 속성 별칭 onClicked 함수는 ApplicationFlow.qml 에 있는 함수에 바인딩됩니다. 사용자가 커피 카드 중 하나를 누르면 선택한 커피 옵션과 관련된 이름의 함수가 호출됩니다. 이 함수는 ApplicationFlow.qml 의 상태를 Settings 로 설정하고 새 컴포넌트를 StackView 로 푸시하고 그에 따라 재료 속성을 설정합니다.

커피 설정 구현하기

이 페이지에서 사용자는 사용자 지정 Slider QML 유형을 조정하여 자신의 취향에 맞게 커피 옵션을 사용자 지정할 수 있습니다. 슬라이더의 값을 변경하면 커피 컵 내부에 보이는 액체 레벨에 영향을 미치며, 이는 Cup.qml 내부의 액체 이미지 높이를 해당 슬라이더의 값에 바인딩하여 가능합니다.

슬라이더의 값은 onValueChanged 함수를 통해 ApplicationFLow.qml 의 속성 변수에 저장됩니다.

coffeeSlider.onValueChanged: {
    applicationFlow.coffeeAmount = coffeeSlider.value
}

시작 버튼을 클릭하면 ApplicationFlow.qml 의 상태가 "삽입"으로 변경되고 앱에 Insert.qml 이 표시됩니다.

컵 삽입 구현하기

이 페이지에서 사용자는 추출 과정을 시작하기 전에 머신에 컵을 삽입하라는 안내를 받습니다.

Continue 버튼을 누르면 앱이 Progress 페이지로 이동합니다.

커피 추출 실행 중

진행률 페이지에는 커피 컵과 진행률 표시줄이 표시되며, 두 가지 모두 커피 추출 과정을 실시간으로 알려줍니다.

커피 컵이 채워지면 Settings 페이지에서 사용자가 선택한 것과 정확히 동일한 구성이 표시되며, 이는 Cup 속성 변수를 ApplicationFlow.qml 에 저장된 해당 값에 바인딩하여 확인합니다.

커피 컵을 채우는 애니메이션은 상태 엔진 TransitionSmoothedAnimation 에서 발생합니다.

Cup {
    id: cup
    Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
    state: "0"
}

진행률 표시줄 애니메이션은 Behavior 로 구현됩니다.

Behavior on greenBar.width {
    SmoothedAnimation {
        easing.type: Easing.Linear
        velocity: (contentItem.width / brewTime) * 1000
    }
}

Timer 는 추출이 완료되면 애플리케이션 상태 업데이트를 처리합니다.

Timer {
    id: timer
    interval: brewTime
    running: true
    onTriggered: {
        applicationFlow.onFinished()
    }
}
커피 준비 구현하기

준비 완료 페이지에는 사용자가 선택한 설정으로 채워진 커피 컵이 "커피가 준비되었습니다"라는 텍스트와 확인 아이콘과 함께 표시됩니다.

이 페이지가 표시되면 Timer 이 실행되기 시작하고 설정된 간격에 도달하면 사용자는 Home.qml 으로 다시 이동합니다.

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

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