[qt6][qml] Clean-up code and harmonize comments

This commit is contained in:
Candice Bentéjac 2024-10-10 20:18:51 +02:00
parent b12d1fed06
commit 5a0b1c0c95
85 changed files with 575 additions and 538 deletions

View file

@ -5,7 +5,10 @@ import Utils 1.0
import MaterialIcons 2.2
/// Meshroom "About" window
/**
* Meshroom "About" window
*/
Dialog {
id: root
@ -24,7 +27,7 @@ Dialog {
modal: true
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
padding: 30
topPadding: 0 // header provides top padding
topPadding: 0 // Header provides top padding
header: Pane {
background: Item {}
@ -175,16 +178,16 @@ Dialog {
sourceComponent: ScrollView {
Component.onCompleted: {
// try to load the local file
// Try to load the local file
var url = Filepath.stringToUrl(modelData.localUrl)
// fallback to the online url if file is not found
// Fallback to the online url if file is not found
if (!Filepath.exists(url))
url = modelData.onlineUrl
Request.get(url,
function(xhr) {
if (xhr.readyState === XMLHttpRequest.DONE)
{
// status is OK
// Status is OK
if (xhr.status === 200)
textArea.text = MeshroomApp.markdownToHtml(xhr.responseText)
else

View file

@ -1,10 +1,10 @@
import QtQuick
import QtQuick.Controls
/**
* A custom CheckBox designed to be used in ChartView's legend.
*/
CheckBox {
id: root

View file

@ -2,12 +2,12 @@ import QtQuick
import QtQuick.Controls
import QtCharts
/**
* ChartViewLegend is an interactive legend component for ChartViews.
* It provides a CheckBox for each series that can control its visibility,
* and highlight on hovering.
*/
Flow {
id: root
@ -49,7 +49,6 @@ Flow {
}
Repeater {
// ChartView series can't be accessed directly as a model.
// Use an intermediate ListModel populated with those series.
model: ListModel {
@ -70,7 +69,7 @@ Flow {
root.hoveredSeries = null
}
// hovered serie properties override
// Hovered serie properties override
states: [
State {
when: series && root.hoveredSeries === series

View file

@ -7,6 +7,7 @@ import Utils 1.0
* ColorChart is a color picker based on a set of predefined colors.
* It takes the form of a ToolButton that pops-up its palette when pressed.
*/
ToolButton {
id: root
@ -28,10 +29,10 @@ ToolButton {
id: palettePopup
padding: 4
// content width is missing side padding (hence the + padding*2)
// Content width is missing side padding (hence the + padding*2)
implicitWidth: colorChart.contentItem.width + padding * 2
// center the current color
// Center the current color
y: -(root.height - padding) / 2
x: -colorChart.currentItem.x - padding
@ -44,14 +45,14 @@ ToolButton {
spacing: 2
currentIndex: root.currentIndex
model: root.colors
// display each color as a ToolButton with a custom background
// Display each color as a ToolButton with a custom background
delegate: ToolButton {
padding: 0
width: root.width
height: root.height
background: Rectangle {
color: modelData
// display border of current/selected item
// Display border of current/selected item
border.width: hovered || index === colorChart.currentIndex ? 1 : 0
border.color: Colors.sysPalette.midlight
}

View file

@ -6,6 +6,7 @@ import QtQuick.Layouts
* FloatingPane provides a Pane with a slightly transparent default background
* using palette.base as color. Useful to create floating toolbar/overlays.
*/
Pane {
id: root
@ -14,5 +15,9 @@ Pane {
padding: 6
anchors.margins: 2
background: Rectangle { color: root.palette.base; opacity: opaque ? 1.0 : 0.7; radius: root.radius }
background: Rectangle {
color: root.palette.base
opacity: opaque ? 1.0 : 0.7
radius: root.radius
}
}

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
/**
* A custom GroupBox with predefined header.
*/
GroupBox {
id: root
@ -20,13 +21,14 @@ GroupBox {
background: Item {}
label: Pane {
padding: 2
width: root.width
background: Rectangle {
id: labelBg
color: palette.base
opacity: 0.8
}
padding: 2
width: root.width
RowLayout {
width: parent.width
Label {

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
/**
* KeyValue allows to create a list of key/value, like a table.
*/
Rectangle {
property alias key: keyLabel.text
property alias value: valueText.text
@ -19,7 +20,6 @@ Rectangle {
Rectangle {
anchors.margins: 2
color: Qt.darker(activePalette.window, 1.1)
// Layout.preferredWidth: sizeHandle.x
Layout.minimumWidth: 10.0 * Qt.application.font.pixelSize
Layout.maximumWidth: 15.0 * Qt.application.font.pixelSize
Layout.fillWidth: false
@ -45,7 +45,10 @@ Rectangle {
readOnly: true
selectByMouse: true
background: Rectangle { anchors.fill: parent; color: Qt.darker(activePalette.window, 1.05) }
background: Rectangle {
anchors.fill: parent
color: Qt.darker(activePalette.window, 1.05)
}
}
}
}

View file

@ -16,7 +16,7 @@ Dialog {
default property alias children: layout.children
// the content of this MessageDialog as a string
// The content of this MessageDialog as a string
readonly property string asString: titleLabel.text + "\n\n" + text + "\n" + detailedText + "\n" + helperText + "\n"
/// Return the text content of this dialog as a simple string.
@ -40,7 +40,7 @@ Dialog {
rightPadding: leftPadding
background: Item {
// hidden text edit to perform copy in clipboard
// Hidden text edit to perform copy in clipboard
TextEdit {
id: textContent
visible: false

View file

@ -2,7 +2,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
/**
* Panel is a container control with preconfigured header/footer.
*
@ -12,6 +11,7 @@ import QtQuick.Layouts
*
* The footer is empty (and not visible) by default. It does not provided any layout.
*/
Page {
id: root

View file

@ -4,10 +4,10 @@ import QtQuick.Layouts
import MaterialIcons 2.2
/**
* Basic SearchBar component with an appropriate icon and a TextField.
*/
FocusScope {
property alias textField: field
property alias text: field.text
@ -36,7 +36,7 @@ FocusScope {
Layout.fillWidth: true
selectByMouse: true
// ensure the field has focus when the text is modified
// Ensure the field has focus when the text is modified
onTextChanged: {
forceActiveFocus()
}

View file

@ -47,16 +47,16 @@ Page {
}
Rectangle {
property bool commonBorder : false
property bool commonBorder: false
property int lBorderwidth : index === mainTabBar.currentIndex ? 2 : 1
property int rBorderwidth : index === mainTabBar.currentIndex ? 2 : 1
property int tBorderwidth : index === mainTabBar.currentIndex ? 2 : 1
property int bBorderwidth : 0
property int lBorderwidth: index === mainTabBar.currentIndex ? 2 : 1
property int rBorderwidth: index === mainTabBar.currentIndex ? 2 : 1
property int tBorderwidth: index === mainTabBar.currentIndex ? 2 : 1
property int bBorderwidth: 0
property int commonBorderWidth : 1
property int commonBorderWidth: 1
z : -1
z: -1
color: Qt.darker(root.palette.window, 1.50)
@ -66,10 +66,10 @@ Page {
top: parent.top
bottom: parent.bottom
topMargin : commonBorder ? -commonBorderWidth : -tBorderwidth
bottomMargin : commonBorder ? -commonBorderWidth : -bBorderwidth
leftMargin : commonBorder ? -commonBorderWidth : -lBorderwidth
rightMargin : commonBorder ? -commonBorderWidth : -rBorderwidth
topMargin: commonBorder ? -commonBorderWidth : -tBorderwidth
bottomMargin: commonBorder ? -commonBorderWidth : -bBorderwidth
leftMargin: commonBorder ? -commonBorderWidth : -lBorderwidth
rightMargin: commonBorder ? -commonBorderWidth : -rBorderwidth
}
}
}

View file

@ -9,6 +9,7 @@ import Utils 1.0
* Text file viewer with auto-reload feature.
* Uses a ListView with one delegate by line instead of a TextArea for performance reasons.
*/
Item {
id: root
@ -115,7 +116,7 @@ Item {
clip: true
focus: true
// custom key navigation handling
// Custom key navigation handling
keyNavigationEnabled: false
highlightFollowsCurrentItem: true
highlightMoveDuration: 0
@ -145,14 +146,14 @@ Item {
}
function setText(value) {
// store current first index
// Store current first index
var topIndex = firstVisibleIndex()
// store whether autoscroll to bottom is active
// Store whether autoscroll to bottom is active
var scrollToBottom = atYEnd && autoscroll.checked
// replace text
// Replace text
text = value
// restore content position by either:
// Restore content position by either:
// - autoscrolling to bottom
if (scrollToBottom)
positionViewAtEnd()
@ -183,7 +184,7 @@ Item {
// TextMetrics for textual progress bar
TextMetrics {
id: progressMetrics
// total number of character in textual progress bar
// Total number of character in textual progress bar
property int count: 51
property string character: '*'
text: character.repeat(count)
@ -230,15 +231,15 @@ Item {
Loader {
id: delegateLoader
Layout.fillWidth: true
// default line delegate
// Default line delegate
sourceComponent: line_component
// line delegate selector based on content
// Line delegate selector based on content
StateGroup {
states: [
State {
name: "progressBar"
// detect textual progressbar (non empty line with only progressbar character)
// Detect textual progressbar (non-empty line with only progressbar character)
when: logLine.line.trim().length
&& logLine.line.split(progressMetrics.character).length - 1 === logLine.line.trim().length
PropertyChanges {
@ -281,7 +282,7 @@ Item {
Keys.forwardTo: [textView]
color: {
// color line according to log level
// Color line according to log level
if (text.indexOf("[warning]") >= 0)
return Colors.orange
else if(text.indexOf("[error]") >= 0)
@ -350,7 +351,7 @@ Item {
if (xhr.readyState === XMLHttpRequest.DONE) {
textView.setText(xhr.status === 200 ? xhr.responseText : "")
loading = false
// re-trigger reload source file
// Re-trigger reload source file
if (autoReload)
reloadTimer.restart()
}

View file

@ -4,6 +4,7 @@ import Controls 1.0
/**
* DialogsFactory is utility object to instantiate generic purpose Dialogs.
*/
QtObject {
readonly property string defaultErrorText: "An unexpected error has occurred"

View file

@ -56,12 +56,10 @@ ListView {
}
}
// Helper MouseArea to lose edit/activeFocus
// when clicking on the background
// Helper MouseArea to lose edit/activeFocus when clicking on the background
MouseArea {
anchors.fill: parent
onClicked: forceActiveFocus()
z: -1
}
}

View file

@ -8,18 +8,19 @@ import Utils 1.0
import Controls 1.0
/**
Instantiate a control to visualize and edit an Attribute based on its type.
*/
* Instantiate a control to visualize and edit an Attribute based on its type.
*/
RowLayout {
id: root
property variant attribute: null
property bool readOnly: false // whether the attribute's value can be modified
property bool readOnly: false // Whether the attribute's value can be modified
property bool objectsHideable: true
property string filterText: ""
property alias label: parameterLabel // accessor to the internal Label (attribute's name)
property int labelWidth // shortcut to set the fixed size of the Label
property alias label: parameterLabel // Accessor to the internal Label (attribute's name)
property int labelWidth // Shortcut to set the fixed size of the Label
readonly property bool editable: !attribute.isOutput && !attribute.isLink && !readOnly
@ -91,10 +92,10 @@ RowLayout {
delay: 800
}
// make label bold if attribute's value is not the default one
// Make label bold if attribute's value is not the default one
font.bold: !object.isOutput && !object.isDefault
// make label italic if attribute is a link
// Make label italic if attribute is a link
font.italic: object.isLink
MouseArea {
@ -295,7 +296,7 @@ RowLayout {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: function(mouse) {
// Do not loose the selection during the right click
// Do not lose the selection during the right click
textField.persistentSelection = true
// We store the status of the activeFocus before opening the popup
textField.memoryActiveFocus = textField.activeFocus
@ -321,7 +322,7 @@ RowLayout {
Clipboard.clear()
Clipboard.setText(attribute.value)
} else {
// copy selection only
// Copy selection only
textField.copy()
}
}
@ -331,12 +332,12 @@ RowLayout {
enabled: Clipboard.getText() != "" && !readOnly
onTriggered: {
if (textField.memoryActiveFocus) {
// replace the selected text with the clipboard
// Replace the selected text with the clipboard
// or if there is no selection insert at the cursor position
var before = textField.text.substr(0, textField.selectionStart)
var after = textField.text.substr(textField.selectionEnd, textField.text.length)
setTextFieldAttribute(before + Clipboard.getText() + after)
// set the cursor at the end of the added text
// Set the cursor at the end of the added text
textField.cursorPosition = before.length + Clipboard.getText().length
} else {
setTextFieldAttribute(Clipboard.getText())
@ -473,7 +474,7 @@ RowLayout {
inputModel: attribute.values
Component.onCompleted: {
// if value not in list, override the text and precise it is not valid
// If value not in list, override the text and precise it is not valid
var idx = find(attribute.value)
if (idx === -1) {
displayText = attribute.value
@ -490,10 +491,10 @@ RowLayout {
Connections {
target: attribute
function onValueChanged() {
// when reset, clear and find the current index
// When reset, clear and find the current index
// but if only reopen the combo box, keep the current value
//convert all values of desc values as string
// Convert all values of desc values as string
var valuesAsString = attribute.values.map(function(value) {
return value.toString()
})
@ -521,9 +522,9 @@ RowLayout {
onToggled: {
var t = attribute.value
if (!checked) {
t.splice(t.indexOf(modelData), 1) // remove element
t.splice(t.indexOf(modelData), 1) // Remove element
} else {
t.push(modelData) // add element
t.push(modelData) // Add element
}
_reconstruction.setAttribute(attribute, t)
}
@ -541,12 +542,12 @@ RowLayout {
}
DoubleValidator {
id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding the system locale
locale: 'C' // Use '.' decimal separator disregarding the system locale
}
implicitWidth: 100
Layout.fillWidth: !slider.active
enabled: root.editable
// cast value to string to avoid intrusive scientific notations on numbers
// Cast value to string to avoid intrusive scientific notations on numbers
property string displayValue: String(slider.active && slider.item.pressed ? slider.item.formattedValue : attribute.value)
text: displayValue
selectByMouse: true
@ -698,7 +699,7 @@ RowLayout {
{
'model': Qt.binding(function() { return attribute.value }),
'readOnly': Qt.binding(function() { return root.readOnly }),
'labelWidth': 100, // reduce label width for children (space gain)
'labelWidth': 100, // Reduce label width for children (space gain)
'objectsHideable': Qt.binding(function() { return root.objectsHideable }),
'filterText': Qt.binding(function() { return root.filterText }),
})
@ -714,12 +715,12 @@ RowLayout {
TextField {
implicitWidth: 100
enabled: root.editable
// cast value to string to avoid intrusive scientific notations on numbers
// Cast value to string to avoid intrusive scientific notations on numbers
property string displayValue: String(slider.pressed ? slider.formattedValue : attribute.value)
text: displayValue
selectByMouse: true
validator: DoubleValidator {
locale: 'C' // use '.' decimal separator disregarding the system locale
locale: 'C' // Use '.' decimal separator disregarding the system locale
}
onEditingFinished: setTextFieldAttribute(text)
onAccepted: setTextFieldAttribute(text)

View file

@ -5,8 +5,9 @@ import QtQuick.Layouts
import Utils 1.0
/**
The representation of an Attribute on a Node.
*/
* The representation of an Attribute on a Node.
*/
RowLayout {
id: root
@ -43,14 +44,12 @@ RowLayout {
x: nameLabel.x
}
function updatePin(isSrc, isVisible)
{
function updatePin(isSrc, isVisible) {
if (isSrc) {
innerOutputAnchor.linkEnabled = isVisible
} else {
innerInputAnchor.linkEnabled = isVisible
}
}
// Instantiate empty Items for each child attribute
@ -96,21 +95,21 @@ RowLayout {
property bool acceptableDrop: false
// add negative margins for DropArea to make the connection zone easier to reach
// Add negative margins for DropArea to make the connection zone easier to reach
anchors.fill: parent
anchors.margins: -2
// add horizontal negative margins according to the current layout
// Add horizontal negative margins according to the current layout
anchors.rightMargin: -root.width * 0.3
keys: [inputDragTarget.objectName]
onEntered: function(drag) {
// Check if attributes are compatible to create a valid connection
if (root.readOnly // cannot connect on a read-only attribute
|| drag.source.objectName != inputDragTarget.objectName // not an edge connector
|| drag.source.baseType !== inputDragTarget.baseType // not the same base type
|| drag.source.nodeItem === inputDragTarget.nodeItem // connection between attributes of the same node
|| (drag.source.isList && childrenRepeater.count) // source/target are lists but target already has children
|| drag.source.connectorType === "input" // refuse to connect an "input pin" on another one (input attr can be connected to input attr, but not the graphical pin)
if (root.readOnly // Cannot connect on a read-only attribute
|| drag.source.objectName != inputDragTarget.objectName // Not an edge connector
|| drag.source.baseType !== inputDragTarget.baseType // Not the same base type
|| drag.source.nodeItem === inputDragTarget.nodeItem // Connection between attributes of the same node
|| (drag.source.isList && childrenRepeater.count) // Source/target are lists but target already has children
|| drag.source.connectorType === "input" // Refuse to connect an "input pin" on another one (input attr can be connected to input attr, but not the graphical pin)
) {
// Refuse attributes connection
drag.accepted = false
@ -119,6 +118,7 @@ RowLayout {
}
inputDropArea.acceptableDrop = drag.accepted
}
onExited: {
if (inputDragTarget.attribute.isLink) { // Already connected attribute
root.edgeAboutToBeRemoved(undefined)
@ -161,7 +161,7 @@ RowLayout {
drag.smoothed: false
enabled: !root.readOnly
anchors.fill: parent
// use the same negative margins as DropArea to ease pin selection
// Use the same negative margins as DropArea to ease pin selection
anchors.margins: inputDropArea.anchors.margins
anchors.leftMargin: inputDropArea.anchors.leftMargin
anchors.rightMargin: inputDropArea.anchors.rightMargin
@ -186,14 +186,11 @@ RowLayout {
}
}
// Attribute name
Item {
id: nameContainer
Layout.fillWidth: true
implicitHeight: childrenRect.height
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Label {
@ -209,13 +206,13 @@ RowLayout {
anchors.right: attribute && attribute.isOutput ? parent.right : undefined
rightPadding: 0
color: {
if ((object.hasOutputConnections || object.isLink) && !object.enabled) return Colors.lightgrey
if ((object.hasOutputConnections || object.isLink) && !object.enabled)
return Colors.lightgrey
return hovered ? palette.highlight : palette.text
}
}
}
Rectangle {
id: outputAnchor
@ -258,12 +255,12 @@ RowLayout {
keys: [outputDragTarget.objectName]
onEntered: function(drag) {
// Check if attributes are compatible to create a valid connection
if (drag.source.objectName != outputDragTarget.objectName // not an edge connector
|| drag.source.baseType !== outputDragTarget.baseType // not the same base type
|| drag.source.nodeItem === outputDragTarget.nodeItem // connection between attributes of the same node
|| (!drag.source.isList && outputDragTarget.isList) // connection between a list and a simple attribute
|| (drag.source.isList && childrenRepeater.count) // source/target are lists but target already has children
|| drag.source.connectorType === "output" // refuse to connect an output pin on another one
if (drag.source.objectName != outputDragTarget.objectName // Not an edge connector
|| drag.source.baseType !== outputDragTarget.baseType // Not the same base type
|| drag.source.nodeItem === outputDragTarget.nodeItem // Connection between attributes of the same node
|| (!drag.source.isList && outputDragTarget.isList) // Connection between a list and a simple attribute
|| (drag.source.isList && childrenRepeater.count) // Source/target are lists but target already has children
|| drag.source.connectorType === "output" // Refuse to connect an output pin on another one
) {
// Refuse attributes connection
drag.accepted = false
@ -310,7 +307,7 @@ RowLayout {
// Move the edge's tip straight to the the current mouse position instead of waiting after the drag operation has started
drag.smoothed: false
anchors.fill: parent
// use the same negative margins as DropArea to ease pin selection
// Use the same negative margins as DropArea to ease pin selection
anchors.margins: outputDropArea.anchors.margins
anchors.leftMargin: outputDropArea.anchors.leftMargin
anchors.rightMargin: outputDropArea.anchors.rightMargin

View file

@ -7,6 +7,7 @@ import "common.js" as Common
/**
* ChunkListView
*/
ColumnLayout {
id: root
property variant chunks

View file

@ -4,12 +4,13 @@ import QtQuick.Layouts
import MaterialIcons 2.2
/** Node Badge to inform about compatibility issues
/**
* Node Badge to inform about compatibility issues
* Provides 2 delegates (can be set using sourceComponent property):
* - iconDelegate (default): icon + tooltip with information about the issue
* - bannerDelegate: banner with issue info + upgrade request button
*/
*/
Loader {
id: root

View file

@ -8,17 +8,18 @@ import Utils 1.0
/**
* CompatibilityManager summarizes and allows to resolve compatibility issues.
*/
*/
MessageDialog {
id: root
// the UIGraph instance
// The UIGraph instance
property var uigraph
// alias to underlying compatibilityNodes model
// Alias to underlying compatibilityNodes model
readonly property var nodesModel: uigraph ? uigraph.graph.compatibilityNodes : undefined
// the total number of compatibility issues
// The total number of compatibility issues
readonly property int issueCount: (nodesModel !== undefined && nodesModel !== null) ? nodesModel.count : 0
// the number of CompatibilityNodes that can be upgraded
// The number of CompatibilityNodes that can be upgraded
readonly property int upgradableCount: {
var count = 0
for (var i = 0; i < issueCount; ++i) {
@ -28,7 +29,7 @@ MessageDialog {
return count
}
// override MessageDialog.getAsString to add compatibility report
// Override MessageDialog.getAsString to add compatibility report
function getAsString() {
var t = asString + "\n"
t += '-------------------------\n'

View file

@ -6,8 +6,9 @@ import GraphEditor 1.0
import MaterialIcons 2.2
/**
A cubic spline representing an edge, going from point1 to point2, providing mouse interaction.
*/
* A cubic spline representing an edge, going from point1 to point2, providing mouse interaction.
*/
Item {
id: root
@ -69,7 +70,6 @@ Item {
control2X: x - ctrlPtDist
control2Y: y
}
}
ShapePath {
@ -98,11 +98,13 @@ Item {
}
}
}
Item {
// Place the label at the middle of the edge
x: (root.startX + root.endX) / 2
y: (root.startY + root.endY) / 2
visible: root.isForLoop
Rectangle {
anchors.centerIn: parent
property int margin: 2
@ -110,6 +112,7 @@ Item {
height: icon.height + 2 * margin
radius: width
color: path.strokeColor
MaterialToolLabel {
id: icon
anchors.centerIn: parent
@ -120,6 +123,7 @@ Item {
color: palette.base
ToolTip.text: "Foreach Loop"
}
MouseArea {
id: loopArea
anchors.fill: parent

View file

@ -7,14 +7,15 @@ import MaterialIcons 2.2
import Utils 1.0
/**
A component displaying a Graph (nodes, attributes and edges).
*/
* A component displaying a Graph (nodes, attributes and edges).
*/
Item {
id: root
property variant uigraph: null /// Meshroom ui graph (UIGraph)
readonly property variant graph: uigraph ? uigraph.graph : null /// core graph contained in ui graph
property variant nodeTypesModel: null /// the list of node types that can be instantiated
property variant uigraph: null /// Meshroom UI graph (UIGraph)
readonly property variant graph: uigraph ? uigraph.graph : null /// Core graph contained in the UI graph
property variant nodeTypesModel: null /// The list of node types that can be instantiated
property real maxZoom: 2.0
property real minZoom: 0.1
@ -22,7 +23,7 @@ Item {
property var _attributeToDelegate: ({})
// signals
// Signals
signal workspaceMoved()
signal workspaceClicked()
@ -34,10 +35,9 @@ Item {
property int nbMeshroomScenes: 0
property int nbDraggedFiles: 0
// Files have been dropped
signal filesDropped(var drop, var mousePosition)
signal filesDropped(var drop, var mousePosition) // Files have been dropped
// trigger initial fit() after initialization
// Trigger initial fit() after initialization
// (ensure GraphEditor has its final size)
Component.onCompleted: firstFitTimer.start()
@ -215,7 +215,7 @@ Item {
onClicked: function(mouse) {
if (mouse.button == Qt.RightButton) {
// store mouse click position in 'draggable' coordinates as new node spawn position
// Store mouse click position in 'draggable' coordinates as new node spawn position
newNodeMenu.spawnPosition = mouseArea.mapToItem(draggable, mouse.x, mouse.y)
newNodeMenu.popup()
}
@ -274,7 +274,7 @@ Item {
onVisibleChanged: {
searchBar.clear()
if (visible) {
// when menu is shown, give focus to the TextField filter
// When menu is shown, give focus to the TextField filter
searchBar.forceActiveFocus()
}
}
@ -298,12 +298,12 @@ Item {
// Forward key events to the search bar to continue typing seamlessly
// even if this delegate took the activeFocus due to mouse hovering
Keys.forwardTo: [searchBar.textField]
Keys.onPressed: {
Keys.onPressed: function(event) {
event.accepted = false;
switch (event.key) {
case Qt.Key_Return:
case Qt.Key_Enter:
// create node on validation (Enter/Return keys)
// Create node on validation (Enter/Return keys)
newNodeMenu.createNode(modelData)
event.accepted = true
break
@ -311,7 +311,7 @@ Item {
case Qt.Key_Down:
case Qt.Key_Left:
case Qt.Key_Right:
break // ignore if arrow key was pressed to let the menu be controlled
break // Ignore if arrow key was pressed to let the menu be controlled
default:
searchBar.forceActiveFocus()
}
@ -326,8 +326,8 @@ Item {
name: "invisible"
PropertyChanges {
target: menuItemDelegate
height: 0 // make sure the item is no visible by setting height to 0
focusPolicy: Qt.NoFocus // don't grab focus when not visible
height: 0 // Make sure the item is no visible by setting height to 0
focusPolicy: Qt.NoFocus // Don't grab focus when not visible
}
}
]
@ -429,7 +429,7 @@ Item {
const newSrcAttr = listAttr.value.at(value - 1)
const dst = edgeMenu.currentEdge.dst
// if the edge exists do not replace it
// If the edge exists, do not replace it
if (newSrcAttr === edgeMenu.currentEdge.src && dst === edgeMenu.currentEdge.dst) {
return
}
@ -488,7 +488,7 @@ Item {
Repeater {
id: edgesRepeater
// delay edges loading after nodes (edges needs attribute pins to be created)
// Delay edges loading after nodes (edges needs attribute pins to be created)
model: nodeRepeater.loaded && root.graph ? root.graph.edges : undefined
delegate: Edge {
@ -512,7 +512,6 @@ Item {
}
return (inFocus) ? 2 : 1
}
point1x: isValidEdge ? src.globalX + src.outputAnchorPos.x : 0
point1y: isValidEdge ? src.globalY + src.outputAnchorPos.y : 0
point2x: isValidEdge ? dst.globalX + dst.inputAnchorPos.x : 0
@ -552,7 +551,7 @@ Item {
id: nodeMenu
property var currentNode: null
property bool canComputeNode: currentNode != null && uigraph.graph.canComputeTopologically(currentNode)
//canSubmitOrCompute: return int n : 0 >= n <= 3 | n=0 cannot submit or compute | n=1 can compute | n=2 can submit | n=3 can compute & submit
// canSubmitOrCompute: return int n : 0 >= n <= 3 | n=0 cannot submit or compute | n=1 can compute | n=2 can submit | n=3 can compute & submit
property int canSubmitOrCompute: currentNode != null && uigraph.graph.canSubmitOrCompute(currentNode)
property bool isComputed: {
var count = 0
@ -601,7 +600,7 @@ Item {
}
}
}
return canCompute //canSubmit if canSubmitOrCompute == 1(can compute) or 3(can compute & submit)
return canCompute // canSubmit if canSubmitOrCompute == 1(can compute) or 3(can compute & submit)
}
onTriggered: {
@ -875,11 +874,9 @@ Item {
onEdgeAboutToBeRemoved: function(input) {
/*
Sometimes the signals are not in the right order
because of weird Qt/QML update order (next DropArea
entered signal before previous DropArea exited signal)
so edgeAboutToBeRemoved must be set to undefined before
it can be set to another attribute object.
* Sometimes the signals are not in the right order because of weird Qt/QML update order
* (next DropArea entered signal before previous DropArea exited signal) so edgeAboutToBeRemoved
* must be set to undefined before it can be set to another attribute object.
*/
if (input === undefined) {
if (nodeRepeater.temporaryEdgeAboutToBeRemoved === undefined) {
@ -899,7 +896,7 @@ Item {
onPositionChanged: {
if (dragging && uigraph.selectedNodes.contains(node)) {
// update all selected nodes positions with this node that is being dragged
// Update all selected nodes positions with this node that is being dragged
for (var i = 0; i < nodeRepeater.count; i++) {
var otherNode = nodeRepeater.itemAt(i)
if (uigraph.selectedNodes.contains(otherNode.node) && otherNode.node !== node) {
@ -910,10 +907,10 @@ Item {
}
}
// allow all nodes to know if they are being dragged
// Allow all nodes to know if they are being dragged
onDraggingChanged: nodeRepeater.dragging = dragging
// must not be enabled during drag because the other nodes will be slow to match the movement of the node being dragged
// Must not be enabled during drag because the other nodes will be slow to match the movement of the node being dragged
Behavior on x {
enabled: !nodeRepeater.dragging
NumberAnimation { duration: 100 }
@ -1122,12 +1119,12 @@ Item {
}
function nextItem() {
// compute bounding box
// Compute bounding box
var node = nodeRepeater.itemAt(filteredNodes.itemAt(navigation.currentIndex).index_)
var bbox = Qt.rect(node.x, node.y, node.width, node.height)
// rescale to fit the bounding box in the view, zoom is limited to prevent huge text
// Rescale to fit the bounding box in the view, zoom is limited to prevent huge text
draggable.scale = Math.min(Math.min(root.width / bbox.width, root.height / bbox.height),maxZoom)
// recenter
// Recenter
draggable.x = bbox.x*draggable.scale * -1 + (root.width - bbox.width * draggable.scale) * 0.5
draggable.y = bbox.y*draggable.scale * -1 + (root.height - bbox.height * draggable.scale) * 0.5
}
@ -1136,6 +1133,7 @@ Item {
function registerAttributePin(attribute, pin) {
root._attributeToDelegate[attribute] = pin
}
function unregisterAttributePin(attribute, pin) {
delete root._attributeToDelegate[attribute]
}
@ -1160,11 +1158,11 @@ Item {
// Fit graph to fill root
function fit() {
// compute bounding box
// Compute bounding box
var bbox = boundingBox()
// rescale to fit the bounding box in the view, zoom is limited to prevent huge text
// Rescale to fit the bounding box in the view, zoom is limited to prevent huge text
draggable.scale = Math.min(Math.min(root.width / bbox.width, root.height / bbox.height), maxZoom)
// recenter
// Recenter
draggable.x = bbox.x * draggable.scale * -1 + (root.width - bbox.width * draggable.scale) * 0.5
draggable.y = bbox.y * draggable.scale * -1 + (root.height - bbox.height * draggable.scale) * 0.5
}

View file

@ -1,10 +1,10 @@
pragma Singleton
import QtCore
/**
* Persistent Settings related to the GraphEditor module.
*/
Settings {
category: 'GraphEditor'
property bool showAdvancedAttributes: false

View file

@ -6,10 +6,10 @@ import Qt5Compat.GraphicalEffects
import MaterialIcons 2.2
import Utils 1.0
/**
* Visual representation of a Graph Node.
*/
Item {
id: root

View file

@ -2,8 +2,6 @@ import QtQuick
import Utils 1.0
//import "common.js" as Common
ListView {
id: root
interactive: false
@ -15,7 +13,7 @@ ListView {
property real chunkHeight: height
property bool modelIsBig: (3 * model.count >= width)
property real chunkWidth: {
if(!model || model.count == 0)
if (!model || model.count == 0)
return 0
return (width / model.count) - spacing
}
@ -30,13 +28,12 @@ ListView {
width: root.chunkWidth
property var chunkColor: Colors.getChunkColor(object, { "NONE": root.defaultColor })
color: {
if(!highlightChunks || model.count == 1)
if (!highlightChunks || model.count == 1)
return chunkColor
if(index % 2 == 0)
if (index % 2 == 0)
return Qt.lighter(chunkColor, 1.1)
else
return Qt.darker(chunkColor, 1.1)
}
}
}

View file

@ -9,6 +9,7 @@ import "common.js" as Common
/**
* Displays Node documentation
*/
FocusScope {
id: root

View file

@ -6,11 +6,11 @@ import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0
/**
* NodeEditor allows to visualize and edit the parameters of a Node.
* It mainly provides an attribute editor and a log inspector.
*/
Panel {
id: root
@ -36,7 +36,7 @@ Panel {
if (node !== null && (node.isFinishedOrRunning() || globalStatus == "ERROR")) {
computationInfo.text = Format.sec2timeStr(node.elapsedTime)
}
else{
else {
computationInfo.text = ""
}
}
@ -118,6 +118,7 @@ Panel {
Menu {
id: settingsMenu
y: parent.height
Menu {
id: filterAttributesMenu
title: "Filter Attributes"
@ -139,7 +140,9 @@ Panel {
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
RowLayout {
CheckBox {
id: defaultToggle
@ -158,7 +161,9 @@ Panel {
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
RowLayout {
CheckBox {
id: linkToggle
@ -177,7 +182,9 @@ Panel {
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
CheckBox {
id: advancedToggle
text: "Advanced"
@ -197,7 +204,9 @@ Panel {
enabled: root.node !== null
onClicked: Qt.openUrlExternally(Filepath.stringToUrl(root.node.internalFolder))
}
MenuSeparator {}
MenuItem {
enabled: root.node !== null
text: "Clear Pending Status"
@ -209,6 +218,7 @@ Panel {
}
}
}
ColumnLayout {
anchors.fill: parent
@ -216,7 +226,7 @@ Panel {
Loader {
active: root.isCompatibilityNode
Layout.fillWidth: true
visible: active // for layout update
visible: active // For layout update
sourceComponent: CompatibilityBadge {
canUpgrade: root.node.canUpgrade

View file

@ -5,11 +5,12 @@ import QtQuick.Layouts
import Controls 1.0
/**
* NodeLog displays log and statistics data of Node's chunks (NodeChunks)
* NodeLog displays the log file of Node's chunks (NodeChunks).
*
* To ease monitoring, it provides periodic auto-reload of the opened file
* if the related NodeChunk is being computed.
*/
FocusScope {
id: root
property variant node
@ -40,7 +41,6 @@ FocusScope {
anchors.fill: parent
source: componentLoader.sourceFile
autoReload: root.currentChunk !== undefined && root.currentChunk.statusName === "RUNNING"
// source is set in fileSelector
}
}
}

View file

@ -5,11 +5,12 @@ import QtQuick.Layouts
import Controls 1.0
/**
* NodeLog displays log and statistics data of Node's chunks (NodeChunks)
* NodeStatistics displays statistics data of Node's chunks (NodeChunks).
*
* To ease monitoring, it provides periodic auto-reload of the opened file
* if the related NodeChunk is being computed.
*/
FocusScope {
id: root

View file

@ -3,11 +3,12 @@ import QtQuick.Controls
import QtQuick.Layouts
/**
* NodeLog displays log and statistics data of Node's chunks (NodeChunks)
* NodeStatus displays the status-related information of Node's chunks (NodeChunks)
*
* To ease monitoring, it provides periodic auto-reload of the opened file
* if the related NodeChunk is being computed.
*/
FocusScope {
id: root
property variant node
@ -42,7 +43,7 @@ FocusScope {
id: statusListModel
function readSourceFile() {
// make sure we are trying to load a statistics file
// Make sure we are trying to load a statistics file
if (!Filepath.urlToString(source).endsWith("status"))
return
@ -51,31 +52,28 @@ FocusScope {
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
// console.warn("StatusListModel: read valid file")
if (lastModified === undefined || lastModified !== xhr.getResponseHeader('Last-Modified')) {
lastModified = xhr.getResponseHeader('Last-Modified')
try {
var jsonObject = JSON.parse(xhr.responseText)
var entries = []
// prepare data to populate the ListModel from the input json object
// Prepare data to populate the ListModel from the input json object
for (var key in jsonObject) {
var entry = {}
entry["key"] = key
entry["value"] = String(jsonObject[key])
entries.push(entry)
}
// reset the model with prepared data (limit to one update event)
// Reset the model with prepared data (limit to one update event)
statusListModel.clear()
statusListModel.append(entries)
} catch(exc) {
// console.warn("StatusListModel: failed to read file")
lastModified = undefined
statusListModel.clear()
}
}
} else {
// console.warn("StatusListModel: invalid file")
lastModified = undefined
statusListModel.clear()
}
@ -99,7 +97,6 @@ FocusScope {
Rectangle {
id: statusKey
anchors.margins: 2
// height: statusValue.height
color: Qt.darker(activePalette.window, 1.1)
Layout.preferredWidth: sizeHandle.x
Layout.minimumWidth: 10.0 * Qt.application.font.pixelSize

View file

@ -37,7 +37,6 @@ Item {
property color textColor: Colors.sysPalette.text
readonly property var colors: [
"#f44336",
"#e91e63",
@ -94,7 +93,7 @@ Item {
}
function readSourceFile() {
// make sure we are trying to load a statistics file
// Make sure we are trying to load a statistics file
if (!Filepath.urlToString(source).endsWith("statistics"))
return
@ -103,7 +102,7 @@ Item {
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) {
if (sourceModified === undefined || sourceModified < xhr.getResponseHeader('Last-Modified')) {
if (sourceModified === undefined || sourceModified < xhr.getResponseHeader("Last-Modified")) {
try {
root.jsonObject = JSON.parse(xhr.responseText)
} catch(exc) {
@ -112,7 +111,7 @@ Item {
return
}
resetCharts()
sourceModified = xhr.getResponseHeader('Last-Modified')
sourceModified = xhr.getResponseHeader("Last-Modified")
root.createCharts()
reloadTimer.restart()
}
@ -130,8 +129,8 @@ Item {
}
function createCharts() {
root.deltaTime = getPropertyWithDefault(jsonObject, 'interval', 30) / 60.0;
root.fileVersion = getPropertyWithDefault(jsonObject, 'fileVersion', 0.0)
root.deltaTime = getPropertyWithDefault(jsonObject, "interval", 30) / 60.0;
root.fileVersion = getPropertyWithDefault(jsonObject, "fileVersion", 0.0)
initCpuChart()
initRamChart()
initGpuChart()
@ -158,7 +157,7 @@ Item {
var nbCores = categories.length
root.nbCores = nbCores
root.cpuFrequency = getPropertyWithDefault(jsonObject.computer, 'cpuFreq', -1)
root.cpuFrequency = getPropertyWithDefault(jsonObject.computer, "cpuFreq", -1)
root.nbReads = categories[0].length-1
@ -219,9 +218,9 @@ Item {
function initRamChart() {
var ram = getPropertyWithDefault(jsonObject.computer.curves, 'ramUsage', -1)
var ram = getPropertyWithDefault(jsonObject.computer.curves, "ramUsage", -1)
root.ramTotal = getPropertyWithDefault(jsonObject.computer, 'ramTotal', -1)
root.ramTotal = getPropertyWithDefault(jsonObject.computer, "ramTotal", -1)
root.ramLabel = "RAM: "
if (root.ramTotal <= 0) {
var maxRamPeak = 0
@ -249,17 +248,18 @@ Item {
ramSerie.color = colors[10]
}
/**************************
*** GPU ***
**************************/
function initGpuChart() {
root.gpuTotalMemory = getPropertyWithDefault(jsonObject.computer, 'gpuMemoryTotal', 0)
root.gpuName = getPropertyWithDefault(jsonObject.computer, 'gpuName', '')
root.gpuTotalMemory = getPropertyWithDefault(jsonObject.computer, "gpuMemoryTotal", 0)
root.gpuName = getPropertyWithDefault(jsonObject.computer, "gpuName", "")
var gpuUsedMemory = getPropertyWithDefault(jsonObject.computer.curves, 'gpuMemoryUsed', 0)
var gpuUsed = getPropertyWithDefault(jsonObject.computer.curves, 'gpuUsed', 0)
var gpuTemperature = getPropertyWithDefault(jsonObject.computer.curves, 'gpuTemperature', 0)
var gpuUsedMemory = getPropertyWithDefault(jsonObject.computer.curves, "gpuMemoryUsed", 0)
var gpuUsed = getPropertyWithDefault(jsonObject.computer.curves, "gpuUsed", 0)
var gpuTemperature = getPropertyWithDefault(jsonObject.computer.curves, "gpuTemperature", 0)
var gpuUsedSerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "GPU", valueGpuX, valueGpuY)
var gpuUsedMemorySerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "Memory", valueGpuX, valueGpuY)
@ -390,7 +390,7 @@ Item {
plotAreaColor: "transparent"
titleColor: textColor
visible: (root.fileVersion > 0.0) // only visible if we have valid information
visible: (root.fileVersion > 0.0) // Only visible if we have valid information
title: "CPU: " + root.nbCores + " cores, " + root.cpuFrequency + "MHz"
ValueAxis {
@ -422,7 +422,6 @@ Item {
}
/**************************
*** RAM UI ***
**************************/
@ -444,7 +443,7 @@ Item {
plotAreaColor: "transparent"
titleColor: textColor
visible: (root.fileVersion > 0.0) // only visible if we have valid information
visible: (root.fileVersion > 0.0) // Only visible if we have valid information
title: root.ramLabel + root.ramTotal + "GB"
ValueAxis {
@ -476,7 +475,6 @@ Item {
}
/**************************
*** GPU UI ***
**************************/

View file

@ -261,7 +261,6 @@ Item {
radius: 3
border.width: 2
border.color: chunkList.node === uigraph.selectedNode ? Colors.sysPalette.text : Colors.getChunkColor(object, {"NONE": bgColor})
}
MouseArea {

View file

@ -1,4 +1,3 @@
var statusColors = {
"NONE": "transparent",
"SUBMITTED": "#009688",

View file

@ -3,14 +3,17 @@ import QtQuick
import MaterialIcons 2.2
import Utils 1.0
/**
* ImageBadge is a preset MaterialLabel to display an icon bagdge on an image.
*/
MaterialLabel {
id: root
font.pointSize: 10
padding: 2
background: Rectangle { color: Colors.sysPalette.window; opacity: 0.6 }
background: Rectangle {
color: Colors.sysPalette.window
opacity: 0.6
}
}

View file

@ -4,10 +4,10 @@ import QtQuick.Layouts
import Utils 1.0
/**
* ImageDelegate for a Viewpoint object.
*/
Item {
id: root
@ -25,7 +25,7 @@ Item {
default property alias children: imageMA.children
// retrieve viewpoints inner data
// Retrieve viewpoints inner data
QtObject {
id: _viewpoint
property url source: viewpoint ? Filepath.stringToUrl(viewpoint.get("path").value) : ''
@ -34,8 +34,8 @@ Item {
property var metadata: metadataStr ? JSON.parse(viewpoint.get("metadata").value) : {}
}
// update thumbnail location
// can be called from the GridView when a new thumbnail has been written on disk
// Update thumbnail location
// Can be called from the GridView when a new thumbnail has been written on disk
function updateThumbnail() {
thumbnail.source = ThumbnailCache.thumbnail(root.source, root.cellID)
}
@ -136,6 +136,7 @@ Item {
running: thumbnail.status != Image.Ready
}
}
// Image basename
Label {
id: imageLabel

View file

@ -12,6 +12,7 @@ import Utils 1.0
* ImageGallery displays as a grid of Images a model containing Viewpoints objects.
* It manages a model of multiple CameraInit nodes as individual groups.
*/
Panel {
id: root
@ -28,7 +29,7 @@ Panel {
property int defaultCellSize: 160
property bool readOnly: false
property var filesByType: {}
property var filesByType: ({})
property int nbMeshroomScenes: 0
property int nbDraggedFiles: 0
@ -98,7 +99,7 @@ Panel {
intrinsic[currentAttribute.name + "." + currentAttribute.value.at(k).name] = currentAttribute.value.at(k)
}
} else if (currentAttribute.type === "ListAttribute") {
// not needed for now
// Not needed for now
} else {
intrinsic[currentAttribute.name] = currentAttribute
}
@ -217,12 +218,12 @@ Panel {
Connections {
target: ThumbnailCache
function onThumbnailCreated(imgSource, callerID) {
let item = grid.itemAtIndex(callerID); // item is an ImageDelegate
let item = grid.itemAtIndex(callerID); // "item" is an ImageDelegate
if (item && item.source === imgSource) {
item.updateThumbnail()
return
}
// fallback in case the ImageDelegate cellID changed
// Fallback in case the ImageDelegate cellID changed
for (let idx = 0; idx < grid.count; idx++) {
item = grid.itemAtIndex(idx)
if (item && item.source === imgSource) {
@ -250,7 +251,7 @@ Panel {
]
property var reconstructionFilter: undefined
// 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_) {
var roleNameAndCmd = roleName_.split(".")
var roleName = roleName_
@ -831,8 +832,7 @@ Panel {
}
}
onEnabledChanged: {
// Reset the toggle to avoid getting stuck
// with the HDR node checked but disabled.
// Reset the toggle to avoid getting stuck with the HDR node checked but disabled
if (checked) {
checked = false
close()
@ -875,8 +875,7 @@ Panel {
}
}
onEnabledChanged: {
// Reset the toggle to avoid getting stuck
// with the HDR node checked but disabled.
// Reset the toggle to avoid getting stuck with the HDR node checked but disabled
if (checked) {
checked = false
close()

View file

@ -190,7 +190,7 @@ RowLayout {
DoubleValidator {
id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding the system locale
locale: 'C' // Use '.' decimal separator disregarding the system locale
}
validator: doubleValidator

View file

@ -4,11 +4,11 @@ import QtQuick.Controls
import MaterialIcons 2.2
import Utils 1.0
/**
* Display camera initialization status and the value of metadata
* that take part in this process.
*/
ImageBadge {
id: root
@ -28,7 +28,8 @@ ImageBadge {
}
return ""
}
// access useful metadata
// Access useful metadata
readonly property var make: findMetadata("Make")
readonly property var model: findMetadata("Model")
readonly property var focalLength: findMetadata("FocalLength")
@ -104,13 +105,12 @@ ImageBadge {
}
},
State {
// fallback status when initialization mode is unset
// Fallback status when initialization mode is unset
name: "none"
PropertyChanges {
target: root
visible: false
}
}
]
}

View file

@ -5,7 +5,6 @@ import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0
MessageDialog {
id: root

View file

@ -3,13 +3,14 @@ import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
import Qt.labs.platform 1.0 as Platform // for FileDialog
import Qt.labs.platform 1.0 as Platform
import Controls 1.0
/**
* LiveSfMView provides controls for setting up and starting a live reconstruction.
*/
Panel {
id: root

View file

@ -1,11 +1,11 @@
import QtQuick
import QtQuick.Controls
/**
* MLabel is a standard Label.
* If ToolTip.text is set, it shows up a tooltip when hovered.
*/
Label {
padding: 4
MouseArea {

View file

@ -1,11 +1,11 @@
import QtQuick
import QtQuick.Controls
/**
* MaterialLabel is a standard Label using MaterialIcons font.
* If ToolTip.text is set, it also shows up a tooltip when hovered.
*/
Label {
font.family: MaterialIcons.fontFamily
font.pointSize: 10

View file

@ -2,11 +2,11 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
/**
* MaterialToolButton is a standard ToolButton using MaterialIcons font.
* It also shows up its tooltip when hovered.
*/
ToolButton {
id: control
font.family: MaterialIcons.fontFamily

View file

@ -2,11 +2,11 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
/**
* MaterialToolLabel is a Label with an icon (using MaterialIcons).
* It shows up its tooltip when hovered.
*/
Item {
id: control
property alias iconText: iconItem.text

View file

@ -2,11 +2,11 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
/**
* MaterialToolButton is a standard ToolButton using MaterialIcons font.
* It also shows up its tooltip when hovered.
*/
ToolButton {
id: control
property alias iconText: icon.text

View file

@ -4,6 +4,7 @@ import Meshroom.Helpers 1.0
/**
* Clipboard singleton object to copy values to paste buffer.
*/
ClipboardHelper {
}

View file

@ -5,6 +5,7 @@ import QtQuick.Controls
/**
* Singleton that gathers useful colors, shades and system palettes.
*/
QtObject {
property SystemPalette sysPalette: SystemPalette {}
property SystemPalette disabledSysPalette: SystemPalette { colorGroup: SystemPalette.Disabled }

View file

@ -2,5 +2,4 @@ pragma Singleton
import Meshroom.Helpers 1.0
Scene3DHelper {
}

View file

@ -17,21 +17,21 @@ import QtQuick.Controls
*
* Based on http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview4-example.html
*/
DelegateModel {
id: sortFilterModel
property string sortRole: "" /// the role to use for sorting
property int sortOrder: Qt.AscendingOrder /// the sorting order
property var filters: [] /// filter format: {role: "roleName", value: "filteringValue"}
property string sortRole: "" /// The role to use for sorting
property int sortOrder: Qt.AscendingOrder /// The sorting order
property var filters: [] /// Filter format: {role: "roleName", value: "filteringValue"}
onSortRoleChanged: invalidateSort()
onSortOrderChanged: invalidateSort()
onFiltersChanged: invalidateFilters()
// display "filtered" group
// Display "filtered" group
filterOnGroup: "filtered"
// don't include elements in "items" group by default
// as they must fall in the "unsorted" group
// Don't include elements in "items" group by default as they must fall in the "unsorted" group
items.includeByDefault: false
groups: [
@ -41,15 +41,15 @@ DelegateModel {
name: "unsorted"
includeByDefault: true
// if the source model changes, perform sorting and filtering
// If the source model changes, perform sorting and filtering
onChanged: {
// no sorting: move everything from unsorted to sorted group
// No sorting: move everything from unsorted to sorted group
if(sortRole == "") {
unsortedItems.setGroups(0, unsortedItems.count, ["items"])
} else {
sort()
}
// perform filter invalidation in both cases
// Perform filter invalidation in both cases
invalidateFilters()
}
},
@ -87,8 +87,7 @@ DelegateModel {
if (filter === undefined) {
return true;
}
switch (value.constructor.name)
{
switch (value.constructor.name) {
case "String":
return value.toLowerCase().indexOf(filter.toLowerCase()) > -1
default:
@ -98,8 +97,8 @@ DelegateModel {
/// Apply respectFilter mapping and logical AND/OR reduction on filters
function respectFilters(item) {
let cond = (filter => respectFilter(modelData(item, filter.role), filter.value));
return filters.every(x => Array.isArray(x) ? x.some(cond) : cond(x));
let cond = (filter => respectFilter(modelData(item, filter.role), filter.value))
return filters.every(x => Array.isArray(x) ? x.some(cond) : cond(x))
}
/// Reverse sort order (toggle between Qt.AscendingOrder / Qt.DescendingOrder)
@ -108,40 +107,42 @@ DelegateModel {
}
property var lessThan: [
function(left, right) { return modelData(left, sortRole) < modelData(right, sortRole) }
function(left, right) {
return modelData(left, sortRole) < modelData(right, sortRole)
}
]
function invalidateSort() {
if (!sortFilterModel.model || !sortFilterModel.model.count)
return;
// move everything from "items" to "unsorted
// will trigger "unsorted" DelegateModelGroup 'changed' signal
// Move everything from "items" to "unsorted", will trigger "unsorted" DelegateModelGroup 'changed' signal
items.setGroups(0, items.count, ["unsorted"])
}
/// Invalidate filtering
function invalidateFilters() {
for (var i = 0; i < items.count; ++i) {
// if the property value contains filterText, add it to the filtered group
// If the property value contains filterText, add it to the filtered group
if (respectFilters(items.get(i))) {
items.addGroups(items.get(i), 1, "filtered")
} else { // otherwise, remove it from the filtered group
} else { // Otherwise, remove it from the filtered group
items.removeGroups(items.get(i), 1, "filtered")
}
}
}
/// Compute insert position of 'item' based on the value
/// of its sortProperty
/// Compute insert position of 'item' based on the value of its sortProperty
function insertPosition(lessThan, item) {
var lower = 0
var upper = items.count
while (lower < upper) {
var middle = Math.floor(lower + (upper - lower) / 2)
var result = lessThan(item, items.get(middle))
if (sortOrder == Qt.DescendingOrder)
if (sortOrder == Qt.DescendingOrder) {
result = !result
}
if (result) {
upper = middle
} else {
@ -159,7 +160,7 @@ DelegateModel {
item.groups = ["items"]
items.move(item.itemsIndex, index)
}
// if some items were actually sorted, filter will be correctly invalidated
// If some items were actually sorted, filter will be correctly invalidated
// as unsortedGroup 'changed' signal will be triggered
}
}

View file

@ -2,5 +2,4 @@ pragma Singleton
import Meshroom.Helpers 1.0
Transformations3DHelper {
}

View file

@ -1,8 +1,7 @@
.pragma library
function intToString(v) {
// use EN locale to get comma separated thousands
// Use EN locale to get comma separated thousands
// + remove automatically added trailing decimals
// (this 'toLocaleString' does not take any option)
return v.toLocaleString(Qt.locale('en-US')).split('.')[0]
@ -10,8 +9,8 @@ function intToString(v) {
// Convert a plain text to an html escaped string.
function plainToHtml(t) {
var escaped = t.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') // escape text
return escaped.replace(/\n/g, '<br>') // replace line breaks
var escaped = t.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') // Escape text
return escaped.replace(/\n/g, '<br>') // Replace line breaks
}
function divmod(x, y) {
@ -41,8 +40,7 @@ function sec2timecode(timeSeconds) {
}
function sec2timeStr(timeSeconds) {
// Need to decide the rounding precision first
// to propagate the right values
// Need to decide the rounding precision first to propagate the right values
if (timeSeconds >= 60.0) {
timeSeconds = Math.round(timeSeconds)
} else {
@ -57,7 +55,7 @@ function sec2timeStr(timeSeconds) {
timeStr += timeObj.minutes + "m"
}
if (timeObj.hours === 0) {
// seconds only matter if the elapsed time is less than 1 hour
// Seconds only matter if the elapsed time is less than 1 hour
if (timeObj.minutes === 0) {
// If less than a minute, keep millisecond precision
timeStr += timeObj.seconds.toFixed(2) + "s"

View file

@ -31,7 +31,7 @@ FloatingPane {
onWheel: {}
}
// note: We need to use csvData.getNbColumns() slot instead of the csvData.nbColumns property to avoid a crash on linux.
// Note: We need to use csvData.getNbColumns() slot instead of the csvData.nbColumns property to avoid a crash on linux.
property bool crfReady: csvData && csvData.ready && (csvData.getNbColumns() >= 4)
onCrfReadyChanged: {
if (crfReady) {
@ -77,24 +77,24 @@ FloatingPane {
}
// We cannot use a Repeater with these Components so we need to instantiate them one by one
// Red curve
LineSeries {
// Red curve
id: redCurve
axisX: valueAxisX
axisY: valueAxisY
name: crfReady ? csvData.getColumn(1).title : ""
color: name.toLowerCase()
}
// Green curve
LineSeries {
// Green curve
id: greenCurve
axisX: valueAxisX
axisY: valueAxisY
name: crfReady ? csvData.getColumn(2).title : ""
color: name.toLowerCase()
}
// Blue curve
LineSeries {
// Blue curve
id: blueCurve
axisX: valueAxisX
axisY: valueAxisY

View file

@ -3,9 +3,9 @@ import QtQuick
Item {
id: root
// required for perspective transform
property real sizeX: 1675.0 // might be overridden in ColorCheckerViewer
property real sizeY: 1125.0 // might be overridden in ColorCheckerViewer
// Required for perspective transform
property real sizeX: 1675.0 // Might be overridden in ColorCheckerViewer
property real sizeY: 1125.0 // Might be overridden in ColorCheckerViewer
property var colors: null
property var window: null
@ -27,10 +27,8 @@ Item {
id: transformation
matrix: Qt.matrix4x4()
}
}
function applyTransform(m) {
transformation.matrix = Qt.matrix4x4(
m[0][0], m[0][1], 0, m[0][2],
@ -38,5 +36,4 @@ Item {
0, 0, 1, 0,
m[2][0], m[2][1], 0, m[2][2])
}
}

View file

@ -12,7 +12,6 @@ FloatingPane {
padding: 4
anchors.rightMargin: 0
ColumnLayout {
anchors.fill: parent
@ -34,9 +33,7 @@ FloatingPane {
height: root.height / grid.rows - grid.spacing * (grid.rows + 1) / grid.rows
color: Qt.rgba(modelData.r, modelData.g, modelData.b, 1.0)
}
}
}
}
}

View file

@ -8,9 +8,9 @@ Item {
property var viewpoint: null
property real zoom: 1.0
// required for perspective transform
// Required for perspective transform
// Match theoretical values in AliceVision
// see https://github.com/alicevision/AliceVision/blob/68ab70bcbc3eb01b73dc8dea78c78d8b4778461c/src/software/utils/main_colorCheckerDetection.cpp#L47
// See https://github.com/alicevision/AliceVision/blob/68ab70bcbc3eb01b73dc8dea78c78d8b4778461c/src/software/utils/main_colorCheckerDetection.cpp#L47
readonly property real ccheckerSizeX: 1675.0
readonly property real ccheckerSizeY: 1125.0
@ -34,16 +34,13 @@ Item {
function readSourceFile() {
var xhr = new XMLHttpRequest
// console.warn("readSourceFile: " + root.source)
xhr.open("GET", root.source)
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) {
try {
root.json = null
// console.warn("readSourceFile: update json from " + root.source)
root.json = JSON.parse(xhr.responseText)
// console.warn("readSourceFile: root.json.checkers.length=" + root.json.checkers.length)
} catch(exc) {
console.warn("Failed to parse ColorCheckerDetection JSON file: " + source)
return

View file

@ -10,6 +10,7 @@ import Utils 1.0
* FeaturesInfoOverlay is an overlay that displays info and
* provides controls over a FeaturesViewer component.
*/
FloatingPane {
id: root
@ -65,7 +66,10 @@ FloatingPane {
ComboBox {
id: featureDisplayModeCB
flat: true
ToolTip.text: "Feature Display Mode:\n* Points: Simple points.\n* Square: Scaled filled squares.\n* Oriented Square: Scaled and oriented squares."
ToolTip.text: "Feature Display Mode:\n" +
"* Points: Simple points.\n" +
"* Square: Scaled filled squares.\n" +
"* Oriented Square: Scaled and oriented squares."
ToolTip.visible: hovered
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight
@ -81,7 +85,10 @@ FloatingPane {
ComboBox {
id: trackDisplayModeCB
flat: true
ToolTip.text: "Track Display Mode:\n* Lines Only: Only track lines.\n* Current Matches: Track lines with current matches / landmarks.\n* All Matches: Track lines with all matches / landmarks."
ToolTip.text: "Track Display Mode:\n" +
"* Lines Only: Only track lines.\n" +
"* Current Matches: Track lines with current matches/landmarks.\n" +
"* All Matches: Track lines with all matches / landmarks."
ToolTip.visible: hovered
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight
@ -152,7 +159,8 @@ FloatingPane {
}
SpinBox {
id: timeWindowSB
ToolTip.text: "Time Window: The number of frames to consider for tracks display.\ne.g. With time window set at x, tracks will start at current frame - x and they will end at current frame + x."
ToolTip.text: "Time Window: The number of frames to consider for tracks display.\n" +
"e.g. With time window set at x, tracks will start at current frame - x and they will end at current frame + x."
ToolTip.visible: hovered
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight

View file

@ -7,6 +7,7 @@ import Utils 1.0
* FeaturesViewer displays the extracted feature points of a View.
* Requires QtAliceVision plugin.
*/
Repeater {
id: root
@ -60,7 +61,7 @@ Repeater {
model: root.describerTypes
// instantiate one FeaturesViewer by describer type
// Instantiate one FeaturesViewer by describer type
delegate: AliceVision.FeaturesViewer {
readonly property int colorIndex: (index + colorOffset) % root.colors.length
property int colorOffset: 0

View file

@ -96,7 +96,6 @@ AliceVision.FloatImageViewer {
if (!isHighlightable) root.surface.mouseOver = false
}
/*
* Principal Point
*/

View file

@ -30,19 +30,18 @@ FloatingPane {
DoubleValidator {
id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale
locale: 'C' // Use '.' decimal separator disregarding of the system locale
}
RowLayout {
id: toolLayout
// anchors.verticalCenter: parent
anchors.fill: parent
// channel mode
// Channel mode
ComboBox {
id: channelsCtrl
// set min size to 4 characters + one margin for the combobox
// Set min size to 4 characters + one margin for the combobox
Layout.minimumWidth: 5.0 * Qt.application.font.pixelSize
Layout.preferredWidth: Layout.minimumWidth
flat: true
@ -57,7 +56,7 @@ FloatingPane {
model: channels
}
// gain slider
// Gain slider
RowLayout {
spacing: 5
@ -97,7 +96,7 @@ FloatingPane {
}
}
// gamma slider
// Gamma slider
RowLayout {
spacing: 5
@ -242,7 +241,7 @@ FloatingPane {
TextMetrics {
id: textMetrics_colorValue
font: red.font
text: "1.2345" // use one more than expected to get the correct value (probably needed due to TextField margin)
text: "1.2345" // Use one more than expected to get the correct value (probably needed due to TextField margin)
}
TextMetrics {
id: textMetrics_gainValue

View file

@ -12,6 +12,7 @@ import Utils 1.0
/**
* ImageMetadataView displays a JSON model representing an image's metadata as a ListView.
*/
FloatingPane {
id: root
@ -94,7 +95,7 @@ FloatingPane {
entry["raw"] = entry["group"] + ":" + entry["key"] + "=" + entry["value"]
entries.push(entry)
}
// reset the model with prepared data (limit to one update event)
// Reset the model with prepared data (limit to one update event)
metadataModel.append(entries)
coordinates = getGPSCoordinates(metadata)
}

View file

@ -31,7 +31,7 @@ FloatingPane {
DoubleValidator {
id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale
locale: 'C' // Use '.' decimal separator disregarding of the system locale
}
RowLayout {
@ -148,12 +148,11 @@ FloatingPane {
}
}
//Fill rectangle to have a better UI
// Fill rectangle to have a better UI
Rectangle {
color: root.palette.window
Layout.fillWidth: true
}
}
TextMetrics {

View file

@ -32,7 +32,7 @@ FloatingPane {
DoubleValidator {
id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale
locale: 'C' // Use '.' decimal separator disregarding of the system locale
}
RowLayout {
@ -132,7 +132,6 @@ FloatingPane {
Layout.fillWidth: false
Layout.maximumWidth: 50
validator: DoubleValidator {
bottom: Math.min(speedSpinBox.from, speedSpinBox.to)
top: Math.max(speedSpinBox.from, speedSpinBox.to)
@ -165,7 +164,6 @@ FloatingPane {
Layout.fillWidth: false
Layout.maximumWidth: 50
validator: DoubleValidator {
bottom: Math.min(downscaleSpinBox.from, downscaleSpinBox.to)
top: Math.max(downscaleSpinBox.from, downscaleSpinBox.to)
@ -180,8 +178,8 @@ FloatingPane {
}
}
}
}
TextMetrics {
id: textMetrics_subdivisionsValue
font: subdivisionsLabel.font

View file

@ -73,7 +73,7 @@ AliceVision.PanoramaViewer {
property double pitchNode: activeNode ? activeNode.attribute("manualTransform.manualRotation.x").value : 0
property double rollNode: activeNode ? activeNode.attribute("manualTransform.manualRotation.z").value : 0
//Convert angle functions
// Convert angle functions
function toDegrees(radians) {
return radians * (180 / Math.PI)
}
@ -82,12 +82,16 @@ AliceVision.PanoramaViewer {
return degrees * (Math.PI / 180)
}
function fmod(a,b) { return Number((a - (Math.floor(a / b) * b)).toPrecision(8)) }
function fmod(a,b) {
return Number((a - (Math.floor(a / b) * b)).toPrecision(8))
}
// Limit angle between -180 and 180
function limitAngle(angle) {
if (angle > 180) angle = -180.0 + (angle - 180.0)
if (angle < -180) angle = 180.0 - (Math.abs(angle) - 180)
if (angle > 180)
angle = -180.0 + (angle - 180.0)
if (angle < -180)
angle = 180.0 - (Math.abs(angle) - 180)
return angle
}
@ -273,33 +277,31 @@ AliceVision.PanoramaViewer {
var sourceItem = Filepath.stringToUrl(msfmData.getUrlFromViewId(idViewItem))
setSource("FloatImage.qml", {
'surface.viewerType': AliceVision.Surface.EViewerType.PANORAMA,
'viewerTypeString': 'panorama',
'surface.subdivisions': Qt.binding(function() { return subdivisionsPano }),
'cropFisheye' : Qt.binding(function(){ return cropFisheyePano }),
'surface.pitch': Qt.binding(function() { return root.pitch }),
'surface.yaw': Qt.binding(function() { return root.yaw }),
'surface.roll': Qt.binding(function() { return root.roll }),
'idView': Qt.binding(function() { return idViewItem }),
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue }),
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue }),
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue }),
'downscaleLevel' : Qt.binding(function() { return downscale }),
'source': Qt.binding(function() { return sourceItem }),
'surface.msfmData': Qt.binding(function() { return root.msfmData }),
'canBeHovered': true,
'useSequence': false
"surface.viewerType": AliceVision.Surface.EViewerType.PANORAMA,
"viewerTypeString": "panorama",
"surface.subdivisions": Qt.binding(function() { return subdivisionsPano }),
"cropFisheye" : Qt.binding(function(){ return cropFisheyePano }),
"surface.pitch": Qt.binding(function() { return root.pitch }),
"surface.yaw": Qt.binding(function() { return root.yaw }),
"surface.roll": Qt.binding(function() { return root.roll }),
"idView": Qt.binding(function() { return idViewItem }),
"gamma": Qt.binding(function() { return hdrImageToolbar.gammaValue }),
"gain": Qt.binding(function() { return hdrImageToolbar.gainValue }),
"channelModeString": Qt.binding(function() { return hdrImageToolbar.channelModeValue }),
"downscaleLevel": Qt.binding(function() { return downscale }),
"source": Qt.binding(function() { return sourceItem }),
"surface.msfmData": Qt.binding(function() { return root.msfmData }),
"canBeHovered": true,
"useSequence": false
})
imageLoaded = Qt.binding(function() { return repeater.itemAt(index).item.imageStatus === Image.Ready ? true : false })
}
}
}
Repeater {
id: repeater
model: 0
delegate: imgPano
}
Connections {
target: root
@ -309,7 +311,7 @@ AliceVision.PanoramaViewer {
// Retrieve downscale value from C++
panoramaViewerToolbar.updateDownscaleValue(root.downscale)
//Changing the repeater model (number of elements)
// Changing the repeater model (number of elements)
panoImages.updateRepeater()
root.readyToLoad = Image.Ready

View file

@ -100,7 +100,6 @@ FloatingPane {
anchors.leftMargin: residualsPerViewChart.width * 0.25
RowLayout {
ChartViewCheckBox {
id: allObservations
text: "ALL"
@ -198,7 +197,6 @@ FloatingPane {
anchors.leftMargin: observationsLengthsPerViewChart.width * 0.25
RowLayout {
ChartViewCheckBox {
id: allModes
text: "ALL"

View file

@ -145,7 +145,6 @@ FloatingPane {
anchors.leftMargin: observationsLengthsChart.width * 0.25
RowLayout {
ChartViewCheckBox {
id: allObservations
text: "ALL"
@ -213,7 +212,6 @@ FloatingPane {
anchors.leftMargin: observationsScaleChart.width * 0.15
RowLayout {
ChartViewCheckBox {
id: allObservationsScales
text: "ALL"

View file

@ -5,6 +5,7 @@ import AliceVision 1.0
/**
* To evaluate if the QtAliceVision plugin is available.
*/
Item {
id: root
}

View file

@ -123,11 +123,13 @@ FocusScope {
anchors.fill: parent
property double factor: 1.2
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onPressed: function(mouse) {
imgContainer.forceActiveFocus()
if (mouse.button & Qt.MiddleButton || (mouse.button & Qt.LeftButton && mouse.modifiers & Qt.ShiftModifier))
drag.target = imgContainer // Start drag
}
onReleased: function(mouse) {
drag.target = undefined // Stop drag
if (mouse.button & Qt.RightButton) {
@ -137,6 +139,7 @@ FocusScope {
menu.open()
}
}
onWheel: function(wheel) {
var zoomFactor = wheel.angleDelta.y > 0 ? factor : 1 / factor
@ -184,17 +187,17 @@ FocusScope {
function tryLoadNode(node) {
useExternal = false
// safety check
// Safety check
if (!node) {
return false
}
// node must be computed or at least running
// Node must be computed or at least running
if (node.isComputable && !node.isPartiallyFinished()) {
return false
}
// node must have at least one output attribute with the image semantic
// Node must have at least one output attribute with the image semantic
if (!node.hasImageOutput && !node.hasSequenceOutput) {
return false
}
@ -212,7 +215,6 @@ FocusScope {
function getViewpoint(viewId) {
// Get viewpoint from cameraInit with matching id
// This requires to loop over all viewpoints
for (var i = 0; i < _reconstruction.viewpoints.count; i++) {
var vp = _reconstruction.viewpoints.at(i)
if (vp.childAttribute("viewId").value == viewId) {
@ -264,11 +266,11 @@ FocusScope {
if (displayedNode && displayedNode.hasSequenceOutput && displayedAttr) {
// reset current frame to 0 if it is imageList but not sequence
// Reset current frame to 0 if it is imageList but not sequence
if (displayedAttr.desc.semantic === "imageList") {
let includesSeqMissingFiles = false // list only the existing files
let [_, filesSeqs] = Filepath.resolveSequence(pathTemplate, includesSeqMissingFiles)
// concat in one array all sequences in resolved
// Concat in one array all sequences in resolved
outputFiles = [].concat.apply([], filesSeqs)
let newFrameRange = [0, outputFiles.length - 1]
@ -287,7 +289,7 @@ FocusScope {
let [frameRanges, filesSeqs] = Filepath.resolveSequence(pathTemplate, includesSeqMissingFiles)
let newFrameRange = [0, 0]
if (filesSeqs.length > 0) {
// if there is one or several sequences, take the first one
// If there is one or several sequences, take the first one
outputFiles = filesSeqs[0]
newFrameRange = frameRanges[0]
@ -321,7 +323,6 @@ FocusScope {
function getSequence() {
// Entry point for getting the current image sequence
if (useExternal) {
return []
}
@ -342,10 +343,10 @@ FocusScope {
root.source = ""
}
// update output attribute names
// Update output attribute names
var names = []
if (displayedNode) {
// store attr name for output attributes that represent images
// Store attr name for output attributes that represent images
for (var i = 0; i < displayedNode.attributes.count; i++) {
var attr = displayedNode.attributes.at(i)
if (attr.isOutput && (attr.desc.semantic === "image" || attr.desc.semantic === "sequence" ||
@ -546,28 +547,28 @@ FocusScope {
// Instantiate and initialize a FloatImage component dynamically using Loader.setSource
// Note: It does not work to use previously created component, so we re-create it with setSource.
floatImageViewerLoader.setSource("FloatImage.qml", {
'source': Qt.binding(function() { return getImageFile() }),
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue }),
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue }),
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue }),
'isPrincipalPointsDisplayed': Qt.binding(function() { return lensDistortionImageToolbar.displayPrincipalPoint }),
'surface.displayGrid': Qt.binding(function() { return lensDistortionImageToolbar.visible && lensDistortionImageToolbar.displayGrid }),
'surface.gridOpacity': Qt.binding(function() { return lensDistortionImageToolbar.opacityValue }),
'surface.gridColor': Qt.binding(function() { return lensDistortionImageToolbar.color }),
'surface.subdivisions': Qt.binding(function() { return root.useFloatImageViewer ? 1 : lensDistortionImageToolbar.subdivisionsValue }),
'viewerTypeString': Qt.binding(function() { return displayLensDistortionViewer.checked ? "distortion" : "hdr" }),
'surface.msfmData': Qt.binding(function() { return (msfmDataLoader.status === Loader.Ready && msfmDataLoader.item != null && msfmDataLoader.item.status === 2) ? msfmDataLoader.item : null }),
'canBeHovered': false,
'idView': Qt.binding(function() { return ((root.displayedNode && !root.displayedNode.hasSequenceOutput && _reconstruction) ? _reconstruction.selectedViewId : -1) }),
'cropFisheye': false,
'sequence': Qt.binding(function() { return ((root.enableSequencePlayer && (_reconstruction || (root.displayedNode && root.displayedNode.hasSequenceOutput))) ? getSequence() : []) }),
'resizeRatio': Qt.binding(function() { return floatImageViewerLoader.resizeRatio }),
'targetSize': Qt.binding(function() { return floatImageViewerLoader.targetSize }),
'useSequence': Qt.binding(function() {
"source": Qt.binding(function() { return getImageFile() }),
"gamma": Qt.binding(function() { return hdrImageToolbar.gammaValue }),
"gain": Qt.binding(function() { return hdrImageToolbar.gainValue }),
"channelModeString": Qt.binding(function() { return hdrImageToolbar.channelModeValue }),
"isPrincipalPointsDisplayed": Qt.binding(function() { return lensDistortionImageToolbar.displayPrincipalPoint }),
"surface.displayGrid": Qt.binding(function() { return lensDistortionImageToolbar.visible && lensDistortionImageToolbar.displayGrid }),
"surface.gridOpacity": Qt.binding(function() { return lensDistortionImageToolbar.opacityValue }),
"surface.gridColor": Qt.binding(function() { return lensDistortionImageToolbar.color }),
"surface.subdivisions": Qt.binding(function() { return root.useFloatImageViewer ? 1 : lensDistortionImageToolbar.subdivisionsValue }),
"viewerTypeString": Qt.binding(function() { return displayLensDistortionViewer.checked ? "distortion" : "hdr" }),
"surface.msfmData": Qt.binding(function() { return (msfmDataLoader.status === Loader.Ready && msfmDataLoader.item != null && msfmDataLoader.item.status === 2) ? msfmDataLoader.item : null }),
"canBeHovered": false,
"idView": Qt.binding(function() { return ((root.displayedNode && !root.displayedNode.hasSequenceOutput && _reconstruction) ? _reconstruction.selectedViewId : -1) }),
"cropFisheye": false,
"sequence": Qt.binding(function() { return ((root.enableSequencePlayer && (_reconstruction || (root.displayedNode && root.displayedNode.hasSequenceOutput))) ? getSequence() : []) }),
"resizeRatio": Qt.binding(function() { return floatImageViewerLoader.resizeRatio }),
"targetSize": Qt.binding(function() { return floatImageViewerLoader.targetSize }),
"useSequence": Qt.binding(function() {
return (root.enableSequencePlayer && !useExternal && (_reconstruction || (root.displayedNode && root.displayedNode.hasSequenceOutput && (displayedAttr.desc.semantic === "imageList" || displayedAttr.desc.semantic === "sequence"))))
}),
'fetchingSequence': Qt.binding(function() { return sequencePlayer.loading }),
'memoryLimit': Qt.binding(function() { return sequencePlayer.settings_SequencePlayer.maxCacheMemory }),
"fetchingSequence": Qt.binding(function() { return sequencePlayer.loading }),
"memoryLimit": Qt.binding(function() { return sequencePlayer.settings_SequencePlayer.maxCacheMemory }),
})
} else {
// Forcing the unload (instead of using Component.onCompleted to load it once and for all) is necessary since Qt 5.14
@ -588,14 +589,14 @@ FocusScope {
onActiveChanged: {
if (active) {
setSource("PanoramaViewer.qml", {
'subdivisionsPano': Qt.binding(function() { return panoramaViewerToolbar.subdivisionsValue }),
'cropFisheyePano': Qt.binding(function() { return root.cropFisheye }),
'downscale': Qt.binding(function() { return panoramaViewerToolbar.downscaleValue }),
'isEditable': Qt.binding(function() { return panoramaViewerToolbar.enableEdit }),
'isHighlightable': Qt.binding(function() { return panoramaViewerToolbar.enableHover }),
'displayGridPano': Qt.binding(function() { return panoramaViewerToolbar.displayGrid }),
'mouseMultiplier': Qt.binding(function() { return panoramaViewerToolbar.mouseSpeed }),
'msfmData': Qt.binding(function() { return (msfmDataLoader && msfmDataLoader.item && msfmDataLoader.status === Loader.Ready
"subdivisionsPano": Qt.binding(function() { return panoramaViewerToolbar.subdivisionsValue }),
"cropFisheyePano": Qt.binding(function() { return root.cropFisheye }),
"downscale": Qt.binding(function() { return panoramaViewerToolbar.downscaleValue }),
"isEditable": Qt.binding(function() { return panoramaViewerToolbar.enableEdit }),
"isHighlightable": Qt.binding(function() { return panoramaViewerToolbar.enableHover }),
"displayGridPano": Qt.binding(function() { return panoramaViewerToolbar.displayGrid }),
"mouseMultiplier": Qt.binding(function() { return panoramaViewerToolbar.mouseSpeed }),
"msfmData": Qt.binding(function() { return (msfmDataLoader && msfmDataLoader.item && msfmDataLoader.status === Loader.Ready
&& msfmDataLoader.item.status === 2) ? msfmDataLoader.item : null }),
})
} else {
@ -622,7 +623,7 @@ FocusScope {
onWidthChanged: if (status==Image.Ready) fit()
source: getImageFile()
onStatusChanged: {
// update cache source when image is loaded
// Update cache source when image is loaded
if (status === Image.Ready)
qtImageViewerCache.source = source
}
@ -655,7 +656,7 @@ FocusScope {
scale: 1.0
// FeatureViewer: display view extracted feature points
// note: requires QtAliceVision plugin - use a Loader to evaluate plugin availability at runtime
// Note: requires QtAliceVision plugin - use a Loader to evaluate plugin availability at runtime
ExifOrientedViewer {
id: featuresViewerLoader
active: displayFeatures.checked && !useExternal
@ -671,12 +672,12 @@ FocusScope {
if (active) {
// Instantiate and initialize a FeaturesViewer component dynamically using Loader.setSource
setSource("FeaturesViewer.qml", {
'model': Qt.binding(function() { return activeNode ? activeNode.attribute("describerTypes").value : "" }),
'currentViewId': Qt.binding(function() { return _reconstruction.selectedViewId }),
'features': Qt.binding(function() { return mfeaturesLoader.status === Loader.Ready ? mfeaturesLoader.item : null }),
'tracks': Qt.binding(function() { return mtracksLoader.status === Loader.Ready ? mtracksLoader.item : null }),
'sfmData': Qt.binding(function() { return msfmDataLoader.status === Loader.Ready ? msfmDataLoader.item : null }),
'syncFeaturesSelected': Qt.binding(function() { return sequencePlayer.syncFeaturesSelected }),
"model": Qt.binding(function() { return activeNode ? activeNode.attribute("describerTypes").value : "" }),
"currentViewId": Qt.binding(function() { return _reconstruction.selectedViewId }),
"features": Qt.binding(function() { return mfeaturesLoader.status === Loader.Ready ? mfeaturesLoader.item : null }),
"tracks": Qt.binding(function() { return mtracksLoader.status === Loader.Ready ? mtracksLoader.item : null }),
"sfmData": Qt.binding(function() { return msfmDataLoader.status === Loader.Ready ? msfmDataLoader.item : null }),
"syncFeaturesSelected": Qt.binding(function() { return sequencePlayer.syncFeaturesSelected }),
})
} else {
// Forcing the unload (instead of using Component.onCompleted to load it once and for all) is necessary since Qt 5.14
@ -756,7 +757,7 @@ FocusScope {
onJsonFolderChanged: {
json = null
if (activeNode.attribute("autoDetect").value) {
// auto detection enabled
// Auto detection enabled
var jsonPath = activeNode.attribute("output").value
Request.get(Filepath.stringToUrl(jsonPath), function(xhr) {
if (xhr.readyState === XMLHttpRequest.DONE) {
@ -778,25 +779,25 @@ FocusScope {
function updateGizmo() {
if (activeNode.attribute("autoDetect").value) {
// update gizmo from auto detection json file
// Update gizmo from auto detection JSON file
if (json) {
// json file found
// JSON file found
var data = json[currentViewId]
if (data && data[0]) {
// current view id found
// Current view id found
circleX = data[0].x
circleY= data[0].y
circleRadius = data[0].r
return
}
}
// no auto detection data
// No auto detection data
circleX = -1
circleY= -1
circleRadius = 0
}
else {
// update gizmo from node manual parameters
// Update gizmo from node manual parameters
circleX = nodeCircleX
circleY = nodeCircleY
circleRadius = nodeCircleRadius
@ -816,7 +817,7 @@ FocusScope {
}
// ColorCheckerViewer: display color checker detection results
// note: use a Loader to evaluate if a ColorCheckerDetection node exist and displayColorChecker checked at runtime
// Note: use a Loader to evaluate if a ColorCheckerDetection node exist and displayColorChecker checked at runtime
ExifOrientedViewer {
id: colorCheckerViewerLoader
anchors.centerIn: parent
@ -845,6 +846,7 @@ FocusScope {
ColumnLayout {
anchors.fill: parent
spacing: 0
FloatingPane {
id: imagePathToolbar
Layout.fillWidth: true
@ -856,7 +858,7 @@ FocusScope {
width: parent.width
height: childrenRect.height
// selectable filepath to source image
// Selectable filepath to source image
TextField {
padding: 0
background: Item {}
@ -869,7 +871,7 @@ FocusScope {
text: Filepath.urlToString(getImageFile())
}
// write which node is being displayed
// Write which node is being displayed
Label {
id: displayedNodeName
text: root.displayedNode ? root.displayedNode.label : ""
@ -881,7 +883,7 @@ FocusScope {
height: contentHeight
}
// button to clear currently displayed file
// Button to clear currently displayed file
MaterialToolButton {
id: clearViewerButton
text: MaterialIcons.close
@ -889,8 +891,10 @@ FocusScope {
enabled: root.displayedNode || root.useExternal
visible: root.displayedNode || root.useExternal
onClicked: {
if (root.displayedNode) root.displayedNode = null
if (root.useExternal) root.useExternal = false
if (root.displayedNode)
root.displayedNode = null
if (root.useExternal)
root.useExternal = false
}
}
}
@ -946,7 +950,7 @@ FocusScope {
}
visible: metadataCB.checked
// only load metadata model if visible
// Only load metadata model if visible
metadata: {
if (visible) {
if (root.useExternal || outputAttribute.name != "gallery") {
@ -985,13 +989,13 @@ FocusScope {
onActiveChanged: {
if (active) {
// instantiate and initialize a MFeatures component dynamically using Loader.setSource
// so it can fail safely if the c++ plugin is not available
// Instantiate and initialize a MFeatures component dynamically using Loader.setSource
// so it can fail safely if the C++ plugin is not available
setSource("MFeatures.qml", {
'describerTypes': Qt.binding(function() {
"describerTypes": Qt.binding(function() {
return activeNode ? activeNode.attribute("describerTypes").value : {}
}),
'featureFolders': Qt.binding(function() {
"featureFolders": Qt.binding(function() {
let result = []
if (activeNode) {
if (activeNode.nodeType == "FeatureExtraction" && isComputed) {
@ -1005,7 +1009,7 @@ FocusScope {
}
return result
}),
'viewIds': Qt.binding(function() {
"viewIds": Qt.binding(function() {
if (_reconstruction) {
let result = [];
for (let i = 0; i < _reconstruction.viewpoints.count; i++) {
@ -1023,6 +1027,7 @@ FocusScope {
}
}
}
Loader {
id: msfmDataLoader
@ -1060,10 +1065,10 @@ FocusScope {
onActiveChanged: {
if (active) {
// instantiate and initialize a SfmStatsView component dynamically using Loader.setSource
// Instantiate and initialize a SfmStatsView component dynamically using Loader.setSource
// so it can fail safely if the c++ plugin is not available
setSource("MSfMData.qml", {
'sfmDataPath': Qt.binding(function() { return filepath }),
"sfmDataPath": Qt.binding(function() { return filepath }),
})
} else {
// Force the unload (instead of using Component.onCompleted to load it once and for all) is necessary since Qt 5.14
@ -1071,6 +1076,7 @@ FocusScope {
}
}
}
Loader {
id: mtracksLoader
@ -1090,7 +1096,7 @@ FocusScope {
// instantiate and initialize a SfmStatsView component dynamically using Loader.setSource
// so it can fail safely if the c++ plugin is not available
setSource("MTracks.qml", {
'matchingFolders': Qt.binding(function() {
"matchingFolders": Qt.binding(function() {
let result = []
if (activeNode) {
if (activeNode.nodeType == "FeatureMatching" && isComputed) {
@ -1111,6 +1117,7 @@ FocusScope {
}
}
}
Loader {
id: sfmStatsView
anchors.fill: parent
@ -1129,6 +1136,7 @@ FocusScope {
}
}
}
Loader {
id: sfmGlobalStats
anchors.fill: parent
@ -1139,8 +1147,8 @@ FocusScope {
// (necessary since Qt 5.14, Component.onCompleted cannot be used anymore to load the data once and for all)
if (active) {
setSource("SfmGlobalStats.qml", {
'msfmData': Qt.binding(function() { return msfmDataLoader.item }),
'mTracks': Qt.binding(function() { return mtracksLoader.item }),
"msfmData": Qt.binding(function() { return msfmDataLoader.item }),
"mTracks": Qt.binding(function() { return mtracksLoader.item }),
})
} else {
@ -1148,6 +1156,7 @@ FocusScope {
}
}
}
Loader {
id: featuresOverlay
anchors {
@ -1171,7 +1180,7 @@ FocusScope {
id: ldrHdrCalibrationGraph
anchors.fill: parent
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('LdrToHdrCalibration').node : null
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("LdrToHdrCalibration").node : null
property var isEnabled: displayLdrHdrCalibrationGraph.checked && activeNode && activeNode.isComputed
active: isEnabled
@ -1183,6 +1192,7 @@ FocusScope {
}
}
}
FloatingPane {
id: bottomToolbar
padding: 4
@ -1192,9 +1202,11 @@ FocusScope {
RowLayout {
anchors.fill: parent
// zoom label
// Zoom label
MLabel {
text: ((imgContainer.image && (imgContainer.image.imageStatus === Image.Ready)) ? imgContainer.scale.toFixed(2) : "1.00") + "x"
ToolTip.text: "Zoom"
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
@ -1210,8 +1222,8 @@ FocusScope {
}
}
}
ToolTip.text: "Zoom"
}
MaterialToolButton {
id: displayAlphaBackground
ToolTip.text: "Alpha Background"
@ -1220,13 +1232,12 @@ FocusScope {
Layout.minimumWidth: 0
checkable: true
}
MaterialToolButton
{
MaterialToolButton {
id: displayHDR
ToolTip.text: "High-Dynamic-Range Image Viewer"
text: MaterialIcons.hdr_on
// larger font but smaller padding,
// so it is visually similar.
// Larger font but smaller padding, so it is visually similar
font.pointSize: 20
padding: 0
Layout.minimumWidth: 0
@ -1241,12 +1252,12 @@ FocusScope {
root.useFloatImageViewer = !root.useFloatImageViewer
}
}
MaterialToolButton {
id: displayLensDistortionViewer
property int numberChanges: 0
property bool previousChecked: false
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('sfmData').node : null
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get("sfmData").node : null
property bool isComputed: {
if (!activeNode)
return false
@ -1294,9 +1305,10 @@ FocusScope {
}
}
}
MaterialToolButton {
id: displayPanoramaViewer
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('SfMTransform').node : null
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get("SfMTransform").node : null
property bool isComputed: {
if (!activeNode)
return false
@ -1333,6 +1345,7 @@ FocusScope {
}
}
}
MaterialToolButton {
id: displayFeatures
ToolTip.text: "Display Features"
@ -1343,16 +1356,18 @@ FocusScope {
checked: false
enabled: root.aliceVisionPluginAvailable && !displayPanoramaViewer.checked && !useExternal
onEnabledChanged : {
if (useExternal) return
if (enabled == false) checked = false
if (useExternal)
return
if (enabled == false)
checked = false
}
}
MaterialToolButton {
id: displayFisheyeCircleLoader
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('PanoramaInit').node : null
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("PanoramaInit").node : null
ToolTip.text: "Display Fisheye Circle: " + (activeNode ? activeNode.label : "No Node")
text: MaterialIcons.vignette
// text: MaterialIcons.panorama_fish_eye
font.pointSize: 11
Layout.minimumWidth: 0
checkable: true
@ -1360,9 +1375,10 @@ FocusScope {
enabled: activeNode && activeNode.attribute("useFisheye").value && !displayPanoramaViewer.checked
visible: activeNode
}
MaterialToolButton {
id: displayLightingCircleLoader
property var activeNode: _reconstruction.activeNodes.get('SphereDetection').node
property var activeNode: _reconstruction.activeNodes.get("SphereDetection").node
ToolTip.text: "Display Lighting Circle: " + (activeNode ? activeNode.label : "No Node")
text: MaterialIcons.location_searching
font.pointSize: 11
@ -1372,11 +1388,12 @@ FocusScope {
enabled: activeNode
visible: activeNode
}
MaterialToolButton {
id: displayColorCheckerViewerLoader
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('ColorCheckerDetection').node : null
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get("ColorCheckerDetection").node : null
ToolTip.text: "Display Color Checker: " + (activeNode ? activeNode.label : "No Node")
text: MaterialIcons.view_comfy //view_module grid_on gradient view_comfy border_all
text: MaterialIcons.view_comfy
font.pointSize: 11
Layout.minimumWidth: 0
checkable: true
@ -1465,7 +1482,7 @@ FocusScope {
MaterialToolButton {
id: displaySfmStatsView
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('sfm').node : null
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get("sfm").node : null
property bool isComputed: activeNode && activeNode.isComputed
font.family: MaterialIcons.fontFamily
@ -1491,7 +1508,7 @@ FocusScope {
MaterialToolButton {
id: displaySfmDataGlobalStats
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get('sfm').node : null
property var activeNode: root.aliceVisionPluginAvailable && _reconstruction ? _reconstruction.activeNodes.get("sfm").node : null
property bool isComputed: activeNode && activeNode.isComputed
font.family: MaterialIcons.fontFamily
@ -1514,6 +1531,7 @@ FocusScope {
}
}
}
MaterialToolButton {
id: metadataCB
@ -1543,7 +1561,11 @@ FocusScope {
id: sequencePlayer
anchors.margins: 0
Layout.fillWidth: true
sortedViewIds: { return (root.enableSequencePlayer && (root.displayedNode && root.displayedNode.hasSequenceOutput)) ? root.sequence : (_reconstruction && _reconstruction.viewpoints.count > 0) ? buildOrderedSequence("<VIEW_ID>") : [] }
sortedViewIds: {
return (root.enableSequencePlayer && (root.displayedNode && root.displayedNode.hasSequenceOutput)) ?
root.sequence :
(_reconstruction && _reconstruction.viewpoints.count > 0) ? buildOrderedSequence("<VIEW_ID>") : []
}
viewer: floatImageViewerLoader.status === Loader.Ready ? floatImageViewerLoader.item : null
visible: root.enableSequencePlayer
enabled: root.enableSequencePlayer
@ -1556,14 +1578,14 @@ FocusScope {
// Busy indicator
BusyIndicator {
anchors.centerIn: parent
// running property binding seems broken, only dynamic binding assignment works
// Running property binding seems broken, only dynamic binding assignment works
Component.onCompleted: {
running = Qt.binding(function() {
return (root.usePanoramaViewer === true && imgContainer.image && imgContainer.image.allImagesLoaded === false)
|| (imgContainer.image && imgContainer.image.imageStatus === Image.Loading)
})
}
// disable the visibility when unused to avoid stealing the mouseEvent to the image color picker
// Disable the visibility when unused to avoid stealing the mouseEvent to the image color picker
visible: running
onVisibleChanged: {
@ -1578,10 +1600,10 @@ FocusScope {
shortcut: "R"
onTriggered: {
if (hdrImageToolbar.channelModeValue !== 'r') {
hdrImageToolbar.channelModeValue = 'r'
if (hdrImageToolbar.channelModeValue !== "r") {
hdrImageToolbar.channelModeValue = "r"
} else {
hdrImageToolbar.channelModeValue = 'rgba'
hdrImageToolbar.channelModeValue = "rgba"
}
}
}
@ -1591,10 +1613,10 @@ FocusScope {
shortcut: "G"
onTriggered: {
if (hdrImageToolbar.channelModeValue !== 'g') {
hdrImageToolbar.channelModeValue = 'g'
if (hdrImageToolbar.channelModeValue !== "g") {
hdrImageToolbar.channelModeValue = "g"
} else {
hdrImageToolbar.channelModeValue = 'rgba'
hdrImageToolbar.channelModeValue = "rgba"
}
}
}
@ -1604,10 +1626,10 @@ FocusScope {
shortcut: "B"
onTriggered: {
if (hdrImageToolbar.channelModeValue !== 'b') {
hdrImageToolbar.channelModeValue = 'b'
if (hdrImageToolbar.channelModeValue !== "b") {
hdrImageToolbar.channelModeValue = "b"
} else {
hdrImageToolbar.channelModeValue = 'rgba'
hdrImageToolbar.channelModeValue = "rgba"
}
}
}
@ -1617,10 +1639,10 @@ FocusScope {
shortcut: "A"
onTriggered: {
if (hdrImageToolbar.channelModeValue !== 'a') {
hdrImageToolbar.channelModeValue = 'a'
if (hdrImageToolbar.channelModeValue !== "a") {
hdrImageToolbar.channelModeValue = "a"
} else {
hdrImageToolbar.channelModeValue = 'rgba'
hdrImageToolbar.channelModeValue = "rgba"
}
}
}

View file

@ -106,7 +106,7 @@ Entity {
onWheel: function(wheel) {
var d = root.camera.viewCenter.minus(root.camera.position).length() // Distance between camera position and center position
var zoomPower = 0.2
var angleStep = 120 // wheel.angleDelta.y = +- 120 * number of wheel rotations
var angleStep = 120
var tz = (wheel.angleDelta.y / angleStep) * d * zoomPower // Translation to apply depending on user action (mouse wheel), bigger absolute value means we'll zoom/dezoom more
var tzThreshold = 0.001
@ -129,8 +129,7 @@ Entity {
sourceDevice: keyboardSourceDevice
property bool _pressed
// When focus is lost while pressing a key, the corresponding action
// stays active, even when it's released.
// When focus is lost while pressing a key, the corresponding action stays active, even when it's released.
// Handle this issue manually by keeping an additional _pressed state
// which is cleared when focus changes (used for 'pickingActive' property).
onFocusChanged: function(focus) {

View file

@ -4,11 +4,12 @@ import DepthMapEntity 2.1
* Support for Depth Map files (EXR) in Qt3d.
* Create this component dynamically to test for DepthMapEntity plugin availability.
*/
DepthMapEntity {
id: root
pointSize: Viewer3DSettings.pointSize * (Viewer3DSettings.fixedPointSize ? 1.0 : 0.001)
// map render modes to custom visualization modes
// Map render modes to custom visualization modes
displayMode: Viewer3DSettings.renderMode == 0 ? DepthMapEntity.Points : DepthMapEntity.Triangles
displayColor: Viewer3DSettings.renderMode == 1
}

View file

@ -3,12 +3,12 @@ import Qt3D.Core 2.6
import Qt3D.Render 2.6
import Qt3D.Extras 2.15
/**
* EnvironmentMap maps an equirectangular image on a Sphere.
* The 'position' property can be used to virually attach it to a camera
* and get the impression of an environment at an infinite distance.
*/
Entity {
id: root

View file

@ -20,30 +20,29 @@ Entity {
buffer: Buffer {
data: {
function buildGrid(first, last, offset, attribute) {
var vertexCount = (((last-first)/offset)+1)*4;
var f32 = new Float32Array(vertexCount*3);
for (var id = 0, i = first; i <= last; i += offset, id++)
{
f32[12*id] = i;
f32[12*id+1] = 0.0;
f32[12*id+2] = first;
var vertexCount = (((last - first) / offset) + 1) * 4
var f32 = new Float32Array(vertexCount * 3)
for (var id = 0, i = first; i <= last; i += offset, id++) {
f32[12 * id] = i
f32[12 * id + 1] = 0.0
f32[12 * id + 2] = first
f32[12*id+3] = i;
f32[12*id+4] = 0.0;
f32[12*id+5] = last;
f32[12 * id + 3] = i
f32[12 * id + 4] = 0.0
f32[12 * id + 5] = last
f32[12*id+6] = first;
f32[12*id+7] = 0.0;
f32[12*id+8] = i;
f32[12 * id + 6] = first
f32[12 * id + 7] = 0.0
f32[12 * id + 8] = i
f32[12*id+9] = last;
f32[12*id+10] = 0.0;
f32[12*id+11] = i;
f32[12 * id + 9] = last
f32[12 * id + 10] = 0.0
f32[12 * id + 11] = i
}
attribute.count = vertexCount;
return f32;
attribute.count = vertexCount
return f32
}
return buildGrid(-12, 12, 1, gridPosition);
return buildGrid(-12, 12, 1, gridPosition)
}
}
}

View file

@ -6,10 +6,11 @@ import QtQuick.Layouts
* It takes the principal point correction into account and handle image ratio to
* correctly fit or crop according to original image ratio and parent Item ratio.
*/
Item {
id: root
/// The url of the image to display
/// The URL of the image to display
property alias source: image.source
/// Source image ratio
property real imageRatio: 1.0
@ -53,14 +54,13 @@ Item {
smooth: false
anchors.fill: parent
visible: false
// Preserver aspect fit while display ratio is aligned with image ratio, crop otherwise
fillMode: width/height >= imageRatio ? Image.PreserveAspectFit : Image.PreserveAspectCrop
// Preserve aspect fit while display ratio is aligned with image ratio, crop otherwise
fillMode: width / height >= imageRatio ? Image.PreserveAspectFit : Image.PreserveAspectCrop
autoTransform: true
}
// Custom shader for displaying undistorted images
// with principal point correction
// Custom shader for displaying undistorted images with principal point correction
ShaderEffect {
id: shader
anchors.centerIn: parent

View file

@ -352,7 +352,7 @@ FloatingPane {
filters: [{role: "label", value: searchBar.text}]
delegate: MouseArea {
id: mediaDelegate
// add mediaLibrary.count in the binding to ensure 'entity'
// Add mediaLibrary.count in the binding to ensure 'entity'
// is re-evaluated when mediaLibrary delegates are modified
property bool loading: model.status === SceneLoader.Loading
property bool hovered: model.attribute ? (uigraph ? uigraph.hoveredNode === model.attribute.node : false) : containsMouse

View file

@ -11,6 +11,7 @@ import "Materials"
* MaterialSwitcher is an Entity that can change its parent's material
* by setting the 'mode' property.
*/
Entity {
id: root
objectName: "MaterialSwitcher"
@ -28,7 +29,7 @@ Entity {
id: m
property Material material
onMaterialChanged: {
// remove previous material(s)
// Remove previous material(s)
removeComponentsByType(parent, "Material")
Scene3DHelper.addComponent(root.parent, material)
}
@ -38,10 +39,8 @@ Entity {
{
if (!entity)
return
for (var i = 0; i < entity.components.length; ++i)
{
for (var i = 0; i < entity.components.length; ++i) {
if (entity.components[i].toString().indexOf(type) !== -1) {
//entity.components[i].enabled = false;
Scene3DHelper.removeComponent(entity, entity.components[i])
}
}

View file

@ -13,7 +13,7 @@ Material {
/// Whether to display normals instead of SH
property bool displayNormals: false
// default coefficients (uniform magenta)
// Default coefficients (uniform magenta)
readonly property var noCoeffs: [
Qt.vector3d(0.0, 0.0, 0.0),
Qt.vector3d(0.0, 0.0, 0.0),
@ -29,10 +29,11 @@ Material {
effect: SphericalHarmonicsEffect {}
onShlSourceChanged: {
if(!shlSource) {
coefficients = noCoeffs;
return;
if (!shlSource) {
coefficients = noCoeffs
return
}
Request.get(Filepath.urlToString(shlSource), function(xhr) {
if (xhr.readyState === XMLHttpRequest.DONE) {
var coeffs = []
@ -40,7 +41,8 @@ Material {
lines.forEach(function(l) {
var lineCoeffs = []
l.split(" ").forEach(function(v) {
if (v) { lineCoeffs.push(v) }
if (v)
lineCoeffs.push(v)
})
if (lineCoeffs.length == 3)
coeffs.push(Qt.vector3d(lineCoeffs[0], lineCoeffs[1], lineCoeffs[2]))

View file

@ -10,7 +10,7 @@ Entity {
/// Enable debug mode (show cache entity with a scale applied)
property bool debug: false
enabled: false // disabled entity
enabled: false // Disabled entity
components: [
Transform {
@ -58,7 +58,7 @@ Entity {
if (debug) { console.log("[cache] add: " + source) }
mediaCache[source] = object
object.parent = root
// remove oldest entry in cache
// Remove oldest entry in cache
if (currentSize() > cacheSize)
shrink()
return true
@ -72,7 +72,7 @@ Entity {
var obj = mediaCache[source]
delete mediaCache[source]
if (debug) { console.log("[cache] pop: " + source) }
// delete cached obj if file does not exist on disk anymore
// Delete cached obj if file does not exist on disk anymore
if (!Filepath.exists(source)) {
if (debug) { console.log("[cache] destroy: " + source) }
obj.destroy()

View file

@ -9,13 +9,14 @@ import Utils 1.0
* MediaLibrary is an Entity that loads and manages a list of 3D media.
* It also uses an internal cache to instantly reload media.
*/
Entity {
id: root
readonly property alias model: m.mediaModel
property int renderMode
property bool pickingEnabled: false
readonly property alias count: instantiator.count // number of instantiated media delegates
readonly property alias count: instantiator.count // Number of instantiated media delegates
// For TransformGizmo in BoundingBox
property DefaultCameraController sceneCameraController
@ -47,10 +48,10 @@ Entity {
"valid": true,
"label": "",
"visible": true,
"hasBoundingBox": false, // for Meshing node only
"displayBoundingBox": true, // for Meshing node only
"hasTransform": false, // for SfMTransform node only
"displayTransform": true, // for SfMTransform node only
"hasBoundingBox": false, // For Meshing node only
"displayBoundingBox": true, // For Meshing node only
"hasTransform": false, // For SfMTransform node only
"displayTransform": true, // For SfMTransform node only
"section": "",
"attribute": null,
"entity": null,
@ -71,7 +72,7 @@ Entity {
}
function ensureVisible(source) {
var idx = find(source);
var idx = find(source)
if (idx === -1)
return
m.mediaModel.get(idx).visible = true
@ -194,7 +195,7 @@ Entity {
// Whether MediaLoader has been fully instantiated by the NodeInstantiator
property bool fullyInstantiated: false
// explicitly store some attached model properties for outside use and ease binding
// Explicitly store some attached model properties for outside use and ease binding
readonly property var attribute: model.attribute
readonly property int idx: index
readonly property var modelSource: attribute || model.source
@ -340,7 +341,7 @@ Entity {
}
// Transform: display a TransformGizmo for SfMTransform node only
// note: use a NodeInstantiator to evaluate if the current node is a SfMTransform node and if the transform mode is set to Manual
// Note: use a NodeInstantiator to evaluate if the current node is a SfMTransform node and if the transform mode is set to Manual
NodeInstantiator {
id: sfmTransformGizmoInstantiator
active: instantiatedEntity.hasTransform
@ -362,7 +363,7 @@ Entity {
}
// BoundingBox: display bounding box for MESHING computation
// note: use a NodeInstantiator to evaluate if the current node is a MESHING node and if the checkbox is active
// Note: use a NodeInstantiator to evaluate if the current node is a MESHING node and if the checkbox is active
NodeInstantiator {
id: boundingBoxInstantiator
active: instantiatedEntity.hasBoundingBox

View file

@ -7,11 +7,11 @@ import QtQuick.Scene3D 2.6
import "Materials"
import Utils 1.0
/**
* MediaLoader provides a single entry point for 3D media loading.
* It encapsulates all available plugins/loaders.
*/
Entity {
id: root
@ -32,7 +32,7 @@ import Utils 1.0
return
}
// clear previously created object if any
// Clear previously created object if any
if (object) {
object.destroy()
object = null
@ -48,13 +48,11 @@ import Utils 1.0
switch (Filepath.extension(source)) {
case ".ply":
if ((Filepath.extension(Filepath.removeExtension(source))) == ".pc")
{
if ((Filepath.extension(Filepath.removeExtension(source))) == ".pc") {
if (Viewer3DSettings.supportSfmData)
component = sfmDataLoaderEntityComponent
}
else
{
else {
component = sceneLoaderEntityComponent
}
break
@ -108,28 +106,28 @@ import Utils 1.0
id: sfmDataLoaderEntity
Component.onCompleted: {
var obj = Viewer3DSettings.sfmDataLoaderComp.createObject(sfmDataLoaderEntity, {
'source': source,
'pointSize': Qt.binding(function() { return 0.01 * Viewer3DSettings.pointSize }),
'locatorScale': Qt.binding(function() { return Viewer3DSettings.cameraScale }),
'cameraPickingEnabled': Qt.binding(function() { return root.enabled }),
'resectionId': Qt.binding(function() { return Viewer3DSettings.resectionId }),
'displayResections': Qt.binding(function() { return Viewer3DSettings.displayResectionIds }),
'syncPickedViewId': Qt.binding(function() { return Viewer3DSettings.syncWithPickedViewId })
"source": source,
"pointSize": Qt.binding(function() { return 0.01 * Viewer3DSettings.pointSize }),
"locatorScale": Qt.binding(function() { return Viewer3DSettings.cameraScale }),
"cameraPickingEnabled": Qt.binding(function() { return root.enabled }),
"resectionId": Qt.binding(function() { return Viewer3DSettings.resectionId }),
"displayResections": Qt.binding(function() { return Viewer3DSettings.displayResectionIds }),
"syncPickedViewId": Qt.binding(function() { return Viewer3DSettings.syncWithPickedViewId })
});
obj.statusChanged.connect(function() {
if (obj.status === SceneLoader.Ready) {
for (var i = 0; i < obj.pointClouds.length; ++i) {
vertexCount += Scene3DHelper.vertexCount(obj.pointClouds[i]);
vertexCount += Scene3DHelper.vertexCount(obj.pointClouds[i])
}
cameraCount = obj.spawnCameraSelectors();
cameraCount = obj.spawnCameraSelectors()
}
Viewer3DSettings.resectionIdCount = obj.countResectionIds();
Viewer3DSettings.resectionGroups = obj.countResectionGroups(Viewer3DSettings.resectionIdCount + 1);
Viewer3DSettings.resectionIdCount = obj.countResectionIds()
Viewer3DSettings.resectionGroups = obj.countResectionGroups(Viewer3DSettings.resectionIdCount + 1)
resectionIdCount = Viewer3DSettings.resectionIdCount
resectionGroups = Viewer3DSettings.resectionGroups
resectionId = Viewer3DSettings.resectionIdCount
root.status = obj.status;
root.status = obj.status
})
obj.cameraSelected.connect(
@ -158,7 +156,7 @@ import Utils 1.0
// - [1] as a depth map
var obj = Viewer3DSettings.depthMapLoaderComp.createObject(
exrLoaderEntity, {
'source': source
"source": source
})
if (obj.status === SceneLoader.Ready) {
@ -172,8 +170,8 @@ import Utils 1.0
root.status = SceneLoader.Loading
obj = Qt.createComponent("EnvironmentMapEntity.qml").createObject(
exrLoaderEntity, {
'source': source,
'position': Qt.binding(function() { return root.camera.position })
"source": source,
"position": Qt.binding(function() { return root.camera.position })
})
obj.statusChanged.connect(function() {
root.status = obj.status;
@ -208,9 +206,9 @@ import Utils 1.0
{
var comp = entity.components[i]
// handle DiffuseMapMaterials created by SceneLoader
// Handle DiffuseMapMaterials created by SceneLoader
if (comp.toString().indexOf("QDiffuseMapMaterial") > -1) {
// store material definition
// Store material definition
var m = {
"diffuseMap": comp.diffuse.data[0].source,
"shininess": comp.shininess,
@ -224,19 +222,19 @@ import Utils 1.0
}
if (comp.toString().indexOf("QPhongMaterial") > -1) {
// create MaterialSwitcher with default colors
// Create MaterialSwitcher with default colors
mats.push({})
componentsToRemove.push(comp)
}
}
mats.forEach(function(m) {
// create a material switcher for each material definition
// Create a material switcher for each material definition
var matSwitcher = materialSwitcherComponent.createObject(entity, m)
matSwitcher.mode = Qt.binding(function(){ return root.renderMode })
matSwitcher.mode = Qt.binding(function() { return root.renderMode })
})
// remove replaced components
// Remove replaced components
componentsToRemove.forEach(function(comp) {
Scene3DHelper.removeComponent(entity, comp)
})

View file

@ -1,11 +1,11 @@
import QtQuick
import Qt3D.Core 2.6
/**
* MediaLoaderEntity provides a unified interface for accessing statistics
* of a 3D media independently from the way it was loaded.
*/
Entity {
property url source

View file

@ -9,6 +9,7 @@ import QtQuick
* Gizmo for SfMTransform node.
* Uses EntityWithGizmo wrapper because we should not instantiate TransformGizmo alone.
*/
Entity {
id: root
property DefaultCameraController sceneCameraController

View file

@ -8,13 +8,13 @@ import QtQuick.Controls
import Utils 1.0
/**
* Simple transformation gizmo entirely made with Qt3D entities.
* Uses Python Transformations3DHelper to compute matrices.
* This TransformGizmo entity should only be instantiated in EntityWithGizmo entity which is its wrapper.
* It means, to use it for a specified application, make sure to instantiate EntityWithGizmo.
*/
Entity {
id: root
property Camera camera
@ -284,7 +284,8 @@ Entity {
const gizmoToCameraVector = camera.position.toVector4d().minus(gizmoCenterPoint)
const orientation = gizmoLocalAxisVector.dotProduct(gizmoToCameraVector) > 0 ? 1 : -1
if (angle !== 0) doRelativeRotation(objectPicker.modelMatrix, pickedAxis, angle * orientation) // Do a rotation from the initial Object Model Matrix when we picked the gizmo
if (angle !== 0)
doRelativeRotation(objectPicker.modelMatrix, pickedAxis, angle * orientation) // Do a rotation from the initial Object Model Matrix when we picked the gizmo
return
}
@ -564,7 +565,7 @@ Entity {
// These three surfaces have "forward direction". The three othersurfaces have "backward direction".
NodeInstantiator {
model: 2
active: !root.uniformScale // shouldn't be active for SfmTransform Gizmo node for example
active: !root.uniformScale // Shouldn't be active for SfmTransform Gizmo node for example
Entity {

View file

@ -26,7 +26,7 @@ FocusScope {
readonly property var viewpoint: _reconstruction ? _reconstruction.selectedViewpoint : null
readonly property bool doSyncViewpointCamera: Viewer3DSettings.syncViewpointCamera && (viewpoint && viewpoint.isReconstructed)
// functions
// Functions
function resetCameraPosition() {
mainCamera.position = defaultCamPosition
mainCamera.upVector = defaultCamUpVector
@ -63,7 +63,7 @@ FocusScope {
id: scene3D
anchors.fill: parent
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio // vs. UserAspectRatio
hoverEnabled: true // if true, will trigger positionChanged events in attached MouseHandler
hoverEnabled: true // If true, will trigger positionChanged events in attached MouseHandler
aspects: ["logic", "input"]
focus: true
@ -85,9 +85,9 @@ FocusScope {
Keys.onPressed: function(event) {
if (event.key === Qt.Key_F) {
resetCameraPosition();
resetCameraPosition()
} else if (Qt.Key_1 <= event.key && event.key < Qt.Key_1 + Viewer3DSettings.renderModes.length) {
Viewer3DSettings.renderMode = event.key - Qt.Key_1;
Viewer3DSettings.renderMode = event.key - Qt.Key_1
} else {
event.accepted = false
}
@ -179,7 +179,7 @@ FocusScope {
components: [
RenderSettings {
pickingSettings.pickMethod: PickingSettings.PrimitivePicking // enables point/edge/triangle picking
pickingSettings.pickMethod: PickingSettings.PrimitivePicking // Enables point/edge/triangle picking
pickingSettings.pickResultMode: PickingSettings.NearestPick
renderPolicy: RenderSettings.Always
@ -326,7 +326,7 @@ FocusScope {
font.pointSize: 11
onClicked: Viewer3DSettings.renderMode = index
checked: Viewer3DSettings.renderMode === index
checkable: !checked // hack to disabled check on toggle
checkable: !checked // Hack to disabled check on toggle
}
}
}

View file

@ -6,22 +6,23 @@ import MaterialIcons 2.2
/**
* Viewer3DSettings singleton gathers properties related to the 3D Viewer capabilities, state and display options.
*/
Item {
readonly property Component sfmDataLoaderComp: Qt.createComponent("SfmDataLoader.qml")
readonly property bool supportSfmData: sfmDataLoaderComp.status == Component.Ready
readonly property Component depthMapLoaderComp: Qt.createComponent("DepthMapLoader.qml")
readonly property bool supportDepthMap: depthMapLoaderComp.status == Component.Ready
// supported 3D files extensions
// Supported 3D files extensions
readonly property var supportedExtensions: {
var exts = ['.obj', '.stl', '.fbx', '.gltf', '.ply'];
var exts = [".obj", ".stl", ".fbx", ".gltf", ".ply"];
if (supportSfmData) {
exts.push('.abc')
exts.push('.json')
exts.push('.sfm')
exts.push(".abc")
exts.push(".json")
exts.push(".sfm")
}
if (supportDepthMap)
exts.push('.exr')
exts.push(".exr")
return exts;
}

View file

@ -2,10 +2,10 @@ import QtQuick
import Qt3D.Core 2.6
import Qt3D.Render 2.6
/**
* ViewpointCamera sets up a Camera to match a Viewpoint's internal parameters.
*/
Entity {
id: root
@ -51,5 +51,4 @@ Entity {
}
]
}
}

View file

@ -143,8 +143,7 @@ ApplicationWindow {
// Check and return whether no local computation is in progress
function ensureNotComputing()
{
if (_reconstruction.computingLocally)
{
if (_reconstruction.computingLocally) {
// Open a warning dialog to ask for computation to be stopped
mainStack.currentItem.computingAtExitDialog.open()
return false