Skip to content

PySideでUSD関係のGUIを作ろう - SceneGraph(1)

最近PySideを触っていなくてすっかり頭から抜けてしまったので、リハビリがてらUsd関係の共通GUIを
作っていこうと思います。
まずはUsdのシーングラフをTreeViewで表示するためのModelを作って見ます。

完成はこんな感じ。

表示するデータ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#usda 1.0

def Xform "test"
{
    def "hello"{}
    def "world"{}
    def "hoge"
    {
        def "fuga"{}
    }
}

表示するUSDはこんな感じ。
とりあえずはシンプルな状態から。

ソースコード

長いけど全コード

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# -*- coding: utf-8 -*-

import sys
import os.path
from PySide2.QtWidgets import (QApplication, QMainWindow, QTreeView)
from PySide2.QtCore import (QModelIndex, Qt, QAbstractItemModel)
from PySide2.QtUiTools import QUiLoader

from pxr import Usd, Sdf

sample_usd = "D:/work/usd_py36/usd/tree_view_sample.usda"


class UISample(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.resize(600, 400)

        self.view = QTreeView()

        self.ui = QUiLoader().load('D:/work/usd_py36/pysidse/tree_view.ui')
        self.setCentralWidget(self.ui)

        self.model = UsdStageModel(sample_usd)
        self.ui.treeView.setModel(self.model)


class PrimItem(object):

    def __init__(self, prim=None, parentItem=None):
        self._prim = prim
        self._parentItem = parentItem
        self._childItems = []

    def addChild(self, item):
        self._childItems.append(item)

    def getChild(self, row):
        if row <= len(self._childItems):
            return self._childItems[row]
        return None

    def getChildren(self):

        return self._childItems

    def getParentItem(self):
        return self._parentItem

    def getPrim(self):
        return self._prim

    def row(self):
        return self._parentItem.getChildren().index(self)

    def data(self, column):

        if column == 0:
            return self._prim.GetName()
        if column == 1:
            return self._prim.GetTypeName()
        if column == 2:
            return str(self._prim.GetPath())


class UsdStageModel(QAbstractItemModel):
    header = ["PrimName", "Type", "SdfPath"]

    def __init__(self, usdPath: str, parent=None):
        super().__init__(parent)

        # self.setupModelData(data.split('\n'), self.rootItem)
        self.stage = Usd.Stage.Open(usdPath)

        self.createModelTree()

    def createModelTree(self):

        # {SdfPath:Item}
        self.rootItem = PrimItem(self.stage.GetPrimAtPath("/"), None)
        prims = {Sdf.Path("/"): self.rootItem}
        for prim in self.stage.Traverse():
            print(prims)
            parentPath = prim.GetParent().GetPath()
            item = PrimItem(prim, prims[parentPath])
            prims[parentPath].addChild(item)
            prims[prim.GetPath()] = item

    def columnCount(self, parent):
        return 3

    def data(self, index, role):

        if not index.isValid():
            return None

        if role != Qt.DisplayRole:
            return None

        item = index.internalPointer()
        return item.data(index.column())

    def index(self, row, column, parent):

        if not self.hasIndex(row, column, parent):
            return QModelIndex()

        if not parent.isValid():
            parentItem = self.rootItem
        else:
            parentItem = parent.internalPointer()

        childItem = parentItem.getChild(row)

        if childItem:
            index = self.createIndex(row, column, childItem)
            return index
        else:
            return QModelIndex()

    def parent(self, index):

        if not index.isValid():
            return QModelIndex()

        childItem = index.internalPointer()
        parentItem = childItem.getParentItem()

        if parentItem == self.rootItem:
            return QModelIndex()

        return self.createIndex(parentItem.row(), 0, parentItem)

    def rowCount(self, parent):

        if parent.column() > 0:
            return 0

        if not parent.isValid():
            parentItem = self.rootItem
        else:
            parentItem = parent.internalPointer()
        return len(parentItem.getChildren())

    def headerData(self, section, orientation, role):

        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.header[section]


if __name__ == '__main__':
    app = QApplication(sys.argv)
    a = UISample()
    a.show()
    sys.exit(app.exec_())

以前にも作ったTreeViewの基本構造そのままです。
Modelに渡したUsdFileをTraverseして、Parent/Childの構造をつくります。
ItemにPrimObjectを入れて、そのItemを経由してViewに表示したい文字列を返すようにします。

コレを作る前は、
別にItem作らなくても、CreateIndexでPrimObjectをセットして、
GetParent()/GetChildren()使えばいいんじゃね?
と、試してみたのですが
その場合、internalPointerの部分で問答無用でスクリプトが落ちてしまい動きませんでした。
ポインタでオブジェクト取得してくる当たりが具合が悪いのか....

USDのリポジトリにはusdviewのソースコードも含まれているので
じゃあこちらはどうやってるのかなぁとみてみたら

pxr/usdImaging/lib/usdviewq/primViewItem.py

ここにコードがありました。
usdviewは、TreeWidgetつかってるのかぁとかWidgetItemで実装できるんだ、とか
色々と発見があったのですが
こちらでもItemにPrimObjectを入れて色々やっていたので
私もPrimItemを作る構造にしました。

次は

とりあえずこれで基本構造はできたので、今度はこれをGUI上から編集できるように
してみようとおもいます。