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 # [nodes] Linting: Clean-up files
4c0409f573c2694325b104c2686a1532f95cb9bc 4c0409f573c2694325b104c2686a1532f95cb9bc
# Linting: Clean-up files # 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. Copyright (c) 2001-2018 Python Software Foundation.
Distributed under the [PSFL V2 license](https://www.python.org/download/releases/2.7/license/). 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) [https://www.qt.io](https://www.qt.io)
Copyright (C) 2018 The Qt Company Ltd and other contributors. Copyright (C) 2018 The Qt Company Ltd and other contributors.
Distributed under the [LGPL V3 license](https://opensource.org/licenses/LGPL-3.0). 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_READ"] = '1'
os.environ["QML_XHR_ALLOW_FILE_WRITE"] = '1' os.environ["QML_XHR_ALLOW_FILE_WRITE"] = '1'
os.environ["PYSEQ_STRICT_PAD"] = '1' os.environ["PYSEQ_STRICT_PAD"] = '1'
os.environ["QSG_RHI_BACKEND"] = "opengl"

View file

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

View file

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

View file

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

View file

@ -10,4 +10,4 @@ import meshroom.ui
import meshroom.ui.app import meshroom.ui.app
meshroom.ui.uiInstance = meshroom.ui.app.MeshroomApp(sys.argv) 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 argparse
import json import json
from PySide2 import QtCore from PySide6 import __version__ as PySideVersion
from PySide2.QtCore import Qt, QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings from PySide6 import QtCore
from PySide2.QtGui import QIcon from PySide6.QtCore import Qt, QUrl, QJsonValue, qInstallMessageHandler, QtMsgType, QSettings
from PySide2.QtWidgets import QApplication from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication
import meshroom import meshroom
from meshroom.core import nodesDesc from meshroom.core import nodesDesc
@ -188,7 +189,7 @@ class MeshroomApp(QApplication):
def __init__(self, args): def __init__(self, args):
meshroom.core.initPipelines() 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) args = createMeshroomParser(args)
@ -202,8 +203,6 @@ class MeshroomApp(QApplication):
} }
logging.getLogger().setLevel(logStringToPython[args.verbose]) logging.getLogger().setLevel(logStringToPython[args.verbose])
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
super(MeshroomApp, self).__init__(QtArgs) super(MeshroomApp, self).__init__(QtArgs)
self.setOrganizationName('AliceVision') self.setOrganizationName('AliceVision')
@ -355,6 +354,7 @@ class MeshroomApp(QApplication):
p = {"path": p, "thumbnail": thumbnail} p = {"path": p, "thumbnail": thumbnail}
projects.append(p) projects.append(p)
settings.endArray() settings.endArray()
settings.endGroup()
return projects return projects
@Slot(str) @Slot(str)
@ -394,6 +394,7 @@ class MeshroomApp(QApplication):
settings.setArrayIndex(i) settings.setArrayIndex(i)
settings.setValue("filepath", p) settings.setValue("filepath", p)
settings.endArray() settings.endArray()
settings.endGroup()
settings.sync() settings.sync()
self.recentProjectFilesChanged.emit() self.recentProjectFilesChanged.emit()
@ -539,7 +540,8 @@ class MeshroomApp(QApplication):
import sys import sys
return { return {
'platform': '{} {}'.format(platform.system(), platform.release()), '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) systemInfo = Property(QJsonValue, _systemInfo, constant=True)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,9 @@
from math import acos, pi, sqrt, atan2, cos, sin, asin from math import acos, pi, sqrt, atan2, cos, sin, asin
from PySide2.QtCore import QObject, Slot, QSize, Signal, QPointF from PySide6.QtCore import QObject, Slot, QSize, Signal, QPointF
from PySide2.Qt3DCore import Qt3DCore from PySide6.Qt3DCore import Qt3DCore
from PySide2.Qt3DRender import Qt3DRender from PySide6.Qt3DRender import Qt3DRender
from PySide2.QtGui import QVector3D, QQuaternion, QVector2D, QVector4D, QMatrix4x4 from PySide6.QtGui import QVector3D, QQuaternion, QVector2D, QVector4D, QMatrix4x4
from meshroom.ui.utils import makeProperty from meshroom.ui.utils import makeProperty
@ -41,14 +41,14 @@ class Scene3DHelper(QObject):
def faceCount(self, entity): def faceCount(self, entity):
""" Returns face count based on children QGeometry buffers size.""" """ Returns face count based on children QGeometry buffers size."""
count = 0 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"]) count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexPosition"])
return count / 3 return count / 3
@Slot(Qt3DCore.QEntity, result=int) @Slot(Qt3DCore.QEntity, result=int)
def vertexColorCount(self, entity): def vertexColorCount(self, entity):
count = 0 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"]) count += sum([attr.count() for attr in geo.attributes() if attr.name() == "vertexColor"])
return count return count
@ -59,10 +59,12 @@ class TrackballController(QObject):
Based on the C++ version from https://github.com/cjmdaixi/Qt3DTrackball Based on the C++ version from https://github.com/cjmdaixi/Qt3DTrackball
""" """
_windowSize = QSize() def __init__(self, parent=None):
_camera = None super().__init__(parent)
_trackballSize = 1.0 self._windowSize = QSize()
_rotationSpeed = 5.0 self._camera = None
self._trackballSize = 1.0
self._rotationSpeed = 5.0
def projectToTrackball(self, screenCoords): def projectToTrackball(self, screenCoords):
sx = screenCoords.x() sx = screenCoords.x()
@ -98,7 +100,7 @@ class TrackballController(QObject):
windowSizeChanged = Signal() windowSizeChanged = Signal()
windowSize = makeProperty(QSize, '_windowSize', windowSizeChanged) windowSize = makeProperty(QSize, '_windowSize', windowSizeChanged)
cameraChanged = Signal() cameraChanged = Signal()
camera = makeProperty(Qt3DRender.QCamera, '_camera', cameraChanged) camera = makeProperty(QObject, '_camera', cameraChanged)
trackballSizeChanged = Signal() trackballSizeChanged = Signal()
trackballSize = makeProperty(float, '_trackballSize', trackballSizeChanged) trackballSize = makeProperty(float, '_trackballSize', trackballSizeChanged)
rotationSpeedChanged = Signal() rotationSpeedChanged = Signal()
@ -352,7 +354,23 @@ class Transformations3DHelper(QObject):
U = M * quaternion * M U = M * quaternion * M
return U.toEulerAngles() 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) @Slot(QVector3D, QMatrix4x4, Qt3DRender.QCamera, QSize, result=float)
def computeScaleUnitFromModelMatrix(self, axis, modelMat, camera, windowSize): def computeScaleUnitFromModelMatrix(self, axis, modelMat, camera, windowSize):
""" Compute the length of the screen projected vector axis unit transformed by the model matrix. """ 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 io import StringIO
from contextlib import redirect_stdout from contextlib import redirect_stdout

View file

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

View file

@ -8,7 +8,7 @@ from enum import Enum
from threading import Thread, Event, Lock from threading import Thread, Event, Lock
from multiprocessing.pool import ThreadPool 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.core import sessionUid
from meshroom.common.qt import QObjectListModel from meshroom.common.qt import QObjectListModel

View file

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

View file

@ -1,11 +1,14 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import Utils 1.0 import Utils 1.0
import MaterialIcons 2.2 import MaterialIcons 2.2
/// Meshroom "About" window /**
* Meshroom "About" window
*/
Dialog { Dialog {
id: root id: root
@ -24,7 +27,7 @@ Dialog {
modal: true modal: true
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
padding: 30 padding: 30
topPadding: 0 // header provides top padding topPadding: 0 // Header provides top padding
header: Pane { header: Pane {
background: Item {} background: Item {}
@ -56,7 +59,8 @@ Dialog {
selectByMouse: true selectByMouse: true
text: "Version " + Qt.application.version + "\n" text: "Version " + Qt.application.version + "\n"
+ MeshroomApp.systemInfo["platform"] + " \n" + MeshroomApp.systemInfo["platform"] + " \n"
+ MeshroomApp.systemInfo["python"] + MeshroomApp.systemInfo["python"] + "\n"
+ MeshroomApp.systemInfo["pyside"]
} }
} }
@ -174,16 +178,16 @@ Dialog {
sourceComponent: ScrollView { sourceComponent: ScrollView {
Component.onCompleted: { Component.onCompleted: {
// try to load the local file // Try to load the local file
var url = Filepath.stringToUrl(modelData.localUrl) 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)) if (!Filepath.exists(url))
url = modelData.onlineUrl url = modelData.onlineUrl
Request.get(url, Request.get(url,
function(xhr) { function(xhr) {
if (xhr.readyState === XMLHttpRequest.DONE) if (xhr.readyState === XMLHttpRequest.DONE)
{ {
// status is OK // Status is OK
if (xhr.status === 200) if (xhr.status === 200)
textArea.text = MeshroomApp.markdownToHtml(xhr.responseText) textArea.text = MeshroomApp.markdownToHtml(xhr.responseText)
else else

View file

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

View file

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

View file

@ -1,13 +1,13 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtCharts 2.15 import QtCharts
/** /**
* ChartViewLegend is an interactive legend component for ChartViews. * ChartViewLegend is an interactive legend component for ChartViews.
* It provides a CheckBox for each series that can control its visibility, * It provides a CheckBox for each series that can control its visibility,
* and highlight on hovering. * and highlight on hovering.
*/ */
Flow { Flow {
id: root id: root
@ -49,7 +49,6 @@ Flow {
} }
Repeater { Repeater {
// ChartView series can't be accessed directly as a model. // ChartView series can't be accessed directly as a model.
// Use an intermediate ListModel populated with those series. // Use an intermediate ListModel populated with those series.
model: ListModel { model: ListModel {
@ -70,7 +69,7 @@ Flow {
root.hoveredSeries = null root.hoveredSeries = null
} }
// hovered serie properties override // Hovered serie properties override
states: [ states: [
State { State {
when: series && root.hoveredSeries === series when: series && root.hoveredSeries === series
@ -84,7 +83,7 @@ Flow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: function(mouse) {
if (mouse.modifiers & Qt.ControlModifier) if (mouse.modifiers & Qt.ControlModifier)
root.soloSeries(index) root.soloSeries(index)
else else

View file

@ -1,15 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Layouts
import QtQuick.Layouts 1.11 import QtCharts
import MaterialIcons 2.2
import QtPositioning 5.15
import QtLocation 5.15
import QtCharts 2.15
import Controls 1.0
import Utils 1.0
ChartView { ChartView {
id: root id: root
@ -26,32 +17,11 @@ ChartView {
MouseArea { MouseArea {
anchors.fill: parent 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 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: { onClicked: {
root.zoomReset() root.zoomReset()
} }
} }
} }
} }

View file

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

View file

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

View file

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

View file

@ -1,11 +1,12 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
/** /**
* FloatingPane provides a Pane with a slightly transparent default background * FloatingPane provides a Pane with a slightly transparent default background
* using palette.base as color. Useful to create floating toolbar/overlays. * using palette.base as color. Useful to create floating toolbar/overlays.
*/ */
Pane { Pane {
id: root id: root
@ -14,5 +15,9 @@ Pane {
padding: 6 padding: 6
anchors.margins: 2 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
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
/** /**
* A custom GroupBox with predefined header. * A custom GroupBox with predefined header.
*/ */
GroupBox { GroupBox {
id: root id: root
@ -21,13 +21,14 @@ GroupBox {
background: Item {} background: Item {}
label: Pane { label: Pane {
padding: 2
width: root.width
background: Rectangle { background: Rectangle {
id: labelBg id: labelBg
color: palette.base color: palette.base
opacity: 0.8 opacity: 0.8
} }
padding: 2
width: root.width
RowLayout { RowLayout {
width: parent.width width: parent.width
Label { Label {

View file

@ -1,7 +1,8 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import MaterialIcons 2.2 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 * IntSelector with arrows and a text input to select a number

View file

@ -1,11 +1,11 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
/** /**
* KeyValue allows to create a list of key/value, like a table. * KeyValue allows to create a list of key/value, like a table.
*/ */
Rectangle { Rectangle {
property alias key: keyLabel.text property alias key: keyLabel.text
property alias value: valueText.text property alias value: valueText.text
@ -20,7 +20,6 @@ Rectangle {
Rectangle { Rectangle {
anchors.margins: 2 anchors.margins: 2
color: Qt.darker(activePalette.window, 1.1) color: Qt.darker(activePalette.window, 1.1)
// Layout.preferredWidth: sizeHandle.x
Layout.minimumWidth: 10.0 * Qt.application.font.pixelSize Layout.minimumWidth: 10.0 * Qt.application.font.pixelSize
Layout.maximumWidth: 15.0 * Qt.application.font.pixelSize Layout.maximumWidth: 15.0 * Qt.application.font.pixelSize
Layout.fillWidth: false Layout.fillWidth: false
@ -46,7 +45,10 @@ Rectangle {
readOnly: true readOnly: true
selectByMouse: 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
import QtQuick.Controls 2.0 import QtQuick.Controls
/** /**
* MScrollBar is a custom scrollbar implementation. * MScrollBar is a custom scrollbar implementation.
* It is a vertical scrollbar that can be used to scroll a ListView. * 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
import QtQuick.Controls 2.15 import QtQuick.Controls
SplitView { SplitView {
id: splitView id: splitView

View file

@ -1,6 +1,7 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2 import MaterialIcons 2.2
Dialog { Dialog {
@ -15,7 +16,7 @@ Dialog {
default property alias children: layout.children 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" 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. /// Return the text content of this dialog as a simple string.
@ -39,7 +40,7 @@ Dialog {
rightPadding: leftPadding rightPadding: leftPadding
background: Item { background: Item {
// hidden text edit to perform copy in clipboard // Hidden text edit to perform copy in clipboard
TextEdit { TextEdit {
id: textContent id: textContent
visible: false visible: false

View file

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

View file

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

View file

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

View file

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

View file

@ -14,4 +14,5 @@ ExifOrientedViewer 1.0 ExifOrientedViewer.qml
FilterComboBox 1.0 FilterComboBox.qml FilterComboBox 1.0 FilterComboBox.qml
IntSelector 1.0 IntSelector.qml IntSelector 1.0 IntSelector.qml
MScrollBar 1.0 MScrollBar.qml MScrollBar 1.0 MScrollBar.qml
MSplitView 1.0 MSplitView.qml MSplitView 1.0 MSplitView.qml
DirectionalLightPane 1.0 DirectionalLightPane.qml

View file

@ -1,10 +1,10 @@
import QtQuick 2.15 import QtQuick
import MaterialIcons 2.2
import Controls 1.0 import Controls 1.0
/** /**
* DialogsFactory is utility object to instantiate generic purpose Dialogs. * DialogsFactory is utility object to instantiate generic purpose Dialogs.
*/ */
QtObject { QtObject {
readonly property string defaultErrorText: "An unexpected error has occurred" readonly property string defaultErrorText: "An unexpected error has occurred"

View file

@ -1,8 +1,6 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Layouts 1.11 import QtQuick.Controls
import QtQuick.Controls 2.15
import MaterialIcons 2.2
import Utils 1.0
import Controls 1.0 import Controls 1.0
/** /**
@ -42,7 +40,9 @@ ListView {
filterText: root.filterText filterText: root.filterText
objectsHideable: root.objectsHideable objectsHideable: root.objectsHideable
attribute: object attribute: object
onDoubleClicked: root.attributeDoubleClicked(mouse, attr) onDoubleClicked: function(mouse, attr) {
root.attributeDoubleClicked(mouse, attr)
}
} }
onActiveChanged: height = active ? item.implicitHeight : -spacing onActiveChanged: height = active ? item.implicitHeight : -spacing
@ -56,12 +56,10 @@ ListView {
} }
} }
// Helper MouseArea to lose edit/activeFocus // Helper MouseArea to lose edit/activeFocus when clicking on the background
// when clicking on the background
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: forceActiveFocus() onClicked: forceActiveFocus()
z: -1 z: -1
} }
} }

View file

@ -1,24 +1,26 @@
import QtQuick 2.9 import QtQuick
import QtQuick.Layouts 1.3 import QtQuick.Layouts
import QtQuick.Controls 2.2 import QtQuick.Controls
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs
import MaterialIcons 2.2 import MaterialIcons 2.2
import Utils 1.0 import Utils 1.0
import Controls 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 { RowLayout {
id: root id: root
property variant attribute: null 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 bool objectsHideable: true
property string filterText: "" property string filterText: ""
property alias label: parameterLabel // accessor to the internal Label (attribute's name) 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 int labelWidth // Shortcut to set the fixed size of the Label
readonly property bool editable: !attribute.isOutput && !attribute.isLink && !readOnly readonly property bool editable: !attribute.isOutput && !attribute.isLink && !readOnly
@ -42,7 +44,7 @@ RowLayout {
Pane { Pane {
background: Rectangle { background: Rectangle {
id: background 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 padding: 0
Layout.preferredWidth: labelWidth || implicitWidth Layout.preferredWidth: labelWidth || implicitWidth
@ -66,8 +68,10 @@ RowLayout {
text: object.label text: object.label
color: { color: {
if ((object.hasOutputConnections || object.isLink) && !object.enabled) return Colors.lightgrey if (object != undefined && (object.hasOutputConnections || object.isLink) && !object.enabled)
else return palette.text return Colors.lightgrey
else
return palette.text
} }
// Tooltip hint with attribute's description // Tooltip hint with attribute's description
@ -88,10 +92,10 @@ RowLayout {
delay: 800 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 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 font.italic: object.isLink
MouseArea { MouseArea {
@ -152,7 +156,7 @@ RowLayout {
} }
} }
onClicked: { onClicked: function(mouse) {
forceActiveFocus() forceActiveFocus()
if (mouse.button == Qt.RightButton) { if (mouse.button == Qt.RightButton) {
var menu = menuComp.createObject(parameterLabel) var menu = menuComp.createObject(parameterLabel)
@ -198,37 +202,37 @@ RowLayout {
sourceComponent: { sourceComponent: {
// PushButtonParam always has value == undefined, so it needs to be excluded from this check // PushButtonParam always has value == undefined, so it needs to be excluded from this check
if (attribute.type != "PushButtonParam" && attribute.value === undefined) { if (attribute.type != "PushButtonParam" && attribute.value === undefined) {
return notComputed_component return notComputedComponent
} }
switch (attribute.type) { switch (attribute.type) {
case "PushButtonParam": case "PushButtonParam":
return pushButton_component return pushButtonComponent
case "ChoiceParam": case "ChoiceParam":
return attribute.desc.exclusive ? comboBox_component : multiChoice_component return attribute.desc.exclusive ? comboBoxComponent : multiChoiceComponent
case "IntParam": return slider_component case "IntParam": return sliderComponent
case "FloatParam": case "FloatParam":
if (attribute.desc.semantic === 'color/hue') if (attribute.desc.semantic === 'color/hue')
return color_hue_component return colorHueComponent
return slider_component return sliderComponent
case "BoolParam": case "BoolParam":
return checkbox_component return checkboxComponent
case "ListAttribute": case "ListAttribute":
return listAttribute_component return listAttributeComponent
case "GroupAttribute": case "GroupAttribute":
return groupAttribute_component return groupAttributeComponent
case "StringParam": case "StringParam":
if (attribute.desc.semantic.includes('multiline')) if (attribute.desc.semantic.includes('multiline'))
return textArea_component return textAreaComponent
return textField_component return textFieldComponent
case "ColorParam": case "ColorParam":
return color_component return colorComponent
default: default:
return textField_component return textFieldComponent
} }
} }
Component { Component {
id: notComputed_component id: notComputedComponent
Label { Label {
anchors.fill: parent anchors.fill: parent
text: MaterialIcons.do_not_disturb_alt text: MaterialIcons.do_not_disturb_alt
@ -244,7 +248,7 @@ RowLayout {
} }
Component { Component {
id: pushButton_component id: pushButtonComponent
Button { Button {
text: attribute.label text: attribute.label
enabled: root.editable enabled: root.editable
@ -255,7 +259,7 @@ RowLayout {
} }
Component { Component {
id: textField_component id: textFieldComponent
TextField { TextField {
id: textField id: textField
readOnly: !root.editable readOnly: !root.editable
@ -268,7 +272,7 @@ RowLayout {
setTextFieldAttribute(text) setTextFieldAttribute(text)
parameterLabel.forceActiveFocus() parameterLabel.forceActiveFocus()
} }
Keys.onPressed: (event)=> { Keys.onPressed: function(event) {
if ((event.key == Qt.Key_Escape)) { if ((event.key == Qt.Key_Escape)) {
event.accepted = true event.accepted = true
parameterLabel.forceActiveFocus() parameterLabel.forceActiveFocus()
@ -281,7 +285,7 @@ RowLayout {
DropArea { DropArea {
enabled: root.editable enabled: root.editable
anchors.fill: parent anchors.fill: parent
onDropped: { onDropped: function(drop) {
if (drop.hasUrls) if (drop.hasUrls)
setTextFieldAttribute(Filepath.urlToString(drop.urls[0])) setTextFieldAttribute(Filepath.urlToString(drop.urls[0]))
else if (drop.hasText && drop.text != '') else if (drop.hasText && drop.text != '')
@ -291,8 +295,8 @@ RowLayout {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onClicked: (mouse)=> { onClicked: function(mouse) {
// Do not loose the selection during the right click // Do not lose the selection during the right click
textField.persistentSelection = true textField.persistentSelection = true
// We store the status of the activeFocus before opening the popup // We store the status of the activeFocus before opening the popup
textField.memoryActiveFocus = textField.activeFocus textField.memoryActiveFocus = textField.activeFocus
@ -318,7 +322,7 @@ RowLayout {
Clipboard.clear() Clipboard.clear()
Clipboard.setText(attribute.value) Clipboard.setText(attribute.value)
} else { } else {
// copy selection only // Copy selection only
textField.copy() textField.copy()
} }
} }
@ -328,12 +332,12 @@ RowLayout {
enabled: Clipboard.getText() != "" && !readOnly enabled: Clipboard.getText() != "" && !readOnly
onTriggered: { onTriggered: {
if (textField.memoryActiveFocus) { 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 // or if there is no selection insert at the cursor position
var before = textField.text.substr(0, textField.selectionStart) var before = textField.text.substr(0, textField.selectionStart)
var after = textField.text.substr(textField.selectionEnd, textField.text.length) var after = textField.text.substr(textField.selectionEnd, textField.text.length)
setTextFieldAttribute(before + Clipboard.getText() + after) 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 textField.cursorPosition = before.length + Clipboard.getText().length
} else { } else {
setTextFieldAttribute(Clipboard.getText()) setTextFieldAttribute(Clipboard.getText())
@ -346,7 +350,7 @@ RowLayout {
} }
Component { Component {
id: textArea_component id: textAreaComponent
Rectangle { Rectangle {
// Fixed background for the flickable object // Fixed background for the flickable object
@ -395,17 +399,20 @@ RowLayout {
} }
Component { Component {
id: color_component id: colorComponent
RowLayout { RowLayout {
CheckBox { CheckBox {
id: color_checkbox id: colorCheckbox
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
checked: node && node.color === "" ? false : true checked: node && node.color === "" ? false : true
checkable: root.editable checkable: root.editable
text: "Custom Color" text: "Custom Color"
onClicked: { onClicked: {
if (checked) { if (checked) {
_reconstruction.setAttribute(attribute, "#0000FF") if (colorText.text == "")
_reconstruction.setAttribute(attribute, "#0000FF")
else
_reconstruction.setAttribute(attribute, colorText.text)
} else { } else {
_reconstruction.setAttribute(attribute, "") _reconstruction.setAttribute(attribute, "")
} }
@ -415,9 +422,9 @@ RowLayout {
id: colorText id: colorText
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
implicitWidth: 100 implicitWidth: 100
enabled: color_checkbox.checked && root.editable enabled: colorCheckbox.checked && root.editable
visible: color_checkbox.checked visible: colorCheckbox.checked
text: color_checkbox.checked ? attribute.value : "" text: colorCheckbox.checked ? attribute.value : ""
selectByMouse: true selectByMouse: true
onEditingFinished: setTextFieldAttribute(text) onEditingFinished: setTextFieldAttribute(text)
onAccepted: setTextFieldAttribute(text) onAccepted: setTextFieldAttribute(text)
@ -431,8 +438,8 @@ RowLayout {
height: colorText.height height: colorText.height
width: colorText.width / 2 width: colorText.width / 2
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
visible: color_checkbox.checked visible: colorCheckbox.checked
color: color_checkbox.checked ? attribute.value : "" color: colorCheckbox.checked ? colorDialog.selectedColor : ""
MouseArea { MouseArea {
enabled: root.editable enabled: root.editable
@ -444,9 +451,9 @@ RowLayout {
ColorDialog { ColorDialog {
id: colorDialog id: colorDialog
title: "Please choose a color" title: "Please choose a color"
color: attribute.value selectedColor: colorText.text
onAccepted: { onAccepted: {
colorText.text = color colorText.text = colorDialog.selectedColor
// Artificially trigger change of attribute value // Artificially trigger change of attribute value
colorText.editingFinished() colorText.editingFinished()
close() close()
@ -461,13 +468,13 @@ RowLayout {
} }
Component { Component {
id: comboBox_component id: comboBoxComponent
FilterComboBox { FilterComboBox {
inputModel: attribute.values inputModel: attribute.values
Component.onCompleted: { 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) var idx = find(attribute.value)
if (idx === -1) { if (idx === -1) {
displayText = attribute.value displayText = attribute.value
@ -484,10 +491,10 @@ RowLayout {
Connections { Connections {
target: attribute target: attribute
function onValueChanged() { 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 // 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) { var valuesAsString = attribute.values.map(function(value) {
return value.toString() return value.toString()
}) })
@ -503,10 +510,10 @@ RowLayout {
} }
Component { Component {
id: multiChoice_component id: multiChoiceComponent
Flow { Flow {
Repeater { Repeater {
id: checkbox_repeater id: checkboxRepeater
model: attribute.values model: attribute.values
delegate: CheckBox { delegate: CheckBox {
enabled: root.editable enabled: root.editable
@ -515,9 +522,9 @@ RowLayout {
onToggled: { onToggled: {
var t = attribute.value var t = attribute.value
if (!checked) { if (!checked) {
t.splice(t.indexOf(modelData), 1) // remove element t.splice(t.indexOf(modelData), 1) // Remove element
} else { } else {
t.push(modelData) // add element t.push(modelData) // Add element
} }
_reconstruction.setAttribute(attribute, t) _reconstruction.setAttribute(attribute, t)
} }
@ -527,7 +534,7 @@ RowLayout {
} }
Component { Component {
id: slider_component id: sliderComponent
RowLayout { RowLayout {
TextField { TextField {
IntValidator { IntValidator {
@ -535,12 +542,12 @@ RowLayout {
} }
DoubleValidator { DoubleValidator {
id: doubleValidator id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding the system locale locale: 'C' // Use '.' decimal separator disregarding the system locale
} }
implicitWidth: 100 implicitWidth: 100
Layout.fillWidth: !slider.active Layout.fillWidth: !slider.active
enabled: root.editable 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) property string displayValue: String(slider.active && slider.item.pressed ? slider.item.formattedValue : attribute.value)
text: displayValue text: displayValue
selectByMouse: true selectByMouse: true
@ -594,7 +601,7 @@ RowLayout {
} }
Component { Component {
id: checkbox_component id: checkboxComponent
Row { Row {
CheckBox { CheckBox {
enabled: root.editable enabled: root.editable
@ -605,17 +612,17 @@ RowLayout {
} }
Component { Component {
id: listAttribute_component id: listAttributeComponent
ColumnLayout { ColumnLayout {
id: listAttribute_layout id: listAttributeLayout
width: parent.width width: parent.width
property bool expanded: false property bool expanded: false
RowLayout { RowLayout {
spacing: 4 spacing: 4
ToolButton { 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 font.family: MaterialIcons.fontFamily
onClicked: listAttribute_layout.expanded = !listAttribute_layout.expanded onClicked: listAttributeLayout.expanded = !listAttributeLayout.expanded
} }
Label { Label {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
@ -632,7 +639,7 @@ RowLayout {
} }
ListView { ListView {
id: lv id: lv
model: listAttribute_layout.expanded ? attribute.value : undefined model: listAttributeLayout.expanded ? attribute.value : undefined
visible: model !== undefined && count > 0 visible: model !== undefined && count > 0
implicitHeight: Math.min(contentHeight, 300) implicitHeight: Math.min(contentHeight, 300)
Layout.fillWidth: true Layout.fillWidth: true
@ -683,7 +690,7 @@ RowLayout {
} }
Component { Component {
id: groupAttribute_component id: groupAttributeComponent
ColumnLayout { ColumnLayout {
id: groupItem id: groupItem
Component.onCompleted: { Component.onCompleted: {
@ -692,7 +699,7 @@ RowLayout {
{ {
'model': Qt.binding(function() { return attribute.value }), 'model': Qt.binding(function() { return attribute.value }),
'readOnly': Qt.binding(function() { return root.readOnly }), '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 }), 'objectsHideable': Qt.binding(function() { return root.objectsHideable }),
'filterText': Qt.binding(function() { return root.filterText }), 'filterText': Qt.binding(function() { return root.filterText }),
}) })
@ -703,17 +710,17 @@ RowLayout {
} }
Component { Component {
id: color_hue_component id: colorHueComponent
RowLayout { RowLayout {
TextField { TextField {
implicitWidth: 100 implicitWidth: 100
enabled: root.editable 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) property string displayValue: String(slider.pressed ? slider.formattedValue : attribute.value)
text: displayValue text: displayValue
selectByMouse: true selectByMouse: true
validator: DoubleValidator { validator: DoubleValidator {
locale: 'C' // use '.' decimal separator disregarding the system locale locale: 'C' // Use '.' decimal separator disregarding the system locale
} }
onEditingFinished: setTextFieldAttribute(text) onEditingFinished: setTextFieldAttribute(text)
onAccepted: setTextFieldAttribute(text) onAccepted: setTextFieldAttribute(text)
@ -748,16 +755,7 @@ RowLayout {
width: control.availableWidth width: control.availableWidth
height: control.availableHeight height: control.availableHeight
blending: false blending: false
fragmentShader: " fragmentShader: "qrc:/shaders/AttributeItemDelegate.frag.qsb"
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);
}"
} }
} }
} }

View file

@ -1,12 +1,13 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Utils 1.0 import Utils 1.0
/** /**
The representation of an Attribute on a Node. * The representation of an Attribute on a Node.
*/ */
RowLayout { RowLayout {
id: root id: root
@ -43,24 +44,23 @@ RowLayout {
x: nameLabel.x x: nameLabel.x
} }
function updatePin(isSrc, isVisible) function updatePin(isSrc, isVisible) {
{
if (isSrc) { if (isSrc) {
innerOutputAnchor.linkEnabled = isVisible innerOutputAnchor.linkEnabled = isVisible
} else { } else {
innerInputAnchor.linkEnabled = isVisible innerInputAnchor.linkEnabled = isVisible
} }
} }
// Instantiate empty Items for each child attribute // Instantiate empty Items for each child attribute
Repeater { Repeater {
id: childrenRepeater id: childrenRepeater
model: isList && !attribute.isLink ? attribute.value : 0 model: isList && !attribute.isLink ? attribute.value : 0
onItemAdded: childPinCreated(item.childAttribute, item) onItemAdded: function(index, item) { childPinCreated(item.childAttribute, root) }
onItemRemoved: childPinDeleted(item.childAttribute, item) onItemRemoved: function(index, item) { childPinDeleted(item.childAttribute, root) }
delegate: Item { delegate: Item {
property var childAttribute: object property var childAttribute: object
visible: false
} }
} }
@ -95,38 +95,39 @@ RowLayout {
property bool acceptableDrop: false 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.fill: parent
anchors.margins: -2 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 anchors.rightMargin: -root.width * 0.3
keys: [inputDragTarget.objectName] keys: [inputDragTarget.objectName]
onEntered: { onEntered: function(drag) {
// Check if attributes are compatible to create a valid connection // Check if attributes are compatible to create a valid connection
if (root.readOnly // cannot connect on a read-only attribute if (root.readOnly // Cannot connect on a read-only attribute
|| drag.source.objectName != inputDragTarget.objectName // not an edge connector || drag.source.objectName != inputDragTarget.objectName // Not an edge connector
|| drag.source.baseType !== inputDragTarget.baseType // not the same base type || drag.source.baseType !== inputDragTarget.baseType // Not the same base type
|| drag.source.nodeItem === inputDragTarget.nodeItem // connection between attributes of the same node || 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.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) || 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 // Refuse attributes connection
drag.accepted = false drag.accepted = false
} else if (inputDragTarget.attribute.isLink) { // already connected attribute } else if (inputDragTarget.attribute.isLink) { // Already connected attribute
root.edgeAboutToBeRemoved(inputDragTarget.attribute) root.edgeAboutToBeRemoved(inputDragTarget.attribute)
} }
inputDropArea.acceptableDrop = drag.accepted inputDropArea.acceptableDrop = drag.accepted
} }
onExited: { onExited: {
if (inputDragTarget.attribute.isLink) { // already connected attribute if (inputDragTarget.attribute.isLink) { // Already connected attribute
root.edgeAboutToBeRemoved(undefined) root.edgeAboutToBeRemoved(undefined)
} }
acceptableDrop = false acceptableDrop = false
drag.source.dropAccepted = false drag.source.dropAccepted = false
} }
onDropped: { onDropped: function(drop) {
root.edgeAboutToBeRemoved(undefined) root.edgeAboutToBeRemoved(undefined)
_reconstruction.addEdge(drag.source.attribute, inputDragTarget.attribute) _reconstruction.addEdge(drag.source.attribute, inputDragTarget.attribute)
} }
@ -160,11 +161,11 @@ RowLayout {
drag.smoothed: false drag.smoothed: false
enabled: !root.readOnly enabled: !root.readOnly
anchors.fill: parent 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.margins: inputDropArea.anchors.margins
anchors.leftMargin: inputDropArea.anchors.leftMargin anchors.leftMargin: inputDropArea.anchors.leftMargin
anchors.rightMargin: inputDropArea.anchors.rightMargin anchors.rightMargin: inputDropArea.anchors.rightMargin
onPressed: { onPressed: function(mouse) {
root.pressed(mouse) root.pressed(mouse)
} }
onReleased: { onReleased: {
@ -185,14 +186,11 @@ RowLayout {
} }
} }
// Attribute name // Attribute name
Item { Item {
id: nameContainer id: nameContainer
Layout.fillWidth: true
implicitHeight: childrenRect.height implicitHeight: childrenRect.height
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Label { Label {
@ -208,13 +206,13 @@ RowLayout {
anchors.right: attribute && attribute.isOutput ? parent.right : undefined anchors.right: attribute && attribute.isOutput ? parent.right : undefined
rightPadding: 0 rightPadding: 0
color: { 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 return hovered ? palette.highlight : palette.text
} }
} }
} }
Rectangle { Rectangle {
id: outputAnchor id: outputAnchor
@ -248,25 +246,25 @@ RowLayout {
property bool acceptableDrop: false 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.fill: parent
anchors.margins: -2 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 anchors.leftMargin: -root.width * 0.2
keys: [outputDragTarget.objectName] keys: [outputDragTarget.objectName]
onEntered: { onEntered: function(drag) {
// Check if attributes are compatible to create a valid connection // Check if attributes are compatible to create a valid connection
if (drag.source.objectName != outputDragTarget.objectName // not an edge connector if (drag.source.objectName != outputDragTarget.objectName // Not an edge connector
|| drag.source.baseType !== outputDragTarget.baseType // not the same base type || drag.source.baseType !== outputDragTarget.baseType // Not the same base type
|| drag.source.nodeItem === outputDragTarget.nodeItem // connection between attributes of the same node || 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 && 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.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 || drag.source.connectorType === "output" // Refuse to connect an output pin on another one
) { ) {
// Refuse attributes connection // Refuse attributes connection
drag.accepted = false 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) root.edgeAboutToBeRemoved(drag.source.attribute)
} }
outputDropArea.acceptableDrop = drag.accepted outputDropArea.acceptableDrop = drag.accepted
@ -276,7 +274,7 @@ RowLayout {
acceptableDrop = false acceptableDrop = false
} }
onDropped: { onDropped: function(drop) {
root.edgeAboutToBeRemoved(undefined) root.edgeAboutToBeRemoved(undefined)
_reconstruction.addEdge(outputDragTarget.attribute, drag.source.attribute) _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 // Move the edge's tip straight to the the current mouse position instead of waiting after the drag operation has started
drag.smoothed: false drag.smoothed: false
anchors.fill: parent 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.margins: outputDropArea.anchors.margins
anchors.leftMargin: outputDropArea.anchors.leftMargin anchors.leftMargin: outputDropArea.anchors.leftMargin
anchors.rightMargin: outputDropArea.anchors.rightMargin anchors.rightMargin: outputDropArea.anchors.rightMargin
onPressed: root.pressed(mouse) onPressed: function(mouse) { root.pressed(mouse) }
onReleased: outputDragTarget.Drag.drop() onReleased: outputDragTarget.Drag.drop()
hoverEnabled: true hoverEnabled: true

View file

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

View file

@ -1,14 +1,16 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2 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): * Provides 2 delegates (can be set using sourceComponent property):
* - iconDelegate (default): icon + tooltip with information about the issue * - iconDelegate (default): icon + tooltip with information about the issue
* - bannerDelegate: banner with issue info + upgrade request button * - bannerDelegate: banner with issue info + upgrade request button
*/ */
Loader { Loader {
id: root id: root

View file

@ -1,23 +1,25 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2 import MaterialIcons 2.2
import Controls 1.0 import Controls 1.0
import Utils 1.0 import Utils 1.0
/** /**
* CompatibilityManager summarizes and allows to resolve compatibility issues. * CompatibilityManager summarizes and allows to resolve compatibility issues.
*/ */
MessageDialog { MessageDialog {
id: root id: root
// the UIGraph instance // The UIGraph instance
property var uigraph property var uigraph
// alias to underlying compatibilityNodes model // Alias to underlying compatibilityNodes model
readonly property var nodesModel: uigraph ? uigraph.graph.compatibilityNodes : undefined 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 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: { readonly property int upgradableCount: {
var count = 0 var count = 0
for (var i = 0; i < issueCount; ++i) { for (var i = 0; i < issueCount; ++i) {
@ -27,7 +29,7 @@ MessageDialog {
return count return count
} }
// override MessageDialog.getAsString to add compatibility report // Override MessageDialog.getAsString to add compatibility report
function getAsString() { function getAsString() {
var t = asString + "\n" var t = asString + "\n"
t += '-------------------------\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 GraphEditor 1.0
import QtQuick.Shapes 1.15
import MaterialIcons 2.2 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 { Item {
id: root id: root
@ -40,7 +42,7 @@ Item {
Shape { Shape {
anchors.fill: parent 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 vendorExtensionsEnabled: false
opacity: 0.7 opacity: 0.7
@ -53,7 +55,7 @@ Item {
strokeColor: "#3E3E3E" strokeColor: "#3E3E3E"
strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine
strokeWidth: 1 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) readonly property real visualWidth: Math.max(strokeWidth, 1)
dashPattern: [6 / visualWidth, 4 / visualWidth] dashPattern: [6 / visualWidth, 4 / visualWidth]
capStyle: ShapePath.RoundCap capStyle: ShapePath.RoundCap
@ -68,7 +70,6 @@ Item {
control2X: x - ctrlPtDist control2X: x - ctrlPtDist
control2Y: y control2Y: y
} }
} }
ShapePath { ShapePath {
@ -80,7 +81,7 @@ Item {
strokeColor: root.isForLoop ? root.color : "transparent" strokeColor: root.isForLoop ? root.color : "transparent"
strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine strokeStyle: edge !== undefined && ((edge.src !== undefined && edge.src.isOutput) || edge.dst === undefined) ? ShapePath.SolidLine : ShapePath.DashLine
strokeWidth: root.thickness 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) readonly property real visualWidth: Math.max(strokeWidth, 1)
dashPattern: [6 / visualWidth, 4 / visualWidth] dashPattern: [6 / visualWidth, 4 / visualWidth]
capStyle: ShapePath.RoundCap capStyle: ShapePath.RoundCap
@ -97,11 +98,13 @@ Item {
} }
} }
} }
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 x: (root.startX + root.endX) / 2
y: (root.startY + root.endY) / 2 y: (root.startY + root.endY) / 2
visible: root.isForLoop visible: root.isForLoop
Rectangle { Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
property int margin: 2 property int margin: 2
@ -109,6 +112,7 @@ Item {
height: icon.height + 2 * margin height: icon.height + 2 * margin
radius: width radius: width
color: path.strokeColor color: path.strokeColor
MaterialToolLabel { MaterialToolLabel {
id: icon id: icon
anchors.centerIn: parent anchors.centerIn: parent
@ -119,6 +123,7 @@ Item {
color: palette.base color: palette.base
ToolTip.text: "Foreach Loop" ToolTip.text: "Foreach Loop"
} }
MouseArea { MouseArea {
id: loopArea id: loopArea
anchors.fill: parent anchors.fill: parent
@ -126,16 +131,25 @@ Item {
onClicked: root.pressed(arguments[0]) onClicked: root.pressed(arguments[0])
} }
} }
} }
EdgeMouseArea { EdgeMouseArea {
id: edgeArea id: edgeArea
anchors.fill: parent anchors.fill: parent
curveScale: cubic.ctrlPtDist / root.width // normalize by width
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
thickness: root.thickness + 4 thickness: root.thickness + 4
onPressed: root.pressed(arguments[0]) // can't get named args, use arguments array onPressed: function(event) {
onReleased: root.released(arguments[0]) 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
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import Controls 1.0 import Controls 1.0
import Utils 1.0
import MaterialIcons 2.2 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 { Item {
id: root id: root
property variant uigraph: null /// Meshroom ui graph (UIGraph) property variant uigraph: null /// Meshroom UI graph (UIGraph)
readonly property variant graph: uigraph ? uigraph.graph : null /// core graph contained in ui graph 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 variant nodeTypesModel: null /// The list of node types that can be instantiated
property real maxZoom: 2.0 property real maxZoom: 2.0
property real minZoom: 0.1 property real minZoom: 0.1
@ -21,7 +23,7 @@ Item {
property var _attributeToDelegate: ({}) property var _attributeToDelegate: ({})
// signals // Signals
signal workspaceMoved() signal workspaceMoved()
signal workspaceClicked() signal workspaceClicked()
@ -33,10 +35,9 @@ Item {
property int nbMeshroomScenes: 0 property int nbMeshroomScenes: 0
property int nbDraggedFiles: 0 property int nbDraggedFiles: 0
// Files have been dropped signal filesDropped(var drop, var mousePosition) // Files have been dropped
signal filesDropped(var drop, var mousePosition)
// trigger initial fit() after initialization // Trigger initial fit() after initialization
// (ensure GraphEditor has its final size) // (ensure GraphEditor has its final size)
Component.onCompleted: firstFitTimer.start() Component.onCompleted: firstFitTimer.start()
@ -132,7 +133,7 @@ Item {
return mapToItem(draggable, mouseArea.width / 2, mouseArea.height / 2) return mapToItem(draggable, mouseArea.width / 2, mouseArea.height / 2)
} }
Keys.onPressed: { Keys.onPressed: function(event) {
if (event.key === Qt.Key_F) { if (event.key === Qt.Key_F) {
fit() fit()
} else if (event.key === Qt.Key_Delete) { } else if (event.key === Qt.Key_Delete) {
@ -172,7 +173,7 @@ Item {
drag.threshold: 0 drag.threshold: 0
cursorShape: drag.target == draggable ? Qt.ClosedHandCursor : Qt.ArrowCursor cursorShape: drag.target == draggable ? Qt.ClosedHandCursor : Qt.ArrowCursor
onWheel: { onWheel: function(wheel) {
var zoomFactor = wheel.angleDelta.y > 0 ? factor : 1 / factor var zoomFactor = wheel.angleDelta.y > 0 ? factor : 1 / factor
var scale = draggable.scale * zoomFactor var scale = draggable.scale * zoomFactor
scale = Math.min(Math.max(minZoom, scale), maxZoom) scale = Math.min(Math.max(minZoom, scale), maxZoom)
@ -185,7 +186,7 @@ Item {
workspaceMoved() workspaceMoved()
} }
onPressed: { onPressed: function(mouse) {
if (mouse.button != Qt.MiddleButton && mouse.modifiers == Qt.NoModifier) { if (mouse.button != Qt.MiddleButton && mouse.modifiers == Qt.NoModifier) {
uigraph.clearNodeSelection() uigraph.clearNodeSelection()
} }
@ -200,19 +201,21 @@ Item {
drag.target = draggable // start drag drag.target = draggable // start drag
} }
} }
onReleased: { onReleased: {
drag.target = undefined // stop drag drag.target = undefined // stop drag
root.forceActiveFocus() root.forceActiveFocus()
workspaceClicked() workspaceClicked()
} }
onPositionChanged: { onPositionChanged: {
if (drag.active) if (drag.active)
workspaceMoved() workspaceMoved()
} }
onClicked: { onClicked: function(mouse) {
if (mouse.button == Qt.RightButton) { 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.spawnPosition = mouseArea.mapToItem(draggable, mouse.x, mouse.y)
newNodeMenu.popup() newNodeMenu.popup()
} }
@ -271,7 +274,7 @@ Item {
onVisibleChanged: { onVisibleChanged: {
searchBar.clear() searchBar.clear()
if (visible) { if (visible) {
// when menu is shown, give focus to the TextField filter // When menu is shown, give focus to the TextField filter
searchBar.forceActiveFocus() searchBar.forceActiveFocus()
} }
} }
@ -295,12 +298,12 @@ Item {
// Forward key events to the search bar to continue typing seamlessly // Forward key events to the search bar to continue typing seamlessly
// even if this delegate took the activeFocus due to mouse hovering // even if this delegate took the activeFocus due to mouse hovering
Keys.forwardTo: [searchBar.textField] Keys.forwardTo: [searchBar.textField]
Keys.onPressed: { Keys.onPressed: function(event) {
event.accepted = false; event.accepted = false;
switch (event.key) { switch (event.key) {
case Qt.Key_Return: case Qt.Key_Return:
case Qt.Key_Enter: case Qt.Key_Enter:
// create node on validation (Enter/Return keys) // Create node on validation (Enter/Return keys)
newNodeMenu.createNode(modelData) newNodeMenu.createNode(modelData)
event.accepted = true event.accepted = true
break break
@ -308,7 +311,7 @@ Item {
case Qt.Key_Down: case Qt.Key_Down:
case Qt.Key_Left: case Qt.Key_Left:
case Qt.Key_Right: 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: default:
searchBar.forceActiveFocus() searchBar.forceActiveFocus()
} }
@ -323,8 +326,8 @@ Item {
name: "invisible" name: "invisible"
PropertyChanges { PropertyChanges {
target: menuItemDelegate target: menuItemDelegate
height: 0 // make sure the item is no visible by setting height to 0 height: 0 // Make sure the item is no visible by setting height to 0
focusPolicy: Qt.NoFocus // don't grab focus when not visible focusPolicy: Qt.NoFocus // Don't grab focus when not visible
} }
} }
] ]
@ -335,7 +338,7 @@ Item {
id: nodeMenuRepeater id: nodeMenuRepeater
model: searchBar.text !== "" ? Object.values(newNodeMenu.menuKeys) : undefined model: searchBar.text !== "" ? Object.values(newNodeMenu.menuKeys) : undefined
// create Menu items from available items // Create Menu items from available items
delegate: menuItemDelegateComponent delegate: menuItemDelegateComponent
} }
@ -343,8 +346,13 @@ Item {
Instantiator { Instantiator {
id: instantiator id: instantiator
model: (searchBar.text === "") ? Object.keys(newNodeMenu.parseCategories()).sort() : undefined model: (searchBar.text === "") ? Object.keys(newNodeMenu.parseCategories()).sort() : undefined
onObjectAdded: newNodeMenu.insertMenu(index + 1, object ) // add sub-menu under the search bar onObjectAdded: function(index, object) {
onObjectRemoved: newNodeMenu.removeMenu(object) // Add sub-menu under the search bar
newNodeMenu.insertMenu(index + 1, object)
}
onObjectRemoved: function(index, object) {
newNodeMenu.removeMenu(object)
}
delegate: Menu { delegate: Menu {
title: modelData title: modelData
@ -352,8 +360,12 @@ Item {
Instantiator { Instantiator {
model: newNodeMenu.visible ? newNodeMenu.parseCategories()[modelData] : undefined model: newNodeMenu.visible ? newNodeMenu.parseCategories()[modelData] : undefined
onObjectAdded: newNodeSubMenu.insertItem(index, object) onObjectAdded: function(index, object) {
onObjectRemoved: newNodeSubMenu.removeItem(object) newNodeSubMenu.insertItem(index, object)
}
onObjectRemoved: function(index, object) {
newNodeSubMenu.removeItem(object)
}
delegate: menuItemDelegateComponent delegate: menuItemDelegateComponent
} }
} }
@ -417,7 +429,7 @@ Item {
const newSrcAttr = listAttr.value.at(value - 1) const newSrcAttr = listAttr.value.at(value - 1)
const dst = edgeMenu.currentEdge.dst 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) { if (newSrcAttr === edgeMenu.currentEdge.src && dst === edgeMenu.currentEdge.dst) {
return return
} }
@ -476,7 +488,7 @@ Item {
Repeater { Repeater {
id: edgesRepeater 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 model: nodeRepeater.loaded && root.graph ? root.graph.edges : undefined
delegate: Edge { delegate: Edge {
@ -500,12 +512,11 @@ Item {
} }
return (inFocus) ? 2 : 1 return (inFocus) ? 2 : 1
} }
point1x: isValidEdge ? src.globalX + src.outputAnchorPos.x : 0 point1x: isValidEdge ? src.globalX + src.outputAnchorPos.x : 0
point1y: isValidEdge ? src.globalY + src.outputAnchorPos.y : 0 point1y: isValidEdge ? src.globalY + src.outputAnchorPos.y : 0
point2x: isValidEdge ? dst.globalX + dst.inputAnchorPos.x : 0 point2x: isValidEdge ? dst.globalX + dst.inputAnchorPos.x : 0
point2y: isValidEdge ? dst.globalY + dst.inputAnchorPos.y : 0 point2y: isValidEdge ? dst.globalY + dst.inputAnchorPos.y : 0
onPressed: { onPressed: function(event) {
const canEdit = !edge.dst.node.locked const canEdit = !edge.dst.node.locked
if (event.button) { if (event.button) {
@ -540,7 +551,7 @@ Item {
id: nodeMenu id: nodeMenu
property var currentNode: null property var currentNode: null
property bool canComputeNode: currentNode != null && uigraph.graph.canComputeTopologically(currentNode) 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 int canSubmitOrCompute: currentNode != null && uigraph.graph.canSubmitOrCompute(currentNode)
property bool isComputed: { property bool isComputed: {
var count = 0 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: { onTriggered: {
@ -823,14 +834,14 @@ Item {
selected: uigraph.selectedNodes.contains(node) selected: uigraph.selectedNodes.contains(node)
hovered: uigraph.hoveredNode === node hovered: uigraph.hoveredNode === node
onAttributePinCreated: registerAttributePin(attribute, pin) onAttributePinCreated: function(attribute, pin) { registerAttributePin(attribute, pin) }
onAttributePinDeleted: unregisterAttributePin(attribute, pin) onAttributePinDeleted: function(attribute, pin) { unregisterAttributePin(attribute, pin) }
onPressed: { onPressed: function(mouse) {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (mouse.modifiers & Qt.ControlModifier && !(mouse.modifiers & Qt.AltModifier)) { if (mouse.modifiers & Qt.ControlModifier && !(mouse.modifiers & Qt.AltModifier)) {
if (mainSelected && selected) { 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.selectedNodes.remove(node)
uigraph.selectedNodesChanged() uigraph.selectedNodesChanged()
selectNode(null) selectNode(null)
@ -854,21 +865,19 @@ Item {
selectNode(node) 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 onEntered: uigraph.hoveredNode = node
onExited: uigraph.hoveredNode = null onExited: uigraph.hoveredNode = null
onEdgeAboutToBeRemoved: { onEdgeAboutToBeRemoved: function(input) {
/* /*
Sometimes the signals are not in the right order * Sometimes the signals are not in the right order because of weird Qt/QML update order
because of weird Qt/QML update order (next DropArea * (next DropArea entered signal before previous DropArea exited signal) so edgeAboutToBeRemoved
entered signal before previous DropArea exited signal) * must be set to undefined before it can be set to another attribute object.
so edgeAboutToBeRemoved must be set to undefined before */
it can be set to another attribute object.
*/
if (input === undefined) { if (input === undefined) {
if (nodeRepeater.temporaryEdgeAboutToBeRemoved === undefined) { if (nodeRepeater.temporaryEdgeAboutToBeRemoved === undefined) {
root.edgeAboutToBeRemoved = input root.edgeAboutToBeRemoved = input
@ -887,7 +896,7 @@ Item {
onPositionChanged: { onPositionChanged: {
if (dragging && uigraph.selectedNodes.contains(node)) { 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++) { for (var i = 0; i < nodeRepeater.count; i++) {
var otherNode = nodeRepeater.itemAt(i) var otherNode = nodeRepeater.itemAt(i)
if (uigraph.selectedNodes.contains(otherNode.node) && otherNode.node !== node) { 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 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 { Behavior on x {
enabled: !nodeRepeater.dragging enabled: !nodeRepeater.dragging
NumberAnimation { duration: 100 } NumberAnimation { duration: 100 }
@ -956,12 +965,12 @@ Item {
}) })
} }
onDropped: { onDropped: function(drop) {
if (nbMeshroomScenes == nbDraggedFiles || nbMeshroomScenes == 0) { 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 // from pixel values to graph reference system
var mousePosition = mapToItem(draggable, drag.x, drag.y) 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 // to create the corresponding nodes or open another scene
filesDropped(drop, mousePosition) filesDropped(drop, mousePosition)
} else { } else {
@ -1110,12 +1119,12 @@ Item {
} }
function nextItem() { function nextItem() {
// compute bounding box // Compute bounding box
var node = nodeRepeater.itemAt(filteredNodes.itemAt(navigation.currentIndex).index_) var node = nodeRepeater.itemAt(filteredNodes.itemAt(navigation.currentIndex).index_)
var bbox = Qt.rect(node.x, node.y, node.width, node.height) 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) 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.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 draggable.y = bbox.y*draggable.scale * -1 + (root.height - bbox.height * draggable.scale) * 0.5
} }
@ -1124,6 +1133,7 @@ Item {
function registerAttributePin(attribute, pin) { function registerAttributePin(attribute, pin) {
root._attributeToDelegate[attribute] = pin root._attributeToDelegate[attribute] = pin
} }
function unregisterAttributePin(attribute, pin) { function unregisterAttributePin(attribute, pin) {
delete root._attributeToDelegate[attribute] delete root._attributeToDelegate[attribute]
} }
@ -1148,11 +1158,11 @@ Item {
// Fit graph to fill root // Fit graph to fill root
function fit() { function fit() {
// compute bounding box // Compute bounding box
var bbox = boundingBox() 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) 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.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 draggable.y = bbox.y * draggable.scale * -1 + (root.height - bbox.height * draggable.scale) * 0.5
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,17 +1,16 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0 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 * To ease monitoring, it provides periodic auto-reload of the opened file
* if the related NodeChunk is being computed. * if the related NodeChunk is being computed.
*/ */
FocusScope { FocusScope {
id: root id: root
property variant node property variant node
@ -29,7 +28,7 @@ FocusScope {
anchors.fill: parent anchors.fill: parent
property string currentFile: (root.currentChunkIndex >= 0 && root.currentChunk) ? root.currentChunk["logFile"] : "" 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 sourceComponent: textFileViewerComponent
} }
@ -40,9 +39,8 @@ FocusScope {
TextFileViewer { TextFileViewer {
id: textFileViewer id: textFileViewer
anchors.fill: parent anchors.fill: parent
source: componentLoader.source source: componentLoader.sourceFile
autoReload: root.currentChunk !== undefined && root.currentChunk.statusName === "RUNNING" 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
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0
import Utils 1.0
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 * To ease monitoring, it provides periodic auto-reload of the opened file
* if the related NodeChunk is being computed. * if the related NodeChunk is being computed.
*/ */
FocusScope { FocusScope {
id: root id: root
@ -27,7 +25,7 @@ FocusScope {
clip: true clip: true
anchors.fill: parent anchors.fill: parent
property string currentFile: currentChunk ? currentChunk["statisticsFile"] : "" property string currentFile: currentChunk ? currentChunk["statisticsFile"] : ""
property url source: Filepath.stringToUrl(currentFile) property url sourceFile: Filepath.stringToUrl(currentFile)
sourceComponent: chunksLV.chunksSummary ? statViewerComponent : chunkStatViewerComponent sourceComponent: chunksLV.chunksSummary ? statViewerComponent : chunkStatViewerComponent
} }
@ -37,7 +35,7 @@ FocusScope {
StatViewer { StatViewer {
id: statViewer id: statViewer
anchors.fill: parent anchors.fill: parent
source: componentLoader.source source: componentLoader.sourceFile
} }
} }

View file

@ -1,17 +1,14 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0
import "common.js" as Common
/** /**
* 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 * To ease monitoring, it provides periodic auto-reload of the opened file
* if the related NodeChunk is being computed. * if the related NodeChunk is being computed.
*/ */
FocusScope { FocusScope {
id: root id: root
property variant node property variant node
@ -26,7 +23,7 @@ FocusScope {
anchors.fill: parent anchors.fill: parent
property string currentFile: (root.currentChunkIndex >= 0) ? root.currentChunk["statusFile"] : "" property string currentFile: (root.currentChunkIndex >= 0) ? root.currentChunk["statusFile"] : ""
property url source: Filepath.stringToUrl(currentFile) property url sourceFile: Filepath.stringToUrl(currentFile)
sourceComponent: statViewerComponent sourceComponent: statViewerComponent
} }
@ -35,7 +32,7 @@ FocusScope {
id: statViewerComponent id: statViewerComponent
Item { Item {
id: statusViewer id: statusViewer
property url source: componentLoader.source property url source: componentLoader.sourceFile
property var lastModified: undefined property var lastModified: undefined
onSourceChanged: { onSourceChanged: {
@ -46,7 +43,7 @@ FocusScope {
id: statusListModel id: statusListModel
function readSourceFile() { 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")) if (!Filepath.urlToString(source).endsWith("status"))
return return
@ -55,31 +52,28 @@ FocusScope {
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
// console.warn("StatusListModel: read valid file")
if (lastModified === undefined || lastModified !== xhr.getResponseHeader('Last-Modified')) { if (lastModified === undefined || lastModified !== xhr.getResponseHeader('Last-Modified')) {
lastModified = xhr.getResponseHeader('Last-Modified') lastModified = xhr.getResponseHeader('Last-Modified')
try { try {
var jsonObject = JSON.parse(xhr.responseText) var jsonObject = JSON.parse(xhr.responseText)
var entries = [] 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) { for (var key in jsonObject) {
var entry = {} var entry = {}
entry["key"] = key entry["key"] = key
entry["value"] = String(jsonObject[key]) entry["value"] = String(jsonObject[key])
entries.push(entry) 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.clear()
statusListModel.append(entries) statusListModel.append(entries)
} catch(exc) { } catch(exc) {
// console.warn("StatusListModel: failed to read file")
lastModified = undefined lastModified = undefined
statusListModel.clear() statusListModel.clear()
} }
} }
} else { } else {
// console.warn("StatusListModel: invalid file")
lastModified = undefined lastModified = undefined
statusListModel.clear() statusListModel.clear()
} }
@ -103,7 +97,6 @@ FocusScope {
Rectangle { Rectangle {
id: statusKey id: statusKey
anchors.margins: 2 anchors.margins: 2
// height: statusValue.height
color: Qt.darker(activePalette.window, 1.1) color: Qt.darker(activePalette.window, 1.1)
Layout.preferredWidth: sizeHandle.x Layout.preferredWidth: sizeHandle.x
Layout.minimumWidth: 10.0 * Qt.application.font.pixelSize Layout.minimumWidth: 10.0 * Qt.application.font.pixelSize

View file

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

View file

@ -1,10 +1,11 @@
import QtQuick 2.15 import QtCharts
import QtQuick.Controls 2.15 import QtQuick
import QtCharts 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import Utils 1.0
import Charts 1.0 import Charts 1.0
import MaterialIcons 2.2 import MaterialIcons 2.2
import Utils 1.0
Item { Item {
@ -36,8 +37,7 @@ Item {
property color textColor: Colors.sysPalette.text property color textColor: Colors.sysPalette.text
readonly property var colors: [
readonly property var colors: [
"#f44336", "#f44336",
"#e91e63", "#e91e63",
"#9c27b0", "#9c27b0",
@ -93,7 +93,7 @@ Item {
} }
function readSourceFile() { 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")) if (!Filepath.urlToString(source).endsWith("statistics"))
return return
@ -102,7 +102,7 @@ Item {
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) { 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 { try {
root.jsonObject = JSON.parse(xhr.responseText) root.jsonObject = JSON.parse(xhr.responseText)
} catch(exc) { } catch(exc) {
@ -111,7 +111,7 @@ Item {
return return
} }
resetCharts() resetCharts()
sourceModified = xhr.getResponseHeader('Last-Modified') sourceModified = xhr.getResponseHeader("Last-Modified")
root.createCharts() root.createCharts()
reloadTimer.restart() reloadTimer.restart()
} }
@ -129,8 +129,8 @@ Item {
} }
function createCharts() { function createCharts() {
root.deltaTime = getPropertyWithDefault(jsonObject, 'interval', 30) / 60.0; root.deltaTime = getPropertyWithDefault(jsonObject, "interval", 30) / 60.0;
root.fileVersion = getPropertyWithDefault(jsonObject, 'fileVersion', 0.0) root.fileVersion = getPropertyWithDefault(jsonObject, "fileVersion", 0.0)
initCpuChart() initCpuChart()
initRamChart() initRamChart()
initGpuChart() initGpuChart()
@ -157,7 +157,7 @@ Item {
var nbCores = categories.length var nbCores = categories.length
root.nbCores = nbCores root.nbCores = nbCores
root.cpuFrequency = getPropertyWithDefault(jsonObject.computer, 'cpuFreq', -1) root.cpuFrequency = getPropertyWithDefault(jsonObject.computer, "cpuFreq", -1)
root.nbReads = categories[0].length-1 root.nbReads = categories[0].length-1
@ -218,9 +218,9 @@ Item {
function initRamChart() { 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: " root.ramLabel = "RAM: "
if (root.ramTotal <= 0) { if (root.ramTotal <= 0) {
var maxRamPeak = 0 var maxRamPeak = 0
@ -248,17 +248,18 @@ Item {
ramSerie.color = colors[10] ramSerie.color = colors[10]
} }
/************************** /**************************
*** GPU *** *** GPU ***
**************************/ **************************/
function initGpuChart() { function initGpuChart() {
root.gpuTotalMemory = getPropertyWithDefault(jsonObject.computer, 'gpuMemoryTotal', 0) root.gpuTotalMemory = getPropertyWithDefault(jsonObject.computer, "gpuMemoryTotal", 0)
root.gpuName = getPropertyWithDefault(jsonObject.computer, 'gpuName', '') root.gpuName = getPropertyWithDefault(jsonObject.computer, "gpuName", "")
var gpuUsedMemory = getPropertyWithDefault(jsonObject.computer.curves, 'gpuMemoryUsed', 0) var gpuUsedMemory = getPropertyWithDefault(jsonObject.computer.curves, "gpuMemoryUsed", 0)
var gpuUsed = getPropertyWithDefault(jsonObject.computer.curves, 'gpuUsed', 0) var gpuUsed = getPropertyWithDefault(jsonObject.computer.curves, "gpuUsed", 0)
var gpuTemperature = getPropertyWithDefault(jsonObject.computer.curves, 'gpuTemperature', 0) var gpuTemperature = getPropertyWithDefault(jsonObject.computer.curves, "gpuTemperature", 0)
var gpuUsedSerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "GPU", valueGpuX, valueGpuY) var gpuUsedSerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "GPU", valueGpuX, valueGpuY)
var gpuUsedMemorySerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "Memory", valueGpuX, valueGpuY) var gpuUsedMemorySerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "Memory", valueGpuX, valueGpuY)
@ -389,7 +390,7 @@ Item {
plotAreaColor: "transparent" plotAreaColor: "transparent"
titleColor: textColor 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" title: "CPU: " + root.nbCores + " cores, " + root.cpuFrequency + "MHz"
ValueAxis { ValueAxis {
@ -421,7 +422,6 @@ Item {
} }
/************************** /**************************
*** RAM UI *** *** RAM UI ***
**************************/ **************************/
@ -443,7 +443,7 @@ Item {
plotAreaColor: "transparent" plotAreaColor: "transparent"
titleColor: textColor 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" title: root.ramLabel + root.ramTotal + "GB"
ValueAxis { ValueAxis {
@ -475,7 +475,6 @@ Item {
} }
/************************** /**************************
*** GPU UI *** *** GPU UI ***
**************************/ **************************/

View file

@ -1,9 +1,9 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import Controls 1.0 import Controls 1.0
import Utils 1.0 import Utils 1.0
import MaterialIcons 2.2
Item { Item {
id: root id: root
@ -133,7 +133,7 @@ Item {
} }
delegate: RowLayout { delegate: RowLayout {
width: parent != null ? parent.width : undefined width: ListView.view.width
height: 18 height: 18
spacing: 3 spacing: 3
@ -261,7 +261,6 @@ Item {
radius: 3 radius: 3
border.width: 2 border.width: 2
border.color: chunkList.node === uigraph.selectedNode ? Colors.sysPalette.text : Colors.getChunkColor(object, {"NONE": bgColor}) border.color: chunkList.node === uigraph.selectedNode ? Colors.sysPalette.text : Colors.getChunkColor(object, {"NONE": bgColor})
} }
MouseArea { MouseArea {

View file

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

View file

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

View file

@ -1,15 +1,19 @@
import QtQuick 2.15 import QtQuick
import MaterialIcons 2.2 import MaterialIcons 2.2
import Utils 1.0 import Utils 1.0
/** /**
* ImageBadge is a preset MaterialLabel to display an icon bagdge on an image. * ImageBadge is a preset MaterialLabel to display an icon bagdge on an image.
*/ */
MaterialLabel { MaterialLabel {
id: root id: root
font.pointSize: 10 font.pointSize: 10
padding: 2 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
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import Utils 1.0
import Utils 1.0
/** /**
* ImageDelegate for a Viewpoint object. * ImageDelegate for a Viewpoint object.
*/ */
Item { Item {
id: root id: root
@ -24,7 +25,7 @@ Item {
default property alias children: imageMA.children default property alias children: imageMA.children
// retrieve viewpoints inner data // Retrieve viewpoints inner data
QtObject { QtObject {
id: _viewpoint id: _viewpoint
property url source: viewpoint ? Filepath.stringToUrl(viewpoint.get("path").value) : '' 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) : {} property var metadata: metadataStr ? JSON.parse(viewpoint.get("metadata").value) : {}
} }
// update thumbnail location // Update thumbnail location
// can be called from the GridView when a new thumbnail has been written on disk // Can be called from the GridView when a new thumbnail has been written on disk
function updateThumbnail() { function updateThumbnail() {
thumbnail.source = ThumbnailCache.thumbnail(root.source, root.cellID) thumbnail.source = ThumbnailCache.thumbnail(root.source, root.cellID)
} }
@ -60,7 +61,7 @@ Item {
anchors.margins: 6 anchors.margins: 6
hoverEnabled: true hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
onPressed: { onPressed: function(mouse) {
if (mouse.button == Qt.RightButton) if (mouse.button == Qt.RightButton)
imageMenu.popup() imageMenu.popup()
root.pressed(mouse) root.pressed(mouse)
@ -135,6 +136,7 @@ Item {
running: thumbnail.status != Image.Ready running: thumbnail.status != Image.Ready
} }
} }
// Image basename // Image basename
Label { Label {
id: imageLabel id: imageLabel

View file

@ -1,17 +1,18 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2 import QtQml.Models
import QtQml.Models 2.15
import Qt.labs.qmlmodels 1.0 import Qt.labs.qmlmodels 1.0
import Controls 1.0 import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0 import Utils 1.0
/** /**
* ImageGallery displays as a grid of Images a model containing Viewpoints objects. * ImageGallery displays as a grid of Images a model containing Viewpoints objects.
* It manages a model of multiple CameraInit nodes as individual groups. * It manages a model of multiple CameraInit nodes as individual groups.
*/ */
Panel { Panel {
id: root id: root
@ -28,7 +29,7 @@ Panel {
property int defaultCellSize: 160 property int defaultCellSize: 160
property bool readOnly: false property bool readOnly: false
property var filesByType: {} property var filesByType: ({})
property int nbMeshroomScenes: 0 property int nbMeshroomScenes: 0
property int nbDraggedFiles: 0 property int nbDraggedFiles: 0
@ -98,7 +99,7 @@ Panel {
intrinsic[currentAttribute.name + "." + currentAttribute.value.at(k).name] = currentAttribute.value.at(k) intrinsic[currentAttribute.name + "." + currentAttribute.value.at(k).name] = currentAttribute.value.at(k)
} }
} else if (currentAttribute.type === "ListAttribute") { } else if (currentAttribute.type === "ListAttribute") {
// not needed for now // Not needed for now
} else { } else {
intrinsic[currentAttribute.name] = currentAttribute intrinsic[currentAttribute.name] = currentAttribute
} }
@ -217,12 +218,12 @@ Panel {
Connections { Connections {
target: ThumbnailCache target: ThumbnailCache
function onThumbnailCreated(imgSource, callerID) { 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) { if (item && item.source === imgSource) {
item.updateThumbnail() item.updateThumbnail()
return return
} }
// fallback in case the ImageDelegate cellID changed // Fallback in case the ImageDelegate cellID changed
for (let idx = 0; idx < grid.count; idx++) { for (let idx = 0; idx < grid.count; idx++) {
item = grid.itemAtIndex(idx) item = grid.itemAtIndex(idx)
if (item && item.source === imgSource) { if (item && item.source === imgSource) {
@ -250,7 +251,7 @@ Panel {
] ]
property var reconstructionFilter: undefined 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_) { function modelData(item, roleName_) {
var roleNameAndCmd = roleName_.split(".") var roleNameAndCmd = roleName_.split(".")
var roleName = roleName_ var roleName = roleName_
@ -305,7 +306,7 @@ Panel {
} }
onRemoveRequest: sendRemoveRequest() onRemoveRequest: sendRemoveRequest()
Keys.onPressed: (event) => { Keys.onPressed: function(event) {
if (event.key === Qt.Key_Delete && event.modifiers === Qt.ShiftModifier) { if (event.key === Qt.Key_Delete && event.modifiers === Qt.ShiftModifier) {
removeAllImages() removeAllImages()
} else if (event.key === Qt.Key_Delete) { } else if (event.key === Qt.Key_Delete) {
@ -375,7 +376,7 @@ Panel {
// Keyboard shortcut to change current image group // Keyboard shortcut to change current image group
Keys.priority: Keys.BeforeItem Keys.priority: Keys.BeforeItem
Keys.onPressed: { Keys.onPressed: function(event) {
if (event.modifiers & Qt.AltModifier) { if (event.modifiers & Qt.AltModifier) {
if (event.key === Qt.Key_Right) { if (event.key === Qt.Key_Right) {
_reconstruction.cameraInitIndex = Math.min(root.cameraInits.count - 1, root.cameraInitIndex + 1) _reconstruction.cameraInitIndex = Math.min(root.cameraInits.count - 1, root.cameraInitIndex + 1)
@ -442,12 +443,12 @@ Panel {
anchors.fill: parent anchors.fill: parent
enabled: !m.readOnly && !intrinsicsFilterButton.checked enabled: !m.readOnly && !intrinsicsFilterButton.checked
keys: ["text/uri-list"] keys: ["text/uri-list"]
onEntered: { onEntered: function(drag) {
nbDraggedFiles = drag.urls.length nbDraggedFiles = drag.urls.length
filesByType = _reconstruction.getFilesByTypeFromDrop(drag.urls) filesByType = _reconstruction.getFilesByTypeFromDrop(drag.urls)
nbMeshroomScenes = filesByType["meshroomScenes"].length nbMeshroomScenes = filesByType["meshroomScenes"].length
} }
onDropped: { onDropped: function(drop) {
var augmentSfm = augmentArea.hovered var augmentSfm = augmentArea.hovered
if (nbMeshroomScenes == nbDraggedFiles || nbMeshroomScenes == 0) { if (nbMeshroomScenes == nbDraggedFiles || nbMeshroomScenes == 0) {
root.filesDropped(filesByType, augmentSfm) root.filesDropped(filesByType, augmentSfm)
@ -529,7 +530,7 @@ Panel {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onPressed: { onPressed: function(mouse) {
if (mouse.button == Qt.LeftButton) if (mouse.button == Qt.LeftButton)
grid.forceActiveFocus() grid.forceActiveFocus()
mouse.accepted = false mouse.accepted = false
@ -831,8 +832,7 @@ Panel {
} }
} }
onEnabledChanged: { onEnabledChanged: {
// Reset the toggle to avoid getting stuck // Reset the toggle to avoid getting stuck with the HDR node checked but disabled
// with the HDR node checked but disabled.
if (checked) { if (checked) {
checked = false checked = false
close() close()
@ -875,8 +875,7 @@ Panel {
} }
} }
onEnabledChanged: { onEnabledChanged: {
// Reset the toggle to avoid getting stuck // Reset the toggle to avoid getting stuck with the HDR node checked but disabled
// with the HDR node checked but disabled.
if (checked) { if (checked) {
checked = false checked = false
close() close()

View file

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

View file

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

View file

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

View file

@ -1,14 +1,16 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2 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 import Controls 1.0
/** /**
* LiveSfMView provides controls for setting up and starting a live reconstruction. * LiveSfMView provides controls for setting up and starting a live reconstruction.
*/ */
Panel { Panel {
id: root id: root

View file

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

View file

@ -1,5 +1,5 @@
pragma Singleton pragma Singleton
import QtQuick 2.15 import QtQuick
QtObject { QtObject {
property FontLoader fl: FontLoader { property FontLoader fl: FontLoader {

View file

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

View file

@ -1,12 +1,12 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
/** /**
* MaterialToolButton is a standard ToolButton using MaterialIcons font. * MaterialToolButton is a standard ToolButton using MaterialIcons font.
* It also shows up its tooltip when hovered. * It also shows up its tooltip when hovered.
*/ */
ToolButton { ToolButton {
id: control id: control
font.family: MaterialIcons.fontFamily font.family: MaterialIcons.fontFamily
@ -19,7 +19,7 @@ ToolButton {
} }
background: Rectangle { background: Rectangle {
color: { color: {
if (pressed || checked || hovered) { if (enabled && (pressed || checked || hovered)) {
if (pressed || checked) if (pressed || checked)
return Qt.darker(parent.palette.base, 1.3) return Qt.darker(parent.palette.base, 1.3)
if (hovered) if (hovered)

View file

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

View file

@ -1,12 +1,12 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
/** /**
* MaterialToolButton is a standard ToolButton using MaterialIcons font. * MaterialToolButton is a standard ToolButton using MaterialIcons font.
* It also shows up its tooltip when hovered. * It also shows up its tooltip when hovered.
*/ */
ToolButton { ToolButton {
id: control id: control
property alias iconText: icon.text property alias iconText: icon.text
@ -41,7 +41,7 @@ ToolButton {
} }
background: Rectangle { background: Rectangle {
color: { color: {
if (pressed || checked || hovered) { if (enabled && (pressed || checked || hovered)) {
if (pressed || checked) if (pressed || checked)
return Qt.darker(parent.palette.base, 1.3) return Qt.darker(parent.palette.base, 1.3)
if (hovered) if (hovered)

View file

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

View file

@ -1,10 +1,11 @@
pragma Singleton pragma Singleton
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
/** /**
* Singleton that gathers useful colors, shades and system palettes. * Singleton that gathers useful colors, shades and system palettes.
*/ */
QtObject { QtObject {
property SystemPalette sysPalette: SystemPalette {} property SystemPalette sysPalette: SystemPalette {}
property SystemPalette disabledSysPalette: SystemPalette { colorGroup: SystemPalette.Disabled } property SystemPalette disabledSysPalette: SystemPalette { colorGroup: SystemPalette.Disabled }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,14 +1,11 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import QtPositioning 5.15
import QtLocation 5.15
import QtCharts 2.15
import Charts 1.0
import QtCharts
import Charts 1.0
import Controls 1.0 import Controls 1.0
import Utils 1.0
import DataObjects 1.0 import DataObjects 1.0
FloatingPane { FloatingPane {
@ -34,7 +31,7 @@ FloatingPane {
onWheel: {} 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) property bool crfReady: csvData && csvData.ready && (csvData.getNbColumns() >= 4)
onCrfReadyChanged: { onCrfReadyChanged: {
if (crfReady) { if (crfReady) {
@ -80,24 +77,24 @@ FloatingPane {
} }
// We cannot use a Repeater with these Components so we need to instantiate them one by one // We cannot use a Repeater with these Components so we need to instantiate them one by one
// Red curve
LineSeries { LineSeries {
// Red curve
id: redCurve id: redCurve
axisX: valueAxisX axisX: valueAxisX
axisY: valueAxisY axisY: valueAxisY
name: crfReady ? csvData.getColumn(1).title : "" name: crfReady ? csvData.getColumn(1).title : ""
color: name.toLowerCase() color: name.toLowerCase()
} }
// Green curve
LineSeries { LineSeries {
// Green curve
id: greenCurve id: greenCurve
axisX: valueAxisX axisX: valueAxisX
axisY: valueAxisY axisY: valueAxisY
name: crfReady ? csvData.getColumn(2).title : "" name: crfReady ? csvData.getColumn(2).title : ""
color: name.toLowerCase() color: name.toLowerCase()
} }
// Blue curve
LineSeries { LineSeries {
// Blue curve
id: blueCurve id: blueCurve
axisX: valueAxisX axisX: valueAxisX
axisY: valueAxisY axisY: valueAxisY

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
import QtQuick 2.15 import QtQuick
Item { Item {
id: root id: root
@ -8,9 +8,9 @@ Item {
property var viewpoint: null property var viewpoint: null
property real zoom: 1.0 property real zoom: 1.0
// required for perspective transform // Required for perspective transform
// Match theoretical values in AliceVision // 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 ccheckerSizeX: 1675.0
readonly property real ccheckerSizeY: 1125.0 readonly property real ccheckerSizeY: 1125.0
@ -34,16 +34,13 @@ Item {
function readSourceFile() { function readSourceFile() {
var xhr = new XMLHttpRequest var xhr = new XMLHttpRequest
// console.warn("readSourceFile: " + root.source)
xhr.open("GET", root.source) xhr.open("GET", root.source)
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) {
try { try {
root.json = null root.json = null
// console.warn("readSourceFile: update json from " + root.source)
root.json = JSON.parse(xhr.responseText) root.json = JSON.parse(xhr.responseText)
// console.warn("readSourceFile: root.json.checkers.length=" + root.json.checkers.length)
} catch(exc) { } catch(exc) {
console.warn("Failed to parse ColorCheckerDetection JSON file: " + source) console.warn("Failed to parse ColorCheckerDetection JSON file: " + source)
return return

View file

@ -1,15 +1,16 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Utils 1.0
import Controls 1.0 import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0
/** /**
* FeaturesInfoOverlay is an overlay that displays info and * FeaturesInfoOverlay is an overlay that displays info and
* provides controls over a FeaturesViewer component. * provides controls over a FeaturesViewer component.
*/ */
FloatingPane { FloatingPane {
id: root id: root
@ -65,7 +66,10 @@ FloatingPane {
ComboBox { ComboBox {
id: featureDisplayModeCB id: featureDisplayModeCB
flat: true 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 ToolTip.visible: hovered
Layout.fillHeight: true Layout.fillHeight: true
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
@ -81,7 +85,10 @@ FloatingPane {
ComboBox { ComboBox {
id: trackDisplayModeCB id: trackDisplayModeCB
flat: true 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 ToolTip.visible: hovered
Layout.fillHeight: true Layout.fillHeight: true
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
@ -152,7 +159,8 @@ FloatingPane {
} }
SpinBox { SpinBox {
id: timeWindowSB 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 ToolTip.visible: hovered
Layout.fillHeight: true Layout.fillHeight: true
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
@ -261,7 +269,9 @@ FloatingPane {
colors: root.featuresViewer.colors colors: root.featuresViewer.colors
currentIndex: featureType.viewer.colorIndex currentIndex: featureType.viewer.colorIndex
// offset featuresViewer color set when changing the color of one feature type // 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 // Feature type name
Label { Label {

View file

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

View file

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

View file

@ -1,7 +1,7 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0 import Controls 1.0
FloatingPane { FloatingPane {
@ -30,19 +30,18 @@ FloatingPane {
DoubleValidator { DoubleValidator {
id: doubleValidator id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale locale: 'C' // Use '.' decimal separator disregarding of the system locale
} }
RowLayout { RowLayout {
id: toolLayout id: toolLayout
// anchors.verticalCenter: parent
anchors.fill: parent anchors.fill: parent
// channel mode // Channel mode
ComboBox { ComboBox {
id: channelsCtrl 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.minimumWidth: 5.0 * Qt.application.font.pixelSize
Layout.preferredWidth: Layout.minimumWidth Layout.preferredWidth: Layout.minimumWidth
flat: true flat: true
@ -57,7 +56,7 @@ FloatingPane {
model: channels model: channels
} }
// gain slider // Gain slider
RowLayout { RowLayout {
spacing: 5 spacing: 5
@ -97,7 +96,7 @@ FloatingPane {
} }
} }
// gamma slider // Gamma slider
RowLayout { RowLayout {
spacing: 5 spacing: 5
@ -242,7 +241,7 @@ FloatingPane {
TextMetrics { TextMetrics {
id: textMetrics_colorValue id: textMetrics_colorValue
font: red.font 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 { TextMetrics {
id: textMetrics_gainValue id: textMetrics_gainValue

View file

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

View file

@ -1,8 +1,9 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0 import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0 import Utils 1.0
FloatingPane { FloatingPane {
@ -30,7 +31,7 @@ FloatingPane {
DoubleValidator { DoubleValidator {
id: doubleValidator id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale locale: 'C' // Use '.' decimal separator disregarding of the system locale
} }
RowLayout { RowLayout {
@ -62,7 +63,7 @@ FloatingPane {
padding : 10 padding : 10
colors: root.colors colors: root.colors
currentIndex: root.colorIndex currentIndex: root.colorIndex
onColorPicked: root.colorOffset = colorIndex onColorPicked: function(colorIndex) { root.colorOffset = colorIndex }
} }
// Grid opacity slider // Grid opacity slider
@ -147,12 +148,11 @@ FloatingPane {
} }
} }
//Fill rectangle to have a better UI // Fill rectangle to have a better UI
Rectangle { Rectangle {
color: root.palette.window color: root.palette.window
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
TextMetrics { TextMetrics {

View file

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

View file

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

View file

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

View file

@ -1,8 +1,9 @@
import QtQuick 2.15 import QtQuick
import QtQuick.Controls 2.15 import QtQuick.Controls
import QtQuick.Layouts 1.11 import QtQuick.Layouts
import MaterialIcons 2.2
import Controls 1.0 import Controls 1.0
import MaterialIcons 2.2
import Utils 1.0 import Utils 1.0
FloatingPane { FloatingPane {
@ -31,7 +32,7 @@ FloatingPane {
DoubleValidator { DoubleValidator {
id: doubleValidator id: doubleValidator
locale: 'C' // use '.' decimal separator disregarding of the system locale locale: 'C' // Use '.' decimal separator disregarding of the system locale
} }
RowLayout { RowLayout {
@ -131,7 +132,6 @@ FloatingPane {
Layout.fillWidth: false Layout.fillWidth: false
Layout.maximumWidth: 50 Layout.maximumWidth: 50
validator: DoubleValidator { validator: DoubleValidator {
bottom: Math.min(speedSpinBox.from, speedSpinBox.to) bottom: Math.min(speedSpinBox.from, speedSpinBox.to)
top: Math.max(speedSpinBox.from, speedSpinBox.to) top: Math.max(speedSpinBox.from, speedSpinBox.to)
@ -164,7 +164,6 @@ FloatingPane {
Layout.fillWidth: false Layout.fillWidth: false
Layout.maximumWidth: 50 Layout.maximumWidth: 50
validator: DoubleValidator { validator: DoubleValidator {
bottom: Math.min(downscaleSpinBox.from, downscaleSpinBox.to) bottom: Math.min(downscaleSpinBox.from, downscaleSpinBox.to)
top: Math.max(downscaleSpinBox.from, downscaleSpinBox.to) top: Math.max(downscaleSpinBox.from, downscaleSpinBox.to)
@ -179,8 +178,8 @@ FloatingPane {
} }
} }
} }
} }
TextMetrics { TextMetrics {
id: textMetrics_subdivisionsValue id: textMetrics_subdivisionsValue
font: subdivisionsLabel.font font: subdivisionsLabel.font

View file

@ -1,7 +1,7 @@
import QtQuick 2.15 import QtQuick
import Utils 1.0
import AliceVision 1.0 as AliceVision import AliceVision 1.0 as AliceVision
import Utils 1.0
/** /**
* PanoramaViwer displays a list of Float Images * 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 pitchNode: activeNode ? activeNode.attribute("manualTransform.manualRotation.x").value : 0
property double rollNode: activeNode ? activeNode.attribute("manualTransform.manualRotation.z").value : 0 property double rollNode: activeNode ? activeNode.attribute("manualTransform.manualRotation.z").value : 0
//Convert angle functions // Convert angle functions
function toDegrees(radians) { function toDegrees(radians) {
return radians * (180 / Math.PI) return radians * (180 / Math.PI)
} }
@ -82,12 +82,16 @@ AliceVision.PanoramaViewer {
return degrees * (Math.PI / 180) 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 // Limit angle between -180 and 180
function limitAngle(angle) { function limitAngle(angle) {
if (angle > 180) angle = -180.0 + (angle - 180.0) if (angle > 180)
if (angle < -180) angle = 180.0 - (Math.abs(angle) - 180) angle = -180.0 + (angle - 180.0)
if (angle < -180)
angle = 180.0 - (Math.abs(angle) - 180)
return angle return angle
} }
@ -121,7 +125,7 @@ AliceVision.PanoramaViewer {
if (isEditable) if (isEditable)
isRotating ? Qt.ClosedHandCursor : Qt.OpenHandCursor isRotating ? Qt.ClosedHandCursor : Qt.OpenHandCursor
} }
onPositionChanged: { onPositionChanged: function(mouse) {
// Send Mouse Coordinates to Float Images Viewers // Send Mouse Coordinates to Float Images Viewers
idSelected = -1 idSelected = -1
for (var i = 0; i < repeater.model && isHighlightable; ++i) { for (var i = 0; i < repeater.model && isHighlightable; ++i) {
@ -171,7 +175,7 @@ AliceVision.PanoramaViewer {
} }
} }
onPressed:{ onPressed: function(mouse) {
_reconstruction.beginModification("Panorama Manual Rotation") _reconstruction.beginModification("Panorama Manual Rotation")
isRotating = true isRotating = true
lastX = mouse.x lastX = mouse.x
@ -185,7 +189,7 @@ AliceVision.PanoramaViewer {
previous_roll = roll previous_roll = roll
} }
onReleased: { onReleased: function(mouse) {
_reconstruction.endModification() _reconstruction.endModification()
isRotating = false isRotating = false
lastX = 0 lastX = 0
@ -273,33 +277,31 @@ AliceVision.PanoramaViewer {
var sourceItem = Filepath.stringToUrl(msfmData.getUrlFromViewId(idViewItem)) var sourceItem = Filepath.stringToUrl(msfmData.getUrlFromViewId(idViewItem))
setSource("FloatImage.qml", { setSource("FloatImage.qml", {
'surface.viewerType': AliceVision.Surface.EViewerType.PANORAMA, "surface.viewerType": AliceVision.Surface.EViewerType.PANORAMA,
'viewerTypeString': 'panorama', "viewerTypeString": "panorama",
'surface.subdivisions': Qt.binding(function() { return subdivisionsPano }), "surface.subdivisions": Qt.binding(function() { return subdivisionsPano }),
'cropFisheye' : Qt.binding(function(){ return cropFisheyePano }), "cropFisheye" : Qt.binding(function(){ return cropFisheyePano }),
'surface.pitch': Qt.binding(function() { return root.pitch }), "surface.pitch": Qt.binding(function() { return root.pitch }),
'surface.yaw': Qt.binding(function() { return root.yaw }), "surface.yaw": Qt.binding(function() { return root.yaw }),
'surface.roll': Qt.binding(function() { return root.roll }), "surface.roll": Qt.binding(function() { return root.roll }),
'idView': Qt.binding(function() { return idViewItem }), "idView": Qt.binding(function() { return idViewItem }),
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue }), "gamma": Qt.binding(function() { return hdrImageToolbar.gammaValue }),
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue }), "gain": Qt.binding(function() { return hdrImageToolbar.gainValue }),
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue }), "channelModeString": Qt.binding(function() { return hdrImageToolbar.channelModeValue }),
'downscaleLevel' : Qt.binding(function() { return downscale }), "downscaleLevel": Qt.binding(function() { return downscale }),
'source': Qt.binding(function() { return sourceItem }), "source": Qt.binding(function() { return sourceItem }),
'surface.msfmData': Qt.binding(function() { return root.msfmData }), "surface.msfmData": Qt.binding(function() { return root.msfmData }),
'canBeHovered': true, "canBeHovered": true,
'useSequence': false "useSequence": false
}) })
imageLoaded = Qt.binding(function() { return repeater.itemAt(index).item.imageStatus === Image.Ready ? true : false }) imageLoaded = Qt.binding(function() { return repeater.itemAt(index).item.imageStatus === Image.Ready ? true : false })
} }
} }
} }
Repeater { Repeater {
id: repeater id: repeater
model: 0 model: 0
delegate: imgPano delegate: imgPano
} }
Connections { Connections {
target: root target: root
@ -309,7 +311,7 @@ AliceVision.PanoramaViewer {
// Retrieve downscale value from C++ // Retrieve downscale value from C++
panoramaViewerToolbar.updateDownscaleValue(root.downscale) panoramaViewerToolbar.updateDownscaleValue(root.downscale)
//Changing the repeater model (number of elements) // Changing the repeater model (number of elements)
panoImages.updateRepeater() panoImages.updateRepeater()
root.readyToLoad = Image.Ready 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