mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-14 15:27:21 +02:00
[ui] Attribute: Attribute actually displayed in the 3dViewer are flagged with a visibility icon (eye)
This commit is contained in:
parent
4a67736d9e
commit
cfd2b1e00a
7 changed files with 85 additions and 23 deletions
|
@ -44,6 +44,9 @@ class Attribute(BaseObject):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
stringIsLinkRe = re.compile(r'^\{[A-Za-z]+[A-Za-z0-9_.\[\]]*\}$')
|
stringIsLinkRe = re.compile(r'^\{[A-Za-z]+[A-Za-z0-9_.\[\]]*\}$')
|
||||||
|
|
||||||
|
VALID_IMAGE_SEMANTICS = ["image", "imageList", "sequence"]
|
||||||
|
VALID_3D_EXTENSIONS = ['.obj', '.stl', '.fbx', '.gltf', '.abc', '.ply']
|
||||||
|
|
||||||
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
|
||||||
"""
|
"""
|
||||||
|
@ -418,6 +421,28 @@ class Attribute(BaseObject):
|
||||||
# Emit if the enable status has changed
|
# Emit if the enable status has changed
|
||||||
self.setEnabled(self.getEnabled())
|
self.setEnabled(self.getEnabled())
|
||||||
|
|
||||||
|
def _is3D(self) -> bool:
|
||||||
|
""" Return True if the current attribute is considered as a 3d file """
|
||||||
|
|
||||||
|
# List of supported extensions, taken from Viewer3DSettings
|
||||||
|
if self.desc.semantic == "3d":
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If the attribute is a File attribute, it is an instance of str and can be iterated over
|
||||||
|
hasSupportedExt = isinstance(self.value, str) and any(ext in self.value for ext in Attribute.VALID_3D_EXTENSIONS)
|
||||||
|
if hasSupportedExt:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is2D(self) -> bool:
|
||||||
|
""" Return True if the current attribute is considered as a 2d file """
|
||||||
|
|
||||||
|
if not self.desc.semantic:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return next((imageSemantic for imageSemantic in Attribute.VALID_IMAGE_SEMANTICS if self.desc.semantic == imageSemantic), None) is not None
|
||||||
|
|
||||||
name = Property(str, getName, constant=True)
|
name = Property(str, getName, constant=True)
|
||||||
fullName = Property(str, getFullName, constant=True)
|
fullName = Property(str, getFullName, constant=True)
|
||||||
fullNameToNode = Property(str, getFullNameToNode, constant=True)
|
fullNameToNode = Property(str, getFullNameToNode, constant=True)
|
||||||
|
@ -430,6 +455,8 @@ class Attribute(BaseObject):
|
||||||
type = Property(str, getType, constant=True)
|
type = Property(str, getType, constant=True)
|
||||||
baseType = Property(str, getType, constant=True)
|
baseType = Property(str, getType, constant=True)
|
||||||
isReadOnly = Property(bool, _isReadOnly, constant=True)
|
isReadOnly = Property(bool, _isReadOnly, constant=True)
|
||||||
|
is3D = Property(bool, _is3D, constant=True)
|
||||||
|
is2D = Property(bool, _is2D, constant=True)
|
||||||
|
|
||||||
# Description of the attribute
|
# Description of the attribute
|
||||||
descriptionChanged = Signal()
|
descriptionChanged = Signal()
|
||||||
|
|
|
@ -1617,18 +1617,8 @@ class BaseNode(BaseObject):
|
||||||
Return True if at least one attribute is a File that can be loaded in the 3D Viewer,
|
Return True if at least one attribute is a File that can be loaded in the 3D Viewer,
|
||||||
False otherwise.
|
False otherwise.
|
||||||
"""
|
"""
|
||||||
# List of supported extensions, taken from Viewer3DSettings
|
|
||||||
supportedExts = ['.obj', '.stl', '.fbx', '.gltf', '.abc', '.ply']
|
return next((attr for attr in self._attributes if attr.enabled and attr.isOutput and attr.is3D), None) is not None
|
||||||
for attr in self._attributes:
|
|
||||||
if not attr.enabled or not attr.isOutput:
|
|
||||||
continue
|
|
||||||
if attr.desc.semantic == "3d":
|
|
||||||
return True
|
|
||||||
# If the attribute is a File attribute, it is an instance of str and can be iterated over
|
|
||||||
hasSupportedExt = isinstance(attr.value, str) and any(ext in attr.value for ext in supportedExts)
|
|
||||||
if hasSupportedExt:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
name = Property(str, getName, constant=True)
|
name = Property(str, getName, constant=True)
|
||||||
defaultLabel = Property(str, getDefaultLabel, constant=True)
|
defaultLabel = Property(str, getDefaultLabel, constant=True)
|
||||||
|
|
|
@ -1105,20 +1105,30 @@ Page {
|
||||||
for (var i = 0; i < node.attributes.count; i++) {
|
for (var i = 0; i < node.attributes.count; i++) {
|
||||||
var attr = node.attributes.at(i)
|
var attr = node.attributes.at(i)
|
||||||
if (attr.isOutput && attr.desc.semantic !== "image")
|
if (attr.isOutput && attr.desc.semantic !== "image")
|
||||||
if (!alreadyDisplay || attr.desc.semantic == "3D")
|
if (!alreadyDisplay || attr.desc.semantic == "3D") {
|
||||||
if (workspaceView.viewIn3D(attr, mouse))
|
if (workspaceView.viewIn3D(attr, mouse))
|
||||||
alreadyDisplay = true
|
alreadyDisplay = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function viewIn2D(attribute, mouse) {
|
||||||
|
workspaceView.viewer2D.tryLoadNode(attribute.node)
|
||||||
|
workspaceView.viewer2D.setAttributeName(attribute.name)
|
||||||
|
}
|
||||||
|
|
||||||
function viewIn3D(attribute, mouse) {
|
function viewIn3D(attribute, mouse) {
|
||||||
if (!panel3dViewer || (!attribute.node.has3DOutput && !attribute.node.hasAttribute("useBoundingBox")))
|
|
||||||
|
if (!panel3dViewer || (!attribute.node.has3DOutput && !attribute.node.hasAttribute("useBoundingBox"))) {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
var loaded = panel3dViewer.viewer3D.view(attribute)
|
var loaded = panel3dViewer.viewer3D.view(attribute)
|
||||||
|
|
||||||
// solo media if Control modifier was held
|
// solo media if Control modifier was held
|
||||||
if (loaded && mouse && mouse.modifiers & Qt.ControlModifier)
|
if (loaded && mouse && mouse.modifiers & Qt.ControlModifier) {
|
||||||
panel3dViewer.viewer3D.solo(attribute)
|
panel3dViewer.viewer3D.solo(attribute)
|
||||||
|
}
|
||||||
return loaded
|
return loaded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1343,8 +1353,21 @@ Page {
|
||||||
var n = _reconstruction.upgradeNode(node)
|
var n = _reconstruction.upgradeNode(node)
|
||||||
_reconstruction.selectedNode = n
|
_reconstruction.selectedNode = n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAttributeDoubleClicked: function(mouse, attribute) {
|
||||||
|
|
||||||
|
if (attribute.is2D) {
|
||||||
|
workspaceView.viewIn2D(attribute, mouse)
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (attribute.is3D) {
|
||||||
|
workspaceView.viewIn3D(attribute, mouse)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -50,12 +50,12 @@ RowLayout {
|
||||||
padding: 0
|
padding: 0
|
||||||
Layout.preferredWidth: labelWidth || implicitWidth
|
Layout.preferredWidth: labelWidth || implicitWidth
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: parameterLabel
|
id: parameterLabel
|
||||||
|
|
||||||
|
@ -155,6 +155,7 @@ RowLayout {
|
||||||
text: "Open File"
|
text: "Open File"
|
||||||
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(attribute.evalValue))
|
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(attribute.evalValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: function(mouse) {
|
onClicked: function(mouse) {
|
||||||
|
@ -171,7 +172,7 @@ RowLayout {
|
||||||
MaterialLabel {
|
MaterialLabel {
|
||||||
text: MaterialIcons.visibility
|
text: MaterialIcons.visibility
|
||||||
font.pointSize: 7
|
font.pointSize: 7
|
||||||
visible: attribute.isOutput && attribute === _reconstruction.displayedAttr2D
|
visible: attribute.isOutput && (attribute === _reconstruction.displayedAttr2D || _reconstruction.displayedAttrs3D.count && _reconstruction.displayedAttrs3D.contains(attribute))
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialLabel {
|
MaterialLabel {
|
||||||
|
@ -180,7 +181,8 @@ RowLayout {
|
||||||
color: palette.mid
|
color: palette.mid
|
||||||
font.pointSize: 8
|
font.pointSize: 8
|
||||||
padding: 4
|
padding: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -343,6 +343,10 @@ FocusScope {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAttributeName(attrName) {
|
||||||
|
outputAttribute.setName(attrName)
|
||||||
|
}
|
||||||
|
|
||||||
onDisplayedNodeChanged: {
|
onDisplayedNodeChanged: {
|
||||||
if (!displayedNode) {
|
if (!displayedNode) {
|
||||||
root.source = ""
|
root.source = ""
|
||||||
|
@ -1608,6 +1612,13 @@ FocusScope {
|
||||||
root.source = getImageFile()
|
root.source = getImageFile()
|
||||||
root.sequence = getSequence()
|
root.sequence = getSequence()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setName(attrName) {
|
||||||
|
const attrIndex = outputAttribute.names.indexOf(attrName)
|
||||||
|
if (attrIndex > -1) {
|
||||||
|
outputAttribute.currentIndex = attrIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
|
|
|
@ -107,6 +107,7 @@ Entity {
|
||||||
"label": label ? label : Filepath.basename(pathStr),
|
"label": label ? label : Filepath.basename(pathStr),
|
||||||
"section": "External"
|
"section": "External"
|
||||||
}))
|
}))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function view(attribute) {
|
function view(attribute) {
|
||||||
|
@ -115,15 +116,16 @@ Entity {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var attrLabel = attribute.isOutput ? "" : attribute.fullName.replace(attribute.node.name, "")
|
|
||||||
var section = attribute.node.label
|
var section = attribute.node.label
|
||||||
|
|
||||||
// Add file to the internal ListModel
|
// Add file to the internal ListModel
|
||||||
m.mediaModel.append(
|
m.mediaModel.append(
|
||||||
makeElement({
|
makeElement({
|
||||||
"label": section + attrLabel,
|
"label": `${section}.${attribute.label}`,
|
||||||
"section": section,
|
"section": section,
|
||||||
"attribute": attribute
|
"attribute": attribute
|
||||||
}))
|
}))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove(index) {
|
function remove(index) {
|
||||||
|
@ -384,11 +386,15 @@ Entity {
|
||||||
onObjectAdded: function(index, object) {
|
onObjectAdded: function(index, object) {
|
||||||
// Notify object that it is now fully instantiated
|
// Notify object that it is now fully instantiated
|
||||||
object.fullyInstantiated = true
|
object.fullyInstantiated = true
|
||||||
|
_reconstruction.displayedAttrs3D.append(object.modelSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
onObjectRemoved: function(index, object) {
|
onObjectRemoved: function(index, object) {
|
||||||
if (m.sourceToEntity[object.modelSource])
|
if (m.sourceToEntity[object.modelSource])
|
||||||
|
|
||||||
delete m.sourceToEntity[object.modelSource]
|
delete m.sourceToEntity[object.modelSource]
|
||||||
|
_reconstruction.displayedAttrs3D.remove(object.modelSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,6 +476,7 @@ class Reconstruction(UIGraph):
|
||||||
|
|
||||||
# initialize activeAttributes (attributes currently visible in some viewers)
|
# initialize activeAttributes (attributes currently visible in some viewers)
|
||||||
self._displayedAttr2D = None
|
self._displayedAttr2D = None
|
||||||
|
self._displayedAttrs3D = meshroom.common.ListModel()
|
||||||
|
|
||||||
# - CameraInit
|
# - CameraInit
|
||||||
self._cameraInit = None # current CameraInit node
|
self._cameraInit = None # current CameraInit node
|
||||||
|
@ -515,7 +516,6 @@ class Reconstruction(UIGraph):
|
||||||
def setActive(self, active):
|
def setActive(self, active):
|
||||||
self._active = active
|
self._active = active
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.clearActiveNodes()
|
self.clearActiveNodes()
|
||||||
super().clear()
|
super().clear()
|
||||||
|
@ -1069,7 +1069,10 @@ class Reconstruction(UIGraph):
|
||||||
liveSfmManager = Property(QObject, lambda self: self._liveSfmManager, constant=True)
|
liveSfmManager = Property(QObject, lambda self: self._liveSfmManager, constant=True)
|
||||||
|
|
||||||
displayedAttr2DChanged = Signal()
|
displayedAttr2DChanged = Signal()
|
||||||
displayedAttr2D = makeProperty(QObject, "_displayedAttr2D", displayedAttr2DChanged)
|
displayedAttr2D = makeProperty(QObject, "_displayedAttr2D", displayedAttr2DChanged)
|
||||||
|
|
||||||
|
displayedAttrs3DChanged = Signal()
|
||||||
|
displayedAttrs3D = Property(QObject, lambda self: self._displayedAttrs3D, notify=displayedAttrs3DChanged)
|
||||||
|
|
||||||
@Slot(QObject)
|
@Slot(QObject)
|
||||||
def setActiveNode(self, node, categories=True, inputs=True):
|
def setActiveNode(self, node, categories=True, inputs=True):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue