mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-08-03 00:38:41 +02:00
Merge remote-tracking branch 'origin/develop' into dev/syncCamera
This commit is contained in:
commit
633b8c0e00
12 changed files with 857 additions and 25 deletions
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -24,15 +24,15 @@ A clear and concise description of what you expected to happen.
|
|||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Log**
|
||||
If applicable, copy paste the relevant log output (please embed the text in a markdown code tag "```" )
|
||||
If applicable, copy paste the relevant log output (please embed the text in a markdown code tag "\`\`\`" )
|
||||
|
||||
**Desktop (please complete the following and other pertinent information):**
|
||||
- OS: [e.g. win 10, osx, ]
|
||||
- Version [e.g. 2019.1]
|
||||
- Python version [e.g. 2.6]
|
||||
- Qt/PySide version [e.g. 5.12.4]
|
||||
- Binary version (if applicable) [e.g. 2019.1]
|
||||
- Commit reference (if applicable) [e.g. 08ddbe2]
|
||||
- Meshroom version: please specify if you are using a release version or your own build
|
||||
- Binary version (if applicable) [e.g. 2019.1]
|
||||
- Commit reference (if applicable) [e.g. 08ddbe2]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
|
31
.github/ISSUE_TEMPLATE/question_help.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/question_help.md
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
name: Question or help needed
|
||||
about: Ask question or for help for issues not related to program failures (e.g. "where I can find this feature", "my dataset is not reconstructed properly", "which parameter setting shall I use" etc...)
|
||||
title: "[question]"
|
||||
labels: type:question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the problem**
|
||||
A clear and concise description of what the problem is.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Dataset**
|
||||
If applicable, add a link or *few* images to help better understand where the problem may come from.
|
||||
|
||||
**Log**
|
||||
If applicable, copy paste the relevant log output (please embed the text in a markdown code tag "\`\`\`" )
|
||||
|
||||
**Desktop (please complete the following and other pertinent information):**
|
||||
- OS: [e.g. win 10, osx, ]
|
||||
- Python version [e.g. 2.6]
|
||||
- Qt/PySide version [e.g. 5.12.4]
|
||||
- Meshroom version: please specify if you are using a release version or your own build
|
||||
- Binary version (if applicable) [e.g. 2019.1]
|
||||
- Commit reference (if applicable) [e.g. 08ddbe2]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
|
@ -1,5 +1,5 @@
|
|||
# packaging
|
||||
cx_Freeze
|
||||
cx_Freeze==5.1.1
|
||||
|
||||
# testing
|
||||
pytest
|
|
@ -285,6 +285,7 @@ class NodeChunk(BaseObject):
|
|||
# ask and wait for the stats thread to stop
|
||||
self.statThread.stopRequest()
|
||||
self.statThread.join()
|
||||
self.statistics = stats.Statistics()
|
||||
del runningProcesses[self.name]
|
||||
|
||||
self.upgradeStatusTo(Status.SUCCESS)
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
from collections import defaultdict
|
||||
import subprocess
|
||||
import logging
|
||||
import psutil
|
||||
import time
|
||||
import threading
|
||||
import platform
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
# On Python 2 use C implementation for performance and to avoid lots of warnings
|
||||
from xml.etree import cElementTree as ET
|
||||
else:
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
def bytes2human(n):
|
||||
|
@ -25,15 +35,58 @@ def bytes2human(n):
|
|||
|
||||
class ComputerStatistics:
|
||||
def __init__(self):
|
||||
# TODO: init
|
||||
self.nbCores = 0
|
||||
self.cpuFreq = 0
|
||||
self.ramTotal = 0
|
||||
self.ramAvailable = 0 # GB
|
||||
self.vramAvailable = 0 # GB
|
||||
self.swapAvailable = 0
|
||||
|
||||
self.gpuMemoryTotal = 0
|
||||
self.gpuName = ''
|
||||
self.curves = defaultdict(list)
|
||||
|
||||
self._isInit = False
|
||||
|
||||
def initOnFirstTime(self):
|
||||
if self._isInit:
|
||||
return
|
||||
self._isInit = True
|
||||
|
||||
self.cpuFreq = psutil.cpu_freq().max
|
||||
self.ramTotal = psutil.virtual_memory().total / 1024/1024/1024
|
||||
|
||||
if platform.system() == "Windows":
|
||||
from distutils import spawn
|
||||
# If the platform is Windows and nvidia-smi
|
||||
# could not be found from the environment path,
|
||||
# try to find it from system drive with default installation path
|
||||
self.nvidia_smi = spawn.find_executable('nvidia-smi')
|
||||
if self.nvidia_smi is None:
|
||||
self.nvidia_smi = "%s\\Program Files\\NVIDIA Corporation\\NVSMI\\nvidia-smi.exe" % os.environ['systemdrive']
|
||||
else:
|
||||
self.nvidia_smi = "nvidia-smi"
|
||||
|
||||
try:
|
||||
p = subprocess.Popen([self.nvidia_smi, "-q", "-x"], stdout=subprocess.PIPE)
|
||||
xmlGpu, stdError = p.communicate()
|
||||
|
||||
smiTree = ET.fromstring(xmlGpu)
|
||||
gpuTree = smiTree.find('gpu')
|
||||
|
||||
try:
|
||||
self.gpuMemoryTotal = gpuTree.find('fb_memory_usage').find('total').text.split(" ")[0]
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get gpuMemoryTotal: "{}".'.format(str(e)))
|
||||
pass
|
||||
try:
|
||||
self.gpuName = gpuTree.find('product_name').text
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get gpuName: "{}".'.format(str(e)))
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get information from nvidia_smi at init: "{}".'.format(str(e)))
|
||||
|
||||
def _addKV(self, k, v):
|
||||
if isinstance(v, tuple):
|
||||
for ki, vi in v._asdict().items():
|
||||
|
@ -45,11 +98,41 @@ class ComputerStatistics:
|
|||
self.curves[k].append(v)
|
||||
|
||||
def update(self):
|
||||
self.initOnFirstTime()
|
||||
self._addKV('cpuUsage', psutil.cpu_percent(percpu=True)) # interval=None => non-blocking (percentage since last call)
|
||||
self._addKV('ramUsage', psutil.virtual_memory().percent)
|
||||
self._addKV('swapUsage', psutil.swap_memory().percent)
|
||||
self._addKV('vramUsage', 0)
|
||||
self._addKV('ioCounters', psutil.disk_io_counters())
|
||||
self.updateGpu()
|
||||
|
||||
def updateGpu(self):
|
||||
try:
|
||||
p = subprocess.Popen([self.nvidia_smi, "-q", "-x"], stdout=subprocess.PIPE)
|
||||
xmlGpu, stdError = p.communicate()
|
||||
|
||||
smiTree = ET.fromstring(xmlGpu)
|
||||
gpuTree = smiTree.find('gpu')
|
||||
|
||||
try:
|
||||
self._addKV('gpuMemoryUsed', gpuTree.find('fb_memory_usage').find('used').text.split(" ")[0])
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get gpuMemoryUsed: "{}".'.format(str(e)))
|
||||
pass
|
||||
try:
|
||||
self._addKV('gpuUsed', gpuTree.find('utilization').find('gpu_util').text.split(" ")[0])
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get gpuUsed: "{}".'.format(str(e)))
|
||||
pass
|
||||
try:
|
||||
self._addKV('gpuTemperature', gpuTree.find('temperature').find('gpu_temp').text.split(" ")[0])
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get gpuTemperature: "{}".'.format(str(e)))
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logging.debug('Failed to get information from nvidia_smi: "{}".'.format(str(e)))
|
||||
return
|
||||
|
||||
def toDict(self):
|
||||
return self.__dict__
|
||||
|
@ -145,12 +228,13 @@ class ProcStatistics:
|
|||
class Statistics:
|
||||
"""
|
||||
"""
|
||||
fileVersion = 1.0
|
||||
fileVersion = 2.0
|
||||
|
||||
def __init__(self):
|
||||
self.computer = ComputerStatistics()
|
||||
self.process = ProcStatistics()
|
||||
self.times = []
|
||||
self.interval = 5
|
||||
|
||||
def update(self, proc):
|
||||
'''
|
||||
|
@ -169,19 +253,28 @@ class Statistics:
|
|||
'computer': self.computer.toDict(),
|
||||
'process': self.process.toDict(),
|
||||
'times': self.times,
|
||||
'interval': self.interval
|
||||
}
|
||||
|
||||
def fromDict(self, d):
|
||||
version = d.get('fileVersion', 1.0)
|
||||
version = d.get('fileVersion', 0.0)
|
||||
if version != self.fileVersion:
|
||||
logging.info('Cannot load statistics, version was {} and we only support {}.'.format(version, self.fileVersion))
|
||||
self.computer = {}
|
||||
self.process = {}
|
||||
self.times = []
|
||||
return
|
||||
self.computer.fromDict(d.get('computer', {}))
|
||||
self.process.fromDict(d.get('process', {}))
|
||||
self.times = d.get('times', [])
|
||||
logging.debug('Statistics: file version was {} and the current version is {}.'.format(version, self.fileVersion))
|
||||
self.computer = {}
|
||||
self.process = {}
|
||||
self.times = []
|
||||
try:
|
||||
self.computer.fromDict(d.get('computer', {}))
|
||||
except Exception as e:
|
||||
logging.debug('Failed while loading statistics: computer: "{}".'.format(str(e)))
|
||||
try:
|
||||
self.process.fromDict(d.get('process', {}))
|
||||
except Exception as e:
|
||||
logging.debug('Failed while loading statistics: process: "{}".'.format(str(e)))
|
||||
try:
|
||||
self.times = d.get('times', [])
|
||||
except Exception as e:
|
||||
logging.debug('Failed while loading statistics: times: "{}".'.format(str(e)))
|
||||
|
||||
|
||||
bytesPerGiga = 1024. * 1024. * 1024.
|
||||
|
@ -204,7 +297,7 @@ class StatisticsThread(threading.Thread):
|
|||
try:
|
||||
while True:
|
||||
self.updateStats()
|
||||
if self._stopFlag.wait(60):
|
||||
if self._stopFlag.wait(self.statistics.interval):
|
||||
# stopFlag has been set
|
||||
# update stats one last time and exit main loop
|
||||
if self.proc.is_running():
|
||||
|
|
|
@ -30,7 +30,7 @@ class ConvertSfMFormat(desc.CommandLineNode):
|
|||
label='Describer Types',
|
||||
description='Describer types to keep.',
|
||||
value=['sift'],
|
||||
values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv'],
|
||||
values=['sift', 'sift_float', 'sift_upright', 'akaze', 'akaze_liop', 'akaze_mldb', 'cctag3', 'cctag4', 'sift_ocv', 'akaze_ocv', 'unknown'],
|
||||
exclusive=False,
|
||||
uid=[0],
|
||||
joinChar=',',
|
||||
|
|
34
meshroom/ui/qml/Charts/ChartViewCheckBox.qml
Normal file
34
meshroom/ui/qml/Charts/ChartViewCheckBox.qml
Normal file
|
@ -0,0 +1,34 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
|
||||
/**
|
||||
* A custom CheckBox designed to be used in ChartView's legend.
|
||||
*/
|
||||
CheckBox {
|
||||
id: root
|
||||
|
||||
property color color
|
||||
|
||||
leftPadding: 0
|
||||
font.pointSize: 8
|
||||
|
||||
indicator: Rectangle {
|
||||
width: 11
|
||||
height: width
|
||||
border.width: 1
|
||||
border.color: root.color
|
||||
color: "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.border.width + 1
|
||||
visible: parent.parent.checkState != Qt.Unchecked
|
||||
anchors.topMargin: parent.parent.checkState === Qt.PartiallyChecked ? 5 : 2
|
||||
anchors.bottomMargin: anchors.topMargin
|
||||
color: parent.border.color
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
105
meshroom/ui/qml/Charts/ChartViewLegend.qml
Normal file
105
meshroom/ui/qml/Charts/ChartViewLegend.qml
Normal file
|
@ -0,0 +1,105 @@
|
|||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.9
|
||||
import QtCharts 2.3
|
||||
|
||||
|
||||
/**
|
||||
* ChartViewLegend is an interactive legend component for ChartViews.
|
||||
* It provides a CheckBox for each series that can control its visibility,
|
||||
* and highlight on hovering.
|
||||
*/
|
||||
Flow {
|
||||
id: root
|
||||
|
||||
// The ChartView to create the legend for
|
||||
property ChartView chartView
|
||||
// Currently hovered series
|
||||
property var hoveredSeries: null
|
||||
|
||||
readonly property ButtonGroup buttonGroup: ButtonGroup {
|
||||
id: legendGroup
|
||||
exclusive: false
|
||||
}
|
||||
|
||||
/// Shortcut function to clear legend
|
||||
function clear() {
|
||||
seriesModel.clear();
|
||||
}
|
||||
|
||||
// Update internal ListModel when ChartView's series change
|
||||
Connections {
|
||||
target: chartView
|
||||
onSeriesAdded: seriesModel.append({"series": series})
|
||||
onSeriesRemoved: {
|
||||
for(var i = 0; i < seriesModel.count; ++i)
|
||||
{
|
||||
if(seriesModel.get(i)["series"] === series)
|
||||
{
|
||||
seriesModel.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onChartViewChanged: {
|
||||
clear();
|
||||
for(var i = 0; i < chartView.count; ++i)
|
||||
seriesModel.append({"series": chartView.series(i)});
|
||||
}
|
||||
|
||||
Repeater {
|
||||
|
||||
// ChartView series can't be accessed directly as a model.
|
||||
// Use an intermediate ListModel populated with those series.
|
||||
model: ListModel {
|
||||
id: seriesModel
|
||||
}
|
||||
|
||||
ChartViewCheckBox {
|
||||
ButtonGroup.group: legendGroup
|
||||
|
||||
checked: series.visible
|
||||
text: series.name
|
||||
color: series.color
|
||||
|
||||
onHoveredChanged: {
|
||||
if(hovered && series.visible)
|
||||
root.hoveredSeries = series;
|
||||
else
|
||||
root.hoveredSeries = null;
|
||||
}
|
||||
|
||||
// hovered serie properties override
|
||||
states: [
|
||||
State {
|
||||
when: series && root.hoveredSeries === series
|
||||
PropertyChanges { target: series; width: 5.0 }
|
||||
},
|
||||
State {
|
||||
when: series && root.hoveredSeries && root.hoveredSeries !== series
|
||||
PropertyChanges { target: series; width: 0.2 }
|
||||
}
|
||||
]
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if(mouse.modifiers & Qt.ControlModifier)
|
||||
root.soloSeries(index);
|
||||
else
|
||||
series.visible = !series.visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hide all series but the one at index 'idx'
|
||||
function soloSeries(idx) {
|
||||
for(var i = 0; i < seriesModel.count; i++) {
|
||||
chartView.series(i).visible = false;
|
||||
}
|
||||
chartView.series(idx).visible = true;
|
||||
}
|
||||
|
||||
}
|
4
meshroom/ui/qml/Charts/qmldir
Normal file
4
meshroom/ui/qml/Charts/qmldir
Normal file
|
@ -0,0 +1,4 @@
|
|||
module Charts
|
||||
|
||||
ChartViewLegend 1.0 ChartViewLegend.qml
|
||||
ChartViewCheckBox 1.0 ChartViewCheckBox.qml
|
|
@ -89,8 +89,10 @@ FocusScope {
|
|||
// only set text file viewer source when ListView is fully ready
|
||||
// (either empty or fully populated with a valid currentChunk)
|
||||
// to avoid going through an empty url when switching between two nodes
|
||||
|
||||
if(!chunksLV.count || chunksLV.currentChunk)
|
||||
textFileViewer.source = Filepath.stringToUrl(currentFile);
|
||||
logComponentLoader.source = Filepath.stringToUrl(currentFile);
|
||||
|
||||
}
|
||||
|
||||
TabButton {
|
||||
|
@ -111,12 +113,35 @@ FocusScope {
|
|||
}
|
||||
}
|
||||
|
||||
TextFileViewer {
|
||||
id: textFileViewer
|
||||
Loader {
|
||||
id: logComponentLoader
|
||||
clip: true
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
autoReload: chunksLV.currentChunk !== undefined && chunksLV.currentChunk.statusName === "RUNNING"
|
||||
// source is set in fileSelector
|
||||
property url source
|
||||
sourceComponent: fileSelector.currentItem.fileProperty === "statisticsFile" ? statViewerComponent : textFileViewerComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: textFileViewerComponent
|
||||
TextFileViewer {
|
||||
id: textFileViewer
|
||||
source: logComponentLoader.source
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
autoReload: chunksLV.currentChunk !== undefined && chunksLV.currentChunk.statusName === "RUNNING"
|
||||
// source is set in fileSelector
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: statViewerComponent
|
||||
StatViewer {
|
||||
id: statViewer
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
source: logComponentLoader.source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
539
meshroom/ui/qml/GraphEditor/StatViewer.qml
Normal file
539
meshroom/ui/qml/GraphEditor/StatViewer.qml
Normal file
|
@ -0,0 +1,539 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.3
|
||||
import QtCharts 2.2
|
||||
import QtQuick.Layouts 1.11
|
||||
import Utils 1.0
|
||||
import Charts 1.0
|
||||
import MaterialIcons 2.2
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: 500
|
||||
implicitHeight: 500
|
||||
|
||||
/// Statistics source file
|
||||
property url source
|
||||
|
||||
property var sourceModified: undefined
|
||||
property var jsonObject
|
||||
property real fileVersion: 0.0
|
||||
|
||||
property int nbReads: 1
|
||||
property real deltaTime: 1
|
||||
|
||||
property int nbCores: 0
|
||||
property int cpuFrequency: 0
|
||||
|
||||
property int ramTotal
|
||||
property string ramLabel: "RAM: "
|
||||
|
||||
property int gpuTotalMemory
|
||||
property int gpuMaxAxis: 100
|
||||
property string gpuName
|
||||
|
||||
property color textColor: Colors.sysPalette.text
|
||||
|
||||
|
||||
readonly property var colors: [
|
||||
"#f44336",
|
||||
"#e91e63",
|
||||
"#9c27b0",
|
||||
"#673ab7",
|
||||
"#3f51b5",
|
||||
"#2196f3",
|
||||
"#03a9f4",
|
||||
"#00bcd4",
|
||||
"#009688",
|
||||
"#4caf50",
|
||||
"#8bc34a",
|
||||
"#cddc39",
|
||||
"#ffeb3b",
|
||||
"#ffc107",
|
||||
"#ff9800",
|
||||
"#ff5722",
|
||||
"#b71c1c",
|
||||
"#880E4F",
|
||||
"#4A148C",
|
||||
"#311B92",
|
||||
"#1A237E",
|
||||
"#0D47A1",
|
||||
"#01579B",
|
||||
"#006064",
|
||||
"#004D40",
|
||||
"#1B5E20",
|
||||
"#33691E",
|
||||
"#827717",
|
||||
"#F57F17",
|
||||
"#FF6F00",
|
||||
"#E65100",
|
||||
"#BF360C"
|
||||
]
|
||||
|
||||
onSourceChanged: {
|
||||
sourceModified = undefined;
|
||||
resetCharts()
|
||||
readSourceFile()
|
||||
}
|
||||
|
||||
function getPropertyWithDefault(prop, name, defaultValue) {
|
||||
if(prop.hasOwnProperty(name)) {
|
||||
return prop[name];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: reloadTimer
|
||||
interval: root.deltaTime * 60000; running: true; repeat: false
|
||||
onTriggered: readSourceFile()
|
||||
|
||||
}
|
||||
|
||||
function readSourceFile() {
|
||||
// make sure we are trying to load a statistics file
|
||||
if(!Filepath.urlToString(source).endsWith("statistics"))
|
||||
return;
|
||||
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("GET", source);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) {
|
||||
|
||||
if(sourceModified === undefined || sourceModified < xhr.getResponseHeader('Last-Modified')) {
|
||||
try {
|
||||
root.jsonObject = JSON.parse(xhr.responseText);
|
||||
}
|
||||
catch(exc)
|
||||
{
|
||||
console.warning("Failed to parse statistics file: " + source)
|
||||
root.jsonObject = {};
|
||||
return;
|
||||
}
|
||||
resetCharts();
|
||||
sourceModified = xhr.getResponseHeader('Last-Modified')
|
||||
root.createCharts();
|
||||
reloadTimer.restart();
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function resetCharts() {
|
||||
root.fileVersion = 0.0
|
||||
cpuLegend.clear()
|
||||
cpuChart.removeAllSeries()
|
||||
ramChart.removeAllSeries()
|
||||
gpuChart.removeAllSeries()
|
||||
}
|
||||
|
||||
function createCharts() {
|
||||
root.deltaTime = getPropertyWithDefault(jsonObject, 'interval', 30) / 60.0;
|
||||
root.fileVersion = getPropertyWithDefault(jsonObject, 'fileVersion', 0.0)
|
||||
initCpuChart()
|
||||
initRamChart()
|
||||
initGpuChart()
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
*** CPU ***
|
||||
**************************/
|
||||
|
||||
function initCpuChart() {
|
||||
|
||||
var categories = []
|
||||
var categoryCount = 0
|
||||
var category
|
||||
do {
|
||||
category = jsonObject.computer.curves["cpuUsage." + categoryCount]
|
||||
if(category !== undefined) {
|
||||
categories.push(category)
|
||||
categoryCount++
|
||||
}
|
||||
} while(category !== undefined)
|
||||
|
||||
var nbCores = categories.length
|
||||
root.nbCores = nbCores
|
||||
|
||||
root.cpuFrequency = getPropertyWithDefault(jsonObject.computer, 'cpuFreq', -1)
|
||||
|
||||
root.nbReads = categories[0].length-1
|
||||
|
||||
for(var j = 0; j < nbCores; j++) {
|
||||
var lineSerie = cpuChart.createSeries(ChartView.SeriesTypeLine, "CPU" + j, valueAxisX, valueAxisY)
|
||||
|
||||
if(categories[j].length === 1) {
|
||||
lineSerie.append(0, categories[j][0])
|
||||
lineSerie.append(root.deltaTime, categories[j][0])
|
||||
} else {
|
||||
for(var k = 0; k < categories[j].length; k++) {
|
||||
lineSerie.append(k * root.deltaTime, categories[j][k])
|
||||
}
|
||||
}
|
||||
lineSerie.color = colors[j % colors.length]
|
||||
}
|
||||
|
||||
var averageLine = cpuChart.createSeries(ChartView.SeriesTypeLine, "AVERAGE", valueAxisX, valueAxisY)
|
||||
var average = []
|
||||
|
||||
for(var l = 0; l < categories[0].length; l++) {
|
||||
average.push(0)
|
||||
}
|
||||
|
||||
for(var m = 0; m < categories.length; m++) {
|
||||
for(var n = 0; n < categories[m].length; n++) {
|
||||
average[n] += categories[m][n]
|
||||
}
|
||||
}
|
||||
|
||||
for(var q = 0; q < average.length; q++) {
|
||||
average[q] = average[q] / (categories.length)
|
||||
|
||||
averageLine.append(q * root.deltaTime, average[q])
|
||||
}
|
||||
|
||||
averageLine.color = colors[colors.length-1]
|
||||
}
|
||||
|
||||
function hideOtherCpu(index) {
|
||||
for(var i = 0; i < cpuChart.count; i++) {
|
||||
cpuChart.series(i).visible = false;
|
||||
}
|
||||
cpuChart.series(index).visible = true;
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
*** RAM ***
|
||||
**************************/
|
||||
|
||||
function initRamChart() {
|
||||
|
||||
var ram = getPropertyWithDefault(jsonObject.computer.curves, 'ramUsage', -1)
|
||||
|
||||
root.ramTotal = getPropertyWithDefault(jsonObject.computer, 'ramTotal', -1)
|
||||
root.ramLabel = "RAM: "
|
||||
if(root.ramTotal <= 0)
|
||||
{
|
||||
var maxRamPeak = 0
|
||||
for(var i = 0; i < ram.length; i++) {
|
||||
maxRamPeak = Math.max(maxRamPeak, ram[i])
|
||||
}
|
||||
root.ramTotal = maxRamPeak
|
||||
root.ramLabel = "RAM Max Peak: "
|
||||
}
|
||||
|
||||
var ramSerie = ramChart.createSeries(ChartView.SeriesTypeLine, root.ramLabel + root.ramTotal + "GB", valueAxisX2, valueAxisRam)
|
||||
|
||||
if(ram.length === 1) {
|
||||
// Create 2 entries if we have only one input value to create a segment that can be display
|
||||
ramSerie.append(0, ram[0])
|
||||
ramSerie.append(root.deltaTime, ram[0])
|
||||
} else {
|
||||
for(var i = 0; i < ram.length; i++) {
|
||||
ramSerie.append(i * root.deltaTime, ram[i])
|
||||
}
|
||||
}
|
||||
ramSerie.color = colors[10]
|
||||
}
|
||||
|
||||
/**************************
|
||||
*** GPU ***
|
||||
**************************/
|
||||
|
||||
function initGpuChart() {
|
||||
root.gpuTotalMemory = getPropertyWithDefault(jsonObject.computer, 'gpuMemoryTotal', 0)
|
||||
root.gpuName = getPropertyWithDefault(jsonObject.computer, 'gpuName', '')
|
||||
|
||||
var gpuUsedMemory = getPropertyWithDefault(jsonObject.computer.curves, 'gpuMemoryUsed', 0)
|
||||
var gpuUsed = getPropertyWithDefault(jsonObject.computer.curves, 'gpuUsed', 0)
|
||||
var gpuTemperature = getPropertyWithDefault(jsonObject.computer.curves, 'gpuTemperature', 0)
|
||||
|
||||
var gpuUsedSerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "GPU", valueAxisX3, valueAxisY3)
|
||||
var gpuUsedMemorySerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "Memory", valueAxisX3, valueAxisY3)
|
||||
var gpuTemperatureSerie = gpuChart.createSeries(ChartView.SeriesTypeLine, "Temperature", valueAxisX3, valueAxisY3)
|
||||
|
||||
if(gpuUsedMemory.length === 1) {
|
||||
gpuUsedSerie.append(0, gpuUsed[0])
|
||||
gpuUsedSerie.append(1 * root.deltaTime, gpuUsed[0])
|
||||
|
||||
gpuUsedMemorySerie.append(0, gpuUsedMemory[0] / root.gpuTotalMemory * 100)
|
||||
gpuUsedMemorySerie.append(1 * root.deltaTime, gpuUsedMemory[0] / root.gpuTotalMemory * 100)
|
||||
|
||||
gpuTemperatureSerie.append(0, gpuTemperature[0])
|
||||
gpuTemperatureSerie.append(1 * root.deltaTime, gpuTemperature[0])
|
||||
root.gpuMaxAxis = Math.max(gpuMaxAxis, gpuTemperature[0])
|
||||
} else {
|
||||
for(var i = 0; i < gpuUsedMemory.length; i++) {
|
||||
gpuUsedSerie.append(i * root.deltaTime, gpuUsed[i])
|
||||
|
||||
gpuUsedMemorySerie.append(i * root.deltaTime, gpuUsedMemory[i] / root.gpuTotalMemory * 100)
|
||||
|
||||
gpuTemperatureSerie.append(i * root.deltaTime, gpuTemperature[i])
|
||||
root.gpuMaxAxis = Math.max(gpuMaxAxis, gpuTemperature[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
*** UI ***
|
||||
**************************/
|
||||
|
||||
ScrollView {
|
||||
height: root.height
|
||||
width: root.width
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
|
||||
ColumnLayout {
|
||||
width: root.width
|
||||
|
||||
|
||||
/**************************
|
||||
*** CPU UI ***
|
||||
**************************/
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Button {
|
||||
id: toggleCpuBtn
|
||||
Layout.fillWidth: true
|
||||
text: "Toggle CPU's"
|
||||
state: "closed"
|
||||
|
||||
onClicked: state === "opened" ? state = "closed" : state = "opened"
|
||||
|
||||
MaterialLabel {
|
||||
text: MaterialIcons.arrow_drop_down
|
||||
font.pointSize: 14
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "opened"
|
||||
PropertyChanges { target: cpuBtnContainer; visible: true }
|
||||
PropertyChanges { target: toggleCpuBtn; down: true }
|
||||
},
|
||||
State {
|
||||
name: "closed"
|
||||
PropertyChanges { target: cpuBtnContainer; visible: false }
|
||||
PropertyChanges { target: toggleCpuBtn; down: false }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Item {
|
||||
id: cpuBtnContainer
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: childrenRect.height
|
||||
Layout.leftMargin: 25
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
|
||||
ChartViewCheckBox {
|
||||
id: allCPU
|
||||
text: "ALL"
|
||||
color: textColor
|
||||
checkState: cpuLegend.buttonGroup.checkState
|
||||
leftPadding: 0
|
||||
onClicked: {
|
||||
var _checked = checked;
|
||||
for(var i = 0; i < cpuChart.count; ++i)
|
||||
{
|
||||
cpuChart.series(i).visible = _checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChartViewLegend {
|
||||
id: cpuLegend
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
chartView: cpuChart
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ChartView {
|
||||
id: cpuChart
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width/2
|
||||
margins.top: 0
|
||||
margins.bottom: 0
|
||||
antialiasing: true
|
||||
|
||||
legend.visible: false
|
||||
theme: ChartView.ChartThemeLight
|
||||
backgroundColor: "transparent"
|
||||
plotAreaColor: "transparent"
|
||||
titleColor: textColor
|
||||
|
||||
visible: (root.fileVersion > 0.0) // only visible if we have valid information
|
||||
title: "CPU: " + root.nbCores + " cores, " + root.cpuFrequency + "Hz"
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisY
|
||||
min: 0
|
||||
max: 100
|
||||
titleText: "<span style='color: " + textColor + "'>%</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisX
|
||||
min: 0
|
||||
max: root.deltaTime * Math.max(1, root.nbReads)
|
||||
titleText: "<span style='color: " + textColor + "'>Minutes</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************
|
||||
*** RAM UI ***
|
||||
**************************/
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
ChartView {
|
||||
id: ramChart
|
||||
margins.top: 0
|
||||
margins.bottom: 0
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width/2
|
||||
antialiasing: true
|
||||
legend.color: textColor
|
||||
legend.labelColor: textColor
|
||||
legend.visible: false
|
||||
theme: ChartView.ChartThemeLight
|
||||
backgroundColor: "transparent"
|
||||
plotAreaColor: "transparent"
|
||||
titleColor: textColor
|
||||
|
||||
visible: (root.fileVersion > 0.0) // only visible if we have valid information
|
||||
title: root.ramLabel + root.ramTotal + "GB"
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisY2
|
||||
min: 0
|
||||
max: 100
|
||||
titleText: "<span style='color: " + textColor + "'>%</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisRam
|
||||
min: 0
|
||||
max: root.ramTotal
|
||||
titleText: "<span style='color: " + textColor + "'>GB</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisX2
|
||||
min: 0
|
||||
max: root.deltaTime * Math.max(1, root.nbReads)
|
||||
titleText: "<span style='color: " + textColor + "'>Minutes</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************
|
||||
*** GPU UI ***
|
||||
**************************/
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
|
||||
ChartView {
|
||||
id: gpuChart
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width/2
|
||||
margins.top: 0
|
||||
margins.bottom: 0
|
||||
antialiasing: true
|
||||
legend.color: textColor
|
||||
legend.labelColor: textColor
|
||||
theme: ChartView.ChartThemeLight
|
||||
backgroundColor: "transparent"
|
||||
plotAreaColor: "transparent"
|
||||
titleColor: textColor
|
||||
|
||||
visible: (root.fileVersion >= 2.0) // No GPU information was collected before stats 2.0 fileVersion
|
||||
title: (root.gpuName || root.gpuTotalMemory) ? ("GPU: " + root.gpuName + ", " + root.gpuTotalMemory + "MB") : "No GPU"
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisY3
|
||||
min: 0
|
||||
max: root.gpuMaxAxis
|
||||
titleText: "<span style='color: " + textColor + "'>%, °C</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
|
||||
ValueAxis {
|
||||
id: valueAxisX3
|
||||
min: 0
|
||||
max: root.deltaTime * Math.max(1, root.nbReads)
|
||||
titleText: "<span style='color: " + textColor + "'>Minutes</span>"
|
||||
color: textColor
|
||||
gridLineColor: textColor
|
||||
minorGridLineColor: textColor
|
||||
shadesColor: textColor
|
||||
shadesBorderColor: textColor
|
||||
labelsColor: textColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
# runtime
|
||||
psutil
|
||||
psutil>=5.6.3
|
||||
enum34;python_version<"3.4"
|
||||
PySide2==5.13.0
|
||||
markdown==2.6.11
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue