728x90

이전 포스팅에서 ListView에 아이템을 QAbstractModel을 상속받은 클래스를 통해 동적으로 관리하는 예제를 진행해봤습니다.

 

[Qt] QML(7) - ListView/Model C++ 로 처리하기

이전 포스팅에서 ListView를 QML로 보여주고 그 안에서 동적으로 관리하는 예제를 살펴봤습니다. [Qt] QML(2) - State/Transition, ListView, Property 지난 포스팅에선 간단히 오브젝트와 컨테이너를 이용해서

1d1cblog.tistory.com

이번 포스팅에서는 Combobox에 위와 같이 Model을 적용시켜 보고 이전에 만들었던 TimeTimer에 시간이 다 되었을 때 알림 소리를 낼 수 있게 SoundEffect, Sound를 적용시켜 보겠습니다.

위 예제는 이전 TimeTimer 예제를 보고 오시면 이해하는데 더 많은 도움을 받으실 수 있습니다.

 

[Qt] QML(5) - TimeTimer 프로그램 만들기

QML으로 간단하게 TimeTimer라는 프로그램을 제작하면서 다른 기능도 사용해 보겠습니다. 이 예제를 진행하면서 만들어볼 간단한 프로그램은 TimeTimer입니다. TimeTimer는 위와 같이 생긴 시계인데 예

1d1cblog.tistory.com

 

먼저 Combobox에 적용시킬 Model을 먼저 만들겠습니다. 이번에는 Model을 관리할 Manager 클래스 없이 바로 직접 지정해보려 합니다.

import SoundListItem 1.0

...

Label {
    text : "알람 소리"
    Layout.preferredWidth: parent.nWidth
    Layout.preferredHeight: parent.nHeight
    Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
    verticalAlignment: Text.AlignVCenter
    horizontalAlignment: Text.AlignHCenter
}

ComboBox {
    id : alarmCombo
    Layout.preferredWidth: parent.nWidth + 70
    Layout.preferredHeight: parent.nHeight
    Layout.alignment: Qt.AlignCenter
    model : SoudListModelItem {}
    delegate: comboDelegate
}

QAbstractListModel을 상속받는 SoundListModelItem을 만들어줍니다. 이번에는 단일 아이템만 노출시키므로 roleName는 따로 override 하지 않았습니다. 하지만 rowCount와 datat는 무조건 override 해주어야 합니다.

#ifndef SoundListModelItem_H
#define SoundListModelItem_H

#include <QAbstractListModel>

class SoundListModelItem : public QAbstractListModel
{
    Q_OBJECT
public:
    explicit SoundListModelItem(QObject *parent = nullptr);

    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

    void loadAlarmList();

private:
    QStringList alarmSoundList;
};

#endif // SoundListModelItem_H

이 예제에서는 Alarm sound를 윈도우 자체가 가지고 있는 항목들을 사용하려 합니다.

#include "SoundListModelItem.h"
#include <QDebug>
#include <QDirIterator>

SoundListModelItem::SoundListModelItem(QObject *parent)
    : QAbstractListModel{parent}
{
    loadAlarmList();
}

int SoundListModelItem::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return alarmSoundList.count();
}

QVariant SoundListModelItem::data(const QModelIndex &index, int role) const
{
    int nRow = index.row();

    if ( nRow < 0 || nRow >= alarmSoundList.count() ) {
        return QVariant();
    }

    switch (role) {
    case Qt::DisplayRole:
        return alarmSoundList.value(nRow);
    }

    return QVariant();
}

void SoundListModelItem::loadAlarmList()
{
    QString sPath = "C:/Windows/Media";

    QDirIterator it(sPath, QDir::Files);

    while (it.hasNext()) {
        it.next();
        if ( it.fileName().indexOf("Alarm") != -1 ) {
            alarmSoundList.append(it.fileName());
        }
    }
}

Windows 폴더 아래 Media 폴더에는 각종 wav 파일이 있는데 여기서 Alarm 파일명을 가진 파일들만 가져오려고 합니다.

물론 qml에서 C++의 클래스를 사용하기 위해서 qmlRegisterType을 해주어야 합니다.

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "TimeTimerMgr.h"
#include "SoundListModelItem.h"

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);
    qmlRegisterType<SoundListModelItem>("SoundListItem", 1,0, "SoundListModelItem");

    QQmlApplicationEngine engine;

    const QUrl url(QStringLiteral("qrc:/TimeTimer.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app,
        [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }
    , Qt::QueuedConnection);

    engine.load(url);

    TimeTimerMgr timerMgr;
    timerMgr.SetEngine(&engine);
    return app.exec();
}

다음으로 qml Combox Model에 Delegate를 설정하겠습니다. 리스트에 파일명이랑 재생 버튼을 두고 버튼을 클릭하면 미리듣기 해보는 방식으로 해보려 합니다.

Component {
    id : comboDelegate
    Row {
        Rectangle {
            width: alarmCombo.width - 30
            height : 30
            Text {
                anchors.fill : parent
                text : model.display
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }
        }
        Button {
            width: 30
            height : 30
            Text {
                anchors.fill : parent
                text : "재생"
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }

            onClicked : {
                // sound play
            }
        }
    }
}

여기서 SoundEffect를 사용해보겠습니다. SoundEfffect Component 생성 후 id 만 지정합니다. 이후 Button의 onClicked에서 source 지정 후 play 시켜주면 됩니다.

Component {
    id : comboDelegate
    Row {
        Rectangle {
            width: alarmCombo.width - 30
            height : 30
            Text {
                anchors.fill : parent
                text : model.display
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter

                MouseArea {
                    anchors.fill: parent
                    onClicked: {
                        alarmSound.stop()
                        alarmCombo.displayText = model.display
                        alarmCombo.popup.close()
                    }
                }
            }
        }
        Button {
            width: 30
            height : 30
            Text {
                anchors.fill : parent
                text : "재생"
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }

            onClicked : {
                alarmSound.source = "file:C:/Windows/Media/" + model.display
                console.log(alarmSound.source)
                alarmSound.play()
            }
        }
    }
}

SoundEffect {
    id : alarmSound
}

해당 효과까지 적용한 결과는 아래와 같습니다. 재생 버튼을 클릭하면 해당 wav 파일을 들어볼 수 있고 원하는 알람 소리를 클릭하면 combobox의 popup을 닫고 combox에 해당 파일명을 display 해줍니다.

이제 이 선택한 알림 소리를 타이머가 다 되었을 때 재생시켜 보겠습니다. SettingDialog.qml로 부터 signal을 받아 알람 파일명을 TimeTimerMgr까지 가져옵니다. 그리고 Timeout이 되었을 때 QSound::play 함수를 실행시킵니다.

#include "TimeTimerMgr.h"
#include <QQmlProperty>
#include <QSound>

TimeTimerMgr::TimeTimerMgr(QObject *parent)
    : QObject{parent}
    , m_pDialog(NULL)
    , m_engine(NULL)
    , m_pWindow(NULL)
    , m_pTimeCanvas(NULL)
    , m_nTime(0)
    , m_nCount(0)
    , m_sAlarmSound("")
{
    connect(&timer, SIGNAL(timeout()), this, SLOT(Slot_Timeout()), Qt::UniqueConnection);
}

void TimeTimerMgr::showDialog()
{
    if ( m_pDialog != NULL ) { return; }
    m_pDialog = new ConfigureDialog(this, m_engine);

    connect(m_pDialog, SIGNAL(Emit_setColor(QColor)), this, SLOT(Slot_GetColor(QColor)), Qt::UniqueConnection);
    connect(m_pDialog, SIGNAL(Emit_setOpacity(double)), this, SLOT(Slot_GetOpacity(double)), Qt::UniqueConnection);
    connect(m_pDialog, SIGNAL(Emit_setAlwaysOnTop(bool)), this, SLOT(Slot_GetAlwaysOnTop(bool)), Qt::UniqueConnection);
    connect(m_pDialog, SIGNAL(Emit_setAlarmSound(QString)), this, SLOT(Slot_GetAlarmSound(QString)), Qt::UniqueConnection);
    connect(m_pDialog, SIGNAL(Emit_Close()), this, SLOT(Slot_DialogClose()), Qt::UniqueConnection);

    TimerSetting settingValue;
    settingValue.color        = m_pTimeCanvas->property("timeColor").value<QColor>();
    settingValue.opacity      = m_pWindow->property("dOpacity").toDouble();
    settingValue.bAlwaysOnTop = m_pWindow->property("bAlwaysOnTop").toBool();
    settingValue.sAlarmSound  = m_sAlarmSound;

    m_pDialog->Show();
    m_pDialog->SetSettingValue(settingValue);
}

... 

void TimeTimerMgr::Slot_Timeout()
{
    m_nCount++;
    m_pTimeCanvas->setProperty("setSecond", (m_nTime*60-m_nCount));
    QMetaObject::invokeMethod(m_pTimeCanvas, "rePaint");

    if ( m_nTime*60 == m_nCount ) {
        timer.stop();
        m_pWindow->setFlag(Qt::WindowStaysOnTopHint, false);
        m_pWindow->setProperty("bAlwaysOnTop", false);

        QSound::play("C:/Windows/Media/" + m_sAlarmSound);
    }
}

...

풀 코드는 아래 Github 링크에서 확인하실 수 있습니다.

 

GitHub - psy1064/TimeTimer_Qt: TimeTimer Use Qt/QML

TimeTimer Use Qt/QML. Contribute to psy1064/TimeTimer_Qt development by creating an account on GitHub.

github.com

 

728x90

+ Recent posts