Merge pull request #2599 from alicevision/dev/qt6.6

Qt6.6.3 / PySide6.6.3.1 upgrade
This commit is contained in:
Fabien Castan 2024-11-22 12:39:09 +00:00 committed by GitHub
commit 66e3dd409d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
145 changed files with 2346 additions and 1702 deletions

View file

@ -1,3 +1,5 @@
# [qt6][qml] Clean-up code and harmonize comments
5a0b1c0c9547b0d00f3f10fae6994d6d8ea0b45e
# [nodes] Linting: Clean-up files
4c0409f573c2694325b104c2686a1532f95cb9bc
# Linting: Clean-up files

View file

@ -15,7 +15,7 @@ Meshroom is licensed under the [MPL2 license](LICENSE-MPL2.md).
Copyright (c) 2001-2018 Python Software Foundation.
Distributed under the [PSFL V2 license](https://www.python.org/download/releases/2.7/license/).
* __Qt/PySide2__
* __Qt/PySide6__
[https://www.qt.io](https://www.qt.io)
Copyright (C) 2018 The Qt Company Ltd and other contributors.
Distributed under the [LGPL V3 license](https://opensource.org/licenses/LGPL-3.0).

View file

@ -157,3 +157,4 @@ def setupEnvironment(backend=Backend.STANDALONE):
os.environ["QML_XHR_ALLOW_FILE_READ"] = '1'
os.environ["QML_XHR_ALLOW_FILE_WRITE"] = '1'
os.environ["PYSEQ_STRICT_PAD"] = '1'
os.environ["QSG_RHI_BACKEND"] = "opengl"

View file

@ -1,5 +1,5 @@
from PySide2 import QtCore, QtQml
import shiboken2
from PySide6 import QtCore, QtQml
import shiboken6
class QObjectListModel(QtCore.QAbstractListModel):
@ -285,7 +285,7 @@ class QObjectListModel(QtCore.QAbstractListModel):
def _dereferenceItem(self, item):
# Ask for object deletion if parented to the model
if shiboken2.isValid(item) and item.parent() == self:
if shiboken6.isValid(item) and item.parent() == self:
# delay deletion until the next event loop
# This avoids warnings when the QML engine tries to evaluate (but should not)
# an object that has already been deleted

View file

@ -116,8 +116,11 @@ class StatusData(BaseObject):
def toDict(self):
d = self.__dict__.copy()
d.pop('destroyed', None) # skip non data attributes from BaseObject
d["elapsedTimeStr"] = self.elapsedTimeStr
# Skip non data attributes from BaseObject
d.pop("destroyed", None)
d.pop("objectNameChanged", None)
return d
def fromDict(self, d):

View file

@ -39,7 +39,7 @@ class SimpleFarmSubmitter(BaseSubmitter):
# logging.info('REZ: {}'.format(str(r)))
v = r.split('-')
# logging.info(' v: {}'.format(str(v)))
if len(v) == 2:
if len(v) >= 2:
resolvedVersions[v[0]] = v[1]
for p in packages:
if p.startswith('~'):

View file

@ -10,4 +10,4 @@ import meshroom.ui
import meshroom.ui.app
meshroom.ui.uiInstance = meshroom.ui.app.MeshroomApp(sys.argv)
meshroom.ui.uiInstance.exec_()
meshroom.ui.uiInstance.exec()

View file

@ -4,10 +4,11 @@ import re
import argparse
import json
from PySide2 import QtCore
from PySide2.QtCore import Qt, QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings
from PySide2.QtGui import QIcon
from PySide2.QtWidgets import QApplication
from PySide6 import __version__ as PySideVersion
from PySide6 import QtCore
from PySide6.QtCore import Qt, QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication
import meshroom
from meshroom.core import nodesDesc
@ -188,7 +189,7 @@ class MeshroomApp(QApplication):
def __init__(self, args):
meshroom.core.initPipelines()
QtArgs = [args[0], '-style', 'fusion'] + args[1:] # force Fusion style by default
QtArgs = [args[0], '-style', 'Fusion'] + args[1:] # force Fusion style by default
args = createMeshroomParser(args)
@ -202,8 +203,6 @@ class MeshroomApp(QApplication):
}
logging.getLogger().setLevel(logStringToPython[args.verbose])
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
super(MeshroomApp, self).__init__(QtArgs)
self.setOrganizationName('AliceVision')
@ -355,6 +354,7 @@ class MeshroomApp(QApplication):
p = {"path": p, "thumbnail": thumbnail}
projects.append(p)
settings.endArray()
settings.endGroup()
return projects
@Slot(str)
@ -394,6 +394,7 @@ class MeshroomApp(QApplication):
settings.setArrayIndex(i)
settings.setValue("filepath", p)
settings.endArray()
settings.endGroup()
settings.sync()
self.recentProjectFilesChanged.emit()
@ -539,7 +540,8 @@ class MeshroomApp(QApplication):
import sys
return {
'platform': '{} {}'.format(platform.system(), platform.release()),
'python': 'Python {}'.format(sys.version.split(" ")[0])
'python': 'Python {}'.format(sys.version.split(" ")[0]),
'pyside': 'PySide6 {}'.format(PySideVersion)
}
systemInfo = Property(QJsonValue, _systemInfo, constant=True)

View file

@ -2,8 +2,8 @@ import logging
import traceback
from contextlib import contextmanager
from PySide2.QtWidgets import QUndoCommand, QUndoStack
from PySide2.QtCore import Property, Signal
from PySide6.QtGui import QUndoCommand, QUndoStack
from PySide6.QtCore import Property, Signal
from meshroom.core.attribute import ListAttribute, Attribute
from meshroom.core.graph import GraphModification

View file

@ -1,6 +1,6 @@
def registerTypes():
from PySide2.QtQml import qmlRegisterType
from PySide6.QtQml import qmlRegisterType
from meshroom.ui.components.clipboard import ClipboardHelper
from meshroom.ui.components.edge import EdgeMouseArea
from meshroom.ui.components.filepath import FilepathHelper

View file

@ -1,5 +1,5 @@
from PySide2.QtCore import Slot, QObject
from PySide2.QtGui import QClipboard
from PySide6.QtCore import Slot, QObject
from PySide6.QtGui import QClipboard
class ClipboardHelper(QObject):

View file

@ -1,7 +1,7 @@
from meshroom.common.qt import QObjectListModel
from PySide2.QtCore import QObject, Slot, Signal, Property
from PySide2.QtCharts import QtCharts
from PySide6.QtCore import QObject, Slot, Signal, Property
from PySide6 import QtCharts
import csv
import os

View file

@ -1,6 +1,6 @@
from PySide2.QtCore import Signal, Property, QPointF, Qt, QObject
from PySide2.QtGui import QPainterPath, QVector2D
from PySide2.QtQuick import QQuickItem
from PySide6.QtCore import Signal, Property, QPointF, Qt, QObject
from PySide6.QtGui import QPainterPath, QVector2D
from PySide6.QtQuick import QQuickItem
class MouseEvent(QObject):
@ -9,8 +9,8 @@ class MouseEvent(QObject):
"""
def __init__(self, evt):
super(MouseEvent, self).__init__()
self._x = evt.x()
self._y = evt.y()
self._x = evt.position().x()
self._y = evt.position().y()
self._button = evt.button()
self._modifiers = evt.modifiers()

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
# coding:utf-8
from PySide2.QtCore import QUrl, QFileInfo
from PySide2.QtCore import QObject, Slot
from PySide6.QtCore import QUrl, QFileInfo
from PySide6.QtCore import QObject, Slot
import os
import glob

View file

@ -1,9 +1,9 @@
from math import acos, pi, sqrt, atan2, cos, sin, asin
from PySide2.QtCore import QObject, Slot, QSize, Signal, QPointF
from PySide2.Qt3DCore import Qt3DCore
from PySide2.Qt3DRender import Qt3DRender
from PySide2.QtGui import QVector3D, QQuaternion, QVector2D, QVector4D, QMatrix4x4
from PySide6.QtCore import QObject, Slot, QSize, Signal, QPointF
from PySide6.Qt3DCore import Qt3DCore
from PySide6.Qt3DRender import Qt3DRender
from PySide6.QtGui import QVector3D, QQuaternion, QVector2D, QVector4D, QMatrix4x4
from meshroom.ui.utils import makeProperty
@ -41,14 +41,14 @@ class Scene3DHelper(QObject):
def faceCount(self, entity):
""" Returns face count based on children QGeometry buffers size."""
count = 0
for geo in entity.findChildren(Qt3DRender.QGeometry):
for geo in entity.findChildren(Qt3DCore.QGeometry):
count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexPosition"])
return count / 3
@Slot(Qt3DCore.QEntity, result=int)
def vertexColorCount(self, entity):
count = 0
for geo in entity.findChildren(Qt3DRender.QGeometry):
for geo in entity.findChildren(Qt3DCore.QGeometry):
count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexColor"])
return count
@ -59,10 +59,12 @@ class TrackballController(QObject):
Based on the C++ version from https://github.com/cjmdaixi/Qt3DTrackball
"""
_windowSize = QSize()
_camera = None
_trackballSize = 1.0
_rotationSpeed = 5.0
def __init__(self, parent=None):
super().__init__(parent)
self._windowSize = QSize()
self._camera = None
self._trackballSize = 1.0
self._rotationSpeed = 5.0
def projectToTrackball(self, screenCoords):
sx = screenCoords.x()
@ -98,7 +100,7 @@ class TrackballController(QObject):
windowSizeChanged = Signal()
windowSize = makeProperty(QSize, '_windowSize', windowSizeChanged)
cameraChanged = Signal()
camera = makeProperty(Qt3DRender.QCamera, '_camera', cameraChanged)
camera = makeProperty(QObject, '_camera', cameraChanged)
trackballSizeChanged = Signal()
trackballSize = makeProperty(float, '_trackballSize', trackballSizeChanged)
rotationSpeedChanged = Signal()
@ -353,6 +355,22 @@ class Transformations3DHelper(QObject):
return U.toEulerAngles()
@Slot(QVector3D, QVector3D, float, float, result=QVector3D)
def getRotatedCameraViewVector(self, camereViewVector, cameraUpVector, pitch, yaw):
""" Compute the rotated camera view vector with given pitch and yaw (in degrees).
Args:
camereViewVector (QVector3D): Camera view vector, the displacement from the camera position to its target
cameraUpVector (QVector3D): Camera up vector, the direction the top of the camera is facing
pitch (float): Rotation pitch (in degrees)
yaw (float): Rotation yaw (in degrees)
Returns:
QVector3D: rotated camera view vector
"""
cameraSideVector = QVector3D.crossProduct(camereViewVector, cameraUpVector)
yawRot = QQuaternion.fromAxisAndAngle(cameraUpVector, yaw)
pitchRot = QQuaternion.fromAxisAndAngle(cameraSideVector, pitch)
return (yawRot * pitchRot).rotatedVector(camereViewVector)
@Slot(QVector3D, QMatrix4x4, Qt3DRender.QCamera, QSize, result=float)
def computeScaleUnitFromModelMatrix(self, axis, modelMat, camera, windowSize):
""" Compute the length of the screen projected vector axis unit transformed by the model matrix.

View file

@ -1,4 +1,4 @@
from PySide2.QtCore import QObject, Slot
from PySide6.QtCore import QObject, Slot
from io import StringIO
from contextlib import redirect_stdout

View file

@ -1,7 +1,7 @@
from meshroom.common import Signal
from PySide2.QtCore import QObject, Slot, QSize, QUrl, Qt, QStandardPaths
from PySide2.QtGui import QImageReader, QImageWriter
from PySide6.QtCore import QObject, Slot, QSize, QUrl, Qt, QStandardPaths
from PySide6.QtGui import QImageReader, QImageWriter
import os
from pathlib import Path

View file

@ -8,7 +8,7 @@ from enum import Enum
from threading import Thread, Event, Lock
from multiprocessing.pool import ThreadPool
from PySide2.QtCore import Slot, QJsonValue, QObject, QUrl, Property, Signal, QPoint
from PySide6.QtCore import Slot, QJsonValue, QObject, QUrl, Property, Signal, QPoint
from meshroom.core import sessionUid
from meshroom.common.qt import QObjectListModel

View file

@ -1,6 +1,6 @@
from PySide2.QtCore import QObject, Qt, Slot, Property, Signal
from PySide2.QtGui import QPalette, QColor
from PySide2.QtWidgets import QApplication
from PySide6.QtCore import QObject, Qt, Slot, Property, Signal
from PySide6.QtGui import QPalette, QColor
from PySide6.QtWidgets import QApplication
class PaletteManager(QObject):

View file

@ -1,11 +1,14 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
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 {}
@ -56,7 +59,8 @@ Dialog {
selectByMouse: true
text: "Version " + Qt.application.version + "\n"
+ MeshroomApp.systemInfo["platform"] + " \n"
+ MeshroomApp.systemInfo["python"]
+ MeshroomApp.systemInfo["python"] + "\n"
+ MeshroomApp.systemInfo["pyside"]
}
}
@ -174,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,13 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick.Window 2.15
import QtQml.Models 2.15
import QtCore
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQml.Models
import Qt.labs.platform 1.0 as Platform
import QtQuick.Dialogs 1.3
import QtQuick.Dialogs
import Qt.labs.settings 1.0
import GraphEditor 1.0
import MaterialIcons 2.2
import Utils 1.0
@ -22,7 +22,7 @@ Page {
Settings {
id: settingsUILayout
category: 'UILayout'
category: "UILayout"
property alias showLiveReconstruction: liveSfMVisibilityCB.checked
property alias showGraphEditor: graphEditorVisibilityCB.checked
property alias showImageViewer: imageViewerVisibilityCB.checked
@ -521,8 +521,8 @@ Page {
text: "Load Template"
onTriggered: {
ensureSaved(function() {
initFileDialogFolder(loadTemplateDialog);
loadTemplateDialog.open();
initFileDialogFolder(loadTemplateDialog)
loadTemplateDialog.open()
})
}
}
@ -1158,15 +1158,15 @@ Page {
uigraph: _reconstruction
nodeTypesModel: _nodeTypes
onNodeDoubleClicked: {
onNodeDoubleClicked: function(mouse, node) {
_reconstruction.setActiveNode(node);
workspaceView.viewNode(node, mouse);
}
onComputeRequest: {
onComputeRequest: function(nodes) {
_reconstruction.forceNodesStatusUpdate();
computeManager.compute(nodes)
}
onSubmitRequest: {
onSubmitRequest: function(nodes) {
_reconstruction.forceNodesStatusUpdate();
computeManager.submit(nodes)
}

View file

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

View file

@ -1,13 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtCharts 2.15
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
@ -84,7 +83,7 @@ Flow {
MouseArea {
anchors.fill: parent
onClicked: {
onClicked: function(mouse) {
if (mouse.modifiers & Qt.ControlModifier)
root.soloSeries(index)
else

View file

@ -1,15 +1,6 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtPositioning 5.15
import QtLocation 5.15
import QtCharts 2.15
import Controls 1.0
import Utils 1.0
import QtQuick
import QtQuick.Layouts
import QtCharts
ChartView {
id: root
@ -26,32 +17,11 @@ ChartView {
MouseArea {
anchors.fill: parent
property double degreeToScale: 1.0 / 120.0 // default mouse scroll is 15 degree
property double degreeToScale: 1.0 / 120.0 // Default mouse scroll is 15 degree
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
// onWheel: {
// console.warn("root.plotArea before: " + root.plotArea)
// var zoomFactor = wheel.angleDelta.y > 0 ? 1.0 / (1.0 + wheel.angleDelta.y * degreeToScale) : (1.0 + Math.abs(wheel.angleDelta.y) * degreeToScale)
// // var mouse_screen = Qt.point(wheel.x, wheel.y)
// var mouse_screen = mapToItem(root, wheel.x, wheel.y)
// var mouse_normalized = Qt.point(mouse_screen.x / plotZone.width, mouse_screen.y / plotZone.height)
// var mouse_plot = Qt.point(mouse_normalized.x * plotZone.width, mouse_normalized.y * plotZone.height)
// // var p = mapToValue(mouse_screen, root.series(0))
// // var pMin = mapToValue(mouse_screen, Qt.point(root.axisX().min, root.axisY().min))
// // var pMax = mapToValue(mouse_screen, Qt.point(root.axisX().max, root.axisY().max))
// // console.warn("p: " + p)
// // Qt.rect()
// var r = Qt.rect(mouse_plot.x, mouse_plot.y, plotZone.width * zoomFactor, plotZone.height * zoomFactor)
// //var r = Qt.rect(pMin.x, pMin.y, (pMax.x-pMin.x) / 2, (pMax.y-pMin.y) / 2)
// root.zoomIn(r)
// }
onClicked: {
root.zoomReset()
}
}
}
}

View file

@ -1,5 +1,5 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick
import QtQuick.Controls
import Utils 1.0
@ -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

@ -0,0 +1,261 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects // for OpacityMask & Glow
import MaterialIcons 2.2
import Utils 1.0
/**
* Directional Light Pane
*
* @biref A small pane to control a directional light with a 2d ball controller.
*
* @param lightYawValue - directional light yaw (degrees)
* @param lightPitchValue - directional light pitch (degrees)
*/
FloatingPane {
id: root
// yaw and pitch properties
property double lightYawValue: 0
property double lightPitchValue: 0
// 2d controller display size properties
readonly property real controllerSize: 80
readonly property real controllerRadius: controllerSize * 0.5
function reset() {
lightYawValue = 0;
lightPitchValue = 0;
}
// update 2d controller if yaw value changed
onLightYawValueChanged: { lightBallController.update() }
// update 2d controller if pitch value changed
onLightPitchValueChanged: { lightBallController.update() }
anchors.margins: 0
padding: 5
radius: 0
ColumnLayout {
anchors.fill: parent
spacing: 5
// header
RowLayout {
// pane title
Label {
text: "Directional Light"
font.bold: true
Layout.fillWidth: true
}
// minimize or maximize button
MaterialToolButton {
id: bodyButton
text: lightPaneBody.visible ? MaterialIcons.arrow_drop_down : MaterialIcons.arrow_drop_up
font.pointSize: 10
ToolTip.text: lightPaneBody.visible ? "Minimize" : "Maximize"
onClicked: { lightPaneBody.visible = !lightPaneBody.visible }
}
// reset button
MaterialToolButton {
id: resetButton
text: MaterialIcons.refresh
font.pointSize: 10
ToolTip.text: "Reset"
onClicked: reset()
}
}
// body
RowLayout {
id: lightPaneBody
spacing: 10
// light parameters
GridLayout {
columns: 3
rowSpacing: 2
columnSpacing: 8
Layout.alignment: Qt.AlignBottom
// light yaw
Label {
text: "Yaw"
}
TextField {
id: lightYawTF
text: lightYawValue.toFixed(2)
selectByMouse: true
horizontalAlignment: TextInput.AlignRight
validator: doubleDegreeValidator
onEditingFinished: { lightYawValue = lightYawTF.text }
ToolTip.text: "Light yaw (degrees)."
ToolTip.visible: hovered
Layout.preferredWidth: textMetricsDegreeValue.width
}
Label {
text: "°"
}
// light pitch
Label {
text: "Pitch"
}
TextField {
id: lightPitchTF
text: lightPitchValue.toFixed(2)
selectByMouse: true
horizontalAlignment: TextInput.AlignRight
validator: doubleDegreeValidator
onEditingFinished: { lightPitchValue = lightPitchTF.text }
ToolTip.text: "Light pitch (degrees)."
ToolTip.visible: hovered
Layout.preferredWidth: textMetricsDegreeValue.width
}
Label {
text: "°"
}
}
// directional light ball controller
Rectangle {
id: lightBallController
anchors.margins: 0
width: controllerSize
height: controllerSize
radius: 180 // circle
color: "#FF000000"
Layout.rightMargin: 5
Layout.leftMargin: 5
Layout.bottomMargin: 5
function update() {
// get point from light yaw and pitch
var y = (lightPitchValue / 90 * controllerRadius)
var xMax = Math.sqrt(controllerRadius * controllerRadius - y * y) // get sphere maximum x coordinate
var x = (lightYawValue / 90 * xMax)
// get angle and distance
var angleRad = Math.atan2(y, x)
var distance = Math.sqrt(x * x + y * y)
// avoid controller overflow
if(distance > controllerRadius)
{
x = controllerRadius * Math.cos(angleRad)
y = controllerRadius * Math.sin(angleRad)
}
// compute distance function for light gradient emulation
var distanceRatio = Math.min(distance, controllerRadius) / controllerRadius
var distanceFunct = distanceRatio * distanceRatio * 0.3
// update light point
lightPoint.x = lightPoint.startOffset + x
lightPoint.y = lightPoint.startOffset + y
// update light gradient
lightGradient.angle = angleRad * (180 / Math.PI) // radians to degrees
lightGradient.horizontalRadius = controllerSize * (1 - distanceFunct)
lightGradient.verticalRadius = controllerSize * (1 + distanceFunct)
lightGradient.horizontalOffset = x * (1 - distanceFunct)
lightGradient.verticalOffset = y * (1 - distanceFunct)
}
RadialGradient {
id: lightGradient
anchors.centerIn: parent
width: controllerSize
height: width
horizontalRadius: controllerSize
verticalRadius: controllerSize
angle: 0
gradient: Gradient {
GradientStop { position: 0.00; color: "#FFFFFFFF" }
GradientStop { position: 0.10; color: "#FFAAAAAA" }
GradientStop { position: 0.50; color: "#FF0C0C0C" }
}
layer.enabled: true
layer.effect: OpacityMask {
id: mask
maskSource: Rectangle {
height: lightGradient.height
width: lightGradient.width
radius: 180 // circle
}
}
}
Rectangle {
id: lightPoint
property double startOffset : (parent.width - width) * 0.5
x: startOffset
y: startOffset
width: controllerRadius / 6
height: width
radius: 180 // circle
color: "white"
}
Glow {
anchors.fill: lightPoint
radius: controllerRadius / 5
samples: 17
color: "white"
source: lightPoint
}
MouseArea {
id: lightMouseArea
anchors.centerIn: parent
anchors.fill: parent
onPositionChanged: {
// get coordinates from center
var x = mouseX - controllerRadius
var y = mouseY - controllerRadius
// get distance to center
var distance = Math.sqrt(x * x + y * y)
// avoid controller overflow
if(distance > controllerRadius)
{
var angleRad = Math.atan2(y, x)
x = controllerRadius * Math.cos(angleRad)
y = controllerRadius * Math.sin(angleRad)
}
// get sphere maximum x coordinate
var xMax = Math.sqrt(controllerRadius * controllerRadius - y * y)
// update light yaw and pitch
lightYawValue = (xMax > 0) ? ((x / xMax) * 90) : 0 // between -90 and 90 degrees
lightPitchValue = (y / controllerRadius) * 90 // between -90 and 90 degrees
}
}
}
}
}
DoubleValidator {
id: doubleDegreeValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale
bottom: -90.0
top: 90.0
}
TextMetrics {
id: textMetricsDegreeValue
font: lightYawTF.font
text: "12.3456"
}
}

View file

@ -1,4 +1,5 @@
import QtQuick 2.11
import QtQuick
import Utils 1.0
/**

View file

@ -1,6 +1,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
/**

View file

@ -1,5 +1,6 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick
import QtQuick.Controls
import Utils 1.0
/**

View file

@ -1,11 +1,12 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
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

@ -1,11 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
/**
* A custom GroupBox with predefined header.
*/
GroupBox {
id: root
@ -21,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

@ -1,7 +1,8 @@
import QtQuick 2.15
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
/*
* IntSelector with arrows and a text input to select a number

View file

@ -1,11 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
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
@ -20,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
@ -46,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

@ -1,5 +1,6 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick
import QtQuick.Controls
/**
* MScrollBar is a custom scrollbar implementation.
* It is a vertical scrollbar that can be used to scroll a ListView.

View file

@ -1,5 +1,5 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick
import QtQuick.Controls
SplitView {
id: splitView

View file

@ -1,6 +1,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
Dialog {
@ -15,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.
@ -39,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

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

View file

@ -1,12 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
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
@ -35,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

@ -1,6 +1,6 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
id: root
@ -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

@ -1,14 +1,15 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
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

@ -15,3 +15,4 @@ FilterComboBox 1.0 FilterComboBox.qml
IntSelector 1.0 IntSelector.qml
MScrollBar 1.0 MScrollBar.qml
MSplitView 1.0 MSplitView.qml
DirectionalLightPane 1.0 DirectionalLightPane.qml

View file

@ -1,10 +1,10 @@
import QtQuick 2.15
import MaterialIcons 2.2
import QtQuick
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

@ -1,8 +1,6 @@
import QtQuick 2.15
import QtQuick.Layouts 1.11
import QtQuick.Controls 2.15
import MaterialIcons 2.2
import Utils 1.0
import QtQuick
import QtQuick.Controls
import Controls 1.0
/**
@ -42,7 +40,9 @@ ListView {
filterText: root.filterText
objectsHideable: root.objectsHideable
attribute: object
onDoubleClicked: root.attributeDoubleClicked(mouse, attr)
onDoubleClicked: function(mouse, attr) {
root.attributeDoubleClicked(mouse, attr)
}
}
onActiveChanged: height = active ? item.implicitHeight : -spacing
@ -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

@ -1,24 +1,26 @@
import QtQuick 2.9
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.3
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Dialogs
import MaterialIcons 2.2
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
@ -42,7 +44,7 @@ RowLayout {
Pane {
background: Rectangle {
id: background
color: object.validValue ? Qt.darker(parent.palette.window, 1.1) : Qt.darker(Colors.red, 1.5)
color: object != undefined && object.validValue ? Qt.darker(parent.palette.window, 1.1) : Qt.darker(Colors.red, 1.5)
}
padding: 0
Layout.preferredWidth: labelWidth || implicitWidth
@ -66,8 +68,10 @@ RowLayout {
text: object.label
color: {
if ((object.hasOutputConnections || object.isLink) && !object.enabled) return Colors.lightgrey
else return palette.text
if (object != undefined && (object.hasOutputConnections || object.isLink) && !object.enabled)
return Colors.lightgrey
else
return palette.text
}
// Tooltip hint with attribute's description
@ -88,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 {
@ -152,7 +156,7 @@ RowLayout {
}
}
onClicked: {
onClicked: function(mouse) {
forceActiveFocus()
if (mouse.button == Qt.RightButton) {
var menu = menuComp.createObject(parameterLabel)
@ -198,37 +202,37 @@ RowLayout {
sourceComponent: {
// PushButtonParam always has value == undefined, so it needs to be excluded from this check
if (attribute.type != "PushButtonParam" && attribute.value === undefined) {
return notComputed_component
return notComputedComponent
}
switch (attribute.type) {
case "PushButtonParam":
return pushButton_component
return pushButtonComponent
case "ChoiceParam":
return attribute.desc.exclusive ? comboBox_component : multiChoice_component
case "IntParam": return slider_component
return attribute.desc.exclusive ? comboBoxComponent : multiChoiceComponent
case "IntParam": return sliderComponent
case "FloatParam":
if (attribute.desc.semantic === 'color/hue')
return color_hue_component
return slider_component
return colorHueComponent
return sliderComponent
case "BoolParam":
return checkbox_component
return checkboxComponent
case "ListAttribute":
return listAttribute_component
return listAttributeComponent
case "GroupAttribute":
return groupAttribute_component
return groupAttributeComponent
case "StringParam":
if (attribute.desc.semantic.includes('multiline'))
return textArea_component
return textField_component
return textAreaComponent
return textFieldComponent
case "ColorParam":
return color_component
return colorComponent
default:
return textField_component
return textFieldComponent
}
}
Component {
id: notComputed_component
id: notComputedComponent
Label {
anchors.fill: parent
text: MaterialIcons.do_not_disturb_alt
@ -244,7 +248,7 @@ RowLayout {
}
Component {
id: pushButton_component
id: pushButtonComponent
Button {
text: attribute.label
enabled: root.editable
@ -255,7 +259,7 @@ RowLayout {
}
Component {
id: textField_component
id: textFieldComponent
TextField {
id: textField
readOnly: !root.editable
@ -268,7 +272,7 @@ RowLayout {
setTextFieldAttribute(text)
parameterLabel.forceActiveFocus()
}
Keys.onPressed: (event)=> {
Keys.onPressed: function(event) {
if ((event.key == Qt.Key_Escape)) {
event.accepted = true
parameterLabel.forceActiveFocus()
@ -281,7 +285,7 @@ RowLayout {
DropArea {
enabled: root.editable
anchors.fill: parent
onDropped: {
onDropped: function(drop) {
if (drop.hasUrls)
setTextFieldAttribute(Filepath.urlToString(drop.urls[0]))
else if (drop.hasText && drop.text != '')
@ -291,8 +295,8 @@ RowLayout {
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: (mouse)=> {
// Do not loose the selection during the right click
onClicked: function(mouse) {
// 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
@ -318,7 +322,7 @@ RowLayout {
Clipboard.clear()
Clipboard.setText(attribute.value)
} else {
// copy selection only
// Copy selection only
textField.copy()
}
}
@ -328,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())
@ -346,7 +350,7 @@ RowLayout {
}
Component {
id: textArea_component
id: textAreaComponent
Rectangle {
// Fixed background for the flickable object
@ -395,17 +399,20 @@ RowLayout {
}
Component {
id: color_component
id: colorComponent
RowLayout {
CheckBox {
id: color_checkbox
id: colorCheckbox
Layout.alignment: Qt.AlignLeft
checked: node && node.color === "" ? false : true
checkable: root.editable
text: "Custom Color"
onClicked: {
if (checked) {
_reconstruction.setAttribute(attribute, "#0000FF")
if (colorText.text == "")
_reconstruction.setAttribute(attribute, "#0000FF")
else
_reconstruction.setAttribute(attribute, colorText.text)
} else {
_reconstruction.setAttribute(attribute, "")
}
@ -415,9 +422,9 @@ RowLayout {
id: colorText
Layout.alignment: Qt.AlignLeft
implicitWidth: 100
enabled: color_checkbox.checked && root.editable
visible: color_checkbox.checked
text: color_checkbox.checked ? attribute.value : ""
enabled: colorCheckbox.checked && root.editable
visible: colorCheckbox.checked
text: colorCheckbox.checked ? attribute.value : ""
selectByMouse: true
onEditingFinished: setTextFieldAttribute(text)
onAccepted: setTextFieldAttribute(text)
@ -431,8 +438,8 @@ RowLayout {
height: colorText.height
width: colorText.width / 2
Layout.alignment: Qt.AlignLeft
visible: color_checkbox.checked
color: color_checkbox.checked ? attribute.value : ""
visible: colorCheckbox.checked
color: colorCheckbox.checked ? colorDialog.selectedColor : ""
MouseArea {
enabled: root.editable
@ -444,9 +451,9 @@ RowLayout {
ColorDialog {
id: colorDialog
title: "Please choose a color"
color: attribute.value
selectedColor: colorText.text
onAccepted: {
colorText.text = color
colorText.text = colorDialog.selectedColor
// Artificially trigger change of attribute value
colorText.editingFinished()
close()
@ -461,13 +468,13 @@ RowLayout {
}
Component {
id: comboBox_component
id: comboBoxComponent
FilterComboBox {
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
@ -484,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()
})
@ -503,10 +510,10 @@ RowLayout {
}
Component {
id: multiChoice_component
id: multiChoiceComponent
Flow {
Repeater {
id: checkbox_repeater
id: checkboxRepeater
model: attribute.values
delegate: CheckBox {
enabled: root.editable
@ -515,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)
}
@ -527,7 +534,7 @@ RowLayout {
}
Component {
id: slider_component
id: sliderComponent
RowLayout {
TextField {
IntValidator {
@ -535,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
@ -594,7 +601,7 @@ RowLayout {
}
Component {
id: checkbox_component
id: checkboxComponent
Row {
CheckBox {
enabled: root.editable
@ -605,17 +612,17 @@ RowLayout {
}
Component {
id: listAttribute_component
id: listAttributeComponent
ColumnLayout {
id: listAttribute_layout
id: listAttributeLayout
width: parent.width
property bool expanded: false
RowLayout {
spacing: 4
ToolButton {
text: listAttribute_layout.expanded ? MaterialIcons.keyboard_arrow_down : MaterialIcons.keyboard_arrow_right
text: listAttributeLayout.expanded ? MaterialIcons.keyboard_arrow_down : MaterialIcons.keyboard_arrow_right
font.family: MaterialIcons.fontFamily
onClicked: listAttribute_layout.expanded = !listAttribute_layout.expanded
onClicked: listAttributeLayout.expanded = !listAttributeLayout.expanded
}
Label {
Layout.alignment: Qt.AlignVCenter
@ -632,7 +639,7 @@ RowLayout {
}
ListView {
id: lv
model: listAttribute_layout.expanded ? attribute.value : undefined
model: listAttributeLayout.expanded ? attribute.value : undefined
visible: model !== undefined && count > 0
implicitHeight: Math.min(contentHeight, 300)
Layout.fillWidth: true
@ -683,7 +690,7 @@ RowLayout {
}
Component {
id: groupAttribute_component
id: groupAttributeComponent
ColumnLayout {
id: groupItem
Component.onCompleted: {
@ -692,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 }),
})
@ -703,17 +710,17 @@ RowLayout {
}
Component {
id: color_hue_component
id: colorHueComponent
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)
@ -748,16 +755,7 @@ RowLayout {
width: control.availableWidth
height: control.availableHeight
blending: false
fragmentShader: "
varying mediump vec2 qt_TexCoord0;
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
gl_FragColor = vec4(hsv2rgb(vec3(qt_TexCoord0.x, 1.0, 1.0)), 1.0);
}"
fragmentShader: "qrc:/shaders/AttributeItemDelegate.frag.qsb"
}
}
}

View file

@ -1,12 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
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,24 +44,23 @@ 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
Repeater {
id: childrenRepeater
model: isList && !attribute.isLink ? attribute.value : 0
onItemAdded: childPinCreated(item.childAttribute, item)
onItemRemoved: childPinDeleted(item.childAttribute, item)
onItemAdded: function(index, item) { childPinCreated(item.childAttribute, root) }
onItemRemoved: function(index, item) { childPinDeleted(item.childAttribute, root) }
delegate: Item {
property var childAttribute: object
visible: false
}
}
@ -95,38 +95,39 @@ 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: {
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
} else if (inputDragTarget.attribute.isLink) { // already connected attribute
} else if (inputDragTarget.attribute.isLink) { // Already connected attribute
root.edgeAboutToBeRemoved(inputDragTarget.attribute)
}
inputDropArea.acceptableDrop = drag.accepted
}
onExited: {
if (inputDragTarget.attribute.isLink) { // already connected attribute
if (inputDragTarget.attribute.isLink) { // Already connected attribute
root.edgeAboutToBeRemoved(undefined)
}
acceptableDrop = false
drag.source.dropAccepted = false
}
onDropped: {
onDropped: function(drop) {
root.edgeAboutToBeRemoved(undefined)
_reconstruction.addEdge(drag.source.attribute, inputDragTarget.attribute)
}
@ -160,11 +161,11 @@ 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
onPressed: {
onPressed: function(mouse) {
root.pressed(mouse)
}
onReleased: {
@ -185,14 +186,11 @@ RowLayout {
}
}
// Attribute name
Item {
id: nameContainer
Layout.fillWidth: true
implicitHeight: childrenRect.height
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Label {
@ -208,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
@ -248,25 +246,25 @@ 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.leftMargin: -root.width * 0.2
keys: [outputDragTarget.objectName]
onEntered: {
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
} else if (drag.source.attribute.isLink) { // already connected attribute
} else if (drag.source.attribute.isLink) { // Already connected attribute
root.edgeAboutToBeRemoved(drag.source.attribute)
}
outputDropArea.acceptableDrop = drag.accepted
@ -276,7 +274,7 @@ RowLayout {
acceptableDrop = false
}
onDropped: {
onDropped: function(drop) {
root.edgeAboutToBeRemoved(undefined)
_reconstruction.addEdge(outputDragTarget.attribute, drag.source.attribute)
}
@ -309,12 +307,12 @@ 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
onPressed: root.pressed(mouse)
onPressed: function(mouse) { root.pressed(mouse) }
onReleased: outputDragTarget.Drag.drop()
hoverEnabled: true

View file

@ -1,14 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import Controls 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "common.js" as Common
/**
* ChunkListView
*/
ColumnLayout {
id: root
property variant chunks
@ -78,7 +77,7 @@ ColumnLayout {
id: chunkDelegate
property var chunk: object
text: index
width: parent ? parent.width : 0
width: ListView.view.width
leftPadding: 8
onClicked: {
chunksLV.forceActiveFocus()

View file

@ -1,14 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
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

@ -1,23 +1,25 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0
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) {
@ -27,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

@ -1,12 +1,14 @@
import QtQuick 2.15
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes 1.6
import GraphEditor 1.0
import QtQuick.Shapes 1.15
import MaterialIcons 2.2
import QtQuick.Controls 2.15
/**
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
@ -40,7 +42,7 @@ Item {
Shape {
anchors.fill: parent
// cause rendering artifacts when enabled (and don't support hot reload really well)
// Cause rendering artifacts when enabled (and don't support hot reload really well)
vendorExtensionsEnabled: false
opacity: 0.7
@ -53,7 +55,7 @@ Item {
strokeColor: "#3E3E3E"
strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine
strokeWidth: 1
// final visual width of this path (never below 1)
// Final visual width of this path (never below 1)
readonly property real visualWidth: Math.max(strokeWidth, 1)
dashPattern: [6 / visualWidth, 4 / visualWidth]
capStyle: ShapePath.RoundCap
@ -68,7 +70,6 @@ Item {
control2X: x - ctrlPtDist
control2Y: y
}
}
ShapePath {
@ -80,7 +81,7 @@ Item {
strokeColor: root.isForLoop ? root.color : "transparent"
strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine
strokeWidth: root.thickness
// final visual width of this path (never below 1)
// Final visual width of this path (never below 1)
readonly property real visualWidth: Math.max(strokeWidth, 1)
dashPattern: [6 / visualWidth, 4 / visualWidth]
capStyle: ShapePath.RoundCap
@ -97,11 +98,13 @@ Item {
}
}
}
Item {
// place the label at the middle of the edge
// 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
@ -109,6 +112,7 @@ Item {
height: icon.height + 2 * margin
radius: width
color: path.strokeColor
MaterialToolLabel {
id: icon
anchors.centerIn: parent
@ -119,6 +123,7 @@ Item {
color: palette.base
ToolTip.text: "Foreach Loop"
}
MouseArea {
id: loopArea
anchors.fill: parent
@ -126,16 +131,25 @@ Item {
onClicked: root.pressed(arguments[0])
}
}
}
EdgeMouseArea {
id: edgeArea
anchors.fill: parent
curveScale: cubic.ctrlPtDist / root.width // normalize by width
acceptedButtons: Qt.LeftButton | Qt.RightButton
thickness: root.thickness + 4
onPressed: root.pressed(arguments[0]) // can't get named args, use arguments array
onReleased: root.released(arguments[0])
onPressed: function(event) {
root.pressed(event)
}
onReleased: function(event) {
root.released(event)
}
Component.onCompleted: {
/* The curve scale must be set only once the component has been fully created, so
* that all the events following the update of the curve scale can be taken into
* account */
curveScale = cubic.ctrlPtDist / root.width // Normalize by width
}
}
}

View file

@ -1,19 +1,21 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import Utils 1.0
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
@ -21,7 +23,7 @@ Item {
property var _attributeToDelegate: ({})
// signals
// Signals
signal workspaceMoved()
signal workspaceClicked()
@ -33,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()
@ -132,7 +133,7 @@ Item {
return mapToItem(draggable, mouseArea.width / 2, mouseArea.height / 2)
}
Keys.onPressed: {
Keys.onPressed: function(event) {
if (event.key === Qt.Key_F) {
fit()
} else if (event.key === Qt.Key_Delete) {
@ -172,7 +173,7 @@ Item {
drag.threshold: 0
cursorShape: drag.target == draggable ? Qt.ClosedHandCursor : Qt.ArrowCursor
onWheel: {
onWheel: function(wheel) {
var zoomFactor = wheel.angleDelta.y > 0 ? factor : 1 / factor
var scale = draggable.scale * zoomFactor
scale = Math.min(Math.max(minZoom, scale), maxZoom)
@ -185,7 +186,7 @@ Item {
workspaceMoved()
}
onPressed: {
onPressed: function(mouse) {
if (mouse.button != Qt.MiddleButton && mouse.modifiers == Qt.NoModifier) {
uigraph.clearNodeSelection()
}
@ -200,19 +201,21 @@ Item {
drag.target = draggable // start drag
}
}
onReleased: {
drag.target = undefined // stop drag
root.forceActiveFocus()
workspaceClicked()
}
onPositionChanged: {
if (drag.active)
workspaceMoved()
}
onClicked: {
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()
}
@ -271,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()
}
}
@ -295,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
@ -308,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()
}
@ -323,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
}
}
]
@ -335,7 +338,7 @@ Item {
id: nodeMenuRepeater
model: searchBar.text !== "" ? Object.values(newNodeMenu.menuKeys) : undefined
// create Menu items from available items
// Create Menu items from available items
delegate: menuItemDelegateComponent
}
@ -343,8 +346,13 @@ Item {
Instantiator {
id: instantiator
model: (searchBar.text === "") ? Object.keys(newNodeMenu.parseCategories()).sort() : undefined
onObjectAdded: newNodeMenu.insertMenu(index + 1, object ) // add sub-menu under the search bar
onObjectRemoved: newNodeMenu.removeMenu(object)
onObjectAdded: function(index, object) {
// Add sub-menu under the search bar
newNodeMenu.insertMenu(index + 1, object)
}
onObjectRemoved: function(index, object) {
newNodeMenu.removeMenu(object)
}
delegate: Menu {
title: modelData
@ -352,8 +360,12 @@ Item {
Instantiator {
model: newNodeMenu.visible ? newNodeMenu.parseCategories()[modelData] : undefined
onObjectAdded: newNodeSubMenu.insertItem(index, object)
onObjectRemoved: newNodeSubMenu.removeItem(object)
onObjectAdded: function(index, object) {
newNodeSubMenu.insertItem(index, object)
}
onObjectRemoved: function(index, object) {
newNodeSubMenu.removeItem(object)
}
delegate: menuItemDelegateComponent
}
}
@ -417,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
}
@ -476,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 {
@ -500,12 +512,11 @@ 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
point2y: isValidEdge ? dst.globalY + dst.inputAnchorPos.y : 0
onPressed: {
onPressed: function(event) {
const canEdit = !edge.dst.node.locked
if (event.button) {
@ -540,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
@ -589,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: {
@ -823,14 +834,14 @@ Item {
selected: uigraph.selectedNodes.contains(node)
hovered: uigraph.hoveredNode === node
onAttributePinCreated: registerAttributePin(attribute, pin)
onAttributePinDeleted: unregisterAttributePin(attribute, pin)
onAttributePinCreated: function(attribute, pin) { registerAttributePin(attribute, pin) }
onAttributePinDeleted: function(attribute, pin) { unregisterAttributePin(attribute, pin) }
onPressed: {
onPressed: function(mouse) {
if (mouse.button === Qt.LeftButton) {
if (mouse.modifiers & Qt.ControlModifier && !(mouse.modifiers & Qt.AltModifier)) {
if (mainSelected && selected) {
// left clicking a selected node twice with control will deselect it
// Left clicking a selected node twice with control will deselect it
uigraph.selectedNodes.remove(node)
uigraph.selectedNodesChanged()
selectNode(null)
@ -854,21 +865,19 @@ Item {
selectNode(node)
}
onDoubleClicked: root.nodeDoubleClicked(mouse, node)
onDoubleClicked: function(mouse) { root.nodeDoubleClicked(mouse, node) }
onMoved: uigraph.moveNode(node, position, uigraph.selectedNodes)
onMoved: function(position) { uigraph.moveNode(node, position, uigraph.selectedNodes) }
onEntered: uigraph.hoveredNode = node
onExited: uigraph.hoveredNode = null
onEdgeAboutToBeRemoved: {
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) {
root.edgeAboutToBeRemoved = input
@ -887,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) {
@ -898,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 }
@ -956,12 +965,12 @@ Item {
})
}
onDropped: {
onDropped: function(drop) {
if (nbMeshroomScenes == nbDraggedFiles || nbMeshroomScenes == 0) {
// retrieve mouse position and convert coordinate system
// Retrieve mouse position and convert coordinate system
// from pixel values to graph reference system
var mousePosition = mapToItem(draggable, drag.x, drag.y)
// send the list of files,
// Send the list of files,
// to create the corresponding nodes or open another scene
filesDropped(drop, mousePosition)
} else {
@ -1110,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
}
@ -1124,6 +1133,7 @@ Item {
function registerAttributePin(attribute, pin) {
root._attributeToDelegate[attribute] = pin
}
function unregisterAttributePin(attribute, pin) {
delete root._attributeToDelegate[attribute]
}
@ -1148,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 Qt.labs.settings 1.0
import QtCore
/**
* Persistent Settings related to the GraphEditor module.
*/
Settings {
category: 'GraphEditor'
property bool showAdvancedAttributes: false

View file

@ -1,15 +1,15 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtGraphicalEffects 1.12
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Utils 1.0
import MaterialIcons 2.2
import Utils 1.0
/**
* Visual representation of a Graph Node.
*/
Item {
id: root
@ -121,12 +121,12 @@ Item {
width: parent.width
height: body.height
drag.target: root
// small drag threshold to avoid moving the node by mistake
// Small drag threshold to avoid moving the node by mistake
drag.threshold: 2
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: root.pressed(mouse)
onDoubleClicked: root.doubleClicked(mouse)
onPressed: function(mouse) { root.pressed(mouse) }
onDoubleClicked: function(mouse) { root.doubleClicked(mouse) }
onEntered: root.entered()
onExited: root.exited()
drag.onActiveChanged: {
@ -412,8 +412,8 @@ Item {
property real globalX: root.x + nodeAttributes.x + outputs.x + outputLoader.x + outPin.x
property real globalY: root.y + nodeAttributes.y + outputs.y + outputLoader.y + outPin.y
onPressed: root.pressed(mouse)
onEdgeAboutToBeRemoved: root.edgeAboutToBeRemoved(input)
onPressed: function(mouse) { root.pressed(mouse) }
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
Component.onCompleted: attributePinCreated(attribute, outPin)
onChildPinCreated: attributePinCreated(childAttribute, outPin)
@ -446,13 +446,13 @@ Item {
property real globalX: root.x + nodeAttributes.x + inputs.x + inputLoader.x + inPin.x
property real globalY: root.y + nodeAttributes.y + inputs.y + inputLoader.y + inPin.y
readOnly: root.readOnly || object.isReadOnly
readOnly: Boolean(root.readOnly || object.isReadOnly)
Component.onCompleted: attributePinCreated(attribute, inPin)
Component.onDestruction: attributePinDeleted(attribute, inPin)
onPressed: root.pressed(mouse)
onEdgeAboutToBeRemoved: root.edgeAboutToBeRemoved(input)
onChildPinCreated: attributePinCreated(childAttribute, inPin)
onChildPinDeleted: attributePinDeleted(childAttribute, inPin)
onPressed: function(mouse) { root.pressed(mouse) }
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
onChildPinCreated: function(childAttribute, inPin) { attributePinCreated(childAttribute, inPin) }
onChildPinDeleted: function(childAttribute, inPin) { attributePinDeleted(childAttribute, inPin) }
}
}
}
@ -512,10 +512,10 @@ Item {
readOnly: Boolean(root.readOnly || object.isReadOnly)
Component.onCompleted: attributePinCreated(attribute, inParamsPin)
Component.onDestruction: attributePinDeleted(attribute, inParamsPin)
onPressed: root.pressed(mouse)
onEdgeAboutToBeRemoved: root.edgeAboutToBeRemoved(input)
onChildPinCreated: attributePinCreated(childAttribute, inParamsPin)
onChildPinDeleted: attributePinDeleted(childAttribute, inParamsPin)
onPressed: function(mouse) { root.pressed(mouse) }
onEdgeAboutToBeRemoved: function(input) { root.edgeAboutToBeRemoved(input) }
onChildPinCreated: function(childAttribute, inParamsPin) { attributePinCreated(childAttribute, inParamsPin) }
onChildPinDeleted: function(childAttribute, inParamsPin) { attributePinDeleted(childAttribute, inParamsPin) }
}
}
}

View file

@ -1,7 +1,6 @@
import QtQuick 2.15
import Utils 1.0
import QtQuick
//import "common.js" as Common
import Utils 1.0
ListView {
id: root
@ -14,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
}
@ -29,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

@ -1,6 +1,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import "common.js" as Common
@ -8,6 +9,7 @@ import "common.js" as Common
/**
* Displays Node documentation
*/
FocusScope {
id: root

View file

@ -1,15 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import Controls 1.0
import Utils 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
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
@ -35,7 +36,7 @@ Panel {
if (node !== null && (node.isFinishedOrRunning() || globalStatus == "ERROR")) {
computationInfo.text = Format.sec2timeStr(node.elapsedTime)
}
else{
else {
computationInfo.text = ""
}
}
@ -117,6 +118,7 @@ Panel {
Menu {
id: settingsMenu
y: parent.height
Menu {
id: filterAttributesMenu
title: "Filter Attributes"
@ -138,7 +140,9 @@ Panel {
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
RowLayout {
CheckBox {
id: defaultToggle
@ -157,7 +161,9 @@ Panel {
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
RowLayout {
CheckBox {
id: linkToggle
@ -176,7 +182,9 @@ Panel {
enabled: tabBar.currentIndex === 0
}
}
MenuSeparator {}
CheckBox {
id: advancedToggle
text: "Advanced"
@ -196,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"
@ -208,6 +218,7 @@ Panel {
}
}
}
ColumnLayout {
anchors.fill: parent
@ -215,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

@ -1,17 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import "common.js" as Common
/**
* 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
@ -29,7 +28,7 @@ FocusScope {
anchors.fill: parent
property string currentFile: (root.currentChunkIndex >= 0 && root.currentChunk) ? root.currentChunk["logFile"] : ""
property url source: Filepath.stringToUrl(currentFile)
property url sourceFile: Filepath.stringToUrl(currentFile)
sourceComponent: textFileViewerComponent
}
@ -40,9 +39,8 @@ FocusScope {
TextFileViewer {
id: textFileViewer
anchors.fill: parent
source: componentLoader.source
source: componentLoader.sourceFile
autoReload: root.currentChunk !== undefined && root.currentChunk.statusName === "RUNNING"
// source is set in fileSelector
}
}
}

View file

@ -1,18 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import Controls 1.0
import Utils 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "common.js" as Common
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
@ -27,7 +25,7 @@ FocusScope {
clip: true
anchors.fill: parent
property string currentFile: currentChunk ? currentChunk["statisticsFile"] : ""
property url source: Filepath.stringToUrl(currentFile)
property url sourceFile: Filepath.stringToUrl(currentFile)
sourceComponent: chunksLV.chunksSummary ? statViewerComponent : chunkStatViewerComponent
}
@ -37,7 +35,7 @@ FocusScope {
StatViewer {
id: statViewer
anchors.fill: parent
source: componentLoader.source
source: componentLoader.sourceFile
}
}

View file

@ -1,17 +1,14 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import Controls 1.0
import "common.js" as Common
import QtQuick
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
@ -26,7 +23,7 @@ FocusScope {
anchors.fill: parent
property string currentFile: (root.currentChunkIndex >= 0) ? root.currentChunk["statusFile"] : ""
property url source: Filepath.stringToUrl(currentFile)
property url sourceFile: Filepath.stringToUrl(currentFile)
sourceComponent: statViewerComponent
}
@ -35,7 +32,7 @@ FocusScope {
id: statViewerComponent
Item {
id: statusViewer
property url source: componentLoader.source
property url source: componentLoader.sourceFile
property var lastModified: undefined
onSourceChanged: {
@ -46,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
@ -55,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()
}
@ -103,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

@ -1,12 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import Controls 1.0
import Utils 1.0
import MaterialIcons 2.2
import Utils 1.0
import Qt.labs.platform 1.0 as Platform
import QtQuick.Dialogs 1.3
Item {
id: root

View file

@ -1,10 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtCharts 2.15
import QtQuick.Layouts 1.11
import Utils 1.0
import QtCharts
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Charts 1.0
import MaterialIcons 2.2
import Utils 1.0
Item {
@ -36,8 +37,7 @@ Item {
property color textColor: Colors.sysPalette.text
readonly property var colors: [
readonly property var colors: [
"#f44336",
"#e91e63",
"#9c27b0",
@ -93,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
@ -102,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) {
@ -111,7 +111,7 @@ Item {
return
}
resetCharts()
sourceModified = xhr.getResponseHeader('Last-Modified')
sourceModified = xhr.getResponseHeader("Last-Modified")
root.createCharts()
reloadTimer.restart()
}
@ -129,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()
@ -157,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
@ -218,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
@ -248,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)
@ -389,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 {
@ -421,7 +422,6 @@ Item {
}
/**************************
*** RAM UI ***
**************************/
@ -443,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 {
@ -475,7 +475,6 @@ Item {
}
/**************************
*** GPU UI ***
**************************/

View file

@ -1,9 +1,9 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import Utils 1.0
import MaterialIcons 2.2
Item {
id: root
@ -133,7 +133,7 @@ Item {
}
delegate: RowLayout {
width: parent != null ? parent.width : undefined
width: ListView.view.width
height: 18
spacing: 3
@ -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

@ -1,6 +1,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Utils 1.0
import MaterialIcons 2.2
import Controls 1.0
@ -21,7 +22,6 @@ Page {
anchors.fill: parent
Item {
SplitView.minimumWidth: 250
SplitView.preferredWidth: 330
SplitView.maximumWidth: 500
@ -372,10 +372,10 @@ Page {
Connections {
target: projectDelegate
function onClicked() {
if (!modelData["path"]){
if (!modelData["path"]) {
initFileDialogFolder(openFileDialog)
openFileDialog.open()
} else{
} else {
// Open project
mainStack.push("Application.qml")
if (_reconstruction.loadUrl(modelData["path"])) {

View file

@ -1,15 +1,19 @@
import QtQuick 2.15
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

@ -1,12 +1,13 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import Utils 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Utils 1.0
/**
* ImageDelegate for a Viewpoint object.
*/
Item {
id: root
@ -24,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) : ''
@ -33,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)
}
@ -60,7 +61,7 @@ Item {
anchors.margins: 6
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: {
onPressed: function(mouse) {
if (mouse.button == Qt.RightButton)
imageMenu.popup()
root.pressed(mouse)
@ -135,6 +136,7 @@ Item {
running: thumbnail.status != Image.Ready
}
}
// Image basename
Label {
id: imageLabel

View file

@ -1,17 +1,18 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQml.Models 2.15
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQml.Models
import Qt.labs.qmlmodels 1.0
import Controls 1.0
import MaterialIcons 2.2
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_
@ -305,7 +306,7 @@ Panel {
}
onRemoveRequest: sendRemoveRequest()
Keys.onPressed: (event) => {
Keys.onPressed: function(event) {
if (event.key === Qt.Key_Delete && event.modifiers === Qt.ShiftModifier) {
removeAllImages()
} else if (event.key === Qt.Key_Delete) {
@ -375,7 +376,7 @@ Panel {
// Keyboard shortcut to change current image group
Keys.priority: Keys.BeforeItem
Keys.onPressed: {
Keys.onPressed: function(event) {
if (event.modifiers & Qt.AltModifier) {
if (event.key === Qt.Key_Right) {
_reconstruction.cameraInitIndex = Math.min(root.cameraInits.count - 1, root.cameraInitIndex + 1)
@ -442,12 +443,12 @@ Panel {
anchors.fill: parent
enabled: !m.readOnly && !intrinsicsFilterButton.checked
keys: ["text/uri-list"]
onEntered: {
onEntered: function(drag) {
nbDraggedFiles = drag.urls.length
filesByType = _reconstruction.getFilesByTypeFromDrop(drag.urls)
nbMeshroomScenes = filesByType["meshroomScenes"].length
}
onDropped: {
onDropped: function(drop) {
var augmentSfm = augmentArea.hovered
if (nbMeshroomScenes == nbDraggedFiles || nbMeshroomScenes == 0) {
root.filesDropped(filesByType, augmentSfm)
@ -529,7 +530,7 @@ Panel {
MouseArea {
anchors.fill: parent
onPressed: {
onPressed: function(mouse) {
if (mouse.button == Qt.LeftButton)
grid.forceActiveFocus()
mouse.accepted = false
@ -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

@ -1,8 +1,6 @@
import QtQuick 2.15
import QtQuick.Layouts 1.11
import QtQuick.Controls 2.15
import MaterialIcons 2.2
import Utils 1.0
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
RowLayout {
id: root
@ -49,11 +47,12 @@ RowLayout {
if (!attribute)
return undefined
switch (attribute.type) {
case "ChoiceParam": return choice_component
case "IntParam": return int_component
case "FloatParam": return float_component
case "BoolParam": return bool_component
case "StringParam": return textField_component
case "ChoiceParam": return choiceComponent
case "IntParam": return intComponent
case "FloatParam": return floatComponent
case "BoolParam": return boolComponent
case "StringParam": return textFieldComponent
case "File": return textFieldComponent
default: return undefined
}
}
@ -62,7 +61,7 @@ RowLayout {
}
Component {
id: textField_component
id: textFieldComponent
TextInput {
text: attribute.value
width: intrinsicModel.columnWidths[columnIndex]
@ -81,14 +80,14 @@ RowLayout {
_reconstruction.setAttribute(attribute, text)
}
Component.onDestruction: {
if(activeFocus)
if (activeFocus)
_reconstruction.setAttribute(attribute, text)
}
}
}
Component {
id: int_component
id: intComponent
TextInput {
text: model.display.value
@ -121,7 +120,7 @@ RowLayout {
}
Component {
id: choice_component
id: choiceComponent
ComboBox {
id: combo
model: attribute.desc !== undefined ? attribute.desc.values : undefined
@ -146,7 +145,7 @@ RowLayout {
}
Component {
id: bool_component
id: boolComponent
CheckBox {
checked: attribute ? attribute.value : false
padding: 12
@ -156,7 +155,7 @@ RowLayout {
}
Component {
id: float_component
id: floatComponent
TextInput {
readonly property real formattedValue: attribute.value.toFixed(2)
property string displayValue: String(formattedValue)
@ -179,9 +178,9 @@ RowLayout {
autoScroll: activeFocus
//Use this function to ensure the left part is visible
//while keeping the trick for formatting the text
//Timing issues otherwise
// Use this function to ensure the left part is visible
// while keeping the trick for formatting the text
// Timing issues otherwise
onActiveFocusChanged: {
if (activeFocus)
text = String(attribute.value)
@ -192,7 +191,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

@ -1,13 +1,14 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick
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
@ -27,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")
@ -103,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

@ -1,11 +1,10 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0
MessageDialog {
id: root

View file

@ -1,14 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import QtQuick
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 2.15
import QtQuick.Controls 2.15
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,5 +1,5 @@
pragma Singleton
import QtQuick 2.15
import QtQuick
QtObject {
property FontLoader fl: FontLoader {

View file

@ -1,11 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
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

@ -1,12 +1,12 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.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
@ -19,7 +19,7 @@ ToolButton {
}
background: Rectangle {
color: {
if (pressed || checked || hovered) {
if (enabled && (pressed || checked || hovered)) {
if (pressed || checked)
return Qt.darker(parent.palette.base, 1.3)
if (hovered)

View file

@ -1,12 +1,12 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.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

@ -1,12 +1,12 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.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
@ -41,7 +41,7 @@ ToolButton {
}
background: Rectangle {
color: {
if (pressed || checked || hovered) {
if (enabled && (pressed || checked || hovered)) {
if (pressed || checked)
return Qt.darker(parent.palette.base, 1.3)
if (hovered)

View file

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

View file

@ -1,10 +1,11 @@
pragma Singleton
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick
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

@ -1,5 +1,5 @@
pragma Singleton
import QtQuick 2.11
import QtQuick
/**
* Singleton that defines utility functions for supporting exif orientation tags.

View file

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

View file

@ -1,6 +1,6 @@
import QtQuick 2.15
import QtQml.Models 2.15
import QtQuick.Controls 2.15
import QtQuick
import QtQml.Models
import QtQuick.Controls
/**
* SortFilderDelegateModel adds sorting and filtering capabilities on a source model.
@ -17,21 +17,21 @@ import QtQuick.Controls 2.15
*
* 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

@ -1,14 +1,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtPositioning 5.15
import QtLocation 5.15
import QtCharts 2.15
import Charts 1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtCharts
import Charts 1.0
import Controls 1.0
import Utils 1.0
import DataObjects 1.0
FloatingPane {
@ -34,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) {
@ -80,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

@ -1,4 +1,4 @@
import QtQuick 2.15
import QtQuick
Item {
id: root
@ -49,7 +49,7 @@ Item {
propagateComposedEvents: true
property bool controlModifierEnabled: false
onPositionChanged: {
onPositionChanged: function(mouse) {
mArea.controlModifierEnabled = (mouse.modifiers & Qt.ControlModifier)
mouse.accepted = false
}
@ -65,7 +65,7 @@ Item {
onPressed: {
forceActiveFocus()
}
onWheel: {
onWheel: function(wheel) {
mArea.controlModifierEnabled = (wheel.modifiers & Qt.ControlModifier)
if (wheel.modifiers & Qt.ControlModifier) {
root.incrementRadius(wheel.angleDelta.y / 120.0)

View file

@ -1,11 +1,11 @@
import QtQuick 2.15
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

@ -1,5 +1,5 @@
import QtQuick 2.15
import QtQuick.Layouts 1.11
import QtQuick
import QtQuick.Layouts
import Controls 1.0
@ -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

@ -1,4 +1,4 @@
import QtQuick 2.15
import QtQuick
Item {
id: root
@ -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

@ -1,15 +1,16 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Utils 1.0
import Controls 1.0
import MaterialIcons 2.2
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
@ -261,7 +269,9 @@ FloatingPane {
colors: root.featuresViewer.colors
currentIndex: featureType.viewer.colorIndex
// offset featuresViewer color set when changing the color of one feature type
onColorPicked: featureType.viewer.colorOffset = colorIndex - index
onColorPicked: function(colorIndex) {
featureType.viewer.colorOffset = colorIndex - index
}
}
// Feature type name
Label {

View file

@ -1,12 +1,13 @@
import QtQuick 2.15
import AliceVision 1.0 as AliceVision
import QtQuick
import AliceVision 1.0 as AliceVision
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

@ -1,7 +1,7 @@
import QtQuick 2.15
import Utils 1.0
import QtQuick
import AliceVision 1.0 as AliceVision
import Utils 1.0
/**
* FloatImage displays an Image with gamma / offset / channel controls
@ -96,7 +96,6 @@ AliceVision.FloatImageViewer {
if (!isHighlightable) root.surface.mouseOver = false
}
/*
* Principal Point
*/

View file

@ -1,7 +1,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
FloatingPane {
@ -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

@ -1,16 +1,18 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtPositioning 5.15
import QtLocation 5.15
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtPositioning 6.6
import QtLocation 6.6
import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0
/**
* ImageMetadataView displays a JSON model representing an image's metadata as a ListView.
*/
FloatingPane {
id: root
@ -31,11 +33,11 @@ FloatingPane {
var values = value.split(",")
var result = 0
for (var i = 0; i < values.length; ++i) {
// divide each component by the corresponding power of 60
// Divide each component by the corresponding power of 60
// 1 for degree, 60 for minutes, 3600 for seconds
result += Number(values[i]) / Math.pow(60, i)
}
// handle opposite reference: South (latitude) or West (longitude)
// Handle opposite reference: South (latitude) or West (longitude)
return (ref === "S" || ref === "W") ? -result : result
}
@ -62,14 +64,14 @@ FloatingPane {
id: metadataModel
property var metadata: ({})
// reset model when metadata changes
// Reset model when metadata changes
onMetadataChanged: {
metadataModel.clear()
var entries = []
// prepare data to populate the model from the input metadata object
// Prepare data to populate the model from the input metadata object
for (var key in metadata) {
var entry = {}
// split on ":" to get group and key
// Split on ":" to get group and key
var i = key.lastIndexOf(":")
if (i === -1) {
i = key.lastIndexOf("/")
@ -79,7 +81,7 @@ FloatingPane {
entry["group"] = key.substr(0, i)
entry["key"] = key.substr(i+1)
} else {
// set default group to something convenient for sorting
// Set default group to something convenient for sorting
entry["group"] = "-"
entry["key"] = key
}
@ -93,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)
}
@ -103,7 +105,7 @@ FloatingPane {
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.MiddleButton
onWheel: wheel.accepted = true
onWheel: function(wheel) { wheel.accepted = true }
}
// Main Layout
@ -174,7 +176,7 @@ FloatingPane {
sortRole: "raw"
filters: [{role: "raw", value: searchBar.text}]
delegate: RowLayout {
width: parent ? parent.width : 0
width: ListView.view.width
Label {
text: key
leftPadding: 6

View file

@ -1,8 +1,9 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0
FloatingPane {
@ -30,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 {
@ -62,7 +63,7 @@ FloatingPane {
padding : 10
colors: root.colors
currentIndex: root.colorIndex
onColorPicked: root.colorOffset = colorIndex
onColorPicked: function(colorIndex) { root.colorOffset = colorIndex }
}
// Grid opacity slider
@ -147,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

@ -1,4 +1,5 @@
import QtQuick 2.15
import QtQuick
import AliceVision 1.0 as AliceVision
// Data from the View / Features.

View file

@ -1,4 +1,5 @@
import QtQuick 2.15
import QtQuick
import AliceVision 1.0 as AliceVision
// Data from the SfM

View file

@ -1,4 +1,5 @@
import QtQuick 2.15
import QtQuick
import AliceVision 1.0 as AliceVision
AliceVision.MTracks {

View file

@ -1,8 +1,9 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.11
import MaterialIcons 2.2
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0
FloatingPane {
@ -31,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 {
@ -131,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)
@ -164,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)
@ -179,8 +178,8 @@ FloatingPane {
}
}
}
}
TextMetrics {
id: textMetrics_subdivisionsValue
font: subdivisionsLabel.font

View file

@ -1,7 +1,7 @@
import QtQuick 2.15
import Utils 1.0
import QtQuick
import AliceVision 1.0 as AliceVision
import Utils 1.0
/**
* PanoramaViwer displays a list of Float Images
@ -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
}
@ -121,7 +125,7 @@ AliceVision.PanoramaViewer {
if (isEditable)
isRotating ? Qt.ClosedHandCursor : Qt.OpenHandCursor
}
onPositionChanged: {
onPositionChanged: function(mouse) {
// Send Mouse Coordinates to Float Images Viewers
idSelected = -1
for (var i = 0; i < repeater.model && isHighlightable; ++i) {
@ -171,7 +175,7 @@ AliceVision.PanoramaViewer {
}
}
onPressed:{
onPressed: function(mouse) {
_reconstruction.beginModification("Panorama Manual Rotation")
isRotating = true
lastX = mouse.x
@ -185,7 +189,7 @@ AliceVision.PanoramaViewer {
previous_roll = roll
}
onReleased: {
onReleased: function(mouse) {
_reconstruction.endModification()
isRotating = false
lastX = 0
@ -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

@ -0,0 +1,48 @@
import QtQuick
import Utils 1.0
import AliceVision 1.0 as AliceVision
/**
* PhongImageViewer displays an Image (albedo + normal) with a given light direction.
* Shading is done using Blinn-Phong reflection model, material and light direction parameters available.
* Accept HdrImageToolbar controls (gamma / offset / channel).
*
* <!> Requires QtAliceVision plugin.
*/
AliceVision.PhongImageViewer {
id: root
width: sourceSize.width
height: sourceSize.height
visible: true
// paintedWidth / paintedHeight / imageStatus for compatibility with standard Image
property int paintedWidth: sourceSize.width
property int paintedHeight: sourceSize.height
property var imageStatus: {
if (root.status === AliceVision.PhongImageViewer.EStatus.LOADING) {
return Image.Loading
} else if (root.status === AliceVision.PhongImageViewer.EStatus.LOADING_ERROR ||
root.status === AliceVision.PhongImageViewer.EStatus.MISSING_FILE) {
return Image.Error
} else if ((root.sourcePath === "") || (root.sourceSize.height <= 0) || (root.sourceSize.width <= 0)) {
return Image.Null
}
return Image.Ready
}
property string channelModeString : "rgba"
channelMode: {
switch (channelModeString) {
case "rgb": return AliceVision.PhongImageViewer.EChannelMode.RGB
case "r": return AliceVision.PhongImageViewer.EChannelMode.R
case "g": return AliceVision.PhongImageViewer.EChannelMode.G
case "b": return AliceVision.PhongImageViewer.EChannelMode.B
case "a": return AliceVision.PhongImageViewer.EChannelMode.A
default: return AliceVision.PhongImageViewer.EChannelMode.RGBA
}
}
}

Some files were not shown because too many files have changed in this diff Show more