QStandardItemModel & QItemSelectionModel

     仕事でPyQtでGUIを作る事になって、早2ヶ月。流石にちょっと慣れてきたところで、自分がよく使うリストに関してまとめとこうと思います。

     MelなんかではtextScrollListと呼ばれるリスト表示形式がありましたが、Pythonではボクが愛用しているQTreeViewをはじめ、QTreeViewや・・・なんか忘れましたけど(ヲイ・・・)QAbstractItemViewから派生する各種UIが、とにかく色々用意されています。


     それに付随するのがQAbstractItemModelから派生するQStandardItemModelなどのItemModel系と、QItemSelectionModelです。
     最初これらの関係がチンプンカンプンだったのですが、最近ようやく理解してきたのでここでまとめときます。



    ■QTreeView、QStandardItemModel、QItemSelectionModel・・・
     MelのtextScrollListライクなものリストを作るのに、最大これだけ使います(いや、ホントはもっと使うんですけど、構成に必要なのはこんなもん・・・なハズ)
     MelではtextScrollListで済んでいたものが、PyQtではこんなに必要になっちゃうわけですね。
     ではなんでこんな事になってしまったかと言うと。。。
     Qtではリストの機能を分割しているからです。大雑把に言うと
    • QTreeViewは見た目と、選択の挙動を
    • QStandardItemModelはリストに表示する項目を
    • QItemSelectionModelは選択に関する情報を
    それぞれ分割して持っています。(そう言えばマニュアルにそんな事書いてあったな・・・なんて事を今思い出した・・・)
     なんでそんな事を・・・? とお思いでしょう。思いますよね? いや思ったはずだ!! いや思えっ!!!
     機能が分散しているため、最初目的の機能を探すことが非常に厄介で、何度も絶望したもんです。。。

     しかし、Qtでは先に述べたようにQAbstractItemViewから派生する複数のビュー表示形式を持っています。その複数ある表示形式でデータのやり取りや共有を柔軟に行うためにあえて分割して管理する事にしたのです。


     試しにどんな事が出来るかやってみましょ~
    # -*- coding:utf-8 -*-
    import sys
    from PyQt4 import QtGui, QtCore
    
    TEXTS = [
        ['abc', 'def', 'ghi'],
        ['abeshi', '!', 'you are shocked.'],
        ['行1', '行2', '行3!'],
        ['ItemModelと', 'ItemSelctionModelの', 'テスト']
    ]
    COLOR_LIST = [
        QtGui.QBrush( QtGui.QColor( 255, 255, 255 ) ),
        QtGui.QBrush( QtGui.QColor( 240, 240, 240 ) )
    ]
    
    
    
    class MyModel( QtGui.QStandardItemModel ):
        def __init__( self, row, column, parent=None ):
            super( MyModel, self ).__init__( row, column, parent )
    
        def addItemInRow( self, *texts ):
            row = self.rowCount()                           # 列数を取得。
            for i in range( len(texts) ):
                # QStandardItemを作成し、StandardItemModelにセットする。
                item = QtGui.QStandardItem()
                item.setText( texts[i] )
                item.setBackground( COLOR_LIST[row%2] )
    
                self.setItem( row, i, item )
    
    
    
    class Window( QtGui.QWidget ):
        def __init__( self, parent=None ):
            super( Window, self ).__init__( parent )
            layout = QtGui.QVBoxLayout( self )              # メインレイアウト。
    
            # Standard item model
            self.model    = MyModel( 0, 3, self )
            self.selModel = QtGui.QItemSelectionModel( self.model )
    
            # 画面2分割用スプリッタウィジット。
            splitter = QtGui.QSplitter()
            self.treeview = QtGui.QTreeView( self )         # TreeView
            self.table    = QtGui.QTableView( self )        # Table
    
            # 各種ツリーにItemModelとItemSelectionModelを追加。
            self.treeview.setModel( self.model )
            self.treeview.setSelectionModel( self.selModel )
            self.table.setModel( self.model )
            self.table.setSelectionModel( self.selModel )
    
            # Standard item modelにアイテムを追加。
            for t in TEXTS:
                self.model.addItemInRow( *t )
    
            # TreeViewの設定
            self.treeview.setColumnWidth( 1, 140 )          # 1列目の幅を140に調整。
            self.treeview.setSelectionMode(                 # 選択モードをマルチに変更
                QtGui.QAbstractItemView.MultiSelection
            )
    
    
            # スプリッタウィジットにtreeViewとtableWidgetを追加。
            splitter.addWidget( self.treeview )
            splitter.addWidget( self.table )
    
            layout.addWidget( splitter )
    
            # ウィンドウの状態を編集-----------------------------------------------
            self.resize( 800, 240 )                         # ウィンドウのサイズ
            self.setWindowTitle( 'Tree View Sample' )       # ウィンドウタイトル
            # ---------------------------------------------------------------------
    
    
    if __name__ == '__main__':
        app = QtGui.QApplication( sys.argv )
    
        # 日本語文字コードを正常表示するための設定。
        QtCore.QTextCodec.setCodecForCStrings( QtCore.QTextCodec.codecForLocale() )
    
        window = Window()
        window.show()
    
        app.exec_()
    
    
    treeViewAndTableViewSample.jpg
     こんか感じで2つのリストができます。一見両方同じですが、片方は列選択がないリスト形式、もう片方はEXCELなどでお馴染みの行列表示方式です。このウィンドウには2つのリストが存在しているわけですね。

    ・・・

     いや待て

     俺たちはとんでもない勘違いをしていたらしい・・・
    この2つ、一見別々のリストに見えるが・・・
        ,ィィr--  ..__、j
       ル! {       `ヽ,       ∧
      N { l `    ,、   i _|\/ ∨ ∨
      ゝヽ   _,,ィjjハ、   | \
      `ニr‐tミ-rr‐tュ<≧rヘ   >
         {___,リ ヽ二´ノ  }ソ ∠ 実は2つのリストは裏では情報を共有していたんだ!!
        '、 `,-_-ュ  u /|   ∠
          ヽ`┴ ' //l\  |/\∧  /
    --─‐ァ'| `ニ--‐'´ /  |`ー ..__   `´
        く__レ1;';';';>、  / __ |  ,=、 ___
       「 ∧ 7;';';'| ヽ/ _,|‐、|」 |L..! {L..l ))
       |  |::.V;';';';'| /.:.|トl`´.! l _,,,l | _,,|  , -,
        ! |:.:.:l;;';';';'|/.:.:.:||=|=; | |   | | .l / 〃 ))
        l |:.:.:.:l;';';'/.:.:.:.:| ! ヽ \!‐=:l/ `:lj  7
        | |:.:.:.:.l;'/.:.:.:.:.:.! ヽ:::\::  ::::|  ::l /


             ナ ゝ   ナ ゝ /    十_"    ー;=‐         |! |!
              cト    cト /^、_ノ  | 、.__ つ  (.__    ̄ ̄ ̄ ̄   ・ ・
    ミミ:::;,!      u       `゙"~´   ヾ彡::l/VvVw、 ,yvヾNヽ  ゞヾ  ,. ,. ,. 、、ヾゝヽr=ヾ
    ミ::::;/   ゙̄`ー-.、     u  ;,,;   j   ヾk'! ' l / 'レ ^ヽヘ\   ,r゙ゞ゙-"、ノ / l! !ヽ 、、 |
    ミ/    J   ゙`ー、   " ;, ;;; ,;; ゙  u ヾi    ,,./ , ,、ヾヾ   | '-- 、..,,ヽ  j  ! | Nヾ|
    '"       _,,.. -─ゝ.、   ;, " ;;   _,,..._ゞイ__//〃 i.! ilヾゞヽ  | 、  .r. ヾ-、;;ノ,.:-一'"i
      j    /   ,.- 、  ヾヽ、 ;; ;; _,-<  //_,,\' "' !| :l ゙i !_,,ヽ.l `ー─--  エィ' (. 7 /
          :    ' ・丿   ̄≠Ξイ´,-、 ヽ /イ´ r. `ー-'メ ,.-´、  i     u  ヾ``ー' イ
           \_    _,,......::   ´゙i、 `¨ / i ヽ.__,,... '  u ゙l´.i・j.冫,イ゙l  / ``-、..- ノ :u l
       u      ̄ ̄  彡"   、ヾ ̄``ミ::.l  u   j  i、`ー' .i / /、._    `'y   /
                  u      `ヽ  ゙:l   ,.::- 、,, ,. ノ ゙ u ! /_   ̄ ー/ u /
               _,,..,,_    ,.ィ、  /   |  /__   ``- 、_    l l  ``ーt、_ /  /
      ゙   u  ,./´ "  ``- 、_J r'´  u 丿 .l,... `ー一''/   ノ  ト 、,,_____ ゙/ /
            ./__        ー7    /、 l   '゙ ヽ/  ,. '"  \`ー--- ",.::く、
           /;;;''"  ̄ ̄ ───/  ゙  ,::'  \ヾニ==='"/ `- 、   ゙ー┬ '´ / \..,,__
    、      .i:⌒`─-、_,....    l   /     `ー┬一'      ヽ    :l  /  , ' `ソヽ
    ヾヽ     l      `  `ヽ、 l  ./  ヽ      l         )  ,; /   ,'    '^i



     スイマセン、一度やってみたかったんです。

     まぁ、ネタはさておき40行目と41行目で作成したQStandardItemModelとQItemSelectionModelを、49行目~52行目の部分でTreeViewとTableViewにセットしています。
     ItemModel系は各アイテムの項目を、QItemSelectionModelは選択の情報を持っているため、片方のリストを更新すると、もう片方もちゃんと反映されます。つまりデータを保有するコアの部分さえしっかり作っておけば、表示形式はあとから簡単に変更可能、また今回のサンプルのように各ビューでリンクさせたりすることが簡単にできる訳です。

     また、これらの構造を理解すれば、自ずと目的の機能も探しやすくなりますね。
    (選択方式(マルチかシングルかなど)は表示形式に依存しているので、変更するにはTreeViewなどで設定する。
    現在の選択アイテムを取得するには、選択を司るQItemSelectionModelを参照する・・・など)


     昔はQtもMelのtextScrollListのように全てを一括管理していたようですが、最近ではこれらの分割管理方式が主流のようです。
     下位互換のため昔の方式も残っていますが、マニュアルでもこちらの分割管理方式を推奨しています。


     う~ん、Qtは奥深いです。。。
     
    スポンサーサイト

    コメントの投稿

    非公開コメント

    プロフィール

    Eske

    Author:Eske
    萌えイラストレーターを目指す3DCGイラストレーター。
    現在ポケモンカードゲーム、ガンダムトライエイジ、ガンダムコンクエスト、妖怪ウォッチとりつきカードゲームなどで3DCGを使用したイラストレーターとして参加中。

    主にここでは日々気づいたメモなんかを残してます。
    イラストのお仕事も受け付けております。ココのメールアドレスからご連絡できますので、お気軽にご相談下さい。

    最新記事
    最新コメント
    カテゴリ
    最新トラックバック
    月別アーカイブ
    検索フォーム
    リンク
    QRコード
    QR