From f5efaa2193e56045c7b74997daccaf498f8fde0d Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Mon, 10 Apr 2023 16:47:09 -0400 Subject: [PATCH 01/17] Please linter --- .../mf/ov/ndi/tests/test_USDtools.py | 22 +++++ exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py | 99 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py create mode 100644 exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py new file mode 100644 index 0000000..1e741d6 --- /dev/null +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py @@ -0,0 +1,22 @@ +import omni.kit.test +import mf.ov.ndi as ext + + +class USDUnitTest(omni.kit.test.AsyncTestCase): + async def test_name_valid(self): + self.check_name_valid("myDynamicMaterial", "myDynamicMaterial") + self.check_name_valid("789testing123numbers456", "_89testing123numbers456") + self.check_name_valid("", "_") + self.check_name_valid("àâáäãåÀÂÁÃÅÄ", "aaaaaaAAAAAA") + self.check_name_valid("èêéëÈÊÉË", "eeeeEEEE") + self.check_name_valid("ìîíïÌÎÍÏ", "iiiiIIII") + self.check_name_valid("òôóöõøÒÔÓÕÖØ", "ooooooOOOOOO") + self.check_name_valid("ùûúüÙÛÚÜ", "uuuuUUUU") + self.check_name_valid("æœÆŒçÇ°ðÐñÑýÝþÞÿß", "aeoeAEOEcCdegdDnNyYthThyss") + self.check_name_valid("!¡¿@#$%?&*()-_=+/`^~.,'\\<>`;:¤{}[]|\"¦¨«»¬¯±´·¸÷", + "____________________________________________________") + self.check_name_valid("¢£¥§©ªº®¹²³µ¶¼½¾×", "C_PSY_SS_c_ao_r_123uP_1_4_1_2_3_4x") + + def check_name_valid(self, source, expected): + v: str = ext.USDtools.make_name_valid(source) + self.assertEqual(v, expected, f"Expected \"{v}\", derived from \"{source}\", to equals \"{expected}\"") diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py new file mode 100644 index 0000000..a47e547 --- /dev/null +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py @@ -0,0 +1,99 @@ +import omni.kit.test +import mf.ov.ndi as ext + + +SOURCE1 = "MY-PC (Test Pattern)" +SOURCE2 = "MY-PC (Test Pattern 2)" +DYNAMIC_ID1 = "myDynamicMaterial1" +DYNAMIC_ID2 = "myDynamicMaterial2" +PATH = "/path/to/dummy" + + +class NDIBindingsUnitTest(omni.kit.test.AsyncTestCase): + async def test_dynamic_id(self): + ndi_data = ext.NDIData(SOURCE1) + ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) + + self.assertEqual(ndi_binding.get_id(), DYNAMIC_ID1) + self.assertEqual(ndi_binding.get_id_full(), ext.USDtools.PREFIX + DYNAMIC_ID1) + + async def test_source(self): + ndi_data1 = ext.NDIData(SOURCE1) + ndi_data2 = ext.NDIData(SOURCE2) + + ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data1, PATH, False) + self.assertEqual(ndi_binding.get_source(), SOURCE1) + + ndi_binding.set_ndi_id(ndi_data2) + self.assertEqual(ndi_binding.get_source(), SOURCE2) + + async def test_lowbandwidth(self): + ndi_data = ext.NDIData(SOURCE1) + ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) + self.assertFalse(ndi_binding.get_lowbandwidth()) + + ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data, PATH, True) + self.assertTrue(ndi_binding.get_lowbandwidth()) + + ndi_binding.set_lowbandwidth(False) + self.assertFalse(ndi_binding.get_lowbandwidth()) + ndi_binding.set_lowbandwidth(True) + self.assertTrue(ndi_binding.get_lowbandwidth()) + pass + + +class ModelUnitTest(omni.kit.test.AsyncTestCase): + def setUp(self): + self._model = ext.NDIModel(None) + + def tearDown(self): + self._model.on_shutdown() + + async def test_no_stream_at_start(self): + streams_length = len(self._model._streams) + self.assertEqual(streams_length, 0) + + async def test_add_stream_NONE(self): + streams_base_length = len(self._model._streams) + + self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.NONE_VALUE, False) + self.assertEqual(len(self._model._streams), streams_base_length) + + async def test_add_stream_PROXY(self): + streams_length = len(self._model._streams) + + self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE, False) + self.assertEqual(len(self._model._streams), streams_length + 1) + + async def test_kill_all_streams(self): + self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID2, ext.ComboboxModel.PROXY_VALUE, False) + self.assertGreater(len(self._model._streams), 0) + + self._model.kill_all_streams() + self.assertEqual(len(self._model._streams), 0) + + async def test_remove_stream(self): + self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID2, ext.ComboboxModel.PROXY_VALUE, False) + streams_length = len(self._model._streams) + self.assertGreater(streams_length, 0) + + self._model.remove_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE) + self.assertEqual(len(self._model._streams), streams_length - 1) + self._model.remove_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE) + self.assertEqual(len(self._model._streams), streams_length - 1) + self._model.remove_stream(DYNAMIC_ID2, ext.ComboboxModel.PROXY_VALUE) + self.assertEqual(len(self._model._streams), streams_length - 2) + + +""" + async def test_add_stream(self): + model = ext.NDIModel(None) + streams_length = len(model._streams) + + model.add_stream(ModelUnitTest.DYNAMIC_ID, ModelUnitTest.SOURCE1, False) + self.assertEqual(len(model._streams), streams_length + 1) + model.add_stream(ModelUnitTest.DYNAMIC_ID, ModelUnitTest.SOURCE2, False) + self.assertEqual(len(model._streams), streams_length + 2) +""" From d1f0162eefa14db197a45f84614b8e4a91004d14 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Mon, 10 Apr 2023 16:56:21 -0400 Subject: [PATCH 02/17] Fix USDtools test --- exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py index 1e741d6..87726f2 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py @@ -14,7 +14,7 @@ async def test_name_valid(self): self.check_name_valid("ùûúüÙÛÚÜ", "uuuuUUUU") self.check_name_valid("æœÆŒçÇ°ðÐñÑýÝþÞÿß", "aeoeAEOEcCdegdDnNyYthThyss") self.check_name_valid("!¡¿@#$%?&*()-_=+/`^~.,'\\<>`;:¤{}[]|\"¦¨«»¬¯±´·¸÷", - "____________________________________________________") + "___________________________________________________") self.check_name_valid("¢£¥§©ªº®¹²³µ¶¼½¾×", "C_PSY_SS_c_ao_r_123uP_1_4_1_2_3_4x") def check_name_valid(self, source, expected): From 86a93594091513e2d1c8e94c7736b238e60da3cc Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Mon, 10 Apr 2023 17:00:36 -0400 Subject: [PATCH 03/17] Added NDIData tests --- exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py | 3 +++ .../mf/ov/ndi/tests/test_NDItools.py | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py create mode 100644 exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py new file mode 100644 index 0000000..7e643f3 --- /dev/null +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py @@ -0,0 +1,3 @@ +from .test_USDtools import * +from .test_model import * +from .test_NDItools import * diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py new file mode 100644 index 0000000..6bbc13b --- /dev/null +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py @@ -0,0 +1,27 @@ +import omni.kit.test +import mf.ov.ndi as ext + + +SOURCE = "MY-PC (Test Pattern)" + + +class NDIDataUnitTest(omni.kit.test.AsyncTestCase): + async def test_source(self): + data = ext.NDIData(SOURCE, False) + self.assertEqual(data.get_source(), SOURCE) + + async def test_active(self): + data = ext.NDIData(SOURCE) + self.assertFalse(data.is_active()) + + data = ext.NDIData(SOURCE, False) + self.assertFalse(data.is_active()) + + data = ext.NDIData(SOURCE, True) + self.assertTrue(data.is_active()) + + data.set_active(False) + self.assertFalse(data.is_active()) + + data.set_active(True) + self.assertTrue(data.is_active()) From 543c6c98431c4c3962fe75d8f3a28397ae0deb7f Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Mon, 10 Apr 2023 17:57:06 -0400 Subject: [PATCH 04/17] Complete USDtools unit tests --- .../mf/ov/ndi/tests/test_USDtools.py | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py index 87726f2..5b838fd 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py @@ -1,8 +1,12 @@ import omni.kit.test import mf.ov.ndi as ext +from pxr import UsdLux -class USDUnitTest(omni.kit.test.AsyncTestCase): +SOURCE = "MY-PC (Test Pattern)" + + +class USDValidNameUnitTest(omni.kit.test.AsyncTestCase): async def test_name_valid(self): self.check_name_valid("myDynamicMaterial", "myDynamicMaterial") self.check_name_valid("789testing123numbers456", "_89testing123numbers456") @@ -20,3 +24,52 @@ async def test_name_valid(self): def check_name_valid(self, source, expected): v: str = ext.USDtools.make_name_valid(source) self.assertEqual(v, expected, f"Expected \"{v}\", derived from \"{source}\", to equals \"{expected}\"") + + +class USDToolsUnitTest(omni.kit.test.AsyncTestCase): + def setUp(self): + usd_context = omni.usd.get_context() + usd_context.new_stage() + # self._stage = Usd.Stage.CreateInMemory() + + self._stage = ext.USDtools.get_stage() + prim = self._stage.DefinePrim("/World") + self._stage.SetDefaultPrim(prim) + + def tearDown(self): + usd_context = omni.usd.get_context() + usd_context.close_stage() + + async def test_create_dynamic_material(self): + material = ext.USDtools.create_dynamic_material("myDynamicMaterial") + prim = self._stage.GetPrimAtPath(material.GetPath()) + self.assertIsNotNone(prim) + + async def test_find_dynamic_sources(self): + ext.USDtools.create_dynamic_material("myDynamicMaterial1") + + path: str = f"{self._stage.GetDefaultPrim().GetPath()}/myRectLight" + light = UsdLux.RectLight.Define(self._stage, path) + light.GetPrim().GetAttribute("texture:file").Set(f"{ext.USDtools.PREFIX}/myDynamicMaterial2") + + sources = ext.USDtools.find_all_dynamic_sources() + self.assertEqual(len(sources), 2) + + async def test_set_property_ndi(self): + material = ext.USDtools.create_dynamic_material("myDynamicMaterial") + path = material.GetPath() + ext.USDtools.set_prim_ndi_attribute(path, SOURCE) + + attr = material.GetPrim().GetAttribute(ext.USDtools.ATTR_NDI_NAME) + self.assertEqual(attr.Get(), SOURCE) + + async def test_set_property_bandwidth(self): + material = ext.USDtools.create_dynamic_material("myDynamicMaterial") + path = material.GetPath() + ext.USDtools.set_prim_bandwidth_attribute(path, True) + + attr = material.GetPrim().GetAttribute(ext.USDtools.ATTR_BANDWIDTH_NAME) + self.assertTrue(attr.Get()) + + ext.USDtools.set_prim_bandwidth_attribute(path, False) + self.assertFalse(attr.Get()) From 3193ecf6cee1ae47712affbc4daa7977ccfe63c7 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 09:32:33 -0400 Subject: [PATCH 05/17] Fix import local dependencies --- .../mf/ov/ndi/tests/test_NDItools.py | 10 ++--- .../mf/ov/ndi/tests/test_USDtools.py | 28 ++++++------ exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py | 45 ++++++++++--------- 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py index 6bbc13b..6a3bca4 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py @@ -1,5 +1,5 @@ import omni.kit.test -import mf.ov.ndi as ext +from ..NDItools import NDIData SOURCE = "MY-PC (Test Pattern)" @@ -7,17 +7,17 @@ class NDIDataUnitTest(omni.kit.test.AsyncTestCase): async def test_source(self): - data = ext.NDIData(SOURCE, False) + data = NDIData(SOURCE, False) self.assertEqual(data.get_source(), SOURCE) async def test_active(self): - data = ext.NDIData(SOURCE) + data = NDIData(SOURCE) self.assertFalse(data.is_active()) - data = ext.NDIData(SOURCE, False) + data = NDIData(SOURCE, False) self.assertFalse(data.is_active()) - data = ext.NDIData(SOURCE, True) + data = NDIData(SOURCE, True) self.assertTrue(data.is_active()) data.set_active(False) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py index 5b838fd..2f87084 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py @@ -1,5 +1,5 @@ import omni.kit.test -import mf.ov.ndi as ext +from ..USDtools import USDtools from pxr import UsdLux @@ -22,7 +22,7 @@ async def test_name_valid(self): self.check_name_valid("¢£¥§©ªº®¹²³µ¶¼½¾×", "C_PSY_SS_c_ao_r_123uP_1_4_1_2_3_4x") def check_name_valid(self, source, expected): - v: str = ext.USDtools.make_name_valid(source) + v: str = USDtools.make_name_valid(source) self.assertEqual(v, expected, f"Expected \"{v}\", derived from \"{source}\", to equals \"{expected}\"") @@ -32,7 +32,7 @@ def setUp(self): usd_context.new_stage() # self._stage = Usd.Stage.CreateInMemory() - self._stage = ext.USDtools.get_stage() + self._stage = USDtools.get_stage() prim = self._stage.DefinePrim("/World") self._stage.SetDefaultPrim(prim) @@ -41,35 +41,35 @@ def tearDown(self): usd_context.close_stage() async def test_create_dynamic_material(self): - material = ext.USDtools.create_dynamic_material("myDynamicMaterial") + material = USDtools.create_dynamic_material("myDynamicMaterial") prim = self._stage.GetPrimAtPath(material.GetPath()) self.assertIsNotNone(prim) async def test_find_dynamic_sources(self): - ext.USDtools.create_dynamic_material("myDynamicMaterial1") + USDtools.create_dynamic_material("myDynamicMaterial1") path: str = f"{self._stage.GetDefaultPrim().GetPath()}/myRectLight" light = UsdLux.RectLight.Define(self._stage, path) - light.GetPrim().GetAttribute("texture:file").Set(f"{ext.USDtools.PREFIX}/myDynamicMaterial2") + light.GetPrim().GetAttribute("texture:file").Set(f"{USDtools.PREFIX}/myDynamicMaterial2") - sources = ext.USDtools.find_all_dynamic_sources() + sources = USDtools.find_all_dynamic_sources() self.assertEqual(len(sources), 2) async def test_set_property_ndi(self): - material = ext.USDtools.create_dynamic_material("myDynamicMaterial") + material = USDtools.create_dynamic_material("myDynamicMaterial") path = material.GetPath() - ext.USDtools.set_prim_ndi_attribute(path, SOURCE) + USDtools.set_prim_ndi_attribute(path, SOURCE) - attr = material.GetPrim().GetAttribute(ext.USDtools.ATTR_NDI_NAME) + attr = material.GetPrim().GetAttribute(USDtools.ATTR_NDI_NAME) self.assertEqual(attr.Get(), SOURCE) async def test_set_property_bandwidth(self): - material = ext.USDtools.create_dynamic_material("myDynamicMaterial") + material = USDtools.create_dynamic_material("myDynamicMaterial") path = material.GetPath() - ext.USDtools.set_prim_bandwidth_attribute(path, True) + USDtools.set_prim_bandwidth_attribute(path, True) - attr = material.GetPrim().GetAttribute(ext.USDtools.ATTR_BANDWIDTH_NAME) + attr = material.GetPrim().GetAttribute(USDtools.ATTR_BANDWIDTH_NAME) self.assertTrue(attr.Get()) - ext.USDtools.set_prim_bandwidth_attribute(path, False) + USDtools.set_prim_bandwidth_attribute(path, False) self.assertFalse(attr.Get()) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py index a47e547..0c94411 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py @@ -1,5 +1,8 @@ import omni.kit.test -import mf.ov.ndi as ext +from ..NDItools import NDIData +from ..model import NDIBinding, NDIModel +from ..USDtools import USDtools +from ..comboboxModel import ComboboxModel SOURCE1 = "MY-PC (Test Pattern)" @@ -11,28 +14,28 @@ class NDIBindingsUnitTest(omni.kit.test.AsyncTestCase): async def test_dynamic_id(self): - ndi_data = ext.NDIData(SOURCE1) - ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) + ndi_data = NDIData(SOURCE1) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) self.assertEqual(ndi_binding.get_id(), DYNAMIC_ID1) - self.assertEqual(ndi_binding.get_id_full(), ext.USDtools.PREFIX + DYNAMIC_ID1) + self.assertEqual(ndi_binding.get_id_full(), USDtools.PREFIX + DYNAMIC_ID1) async def test_source(self): - ndi_data1 = ext.NDIData(SOURCE1) - ndi_data2 = ext.NDIData(SOURCE2) + ndi_data1 = NDIData(SOURCE1) + ndi_data2 = NDIData(SOURCE2) - ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data1, PATH, False) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data1, PATH, False) self.assertEqual(ndi_binding.get_source(), SOURCE1) ndi_binding.set_ndi_id(ndi_data2) self.assertEqual(ndi_binding.get_source(), SOURCE2) async def test_lowbandwidth(self): - ndi_data = ext.NDIData(SOURCE1) - ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) + ndi_data = NDIData(SOURCE1) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) self.assertFalse(ndi_binding.get_lowbandwidth()) - ndi_binding = ext.NDIBinding(DYNAMIC_ID1, ndi_data, PATH, True) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, PATH, True) self.assertTrue(ndi_binding.get_lowbandwidth()) ndi_binding.set_lowbandwidth(False) @@ -44,7 +47,7 @@ async def test_lowbandwidth(self): class ModelUnitTest(omni.kit.test.AsyncTestCase): def setUp(self): - self._model = ext.NDIModel(None) + self._model = NDIModel(None) def tearDown(self): self._model.on_shutdown() @@ -56,40 +59,40 @@ async def test_no_stream_at_start(self): async def test_add_stream_NONE(self): streams_base_length = len(self._model._streams) - self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.NONE_VALUE, False) + self._model.add_stream(DYNAMIC_ID1, ComboboxModel.NONE_VALUE, False) self.assertEqual(len(self._model._streams), streams_base_length) async def test_add_stream_PROXY(self): streams_length = len(self._model._streams) - self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID1, ComboboxModel.PROXY_VALUE, False) self.assertEqual(len(self._model._streams), streams_length + 1) async def test_kill_all_streams(self): - self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE, False) - self._model.add_stream(DYNAMIC_ID2, ext.ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID1, ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID2, ComboboxModel.PROXY_VALUE, False) self.assertGreater(len(self._model._streams), 0) self._model.kill_all_streams() self.assertEqual(len(self._model._streams), 0) async def test_remove_stream(self): - self._model.add_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE, False) - self._model.add_stream(DYNAMIC_ID2, ext.ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID1, ComboboxModel.PROXY_VALUE, False) + self._model.add_stream(DYNAMIC_ID2, ComboboxModel.PROXY_VALUE, False) streams_length = len(self._model._streams) self.assertGreater(streams_length, 0) - self._model.remove_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE) + self._model.remove_stream(DYNAMIC_ID1, ComboboxModel.PROXY_VALUE) self.assertEqual(len(self._model._streams), streams_length - 1) - self._model.remove_stream(DYNAMIC_ID1, ext.ComboboxModel.PROXY_VALUE) + self._model.remove_stream(DYNAMIC_ID1, ComboboxModel.PROXY_VALUE) self.assertEqual(len(self._model._streams), streams_length - 1) - self._model.remove_stream(DYNAMIC_ID2, ext.ComboboxModel.PROXY_VALUE) + self._model.remove_stream(DYNAMIC_ID2, ComboboxModel.PROXY_VALUE) self.assertEqual(len(self._model._streams), streams_length - 2) """ async def test_add_stream(self): - model = ext.NDIModel(None) + model = NDIModel(None) streams_length = len(model._streams) model.add_stream(ModelUnitTest.DYNAMIC_ID, ModelUnitTest.SOURCE1, False) From 13d2e6e2ad17b73d291b339dbe183ed47025a291 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 13:11:55 -0400 Subject: [PATCH 06/17] Added UI tests --- exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py | 1 + exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py | 61 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py index 7e643f3..8d293b6 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/__init__.py @@ -1,3 +1,4 @@ from .test_USDtools import * from .test_model import * from .test_NDItools import * +from .test_ui import * diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py new file mode 100644 index 0000000..5c11010 --- /dev/null +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py @@ -0,0 +1,61 @@ +import omni.kit.test +import omni.kit.ui_test as ui_test +from ..window import NDIWindow +from ..USDtools import USDtools +from pxr import Usd, UsdLux + + +DYNAMIC_ID1 = "myDynamicMaterial1" +DYNAMIC_ID2 = "myDynamicMaterial2" + + +class UITestsHeader(omni.kit.test.AsyncTestCase): + def _make_stage(self) -> Usd.Stage: + usd_context = omni.usd.get_context() + usd_context.new_stage() + stage = usd_context.get_stage() + stage.DefinePrim("/World") + return stage + + async def test_create_material_button(self): + stage = self._make_stage() + window = ui_test.find(NDIWindow.WINDOW_NAME) + + field = window.find("**/StringField[*]") + field.widget.model.set_value(DYNAMIC_ID1) + self.assertEqual(field.widget.model.get_value_as_string(), DYNAMIC_ID1) + + button = window.find("**/Button[*].text=='Create Dynamic Texture'") + await button.click() + + prim = stage.GetPrimAtPath(f"{stage.GetDefaultPrim().GetPath()}/NDI_Looks/{DYNAMIC_ID1}") + self.assertTrue(prim.IsValid) + + async def test_texture_discovery(self): + stage = self._make_stage() + window = ui_test.find(NDIWindow.WINDOW_NAME) + + USDtools.create_dynamic_material(DYNAMIC_ID1) + + path: str = f"{stage.GetDefaultPrim().GetPath()}/myRectLight" + light = UsdLux.RectLight.Define(stage, path) + light.GetPrim().GetAttribute("texture:file").Set(f"{USDtools.PREFIX}{DYNAMIC_ID2}") + + button = window.find("**/Button[*].text=='Discover Dynamic Textures'") + await button.click() + + panels = window.find_all("**/NDIBindingPanel[*]") + self.assertEqual(len(panels), 2) + + panel1_found = False + panel2_found = False + for panel in panels: + labels = panel.find_all("**/Label[*]") + for label in labels: + if label.widget.text == DYNAMIC_ID1: + panel1_found = True + elif label.widget.text == DYNAMIC_ID2: + panel2_found = True + + self.assertTrue(panel1_found) + self.assertTrue(panel2_found) From 96c6568904d05de4b204d4e43ca67abf16681194 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 13:25:07 -0400 Subject: [PATCH 07/17] Additional UI tests --- exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py index 5c11010..1497344 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py @@ -59,3 +59,24 @@ async def test_texture_discovery(self): self.assertTrue(panel1_found) self.assertTrue(panel2_found) + + +class UITestsPanel(omni.kit.test.AsyncTestCase): + async def test_no_panel_on_start(self): + window = ui_test.find(NDIWindow.WINDOW_NAME) + panel = window.find("**/NDIBindingPanel[*]") + self.assertIsNone(panel) + + label = window.find("**/Label[*]") + self.assertEqual(label.widget.text, "No dynamic texture found") + + +""" + async def test_combobox(self): + self._make + window = ui_test.find(NDIWindow.WINDOW_NAME) + button = window.find("**/Button[*].text=='Create Dynamic Texture'") + await button.click() + + combobox = window.find("**/ComboBox[*]") +""" From ced79e416d24e221500c7cc5de4ad51b3aeef4d6 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 14:32:51 -0400 Subject: [PATCH 08/17] Unit tests - moved utils fct to single file --- .../mf/ov/ndi/tests/test_NDItools.py | 14 ++-- .../mf/ov/ndi/tests/test_USDtools.py | 33 +++------- exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py | 20 ++---- exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py | 64 ++++++++----------- exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py | 55 ++++++++++++++++ 5 files changed, 105 insertions(+), 81 deletions(-) create mode 100644 exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py index 6a3bca4..29d9541 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_NDItools.py @@ -1,23 +1,21 @@ import omni.kit.test from ..NDItools import NDIData - - -SOURCE = "MY-PC (Test Pattern)" +from .test_utils import SOURCE1 class NDIDataUnitTest(omni.kit.test.AsyncTestCase): async def test_source(self): - data = NDIData(SOURCE, False) - self.assertEqual(data.get_source(), SOURCE) + data = NDIData(SOURCE1, False) + self.assertEqual(data.get_source(), SOURCE1) async def test_active(self): - data = NDIData(SOURCE) + data = NDIData(SOURCE1) self.assertFalse(data.is_active()) - data = NDIData(SOURCE, False) + data = NDIData(SOURCE1, False) self.assertFalse(data.is_active()) - data = NDIData(SOURCE, True) + data = NDIData(SOURCE1, True) self.assertTrue(data.is_active()) data.set_active(False) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py index 2f87084..c35ff06 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_USDtools.py @@ -1,9 +1,6 @@ import omni.kit.test from ..USDtools import USDtools -from pxr import UsdLux - - -SOURCE = "MY-PC (Test Pattern)" +from .test_utils import make_stage, close_stage, create_dynamic_material, create_dynamic_rectlight, SOURCE1 class USDValidNameUnitTest(omni.kit.test.AsyncTestCase): @@ -28,43 +25,33 @@ def check_name_valid(self, source, expected): class USDToolsUnitTest(omni.kit.test.AsyncTestCase): def setUp(self): - usd_context = omni.usd.get_context() - usd_context.new_stage() - # self._stage = Usd.Stage.CreateInMemory() - - self._stage = USDtools.get_stage() - prim = self._stage.DefinePrim("/World") - self._stage.SetDefaultPrim(prim) + self._stage = make_stage() def tearDown(self): - usd_context = omni.usd.get_context() - usd_context.close_stage() + close_stage() async def test_create_dynamic_material(self): - material = USDtools.create_dynamic_material("myDynamicMaterial") + material = create_dynamic_material() prim = self._stage.GetPrimAtPath(material.GetPath()) self.assertIsNotNone(prim) async def test_find_dynamic_sources(self): - USDtools.create_dynamic_material("myDynamicMaterial1") - - path: str = f"{self._stage.GetDefaultPrim().GetPath()}/myRectLight" - light = UsdLux.RectLight.Define(self._stage, path) - light.GetPrim().GetAttribute("texture:file").Set(f"{USDtools.PREFIX}/myDynamicMaterial2") + create_dynamic_material() + create_dynamic_rectlight() sources = USDtools.find_all_dynamic_sources() self.assertEqual(len(sources), 2) async def test_set_property_ndi(self): - material = USDtools.create_dynamic_material("myDynamicMaterial") + material = create_dynamic_material() path = material.GetPath() - USDtools.set_prim_ndi_attribute(path, SOURCE) + USDtools.set_prim_ndi_attribute(path, SOURCE1) attr = material.GetPrim().GetAttribute(USDtools.ATTR_NDI_NAME) - self.assertEqual(attr.Get(), SOURCE) + self.assertEqual(attr.Get(), SOURCE1) async def test_set_property_bandwidth(self): - material = USDtools.create_dynamic_material("myDynamicMaterial") + material = create_dynamic_material() path = material.GetPath() USDtools.set_prim_bandwidth_attribute(path, True) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py index 0c94411..df5113f 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_model.py @@ -3,19 +3,13 @@ from ..model import NDIBinding, NDIModel from ..USDtools import USDtools from ..comboboxModel import ComboboxModel - - -SOURCE1 = "MY-PC (Test Pattern)" -SOURCE2 = "MY-PC (Test Pattern 2)" -DYNAMIC_ID1 = "myDynamicMaterial1" -DYNAMIC_ID2 = "myDynamicMaterial2" -PATH = "/path/to/dummy" +from .test_utils import SOURCE1, SOURCE2, DYNAMIC_ID1, DYNAMIC_ID2, DUMMY_PATH class NDIBindingsUnitTest(omni.kit.test.AsyncTestCase): async def test_dynamic_id(self): ndi_data = NDIData(SOURCE1) - ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, DUMMY_PATH, False) self.assertEqual(ndi_binding.get_id(), DYNAMIC_ID1) self.assertEqual(ndi_binding.get_id_full(), USDtools.PREFIX + DYNAMIC_ID1) @@ -24,7 +18,7 @@ async def test_source(self): ndi_data1 = NDIData(SOURCE1) ndi_data2 = NDIData(SOURCE2) - ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data1, PATH, False) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data1, DUMMY_PATH, False) self.assertEqual(ndi_binding.get_source(), SOURCE1) ndi_binding.set_ndi_id(ndi_data2) @@ -32,10 +26,10 @@ async def test_source(self): async def test_lowbandwidth(self): ndi_data = NDIData(SOURCE1) - ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, PATH, False) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, DUMMY_PATH, False) self.assertFalse(ndi_binding.get_lowbandwidth()) - ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, PATH, True) + ndi_binding = NDIBinding(DYNAMIC_ID1, ndi_data, DUMMY_PATH, True) self.assertTrue(ndi_binding.get_lowbandwidth()) ndi_binding.set_lowbandwidth(False) @@ -95,8 +89,8 @@ async def test_add_stream(self): model = NDIModel(None) streams_length = len(model._streams) - model.add_stream(ModelUnitTest.DYNAMIC_ID, ModelUnitTest.SOURCE1, False) + model.add_stream(DYNAMIC_ID, SOURCE1, False) self.assertEqual(len(model._streams), streams_length + 1) - model.add_stream(ModelUnitTest.DYNAMIC_ID, ModelUnitTest.SOURCE2, False) + model.add_stream(DYNAMIC_ID, SOURCE2, False) self.assertEqual(len(model._streams), streams_length + 2) """ diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py index 1497344..0f8fbb5 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py @@ -1,50 +1,34 @@ import omni.kit.test -import omni.kit.ui_test as ui_test -from ..window import NDIWindow -from ..USDtools import USDtools -from pxr import Usd, UsdLux - - -DYNAMIC_ID1 = "myDynamicMaterial1" -DYNAMIC_ID2 = "myDynamicMaterial2" +from .test_utils import (make_stage, close_stage, get_window, DYNAMIC_ID1, DYNAMIC_ID2, create_dynamic_material, + create_dynamic_rectlight, refresh_dynamic_list) class UITestsHeader(omni.kit.test.AsyncTestCase): - def _make_stage(self) -> Usd.Stage: - usd_context = omni.usd.get_context() - usd_context.new_stage() - stage = usd_context.get_stage() - stage.DefinePrim("/World") - return stage + def setUp(self): + self._stage = make_stage() + self._window = get_window() - async def test_create_material_button(self): - stage = self._make_stage() - window = ui_test.find(NDIWindow.WINDOW_NAME) + def tearDown(self): + close_stage() - field = window.find("**/StringField[*]") + async def test_create_material_button(self): + field = self._window.find("**/StringField[*]") field.widget.model.set_value(DYNAMIC_ID1) self.assertEqual(field.widget.model.get_value_as_string(), DYNAMIC_ID1) - button = window.find("**/Button[*].text=='Create Dynamic Texture'") + button = self._window.find("**/Button[*].text=='Create Dynamic Texture'") await button.click() - prim = stage.GetPrimAtPath(f"{stage.GetDefaultPrim().GetPath()}/NDI_Looks/{DYNAMIC_ID1}") + prim = self._stage.GetPrimAtPath(f"{self._stage.GetDefaultPrim().GetPath()}/NDI_Looks/{DYNAMIC_ID1}") self.assertTrue(prim.IsValid) async def test_texture_discovery(self): - stage = self._make_stage() - window = ui_test.find(NDIWindow.WINDOW_NAME) - - USDtools.create_dynamic_material(DYNAMIC_ID1) + create_dynamic_material() + create_dynamic_rectlight() - path: str = f"{stage.GetDefaultPrim().GetPath()}/myRectLight" - light = UsdLux.RectLight.Define(stage, path) - light.GetPrim().GetAttribute("texture:file").Set(f"{USDtools.PREFIX}{DYNAMIC_ID2}") + await refresh_dynamic_list(self._window) - button = window.find("**/Button[*].text=='Discover Dynamic Textures'") - await button.click() - - panels = window.find_all("**/NDIBindingPanel[*]") + panels = self._window.find_all("**/NDIBindingPanel[*]") self.assertEqual(len(panels), 2) panel1_found = False @@ -62,20 +46,26 @@ async def test_texture_discovery(self): class UITestsPanel(omni.kit.test.AsyncTestCase): + def setUp(self): + self._stage = make_stage() + self._window = get_window() + + def tearDown(self): + close_stage() + async def test_no_panel_on_start(self): - window = ui_test.find(NDIWindow.WINDOW_NAME) - panel = window.find("**/NDIBindingPanel[*]") + await refresh_dynamic_list(self._window) + + panel = self._window.find("**/NDIBindingPanel[*]") self.assertIsNone(panel) - label = window.find("**/Label[*]") + label = self._window.find("**/Label[*]") self.assertEqual(label.widget.text, "No dynamic texture found") """ async def test_combobox(self): - self._make - window = ui_test.find(NDIWindow.WINDOW_NAME) - button = window.find("**/Button[*].text=='Create Dynamic Texture'") + button = self._window.find("**/Button[*].text=='Create Dynamic Texture'") await button.click() combobox = window.find("**/ComboBox[*]") diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py new file mode 100644 index 0000000..e202a00 --- /dev/null +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py @@ -0,0 +1,55 @@ +import omni +import omni.kit.ui_test as ui_test +from pxr import Usd, UsdLux, UsdShade +from ..USDtools import USDtools +from ..window import NDIWindow + +SOURCE1 = "MY-PC (Test Pattern)" +SOURCE2 = "MY-PC (Test Pattern 2)" +DYNAMIC_ID1 = "myDynamicMaterial1" +DYNAMIC_ID2 = "myDynamicMaterial2" +DUMMY_PATH = "/path/to/dummy" + + +def make_stage() -> Usd.Stage: + usd_context = omni.usd.get_context() + usd_context.new_stage() + # self._stage = Usd.Stage.CreateInMemory() + + stage = usd_context.get_stage() + prim = stage.DefinePrim("/World") + stage.SetDefaultPrim(prim) + + return stage + + +def get_stage() -> Usd.Stage: + usd_context = omni.usd.get_context() + stage = usd_context.get_stage() + return stage + + +def close_stage(): + usd_context = omni.usd.get_context() + assert usd_context.can_close_stage() + usd_context.close_stage() + + +def get_window(): + return ui_test.find(NDIWindow.WINDOW_NAME) + + +def create_dynamic_material() -> UsdShade.Material: + return USDtools.create_dynamic_material(DYNAMIC_ID1) + + +def create_dynamic_rectlight(): + stage = get_stage() + path: str = f"{stage.GetDefaultPrim().GetPath()}/myRectLight" + light = UsdLux.RectLight.Define(stage, path) + light.GetPrim().GetAttribute("texture:file").Set(f"{USDtools.PREFIX}{DYNAMIC_ID2}") + + +async def refresh_dynamic_list(window): + button = window.find("**/Button[*].text=='Discover Dynamic Textures'") + await button.click() From 662494fa52e6e4c2f9d413f90a3b50c13bcc8a65 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 14:44:22 -0400 Subject: [PATCH 09/17] Moved text that appear in ui to var so can be used in unit tests --- exts/mf.ov.ndi/mf/ov/ndi/USDtools.py | 3 ++- exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py | 3 ++- exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py | 9 +++++---- exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py | 12 ++++++++++-- exts/mf.ov.ndi/mf/ov/ndi/window.py | 15 ++++++++++----- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py index 8d55232..6a73612 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py @@ -19,12 +19,13 @@ class USDtools(): ATTR_NDI_NAME = 'ndi:source' ATTR_BANDWIDTH_NAME = "ndi:lowbandwidth" PREFIX = "dynamic://" + SCOPE_NAME = "NDI_Looks" def create_dynamic_material(name: str) -> UsdShade.Material: usd_context = omni.usd.get_context() stage: Usd.Stage = usd_context.get_stage() - scope_path: str = f"{stage.GetDefaultPrim().GetPath()}/NDI_Looks" + scope_path: str = f"{stage.GetDefaultPrim().GetPath()}/{USDtools.SCOPE_NAME}" UsdGeom.Scope.Define(stage, scope_path) safename = Tf.MakeValidIdentifier(unidecode(name)) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py b/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py index 6a8f369..2cb953c 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py @@ -18,6 +18,7 @@ def is_active(self): class ComboboxModel(ui.AbstractItemModel): NONE_VALUE = "NONE" PROXY_VALUE = "PROXY (1080p30) - RED" + RUNNING_LABEL_SUFFIX = " - running" items: List[ComboboxItem] = [] watchers = [] @@ -65,7 +66,7 @@ def _current_index_changed_fn(self): self.set_alt_value() def set_alt_value(self): - self._combobox_alt.text = self.currentvalue() + " - running" + self._combobox_alt.text = self.currentvalue() + ComboboxModel.RUNNING_LABEL_SUFFIX def currentvalue(self): self._current_item = ComboboxModel.items[self._current_index.get_value_as_int()] diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py index 0f8fbb5..dff7036 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py @@ -1,6 +1,7 @@ import omni.kit.test +from ..window import NDIWindow from .test_utils import (make_stage, close_stage, get_window, DYNAMIC_ID1, DYNAMIC_ID2, create_dynamic_material, - create_dynamic_rectlight, refresh_dynamic_list) + create_dynamic_rectlight, refresh_dynamic_list, get_dynamic_material_prim) class UITestsHeader(omni.kit.test.AsyncTestCase): @@ -16,10 +17,10 @@ async def test_create_material_button(self): field.widget.model.set_value(DYNAMIC_ID1) self.assertEqual(field.widget.model.get_value_as_string(), DYNAMIC_ID1) - button = self._window.find("**/Button[*].text=='Create Dynamic Texture'") + button = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") await button.click() - prim = self._stage.GetPrimAtPath(f"{self._stage.GetDefaultPrim().GetPath()}/NDI_Looks/{DYNAMIC_ID1}") + prim = get_dynamic_material_prim(DYNAMIC_ID1) self.assertTrue(prim.IsValid) async def test_texture_discovery(self): @@ -60,7 +61,7 @@ async def test_no_panel_on_start(self): self.assertIsNone(panel) label = self._window.find("**/Label[*]") - self.assertEqual(label.widget.text, "No dynamic texture found") + self.assertEqual(label.widget.text, NDIWindow.EMPTY_TEXTURE_LIST_TXT) """ diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py index e202a00..d8f6881 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_utils.py @@ -9,6 +9,8 @@ DYNAMIC_ID1 = "myDynamicMaterial1" DYNAMIC_ID2 = "myDynamicMaterial2" DUMMY_PATH = "/path/to/dummy" +RECTLIGHT_NAME = "MyRectLight" +DEFAULT_PRIM_NAME = "World" def make_stage() -> Usd.Stage: @@ -17,7 +19,7 @@ def make_stage() -> Usd.Stage: # self._stage = Usd.Stage.CreateInMemory() stage = usd_context.get_stage() - prim = stage.DefinePrim("/World") + prim = stage.DefinePrim(f"/{DEFAULT_PRIM_NAME}") stage.SetDefaultPrim(prim) return stage @@ -45,11 +47,17 @@ def create_dynamic_material() -> UsdShade.Material: def create_dynamic_rectlight(): stage = get_stage() - path: str = f"{stage.GetDefaultPrim().GetPath()}/myRectLight" + path: str = f"{stage.GetDefaultPrim().GetPath()}/{RECTLIGHT_NAME}" light = UsdLux.RectLight.Define(stage, path) light.GetPrim().GetAttribute("texture:file").Set(f"{USDtools.PREFIX}{DYNAMIC_ID2}") +def get_dynamic_material_prim(name: str): + usd_context = omni.usd.get_context() + stage = usd_context.get_stage() + return stage.GetPrimAtPath(f"{stage.GetDefaultPrim().GetPath()}/{USDtools.SCOPE_NAME}/{name}") + + async def refresh_dynamic_list(window): button = window.find("**/Button[*].text=='Discover Dynamic Textures'") await button.click() diff --git a/exts/mf.ov.ndi/mf/ov/ndi/window.py b/exts/mf.ov.ndi/mf/ov/ndi/window.py index 6b88d4e..8b72bbd 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/window.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/window.py @@ -7,6 +7,11 @@ class NDIWindow(ui.Window): WINDOW_NAME = "NDI®" + DEFAULT_TEXTURE_NAME = "myDynamicMaterial" + NEW_TEXTURE_BTN_TXT = "Create Dynamic Texture" + DISCOVER_TEX_BTN_TXT = "Discover Dynamic Textures" + STOP_STREAMS_BTN_TXT = "Stop all streams" + EMPTY_TEXTURE_LIST_TXT = "No dynamic texture found" def __init__(self, delegate=None, **kwargs): super().__init__(NDIWindow.WINDOW_NAME, **kwargs) @@ -35,13 +40,13 @@ def _ui_section_header(self): with ui.HStack(height=0): self._dynamic_name = ui.StringField() - self._dynamic_name.model.set_value("myDynamicMaterial") - ui.Button("Create Dynamic Texture", image_url="resources/glyphs/menu_plus.svg", image_width=24, + self._dynamic_name.model.set_value(NDIWindow.DEFAULT_TEXTURE_NAME) + ui.Button(NDIWindow.NEW_TEXTURE_BTN_TXT, image_url="resources/glyphs/menu_plus.svg", image_width=24, style=button_style, clicked_fn=self._on_click_create_dynamic_material) with ui.HStack(height=0): - ui.Button("Discover Dynamic Textures", image_url="resources/glyphs/menu_refresh.svg", image_width=24, + ui.Button(NDIWindow.DISCOVER_TEX_BTN_TXT, image_url="resources/glyphs/menu_refresh.svg", image_width=24, style=button_style, clicked_fn=self._on_click_refresh_materials) - ui.Button("Stop all streams", clicked_fn=self._kill_all_streams) + ui.Button(NDIWindow.STOP_STREAMS_BTN_TXT, clicked_fn=self._kill_all_streams) def _ui_section_bindings(self): ComboboxModel.ResetWatchers() @@ -50,7 +55,7 @@ def _ui_section_bindings(self): with ui.VStack(): bindings: NDIBinding = self._model.get_bindings() if len(bindings) == 0: - ui.Label("No dynamic texture found") + ui.Label(NDIWindow.EMPTY_TEXTURE_LIST_TXT) else: for binding in bindings: self._bindingPanels.append(NDIBindingPanel(binding, self._model, self, height=0)) From 0fa1b6482541180db8a9504d22b3ff8584a26d9b Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 15:21:21 -0400 Subject: [PATCH 10/17] Removed obsolete functions to comboboxmodel --- exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py b/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py index 2cb953c..d6b3df6 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py @@ -72,13 +72,6 @@ def currentvalue(self): self._current_item = ComboboxModel.items[self._current_index.get_value_as_int()] return self._current_item.value() - def currentItem(self): - self._current_item = ComboboxModel.items[self._current_index.get_value_as_int()] - return self._current_item - - def getCurrentItemIndex(self): - return self._current_index.get_value_as_int() - def get_item_children(self, item): return ComboboxModel.items @@ -87,8 +80,5 @@ def get_item_value_model(self, item, column_id): return self._current_index return item.model - def append_child_item(self, parentItem, text): - ComboboxModel.AddItem(text) - def select_none(self): self._current_index.set_value(0) From 1fec36797c1abf8172ef52ce7ac54fe038496a21 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Tue, 11 Apr 2023 15:31:15 -0400 Subject: [PATCH 11/17] Fix ndi status icon not correct on start --- exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py | 8 ++++++-- exts/mf.ov.ndi/mf/ov/ndi/window.py | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py b/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py index d6b3df6..bd12d38 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/comboboxModel.py @@ -69,8 +69,8 @@ def set_alt_value(self): self._combobox_alt.text = self.currentvalue() + ComboboxModel.RUNNING_LABEL_SUFFIX def currentvalue(self): - self._current_item = ComboboxModel.items[self._current_index.get_value_as_int()] - return self._current_item.value() + current_item = ComboboxModel.items[self._current_index.get_value_as_int()] + return current_item.value() def get_item_children(self, item): return ComboboxModel.items @@ -82,3 +82,7 @@ def get_item_value_model(self, item, column_id): def select_none(self): self._current_index.set_value(0) + + def is_active(self): + current_item = ComboboxModel.items[self._current_index.get_value_as_int()] + return current_item.is_active() diff --git a/exts/mf.ov.ndi/mf/ov/ndi/window.py b/exts/mf.ov.ndi/mf/ov/ndi/window.py index 8b72bbd..2f49c7f 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/window.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/window.py @@ -124,6 +124,7 @@ def __init__(self, binding: NDIBinding, model: NDIModel, window: NDIWindow, **kw clicked_fn=self._set_low_bandwidth_value) ui.Button("", image_url=NDIBindingPanel.COPY_ICON, width=30, height=30, clicked_fn=self._on_click_copy, tooltip="Copy dynamic texture path(dynamic://*)") + self._set_ndi_status_icon(self._combobox.is_active()) def _set_low_bandwidth_value(self): self._binding.set_lowbandwidth(not self._binding.get_lowbandwidth()) @@ -167,8 +168,12 @@ def _toggle_play_pause_btn(self): def on_ndi_status_change(self): status = self._binding.get_ndi_status() - if status: + self._set_ndi_status_icon(status) + if not status: + self._kill_stream() + + def _set_ndi_status_icon(self, active: bool): + if active: self._status_icon.source_url = NDIBindingPanel.NDI_ACTIVE else: self._status_icon.source_url = NDIBindingPanel.NDI_INACTIVE - self._kill_stream() From a9694faef01cff371bd3c1f74e1130714dfd2cfb Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Wed, 12 Apr 2023 11:21:37 -0400 Subject: [PATCH 12/17] Change default window height so emulated clicks in tests can work --- exts/mf.ov.ndi/mf/ov/ndi/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/extension.py b/exts/mf.ov.ndi/mf/ov/ndi/extension.py index 9d82ebc..e81efeb 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/extension.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/extension.py @@ -40,7 +40,7 @@ def _visibility_changed_fn(self, visible): def show_window(self, menu, value): if value: - self._window = NDIWindow(width=800, height=230) + self._window = NDIWindow(width=800, height=275) self._window.set_visibility_changed_fn(self._visibility_changed_fn) elif self._window: self._window.destroy() From ed63b3f263b67b42e19477b3e0557f48a87ed14f Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Wed, 12 Apr 2023 11:22:27 -0400 Subject: [PATCH 13/17] Moved more text to variables so they can be checked against in tests --- exts/mf.ov.ndi/mf/ov/ndi/window.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/window.py b/exts/mf.ov.ndi/mf/ov/ndi/window.py index 2f49c7f..eefaefb 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/window.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/window.py @@ -97,6 +97,10 @@ class NDIBindingPanel(ui.CollapsableFrame): COPY_ICON = "resources/glyphs/copy.svg" LOW_BANDWIDTH_ICON = "resources/glyphs/AOV_dark.svg" + PLAYPAUSE_BTN_NAME = "play_pause_btn" + BANDWIDTH_BTN_NAME = "low_bandwidth_btn" + COPYPATH_BTN_NAME = "copy_path_btn" + def __init__(self, binding: NDIBinding, model: NDIModel, window: NDIWindow, **kwargs): self._name = binding.get_id() super().__init__(self._name, **kwargs) @@ -118,12 +122,15 @@ def __init__(self, binding: NDIBinding, model: NDIModel, window: NDIWindow, **kw self._combobox_ui = ui.ComboBox(self._combobox) self.playPauseToolButton = ui.Button(text="", image_url=NDIBindingPanel.PLAY_ICON, height=30, - width=30, clicked_fn=self._on_click_play_pause_ndi) + width=30, clicked_fn=self._on_click_play_pause_ndi, + name=NDIBindingPanel.PLAYPAUSE_BTN_NAME) self._lowBandWidthButton = ui.ToolButton(image_url=NDIBindingPanel.LOW_BANDWIDTH_ICON, width=30, height=30, tooltip="Low bandwidth mode", - clicked_fn=self._set_low_bandwidth_value) + clicked_fn=self._set_low_bandwidth_value, + name=NDIBindingPanel.BANDWIDTH_BTN_NAME) ui.Button("", image_url=NDIBindingPanel.COPY_ICON, width=30, height=30, - clicked_fn=self._on_click_copy, tooltip="Copy dynamic texture path(dynamic://*)") + clicked_fn=self._on_click_copy, tooltip="Copy dynamic texture path(dynamic://*)", + name=NDIBindingPanel.COPYPATH_BTN_NAME) self._set_ndi_status_icon(self._combobox.is_active()) def _set_low_bandwidth_value(self): @@ -132,10 +139,6 @@ def _set_low_bandwidth_value(self): def _on_click_copy(self): pyperclip.copy(self._binding.get_id_full()) - def _on_click_reset(self): - self._combobox.select_none() - self.collapsed = True - def _start_ndi(self): lowbandwidth = self._lowBandWidthButton.model.get_value_as_bool() if self._model.add_stream(self._binding.get_id(), self._binding.get_source(), lowbandwidth): From 77bd5183b6bb8bcf0896f954f1eff4d526b83cdb Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Wed, 12 Apr 2023 11:22:48 -0400 Subject: [PATCH 14/17] Complete ui tests --- exts/mf.ov.ndi/mf/ov/ndi/NDItools.py | 2 + exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py | 127 +++++++++++++++++++++- 2 files changed, 123 insertions(+), 6 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/NDItools.py b/exts/mf.ov.ndi/mf/ov/ndi/NDItools.py index 075bdeb..8b44095 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/NDItools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/NDItools.py @@ -108,6 +108,7 @@ def __init__(self, name: str, stream_uri: str, lowbandwidth: bool, tools: NDItoo self.uri = stream_uri self.is_ok = False self._thread: threading.Thread + self._lowbandwidth = lowbandwidth if not tools.is_ndi_ok(): return @@ -212,6 +213,7 @@ def __init__(self, name: str, stream_uri: str, fps: float, lowbandwidth: bool): self.name = name self.uri = stream_uri self.is_ok = False + self._lowbandwidth = lowbandwidth denominator = 1 if lowbandwidth: diff --git a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py index dff7036..a10c550 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/tests/test_ui.py @@ -1,5 +1,6 @@ import omni.kit.test -from ..window import NDIWindow +from ..window import NDIWindow, NDIBindingPanel +from ..comboboxModel import ComboboxModel from .test_utils import (make_stage, close_stage, get_window, DYNAMIC_ID1, DYNAMIC_ID2, create_dynamic_material, create_dynamic_rectlight, refresh_dynamic_list, get_dynamic_material_prim) @@ -63,11 +64,125 @@ async def test_no_panel_on_start(self): label = self._window.find("**/Label[*]") self.assertEqual(label.widget.text, NDIWindow.EMPTY_TEXTURE_LIST_TXT) + async def test_no_create_texture_with_empty_name(self): + await refresh_dynamic_list(self._window) + + field = self._window.find("**/StringField[*]") + button_create = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") + + field.widget.model.set_value("") + await button_create.click() + + panels = self._window.find_all("**/NDIBindingPanel[*]") + self.assertEqual(len(panels), 0) + + async def test_combobox_defaults(self): + await refresh_dynamic_list(self._window) + + button = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") + await button.click() + combobox = self._window.find("**/ComboBox[*]") + + model = combobox.widget.model + self.assertEqual(model.currentvalue(), ComboboxModel.NONE_VALUE) + self.assertFalse(model.is_active()) + + model._current_index.set_value(1) + self.assertEqual(model.currentvalue(), ComboboxModel.PROXY_VALUE) + self.assertTrue(model.is_active()) -""" - async def test_combobox(self): - button = self._window.find("**/Button[*].text=='Create Dynamic Texture'") + model.select_none() + self.assertEqual(model.currentvalue(), ComboboxModel.NONE_VALUE) + self.assertFalse(model.is_active()) + + async def test_low_bandwidth_btn(self): + await refresh_dynamic_list(self._window) + + button = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") + await button.click() + + panel = self._window.find("**/NDIBindingPanel[*]") + self.assertFalse(panel.widget._binding.get_lowbandwidth()) + button = panel.find(f"**/ToolButton[*].name=='{NDIBindingPanel.BANDWIDTH_BTN_NAME}'") + await button.click() + self.assertTrue(panel.widget._binding.get_lowbandwidth()) + await button.click() + self.assertFalse(panel.widget._binding.get_lowbandwidth()) + + async def test_low_bandwidth_stream(self): + await refresh_dynamic_list(self._window) + + button = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") await button.click() - combobox = window.find("**/ComboBox[*]") -""" + combobox = self._window.find("**/ComboBox[*]") + combobox.widget.model._set_index_from_value(ComboboxModel.PROXY_VALUE) + + panel = self._window.find("**/NDIBindingPanel[*]") + button_bandwidth = panel.find(f"**/ToolButton[*].name=='{NDIBindingPanel.BANDWIDTH_BTN_NAME}'") + button_playpause = button = panel.find(f"**/Button[*].name=='{NDIBindingPanel.PLAYPAUSE_BTN_NAME}'") + + self.assertTrue(panel.widget._lowBandWidthButton.enabled) + await button_playpause.click() + self.assertFalse(self._window.widget._model._streams[0]._lowbandwidth) + self.assertFalse(panel.widget._lowBandWidthButton.enabled) + await button_playpause.click() + self.assertTrue(panel.widget._lowBandWidthButton.enabled) + + await button_bandwidth.click() + + await button_playpause.click() + self.assertTrue(self._window.widget._model._streams[0]._lowbandwidth) + await button_playpause.click() + + async def test_proxy_play_pause(self): + await refresh_dynamic_list(self._window) + + button_create = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") + await button_create.click() + + combobox = self._window.find("**/ComboBox[*]") + combobox.widget.model._set_index_from_value(ComboboxModel.PROXY_VALUE) + + panel = self._window.find("**/NDIBindingPanel[*]") + button_playpause = panel.find(f"**/Button[*].name=='{NDIBindingPanel.PLAYPAUSE_BTN_NAME}'") + self.assertTrue(panel.widget._combobox_ui.visible) + self.assertFalse(panel.widget._combobox_alt.visible) + await button_playpause.click() + + self.assertGreater(len(self._window.widget._model._streams), 0) + self.assertFalse(panel.widget._combobox_ui.visible) + self.assertTrue(panel.widget._combobox_alt.visible) + + await button_playpause.click() + self.assertEquals(len(self._window.widget._model._streams), 0) + + async def test_proxy_multiple(self): + await refresh_dynamic_list(self._window) + + field = self._window.find("**/StringField[*]") + button_create = self._window.find(f"**/Button[*].text=='{NDIWindow.NEW_TEXTURE_BTN_TXT}'") + + field.widget.model.set_value(DYNAMIC_ID1) + await button_create.click() + field.widget.model.set_value(DYNAMIC_ID2) + await button_create.click() + + comboboxes = self._window.find_all("**/ComboBox[*]") + for combobox in comboboxes: + combobox.widget.model._set_index_from_value(ComboboxModel.PROXY_VALUE) + + buttons_playpause = self._window.find_all(f"**/Button[*].name=='{NDIBindingPanel.PLAYPAUSE_BTN_NAME}'") + for button_playpause in buttons_playpause: + await button_playpause.click() + + self.assertEquals(len(self._window.widget._model._streams), 2) + + button_stopall = self._window.find(f"**/Button[*].text=='{NDIWindow.STOP_STREAMS_BTN_TXT}'") + await button_stopall.click() + self.assertEquals(len(self._window.widget._model._streams), 0) + + panels = self._window.find_all("**/NDIBindingPanel[*]") + for panel in panels: + self.assertTrue(panel.widget._combobox_ui.visible) + self.assertFalse(panel.widget._combobox_alt.visible) From cb4afaebda24cdd04d38449e0a260bd7fdb90e83 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Wed, 12 Apr 2023 14:40:48 -0400 Subject: [PATCH 15/17] Update to v0.10.0 --- exts/mf.ov.ndi/config/extension.toml | 2 +- exts/mf.ov.ndi/docs/CHANGELOG.md | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/exts/mf.ov.ndi/config/extension.toml b/exts/mf.ov.ndi/config/extension.toml index 0e63c87..a076f10 100644 --- a/exts/mf.ov.ndi/config/extension.toml +++ b/exts/mf.ov.ndi/config/extension.toml @@ -1,5 +1,5 @@ [package] -version = "0.9.0" +version = "0.10.0" title = "MF NDI® extension" description = "An extension to enable NDI® live video input in Omniverse." diff --git a/exts/mf.ov.ndi/docs/CHANGELOG.md b/exts/mf.ov.ndi/docs/CHANGELOG.md index c323b07..a208c8e 100644 --- a/exts/mf.ov.ndi/docs/CHANGELOG.md +++ b/exts/mf.ov.ndi/docs/CHANGELOG.md @@ -5,10 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] -- Unit and UI test integration - NDI® source monitoring (dimensions, fps, etc.) -## [0.9.0] - 2023.04-04 +## [0.10.0] - 2023-04-12 + +### Added +- Unit and UI tests + +## [0.9.0] - 2023-04-04 ### Changed - UI rework: less text, more icons From 96fea9cae1ca5e48e377b29ddc5ca4c937bd23dd Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Mon, 10 Apr 2023 17:00:36 -0400 Subject: [PATCH 16/17] Added NDIData tests --- exts/mf.ov.ndi/config/extension.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/exts/mf.ov.ndi/config/extension.toml b/exts/mf.ov.ndi/config/extension.toml index a076f10..bdf4804 100644 --- a/exts/mf.ov.ndi/config/extension.toml +++ b/exts/mf.ov.ndi/config/extension.toml @@ -27,3 +27,18 @@ requirements = [ "unidecode" ] use_online_index = true + +[[test]] +args = [ + "--/app/window/dpiScaleOverride=1.0", + "--/app/window/scaleToMonitor=false" +] +dependencies = [ + "omni.kit.ui_test", + "omni.usd" +] +timeout = 60 +stdoutFailPatterns.exclude = [ + "*Could not get stage*", + "*Model doesn't have a registered window*" +] From c4bb27ac3e164e22a28c791ebf13e3fc49f65dc3 Mon Sep 17 00:00:00 2001 From: Frederic Lestage Date: Wed, 12 Apr 2023 15:08:39 -0400 Subject: [PATCH 17/17] Fix merge conflicts --- exts/mf.ov.ndi/mf/ov/ndi/USDtools.py | 30 ++++++++++++++++++++------- exts/mf.ov.ndi/mf/ov/ndi/extension.py | 1 + exts/mf.ov.ndi/mf/ov/ndi/model.py | 10 ++++++++- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py b/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py index 6a73612..83ebe08 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/USDtools.py @@ -21,14 +21,24 @@ class USDtools(): PREFIX = "dynamic://" SCOPE_NAME = "NDI_Looks" - def create_dynamic_material(name: str) -> UsdShade.Material: + def get_stage() -> Usd.Stage: usd_context = omni.usd.get_context() - stage: Usd.Stage = usd_context.get_stage() + return usd_context.get_stage() + + def make_name_valid(name: str) -> str: + return Tf.MakeValidIdentifier(unidecode(name)) + + def create_dynamic_material(name: str) -> UsdShade.Material: + stage = USDtools.get_stage() + if not stage: + logger = logging.getLogger(__name__) + logger.error("Could not get stage") + return scope_path: str = f"{stage.GetDefaultPrim().GetPath()}/{USDtools.SCOPE_NAME}" UsdGeom.Scope.Define(stage, scope_path) - safename = Tf.MakeValidIdentifier(unidecode(name)) + safename = USDtools.make_name_valid(name) if name != safename: logger = logging.getLogger(__name__) logger.warn(f"Name \"{name}\" was not a valid USD identifier, changed it to \"{safename}\"") @@ -100,8 +110,11 @@ def find_all_dynamic_sources() -> List[DynamicPrim]: return result def set_prim_ndi_attribute(path: str, value: str): - usd_context = omni.usd.get_context() - stage: Usd.Stage = usd_context.get_stage() + stage = USDtools.get_stage() + if not stage: + logger = logging.getLogger(__name__) + logger.error("Could not get stage") + return prim: Usd.Prim = stage.GetPrimAtPath(path) if not prim.IsValid(): @@ -112,8 +125,11 @@ def set_prim_ndi_attribute(path: str, value: str): prim.CreateAttribute(USDtools.ATTR_NDI_NAME, Sdf.ValueTypeNames.String).Set(value) def set_prim_bandwidth_attribute(path: str, value: bool): - usd_context = omni.usd.get_context() - stage: Usd.Stage = usd_context.get_stage() + stage = USDtools.get_stage() + if not stage: + logger = logging.getLogger(__name__) + logger.error("Could not get stage") + return prim: Usd.Prim = stage.GetPrimAtPath(path) if not prim.IsValid(): diff --git a/exts/mf.ov.ndi/mf/ov/ndi/extension.py b/exts/mf.ov.ndi/mf/ov/ndi/extension.py index e81efeb..2909c67 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/extension.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/extension.py @@ -2,6 +2,7 @@ import omni.ext import omni.kit.app import asyncio +import omni.kit.ui class MFOVNdiExtension(omni.ext.IExt): diff --git a/exts/mf.ov.ndi/mf/ov/ndi/model.py b/exts/mf.ov.ndi/mf/ov/ndi/model.py index 16cf489..107d90a 100644 --- a/exts/mf.ov.ndi/mf/ov/ndi/model.py +++ b/exts/mf.ov.ndi/mf/ov/ndi/model.py @@ -95,6 +95,7 @@ def on_shutdown(self): # region streams def add_stream(self, name: str, uri: str, lowbandwidth: bool) -> bool: + # TODO: Stop the possibility of adding 2 streams with the same name if uri == ComboboxModel.NONE_VALUE: logger = logging.getLogger(__name__) logger.warning("Won't create stream without NDI® source") @@ -118,11 +119,18 @@ def _add_stream(self, video_stream, uri) -> bool: return True def kill_all_streams(self): - self._window.on_kill_all_streams() + self._kill_all_streams_window() for stream in self._streams: stream.destroy() self._streams = [] + def _kill_all_streams_window(self): + if self._window: + self._window.on_kill_all_streams() + else: + logger = logging.getLogger(__name__) + logger.error("Model doesn't have a registered window") + def remove_stream(self, name: str, uri: str): stream: NDIVideoStream = next((x for x in self._streams if x.name == name and x.uri == uri), None) if stream is not None: # could be none if already stopped