curses_display.py 7.27 KB
Newer Older
Mario Hock's avatar
Mario Hock committed
1
2
3
4
5
6
7
8
9
10
11
# -*- coding:utf-8 -*-

'''
Curses display for »cpunetlog«.

This code is inspired and partially copied from »bwtop« written by
"Mahmoud Adel <mahmoud.adel2@gmail.com>" and distributed under the MIT license.
'''

import curses
import time
Mario Hock's avatar
Mario Hock committed
12
import helpers
Mario Hock's avatar
Mario Hock committed
13
14
15
16
17
18
19
20

## XXX disable colors
disablecolorskipped = True

stdscr = None

## some "constants"/preferences
nics = None
Mario Hock's avatar
Mario Hock committed
21
nic_speeds = None
Mario Hock's avatar
Mario Hock committed
22
divisor = 1000000.0
Mario Hock's avatar
Mario Hock committed
23
rounding_digits = 2
Mario Hock's avatar
Mario Hock committed
24
unit = "MBits"
Mario Hock's avatar
Mario Hock committed
25

Mario Hock's avatar
Mario Hock committed
26
## GUI, positions of fields
Mario Hock's avatar
Mario Hock committed
27
28
29
LABEL_CPU_UTIL = 18
LABEL_CPU_USER = 48
LABEL_CPU_SYSTEM = 64
Mario Hock's avatar
Mario Hock committed
30
31
32
LABEL_Sent = LABEL_CPU_UTIL
LABEL_Received = LABEL_CPU_USER

Mario Hock's avatar
..    
Mario Hock committed
33
## TODO ideas..
Mario Hock's avatar
Mario Hock committed
34
35
36
37
#   - Add an option to set a fixed max. net-speed manually (for comparison)
#       --> Maybe change colors, when speed higher than this max
#           (and switch to a scale with the real max. value for the nic.)
#
Mario Hock's avatar
..    
Mario Hock committed
38
#   - total (GB transferred)
Mario Hock's avatar
..    
Mario Hock committed
39
#   - other (CPU usage)  -- maybe compacter design for user/sys/other
Mario Hock's avatar
..    
Mario Hock committed
40
41
#   - "Total" field for CPU usage (as well)
#   - Also draw a bar for the totals?
Mario Hock's avatar
..    
Mario Hock committed
42
43
44

## TODO idea: smoothing..?

Mario Hock's avatar
Mario Hock committed
45

Mario Hock's avatar
Mario Hock committed
46

Mario Hock's avatar
Mario Hock committed
47
48
49
50
def _format_net_speed(speed):
    return str( round(speed / divisor, rounding_digits) )


Mario Hock's avatar
Mario Hock committed
51
52
53
54
def _display_cpu_bar(y, x, cpu):
    # Constants
    CPU_BAR_COLORS = ( curses.color_pair(3) | curses.A_REVERSE,    # user
                       curses.color_pair(4) | curses.A_REVERSE,    # system
55
                       curses.color_pair(5) | curses.A_REVERSE,    # softirq
Mario Hock's avatar
Mario Hock committed
56
                       curses.color_pair(6) | curses.A_REVERSE,    # other
Mario Hock's avatar
Mario Hock committed
57
58
59
60
                       curses.color_pair(3) )                      # idle

    # Calculate proportions
    cpu_util = 100-cpu.idle
61
62
    other = 100 - sum( (cpu.user, cpu.system, cpu.softirq, cpu.idle) )
    proportions = [cpu.user, cpu.system, cpu.softirq, other, cpu.idle]
Mario Hock's avatar
Mario Hock committed
63
64
65
66
67
68
69
70
71
72
73
74

    # Prepare text.
    text = '{0:.2%}'.format((cpu_util)/100.0)
    split_text = helpers.split_proprtionally(text, proportions, 20)

    # Write text on screen (curses).
    stdscr.move(y,x)
    for s, options in zip(split_text, CPU_BAR_COLORS):
        stdscr.addstr(s, options)



Mario Hock's avatar
Mario Hock committed
75
76
77

def init():
    global stdscr
Mario Hock's avatar
Mario Hock committed
78
79
80
81
    global nic_speeds

    if not nic_speeds:
        nic_speeds = helpers.get_nic_speeds()
Mario Hock's avatar
Mario Hock committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

    stdscr = curses.initscr()
    curses.noecho()
    curses.cbreak()
    stdscr.keypad(True)
    curses.curs_set(False)
    stdscr.nodelay(True)

    curses.start_color()
    curses.use_default_colors()
    if not disablecolorskipped:
        curses.init_pair(1, -1, -1)
        curses.init_pair(2, -1, -1)
        curses.init_pair(3, -1, -1)
        curses.init_pair(4, -1, -1)
    else:
Mario Hock's avatar
Mario Hock committed
98
99
100
101
102
103
        curses.init_pair(1, curses.COLOR_MAGENTA, -1)         # "CPUX", "ethX"
        curses.init_pair(2, curses.COLOR_BLUE, -1)            # "util", "sent", "received", ...
        curses.init_pair(3, curses.COLOR_GREEN, -1)           # <values>
        curses.init_pair(4, curses.COLOR_YELLOW, -1)          # "Total", (<cpu-system> ?)
        curses.init_pair(5, curses.COLOR_CYAN, -1)            # <cpu-system> / <cpu-other>
        curses.init_pair(6, curses.COLOR_WHITE, -1)           # <cpu-system> / <cpu-other>
Mario Hock's avatar
Mario Hock committed
104
105
106
107
108
109
110
111
112
113

    ## Show some output to avoid upsetting the user
    stdscr.addstr(3, 3, "loading ...", curses.A_BOLD)
    stdscr.refresh()



def display(measurement):
    global stdscr

Mario Hock's avatar
Mario Hock committed
114
    ## Press 'q' to quit.
Mario Hock's avatar
Mario Hock committed
115
116
117
118
    pressedkey = stdscr.getch()
    if pressedkey == ord('q'):
        return False

Mario Hock's avatar
Mario Hock committed
119
    ## Header
Mario Hock's avatar
Mario Hock committed
120
121
122
    stdscr.clear()
    stdscr.border(0)
    timenow = time.strftime("%H:%M:%S")
Mario Hock's avatar
Mario Hock committed
123
124
125
    stdscr.addstr(1, 1, 'CPUnetLOG', curses.A_BOLD)
    stdscr.addstr(1, LABEL_Sent, 'Time: {}'.format(timenow,), curses.A_BOLD)
    stdscr.addstr(1, LABEL_Received, 'Interval: {}s'.format(round(measurement.timespan, 1)), curses.A_BOLD)
Mario Hock's avatar
Mario Hock committed
126
127
    stdscr.refresh()

Mario Hock's avatar
Mario Hock committed
128
129
130
131
132
    y = 3

    ## CPU ##
    num=1
    for cpu in measurement.cpu_times_percent:
Mario Hock's avatar
Mario Hock committed
133
        # static labels
Mario Hock's avatar
Mario Hock committed
134
        stdscr.addstr(y, 1, 'CPU{0}'.format( num ), curses.color_pair(1))
Mario Hock's avatar
Mario Hock committed
135
        stdscr.addstr(y, LABEL_CPU_UTIL, 'util: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
136
        stdscr.addstr(y, LABEL_CPU_UTIL+26, '|', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
137
138
        stdscr.addstr(y, LABEL_CPU_USER, 'user: ', curses.color_pair(2))
        stdscr.addstr(y, LABEL_CPU_SYSTEM, 'system: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
139
140

        # CPU bar
Mario Hock's avatar
Mario Hock committed
141
        _display_cpu_bar( y, LABEL_CPU_UTIL+6, cpu )
Mario Hock's avatar
Mario Hock committed
142
143

        # user/system
Mario Hock's avatar
Mario Hock committed
144
145
        stdscr.addstr(y, LABEL_CPU_USER+6, '{0:.2%}'.format( cpu.user/100.0 ), curses.color_pair(3))
        stdscr.addstr(y, LABEL_CPU_SYSTEM+8, '{0:.2%}'.format( cpu.system/100.0 ), curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
146
147
148
149
150
151
152
153
154
155

        num += 1
        y += 1




    ## Network ##

    y += 1
Mario Hock's avatar
Mario Hock committed
156
    stdscr.hline(y, 1, "-", 78)
Mario Hock's avatar
Mario Hock committed
157
158
    y += 1

Mario Hock's avatar
Mario Hock committed
159
160
161
162
163
164
165
166
167
168
    # display all nics (if not set otherwise)
    if nics:
        active_nics = nics
    else:
        active_nics = measurement.net_io.keys()

    sum_sending = 0
    sum_receiving = 0

    ## display the values
Mario Hock's avatar
Mario Hock committed
169
    for nic in sorted(active_nics):
Mario Hock's avatar
Mario Hock committed
170
171
        values = measurement.net_io[nic]

Mario Hock's avatar
Mario Hock committed
172
173
        _send = values.ratio["bytes_sent"] * 8  # Bits/s
        _recv = values.ratio["bytes_recv"] * 8  # Bits/s
Mario Hock's avatar
Mario Hock committed
174
        sending = _format_net_speed( _send )
Mario Hock's avatar
Mario Hock committed
175
        send_ratio = _send/nic_speeds[nic]
Mario Hock's avatar
Mario Hock committed
176
        receiving = _format_net_speed( _recv )
Mario Hock's avatar
Mario Hock committed
177
        receive_ratio = _recv/nic_speeds[nic]
Mario Hock's avatar
Mario Hock committed
178
179
180

        sum_sending += _send
        sum_receiving += _recv
Mario Hock's avatar
Mario Hock committed
181
182

        stdscr.addstr(y, 1, '{0}'.format(nic), curses.color_pair(1))
Mario Hock's avatar
Mario Hock committed
183
        stdscr.addstr(y, LABEL_Sent, 'Sent: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
184
        stdscr.addstr(y, LABEL_Sent+26, "|", curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
185
        stdscr.addstr(y, LABEL_Received, 'Received: ', curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
186
        stdscr.addstr(y, LABEL_Received+30, "|", curses.color_pair(2))
Mario Hock's avatar
Mario Hock committed
187

Mario Hock's avatar
Mario Hock committed
188
        ## TODO rewrite in nice ^^ [see _display_cpu_bar()]
Mario Hock's avatar
Mario Hock committed
189
190
191
192
        ## XXX prototypical "inline"-coloring
        _snd_str = '{0} {1}/s'.format(sending, unit, send_ratio)
        _snd_str += " " * (20-len(_snd_str))
        _load_len = int(send_ratio * 20)
Mario Hock's avatar
Mario Hock committed
193
194
        stdscr.addstr(y, LABEL_Sent+6, _snd_str[0:_load_len], curses.color_pair(3)|curses.A_REVERSE)
        stdscr.addstr(y, LABEL_Sent+6+_load_len, _snd_str[_load_len:], curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
195
196
197
198

        _recv_str = '{0} {1}/s'.format(receiving, unit, send_ratio)
        _recv_str += " " * (20-len(_recv_str))
        _load_len = int(receive_ratio * 20)
Mario Hock's avatar
Mario Hock committed
199
200
        stdscr.addstr(y, LABEL_Received+10, _recv_str[0:_load_len], curses.color_pair(3)|curses.A_REVERSE)
        stdscr.addstr(y, LABEL_Received+10+_load_len, _recv_str[_load_len:], curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
201

Mario Hock's avatar
Mario Hock committed
202
        ## XXX TESTING
Mario Hock's avatar
Mario Hock committed
203
204
205
206
207
        #y += 1
        #stdscr.addstr(y, 25, "|" + " "*20 + "|", curses.color_pair(2))
        #stdscr.addstr(y, 26, " " * int(send_ratio * 20), curses.color_pair(3)|curses.A_REVERSE)
        #stdscr.addstr(y, 59, "|" + " "*20 + "|", curses.color_pair(2))
        #stdscr.addstr(y, 60, " " * int(receive_ratio * 20), curses.color_pair(3)|curses.A_REVERSE)
Mario Hock's avatar
Mario Hock committed
208

Mario Hock's avatar
Mario Hock committed
209
210
211
212
213
        y += 1

    ## Total
    y+=1
    stdscr.addstr(y, 1, 'Total:', curses.color_pair(4))
Mario Hock's avatar
Mario Hock committed
214
215
216
217
    stdscr.addstr(y, LABEL_Sent, 'Sent:', curses.color_pair(2))
    stdscr.addstr(y, LABEL_Sent+6, '{0} {1}/s'.format(_format_net_speed(sum_sending), unit), curses.color_pair(3))
    stdscr.addstr(y, LABEL_Received, 'Received:', curses.color_pair(2))
    stdscr.addstr(y, LABEL_Received+10, '{0} {1}/s'.format(_format_net_speed(sum_receiving),unit), curses.color_pair(3))
Mario Hock's avatar
Mario Hock committed
218
219
220
221
222
223
224
225
226
227
228
229
230
231

    stdscr.refresh()

    return True


def close():
    global stdscr

    curses.nocbreak()
    stdscr.keypad(False)
    curses.echo()
    curses.curs_set(True)
    curses.endwin()