画像付きのQMLコンボボックスやリストを作るテクニック

Create a QML ComboBox / ListView with an image
QMLではComboBoxやListViewなどのリスト形式のアイテムは「ListModel」と呼ばれるデータをもとにして作成されます。

ListModeへはListElementアイテムにKey-Value形式のデータを追加していきます。通常は「text」のキーを持つデータが反映されますが、独自のテンプレートを用意することで、キーをもとに任意のデータを表示させることが可能です。

独自のキーを定義した場合は、表示のためのQMLを自前で作ります。コンボボックスでは、ComboBoxStyleというアイテム(import QtQuick.Controls.Styles 1.0)のlabelプロパティにQMLレイアウトを割り当てると反映されます。

このサンプルでは、コンボボックスのラベルに内部のアイテムを横に並べることのできる「Row」へ「Image」と「Text」を追加し、モデルに格納されている対象のデータを描画するようにしています。
imgcmb.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Image ComboBox")

    ComboBox{
        id: cb
        width: 100
        style: ComboBoxStyle{
            id: cbstyle
            label: Row{
                // はみ出た部分は描画させない
                clip:true
                Image{
                    id:color
                    // ソースとして選択中インデックスのimgデータを使用
                    source:cbdata.get(cb.currentIndex).img
                    height:parent.height
                    width:this.height
                    // アイテム枠内に画像を縮小させる
                    fillMode: Image.PreserveAspectFit
                }
                Text{
                    id:text
                    text:cbdata.get(cb.currentIndex).text
                    anchors.verticalCenter: parent.verticalCenter
                }
            }
        }
        model: ListModel {
            id: cbdata
            ListElement { text: "Glape"; img:"imgs/icon1.png" }
            ListElement { text: "Banana"; img:"imgs/icon2.png" }
            ListElement { text: "Apple"; img:"imgs/icon3.png" }
        }
    }
}
なお、画像ファイルなどをリソースとしてプロジェクトに登録するには、プロジェクトのメニューより「既存のファイル/ディレクトリを追加」より、ファイルやフォルダを選択することで行えます。リソース内部で階層化した場合は、リソースの参照時も階層を明記します。
リストビューで独自のレイアウトを用いるにはdelegateプロパティにComponentアイテムを割り当てます。コンボボックスとは異なり、クリックして選択したり、アイテムのハイライト表示などの選択状態の処理はQMLで都度作成する必要があります。
listview.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("ListView")

    ListView{
        id: lv
        model: lm
        width: 100
        height: 100
        clip: true

        // リスト描画の定義
        delegate: user

        // ハイライトの描画定義(水色の矩形を左右いっぱいに広げて配置)
        highlight: Rectangle {
            color: "#cde7ff"
            anchors.left:parent.left
            anchors.right:parent.right
        }
    }

    Component{
        // リストアイテムの定義
        id:user
        Item{
            width: lv.width
            height: 25

            // アイテムをクリックできるようにする
            MouseArea{
                anchors.fill: parent
                onClicked: {
                    // アイテムが保有するインデックスをリストに適用させることで、選択状態にさせる
                    lv.currentIndex = index;
                }
            }

            Row{
                anchors.fill: parent
                Text{
                    id:f_name
                    text: name
                }

                Image{
                    id:f_icon
                    source: src
                    height:parent.height
                    fillMode: Image.PreserveAspectFit
                }

                CheckBox{
                    id:f_check
                    checked: chk
                }
            }
        }
    }

    // リストアイテムの初期定義
    ListModel{
        id:lm
        ListElement{
            name: "User1"
            src: "icon1.png"
            chk: false
        }
        ListElement{
            name: "User2"
            src: "icon2.png"
            chk: true
        }
        ListElement{
            name: "User3"
            src: "icon3.png"
            chk: false
        }
    }

    // アイテムを動的に追加する例
    Button{
        anchors.top: lv.bottom
        text: "Add"
        onClicked: {
            lv.model.append({ name:"new", src:"icon1.png", chk:true });
        }
    }
}
2018/10/31