1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 14:05:38 -05:00

Add Performance tab under Process monitor

This commit is contained in:
bbedward
2025-07-15 11:12:59 -04:00
parent 0349b2c361
commit 1396b0b582
3 changed files with 579 additions and 34 deletions

View File

@@ -10,7 +10,7 @@ Singleton {
// Process list properties
property var processes: []
property bool isUpdating: false
property int processUpdateInterval: 1500
property int processUpdateInterval: 3000
// Performance control - only run when process monitor is actually visible
property bool monitoringEnabled: false
@@ -24,6 +24,26 @@ Singleton {
property real totalCpuUsage: 0.0
property bool systemInfoAvailable: false
// Performance history for charts
property var cpuHistory: []
property var memoryHistory: []
property var networkHistory: ({rx: [], tx: []})
property var diskHistory: ({read: [], write: []})
property int historySize: 60 // Keep 60 data points
// Per-core CPU usage
property var perCoreCpuUsage: []
// Network stats
property real networkRxRate: 0 // bytes/sec
property real networkTxRate: 0 // bytes/sec
property var lastNetworkStats: null
// Disk I/O stats
property real diskReadRate: 0 // bytes/sec
property real diskWriteRate: 0 // bytes/sec
property var lastDiskStats: null
// Sorting options
property string sortBy: "cpu" // "cpu", "memory", "name", "pid"
property bool sortDescending: true
@@ -33,12 +53,39 @@ Singleton {
console.log("ProcessMonitorService: Starting initialization...")
updateProcessList()
console.log("ProcessMonitorService: Initialization complete")
// Test monitoring disabled - only monitor when explicitly enabled
// testTimer.start()
}
Timer {
id: testTimer
interval: 3000
running: false
repeat: false
onTriggered: {
console.log("ProcessMonitorService: Starting test monitoring...")
enableMonitoring(true)
// Stop after 8 seconds
stopTestTimer.start()
}
}
Timer {
id: stopTestTimer
interval: 8000
running: false
repeat: false
onTriggered: {
console.log("ProcessMonitorService: Stopping test monitoring...")
enableMonitoring(false)
}
}
// System information monitoring
Process {
id: systemInfoProcess
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu ' /proc/stat"]
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
running: false
stdout: StdioCollector {
@@ -57,6 +104,36 @@ Singleton {
}
}
// Network monitoring process
Process {
id: networkStatsProcess
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
parseNetworkStats(text.trim())
}
}
}
}
// Disk I/O monitoring process
Process {
id: diskStatsProcess
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
parseDiskStats(text.trim())
}
}
}
}
// Process monitoring with ps command
Process {
id: processListProcess
@@ -123,6 +200,8 @@ Singleton {
if (root.monitoringEnabled) {
updateSystemInfo()
updateProcessList()
updateNetworkStats()
updateDiskStats()
}
}
}
@@ -139,9 +218,29 @@ Singleton {
console.log("ProcessMonitorService: Monitoring", enabled ? "enabled" : "disabled")
root.monitoringEnabled = enabled
if (enabled) {
// Clear history when starting
root.cpuHistory = []
root.memoryHistory = []
root.networkHistory = ({rx: [], tx: []})
root.diskHistory = ({read: [], write: []})
// Immediately update when enabled
updateSystemInfo()
updateProcessList()
updateNetworkStats()
updateDiskStats()
// console.log("ProcessMonitorService: Initial data collection started")
}
}
function updateNetworkStats() {
if (!networkStatsProcess.running && root.monitoringEnabled) {
networkStatsProcess.running = true
}
}
function updateDiskStats() {
if (!diskStatsProcess.running && root.monitoringEnabled) {
diskStatsProcess.running = true
}
}
@@ -244,6 +343,7 @@ Singleton {
function parseSystemInfo(text) {
const lines = text.split('\n')
let section = 'memory'
const coreUsages = []
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
@@ -289,10 +389,103 @@ Singleton {
const used = total - idle - iowait
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0
}
} else if (line.match(/^cpu\d+/)) {
const parts = line.split(/\s+/)
if (parts.length >= 8) {
const user = parseInt(parts[1])
const nice = parseInt(parts[2])
const system = parseInt(parts[3])
const idle = parseInt(parts[4])
const iowait = parseInt(parts[5])
const irq = parseInt(parts[6])
const softirq = parseInt(parts[7])
const total = user + nice + system + idle + iowait + irq + softirq
const used = total - idle - iowait
const usage = total > 0 ? (used / total) * 100 : 0
coreUsages.push(usage)
}
}
}
}
// Update per-core usage
root.perCoreCpuUsage = coreUsages
// Update history
addToHistory(root.cpuHistory, root.totalCpuUsage)
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0
addToHistory(root.memoryHistory, memoryPercent)
// console.log("ProcessMonitorService: Updated - CPU:", root.totalCpuUsage.toFixed(1) + "%", "Memory:", memoryPercent.toFixed(1) + "%", "History length:", root.cpuHistory.length)
root.systemInfoAvailable = true
}
function parseNetworkStats(text) {
const lines = text.split('\n')
let totalRx = 0
let totalTx = 0
for (const line of lines) {
const parts = line.trim().split(/\s+/)
if (parts.length >= 3) {
const rx = parseInt(parts[1])
const tx = parseInt(parts[2])
if (!isNaN(rx) && !isNaN(tx)) {
totalRx += rx
totalTx += tx
}
}
}
if (root.lastNetworkStats) {
const timeDiff = root.processUpdateInterval / 1000
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff)
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff)
// Convert to KB/s for history
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024)
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024)
}
root.lastNetworkStats = { rx: totalRx, tx: totalTx }
}
function parseDiskStats(text) {
const lines = text.split('\n')
let totalRead = 0
let totalWrite = 0
for (const line of lines) {
const parts = line.trim().split(/\s+/)
if (parts.length >= 3) {
const readSectors = parseInt(parts[1])
const writeSectors = parseInt(parts[2])
if (!isNaN(readSectors) && !isNaN(writeSectors)) {
totalRead += readSectors * 512 // Convert sectors to bytes
totalWrite += writeSectors * 512
}
}
}
if (root.lastDiskStats) {
const timeDiff = root.processUpdateInterval / 1000
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff)
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff)
// Convert to MB/s for history
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024))
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024))
}
root.lastDiskStats = { read: totalRead, write: totalWrite }
}
function addToHistory(array, value) {
array.push(value)
if (array.length > root.historySize) {
array.shift()
}
}
}

View File

@@ -281,10 +281,10 @@ PanelWindow {
height: 80
radius: Theme.cornerRadiusLarge
color: ProcessMonitorService.totalSwapKB > 0 ?
Qt.rgba(Theme.tertiary.r, Theme.tertiary.g, Theme.tertiary.b, 0.08) :
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.08) :
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.04)
border.color: ProcessMonitorService.totalSwapKB > 0 ?
Qt.rgba(Theme.tertiary.r, Theme.tertiary.g, Theme.tertiary.b, 0.2) :
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.2) :
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12)
border.width: 1
@@ -298,7 +298,7 @@ PanelWindow {
text: "Swap"
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: ProcessMonitorService.totalSwapKB > 0 ? Theme.tertiary : Theme.surfaceText
color: ProcessMonitorService.totalSwapKB > 0 ? Theme.warning : Theme.surfaceText
opacity: 0.8
}

View File

@@ -443,10 +443,10 @@ PanelWindow {
height: 80
radius: Theme.cornerRadiusLarge
color: ProcessMonitorService.totalSwapKB > 0 ?
Qt.rgba(Theme.tertiary.r, Theme.tertiary.g, Theme.tertiary.b, 0.08) :
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.08) :
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.04)
border.color: ProcessMonitorService.totalSwapKB > 0 ?
Qt.rgba(Theme.tertiary.r, Theme.tertiary.g, Theme.tertiary.b, 0.2) :
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.2) :
Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.12)
border.width: 1
@@ -460,7 +460,7 @@ PanelWindow {
text: "Swap"
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: ProcessMonitorService.totalSwapKB > 0 ? Theme.tertiary : Theme.surfaceText
color: ProcessMonitorService.totalSwapKB > 0 ? Theme.warning : Theme.surfaceText
opacity: 0.8
}
@@ -749,36 +749,365 @@ PanelWindow {
// Define inline components for tabs
Component {
id: performanceTabComponent
Rectangle {
color: "transparent"
Column {
anchors.centerIn: parent
spacing: Theme.spacingL
Column {
anchors.fill: parent
spacing: Theme.spacingM
Text {
text: "analytics"
font.family: Theme.iconFont
font.pixelSize: 48
color: Theme.primary
opacity: 0.6
anchors.horizontalCenter: parent.horizontalCenter
// CPU Section - Compact with per-core bars
Rectangle {
width: parent.width
height: 200
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.04)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
border.width: 1
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
// CPU Header with overall usage
Row {
width: parent.width
height: 32
spacing: Theme.spacingM
Text {
text: "CPU"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
width: 80
height: 24
radius: Theme.cornerRadiusSmall
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
anchors.verticalCenter: parent.verticalCenter
Text {
text: ProcessMonitorService.totalCpuUsage.toFixed(1) + "%"
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.primary
anchors.centerIn: parent
}
}
Item { width: parent.width - 280; height: 1 }
Text {
text: ProcessMonitorService.cpuCount + " cores"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
// Per-core CPU bars - Scrollable
ScrollView {
width: parent.width
height: parent.height - 40
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Column {
width: parent.width
spacing: 6
Repeater {
model: ProcessMonitorService.perCoreCpuUsage.length
Row {
width: parent.width
height: 20
spacing: Theme.spacingS
// Core label
Text {
text: "C" + index
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: 24
anchors.verticalCenter: parent.verticalCenter
}
// Usage bar
Rectangle {
width: parent.width - 80
height: 6
radius: 3
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
anchors.verticalCenter: parent.verticalCenter
Rectangle {
width: parent.width * Math.min(1.0, ProcessMonitorService.perCoreCpuUsage[index] / 100)
height: parent.height
radius: parent.radius
color: {
const usage = ProcessMonitorService.perCoreCpuUsage[index]
if (usage > 80) return Theme.error
if (usage > 60) return Theme.warning
return Theme.primary
}
Behavior on width {
NumberAnimation { duration: Theme.shortDuration }
}
}
}
// Usage percentage
Text {
text: ProcessMonitorService.perCoreCpuUsage[index] ?
ProcessMonitorService.perCoreCpuUsage[index].toFixed(0) + "%" : "0%"
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
width: 32
horizontalAlignment: Text.AlignRight
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
}
}
// Memory Section - Simplified
Rectangle {
width: parent.width
height: 80
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.04)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
border.width: 1
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingM
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 4
Text {
text: "Memory"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
}
Text {
text: ProcessMonitorService.formatSystemMemory(ProcessMonitorService.usedMemoryKB) +
" / " + ProcessMonitorService.formatSystemMemory(ProcessMonitorService.totalMemoryKB)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
Item { width: Theme.spacingL; height: 1 }
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 4
width: 200
Rectangle {
width: parent.width
height: 16
radius: 8
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
Rectangle {
width: ProcessMonitorService.totalMemoryKB > 0 ?
parent.width * (ProcessMonitorService.usedMemoryKB / ProcessMonitorService.totalMemoryKB) : 0
height: parent.height
radius: parent.radius
color: {
const usage = ProcessMonitorService.totalMemoryKB > 0 ?
(ProcessMonitorService.usedMemoryKB / ProcessMonitorService.totalMemoryKB) : 0
if (usage > 0.9) return Theme.error
if (usage > 0.7) return Theme.warning
return Theme.secondary
}
Behavior on width {
NumberAnimation { duration: Theme.mediumDuration }
}
}
}
Text {
text: ProcessMonitorService.totalMemoryKB > 0 ?
((ProcessMonitorService.usedMemoryKB / ProcessMonitorService.totalMemoryKB) * 100).toFixed(1) + "% used" :
"No data"
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.surfaceText
}
}
Item { width: parent.width - 300; height: 1 }
// Swap info - compact
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 4
visible: ProcessMonitorService.totalSwapKB > 0
Text {
text: "Swap"
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.warning
}
Text {
text: ProcessMonitorService.formatSystemMemory(ProcessMonitorService.usedSwapKB) +
" / " + ProcessMonitorService.formatSystemMemory(ProcessMonitorService.totalSwapKB)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
}
// Network & Disk I/O - Combined compact view
Row {
width: parent.width
height: 80
spacing: Theme.spacingM
// Network I/O
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: 80
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.04)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
border.width: 1
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "Network"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Bold
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
Row {
spacing: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
Row {
spacing: 4
Text {
text: "↓"
font.pixelSize: Theme.fontSizeSmall
color: Theme.info
}
Text {
text: formatNetworkSpeed(ProcessMonitorService.networkRxRate)
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.surfaceText
}
}
Row {
spacing: 4
Text {
text: "↑"
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
}
Text {
text: formatNetworkSpeed(ProcessMonitorService.networkTxRate)
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.surfaceText
}
}
}
}
}
Text {
text: "Performance Monitoring"
font.pixelSize: Theme.fontSizeLarge + 2
font.weight: Font.Bold
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
// Disk I/O
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: 80
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.04)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
border.width: 1
Text {
text: "Real-time system performance charts\nwill be displayed here"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "Disk"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Bold
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
Row {
spacing: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
Row {
spacing: 4
Text {
text: "R"
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
}
Text {
text: formatDiskSpeed(ProcessMonitorService.diskReadRate)
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.surfaceText
}
}
Row {
spacing: 4
Text {
text: "W"
font.pixelSize: Theme.fontSizeSmall
color: Theme.warning
}
Text {
text: formatDiskSpeed(ProcessMonitorService.diskWriteRate)
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Bold
color: Theme.surfaceText
}
}
}
}
}
}
}
@@ -1168,4 +1497,27 @@ PanelWindow {
show()
}
}
// Helper functions for formatting
function formatNetworkSpeed(bytesPerSec) {
if (bytesPerSec < 1024) {
return bytesPerSec.toFixed(0) + " B/s"
} else if (bytesPerSec < 1024 * 1024) {
return (bytesPerSec / 1024).toFixed(1) + " KB/s"
} else if (bytesPerSec < 1024 * 1024 * 1024) {
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s"
} else {
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s"
}
}
function formatDiskSpeed(bytesPerSec) {
if (bytesPerSec < 1024 * 1024) {
return (bytesPerSec / 1024).toFixed(1) + " KB/s"
} else if (bytesPerSec < 1024 * 1024 * 1024) {
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s"
} else {
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s"
}
}
}