Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions meshroom/core/node.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python
import sys
import atexit
import copy
import datetime
Expand All @@ -11,7 +12,7 @@
import time
import types
import uuid
from collections import namedtuple
from collections import namedtuple, OrderedDict
from enum import Enum, auto
from typing import Callable, Optional

Expand Down Expand Up @@ -743,8 +744,38 @@ def nameToLabel(self, name):
def getDocumentation(self):
if not self.nodeDesc:
return ""
return self.nodeDesc.documentation

if self.nodeDesc.documentation:
return self.nodeDesc.documentation
else:
return self.nodeDesc.__doc__

def getNodeInfos(self):
if not self.nodeDesc:
return []
infos = OrderedDict([
("module", self.nodeDesc.__module__),
("modulePath", self.nodeDesc.plugin.path),
])
# > Infos from the plugin module
plugin_module = sys.modules.get(self.nodeDesc.__module__)
if getattr(plugin_module, "__author__", None):
infos["author"] = plugin_module.__author__
if getattr(plugin_module, "__license__", None):
infos["license"] = plugin_module.__license__
if getattr(plugin_module, "__version__", None):
infos["version"] = plugin_module.__version__
# > Overrides at the node-level
if getattr(self.nodeDesc, "author", None):
infos["author"] = self.nodeDesc.author
if getattr(self.nodeDesc, "version", None):
infos["version"] = self.nodeDesc.version
# > Additional node infos stored in a __nodeInfo__ parameter
additionalNodeInfos = getattr(self.nodeDesc, "__nodeInfo__", None)
if additionalNodeInfos:
for key, value in additionalNodeInfos:
infos[key] = value
return [{"key": k, "value": v} for k, v in infos.items()]

@property
def packageFullName(self):
return '-'.join([self.packageName, self.packageVersion])
Expand Down Expand Up @@ -1626,6 +1657,7 @@ def has3DOutputAttribute(self):
defaultLabel = Property(str, getDefaultLabel, constant=True)
nodeType = Property(str, nodeType.fget, constant=True)
documentation = Property(str, getDocumentation, constant=True)
nodeInfos = Property(Variant, getNodeInfos, constant=True)
positionChanged = Signal()
position = Property(Variant, position.fget, position.fset, notify=positionChanged)
x = Property(float, lambda self: self._position.x, notify=positionChanged)
Expand Down
106 changes: 95 additions & 11 deletions meshroom/ui/qml/GraphEditor/NodeDocumentation.qml
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,101 @@ FocusScope {
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
clip: true

TextEdit {
width: parent.parent.width
height: parent.height

padding: 8
textFormat: TextEdit.MarkdownText
selectByMouse: true
selectionColor: activePalette.highlight
color: activePalette.text
text: node ? node.documentation : ""
wrapMode: TextEdit.Wrap
ColumnLayout {
id: nodeDocColumnLayout
property real keyColumnWidth: 10.0 * Qt.application.font.pixelSize

Component {
id: nodeInfoItem
Rectangle {
color: activePalette.window
width: parent.width
height: childrenRect.height
RowLayout {
width: parent.width
Rectangle {
id: nodeInfoKey
anchors.margins: 2
color: Qt.darker(activePalette.window, 1.1)
Layout.preferredWidth: nodeDocColumnLayout.keyColumnWidth
Layout.minimumWidth: 0.2 * parent.width
Layout.maximumWidth: 0.8 * parent.width
Layout.fillWidth: false
Layout.fillHeight: true
Label {
text: modelData.key
font.capitalization: Font.Capitalize
anchors.fill: parent
anchors.top: parent.top
topPadding: 4
leftPadding: 6
verticalAlignment: TextEdit.AlignTop
elide: Text.ElideRight
}
}
// Drag handle for resizing
Rectangle {
width: 2
Layout.fillHeight: true
color: "transparent"
MouseArea {
anchors.fill: parent
anchors.margins: -2
cursorShape: Qt.SizeHorCursor
drag {
target: parent
axis: Drag.XAxis
threshold: 0
// Not required
minimumX: 0.2 * nodeDocColumnLayout.width
maximumX: 0.8 * nodeDocColumnLayout.width
}
onPositionChanged: (mouse)=> {
nodeDocColumnLayout.keyColumnWidth = parent.x
}
}

}
TextArea {
id: nodeInfoValue
text: modelData.value
anchors.margins: 2
Layout.fillWidth: true
Layout.fillHeight: true
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
textFormat: TextEdit.PlainText
readOnly: true
selectByMouse: true
background: Rectangle { anchors.fill: parent; color: Qt.darker(activePalette.window, 1.05) }
}
}
}
}

ListView {
id: nodeInfoListView
width: parent.width
height: childrenRect.height
Layout.preferredWidth: width
spacing: 3
model: node.nodeInfos
delegate: nodeInfoItem
}

TextEdit {
id: documentationText
padding: 8
topPadding: 20
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.preferredWidth: width
width: parent.parent.parent.width
textFormat: TextEdit.MarkdownText
selectByMouse: true
selectionColor: activePalette.highlight
color: activePalette.text
text: node ? node.documentation : ""
wrapMode: TextEdit.Wrap
}
}
}
}
28 changes: 28 additions & 0 deletions tests/plugins/meshroom/pluginC/PluginCNodeA.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
__version__ = "1.0"
__license__ = "no-license"

from meshroom.core import desc


class PluginCNodeA(desc.Node):
"""PluginCNodeA"""

author = "testAuthor"

inputs = [
desc.File(
name="input",
label="Input",
description="",
value="",
),
]

outputs = [
desc.File(
name="output",
label="Output",
description="",
value="",
),
]
Empty file.
54 changes: 54 additions & 0 deletions tests/test_nodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# coding:utf-8

import os
from pathlib import Path

from meshroom.core import pluginManager, loadClassesNodes
from meshroom.core.plugins import Plugin
from meshroom.core import pluginManager
from meshroom.core import pluginManager
from meshroom.core.graph import Graph

from .utils import registerNodeDesc


class TestNodeInfos:
plugin = None

@classmethod
def setup_class(cls):
cls.folder = os.path.join(os.path.dirname(__file__), "plugins", "meshroom")
package = "pluginC"
cls.plugin = Plugin(package, cls.folder)
nodes = loadClassesNodes(cls.folder, package)
for node in nodes:
cls.plugin.addNodePlugin(node)
pluginManager.addPlugin(cls.plugin)

@classmethod
def teardown_class(cls):
for node in cls.plugin.nodes.values():
pluginManager.unregisterNode(node)
cls.plugin = None

def test_loadedPlugin(self):
assert len(pluginManager.getPlugins()) >= 1
plugin = pluginManager.getPlugin("pluginC")
assert plugin == self.plugin
node = plugin.nodes["PluginCNodeA"]
nodeType = node.nodeDescriptor

g = Graph("")
registerNodeDesc(nodeType)
node = g.addNewNode(nodeType.__name__)

nodeDocumentation = node.getDocumentation()
assert nodeDocumentation == "PluginCNodeA"
nodeInfos = {item["key"]: item["value"] for item in node.getNodeInfos()}
assert nodeInfos["module"] == "pluginC.PluginCNodeA"
plugin_path = os.path.join(self.folder, "pluginC", "PluginCNodeA.py")
assert nodeInfos["modulePath"] == Path(plugin_path).as_posix() # modulePath seems to follow linux convention
assert nodeInfos["author"] == "testAuthor"
assert nodeInfos["license"] == "no-license"
assert nodeInfos["version"] == "1.0"
Loading