mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-19 12:06:28 +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):
|
||||
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):
|
||||
""" Attribute 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):
|
||||
return self.attributeDesc.__class__.__name__
|
||||
|
||||
|
@ -102,6 +107,22 @@ class Attribute(BaseObject):
|
|||
def getLabel(self):
|
||||
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):
|
||||
if isinstance(self.desc.enabled, types.FunctionType):
|
||||
try:
|
||||
|
@ -265,7 +286,12 @@ class Attribute(BaseObject):
|
|||
|
||||
name = Property(str, getName, 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)
|
||||
fullLabel = Property(str, getFullLabel, constant=True)
|
||||
fullLabelToNode = Property(str, getFullLabelToNode, constant=True)
|
||||
fullLabelToGraph = Property(str, getFullLabelToGraph, constant=True)
|
||||
type = Property(str, getType, constant=True)
|
||||
baseType = Property(str, getType, constant=True)
|
||||
isReadOnly = Property(bool, _isReadOnly, constant=True)
|
||||
|
|
|
@ -415,7 +415,7 @@ class Graph(BaseObject):
|
|||
def removeNode(self, nodeName):
|
||||
"""
|
||||
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)
|
||||
inEdges = {}
|
||||
|
@ -425,10 +425,10 @@ class Graph(BaseObject):
|
|||
with GraphModification(self):
|
||||
for edge in self.nodeOutEdges(node):
|
||||
self.removeEdge(edge.dst)
|
||||
outEdges[edge.dst.getFullName()] = edge.src.getFullName()
|
||||
outEdges[edge.dst.getFullNameToNode()] = edge.src.getFullNameToNode()
|
||||
for edge in self.nodeInEdges(node):
|
||||
self.removeEdge(edge.dst)
|
||||
inEdges[edge.dst.getFullName()] = edge.src.getFullName()
|
||||
inEdges[edge.dst.getFullNameToNode()] = edge.src.getFullNameToNode()
|
||||
|
||||
node.alive = False
|
||||
self._nodes.remove(node)
|
||||
|
@ -583,7 +583,7 @@ class Graph(BaseObject):
|
|||
if srcAttr.node.graph != self or dstAttr.node.graph != self:
|
||||
raise RuntimeError('The attributes of the edge should be part of a common graph.')
|
||||
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)
|
||||
self.edges.add(edge)
|
||||
self.markNodesDirty(dstAttr.node)
|
||||
|
@ -600,7 +600,7 @@ class Graph(BaseObject):
|
|||
@changeTopology
|
||||
def removeEdge(self, dstAttr):
|
||||
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)
|
||||
self.markNodesDirty(dstAttr.node)
|
||||
dstAttr.valueChanged.emit()
|
||||
|
@ -1202,4 +1202,3 @@ def loadGraph(filepath):
|
|||
graph.load(filepath)
|
||||
graph.update()
|
||||
return graph
|
||||
|
||||
|
|
|
@ -197,10 +197,10 @@ class DuplicateNodesCommand(GraphCommand):
|
|||
class SetAttributeCommand(GraphCommand):
|
||||
def __init__(self, graph, attribute, value, parent=None):
|
||||
super(SetAttributeCommand, self).__init__(graph, parent)
|
||||
self.attrName = attribute.getFullName()
|
||||
self.attrName = attribute.getFullNameToNode()
|
||||
self.value = value
|
||||
self.oldValue = attribute.getExportValue()
|
||||
self.setText("Set Attribute '{}'".format(attribute.getFullName()))
|
||||
self.setText("Set Attribute '{}'".format(attribute.getFullNameToNode()))
|
||||
|
||||
def redoImpl(self):
|
||||
if self.value == self.oldValue:
|
||||
|
@ -215,8 +215,8 @@ class SetAttributeCommand(GraphCommand):
|
|||
class AddEdgeCommand(GraphCommand):
|
||||
def __init__(self, graph, src, dst, parent=None):
|
||||
super(AddEdgeCommand, self).__init__(graph, parent)
|
||||
self.srcAttr = src.getFullName()
|
||||
self.dstAttr = dst.getFullName()
|
||||
self.srcAttr = src.getFullNameToNode()
|
||||
self.dstAttr = dst.getFullNameToNode()
|
||||
self.setText("Connect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
||||
|
||||
if src.baseType != dst.baseType:
|
||||
|
@ -233,8 +233,8 @@ class AddEdgeCommand(GraphCommand):
|
|||
class RemoveEdgeCommand(GraphCommand):
|
||||
def __init__(self, graph, edge, parent=None):
|
||||
super(RemoveEdgeCommand, self).__init__(graph, parent)
|
||||
self.srcAttr = edge.src.getFullName()
|
||||
self.dstAttr = edge.dst.getFullName()
|
||||
self.srcAttr = edge.src.getFullNameToNode()
|
||||
self.dstAttr = edge.dst.getFullNameToNode()
|
||||
self.setText("Disconnect '{}'->'{}'".format(self.srcAttr, self.dstAttr))
|
||||
|
||||
def redoImpl(self):
|
||||
|
@ -250,7 +250,7 @@ class ListAttributeAppendCommand(GraphCommand):
|
|||
def __init__(self, graph, listAttribute, value, parent=None):
|
||||
super(ListAttributeAppendCommand, self).__init__(graph, parent)
|
||||
assert isinstance(listAttribute, ListAttribute)
|
||||
self.attrName = listAttribute.getFullName()
|
||||
self.attrName = listAttribute.getFullNameToNode()
|
||||
self.index = None
|
||||
self.count = 1
|
||||
self.value = value if value else None
|
||||
|
@ -276,10 +276,10 @@ class ListAttributeRemoveCommand(GraphCommand):
|
|||
super(ListAttributeRemoveCommand, self).__init__(graph, parent)
|
||||
listAttribute = attribute.root
|
||||
assert isinstance(listAttribute, ListAttribute)
|
||||
self.listAttrName = listAttribute.getFullName()
|
||||
self.listAttrName = listAttribute.getFullNameToNode()
|
||||
self.index = listAttribute.index(attribute)
|
||||
self.value = attribute.getExportValue()
|
||||
self.setText("Remove {}".format(attribute.getFullName()))
|
||||
self.setText("Remove {}".format(attribute.getFullNameToNode()))
|
||||
|
||||
def redoImpl(self):
|
||||
listAttribute = self.graph.attribute(self.listAttrName)
|
||||
|
|
|
@ -618,14 +618,14 @@ class UIGraph(QObject):
|
|||
@Slot(Attribute, Attribute)
|
||||
def addEdge(self, src, dst):
|
||||
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._addEdge(src, dst.at(-1))
|
||||
else:
|
||||
self._addEdge(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():
|
||||
self.removeEdge(self._graph.edge(dst))
|
||||
self.push(commands.AddEdgeCommand(self._graph, src, dst))
|
||||
|
@ -633,7 +633,7 @@ class UIGraph(QObject):
|
|||
@Slot(Edge)
|
||||
def removeEdge(self, edge):
|
||||
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.removeAttribute(edge.dst)
|
||||
else:
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.3
|
||||
import MaterialIcons 2.2
|
||||
import QtQml.Models 2.2
|
||||
import Qt.labs.qmlmodels 1.0
|
||||
|
||||
import Controls 1.0
|
||||
import Utils 1.0
|
||||
|
@ -26,6 +27,9 @@ Panel {
|
|||
property int currentIndex: 0
|
||||
property bool readOnly: false
|
||||
|
||||
property string filter: ""
|
||||
property bool filterValue: true
|
||||
|
||||
signal removeImageRequest(var attribute)
|
||||
signal filesDropped(var drop, var augmentSfm)
|
||||
|
||||
|
@ -40,9 +44,66 @@ Panel {
|
|||
id: m
|
||||
property variant currentCameraInit: _reconstruction.tempCameraInit ? _reconstruction.tempCameraInit : root.cameraInit
|
||||
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 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 {
|
||||
MaterialToolButton {
|
||||
text: MaterialIcons.more_vert
|
||||
|
@ -91,7 +152,14 @@ Panel {
|
|||
Layout.fillWidth: 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
|
||||
clip: true
|
||||
|
@ -115,14 +183,16 @@ Panel {
|
|||
model: m.viewpoints
|
||||
sortRole: "path"
|
||||
// TODO: provide filtering on reconstruction status
|
||||
// filterRole: _reconstruction.sfmReport ? "reconstructed" : ""
|
||||
// filterValue: true / false
|
||||
filterRole: _reconstruction.sfmReport ? root.filter : ""
|
||||
filterValue: root.filterValue
|
||||
// in modelData:
|
||||
// if(filterRole == roleName)
|
||||
// return _reconstruction.isReconstructed(item.model.object)
|
||||
|
||||
// override modelData to return basename of viewpoint's path for sorting
|
||||
function modelData(item, roleName) {
|
||||
if(filterRole == roleName)
|
||||
return _reconstruction.isReconstructed(item.model.object)
|
||||
var value = item.model.object.childAttribute(roleName).value
|
||||
if(roleName == sortRole)
|
||||
return Filepath.basename(value)
|
||||
|
@ -138,6 +208,7 @@ Panel {
|
|||
height: grid.cellHeight
|
||||
readOnly: m.readOnly
|
||||
displayViewId: displayViewIdsAction.checked
|
||||
visible: !intrinsicsFilterButton.checked
|
||||
|
||||
isCurrentItem: GridView.isCurrentItem
|
||||
|
||||
|
@ -233,8 +304,9 @@ Panel {
|
|||
|
||||
// Explanatory placeholder when no image has been added yet
|
||||
Column {
|
||||
id: dropImagePlaceholder
|
||||
anchors.centerIn: parent
|
||||
visible: grid.model.count == 0
|
||||
visible: (m.viewpoints ? m.viewpoints.count == 0 : true) && !intrinsicsFilterButton.checked
|
||||
spacing: 4
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
@ -246,11 +318,106 @@ Panel {
|
|||
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 {
|
||||
id: dropArea
|
||||
anchors.fill: parent
|
||||
enabled: !m.readOnly
|
||||
enabled: !m.readOnly && !intrinsicsFilterButton.checked
|
||||
keys: ["text/uri-list"]
|
||||
// TODO: onEntered: call specific method to filter files based on extension
|
||||
onDropped: {
|
||||
|
@ -344,22 +511,123 @@ Panel {
|
|||
|
||||
footerContent: RowLayout {
|
||||
// Images count
|
||||
MaterialToolLabel {
|
||||
id: footer
|
||||
|
||||
function resetButtons(){
|
||||
inputImagesFilterButton.checked = false
|
||||
estimatedCamerasFilterButton.checked = false
|
||||
nonEstimatedCamerasFilterButton.checked = false
|
||||
}
|
||||
|
||||
MaterialToolLabelButton {
|
||||
id : inputImagesFilterButton
|
||||
Layout.minimumWidth: childrenRect.width
|
||||
ToolTip.text: grid.model.count + " Input Images"
|
||||
iconText: MaterialIcons.image
|
||||
label: grid.model.count.toString()
|
||||
// enabled: grid.model.count > 0
|
||||
// margin: 4
|
||||
label: (m.viewpoints ? m.viewpoints.count : 0)
|
||||
padding: 3
|
||||
|
||||
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
|
||||
ToolTip.text: label + " Estimated Cameras"
|
||||
iconText: MaterialIcons.videocam
|
||||
label: _reconstruction ? _reconstruction.nbCameras.toString() : "0"
|
||||
// margin: 4
|
||||
// enabled: _reconstruction.cameraInit && _reconstruction.nbCameras
|
||||
label: _reconstruction.nbCameras ? _reconstruction.nbCameras.toString() : "-"
|
||||
padding: 3
|
||||
|
||||
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 }
|
||||
|
|
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
|
||||
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