mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-16 00:05:26 +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
|
@ -45,6 +45,9 @@ class Attribute(BaseObject):
|
|||
"""
|
||||
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):
|
||||
"""
|
||||
Attribute constructor
|
||||
|
@ -418,6 +421,28 @@ class Attribute(BaseObject):
|
|||
# Emit if the enable status has changed
|
||||
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)
|
||||
fullName = Property(str, getFullName, constant=True)
|
||||
fullNameToNode = Property(str, getFullNameToNode, constant=True)
|
||||
|
@ -430,6 +455,8 @@ class Attribute(BaseObject):
|
|||
type = Property(str, getType, constant=True)
|
||||
baseType = Property(str, getType, constant=True)
|
||||
isReadOnly = Property(bool, _isReadOnly, constant=True)
|
||||
is3D = Property(bool, _is3D, constant=True)
|
||||
is2D = Property(bool, _is2D, constant=True)
|
||||
|
||||
# Description of the attribute
|
||||
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,
|
||||
False otherwise.
|
||||
"""
|
||||
# List of supported extensions, taken from Viewer3DSettings
|
||||
supportedExts = ['.obj', '.stl', '.fbx', '.gltf', '.abc', '.ply']
|
||||
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
|
||||
|
||||
return next((attr for attr in self._attributes if attr.enabled and attr.isOutput and attr.is3D), None) is not None
|
||||
|
||||
name = Property(str, getName, constant=True)
|
||||
defaultLabel = Property(str, getDefaultLabel, constant=True)
|
||||
|
|
|
@ -1105,20 +1105,30 @@ Page {
|
|||
for (var i = 0; i < node.attributes.count; i++) {
|
||||
var attr = node.attributes.at(i)
|
||||
if (attr.isOutput && attr.desc.semantic !== "image")
|
||||
if (!alreadyDisplay || attr.desc.semantic == "3D")
|
||||
if (!alreadyDisplay || attr.desc.semantic == "3D") {
|
||||
if (workspaceView.viewIn3D(attr, mouse))
|
||||
alreadyDisplay = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function viewIn2D(attribute, mouse) {
|
||||
workspaceView.viewer2D.tryLoadNode(attribute.node)
|
||||
workspaceView.viewer2D.setAttributeName(attribute.name)
|
||||
}
|
||||
|
||||
function viewIn3D(attribute, mouse) {
|
||||
if (!panel3dViewer || (!attribute.node.has3DOutput && !attribute.node.hasAttribute("useBoundingBox")))
|
||||
|
||||
if (!panel3dViewer || (!attribute.node.has3DOutput && !attribute.node.hasAttribute("useBoundingBox"))) {
|
||||
return false
|
||||
}
|
||||
var loaded = panel3dViewer.viewer3D.view(attribute)
|
||||
|
||||
// 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)
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
}
|
||||
|
@ -1343,8 +1353,21 @@ Page {
|
|||
var n = _reconstruction.upgradeNode(node)
|
||||
_reconstruction.selectedNode = n
|
||||
}
|
||||
|
||||
onAttributeDoubleClicked: function(mouse, attribute) {
|
||||
|
||||
if (attribute.is2D) {
|
||||
workspaceView.viewIn2D(attribute, mouse)
|
||||
}
|
||||
|
||||
else if (attribute.is3D) {
|
||||
workspaceView.viewIn3D(attribute, mouse)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -155,6 +155,7 @@ RowLayout {
|
|||
text: "Open File"
|
||||
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(attribute.evalValue))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onClicked: function(mouse) {
|
||||
|
@ -171,7 +172,7 @@ RowLayout {
|
|||
MaterialLabel {
|
||||
text: MaterialIcons.visibility
|
||||
font.pointSize: 7
|
||||
visible: attribute.isOutput && attribute === _reconstruction.displayedAttr2D
|
||||
visible: attribute.isOutput && (attribute === _reconstruction.displayedAttr2D || _reconstruction.displayedAttrs3D.count && _reconstruction.displayedAttrs3D.contains(attribute))
|
||||
}
|
||||
|
||||
MaterialLabel {
|
||||
|
@ -181,6 +182,7 @@ RowLayout {
|
|||
font.pointSize: 8
|
||||
padding: 4
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -343,6 +343,10 @@ FocusScope {
|
|||
return []
|
||||
}
|
||||
|
||||
function setAttributeName(attrName) {
|
||||
outputAttribute.setName(attrName)
|
||||
}
|
||||
|
||||
onDisplayedNodeChanged: {
|
||||
if (!displayedNode) {
|
||||
root.source = ""
|
||||
|
@ -1608,6 +1612,13 @@ FocusScope {
|
|||
root.source = getImageFile()
|
||||
root.sequence = getSequence()
|
||||
}
|
||||
|
||||
function setName(attrName) {
|
||||
const attrIndex = outputAttribute.names.indexOf(attrName)
|
||||
if (attrIndex > -1) {
|
||||
outputAttribute.currentIndex = attrIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialToolButton {
|
||||
|
|
|
@ -107,6 +107,7 @@ Entity {
|
|||
"label": label ? label : Filepath.basename(pathStr),
|
||||
"section": "External"
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
function view(attribute) {
|
||||
|
@ -115,15 +116,16 @@ Entity {
|
|||
return
|
||||
}
|
||||
|
||||
var attrLabel = attribute.isOutput ? "" : attribute.fullName.replace(attribute.node.name, "")
|
||||
var section = attribute.node.label
|
||||
|
||||
// Add file to the internal ListModel
|
||||
m.mediaModel.append(
|
||||
makeElement({
|
||||
"label": section + attrLabel,
|
||||
"label": `${section}.${attribute.label}`,
|
||||
"section": section,
|
||||
"attribute": attribute
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
function remove(index) {
|
||||
|
@ -384,11 +386,15 @@ Entity {
|
|||
onObjectAdded: function(index, object) {
|
||||
// Notify object that it is now fully instantiated
|
||||
object.fullyInstantiated = true
|
||||
_reconstruction.displayedAttrs3D.append(object.modelSource)
|
||||
}
|
||||
|
||||
onObjectRemoved: function(index, object) {
|
||||
if (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)
|
||||
self._displayedAttr2D = None
|
||||
self._displayedAttrs3D = meshroom.common.ListModel()
|
||||
|
||||
# - CameraInit
|
||||
self._cameraInit = None # current CameraInit node
|
||||
|
@ -515,7 +516,6 @@ class Reconstruction(UIGraph):
|
|||
def setActive(self, active):
|
||||
self._active = active
|
||||
|
||||
@Slot()
|
||||
def clear(self):
|
||||
self.clearActiveNodes()
|
||||
super().clear()
|
||||
|
@ -1071,6 +1071,9 @@ class Reconstruction(UIGraph):
|
|||
displayedAttr2DChanged = Signal()
|
||||
displayedAttr2D = makeProperty(QObject, "_displayedAttr2D", displayedAttr2DChanged)
|
||||
|
||||
displayedAttrs3DChanged = Signal()
|
||||
displayedAttrs3D = Property(QObject, lambda self: self._displayedAttrs3D, notify=displayedAttrs3DChanged)
|
||||
|
||||
@Slot(QObject)
|
||||
def setActiveNode(self, node, categories=True, inputs=True):
|
||||
""" Set node as the active node of its type and of its categories.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue