Skip to content

Commit

Permalink
prevent double graph setting when opening GraphViewer for first time
Browse files Browse the repository at this point in the history
test composition scene graph bidirectionality
removed unused change check
explicitly target value error messages
add test for re-opening stages and creating assets
  • Loading branch information
chrizzFTD committed Jan 13, 2024
1 parent 8c31d60 commit 3e31cea
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 23 deletions.
18 changes: 5 additions & 13 deletions grill/views/_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ def itemChange(self, change: QtWidgets.QGraphicsItem.GraphicsItemChange, value):
if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged:
for edge in self._edges:
edge.adjust()
elif change == QtWidgets.QGraphicsItem.ItemSelectedChange:
...
return super().itemChange(change, value)

def _activatePlug(self, edge, plug_index, side, position):
Expand Down Expand Up @@ -454,16 +452,6 @@ def _graph_url_changed(self, *_, **__):

self.view((key,))

def set_nx_layout(self, graph):
if not graph:
return

positions = drawing.nx_agraph.graphviz_layout(graph, prog='dot')
max_y = max(pos[1] for pos in positions.values())
for node, (x, y) in positions.items():
# SVG and dot seem to have inverted coordinates, let's flip Y
self._nodes_map[node].setPos(x, max_y - y)

def view(self, node_indices: tuple):
self._viewing = frozenset(node_indices)
graph = self._graph
Expand Down Expand Up @@ -553,4 +541,8 @@ def _add_node(nx_node):
edge = _Edge(source, target, color=color, label=label, is_bidirectional=is_bidirectional, **kwargs)
self.scene().addItem(edge)

self.set_nx_layout(graph)
positions = drawing.nx_agraph.graphviz_layout(graph, prog='dot')
max_y = max(pos[1] for pos in positions.values())
for node, (x, y) in positions.items():
# SVG and dot have inverted coordinates, let's flip Y
self._nodes_map[node].setPos(x, max_y - y)
2 changes: 1 addition & 1 deletion grill/views/description.py
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,7 @@ def _update_graph_from_graph_info(self, graph_info: _GraphInfo):
graph.graph['graph'] = dict(tooltip="LayerStack Composition")
graph.add_nodes_from(self._computed_graph_info.nodes.items())
graph.add_edges_from(self._iedges(graph_info))
self._graph_view.graph = graph
self._graph_view._graph = graph
self._graph_view.sticky_nodes.extend(graph_info.sticky_nodes)
self._layers.model.setLayers(graph_info.ids_by_layers)
# view intersection as we might be seeing nodes that no longer exist
Expand Down
39 changes: 30 additions & 9 deletions tests/test_cook.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,16 @@ def test_define_taxon(self):
# Now, test stages fetched from the start via "common" pipeline calls.
root_stage = cook.fetch_stage(self.root_asset)

with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, "reserved name"):
cook.define_taxon(root_stage, cook._TAXONOMY_NAME)

with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, "reserved id fields"):
cook.define_taxon(root_stage, "taxonomy_not_allowed", id_fields={cook._TAXONOMY_UNIQUE_ID: "by_id_value"})

with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, "reserved id fields"):
cook.define_taxon(root_stage, "taxonomy_not_allowed", id_fields={cook._TAXONOMY_UNIQUE_ID.name: "by_id_name"})

with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, "invalid id_field keys"):
cook.define_taxon(root_stage, "nonexistingfield", id_fields={str(uuid.uuid4()): "by_id_name"})

displayable = cook.define_taxon(root_stage, "DisplayableName")
Expand All @@ -107,24 +107,25 @@ def test_define_taxon(self):
with cook.taxonomy_context(root_stage):
displayable.CreateAttribute("label", Sdf.ValueTypeNames.String)

missing_or_empty_fields_msg = f"Missing or empty '{cook._FIELDS_KEY}'"
not_taxon = root_stage.DefinePrim("/not/a/taxon")
with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, missing_or_empty_fields_msg):
cook.create_unit(not_taxon, "WillFail")

not_taxon.SetAssetInfoByKey(cook._ASSETINFO_KEY, {})
with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, missing_or_empty_fields_msg):
cook.create_unit(not_taxon, "WillFail")

not_taxon.SetAssetInfoByKey(cook._ASSETINFO_KEY, {'invalid': 42})
with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, missing_or_empty_fields_msg):
cook.create_unit(not_taxon, "WillFail")

not_taxon.SetAssetInfoByKey(cook._ASSETINFO_KEY, {cook._FIELDS_KEY: 42})
with self.assertRaises(TypeError):
with self.assertRaisesRegex(TypeError, f"Expected mapping on key '{cook._FIELDS_KEY}'"):
cook.create_unit(not_taxon, "WillFail")

not_taxon.SetAssetInfoByKey(cook._ASSETINFO_KEY, {cook._FIELDS_KEY: {}})
with self.assertRaises(ValueError):
with self.assertRaisesRegex(ValueError, missing_or_empty_fields_msg):
cook.create_unit(not_taxon, "WillFail")

emil = cook.create_unit(person, "EmilSinclair", label="Emil Sinclair")
Expand All @@ -141,6 +142,26 @@ def test_define_taxon(self):
self.assertEqual(expected_people, list(cook.itaxa(stage_prims, person)))
self.assertEqual(expected_heroes, list(cook.itaxa(stage_prims, hero)))

def test_create_on_previous_stage(self):
"""Confirm that creating assets on a previously saved stage works.
The default behavior from layer identifiers that are relative to the resolver search path is to be absolute
when a stage using them is re-opened, so:
original_identifier.usda
becomes
/absolute/path/original_identifier.usda
"""
root_asset = names.UsdAsset.get_anonymous()
root_stage = cook.fetch_stage(root_asset)
# creates taxonomy.usda and adds it to the stage layer stack
cook.define_taxon(root_stage, "FirstTaxon")
root_stage.Save()
del root_stage

reopened_stage = cook.fetch_stage(root_asset)
# the taxonomy.usda now has as identifier /absolute/path/taxonomy.usda, so confirm we can use it still
cook.create_many(cook.define_taxon(reopened_stage, "SecondTaxon"), ["A", "B"])

def test_asset_unit(self):
stage = cook.fetch_stage(self.root_asset)
taxon_name = "Person"
Expand Down
22 changes: 22 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,28 @@ def _sub_test_scenegraph_composition(self):

widget.deleteLater()

def test_layer_stack_bidirectionality(self):
"""Confirm that bidirectionality between layer stacks completes.
Bidirectionality in the composition graph is achieved by:
- parent_stage -> child_stage via a reference, payload arcs
- child_stage -> parent_stage via a inherits, specializes arcs
"""
parent_stage = Usd.Stage.CreateInMemory()
child_stage = Usd.Stage.CreateInMemory()
prim = parent_stage.DefinePrim("/a/b")
child_prim = child_stage.DefinePrim("/child")
child_prim.GetInherits().AddInherit("/foo")
child_prim.GetSpecializes().AddSpecialize("/foo")
child_stage.SetDefaultPrim(child_prim)
child_identifier = child_stage.GetRootLayer().identifier
prim.GetReferences().AddReference(child_identifier)
prim.GetPayloads().AddPayload(child_identifier)

widget = description.LayerStackComposition()
widget.setStage(parent_stage)
widget._layers.table.selectAll()

def test_prim_composition(self):
temp = Usd.Stage.CreateInMemory()
temp.GetRootLayer().subLayerPaths = [self.nested.GetStage().GetRootLayer().identifier]
Expand Down

0 comments on commit 3e31cea

Please sign in to comment.