目次
アーキテクチャ
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 によって、各列にウィジェットを配置して表示と挙動をカスタマイズすることができる。
処理の流れとしては以下の通りである
2番目の列で使用されている QtGui.QLineEdit の内容が変更される
- callbacks で定義されている textchanged に対応する self.valueChanged() が呼ばれる
- モデル内の setValue() が呼ばれる
内部の動作
Group Manager 本体はグループ管理を行う際の共通処理を担当し、モジュールが実処理を担当している。 実際の処理を行う場合は Group Manager 本体の処理の中から必要に応じてモジュールの機能が呼び出されるため、 慣れないと全体像が把握し辛い。ここでは全体の処理の流れの解説を行う
※太字の部分が、モジュール毎に作成する部分
model.py
検索
- search ボタン押下 -- groupManagerWindow
- app.searchCandidate(keyword) -- App
model.query(keyword) -- モジュールの model.py
- model.query で登録するアイテムに該当する値が返ってくる
- 値のフォーマットはモジュール毎に定義してよい
- 結果をリストに登録 -- App
アイテムの追加
- App.addItem() -- App
model.getItem() -- モジュールの model.py
使用する値が引数として渡され、ModelItem を返す
※処理としてはアイテムの追加と同じ。グループとしてどのように扱うかはモジュール依存
グループの編集
- アイテムのドラッグアンドドロップ -- groupManagerWidgets.itemList
- メインウィンドウでドラッグアンドドロップ処理
- この時点では階層の編集をするという命令が伝わるだけ
- Application.modifyHierarchy で定義されている処理の呼び出し
- デフォルトでは modifyHierarchy プラグインが呼び出される
model.checkModify() -- モジュールの model.py
- checkModify はドラッグアンドドロップされたアイテム全てを引数にとって、新しい親の子にできるアイテムだけを返す
- model.checkModify の結果を元に階層編集 -- modifyHierarchy.py
シリアライズ
- model.dumpAll() -- 実体は modelBase.py で定義
model.dump() -- モジュールの model.py
- アイテムを文字列化
- dumpAll がまとめて出力用データを作成(実際はリストにするだけ)
デシリアライズ
- model.restoreAll() -- 実体は modelBase.py で定義
model.restore() -- モジュールの model.py
- 文字列をアイテムを化
ファイルへの保存
- GUI で File/Save を選択
- XML で定義されている Application.fileMenu.saveFile の処理が行われる
- デフォルトでは groupManager.plugins.save
- シリアライズ
- save.py がファイルに保存
ファイルの読み込み
- GUI で File/Open を選択
- XML で定義されている Application.fileMenu.openFile の処理が行われる
- デフォルトでは groupManager.plugins.load
- デシリアライズ
view.py
表示内容の初期化
- GUI の初期化
view.__init__
- self.columns で各列のラベル、ウィジェット、イベントハンドラの登録をおこなう
アイテムの登録
モジュール内で特に考慮する事柄はないので省略
アイテムの表示
- アイテムの追加や再描画などのイベントが発生 -- GUI
view.Item.display()
- 指定されたコラムで表示しなければいけない値を返す
GUI からの値の更新
- GUI 上でチェックのオンオフ、値の変更などがおこなわれる
- モデルの値が更新される
view.updateSubWidgetInfo()
- 更新された情報を元にアイテムの情報を更新する