目次

  1. アーキテクチャ
  2. モジュール
    1. model.py
    2. view.py
  3. 内部の動作
    1. model.py
      1. 検索
      2. アイテムの追加
      3. グループの編集
      4. シリアライズ
      5. デシリアライズ
      6. ファイルへの保存
      7. ファイルの読み込み
    2. view.py
      1. 表示内容の初期化
      2. アイテムの登録
      3. アイテムの表示
      4. GUI からの値の更新

アーキテクチャ

groupManager では、一般的な "グループの管理" という機能と、その中で扱うアイテムの処理を 分離することで、使用するモジュールを切り替えるだけでどのようなものでも階層的なグループとして 扱うことができるような構造になっている。

モジュール

Group Manager の細かい挙動はモジュールで定義される。プラグインは model と view から構成される。

ここでは、environ モジュールを例に解説をおこなう。

environ モジュールは、環境変数をグループ化して扱うためのモジュールである。

モジュールは以下のような構成になっている。

environ
 +__init__.py
 +model.py
 +view.py

model.py が管理するアイテムの処理を担当し、view.py がツリーとして表示する際の 各アイテムの表示の管理を担当する。

model.py

model.py の実装は以下の通りである

   1 from JCGS.Architecture import MVC
   2 
   3 from groupManager import modelBase
   4 from groupManager.modelBase import recursive
   5 
   6 class model(modelBase.modelBase):
   7     def query(self, *args, **kwargs):
   8         '''
   9         get valid objects from scene
  10         args and kwargs are used for filtering
  11         '''
  12 
  13         import os
  14 
  15         ret = []
  16         for k,v in os.environ.iteritems():
  17             ret.append({'name' : k})
  18 
  19         return ret
  20 
  21     def getItem(self, values):
  22         if not 'name' in values.keys():
  23             #group
  24             values['name'] = 'Group'
  25             values['value'] = ''
  26 
  27         return MVC.ModelItem(values)
  28 
  29     def checkModify(self, model, items, newIdx):
  30         #return valid items that are able to modify hierarchy
  31         #for environ plugin, group item only have child.
  32         parentIdx = newIdx[:-1]
  33         if parentIdx == []:
  34             #move to root
  35             return items
  36 
  37         parentItem = model.itemFromIdx(parentIdx)
  38         if 'value' in parentItem.value.keys():
  39             #group
  40             return items
  41 
  42         return []
  43 
  44     @recursive
  45     def setName(self, item, name):
  46         if item.value['name'] == name:
  47             return
  48 
  49         item.value['name'] = name
  50         item.setValue(item.value)
  51 
  52     @recursive
  53     def setValue(self, item, value):
  54         import os
  55 
  56         if 'value' in item.value.keys():
  57             #group
  58             item.value['value'] = value
  59         else:
  60             name = item.value['name']
  61             if os.environ[name] == value:
  62                 return
  63 
  64             os.environ[name] = value
  65 
  66         item.setValue(item.value)

recursive デコレータがついている関数がモジュール特有の処理を担当するもので、GUI 上で値が 変更されるとこれらが呼ばれる。この関数は recursive デコレータによって親子関係に沿って アイテムのに対して全て同じ処理がおこなわれる。

view.py

view.py の実装は以下の通りである

   1 import os
   2 
   3 try:
   4     from PySide import QtCore, QtGui
   5 except:
   6     from PyQt4 import QtCore, QtGui
   7 
   8 from JCGS.Architecture.Qt import View
   9 
  10 class view(object):
  11     class Item(View.TreeWidget.Item):
  12         def _displayGroup(self, column, v):
  13             if column == 0:
  14                 return v.value['name']
  15             if column == 1:
  16                 return v.value['value']
  17 
  18             return None
  19 
  20         def _displayEnviron(self, column, v):
  21             if column == 0:
  22                 return v.value['name']
  23             if column == 1:
  24                 return os.environ[v.value['name']]
  25 
  26             return None
  27 
  28         def display(self, column):
  29             v = self.getPyObject()
  30             if 'value' in v.value.keys():
  31                 return self._displayGroup(column, v)
  32             else:
  33                 return self._displayEnviron(column, v)
  34 
  35             return None
  36 
  37     def __init__(self, model):
  38         self.model = model
  39 
  40         self.columns = [
  41             {
  42                 'label' : 'name',
  43                 'widget' : None,
  44                 'callbacks' : {
  45                     },
  46                 },
  47             {
  48                 'label' : 'value',
  49                 'widget' : QtGui.QLineEdit,
  50                 'callbacks' :{
  51                     'textEdited (const QString&)': self.valueChanged
  52                     },
  53                 },
  54             ]
  55 
  56         self.icons = {}
  57 
  58         f = os.path.join(os.path.dirname(__file__), 'icons', 'brick.png')
  59         self.icons['item'] = QtGui.QIcon(f)
  60         f = os.path.join(os.path.dirname(__file__), 'icons', 'folder.png')
  61         self.icons['folder'] = QtGui.QIcon(f)
  62 
  63     def updateSubWidgetInfo(self, itemListWidget, item, column):
  64         w = itemListWidget.itemWidget(item, column)
  65         data = item.getPyObject()
  66 
  67         if column == 0:
  68             #name
  69             pass
  70         elif column == 1:
  71             #value
  72             if 'value' in data.value.keys():
  73                 w.setText(data.value['value'])
  74             else:
  75                 name = data.value['name']
  76                 w.setText(os.environ[name])
  77 
  78     def valueChanged(self, widget, item, value):
  79         v = item.getPyObject()
  80         self.model.setValue(v, unicode(value))

view.Item が表示の際に実際に使用されるアイテムである。

self.columns によって、各列にウィジェットを配置して表示と挙動をカスタマイズすることができる。

処理の流れとしては以下の通りである

  1. 2番目の列で使用されている QtGui.QLineEdit の内容が変更される

  2. callbacks で定義されている textchanged に対応する self.valueChanged() が呼ばれる
  3. モデル内の setValue() が呼ばれる

内部の動作

Group Manager 本体はグループ管理を行う際の共通処理を担当し、モジュールが実処理を担当している。 実際の処理を行う場合は Group Manager 本体の処理の中から必要に応じてモジュールの機能が呼び出されるため、 慣れないと全体像が把握し辛い。ここでは全体の処理の流れの解説を行う

太字の部分が、モジュール毎に作成する部分

model.py

検索

  1. search ボタン押下 -- groupManagerWindow
  2. app.searchCandidate(keyword) -- App
  3. model.query(keyword) -- モジュールの model.py

    • model.query で登録するアイテムに該当する値が返ってくる
    • 値のフォーマットはモジュール毎に定義してよい
  4. 結果をリストに登録 -- App

アイテムの追加

  1. App.addItem() -- App
  2. model.getItem() -- モジュールの model.py

    • 使用する値が引数として渡され、ModelItem を返す

※処理としてはアイテムの追加と同じ。グループとしてどのように扱うかはモジュール依存

グループの編集

  1. アイテムのドラッグアンドドロップ -- groupManagerWidgets.itemList
  2. メインウィンドウでドラッグアンドドロップ処理
    • この時点では階層の編集をするという命令が伝わるだけ
  3. Application.modifyHierarchy で定義されている処理の呼び出し
    • デフォルトでは modifyHierarchy プラグインが呼び出される
  4. model.checkModify() -- モジュールの model.py

    • checkModify はドラッグアンドドロップされたアイテム全てを引数にとって、新しい親の子にできるアイテムだけを返す
  5. model.checkModify の結果を元に階層編集 -- modifyHierarchy.py

シリアライズ

  1. model.dumpAll() -- 実体は modelBase.py で定義
  2. model.dump() -- モジュールの model.py

    • アイテムを文字列化
  3. dumpAll がまとめて出力用データを作成(実際はリストにするだけ)

デシリアライズ

  1. model.restoreAll() -- 実体は modelBase.py で定義
  2. model.restore() -- モジュールの model.py

    • 文字列をアイテムを化

ファイルへの保存

  1. GUI で File/Save を選択
  2. XML で定義されている Application.fileMenu.saveFile の処理が行われる
    • デフォルトでは groupManager.plugins.save
  3. シリアライズ
  4. save.py がファイルに保存

ファイルの読み込み

  1. GUI で File/Open を選択
  2. XML で定義されている Application.fileMenu.openFile の処理が行われる
    • デフォルトでは groupManager.plugins.load
  3. デシリアライズ

view.py

表示内容の初期化

  1. GUI の初期化
  2. view.__init__

    • self.columns で各列のラベル、ウィジェット、イベントハンドラの登録をおこなう

アイテムの登録

モジュール内で特に考慮する事柄はないので省略

アイテムの表示

  1. アイテムの追加や再描画などのイベントが発生 -- GUI
  2. view.Item.display()

    • 指定されたコラムで表示しなければいけない値を返す

GUI からの値の更新

  1. GUI 上でチェックのオンオフ、値の変更などがおこなわれる
  2. モデルの値が更新される
  3. view.updateSubWidgetInfo()

    • 更新された情報を元にアイテムの情報を更新する