Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 63 additions & 7 deletions src/cui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,16 @@
#include <strings.h>
#include <sys/types.h>

#include <iostream>
#include <locale>
#include <sstream>
#include <iomanip>

#include "nethogs.h"
#include "process.h"
#include <ncurses.h>


std::string *caption;
static int cursOrig;
extern const char version[];
Expand Down Expand Up @@ -61,17 +67,23 @@ const int MIN_COLUMN_WIDTH_DEV = 5;
const int COLUMN_WIDTH_SENT = 11;
const int COLUMN_WIDTH_RECEIVED = 11;
const int COLUMN_WIDTH_UNIT = 6;
const int NUMBER_OF_DECIMALS_SENT = 1;
const int NUMBER_OF_DECIMALS_RECEIVED = 1;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think showing one decimal is better than showing three? Three seems to make more sense to me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, a few collected thoughts on decimals:
. I found nethogs a few days ago and really like it. But, the first thing that was not clear to me on the first run was "that dot is to separate thousands or decimals?". Maybe "B" stands for bit ! etc. etc. A 3 digits block makes (at least me) immediately think of thousand separator. The dot does not help, in central Europe this is a thousand separator.
. In general, it is of scarce interest to show even one decimal for numbers greater than, say 50. It is more noise than information since the error you introduce leaving out the decimal is very little. Still, it is noise that eats a lot of column space.
. Consider "top", there, where decimals are informative it is only 1 decimals, for magnitudes that are expected to be less than 100 most of the time.
. Column are very scarce resource, so if we keep long numbers (no automatic udm adjustment) the choice is between 2 decimals or 2 thousand separators. My preference is to read well the big numbers and forget about the tiny digits dance.


const char *COLUMN_FORMAT_PID = "%7d";
const char *COLUMN_FORMAT_SENT = "%11.3f";
const char *COLUMN_FORMAT_RECEIVED = "%11.3f";

const char *COLUMN_FORMAT_SENT = "%s";
const char *COLUMN_FORMAT_RECEIVED = "%s";


// All descriptions are padded to 6 characters in length with spaces
const char *const desc_view_mode[VIEWMODE_COUNT] = {
"KB/s ", "KB ", "B ", "MB ", "MB/s ", "GB/s "};

constexpr char FILE_SEPARATOR = '/';

std::string prettyFloat(double val, int decimals, int maxWidth);

class Line {
public:
Line(const char *name, const char *cmdline, double n_recv_value,
Expand Down Expand Up @@ -218,9 +230,12 @@ void Line::show(int row, unsigned int proglen, unsigned int devlen) {

mvaddstr(row, column_offset_dev, devicename);

mvprintw(row, column_offset_sent, COLUMN_FORMAT_SENT, sent_value);
mvprintw(row, column_offset_sent, COLUMN_FORMAT_SENT,
prettyFloat(sent_value, NUMBER_OF_DECIMALS_SENT, COLUMN_WIDTH_SENT ).c_str() );

mvprintw(row, column_offset_received, COLUMN_FORMAT_RECEIVED,
prettyFloat(recv_value, NUMBER_OF_DECIMALS_RECEIVED, COLUMN_WIDTH_RECEIVED).c_str() );

mvprintw(row, column_offset_received, COLUMN_FORMAT_RECEIVED, recv_value);
mvaddstr(row, column_offset_unit, desc_view_mode[viewMode]);
}

Expand Down Expand Up @@ -277,7 +292,7 @@ int GreatestFirst(const void *ma, const void *mb) {
return 1;
}

void init_ui() {
void init_ui() {
WINDOW *screen = initscr();
cursOrig = curs_set(0);
raw();
Expand Down Expand Up @@ -387,8 +402,12 @@ void show_ncurses(Line *lines[], int nproc) {
}
attron(A_REVERSE);
int totalrow = std::min(rows - 1, 3 + 1 + i);
mvprintw(totalrow, 0, " TOTAL %-*.*s %-*.*s %11.3f %11.3f ",
proglen, proglen, "", devlen, devlen, "", sent_global, recv_global);
mvprintw(totalrow, 0, " TOTAL %-*.*s %-*.*s %s %s ",
proglen, proglen, "", devlen, devlen, "",
prettyFloat(sent_global, NUMBER_OF_DECIMALS_SENT, COLUMN_WIDTH_SENT).c_str(),
prettyFloat(recv_global, NUMBER_OF_DECIMALS_RECEIVED, COLUMN_WIDTH_RECEIVED).c_str()
);
// recv_global);
mvprintw(3 + 1 + i, cols - COLUMN_WIDTH_UNIT, "%s", desc_view_mode[viewMode]);
attroff(A_REVERSE);
mvprintw(totalrow + 1, 0, "%s", "");
Expand Down Expand Up @@ -461,3 +480,40 @@ void do_refresh() {
if (refreshlimit != 0 && refreshcount >= refreshlimit)
quit_cb(0);
}


// . Returns a String with a nice representation of the floating point value 'val'
// . There will be 'decimals' number of digits after the decimal point
// . There will be thousand separators as "'", independent of the user locale
// . String will have width 'maxWidth', the value is aligned to the right, the padding
// values at the left are white spaces.
// . If the the resulting number-string is bigger in than 'maxWidth' a
// default string "ERR:tooBig" is returned, padded to maxWidth.
// . test with something like:
// std::cout << "Test di prettyFloat: " << prettyFloat(123456.123456, 1, 15) << "\n";
// . Adapted from here: https://stackoverflow.com/a/43482688/2129178
std::string prettyFloat(double val, int decimals, int maxWidth) {

struct separate_thousands : std::numpunct<char> {
char_type do_thousands_sep() const override { return ','; } // separate with commas
string_type do_grouping() const override { return "\3"; } // groups of 3 digit
};

std::stringstream ss1;
auto thousands = std::make_unique<separate_thousands>() ;
ss1.imbue( std::locale(std::cout.getloc(), thousands.release() ) );
ss1.setf(std::ios_base::fixed, std::ios_base::floatfield);
ss1.precision(decimals);
ss1 << std::setw(maxWidth);
ss1 << val;
std::string out = ss1.str();
if (out.length() > (long unsigned)maxWidth) {
ss1.str("");
ss1 << std::setw(maxWidth);
ss1 << "ERR:tooBig";
out = ss1.str();
}
return out;
};