mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-08-03 08:48:40 +02:00
[ui] introduce NodeLog Component to monitor NodeChunks
* give access to Node's chunks log files (log, statistics, status) with auto-reload when current NodeChunk is being computed * add tabs system in the AttributeEditor to switch between attributes edition and Node log
This commit is contained in:
parent
49c3491534
commit
36d2411d06
2 changed files with 276 additions and 32 deletions
|
@ -11,7 +11,7 @@ ColumnLayout {
|
|||
property variant node: null // the node to edit
|
||||
property bool readOnly: false
|
||||
|
||||
spacing: 4
|
||||
spacing: 0
|
||||
|
||||
SystemPalette { id: palette }
|
||||
|
||||
|
@ -52,45 +52,85 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: attributesListView
|
||||
|
||||
StackLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 4
|
||||
clip: true
|
||||
spacing: 4
|
||||
ScrollBar.vertical: ScrollBar { id: scrollBar }
|
||||
|
||||
model: node ? node.attributes : undefined
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
delegate: RowLayout {
|
||||
width: attributesListView.width
|
||||
spacing: 4
|
||||
Item {
|
||||
|
||||
Label {
|
||||
id: parameterLabel
|
||||
text: object.label
|
||||
Layout.preferredWidth: 180
|
||||
color: object.isOutput ? "orange" : palette.text
|
||||
elide: Label.ElideRight
|
||||
ToolTip.text: object.desc.description
|
||||
ToolTip.visible: parameterMA.containsMouse
|
||||
ToolTip.delay: 200
|
||||
MouseArea {
|
||||
id: parameterMA
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
ListView {
|
||||
id: attributesListView
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
|
||||
clip: true
|
||||
spacing: 4
|
||||
ScrollBar.vertical: ScrollBar { id: scrollBar }
|
||||
|
||||
model: node ? node.attributes : undefined
|
||||
|
||||
delegate: RowLayout {
|
||||
width: attributesListView.width
|
||||
spacing: 4
|
||||
|
||||
Label {
|
||||
id: parameterLabel
|
||||
text: object.label
|
||||
Layout.preferredWidth: 180
|
||||
color: object.isOutput ? "orange" : palette.text
|
||||
elide: Label.ElideRight
|
||||
ToolTip.text: object.desc.description
|
||||
ToolTip.visible: parameterMA.containsMouse && object.desc.description
|
||||
ToolTip.delay: 200
|
||||
|
||||
MouseArea {
|
||||
id: parameterMA
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
AttributeItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: scrollBar.width
|
||||
height: childrenRect.height
|
||||
attribute: object
|
||||
readOnly: root.readOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AttributeItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: scrollBar.width
|
||||
height: childrenRect.height
|
||||
attribute: object
|
||||
readOnly: root.readOnly
|
||||
}
|
||||
NodeLog {
|
||||
id: nodeLog
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
node: root.node
|
||||
|
||||
}
|
||||
}
|
||||
TabBar {
|
||||
id: tabBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
width: childrenRect.width
|
||||
position: TabBar.Footer
|
||||
TabButton {
|
||||
text: "Attributes"
|
||||
width: implicitWidth
|
||||
padding: 4
|
||||
leftPadding: 8
|
||||
rightPadding: leftPadding
|
||||
}
|
||||
TabButton {
|
||||
text: "Log"
|
||||
width: implicitWidth
|
||||
leftPadding: 8
|
||||
rightPadding: leftPadding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
204
meshroom/ui/qml/GraphEditor/NodeLog.qml
Normal file
204
meshroom/ui/qml/GraphEditor/NodeLog.qml
Normal file
|
@ -0,0 +1,204 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Controls 1.4 as Controls1 // SplitView
|
||||
import QtQuick.Layouts 1.3
|
||||
import MaterialIcons 2.2
|
||||
import "common.js" as Common
|
||||
|
||||
/**
|
||||
* NodeLog displays log and statistics data of Node's chunks (NodeChunks)
|
||||
*
|
||||
* To ease monitoring, it provides periodic auto-reload of the opened file
|
||||
* if the related NodeChunk is being computed.
|
||||
*/
|
||||
FocusScope {
|
||||
property variant node
|
||||
|
||||
SystemPalette { id: palette }
|
||||
|
||||
Controls1.SplitView {
|
||||
anchors.fill: parent
|
||||
|
||||
// The list of chunks
|
||||
ListView {
|
||||
id: chunksLV
|
||||
|
||||
property variant currentChunk: currentItem ? currentItem.chunk : undefined
|
||||
|
||||
width: 60
|
||||
Layout.fillHeight: true
|
||||
model: node.chunks
|
||||
highlightFollowsCurrentItem: true
|
||||
keyNavigationEnabled: true
|
||||
focus: true
|
||||
currentIndex: -1
|
||||
|
||||
header: Component {
|
||||
Label {
|
||||
width: chunksLV.width
|
||||
elide: Label.ElideRight
|
||||
text: "Chunks"
|
||||
padding: 4
|
||||
z: 10
|
||||
background: Rectangle { color: palette.window }
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Component {
|
||||
Rectangle {
|
||||
color: palette.highlight
|
||||
opacity: 0.3
|
||||
z: 2
|
||||
}
|
||||
}
|
||||
highlightMoveDuration: 0
|
||||
highlightResizeDuration: 0
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: chunkDelegate
|
||||
property var chunk: object
|
||||
text: index
|
||||
width: parent.width
|
||||
leftPadding: 8
|
||||
onClicked: {
|
||||
chunksLV.forceActiveFocus()
|
||||
chunksLV.currentIndex = index
|
||||
}
|
||||
Rectangle {
|
||||
width: 4
|
||||
height: parent.height
|
||||
color: Common.getChunkColor(parent.chunk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: 1
|
||||
|
||||
spacing: 1
|
||||
|
||||
TabBar {
|
||||
id: fileSelector
|
||||
Layout.fillWidth: true
|
||||
property string currentFile: chunksLV.currentChunk ? chunksLV.currentChunk[currentItem.fileProperty] : ""
|
||||
onCurrentFileChanged: if(visible) loadCurrentFile(false)
|
||||
onVisibleChanged: loadCurrentFile()
|
||||
|
||||
TabButton {
|
||||
property string fileProperty: "logFile"
|
||||
text: "Log"
|
||||
padding: 4
|
||||
}
|
||||
TabButton {
|
||||
property string fileProperty: "statisticsFile"
|
||||
text: "Statistics"
|
||||
padding: 4
|
||||
}
|
||||
TabButton {
|
||||
property string fileProperty: "statusFile"
|
||||
text: "Status"
|
||||
padding: 4
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
Pane {
|
||||
id: tb
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
padding: 0
|
||||
background: Rectangle { color: Qt.darker(palette.window, 1.2) }
|
||||
Column {
|
||||
height: parent.height
|
||||
ToolButton {
|
||||
text: MaterialIcons.refresh
|
||||
ToolTip.text: "Refresh"
|
||||
ToolTip.visible: hovered
|
||||
font.family: MaterialIcons.fontFamily
|
||||
onClicked: loadCurrentFile(false)
|
||||
}
|
||||
ToolButton {
|
||||
text: MaterialIcons.vertical_align_top
|
||||
ToolTip.text: "Scroll to Top"
|
||||
ToolTip.visible: hovered
|
||||
font.family: MaterialIcons.fontFamily
|
||||
onClicked: logArea.cursorPosition = 0
|
||||
}
|
||||
ToolButton {
|
||||
text: MaterialIcons.vertical_align_bottom
|
||||
ToolTip.text: "Scroll to Bottom"
|
||||
ToolTip.visible: hovered
|
||||
font.family: MaterialIcons.fontFamily
|
||||
onClicked: logArea.cursorPosition = logArea.length
|
||||
}
|
||||
ToolButton {
|
||||
id: autoScroll
|
||||
text: MaterialIcons.system_update_alt
|
||||
ToolTip.text: "Auto-Scroll to Bottom"
|
||||
ToolTip.visible: hovered
|
||||
font.family: MaterialIcons.fontFamily
|
||||
checkable: true
|
||||
checked: true
|
||||
}
|
||||
ToolButton {
|
||||
text: MaterialIcons.open_in_new
|
||||
ToolTip.text: "Open Externally"
|
||||
ToolTip.visible: hovered
|
||||
font.family: MaterialIcons.fontFamily
|
||||
enabled: fileSelector.currentFile != ""
|
||||
onClicked: Qt.openUrlExternally(fileSelector.currentFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Log display
|
||||
ScrollView {
|
||||
id: logScrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
TextArea {
|
||||
id: logArea
|
||||
selectByMouse: true
|
||||
selectByKeyboard: true
|
||||
persistentSelection: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-reload current file if NodeChunk is being computed
|
||||
Timer {
|
||||
running: chunksLV.currentChunk != undefined && chunksLV.currentChunk.statusName === "RUNNING"
|
||||
interval: 2000
|
||||
repeat: true
|
||||
onTriggered: loadCurrentFile(true)
|
||||
}
|
||||
|
||||
function loadCurrentFile(keepCursorPosition)
|
||||
{
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("GET", fileSelector.currentFile);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == XMLHttpRequest.DONE) {
|
||||
var cursorPosition = logArea.cursorPosition;
|
||||
logArea.text = xhr.responseText;
|
||||
// Reset cursor position to trigger scroll to bottom
|
||||
logArea.cursorPosition = 0;
|
||||
if(autoScroll.checked)
|
||||
{
|
||||
logArea.cursorPosition = logArea.length;
|
||||
}
|
||||
else if(keepCursorPosition)
|
||||
{
|
||||
logArea.cursorPosition = cursorPosition;
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue