From 22a7ffb93ee7ef8d0c05e86e99fb1c70efca0888 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Mon, 8 Apr 2019 12:45:42 +0200 Subject: Implement system load visualization Implement system load visualization in TaskManager as a separate tab showing average system load graph basing on the info provided by the backend. Change-Id: I81359185516ebb1b6218bfd9ca53f1d76dfddeb4 Signed-off-by: Vitaly Wool --- app/app.pro | 2 +- app/main.cpp | 5 +- app/main.qml | 252 ++++++++++++++++++++++++++++++++++------------------ app/taskmanager.cpp | 22 +++++ app/taskmanager.h | 4 + 5 files changed, 196 insertions(+), 89 deletions(-) diff --git a/app/app.pro b/app/app.pro index 345fc5b..3607728 100644 --- a/app/app.pro +++ b/app/app.pro @@ -1,6 +1,6 @@ TARGET = taskmanager -QT = quickcontrols2 websockets +QT = quickcontrols2 websockets charts SOURCES += \ main.cpp \ diff --git a/app/main.cpp b/app/main.cpp index c4d4bba..9a3a1ce 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -17,8 +18,8 @@ int main(int argc, char *argv[]) { QString graphic_role = QString("utility"); QString myname = QString("TaskManager"); - - QGuiApplication app(argc, argv); + + QApplication app(argc, argv); QCommandLineParser parser; parser.addPositionalArgument("port", app.translate("main", "port for binding")); diff --git a/app/main.qml b/app/main.qml index a76aaa8..7e76265 100644 --- a/app/main.qml +++ b/app/main.qml @@ -4,6 +4,7 @@ import QtQuick.Controls 1.4 import QtQuick.Controls 2.0 as NewControls import QtQuick.Layouts 1.1 import TaskManager 1.0 +import QtCharts 2.2 Window { id: root @@ -43,7 +44,11 @@ Window { } onShowProcessInfo: { - infoPopup.infoText = info_; + mainTabview.getTab(0).item.updateInfo(info_); + } + + onUpdateLoadAverage: { + mainTabview.getTab(1).item.tempAdd(value_); } } @@ -51,102 +56,177 @@ Window { id: libraryModel } - Rectangle { - id: mainview - width: parent.width - height: parent.height - - Row { - id: buttonRow - width: parent.width - anchors.top: mainview.top - - RowLayout { - id: buttonRowLayout - spacing: 6 - - NewControls.Popup { - id: infoPopup - x: 200 - y: 200 - width: 300 - height: 200 - modal: true - focus: true - closePolicy: NewControls.Popup.CloseOnPressOutsideParent - property var infoText: "Getting extra information for process" - function doOpen(tid) { - taskmgr.getExtraInfo(tid) - infoPopup.open() - } - contentItem: Text { - text: infoPopup.infoText - } + TabView { + id: mainTabview + currentIndex: 0 + anchors.fill: parent + + Tab { + id: processesTab + active: true + anchors.fill: parent + title: "Processes" + + Rectangle { + id: procsView + width: parent.width + height: parent.height + + function updateInfo(info_) + { + infoPopup.infoText = info_; } - Button { - id: infoButton - text: "Info" - enabled: tableView.currentRow != -1 - onClicked: infoPopup.doOpen(libraryModel.get(tableView.currentRow).tid) + Row { + id: buttonRow + width: parent.width + anchors.top: mainTabview.top + + RowLayout { + id: buttonRowLayout + spacing: 6 + + NewControls.Popup { + id: infoPopup + x: 200 + y: 200 + width: 300 + height: 200 + modal: true + focus: true + closePolicy: NewControls.Popup.CloseOnPressOutsideParent + property var infoText: "Getting extra information for process" + function doOpen(tid) { + taskmgr.getExtraInfo(tid) + infoPopup.open() + } + contentItem: Text { + text: infoPopup.infoText + } + } + + Button { + id: infoButton + text: "Info" + enabled: tableView.currentRow != -1 + onClicked: infoPopup.doOpen(libraryModel.get(tableView.currentRow).tid) + } + + Button { + id: killButton + text: "Kill" + enabled: tableView.currentRow != -1 + onClicked: { + tableView.selection.forEach( function(rowIndex) { + taskmgr.kill(libraryModel.get(rowIndex).tid)} + ) + tableView.selection.clear() + } + } + } } - Button { - id: killButton - text: "Kill" - enabled: tableView.currentRow != -1 - onClicked: { - tableView.selection.forEach( function(rowIndex) { - taskmgr.kill(libraryModel.get(rowIndex).tid)} - ) - tableView.selection.clear() + TableView { + id: tableView + height: parent.height - buttonRow.height + width: parent.width + anchors.top: buttonRow.bottom + + TableViewColumn { + role: "cmd" + title: "Process" + width: 150 + } + TableViewColumn { + role: "tid" + title: "ID" + width: 80 + } + TableViewColumn { + role: "user" + title: "User" + width: 80 } + TableViewColumn { + role: "system_cpu" + title: "System %" + width: 100 + } + TableViewColumn { + role: "user_cpu" + title: "User %" + width: 100 + } + TableViewColumn { + role: "resident_memory" + title: "Memory" + width: 100 + } + TableViewColumn { + role: "state" + title: "State" + width: 90 + } + model: libraryModel } } } - TableView { - id: tableView - height: parent.height - buttonRow.height - width: parent.width - anchors.top: buttonRow.bottom + Tab { + id: systemTab + active: true + anchors.fill: parent + title: "System" + + ChartView { + id: chartView + title: "System load average" + width: parent.width + height: parent.height / 2 + legend.visible: false + antialiasing: true + + LineSeries { + id: loadSeries + + axisX: DateTimeAxis { + id: timeAxis + labelsVisible: false + format: "hh:mm" + } - TableViewColumn { - role: "cmd" - title: "Process" - width: 150 - } - TableViewColumn { - role: "tid" - title: "ID" - width: 80 - } - TableViewColumn { - role: "user" - title: "User" - width: 80 - } - TableViewColumn { - role: "system_cpu" - title: "System %" - width: 100 - } - TableViewColumn { - role: "user_cpu" - title: "User %" - width: 100 - } - TableViewColumn { - role: "resident_memory" - title: "Memory" - width: 100 - } - TableViewColumn { - role: "state" - title: "State" - width: 90 + axisY: ValueAxis { + id: valueAxis + max: 2 + min: 0 + tickCount: 3 + } + + XYPoint { x: new Date().getTime(); y: 0 } + XYPoint { x: (new Date().getTime()+ 10); y: 0 } + + } + + function tempAdd(value_) + { + var time_ = new Date(); + + if (value_ > valueAxis.max) + valueAxis.max = value_; + + loadSeries.append(time_.getTime(), value_); + timeAxis.max = time_; + + if (loadSeries.count > 1800) + { + loadSeries.remove(0); + time_.setMinutes(time_.getMinutes() - 30); + timeAxis.min = time_; + } + + chartView.update(); + } } - model: libraryModel } } } diff --git a/app/taskmanager.cpp b/app/taskmanager.cpp index ece6d83..5726388 100644 --- a/app/taskmanager.cpp +++ b/app/taskmanager.cpp @@ -28,6 +28,11 @@ void TaskManager::onConnected() timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(query())); timer->start(3000); + + loadAvg(); + loadAvgTimer = new QTimer(); + connect(loadAvgTimer, SIGNAL(timeout()), this, SLOT(loadAvg())); + loadAvgTimer->start(1000); } void TaskManager::kill(int tid) { callService("kill_process", QJsonValue(tid)); @@ -37,6 +42,10 @@ void TaskManager::getExtraInfo(int tid) { callService("get_extra_info", QJsonValue(tid)); } +void TaskManager::loadAvg() { + callService("get_load_avg", QJsonValue()); +} + void TaskManager::query() { callService("get_process_list", QJsonValue(QJsonObject({{"processes", QJsonValue()}}))); @@ -71,6 +80,10 @@ void TaskManager::ProcessResponse(Message *message) QJsonObject info = message->replyData()["info"].toObject(); ProcessResponseExtraInfo(info); } + if (QString::compare(msgType, "loadAvgInfo") == 0) { + QJsonObject loadInfo = message->replyData()["loadInfo"].toObject(); + ProcessResponseLoadAvg(loadInfo); + } // more response types to follow } @@ -144,3 +157,12 @@ void TaskManager::ProcessResponseExtraInfo(QJsonObject &info) emit showProcessInfo(infoString); } + +void TaskManager::ProcessResponseLoadAvg(QJsonObject &loadInfo) +{ + if (loadInfo.size() == 0) { + return; + } + + emit updateLoadAverage(loadInfo["value"].toDouble()); +} diff --git a/app/taskmanager.h b/app/taskmanager.h index 0bd53d5..180c379 100644 --- a/app/taskmanager.h +++ b/app/taskmanager.h @@ -22,18 +22,21 @@ public: Q_INVOKABLE void kill(int tid); Q_INVOKABLE void getExtraInfo(int tid); QTimer *timer; + QTimer *loadAvgTimer; signals: void updateProcess(const QString& cmd_, int tid_, int euid_, double scpu_, double ucpu_, double resident_memory_, const QString& state_); void addProcess(const QString& cmd_, int tid_, int euid_, double scpu_, double ucpu_, double resident_memory_, const QString& state_); void removeProcess(int tid_); void showProcessInfo(const QString info_); + void updateLoadAverage(double value_); private slots: void query(); void callService(const QString& ccommand, QJsonValue value); void onConnected(); void onMessageReceived(MessageType type, Message *message); + void loadAvg(); private: MessageEngine *m_loop; @@ -42,6 +45,7 @@ private: void ProcessResponse(Message *message); void ProcessResponseTasklist(QJsonArray& processes); void ProcessResponseExtraInfo(QJsonObject& info); + void ProcessResponseLoadAvg(QJsonObject& loadInfo); }; #endif // TASKMANAGER_H -- cgit 1.2.3-korg