Skip to content

Commit f8c2871

Browse files
author
Fabien Servant
committed
WIP interface categories
1 parent 1feee35 commit f8c2871

4 files changed

Lines changed: 75 additions & 16 deletions

File tree

meshroom/core/desc/interface.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
class Interface:
2+
"""Marker base class for node descriptor interfaces.
3+
4+
Inherit from this to declare a new interface. Classes that inherit from
5+
an Interface are automatically discovered by InterfaceMeta and listed in
6+
the ``interfaces`` attribute of the concrete node descriptor class.
7+
"""
8+
pass
9+
10+
class InterfaceMeta(type):
11+
"""Metaclass that populates the ``interfaces`` attribute on any class that uses it.
12+
13+
When a class is created with this metaclass, ``interfaces`` is set to the list
14+
of ``Interface`` subclass names found in the class's MRO (excluding the class
15+
itself and the base ``Interface`` marker).
16+
"""
17+
def __new__(mcs, name, bases, namespace):
18+
cls = super().__new__(mcs, name, bases, namespace)
19+
cls.interfaces = [
20+
b.__name__ for b in type.mro(cls)
21+
if b is not Interface and b is not cls
22+
and isinstance(b, type) and issubclass(b, Interface)
23+
]
24+
return cls
25+
26+
class InterfacedClass(metaclass=InterfaceMeta):
27+
"""Base class for objects that want automatic interface discovery via InterfaceMeta."""
28+
pass
29+
30+
31+
class FeatureProviderInterface(Interface):
32+
33+
def getFeaturesFolders(self, node) -> list:
34+
raise NotImplementedError
35+

meshroom/core/desc/node.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from .computation import Level, StaticNodeSize
2020
from .attribute import Attribute, ChoiceParam, ColorParam, Flow, IntParam, StringParam, ListAttribute
21+
from .interface import InterfacedClass
2122

2223
_MESHROOM_COMPUTE = (Path(_MESHROOM_ROOT) / "bin" / "meshroom_compute").as_posix()
2324
_MESHROOM_COMPUTE_DEPS = ["psutil"]
@@ -212,7 +213,7 @@ def getInternalFlowOutputs(cls, mrNodeType: MrNodeType) -> list[Attribute]:
212213
return cls.FLOW_OUT
213214

214215

215-
class BaseNode(object):
216+
class BaseNode(InterfacedClass):
216217
"""
217218
"""
218219
cpu = Level.NORMAL

meshroom/ui/qml/Viewer/Viewer2D.qml

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ FocusScope {
844844
ExifOrientedViewer {
845845
id: featuresViewerLoader
846846
active: displayFeatures.checked && !useExternal
847-
property var activeNode: _currentScene ? _currentScene.activeNodes.get("featureProvider").node : null
847+
property var activeNode: _currentScene ? _currentScene.activeNodes.get("FeatureProviderInterface").node : null
848848
width: imgContainer.width
849849
height: imgContainer.height
850850
anchors.centerIn: parent
@@ -1171,7 +1171,7 @@ FocusScope {
11711171
if (!root.aliceVisionPluginAvailable) {
11721172
return null
11731173
}
1174-
return _currentScene ? _currentScene.activeNodes.get("featureProvider").node : null
1174+
return _currentScene ? _currentScene.activeNodes.get("FeatureProviderInterface").node : null
11751175
}
11761176
property bool isComputed: activeNode && activeNode.isComputed
11771177
active: isUsed && isComputed
@@ -1187,18 +1187,8 @@ FocusScope {
11871187
"featureFolders": Qt.binding(function() {
11881188
let result = []
11891189
if (activeNode) {
1190-
if (activeNode.nodeType == "FeatureExtraction" && isComputed) {
1191-
result.push(activeNode.attribute("output").value)
1192-
}
1193-
else if (activeNode.nodeType == "RomaReducer" && isComputed) {
1194-
result.push(activeNode.attribute("featuresFolder").value)
1195-
}
1196-
else if (activeNode.hasAttribute("featuresFolders")) {
1197-
for (let i = 0; i < activeNode.attribute("featuresFolders").value.count; i++) {
1198-
let attr = activeNode.attribute("featuresFolders").value.at(i)
1199-
result.push(attr.value)
1200-
}
1201-
}
1190+
result = _currentScene.callInterfaceMethod("FeatureProviderInterface", "getFeaturesFolders")
1191+
console.log(result)
12021192
}
12031193
return result
12041194
}),

meshroom/ui/scene.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,15 @@ def initActiveNodes(self):
418418
# For all nodes declared to be accessed by the UI
419419
usedNodeTypes = {j for i in self.activeNodeCategories.values() for j in i}
420420
allLoadedNodeTypes = set(meshroom.core.pluginManager.getRegisteredNodePlugins().keys())
421+
422+
for nodeType, nodePlugin in meshroom.core.pluginManager.getRegisteredNodePlugins().items():
423+
nodeDesc = nodePlugin.nodeDescriptor
424+
for interface in nodeDesc.interfaces:
425+
self._activeNodes.add(ActiveNode(interface, parent=self))
426+
421427
allUiNodes = set(self.uiNodes) | usedNodeTypes | allLoadedNodeTypes
422428

423-
for nodeType in allUiNodes:
429+
for nodeType in allUiNodes:
424430
self._activeNodes.add(ActiveNode(nodeType, parent=self))
425431

426432
def clearActiveNodes(self):
@@ -985,11 +991,38 @@ def setBuildingIntrinsics(self, value):
985991

986992
pluginsReloaded = Signal(list, list)
987993

994+
@Slot(str, str, result="QVariant")
995+
def callInterfaceMethod(self, interfaceName, methodName):
996+
"""Call a method defined by an interface on the active node for that interface.
997+
998+
Args:
999+
interfaceName: name of the interface (e.g. "FeatureProviderInterface")
1000+
methodName: name of the method to call on the nodeDesc (e.g. "getFeaturesFolders")
1001+
1002+
Returns:
1003+
The result of the method call, or None if the interface/method is not available.
1004+
"""
1005+
entry = self.activeNodes.get(interfaceName)
1006+
if not entry or not entry.node:
1007+
return None
1008+
node = entry.node
1009+
method = getattr(node.nodeDesc, methodName, None)
1010+
if callable(method):
1011+
try:
1012+
return method(node)
1013+
except Exception as e:
1014+
logging.error(f"callInterfaceMethod: {interfaceName}.{methodName} raised: {e}")
1015+
return None
1016+
return None
1017+
9881018
@Slot(QObject)
9891019
def setActiveNode(self, node, categories=True, inputs=True):
9901020
""" Set node as the active node of its type and of its categories.
9911021
Also upgrade related input nodes.
9921022
"""
1023+
for interface in node.nodeDesc.interfaces:
1024+
self.activeNodes.getr(interface).node = node
1025+
9931026
if categories:
9941027
for category, nodeTypes in self.activeNodeCategories.items():
9951028
if node.nodeType in nodeTypes:

0 commit comments

Comments
 (0)