mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-09 21:07:20 +02:00
Merge branch 'dev/ImageGalleryFilter' of https://github.com/N0Ls/meshroom into N0Ls-dev/ImageGalleryFilter
This commit is contained in:
commit
af4dae9a77
7 changed files with 549 additions and 48 deletions
|
@ -71,25 +71,30 @@ class Attribute(BaseObject):
|
||||||
def root(self):
|
def root(self):
|
||||||
return self._root() if self._root else None
|
return self._root() if self._root else None
|
||||||
|
|
||||||
def absoluteName(self):
|
|
||||||
return '{}.{}.{}'.format(self.node.graph.name, self.node.name, self._name)
|
|
||||||
|
|
||||||
def getFullName(self):
|
|
||||||
""" Name inside the Graph: nodeName.name """
|
|
||||||
if isinstance(self.root, ListAttribute):
|
|
||||||
return '{}[{}]'.format(self.root.getFullName(), self.root.index(self))
|
|
||||||
elif isinstance(self.root, GroupAttribute):
|
|
||||||
return '{}.{}'.format(self.root.getFullName(), self._name)
|
|
||||||
return '{}.{}'.format(self.node.name, self._name)
|
|
||||||
|
|
||||||
def asLinkExpr(self):
|
|
||||||
""" Return link expression for this Attribute """
|
|
||||||
return "{" + self.getFullName() + "}"
|
|
||||||
|
|
||||||
def getName(self):
|
def getName(self):
|
||||||
""" Attribute name """
|
""" Attribute name """
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
|
def getFullName(self):
|
||||||
|
""" Name inside the Graph: groupName.name """
|
||||||
|
if isinstance(self.root, ListAttribute):
|
||||||
|
return '{}[{}]'.format(self.root.getFullName(), self.root.index(self))
|
||||||
|
elif isinstance(self.root, GroupAttribute):
|
||||||
|
return '{}.{}'.format(self.root.getFullName(), self.getName())
|
||||||
|
return self.getName()
|
||||||
|
|
||||||
|
def getFullNameToNode(self):
|
||||||
|
""" Name inside the Graph: nodeName.groupName.name """
|
||||||
|
return '{}.{}'.format(self.node.name, self.getFullName())
|
||||||
|
|
||||||
|
def getFullNameToGraph(self):
|
||||||
|
""" Name inside the Graph: graphName.nodeName.groupName.name """
|
||||||
|
return '{}.{}'.format(self.node.graph.name, self.getFullNameToNode())
|
||||||
|
|
||||||
|
def asLinkExpr(self):
|
||||||
|
""" Return link expression for this Attribute """
|
||||||
|
return "{" + self.getFullNameToNode() + "}"
|
||||||
|
|
||||||
def getType(self):
|
def getType(self):
|
||||||
return self.attributeDesc.__class__.__name__
|
return self.attributeDesc.__class__.__name__
|
||||||
|
|
||||||
|
@ -102,6 +107,22 @@ class Attribute(BaseObject):
|
||||||
def getLabel(self):
|
def getLabel(self):
|
||||||
return self._label
|
return self._label
|
||||||
|
|
||||||
|
def getFullLabel(self):
|
||||||
|
""" Full Label includes the name of all parent groups, e.g. 'groupLabel subGroupLabel Label' """
|
||||||
|
if isinstance(self.root, ListAttribute):
|
||||||
|
return self.root.getFullLabel()
|
||||||
|
elif isinstance(self.root, GroupAttribute):
|
||||||
|
return '{} {}'.format(self.root.getFullLabel(), self.getLabel())
|
||||||
|
return self.getLabel()
|
||||||
|
|
||||||
|
def getFullLabelToNode(self):
|
||||||
|
""" Label inside the Graph: nodeLabel groupLabel Label """
|
||||||
|
return '{} {}'.format(self.node.label, self.getFullLabel())
|
||||||
|
|
||||||
|
def getFullLabelToGraph(self):
|
||||||
|
""" Label inside the Graph: graphName nodeLabel groupLabel Label """
|
||||||
|
return '{} {}'.format(self.node.graph.name, self.getFullLabelToNode())
|
||||||
|
|
||||||
def getEnabled(self):
|
def getEnabled(self):
|
||||||
if isinstance(self.desc.enabled, types.FunctionType):
|
if isinstance(self.desc.enabled, types.FunctionType):
|
||||||
try:
|
try:
|
||||||
|
@ -265,7 +286,12 @@ class Attribute(BaseObject):
|
||||||
|
|
||||||
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)
|
||||||
|
fullNameToGraph = Property(str, getFullNameToGraph, constant=True)
|
||||||
label = Property(str, getLabel, constant=True)
|
label = Property(str, getLabel, constant=True)
|
||||||
|
fullLabel = Property(str, getFullLabel, constant=True)
|
||||||
|
fullLabelToNode = Property(str, getFullLabelToNode, constant=True)
|
||||||
|
fullLabelToGraph = Property(str, getFullLabelToGraph, constant=True)
|
||||||
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)
|
||||||
|
|
|
@ -415,7 +415,7 @@ class Graph(BaseObject):
|
||||||
def removeNode(self, nodeName):
|
def removeNode(self, nodeName):
|
||||||
"""
|
"""
|
||||||
Remove the node identified by 'nodeName' from the graph
|
Remove the node identified by 'nodeName' from the graph
|
||||||
and return in and out edges removed by this operation in two dicts {dstAttr.getFullName(), srcAttr.getFullName()}
|
and return in and out edges removed by this operation in two dicts {dstAttr.getFullNameToNode(), srcAttr.getFullNameToNode()}
|
||||||
"""
|
"""
|
||||||
node = self.node(nodeName)
|
node = self.node(nodeName)
|
||||||
inEdges = {}
|
inEdges = {}
|
||||||
|
@ -425,10 +425,10 @@ class Graph(BaseObject):
|
||||||
with GraphModification(self):
|
with GraphModification(self):
|
||||||
for edge in self.nodeOutEdges(node):
|
for edge in self.nodeOutEdges(node):
|
||||||
self.removeEdge(edge.dst)
|
self.removeEdge(edge.dst)
|
||||||
outEdges[edge.dst.getFullName()] = edge.src.getFullName()
|
outEdges[edge.dst.getFullNameToNode()] = edge.src.getFullNameToNode()
|
||||||
for edge in self.nodeInEdges(node):
|
for edge in self.nodeInEdges(node):
|
||||||
self.removeEdge(edge.dst)
|
self.removeEdge(edge.dst)
|
||||||
inEdges[edge.dst.getFullName()] = edge.src.getFullName()
|
inEdges[edge.dst.getFullNameToNode()] = edge.src.getFullNameToNode()
|
||||||
|
|
||||||
node.alive = False
|
node.alive = False
|
||||||
self._nodes.remove(node)
|
self._nodes.remove(node)
|
||||||
|
@ -583,7 +583,7 @@ class Graph(BaseObject):
|
||||||
if srcAttr.node.graph != self or dstAttr.node.graph != self:
|
if srcAttr.node.graph != self or dstAttr.node.graph != self:
|
||||||
raise RuntimeError('The attributes of the edge should be part of a common graph.')
|
raise RuntimeError('The attributes of the edge should be part of a common graph.')
|
||||||
if dstAttr in self.edges.keys():
|
if dstAttr in self.edges.keys():
|
||||||
raise RuntimeError('Destination attribute "{}" is already connected.'.format(dstAttr.getFullName()))
|
raise RuntimeError('Destination attribute "{}" is already connected.'.format(dstAttr.getFullNameToNode()))
|
||||||
edge = Edge(srcAttr, dstAttr)
|
edge = Edge(srcAttr, dstAttr)
|
||||||
self.edges.add(edge)
|
self.edges.add(edge)
|
||||||
self.markNodesDirty(dstAttr.node)
|
self.markNodesDirty(dstAttr.node)
|
||||||
|
@ -600,7 +600,7 @@ class Graph(BaseObject):
|
||||||
@changeTopology
|
@changeTopology
|
||||||
def removeEdge(self, dstAttr):
|
def removeEdge(self, dstAttr):
|
||||||
if dstAttr not in self.edges.keys():
|
if dstAttr not in self.edges.keys():
|
||||||
raise RuntimeError('Attribute "{}" is not connected'.format(dstAttr.getFullName()))
|
raise RuntimeError('Attribute "{}" is not connected'.format(dstAttr.getFullNameToNode()))
|
||||||
edge = self.edges.pop(dstAttr)
|
edge = self.edges.pop(dstAttr)
|
||||||
self.markNodesDirty(dstAttr.node)
|
self.markNodesDirty(dstAttr.node)
|
||||||
dstAttr.valueChanged.emit()
|
dstAttr.valueChanged.emit()
|
||||||
|
@ -1202,4 +1202,3 @@ def loadGraph(filepath):
|
||||||
graph.load(filepath)
|
graph.load(filepath)
|
||||||
graph.update()
|
graph.update()
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
|
|
|
@ -197,10 +197,10 @@ class DuplicateNodesCommand(GraphCommand):
|
||||||
class SetAttributeCommand(GraphCommand):
|
class SetAttributeCommand(GraphCommand):
|
||||||
def __init__(self, graph, attribute, value, parent=None):
|
def __init__(self, graph, attribute, value, parent=None):
|
||||||
super(SetAttributeCommand, self).__init__(graph, parent)
|
super(SetAttributeCommand, self).__init__(graph, parent)
|
||||||
self.attrName = attribute.getFullName()
|
self.attrName = attribute.getFullNameToNode()
|
||||||
self.value = value
|
self.value = value
|
||||||
self.oldValue = attribute.getExportValue()
|
self.oldValue = attribute.getExportValue()
|
||||||
self.setText("Set Attribute '{}'".format(attribute.getFullName()))
|
self.setText("Set Attribute '{}'".format(attribute.getFullNameToNode()))
|
||||||
|
|
||||||
def redoImpl(self):
|
def redoImpl(self):
|
||||||
if self.value == self.oldValue:
|
if self.value == self.oldValue:
|
||||||
|
@ -215,8 +215,8 @@ class SetAttributeCommand(GraphCommand):
|
||||||
class AddEdgeCommand(GraphCommand):
|
class AddEdgeCommand(GraphCommand):
|
||||||
def __init__(self, graph, src, dst, parent=None):
|
def __init__(self, graph, src, dst, parent=None):
|
||||||
super(AddEdgeCommand, self).__init__(graph, parent)
|
super(AddEdgeCommand, self).__init__(graph, parent)
|
||||||
self.srcAttr = src.getFullName()
|
self.srcAttr = src.getFullNameToNode()
|
||||||
self.dstAttr = dst.getFullName()
|
self.dstAttr = dst.getFullNameToNode()
|
||||||
self.setText("Connect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
self.setText("Connect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
||||||
|
|
||||||
if src.baseType != dst.baseType:
|
if src.baseType != dst.baseType:
|
||||||
|
@ -233,8 +233,8 @@ class AddEdgeCommand(GraphCommand):
|
||||||
class RemoveEdgeCommand(GraphCommand):
|
class RemoveEdgeCommand(GraphCommand):
|
||||||
def __init__(self, graph, edge, parent=None):
|
def __init__(self, graph, edge, parent=None):
|
||||||
super(RemoveEdgeCommand, self).__init__(graph, parent)
|
super(RemoveEdgeCommand, self).__init__(graph, parent)
|
||||||
self.srcAttr = edge.src.getFullName()
|
self.srcAttr = edge.src.getFullNameToNode()
|
||||||
self.dstAttr = edge.dst.getFullName()
|
self.dstAttr = edge.dst.getFullNameToNode()
|
||||||
self.setText("Disconnect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
self.setText("Disconnect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
||||||
|
|
||||||
def redoImpl(self):
|
def redoImpl(self):
|
||||||
|
@ -250,7 +250,7 @@ class ListAttributeAppendCommand(GraphCommand):
|
||||||
def __init__(self, graph, listAttribute, value, parent=None):
|
def __init__(self, graph, listAttribute, value, parent=None):
|
||||||
super(ListAttributeAppendCommand, self).__init__(graph, parent)
|
super(ListAttributeAppendCommand, self).__init__(graph, parent)
|
||||||
assert isinstance(listAttribute, ListAttribute)
|
assert isinstance(listAttribute, ListAttribute)
|
||||||
self.attrName = listAttribute.getFullName()
|
self.attrName = listAttribute.getFullNameToNode()
|
||||||
self.index = None
|
self.index = None
|
||||||
self.count = 1
|
self.count = 1
|
||||||
self.value = value if value else None
|
self.value = value if value else None
|
||||||
|
@ -276,10 +276,10 @@ class ListAttributeRemoveCommand(GraphCommand):
|
||||||
super(ListAttributeRemoveCommand, self).__init__(graph, parent)
|
super(ListAttributeRemoveCommand, self).__init__(graph, parent)
|
||||||
listAttribute = attribute.root
|
listAttribute = attribute.root
|
||||||
assert isinstance(listAttribute, ListAttribute)
|
assert isinstance(listAttribute, ListAttribute)
|
||||||
self.listAttrName = listAttribute.getFullName()
|
self.listAttrName = listAttribute.getFullNameToNode()
|
||||||
self.index = listAttribute.index(attribute)
|
self.index = listAttribute.index(attribute)
|
||||||
self.value = attribute.getExportValue()
|
self.value = attribute.getExportValue()
|
||||||
self.setText("Remove {}".format(attribute.getFullName()))
|
self.setText("Remove {}".format(attribute.getFullNameToNode()))
|
||||||
|
|
||||||
def redoImpl(self):
|
def redoImpl(self):
|
||||||
listAttribute = self.graph.attribute(self.listAttrName)
|
listAttribute = self.graph.attribute(self.listAttrName)
|
||||||
|
|
|
@ -618,14 +618,14 @@ class UIGraph(QObject):
|
||||||
@Slot(Attribute, Attribute)
|
@Slot(Attribute, Attribute)
|
||||||
def addEdge(self, src, dst):
|
def addEdge(self, src, dst):
|
||||||
if isinstance(dst, ListAttribute) and not isinstance(src, ListAttribute):
|
if isinstance(dst, ListAttribute) and not isinstance(src, ListAttribute):
|
||||||
with self.groupedGraphModification("Insert and Add Edge on {}".format(dst.getFullName())):
|
with self.groupedGraphModification("Insert and Add Edge on {}".format(dst.getFullNameToNode())):
|
||||||
self.appendAttribute(dst)
|
self.appendAttribute(dst)
|
||||||
self._addEdge(src, dst.at(-1))
|
self._addEdge(src, dst.at(-1))
|
||||||
else:
|
else:
|
||||||
self._addEdge(src, dst)
|
self._addEdge(src, dst)
|
||||||
|
|
||||||
def _addEdge(self, src, dst):
|
def _addEdge(self, src, dst):
|
||||||
with self.groupedGraphModification("Connect '{}'->'{}'".format(src.getFullName(), dst.getFullName())):
|
with self.groupedGraphModification("Connect '{}'->'{}'".format(src.getFullNameToNode(), dst.getFullNameToNode())):
|
||||||
if dst in self._graph.edges.keys():
|
if dst in self._graph.edges.keys():
|
||||||
self.removeEdge(self._graph.edge(dst))
|
self.removeEdge(self._graph.edge(dst))
|
||||||
self.push(commands.AddEdgeCommand(self._graph, src, dst))
|
self.push(commands.AddEdgeCommand(self._graph, src, dst))
|
||||||
|
@ -633,7 +633,7 @@ class UIGraph(QObject):
|
||||||
@Slot(Edge)
|
@Slot(Edge)
|
||||||
def removeEdge(self, edge):
|
def removeEdge(self, edge):
|
||||||
if isinstance(edge.dst.root, ListAttribute):
|
if isinstance(edge.dst.root, ListAttribute):
|
||||||
with self.groupedGraphModification("Remove Edge and Delete {}".format(edge.dst.getFullName())):
|
with self.groupedGraphModification("Remove Edge and Delete {}".format(edge.dst.getFullNameToNode())):
|
||||||
self.push(commands.RemoveEdgeCommand(self._graph, edge))
|
self.push(commands.RemoveEdgeCommand(self._graph, edge))
|
||||||
self.removeAttribute(edge.dst)
|
self.removeAttribute(edge.dst)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import QtQuick 2.7
|
import QtQuick 2.14
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import MaterialIcons 2.2
|
import MaterialIcons 2.2
|
||||||
import QtQml.Models 2.2
|
import QtQml.Models 2.2
|
||||||
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
|
||||||
import Controls 1.0
|
import Controls 1.0
|
||||||
import Utils 1.0
|
import Utils 1.0
|
||||||
|
@ -26,6 +27,9 @@ Panel {
|
||||||
property int currentIndex: 0
|
property int currentIndex: 0
|
||||||
property bool readOnly: false
|
property bool readOnly: false
|
||||||
|
|
||||||
|
property string filter: ""
|
||||||
|
property bool filterValue: true
|
||||||
|
|
||||||
signal removeImageRequest(var attribute)
|
signal removeImageRequest(var attribute)
|
||||||
signal filesDropped(var drop, var augmentSfm)
|
signal filesDropped(var drop, var augmentSfm)
|
||||||
|
|
||||||
|
@ -40,9 +44,66 @@ Panel {
|
||||||
id: m
|
id: m
|
||||||
property variant currentCameraInit: _reconstruction.tempCameraInit ? _reconstruction.tempCameraInit : root.cameraInit
|
property variant currentCameraInit: _reconstruction.tempCameraInit ? _reconstruction.tempCameraInit : root.cameraInit
|
||||||
property variant viewpoints: currentCameraInit ? currentCameraInit.attribute('viewpoints').value : undefined
|
property variant viewpoints: currentCameraInit ? currentCameraInit.attribute('viewpoints').value : undefined
|
||||||
|
property variant intrinsics: currentCameraInit ? currentCameraInit.attribute('intrinsics').value : undefined
|
||||||
property bool readOnly: root.readOnly || displayHDR.checked
|
property bool readOnly: root.readOnly || displayHDR.checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property variant parsedIntrinsic
|
||||||
|
property int numberOfIntrinsics : m.intrinsics ? m.intrinsics.count : 0
|
||||||
|
|
||||||
|
onNumberOfIntrinsicsChanged: {
|
||||||
|
parseIntr()
|
||||||
|
}
|
||||||
|
|
||||||
|
function populate_model()
|
||||||
|
{
|
||||||
|
intrinsicModel.clear()
|
||||||
|
for (var intr in parsedIntrinsic) {
|
||||||
|
intrinsicModel.appendRow(parsedIntrinsic[intr])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIntr(){
|
||||||
|
parsedIntrinsic = []
|
||||||
|
if(!m.intrinsics)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Loop through all intrinsics
|
||||||
|
for(var i = 0; i < m.intrinsics.count; ++i){
|
||||||
|
var intrinsic = {}
|
||||||
|
|
||||||
|
//Loop through all attributes
|
||||||
|
for(var j=0; j < m.intrinsics.at(i).value.count; ++j){
|
||||||
|
var currentAttribute = m.intrinsics.at(i).value.at(j)
|
||||||
|
if(currentAttribute.type === "GroupAttribute"){
|
||||||
|
for(var k=0; k < currentAttribute.value.count; ++k){
|
||||||
|
intrinsic[currentAttribute.name + "." + currentAttribute.value.at(k).name] = currentAttribute.value.at(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(currentAttribute.type === "ListAttribute"){
|
||||||
|
// not needed for now
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
intrinsic[currentAttribute.name] = currentAttribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Table Model needs to contain an entry for each column.
|
||||||
|
// In case of old file formats, some intrinsic keys that we display may not exist in the model.
|
||||||
|
// So, here we create an empty entry to enforce that the key exists in the model.
|
||||||
|
for(var n = 0; n < intrinsicModel.columnNames.length; ++n)
|
||||||
|
{
|
||||||
|
var name = intrinsicModel.columnNames[n]
|
||||||
|
if(!(name in intrinsic)) {
|
||||||
|
intrinsic[name] = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsedIntrinsic[i] = intrinsic
|
||||||
|
}
|
||||||
|
populate_model()
|
||||||
|
}
|
||||||
|
|
||||||
headerBar: RowLayout {
|
headerBar: RowLayout {
|
||||||
MaterialToolButton {
|
MaterialToolButton {
|
||||||
text: MaterialIcons.more_vert
|
text: MaterialIcons.more_vert
|
||||||
|
@ -91,7 +152,14 @@ Panel {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar { minimumSize: 0.05 }
|
interactive: !intrinsicsFilterButton.checked
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
minimumSize: 0.05
|
||||||
|
active : !intrinsicsFilterButton.checked
|
||||||
|
visible: !intrinsicsFilterButton.checked
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
clip: true
|
clip: true
|
||||||
|
@ -115,14 +183,16 @@ Panel {
|
||||||
model: m.viewpoints
|
model: m.viewpoints
|
||||||
sortRole: "path"
|
sortRole: "path"
|
||||||
// TODO: provide filtering on reconstruction status
|
// TODO: provide filtering on reconstruction status
|
||||||
// filterRole: _reconstruction.sfmReport ? "reconstructed" : ""
|
filterRole: _reconstruction.sfmReport ? root.filter : ""
|
||||||
// filterValue: true / false
|
filterValue: root.filterValue
|
||||||
// in modelData:
|
// in modelData:
|
||||||
// if(filterRole == roleName)
|
// if(filterRole == roleName)
|
||||||
// return _reconstruction.isReconstructed(item.model.object)
|
// return _reconstruction.isReconstructed(item.model.object)
|
||||||
|
|
||||||
// override modelData to return basename of viewpoint's path for sorting
|
// override modelData to return basename of viewpoint's path for sorting
|
||||||
function modelData(item, roleName) {
|
function modelData(item, roleName) {
|
||||||
|
if(filterRole == roleName)
|
||||||
|
return _reconstruction.isReconstructed(item.model.object)
|
||||||
var value = item.model.object.childAttribute(roleName).value
|
var value = item.model.object.childAttribute(roleName).value
|
||||||
if(roleName == sortRole)
|
if(roleName == sortRole)
|
||||||
return Filepath.basename(value)
|
return Filepath.basename(value)
|
||||||
|
@ -138,6 +208,7 @@ Panel {
|
||||||
height: grid.cellHeight
|
height: grid.cellHeight
|
||||||
readOnly: m.readOnly
|
readOnly: m.readOnly
|
||||||
displayViewId: displayViewIdsAction.checked
|
displayViewId: displayViewIdsAction.checked
|
||||||
|
visible: !intrinsicsFilterButton.checked
|
||||||
|
|
||||||
isCurrentItem: GridView.isCurrentItem
|
isCurrentItem: GridView.isCurrentItem
|
||||||
|
|
||||||
|
@ -233,8 +304,9 @@ Panel {
|
||||||
|
|
||||||
// Explanatory placeholder when no image has been added yet
|
// Explanatory placeholder when no image has been added yet
|
||||||
Column {
|
Column {
|
||||||
|
id: dropImagePlaceholder
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: grid.model.count == 0
|
visible: (m.viewpoints ? m.viewpoints.count == 0 : true) && !intrinsicsFilterButton.checked
|
||||||
spacing: 4
|
spacing: 4
|
||||||
Label {
|
Label {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
@ -246,11 +318,106 @@ Panel {
|
||||||
text: "Drop Image Files / Folders"
|
text: "Drop Image Files / Folders"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Placeholder when the filtered images list is empty
|
||||||
|
Column {
|
||||||
|
id: noImageImagePlaceholder
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: (m.viewpoints ? m.viewpoints.count != 0 : false) && !dropImagePlaceholder.visible && grid.model.count == 0 && !intrinsicsFilterButton.checked
|
||||||
|
spacing: 4
|
||||||
|
Label {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: MaterialIcons.filter_none
|
||||||
|
font.pointSize: 24
|
||||||
|
font.family: MaterialIcons.fontFamily
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "No images in this filtered view"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout{
|
||||||
|
anchors.fill: parent
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onTapped: {
|
||||||
|
intrinsicTable.focus = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView{
|
||||||
|
id : intrinsicTable
|
||||||
|
visible: intrinsicsFilterButton.checked
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
interactive : !focus
|
||||||
|
|
||||||
|
boundsMovement : Flickable.StopAtBounds
|
||||||
|
|
||||||
|
//Provide width for column
|
||||||
|
//Note no size provided for the last column (bool comp) so it uses its automated size
|
||||||
|
columnWidthProvider: function (column) { return intrinsicModel.columnWidths[column] }
|
||||||
|
|
||||||
|
model: intrinsicModel
|
||||||
|
|
||||||
|
delegate: IntrinsicDisplayDelegate{}
|
||||||
|
|
||||||
|
ScrollBar.horizontal: ScrollBar { id: sb }
|
||||||
|
ScrollBar.vertical : ScrollBar { id: sbv }
|
||||||
|
}
|
||||||
|
|
||||||
|
TableModel{
|
||||||
|
id : intrinsicModel
|
||||||
|
// Hardcoded default width per column
|
||||||
|
property var columnWidths: [105, 75, 75, 75, 125, 60, 60, 45, 45, 200, 60, 60]
|
||||||
|
property var columnNames: [
|
||||||
|
"intrinsicId",
|
||||||
|
"pxInitialFocalLength",
|
||||||
|
"pxFocalLength.x",
|
||||||
|
"pxFocalLength.y",
|
||||||
|
"type",
|
||||||
|
"width",
|
||||||
|
"height",
|
||||||
|
"sensorWidth",
|
||||||
|
"sensorHeight",
|
||||||
|
"serialNumber",
|
||||||
|
"principalPoint.x",
|
||||||
|
"principalPoint.y",
|
||||||
|
"locked"]
|
||||||
|
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[0]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[1]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[2]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[3]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[4]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[5]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[6]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[7]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[8]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[9]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[10]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[11]]} }
|
||||||
|
TableModelColumn { display: function(modelIndex){return parsedIntrinsic[modelIndex.row][intrinsicModel.columnNames[12]]} }
|
||||||
|
//https://doc.qt.io/qt-5/qml-qt-labs-qmlmodels-tablemodel.html#appendRow-method
|
||||||
|
}
|
||||||
|
|
||||||
|
//CODE FOR HEADERS
|
||||||
|
//UNCOMMENT WHEN COMPATIBLE WITH THE RIGHT QT VERSION
|
||||||
|
|
||||||
|
// HorizontalHeaderView {
|
||||||
|
// id: horizontalHeader
|
||||||
|
// syncView: tableView
|
||||||
|
// anchors.left: tableView.left
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
DropArea {
|
DropArea {
|
||||||
id: dropArea
|
id: dropArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
enabled: !m.readOnly
|
enabled: !m.readOnly && !intrinsicsFilterButton.checked
|
||||||
keys: ["text/uri-list"]
|
keys: ["text/uri-list"]
|
||||||
// TODO: onEntered: call specific method to filter files based on extension
|
// TODO: onEntered: call specific method to filter files based on extension
|
||||||
onDropped: {
|
onDropped: {
|
||||||
|
@ -344,22 +511,123 @@ Panel {
|
||||||
|
|
||||||
footerContent: RowLayout {
|
footerContent: RowLayout {
|
||||||
// Images count
|
// Images count
|
||||||
MaterialToolLabel {
|
id: footer
|
||||||
|
|
||||||
|
function resetButtons(){
|
||||||
|
inputImagesFilterButton.checked = false
|
||||||
|
estimatedCamerasFilterButton.checked = false
|
||||||
|
nonEstimatedCamerasFilterButton.checked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialToolLabelButton {
|
||||||
|
id : inputImagesFilterButton
|
||||||
Layout.minimumWidth: childrenRect.width
|
Layout.minimumWidth: childrenRect.width
|
||||||
ToolTip.text: grid.model.count + " Input Images"
|
ToolTip.text: grid.model.count + " Input Images"
|
||||||
iconText: MaterialIcons.image
|
iconText: MaterialIcons.image
|
||||||
label: grid.model.count.toString()
|
label: (m.viewpoints ? m.viewpoints.count : 0)
|
||||||
// enabled: grid.model.count > 0
|
padding: 3
|
||||||
// margin: 4
|
|
||||||
|
checkable: true
|
||||||
|
checked: true
|
||||||
|
|
||||||
|
onCheckedChanged:{
|
||||||
|
if(checked) {
|
||||||
|
root.filter = ""
|
||||||
|
root.filterValue = true
|
||||||
|
estimatedCamerasFilterButton.checked = false
|
||||||
|
nonEstimatedCamerasFilterButton.checked = false
|
||||||
|
intrinsicsFilterButton.checked = false;
|
||||||
}
|
}
|
||||||
// cameras count
|
}
|
||||||
MaterialToolLabel {
|
}
|
||||||
|
// Estimated cameras count
|
||||||
|
MaterialToolLabelButton {
|
||||||
|
id : estimatedCamerasFilterButton
|
||||||
Layout.minimumWidth: childrenRect.width
|
Layout.minimumWidth: childrenRect.width
|
||||||
ToolTip.text: label + " Estimated Cameras"
|
ToolTip.text: label + " Estimated Cameras"
|
||||||
iconText: MaterialIcons.videocam
|
iconText: MaterialIcons.videocam
|
||||||
label: _reconstruction ? _reconstruction.nbCameras.toString() : "0"
|
label: _reconstruction.nbCameras ? _reconstruction.nbCameras.toString() : "-"
|
||||||
// margin: 4
|
padding: 3
|
||||||
// enabled: _reconstruction.cameraInit && _reconstruction.nbCameras
|
|
||||||
|
enabled: _reconstruction.cameraInit && _reconstruction.nbCameras
|
||||||
|
checkable: true
|
||||||
|
checked: false
|
||||||
|
|
||||||
|
onCheckedChanged:{
|
||||||
|
if(checked) {
|
||||||
|
root.filter = "viewId"
|
||||||
|
root.filterValue = true
|
||||||
|
inputImagesFilterButton.checked = false
|
||||||
|
nonEstimatedCamerasFilterButton.checked = false
|
||||||
|
intrinsicsFilterButton.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEnabledChanged:{
|
||||||
|
if(!enabled) {
|
||||||
|
if(checked) inputImagesFilterButton.checked = true;
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// Non estimated cameras count
|
||||||
|
MaterialToolLabelButton {
|
||||||
|
id : nonEstimatedCamerasFilterButton
|
||||||
|
Layout.minimumWidth: childrenRect.width
|
||||||
|
ToolTip.text: label + " Non Estimated Cameras"
|
||||||
|
iconText: MaterialIcons.videocam_off
|
||||||
|
label: _reconstruction.nbCameras ? ((m.viewpoints ? m.viewpoints.count : 0) - _reconstruction.nbCameras.toString()).toString() : "-"
|
||||||
|
padding: 3
|
||||||
|
|
||||||
|
enabled: _reconstruction.cameraInit && _reconstruction.nbCameras
|
||||||
|
checkable: true
|
||||||
|
checked: false
|
||||||
|
|
||||||
|
onCheckedChanged:{
|
||||||
|
if(checked) {
|
||||||
|
filter = "viewId"
|
||||||
|
root.filterValue = false
|
||||||
|
inputImagesFilterButton.checked = false
|
||||||
|
estimatedCamerasFilterButton.checked = false
|
||||||
|
intrinsicsFilterButton.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEnabledChanged:{
|
||||||
|
if(!enabled) {
|
||||||
|
if(checked) inputImagesFilterButton.checked = true;
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
MaterialToolLabelButton {
|
||||||
|
id : intrinsicsFilterButton
|
||||||
|
Layout.minimumWidth: childrenRect.width
|
||||||
|
ToolTip.text: label + " Number of intrinsics"
|
||||||
|
iconText: MaterialIcons.camera
|
||||||
|
label: _reconstruction ? (m.intrinsics ? m.intrinsics.count : 0) : "0"
|
||||||
|
padding: 3
|
||||||
|
|
||||||
|
|
||||||
|
enabled: m.intrinsics ? m.intrinsics.count > 0 : false
|
||||||
|
checkable: true
|
||||||
|
checked: false
|
||||||
|
|
||||||
|
onCheckedChanged:{
|
||||||
|
if(checked) {
|
||||||
|
inputImagesFilterButton.checked = false
|
||||||
|
estimatedCamerasFilterButton.checked = false
|
||||||
|
nonEstimatedCamerasFilterButton.checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEnabledChanged:{
|
||||||
|
if(!enabled) {
|
||||||
|
if(checked) inputImagesFilterButton.checked = true;
|
||||||
|
checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
Item { Layout.fillHeight: true; Layout.fillWidth: true }
|
||||||
|
|
205
meshroom/ui/qml/ImageGallery/IntrinsicDisplayDelegate.qml
Normal file
205
meshroom/ui/qml/ImageGallery/IntrinsicDisplayDelegate.qml
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import MaterialIcons 2.2
|
||||||
|
import Utils 1.0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
property variant attribute: model.display
|
||||||
|
property int rowIndex: model.row
|
||||||
|
property int columnIndex: model.column
|
||||||
|
property bool readOnly: false
|
||||||
|
property string toolTipText: {
|
||||||
|
if(!attribute || Object.keys(attribute).length === 0)
|
||||||
|
return ""
|
||||||
|
return attribute.fullLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
Pane {
|
||||||
|
Layout.minimumWidth: loaderComponent.width
|
||||||
|
Layout.minimumHeight: loaderComponent.height
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
// Tooltip to replace headers for now (header incompatible atm)
|
||||||
|
ToolTip.delay: 10
|
||||||
|
ToolTip.timeout: 5000
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: toolTipText
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: loaderComponent.height
|
||||||
|
|
||||||
|
color: rowIndex % 2 ? palette.window : Qt.darker(palette.window, 1.1)
|
||||||
|
border.width: 2
|
||||||
|
border.color: Qt.darker(palette.window, 1.2)
|
||||||
|
clip: true
|
||||||
|
Loader {
|
||||||
|
id: loaderComponent
|
||||||
|
active: !!model.display // convert to bool with "!!"
|
||||||
|
sourceComponent: {
|
||||||
|
if(!model.display)
|
||||||
|
return undefined
|
||||||
|
switch(model.display.type)
|
||||||
|
{
|
||||||
|
case "ChoiceParam": return choice_component
|
||||||
|
case "IntParam": return int_component
|
||||||
|
case "FloatParam": return float_component
|
||||||
|
case "BoolParam": return bool_component
|
||||||
|
case "StringParam": return textField_component
|
||||||
|
default: return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: textField_component
|
||||||
|
TextInput{
|
||||||
|
text: model.display.value
|
||||||
|
width: intrinsicModel.columnWidths[columnIndex]
|
||||||
|
horizontalAlignment: TextInput.AlignRight
|
||||||
|
color: 'white'
|
||||||
|
|
||||||
|
padding: 12
|
||||||
|
|
||||||
|
selectByMouse: true
|
||||||
|
selectionColor: 'white'
|
||||||
|
selectedTextColor: Qt.darker(palette.window, 1.1)
|
||||||
|
|
||||||
|
onEditingFinished: _reconstruction.setAttribute(attribute, text)
|
||||||
|
onAccepted: {
|
||||||
|
_reconstruction.setAttribute(attribute, text)
|
||||||
|
}
|
||||||
|
Component.onDestruction: {
|
||||||
|
if(activeFocus)
|
||||||
|
_reconstruction.setAttribute(attribute, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: int_component
|
||||||
|
|
||||||
|
TextInput{
|
||||||
|
text: model.display.value
|
||||||
|
width: intrinsicModel.columnWidths[columnIndex]
|
||||||
|
horizontalAlignment: TextInput.AlignRight
|
||||||
|
color: 'white'
|
||||||
|
|
||||||
|
padding: 12
|
||||||
|
|
||||||
|
selectByMouse: true
|
||||||
|
selectionColor: 'white'
|
||||||
|
selectedTextColor: Qt.darker(palette.window, 1.1)
|
||||||
|
|
||||||
|
IntValidator {
|
||||||
|
id: intValidator
|
||||||
|
}
|
||||||
|
|
||||||
|
validator: intValidator
|
||||||
|
|
||||||
|
onEditingFinished: _reconstruction.setAttribute(attribute, Number(text))
|
||||||
|
onAccepted: {
|
||||||
|
_reconstruction.setAttribute(attribute, Number(text))
|
||||||
|
}
|
||||||
|
Component.onDestruction: {
|
||||||
|
if(activeFocus)
|
||||||
|
_reconstruction.setAttribute(attribute, Number(text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: choice_component
|
||||||
|
ComboBox {
|
||||||
|
id: combo
|
||||||
|
model: attribute.desc.values
|
||||||
|
width: intrinsicModel.columnWidths[columnIndex]
|
||||||
|
|
||||||
|
flat : true
|
||||||
|
|
||||||
|
topInset: 7
|
||||||
|
leftInset: 6
|
||||||
|
rightInset: 6
|
||||||
|
bottomInset: 7
|
||||||
|
|
||||||
|
Component.onCompleted: currentIndex = find(attribute.value)
|
||||||
|
onActivated: _reconstruction.setAttribute(attribute, currentText)
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: attribute
|
||||||
|
onValueChanged: combo.currentIndex = combo.find(attribute.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: bool_component
|
||||||
|
CheckBox {
|
||||||
|
checked: attribute ? attribute.value : false
|
||||||
|
padding: 12
|
||||||
|
onToggled: _reconstruction.setAttribute(attribute, !attribute.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: float_component
|
||||||
|
TextInput{
|
||||||
|
readonly property real formattedValue: model.display.value.toFixed(2)
|
||||||
|
property string displayValue: String(formattedValue)
|
||||||
|
|
||||||
|
text: displayValue
|
||||||
|
width: intrinsicModel.columnWidths[columnIndex]
|
||||||
|
horizontalAlignment: TextInput.AlignRight
|
||||||
|
|
||||||
|
color: 'white'
|
||||||
|
padding: 12
|
||||||
|
|
||||||
|
selectByMouse: true
|
||||||
|
selectionColor: 'white'
|
||||||
|
selectedTextColor: Qt.darker(palette.window, 1.1)
|
||||||
|
|
||||||
|
enabled: !readOnly
|
||||||
|
|
||||||
|
clip: true;
|
||||||
|
|
||||||
|
autoScroll: activeFocus
|
||||||
|
|
||||||
|
//Use this function to ensure the left part is visible
|
||||||
|
//while keeping the trick for formatting the text
|
||||||
|
//Timing issues otherwise
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if(activeFocus) text = String(model.display.value)
|
||||||
|
else text = String(formattedValue)
|
||||||
|
cursorPosition = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleValidator {
|
||||||
|
id: doubleValidator
|
||||||
|
locale: 'C' // use '.' decimal separator disregarding the system locale
|
||||||
|
}
|
||||||
|
|
||||||
|
validator: doubleValidator
|
||||||
|
|
||||||
|
onEditingFinished: _reconstruction.setAttribute(attribute, Number(text))
|
||||||
|
onAccepted: {
|
||||||
|
_reconstruction.setAttribute(attribute, Number(text))
|
||||||
|
}
|
||||||
|
Component.onDestruction: {
|
||||||
|
if(activeFocus)
|
||||||
|
_reconstruction.setAttribute(attribute, Number(text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,3 +2,6 @@ module ImageGallery
|
||||||
|
|
||||||
ImageGallery 1.0 ImageGallery.qml
|
ImageGallery 1.0 ImageGallery.qml
|
||||||
ImageDelegate 1.0 ImageDelegate.qml
|
ImageDelegate 1.0 ImageDelegate.qml
|
||||||
|
ImageIntrinsicDelegate 1.0 ImageIntrinsicDelegate.qml
|
||||||
|
ImageIntrinsicViewer 1.0 ImageIntrinsicViewer.qml
|
||||||
|
IntrinsicDisplayDelegate 1.0 IntrinsicDisplayDelegate.qml
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue