From cbe92fc2b44c61a26a9039851a416f1dd214d8c2 Mon Sep 17 00:00:00 2001 From: shouya <526598+shouya@users.noreply.github.com> Date: Thu, 1 Jan 2026 18:52:01 +0900 Subject: [PATCH] add load average info to system monitor widget --- Assets/Translations/en.json | 8 ++ Assets/settings-default.json | 1 + Assets/settings-widgets-default.json | 1 + Commons/Settings.qml | 1 + Modules/Bar/Widgets/SystemMonitor.qml | 76 +++++++++++++++++++ .../WidgetSettings/SystemMonitorSettings.qml | 11 +++ .../Panels/Settings/Tabs/SystemMonitorTab.qml | 37 +++++++++ Services/System/SystemStatService.qml | 56 ++++++++++++++ Services/UI/BarWidgetRegistry.qml | 1 + 9 files changed, 192 insertions(+) diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 88654d247..cc2d51ea2 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -331,6 +331,10 @@ "description": "Show GPU temperature readings if available.", "label": "GPU temperature" }, + "load-average": { + "description": "Display system load average.", + "label": "Load average" + }, "memory-percentage": { "description": "Show memory usage as a percentage instead of absolute values.", "label": "Memory as percentage" @@ -2407,6 +2411,9 @@ "gpu-section": { "label": "GPU temperature" }, + "load-average-section": { + "label": "Load average" + }, "highlight-colors-section": { "label": "Highlight colors" }, @@ -2663,6 +2670,7 @@ "download": "Download", "download-speed": "Download Speed", "gpu-temp": "GPU Temp", + "load-average": "Load Average", "memory": "Memory", "title": "System Monitor", "upload": "Upload", diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 1ec94cd93..7fa2b89be 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -263,6 +263,7 @@ "cpuPollingInterval": 3000, "tempPollingInterval": 3000, "gpuPollingInterval": 3000, + "loadAvgPollingInterval": 3000, "enableDgpuMonitoring": false, "memPollingInterval": 3000, "diskPollingInterval": 3000, diff --git a/Assets/settings-widgets-default.json b/Assets/settings-widgets-default.json index 672790dbf..b2e63fb91 100644 --- a/Assets/settings-widgets-default.json +++ b/Assets/settings-widgets-default.json @@ -114,6 +114,7 @@ "showCpuUsage": true, "showCpuTemp": true, "showGpuTemp": false, + "showLoadAverage": false, "showMemoryUsage": true, "showMemoryAsPercent": false, "showNetworkStats": false, diff --git a/Commons/Settings.qml b/Commons/Settings.qml index d02f97031..e06a683fa 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -491,6 +491,7 @@ Singleton { property int memPollingInterval: 3000 property int diskPollingInterval: 3000 property int networkPollingInterval: 3000 + property int loadAvgPollingInterval: 3000 property bool useCustomColors: false property string warningColor: "" property string criticalColor: "" diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml index 094678600..961285e44 100644 --- a/Modules/Bar/Widgets/SystemMonitor.qml +++ b/Modules/Bar/Widgets/SystemMonitor.qml @@ -48,6 +48,7 @@ Rectangle { readonly property bool showMemoryAsPercent: (widgetSettings.showMemoryAsPercent !== undefined) ? widgetSettings.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent readonly property bool showNetworkStats: (widgetSettings.showNetworkStats !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats readonly property bool showDiskUsage: (widgetSettings.showDiskUsage !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage + readonly property bool showLoadAverage: (widgetSettings.showLoadAverage !== undefined) ? widgetSettings.showLoadAverage : widgetMetadata.showLoadAverage readonly property string diskPath: (widgetSettings.diskPath !== undefined) ? widgetSettings.diskPath : widgetMetadata.diskPath readonly property string fontFamily: useMonospaceFont ? Settings.data.ui.fontFixed : Settings.data.ui.fontDefault @@ -74,6 +75,11 @@ Rectangle { lines.push(`${I18n.tr("system-monitor.gpu-temp")}: ${Math.round(SystemStatService.gpuTemp)}°C`); } + // Load Average + if (SystemStatService.loadAvg1 >= 0) { + lines.push(`${I18n.tr("system-monitor.load-average")}: ${SystemStatService.loadAvg1.toFixed(2)} ${SystemStatService.loadAvg5.toFixed(2)} ${SystemStatService.loadAvg15.toFixed(2)}`); + } + // Memory lines.push(`${I18n.tr("system-monitor.memory")}: ${Math.round(SystemStatService.memPercent)}% (${SystemStatService.formatMemoryGb(SystemStatService.memGb)})`); @@ -446,6 +452,76 @@ Rectangle { } } + // Load Average Component + Item { + id: loadAvgContainer + implicitWidth: loadAvgContent.implicitWidth + implicitHeight: loadAvgContent.implicitHeight + Layout.preferredWidth: isVertical ? root.width : implicitWidth + Layout.preferredHeight: compactMode ? implicitHeight : Style.capsuleHeight + Layout.alignment: isVertical ? Qt.AlignHCenter : Qt.AlignVCenter + visible: showLoadAverage && SystemStatService.nproc > 0 + + GridLayout { + id: loadAvgContent + anchors.centerIn: parent + flow: (isVertical && !compactMode) ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: (isVertical && !compactMode) ? 2 : 1 + columns: (isVertical && !compactMode) ? 1 : 2 + rowSpacing: Style.marginXXS + columnSpacing: compactMode ? 3 : Style.marginXS + + Item { + Layout.alignment: Qt.AlignCenter + Layout.row: (isVertical && !compactMode) ? 1 : 0 + Layout.column: 0 + Layout.fillWidth: isVertical + implicitWidth: iconSize + implicitHeight: iconSize + + NIcon { + icon: "cpu-usage" + pointSize: iconSize + applyUiScale: false + anchors.centerIn: parent + color: Color.mOnSurface + } + } + + // Text mode + NText { + visible: !compactMode + text: SystemStatService.loadAvg1.toFixed(1) + family: fontFamily + pointSize: textSize + applyUiScale: false + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: textColor + Layout.row: isVertical ? 0 : 0 + Layout.column: isVertical ? 0 : 1 + scale: isVertical ? Math.min(1.0, root.width / implicitWidth) : 1.0 + } + + // Compact mode + Loader { + active: compactMode + visible: compactMode + sourceComponent: miniGaugeComponent + Layout.alignment: Qt.AlignCenter + Layout.row: 0 + Layout.column: 1 + + onLoaded: { + item.ratio = Qt.binding(() => Math.min(1, SystemStatService.loadAvg1 / SystemStatService.nproc)); + item.statColor = Qt.binding(() => Color.mPrimary); + } + } + } + } + // Memory Usage Component Item { id: memoryContainer diff --git a/Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml b/Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml index 236cabb9b..67849546d 100644 --- a/Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml +++ b/Modules/Panels/Settings/Bar/WidgetSettings/SystemMonitorSettings.qml @@ -20,6 +20,7 @@ ColumnLayout { property bool valueShowCpuUsage: widgetData.showCpuUsage !== undefined ? widgetData.showCpuUsage : widgetMetadata.showCpuUsage property bool valueShowCpuTemp: widgetData.showCpuTemp !== undefined ? widgetData.showCpuTemp : widgetMetadata.showCpuTemp property bool valueShowGpuTemp: widgetData.showGpuTemp !== undefined ? widgetData.showGpuTemp : widgetMetadata.showGpuTemp + property bool valueShowLoadAverage: widgetData.showLoadAverage !== undefined ? widgetData.showLoadAverage : widgetMetadata.showLoadAverage property bool valueShowMemoryUsage: widgetData.showMemoryUsage !== undefined ? widgetData.showMemoryUsage : widgetMetadata.showMemoryUsage property bool valueShowMemoryAsPercent: widgetData.showMemoryAsPercent !== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent property bool valueShowNetworkStats: widgetData.showNetworkStats !== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats @@ -34,6 +35,7 @@ ColumnLayout { settings.showCpuUsage = valueShowCpuUsage; settings.showCpuTemp = valueShowCpuTemp; settings.showGpuTemp = valueShowGpuTemp; + settings.showLoadAverage = valueShowLoadAverage; settings.showMemoryUsage = valueShowMemoryUsage; settings.showMemoryAsPercent = valueShowMemoryAsPercent; settings.showNetworkStats = valueShowNetworkStats; @@ -97,6 +99,15 @@ ColumnLayout { visible: SystemStatService.gpuAvailable } + NToggle { + id: showLoadAverage + Layout.fillWidth: true + label: I18n.tr("bar.widget-settings.system-monitor.load-average.label") + description: I18n.tr("bar.widget-settings.system-monitor.load-average.description") + checked: valueShowLoadAverage + onToggled: checked => valueShowLoadAverage = checked + } + NToggle { id: showMemoryUsage Layout.fillWidth: true diff --git a/Modules/Panels/Settings/Tabs/SystemMonitorTab.qml b/Modules/Panels/Settings/Tabs/SystemMonitorTab.qml index 23498a2f6..eb1b6be50 100644 --- a/Modules/Panels/Settings/Tabs/SystemMonitorTab.qml +++ b/Modules/Panels/Settings/Tabs/SystemMonitorTab.qml @@ -380,6 +380,43 @@ ColumnLayout { } } + // Load Average + NText { + Layout.fillWidth: true + Layout.topMargin: Style.marginM + text: I18n.tr("settings.system-monitor.load-average-section.label") + pointSize: Style.fontSizeM + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM + + ColumnLayout { + Layout.fillWidth: true + spacing: Style.marginM + + NText { + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + text: I18n.tr("settings.system-monitor.polling-interval.label") + pointSize: Style.fontSizeS + } + + NSpinBox { + Layout.alignment: Qt.AlignHCenter + from: 250 + to: 10000 + stepSize: 250 + value: Settings.data.systemMonitor.loadAvgPollingInterval + isSettings: true + defaultValue: Settings.getDefaultValue("systemMonitor.loadAvgPollingInterval") + onValueChanged: Settings.data.systemMonitor.loadAvgPollingInterval = value + suffix: " ms" + } + } + } + // Memory Usage NText { Layout.fillWidth: true diff --git a/Services/System/SystemStatService.qml b/Services/System/SystemStatService.qml index 19b304975..0ede9c116 100644 --- a/Services/System/SystemStatService.qml +++ b/Services/System/SystemStatService.qml @@ -32,6 +32,10 @@ Singleton { property real txSpeed: 0 property real zfsArcSizeKb: 0 // ZFS ARC cache size in KB property real zfsArcCminKb: 0 // ZFS ARC minimum (non-reclaimable) size in KB + property real loadAvg1: 0 + property real loadAvg5: 0 + property real loadAvg15: 0 + property int nproc: 0 // Number of cpu cores // Network max speed tracking (learned over time, cached for 7 days) readonly property real rxMaxSpeed: { @@ -187,6 +191,12 @@ Singleton { // Check for ZFS ARC stats on startup zfsArcStatsFile.reload(); + + // Get nproc on startup + nprocProcess.running = true; + + // Get initial load average + loadAvgFile.reload(); } // Re-run GPU detection when dGPU opt-in setting changes @@ -228,6 +238,21 @@ Singleton { onTriggered: cpuStatFile.reload() } + // Timer for load average + Timer { + id: loadAvgTimer + interval: root.normalizeInterval(Settings.data.systemMonitor.loadAvgPollingInterval) + repeat: true + running: true + triggeredOnStart: true + onIntervalChanged: { + if (running) { + restart(); + } + } + onTriggered: loadAvgFile.reload() + } + // Timer for CPU temperature Timer { id: cpuTempTimer @@ -326,6 +351,12 @@ Singleton { onLoaded: calculateNetworkSpeed(text()) } + FileView { + id: loadAvgFile + path: "/proc/loadavg" + onLoaded: parseLoadAverage(text()) + } + // ZFS ARC stats file (only exists on ZFS systems) FileView { id: zfsArcStatsFile @@ -375,6 +406,18 @@ Singleton { } } + // Process to get number of processors + Process { + id: nprocProcess + command: ["nproc"] + running: false + stdout: StdioCollector { + onStreamFinished: { + root.nproc = parseInt(text.trim()); + } + } + } + // -------------------------------------------- // -------------------------------------------- // CPU Temperature @@ -638,6 +681,19 @@ Singleton { } } + // ------------------------------------------------------- + // Parse load average from /proc/loadavg + function parseLoadAverage(text) { + if (!text) + return; + const parts = text.trim().split(/\s+/); + if (parts.length >= 3) { + root.loadAvg1 = parseFloat(parts[0]); + root.loadAvg5 = parseFloat(parts[1]); + root.loadAvg15 = parseFloat(parts[2]); + } + } + // ------------------------------------------------------- // Parse memory info from /proc/meminfo function parseMemoryInfo(text) { diff --git a/Services/UI/BarWidgetRegistry.qml b/Services/UI/BarWidgetRegistry.qml index 19da99627..3a0c4e7c8 100644 --- a/Services/UI/BarWidgetRegistry.qml +++ b/Services/UI/BarWidgetRegistry.qml @@ -184,6 +184,7 @@ Singleton { "showCpuUsage": true, "showCpuTemp": true, "showGpuTemp": false, + "showLoadAverage": false, "showMemoryUsage": true, "showMemoryAsPercent": false, "showNetworkStats": false,