Q~Viewシリーズのアイテムの挙動をまとめて制御する。

    いきなりですが、Maya2017からPySide2になって面倒な事になりましたね。
    保守どうすんのやぁぁぁぁ・・・


    Maya 2017 は PySide2 を使用します。スクリプト内で PySide を使用するには、次のように PySide からモジュールを読み込みます。こうすると、Maya 2017 と Maya 2017 より前のバージョンの両方に対してスクリプトの互換性が維持されます。
    try:
        from PySide2.QtCore import * 
        from PySide2.QtGui import * 
        from PySide2.QtWidgets import *
        from PySide2 import __version__
        from shiboken2 import wrapInstance 
    except ImportError:
        from PySide.QtCore import * 
        from PySide.QtGui import * 
        from PySide import __version__
        from shiboken import wrapInstance 
     なんてとんでもない事書いてますけど、「import *」はPythonでは推奨されていませんからね!!!!!!! 

    一般的には、モジュールやパッケージから * を import するというやり方には賛同できません。 というのは、この操作を行うとしばしば可読性に乏しいコードになるからです。 

     とは言え、ちゃんと対応しようと思ったらとっても大変なんで、その時が来るまでここは目をつぶることにします・・・(Qt用のqtlibモジュール作って、その中でPySide1系のモジュールを擬似的にPySide2系のモジュール名に合わせるしかないかも・・・)

     と、しょっぱなから話しはそれましたが・・・あ、
    新年明けましておめでとうございます。
     この年になるとなんにもおめでたくないんですが、一応・・・

     さて、QtのQ~Viewシリーズ(QTreeView、QListView、QTableViewなど)に表示するアイテムはラベルの編集や選択の可否、有効/無効などさまざまな状態に設定する事が出来ます。
     各個別のアイテムごとに制御できるので、例えばこんな風にすれば
    # -*- coding:utf-8 -*-
    import sys
    import random
    from PySide import QtGui, QtCore
    
    class SampleView(QtGui.QTreeView):
        '''
            QTreeViewから継承したサンプルのビューを表示するクラス。
        '''
        def __init__(self, parent=None):
            super(SampleView, self).__init__(parent)
            self.setWindowTitle('Sample View')
            self.resize(400, 450)
            self.setAlternatingRowColors(True)  #偶数行と奇数行で色を変える。
    
            # 3列のアイテムを持つmodelを作成してセットする。=======================
            model = QtGui.QStandardItemModel(0, 3)
            model.setHeaderData(0, QtCore.Qt.Horizontal, 'Hoge')
            model.setHeaderData(1, QtCore.Qt.Horizontal, 'Boke')
            model.setHeaderData(2, QtCore.Qt.Horizontal, 'Foo')
            self.setModel(model)
            # =====================================================================
    
            # 適当にアイテムを追加。===============================================
            # この時ランダムで選択可能、編集可能、有効/無効にする。
    
            # ランダムにTrueかFalseを返す無名関数を作成。
            test = lambda  : True if random.uniform(0, 1) > 0.5 else False
    
            for i in range(100):
                for j in range(3):
                    item = QtGui.QStandardItem('Item %s-%s' % (i, j))
                    item.setSelectable(test())  # 選択可能かどうかの設定。
                    item.setEditable(test())    # 編集可能かどうかの設定。
                    item.setEnabled(test())     # 有効か無効かの設定。
                    model.setItem(i, j, item)
            # =====================================================================
            
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        view = SampleView()
        view.show()
        sys.exit(app.exec_()) 
    itemFlagsSetting001.png 
     こんな感じでアイテムごとに状態を変更できます。上のキャプ画像だとわかりませんが、選択できないものや編集できなものが入り乱れてます。

     まぁ、これはこれで個別に制御できていいんですけど、だたいツールを作る時って
    ・特定の列だけ編集可能にしたい!
    ・特定の行だけ選択不能にしたい!
    ・特定の列だけチェックボックスをつけたい!
    とかですよね。なかなか各個バラバラに制御したいと言うことは稀だと思います。
     だけどアイテム作る度にこれらの設定を変更してたら面倒なんで、特定の行ごと!列ごと!に制御する方法があります。

     そこで登場するのがQ~Modelシリーズの基底クラスである「QAbstractItemModel」のflagsメソッドです!!
     このflagsメソッドを使用すれば、特定の行・列ごとの制御が可能になります。
     それでは早速やってみましょう~
    # -*- coding:utf-8 -*-
    import sys
    import random
    from PySide import QtGui, QtCore
    
    class SampleModel(QtGui.QStandardItemModel):
        '''
            特定の行や列でアイテムの状態を制御するモデル。
        '''
        def flags(self, index):
            '''
                インデックスの条件によってアイテムの状態を制御する。
            '''
            # 対象となるアイテムの行番号と列番号、そしてアイテム取得する。
            row = index.row()
            column = index.column()
            item = index.model().itemFromIndex(index)
            item.setCheckable(False)
            
            # 基本となるアイテムの状態を設定。=====================================
            # 行が10の倍数だった場合選択不可にする。
            if not row % 10:
                std_item_flags = QtCore.Qt.ItemIsEnabled
            else:
                std_item_flags = (
                    QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
                )
            # =====================================================================
    
            if column == 1:
                # 1列目のアイテムだった場合、有効かつ編集可能にする。
                return std_item_flags | QtCore.Qt.ItemIsEditable
            elif column == 2:
                # 2列目のアイテムだった場合、無効状態にする。
                return QtCore.Qt.NoItemFlags
            elif column == 3:
                # 3列目のアイテムだった場合、チェックボックをつける。
                item.setCheckable(True)
                return std_item_flags | QtCore.Qt.ItemIsUserCheckable
    
            # それ以外は通常状態にする。
            return std_item_flags
    
    
    class SampleView(QtGui.QTreeView):
        '''
            QTreeViewから継承したサンプルのビューを表示するクラス。
        '''
        def __init__(self, parent=None):
            super(SampleView, self).__init__(parent)
            self.setWindowTitle('Sample View')
            self.resize(450, 450)
            # 偶数行と奇数行で色を変える。
            self.setAlternatingRowColors(True) 
            # 複数のアイテムを選択可能にする。
            self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
    
            # 3列のアイテムを持つmodelを作成してセットする。=======================
            model = SampleModel(0, 4)
            model.setHeaderData(0, QtCore.Qt.Horizontal, 'Normal')
            model.setHeaderData(1, QtCore.Qt.Horizontal, 'Editable')
            model.setHeaderData(2, QtCore.Qt.Horizontal, 'Disable')
            model.setHeaderData(3, QtCore.Qt.Horizontal, 'Checkable')
            self.setModel(model)
            # =====================================================================
    
            # 適当にアイテムを追加。===============================================
            for i in range(100):
                for j in range(4):
                    item = QtGui.QStandardItem('Item %s-%s' % (i, j))
                    model.setItem(i, j, item)
            # =====================================================================
    
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        view = SampleView()
        view.show()
        sys.exit(app.exec_())
    
    itemFlagsSetting002.png
     このサンプルでは0列目(Normalの列)が通常、1列目(Editable)では編集可能、2列目(Disable)は無効状態になるように制御しています。
     また10の倍数では選択不能になるようにしています。

     今回の例ではQStandardItemModelのflagsメソッドを上書きしています。
     flagsメソッドはアイテムの状態の更新が必要になったときに、全てのアイテム分だけ内部的に呼ばれます。
     flagsの引数indexにはこれから更新をかけるindexが渡されます。indexはrow()やcolumn()を使って行列の番号を取得できるので、あとはこれを条件にかけてアイテムの状態を返してやればOK。
     アイテムの状態はQt::ItemFlagの通りで、通常は
    ・選択不能、編集不能、無効
    となっています。ここからItemIsSelectable(選択可能)やItemIsEditable(編集可能)などのスイッチを返してやれば、その通りの状態となります。
     また、これらItemFlagsは|でつなぐ事が出来ますので
    ItemIsSelectable | ItemIsEditable(選択可能かつ編集可能)
    などのように複数の状態を同時に指定する事も可能です。

     また、indexはdata()でアイテムに格納している情報をとって来たり
    index().model().itemFromIndex(index)
    でindexに該当するアイテムを取ってくることもできるので特定行だけチェック可能にしたりも、一応ここで制御することが出来ます。

     ちゃんと個別設定以外にも、全体をコントロール手段が用意されてるとは・・・つくづくQtとは恐ろしいヤツよ。
    スポンサーサイト

    コメントの投稿

    非公開コメント

    プロフィール

    Eske

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

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

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