📜 ⬆️ ⬇️

We write our system monitor (Linux)

In this article I will describe the process of creating your own system monitor. This system monitor shows:

If it is not clear to someone in the penultimate point what it means without cached data, then read this , it describes what it means. Also in this system monitor implemented the ability to complete processes.
As a result, we should have such a system monitor:
imageimage
Now its main features are defined, and I can proceed to the description of the code, I warn you right away that the code will only work in Linux, since almost all of its features are based on parsing the / proc folder. I divided my system monitor into three classes, in the first class the general system information is realized, in the second class the process table, and the third class will combine this into one whole. Here is the first class frame, I called it Info:
#ifndef INFO_H #define INFO_H #include <QtGui> #include <iostream> #include <fstream> #include <pwd.h> #include <unistd.h> #include <sys/sysinfo.h> #include <vector> using namespace std; class Info : public QWidget { Q_OBJECT public: explicit Info(QWidget *parent = 0); private: QLabel * hostname; QLabel * user; QLabel * uptime; QLabel * proc; QLabel * freq; QLabel * cpuload; QLabel * mem; QLabel * memload; QProgressBar * cpubar; QProgressBar * membar; QVBoxLayout * layout; QHBoxLayout * hlayout; vector<float> readCpuStats(); int getCpuLoad(double dt); public slots: void update(); }; #endif // INFO_H 

The header files pwd.h and unistd.h are used to get the username, and the header file sys / sysinfo.h is used to get uptime. Now I will describe the implementation of the Info class, it is located in the info.cpp file. At the very beginning of the info.cpp file we have to connect info.h (framework of the info class). The Info class constructor looks like this:
 Info::Info(QWidget *parent) : QWidget(parent) { hostname = new QLabel("Hostname: "); user = new QLabel(": "); uptime = new QLabel("Uptime: "); proc = new QLabel(": "); freq = new QLabel(":"); cpuload = new QLabel(" : "); mem = new QLabel(" : "); memload = new QLabel("  : "); cpubar = new QProgressBar; membar = new QProgressBar; layout = new QVBoxLayout; hlayout = new QHBoxLayout; cpubar->setMaximumHeight(21); membar->setMaximumHeight(21); hlayout->addWidget(cpuload); hlayout->addWidget(cpubar); layout->addWidget(hostname); layout->addWidget(user); layout->addWidget(uptime); layout->addWidget(proc); layout->addWidget(freq); layout->addLayout(hlayout); layout->addWidget(mem); layout->addWidget(memload); layout->addWidget(membar); setLayout(layout); update(); QTimer *timer = new QTimer; connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(2000); } 

at the very beginning we initialize all the graphic elements. By the next action we set up graphic elements in the layouts. After we call the update slot so that all labels and progress bars are filled with values. So that the information is regularly updated we create an object of the QTimer class, and connect its timeout signal with the update slot already familiar to us.
I think it would be logical to describe the following update slot, here is its code:
 void Info::update() { ifstream stream("/proc/sys/kernel/hostname"); string str; getline(stream,str); hostname->setText("Hostname: " + QString::fromStdString(str)); uid_t uid = geteuid(); passwd *pw = getpwuid(uid); user->setText(": " + QString::fromAscii(pw->pw_name)); struct sysinfo o; sysinfo(&o); long up = o.uptime; int hour = up/60/60; int min = (up - hour*60*60) / 60; int sec = ((up - hour*60*60) - min*60); QString e = QString::number(hour) + QString(" h. ") + QString::number(min) + QString(" m. ") + QString::number(sec) + QString(" s."); uptime->setText("Uptime: " + e); stream.close(); stream.open("/proc/cpuinfo"); for(int i = 0; i < 16;i++) stream >> str; getline(stream,str); proc->setText(": " + QString::fromStdString(str)); for(int i = 0; i< 7; i++) stream >> str; freq->setText(": " + QString::fromStdString(str) + " MHz"); cpubar->setValue(getCpuLoad(0.3)); stream.close(); stream.open("/proc/meminfo"); stream >> str; stream >> str; int num = atoi(str.c_str()); int percent = num / 100; int gb = (num / 1024) / 1024; int mb = (num-gb*1024*1024) /1024; int kb = (num - (gb*1024*1024+mb*1024)); if (gb > 0) e = QString::number(gb) + QString(" Gb "); else e = QString(""); if (mb > 0) e += QString::number(mb) + QString(" Mb "); if (kb > 0) e += QString::number(kb) + QString(" Kb "); mem->setText(" : " + e); int free = 0; for (int i = 0 ; i < 3 ; i++) { stream >> str; stream >> str; stream >> str; free += atoi(str.c_str()); } num -= free; gb = num / 1024 / 1024; mb = (num - gb*1024*1024) / 1024; kb = (num - ((mb*1024) + (gb * 1024 * 1024))); if (gb > 0) e = QString::number(gb) + QString(" Gb "); else e = QString(""); if (mb > 0) e += QString::number(mb) + QString(" Mb "); if (kb > 0) e += QString::number(kb) + QString(" Kb "); memload->setText("  : " + e); percent = num / percent; membar->setValue(percent); } 

First, we open for reading (this is why the program does not require superuser rights) file / proc / sys / kernel / hostname and get the name of the machine, after which we fill in the hostname label. after which we create an instance of the passwd and uid_t structure, with which we get the user name to fill in the user label. Information on how to find out the username was taken from the source of the whoami program. After that, we create and populate with an instance of the sysinfo structure, from which we take information about the uptime system. After that, we associate the already created stream with the file / proc / cpuinfo, in which we find information about the name of the processor model and its frequency. The processor load is obtained using the getCpuLoad method, which I will describe below. Now we associate the stream with the file / proc / meminfo from which we take the amount of RAM, and bring it to a conveniently readable form. Then from the same file we get the amount of free RAM, as well as the amount of cached data. To get the amount of occupied RAM, we subtract the free RAM and the amount of cached data from all RAM, and then again we bring it to a conveniently readable form. Now as I promised the above function description getCpuLoad. I did not write this function myself (I took it from here ), so I cannot give a very detailed description of it, I will describe it in general terms. Here she is:
 int Info::getCpuLoad(double dt) { vector<float> stats1 = readCpuStats(); QProcess::execute("sleep",QStringList() << QString::number(dt)); vector<float> stats2 = readCpuStats(); int size1 = stats1.size(); int size2 = stats2.size(); if (!size1 || !size2 || size1 != size2) return 2; for (int i = 0; i < size1; ++i) stats2[i] -= stats1[i]; int sum = 1; for (int i = 0; i < size1; ++i) sum += stats2[i]; int load = 100 - (stats2[size2 - 1] * 100 / sum); return load; } 

we get three values ​​two times using the readCpuStats function (I'll also describe it below), but we call this function with a certain amount of time. After the CPU is calculated based on these values. And now the description of the readCpuStats function:
 vector<float> Info::readCpuStats() { vector<float> ret; ifstream stat_file("/proc/stat"); if (!stat_file.is_open()) { cout << "Unable to open /proc/stat" << std::endl; return ret; } int val; string tmp; stat_file >> tmp; for (int i = 0; i < 4; ++i) { stat_file >> val; ret.push_back(val); } stat_file.close(); return ret; } 

This function was taken from the same site as the getCpuLoad function. In this function, we read the values ​​from the / proc / stat file, and put them into a vector, which we then return with the return statement.
Now the Info class is written, and you can proceed to writing the ProcessTable class. Here is the frame of this class:
 #ifndef PROCESSTABLE_H #define PROCESSTABLE_H #include <QtGui> #include <iostream> #include <fstream> using namespace std; class ProcessTable : public QWidget { Q_OBJECT public: explicit ProcessTable(QWidget *parent = 0); private: QTableWidget * table; QHBoxLayout* hlayout; QPushButton* button; QVBoxLayout* layout; public slots: void update(); void kill(); }; #endif // PROCESSTABLE_H 

The update slot is used to populate the table, and the kill slot is used to terminate processes. In the implementation file of this class, we must first include a file with its framework. Now I come to the description of the constructor of this class, here is the constructor code:
 ProcessTable::ProcessTable(QWidget *parent) : QWidget(parent) { hlayout = new QHBoxLayout; button = new QPushButton(""); button->setToolTip("       PID     \"\""); connect(button,SIGNAL(clicked()),this,SLOT(kill())); hlayout->addStretch(); hlayout->addWidget(button); layout = new QVBoxLayout; table = new QTableWidget; update(); layout->addWidget(table); layout->addLayout(hlayout); this->setLayout(layout); QTimer *timer = new QTimer; connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(4000); } 

First, we initialize the graphic elements, and companion them, as well as connect the clicked signal at the “Finish” button to the slot of our class - kill. We also call the update slot to populate the table, and implement the update of the table using a timer. Now the description of the slot update, here is its code:
 void ProcessTable::update() { table->setColumnCount(2); table->setRowCount(0); QStringList list; list << "Name" << "PID"; table->setHorizontalHeaderLabels(list); QDir * dir = new QDir("/proc"); list = dir->entryList(QStringList("*"),QDir::AllDirs); foreach(QString str, list) { if(str.toInt()) { ifstream stream; stream.open("/proc/" + str.toAscii() + "/comm"); string s; getline(stream,s); int lastRow = table->rowCount(); QString icon = "/usr/share/icons/hicolor/32x32/apps/" + QString::fromStdString(s) + ".png"; QFile file(icon); table->insertRow(lastRow); table->setColumnWidth(0,150); if(!file.exists()) { icon = ":/binary.png"; } table->setItem(lastRow,0,new QTableWidgetItem(QPixmap(icon),QString::fromStdString(s))); table->setItem(lastRow,1,new QTableWidgetItem(str)); } else { continue; } } } 

First we set the number of columns and rows with the table, as well as the names of the columns. After that we get a list of directories in the / proc folder. To fill the table, we cycle through the entire list, skipping directories that do not belong to processes. The process name we get from the file / proc / pid / comm. We take the icon from the / usr / share / icons / hicolor / 32x32 / apps folder, and if the icons are not there, then the binary.png image is used in the binary. Here is the picture:
image
In order to stitch it into the source, I used rcc, you can read about using rcc in the book by Max Schleet - “Qt - professional programming in C ++”, or you can google, I think that there are many manas on the Internet on using rcc.
Now go to the slot slot description, here is its code:
 void ProcessTable::kill() { QList<QTableWidgetItem*> list = table->selectedItems(); QTableWidgetItem* item = list.value(0); QString str = item->text(); QProcess::execute("kill", QStringList() << str); update(); } 

To complete the process, I use a program that is in all Linux (if you don’t have it in your Linux, I can’t even imagine what kind of Linux it is), I use the execute method of the QProcess class to run this program, the second argument of this function is the parameter list for the program, I transfer only the PID of the process that needs to be completed, after calling this program, I update the table so that the complete process disappears from it. Now I turn to the description of the SystemMonitor class, in which I combine these two classes. The frame of this class is quite simple:
 #ifndef SYSTEMMONITOR_H #define SYSTEMMONITOR_H #include <QtGui> #include "info.h" #include "processtable.h" class SystemMonitor : public QWidget { Q_OBJECT public: explicit SystemMonitor(QWidget *parent = 0); }; #endif // SYSTEMMONITOR_H 

Well, there is only one method in this class, so I will describe it now. Here is the code of this constructor method (add a skeleton file connection to the top of the implementation file):
 SystemMonitor::SystemMonitor(QWidget *parent) : QWidget(parent) { QTabWidget * tab = new QTabWidget; Info* info = new Info; ProcessTable* pt = new ProcessTable; tab->addTab(info,"  "); tab->addTab(pt,""); QVBoxLayout * layout = new QVBoxLayout; layout->addWidget(tab); this->setLayout(layout); this->show(); } 

In the constructor, we simply create a tab widget, and add our information widget there, and a table widget, since the addTab method accepts the widget as an agrument, then we inherited the Info and ProcessTable classes from QWidget for this. And now the final touch, the creation of the starting point of the program, that is, the file main.cpp. Here is his code:
 #include "systemmonitor.h" int main(int argc,char** argv) { QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); QApplication app(argc,argv); QStyle * style = QStyleFactory::create("Cleanlooks"); app.setStyle(style); SystemMonitor sys; return app.exec(); } 

The first three lines make the Russian letters in the program correctly displayed. Lines 8 and 9 ensure that in any system our application will look the same.
Well, that's all, the system monitor is ready, Bye everyone!

')

Source: https://habr.com/ru/post/186944/


All Articles