QML(Qt)でラジオボタン的なもの(とグループボックス)を作る

いくつかの選択項目から1つ選ぶラジオボタンを実装してみました。

前回の「QML(Qt)でチェックボックスを作る」を使って作ります。
ついでにグループボックスも作りました。


/// 今回のポイント ///
・初期表示時の選択アイテムを選べるようにした
・Repeaterを使ってListModelで内容を定義するだけでCheckBoxの配置を簡単にできるようにした
・childrenはRepeaterには定義されないのでColumnのを使用する
・Columnのchildere配列にRepeaterも含まれるので注意
・ListModelのパラメータに「_key」を追加して項目を一意に表現できるようにした

Repeaterのchilderenにはオブジェクトは配置されずにColumnに配置されてました。
その際、Repeater自身もColumnのchildrenに含まれるので注意です。
場所的にはリストの最後に居たので子供の数を「-1」しておけば扱い的にはなんとかなります。

ListModelのListElementのパラメータに「_key」を追加して一意に設定しておくと項目の数を増やしたり順番を入れ替えたりした時のデグレを防げますし可読性があがります。
上位のプログラムで選択された項目を振り分ける時のswitch文とかが楽ですよね。
#define的な物がなくて定数化がやりにくいので結構大事かなと思います。
(実はあるのかな?誰かいい方法教えてください)

各チェックボックスはColumnを使用して配置しているのでGridやRowを使えば並べ方もバリエーション作れます。


使用するときはonKeyChangedイベントで項目の選択変更を拾うのが良いと思います。
インデックスもキーもどちらも変更されて新しい状態になっています。


すぐわかりますがグループボックスはちょっと弱点があります。
文字の背景を透過させれません。


/// スクリーンショット ///
サンプルコードを実行した時のものです。
 


/// サンプルコード ///

まず呼び出し側のサンプル
// main.qml

import QtQuick 1.0
import "Components"

Rectangle {
    width: 360
    height: 360

    Column{
        anchors.fill: parent
        anchors.margins: 5
        spacing: 5

        // グループボックス
        GroupBox{
            text: "Radio box test"
            radius: 5
            height: _radio.height + textHeight * 1.5

            // ラジオボックス
            RadioBox{
                id: _radio
                currentIndex: 1     // 選択の初期位置変更
                anchors.top: parent.top
                anchors.margins: parent.textHeight * 1.2
                anchors.left: parent.left
                anchors.leftMargin: 5

                // 項目の設定
                model: RadioBoxModel{
                }

                // 項目が変更された時のイベント
                // こっちが先に呼ばれる(まだキーは変更されてない)
                onCurrentIndexChanged: {
                    console.debug("onCurrentIndexChanged::"
                                  + "index=" + currentIndex
                                  + ", text=" + items[currentIndex].text
                                  + ", text=" + getCurrentItemText()
                                  + ", count=" + getItemCount()
                                  + ", key=" + currentKey);
                }

                // 項目が変更されてキーが変更された時のイベント
                // こっちが後に呼ばれる
                onCurrentKeyChanged: {
                    console.debug("onKeyChanged::"
                                  + "index=" + currentIndex
                                  + ", text=" + items[currentIndex].text
                                  + ", text=" + getCurrentItemText()
                                  + ", count=" + getItemCount()
                                  + ", key=" + currentKey);
                }
            }

        }
    }
}

ラジオボックスの内容を定義するモデル
// RadioBoxModel.qml
import QtQuick 1.0

ListModel {
    ListElement {
        _key: 0
        _text: "CheckBox0"
    }
    ListElement {
        _key: 1
        _text: "CheckBox1"
    }
    ListElement {
        _key: 2
        _text: "CheckBox2"
    }
    ListElement {
        _key: 3
        _text: "CheckBox3"
    }
}

ラジオボックス本体のコード
// RadioBox.qml

import QtQuick 1.0

Item {
    id: _root
    width: _items.width
    height: _items.height

    property int currentKey: 0              // 現在のアイテムのmodelで指定されたキー(読込み専用)
    property int currentIndex: 0            // 現在の選択アイテムのインデックス
    property alias items: _items.children   // checkboxのリスト
    property alias itemSpacing: _items.spacing  // アイテム同士の間隔

    property alias model: _itemsRepeater.model

    // 読込み完了時の処理
    Component.onCompleted: {
        // 初期位置を設定する
        if(currentIndex < 0 || currentIndex >= getItemCount()){
            currentIndex = 0;
        }
        _items.children[currentIndex].checked = true;
        currentKey = _itemsRepeater.model.get(currentIndex)._key;
    }

    // 含まれるcheckboxの数
    function getItemCount(){
        // Repeaterの分を引く
        return _items.children.length - 1;
    }

    // 現在選択されているアイテムのテキスト
    function getCurrentItemText(){
        return _items.children[currentIndex].text;
    }

    // 自分以外のチェックを外す
    function itemClicked(index){
        for(var i=0; i< getItemCount(); i++){
            if(i == index){
                // 自分自身はなにもしない
            }else{
                // 自分以外のチェックを外す
                _items.children[i].checked = false;
            }
        }
    }

    // 実際の内容
    Column{
        id: _items
        spacing: 5

        // リピータでお手軽配置
        // modelは外から指定する
        // _key は一意にすること
        // _text は表示したい文字列
        Repeater {
            id: _itemsRepeater

            delegate: CheckBox{
                id: _item
                text: _text
                property int key: _key

                onClicked: {
                    //インデックスなどを更新する
                    currentIndex = index;
                    currentKey = key;
                    // 自分以外のチェックを外す
                    itemClicked(index);
                }
            }
        }
    }

}
天の声があったので少し修正。
itemClickedの引数をindexだけに変更。
delgateの中では「index」が使えるそうです。

おまけ的なグループボックス
// GroupBox.qml

import QtQuick 1.0

Item {
    width: parent.width
    height: parent.height

    property alias text: _text.text                     // 表示するメッセージ
    property alias color: _text.color                   // 色
    property alias fontPointSize: _text.font.pointSize  // フォントサイズ
    property alias textHeight: _text.height             // テキストの高さ
    property alias textBkColor: _textArea.color         // テキストの背景色

    property alias radius: _border.radius               // 角
    property color borderColor: "#999999"               // 枠の色
    property int borderWidth: 1                         // 枠の太さ

    // 枠線
    Rectangle {
        id: _border
        x: 0
        y: _text.height / 2
        width: parent.width
        height: parent.height - y

        border.color: borderColor
        border.width: borderWidth
    }

    // タイトル
    Rectangle {
        id: _textArea
        width: _text.width
        height: _text.height
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.leftMargin: 5
        color: "#ffffff"

        Text{
            id: _text
            text: "Title"
        }
    }
}