GUI

Python Tkinter widgets 発展的なウィジェット(9) Listbox

リストボックスウィジェットは、リストを表示し、ユーザがリストをブラウズして1つまたは複数を選択できるようにする。

lb = Listbox(parent, height=100)

リストボックスの項目を入力する

リストボックスのすべての項目を入力し、管理する方法には、簡単な方法と難しい方法がある。

簡単な方法を紹介する。各リストボックスにはlistvariable設定オプションがあり、変数(リストを保持する必要がある)をリストボックスにリンクさせることができるようになっている。このリストの各要素は、リストボックスの1つの項目を表す文字列。リストボックスの項目を追加、削除、並べ替えするには、他のリストと同じようにこの変数を変更すればよい。

Tkinterでは通常のPythonのリストをlistboxにリンクすることができない。entryのようなウィジェットで見たように、StringVarを仲介に使う必要がある。これは Python のリストと、その下にある Tk ウィジェットが使用できる文字列表現とのマッピングを提供する。リストを変更するときは StringVar を更新する必要があることを意味する。

choices = ["a", "b", "c"]
choicesvar = StringVar(value=choices)
l = Listbox(parent, listvariable=choicesvar)
...
choices.append("d")
choicesvar.set(choices)

リストボックスウィジェット自体のメソッドを使用するのが、より古くて難しい方法。これらのメソッドは、ウィジェットが保持する (内部の) アイテムリストに対して操作する。

  • insert idx item メソッドを使用して、1つまたは複数のアイテムをリストに追加する。idxは、追加する項目の前の位置を示す0基準のインデックス。endを指定すると、新しい項目はリストの末尾に置かれる。
  • リストから一つ以上の項目を削除するには delete first last メソッドを使う。firstとlastはinsertメソッドに従ったインデックス。
  • 指定した位置にあるひとつの項目の内容、または first と last の間の項目のリストを返すには、 get first lastを使う。
  • size メソッドは、リストに含まれる項目数を返す。

項目の選択

リストボックスから一度に1つの項目しか選択できないようにするか、複数の項目を同時に選択できるようにするかを選択できる。デフォルトでは1つの項目しか選択できないが(ブラウズ)、selectmodeをextendedにすると複数の項目を選択できるようになる。

リストボックス内のどの項目が現在選択されているかを調べるには、curslection メソッドを使用する。これは、現在選択されているすべての項目のインデックスのリストを返す。selectmode が browse のリストでは、このリストが1項目のみ。また、selection_includes index メソッドを使用すると、指定したインデックスの項目が現在選択されているかどうかをチェックできる。

if lb.selection_includes(2): ...

プログラムによって選択範囲を変更するには、 selection_clear first last メソッドを使用して、単一の項目または指定したインデックスの範囲内のすべての項目の選択を解除する。ある範囲の項目またはすべての項目を選択するには、 selection_set first last どちらも、指定された範囲外の項目の選択には触れない。

選択範囲を変更する場合は、新しく選択された項目が表示されていることを確認する必要がある(つまり、スクロールして表示されなくなるようなことがないように)。これには、see index メソッドを使用する。

lb.selection_set(idx)
lb.see(idx)

ユーザーが選択を変更すると、<<ListboxSelect>> 仮想イベントが生成される。これにバインドして、必要なアクションを実行することができる。アプリケーションによっては、ダブルクリックイベントにバインドして、現在選択されている項目に対するアクションを実行できる。

lb.bind("<<ListboxSelect>>", lambda e: updateDetails(lb.curselection()))
lb.bind("<Double-1>", lambda e: invokeAction(lb.curselection()))

リストのスタイリング

ほとんどの「古典的な」Tk ウィジェットのように、リストボックスの外観を柔軟に変更できる。リファレンスマニュアルで説明されているように、リストボックスのアイテムの表示フォント、通常時、選択時、ウィジェット無効時のアイテムの前景(テキスト)色と背景色を変更できる。また、itemconfigureメソッドを使えば、個々のアイテムの前景色や背景色を変更することも可能。

余分なアイテムデータの保持

listvariable(旧来の管理方法であれば内部リスト)には、リストボックスに表示される文字列が格納されている。しかし、表示する文字列が他のデータ項目と関連付けられていることはよくあること。これは、プログラムにとって意味のある内部オブジェクトかもしれないが、ユーザーに表示されることを意図したものではない。つまり、リストボックスに表示される文字列よりも、関連するデータ項目に興味がある。例えば、リストボックスはユーザーに対して名前のリストを表示するが、プログラムが本当に興味があるのは、特定の名前ではなく、それぞれのユーザーオブジェクト(またはID番号)。

このような場合、どのようにすれば表示されている名前と関連付けることができるかを考える。残念ながら、リストボックスウィジェット自体には何の機能もないため、別途管理する必要がある。

まず、表示される文字列が一意であることが保証されている場合、ハッシュテーブルを使用して、各名称を関連する基本オブジェクトにマッピングすることができる。この方法は、重複する可能性のある人名などには適応できないが、一意である国名などには有効。

もうひとつの方法は、リストボックスに表示される文字列のリストと並行して、2つ目のリストを保持すること。この2番目のリストには、表示される各項目に関連する基本オブジェクトが保持される。(つまり、表示されている文字列リストの最初の項目は、基礎となるオブジェクトリストの最初の項目に対応し、2番目の項目は2番目の項目に対応する、といった具合。)一方のリストで行った変更(挿入、削除、並べ替え)は、もう一方のリストで行う必要がある。そして、表示されているリストの項目から、リスト内の位置に基づいて、基礎となるオブジェクトに簡単にマッピングすることができる。