mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-08-03 00:38:41 +02:00
[ui] New TextFileViewer for displaying log files
Introduce a new TextFileViewer component with auto-reload feature based on a ListView instead of a TextArea for performance reasons. Uses the text content split on line breaks as ListView's model. Features: * auto-scroll to bottom * display line numbers * customizable line delegates (e.g.: display a real progress bar) * color lines according to log level
This commit is contained in:
parent
438622a14b
commit
55dba55d19
4 changed files with 363 additions and 173 deletions
|
@ -96,7 +96,7 @@ Panel {
|
|||
}
|
||||
Label {
|
||||
color: Qt.lighter(palette.mid, 1.2)
|
||||
text: "Select a Node to edit its Attributes"
|
||||
text: "Select a Node to access its Details"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,28 +128,30 @@ Panel {
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
|
||||
Layout.fillWidth: true
|
||||
width: childrenRect.width
|
||||
position: TabBar.Footer
|
||||
currentIndex: 1
|
||||
TabButton {
|
||||
text: "Attributes"
|
||||
width: implicitWidth
|
||||
padding: 4
|
||||
leftPadding: 8
|
||||
rightPadding: leftPadding
|
||||
}
|
||||
TabButton {
|
||||
text: "Log"
|
||||
width: implicitWidth
|
||||
leftPadding: 8
|
||||
rightPadding: leftPadding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Controls 1.4 as Controls1 // SplitView
|
||||
import QtQuick.Layouts 1.3
|
||||
import MaterialIcons 2.2
|
||||
import Utils 1.0
|
||||
import Controls 1.0
|
||||
|
||||
import "common.js" as Common
|
||||
|
||||
|
@ -85,17 +85,13 @@ FocusScope {
|
|||
id: fileSelector
|
||||
Layout.fillWidth: true
|
||||
property string currentFile: chunksLV.currentChunk ? chunksLV.currentChunk[currentItem.fileProperty] : ""
|
||||
property string lastLoadedFile
|
||||
property date lastModTime
|
||||
onCurrentFileChanged: if(visible) loadCurrentFile()
|
||||
onVisibleChanged: if(visible) loadCurrentFile()
|
||||
|
||||
|
||||
TabButton {
|
||||
property string fileProperty: "logFile"
|
||||
text: "Log"
|
||||
text: "Output"
|
||||
padding: 4
|
||||
}
|
||||
|
||||
TabButton {
|
||||
property string fileProperty: "statisticsFile"
|
||||
text: "Statistics"
|
||||
|
@ -108,152 +104,12 @@ FocusScope {
|
|||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillHeight: true
|
||||
TextFileViewer {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
Pane {
|
||||
id: tb
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillHeight: true
|
||||
padding: 0
|
||||
background: Rectangle { color: Qt.darker(activePalette.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 {
|
||||
id: autoRefresh
|
||||
text: MaterialIcons.timer
|
||||
ToolTip.text: "Auto-Refresh when Running"
|
||||
ToolTip.visible: hovered
|
||||
font.family: MaterialIcons.fontFamily
|
||||
checked: true
|
||||
checkable: true
|
||||
}
|
||||
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(Filepath.stringToUrl(fileSelector.currentFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Log display
|
||||
ScrollView {
|
||||
id: logScrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
TextArea {
|
||||
id: logArea
|
||||
selectByMouse: true
|
||||
selectByKeyboard: true
|
||||
persistentSelection: true
|
||||
font.family: "Monospace, Consolas, Monaco"
|
||||
}
|
||||
}
|
||||
Layout.fillHeight: true
|
||||
autoReload: chunksLV.currentChunk !== undefined && chunksLV.currentChunk.statusName === "RUNNING"
|
||||
source: Filepath.stringToUrl(fileSelector.currentFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-reload current file if NodeChunk is being computed
|
||||
Timer {
|
||||
running: autoRefresh.checked && chunksLV.currentChunk != undefined && chunksLV.currentChunk.statusName === "RUNNING"
|
||||
interval: 2000
|
||||
repeat: true
|
||||
// reload file on start and stop
|
||||
onRunningChanged: loadCurrentFile(true)
|
||||
onTriggered: loadCurrentFile(true)
|
||||
}
|
||||
|
||||
function loadCurrentFile(keepCursorPosition)
|
||||
{
|
||||
if(keepCursorPosition == undefined)
|
||||
keepCursorPosition = false
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("GET", Filepath.stringToUrl(fileSelector.currentFile));
|
||||
xhr.onreadystatechange = function() {
|
||||
if(xhr.readyState == XMLHttpRequest.HEADERS_RECEIVED)
|
||||
{
|
||||
// if the file is already open
|
||||
// check last modification date
|
||||
var lastMod = new Date(xhr.getResponseHeader("Last-Modified"));
|
||||
if(fileSelector.lastLoadedFile == fileSelector.currentFile
|
||||
&& lastMod.getTime() == fileSelector.lastModTime.getTime() )
|
||||
{
|
||||
// file has not changed, don't reload it
|
||||
xhr.doLoad = false;
|
||||
return
|
||||
}
|
||||
// file is different or last modification time has changed
|
||||
fileSelector.lastModTime = lastMod
|
||||
xhr.doLoad = true
|
||||
}
|
||||
if (xhr.readyState == XMLHttpRequest.DONE) {
|
||||
// store lastLoadedFile url
|
||||
fileSelector.lastLoadedFile = fileSelector.currentFile
|
||||
// if responseText should not be loaded
|
||||
if(!xhr.doLoad)
|
||||
{
|
||||
// file could not be opened, reset text and lastModTime
|
||||
if(xhr.status == 0)
|
||||
{
|
||||
fileSelector.lastModTime = new Date()
|
||||
logArea.text = ''
|
||||
}
|
||||
return;
|
||||
}
|
||||
// store cursor position and content position
|
||||
var cursorPosition = logArea.cursorPosition;
|
||||
var contentY = logScrollView.ScrollBar.vertical.position;
|
||||
|
||||
// replace text
|
||||
logArea.text = xhr.responseText;
|
||||
|
||||
if(autoScroll.checked)
|
||||
{
|
||||
// Reset cursor position to trigger scroll to bottom
|
||||
logArea.cursorPosition = 0;
|
||||
logArea.cursorPosition = logArea.length;
|
||||
}
|
||||
else if(keepCursorPosition)
|
||||
{
|
||||
if(cursorPosition)
|
||||
logArea.cursorPosition = cursorPosition;
|
||||
logScrollView.ScrollBar.vertical.position = contentY
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue