Merge remote-tracking branch 'origin/develop' into dev/syncCamera

This commit is contained in:
Yann Lanthony 2019-09-16 12:24:22 +02:00
commit 633b8c0e00
No known key found for this signature in database
GPG key ID: 519FAE6DF7A70642
12 changed files with 857 additions and 25 deletions

View file

@ -24,13 +24,13 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Log** **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):** **Desktop (please complete the following and other pertinent information):**
- OS: [e.g. win 10, osx, ] - OS: [e.g. win 10, osx, ]
- Version [e.g. 2019.1]
- Python version [e.g. 2.6] - Python version [e.g. 2.6]
- Qt/PySide version [e.g. 5.12.4] - 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] - Binary version (if applicable) [e.g. 2019.1]
- Commit reference (if applicable) [e.g. 08ddbe2] - Commit reference (if applicable) [e.g. 08ddbe2]

31
.github/ISSUE_TEMPLATE/question_help.md vendored Normal file
View 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.

View file

@ -1,5 +1,5 @@
# packaging # packaging
cx_Freeze cx_Freeze==5.1.1
# testing # testing
pytest pytest

View file

@ -285,6 +285,7 @@ class NodeChunk(BaseObject):
# ask and wait for the stats thread to stop # ask and wait for the stats thread to stop
self.statThread.stopRequest() self.statThread.stopRequest()
self.statThread.join() self.statThread.join()
self.statistics = stats.Statistics()
del runningProcesses[self.name] del runningProcesses[self.name]
self.upgradeStatusTo(Status.SUCCESS) self.upgradeStatusTo(Status.SUCCESS)

View file

@ -1,8 +1,18 @@
from collections import defaultdict from collections import defaultdict
import subprocess
import logging import logging
import psutil import psutil
import time import time
import threading 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): def bytes2human(n):
@ -25,15 +35,58 @@ def bytes2human(n):
class ComputerStatistics: class ComputerStatistics:
def __init__(self): def __init__(self):
# TODO: init
self.nbCores = 0 self.nbCores = 0
self.cpuFreq = 0 self.cpuFreq = 0
self.ramTotal = 0
self.ramAvailable = 0 # GB self.ramAvailable = 0 # GB
self.vramAvailable = 0 # GB self.vramAvailable = 0 # GB
self.swapAvailable = 0 self.swapAvailable = 0
self.gpuMemoryTotal = 0
self.gpuName = ''
self.curves = defaultdict(list) 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): def _addKV(self, k, v):
if isinstance(v, tuple): if isinstance(v, tuple):
for ki, vi in v._asdict().items(): for ki, vi in v._asdict().items():
@ -45,11 +98,41 @@ class ComputerStatistics:
self.curves[k].append(v) self.curves[k].append(v)
def update(self): def update(self):
self.initOnFirstTime()
self._addKV('cpuUsage', psutil.cpu_percent(percpu=True)) # interval=None => non-blocking (percentage since last call) 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('ramUsage', psutil.virtual_memory().percent)
self._addKV('swapUsage', psutil.swap_memory().percent) self._addKV('swapUsage', psutil.swap_memory().percent)
self._addKV('vramUsage', 0) self._addKV('vramUsage', 0)
self._addKV('ioCounters', psutil.disk_io_counters()) 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): def toDict(self):
return self.__dict__ return self.__dict__
@ -145,12 +228,13 @@ class ProcStatistics:
class Statistics: class Statistics:
""" """
""" """
fileVersion = 1.0 fileVersion = 2.0
def __init__(self): def __init__(self):
self.computer = ComputerStatistics() self.computer = ComputerStatistics()
self.process = ProcStatistics() self.process = ProcStatistics()
self.times = [] self.times = []
self.interval = 5
def update(self, proc): def update(self, proc):
''' '''
@ -169,19 +253,28 @@ class Statistics:
'computer': self.computer.toDict(), 'computer': self.computer.toDict(),
'process': self.process.toDict(), 'process': self.process.toDict(),
'times': self.times, 'times': self.times,
'interval': self.interval
} }
def fromDict(self, d): def fromDict(self, d):
version = d.get('fileVersion', 1.0) version = d.get('fileVersion', 0.0)
if version != self.fileVersion: if version != self.fileVersion:
logging.info('Cannot load statistics, version was {} and we only support {}.'.format(version, self.fileVersion)) logging.debug('Statistics: file version was {} and the current version is {}.'.format(version, self.fileVersion))
self.computer = {} self.computer = {}
self.process = {} self.process = {}
self.times = [] self.times = []
return try:
self.computer.fromDict(d.get('computer', {})) 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', {})) 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', []) self.times = d.get('times', [])
except Exception as e:
logging.debug('Failed while loading statistics: times: "{}".'.format(str(e)))
bytesPerGiga = 1024. * 1024. * 1024. bytesPerGiga = 1024. * 1024. * 1024.
@ -204,7 +297,7 @@ class StatisticsThread(threading.Thread):
try: try:
while True: while True:
self.updateStats() self.updateStats()
if self._stopFlag.wait(60): if self._stopFlag.wait(self.statistics.interval):
# stopFlag has been set # stopFlag has been set
# update stats one last time and exit main loop # update stats one last time and exit main loop
if self.proc.is_running(): if self.proc.is_running():

View file

@ -30,7 +30,7 @@ class ConvertSfMFormat(desc.CommandLineNode):
label='Describer Types', label='Describer Types',
description='Describer types to keep.', description='Describer types to keep.',
value=['sift'], 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, exclusive=False,
uid=[0], uid=[0],
joinChar=',', joinChar=',',

View 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
}
}
}

View 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;
}
}

View file

@ -0,0 +1,4 @@
module Charts
ChartViewLegend 1.0 ChartViewLegend.qml
ChartViewCheckBox 1.0 ChartViewCheckBox.qml

View file

@ -89,8 +89,10 @@ FocusScope {
// only set text file viewer source when ListView is fully ready // only set text file viewer source when ListView is fully ready
// (either empty or fully populated with a valid currentChunk) // (either empty or fully populated with a valid currentChunk)
// to avoid going through an empty url when switching between two nodes // to avoid going through an empty url when switching between two nodes
if(!chunksLV.count || chunksLV.currentChunk) if(!chunksLV.count || chunksLV.currentChunk)
textFileViewer.source = Filepath.stringToUrl(currentFile); logComponentLoader.source = Filepath.stringToUrl(currentFile);
} }
TabButton { TabButton {
@ -111,13 +113,36 @@ FocusScope {
} }
} }
Loader {
id: logComponentLoader
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
property url source
sourceComponent: fileSelector.currentItem.fileProperty === "statisticsFile" ? statViewerComponent : textFileViewerComponent
}
Component {
id: textFileViewerComponent
TextFileViewer { TextFileViewer {
id: textFileViewer id: textFileViewer
source: logComponentLoader.source
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
autoReload: chunksLV.currentChunk !== undefined && chunksLV.currentChunk.statusName === "RUNNING" autoReload: chunksLV.currentChunk !== undefined && chunksLV.currentChunk.statusName === "RUNNING"
// source is set in fileSelector // source is set in fileSelector
} }
} }
Component {
id: statViewerComponent
StatViewer {
id: statViewer
Layout.fillWidth: true
Layout.fillHeight: true
source: logComponentLoader.source
}
}
}
} }
} }

View 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
}
}
}
}
}
}

View file

@ -1,5 +1,5 @@
# runtime # runtime
psutil psutil>=5.6.3
enum34;python_version<"3.4" enum34;python_version<"3.4"
PySide2==5.13.0 PySide2==5.13.0
markdown==2.6.11 markdown==2.6.11