#!/usr/bin/python3 import gi gi.require_version('Gdk', '3.0') gi.require_version('Gtk', '3.0') from gi.repository import GObject from gi.repository import GLib from gi.repository import Gdk from gi.repository import GdkPixbuf from gi.repository import Gtk from gi.repository import Pango import gc import os from subprocess import Popen, PIPE from stat import * import datetime import getopt import sys import traceback # for address import socket import fcntl import struct import time import signal import queue import textwrap # localization import gettext from functools import reduce try: gettext.install("net_monitor") except IOError: _ = str from net_monitor import Monitor ifaces = {} HISTOGRAM_SIZE=50 # borrowed from gnome-network-monitor 0.9.1 (gnetworkmonitor.sourceforge.net) class LoadGraph: """ This class is able do display a nicely formatted graph if interface bandwidth load. """ # we don't want to allow the window to shrink below this height padding = { "left" : 50, "right" : 10, "top" : 10, "bottom" : 10 } colors = ( "bg", "bg_outer", "in", "out" ) def __init__(self, widget, hist, size, min_height=70, axes_text=_("Bytes"), draw_both=True, max=None, draw_legend = True, legend1=_("Upload"), legend2=_("Download")): """ widget => GtkDrawingArea we paint into hist => a list of integers containing the hist of incoming traffic width, height => initial size of the GtkDrawingArea widget """ # set the minimum height of the widget widget.set_size_request(-1, min_height) # the object holding the history and its size self.__hist = hist self.__size = size # strings holding 0, middle and max values for displayed graph self.__str_min = "0" self.__str_mid = "" # gets computed later self.__str_max = "" # gets computer later # axes descriptions self.axes_text = axes_text self.draw_legend = draw_legend self.legend1 = legend1 self.legend2 = legend2 # are we drawing both values? self.draw_both = draw_both # minimum and maximum values self.min = min self.max = max # size of the GtkDrawingArea self.__rect = self.__inner = Gdk.Rectangle() self.maxval = 0 # maximum value in the history self.__mesh_x = self.__mesh_y = 0 # distance in pixels between items self.__get_max() self.__on_size(widget.get_allocation()) # save reference to the widget we paint into self.__widget = widget # lists holding bandwidth history mapped to actual coordinates self.__in = list() self.__out = list() self.__colors = dict() self.set_color(bg=(0, 0, 0), fg_in=(255, 0, 0), fg_out=(0, 255, 0)) self.__context = None def __set_context_color(self, con, col_tuple): """ Cleaner might be to extend the context class, but who cares? """ con.set_source_rgb(col_tuple[0], col_tuple[1], col_tuple[2]) def __draw(self): """ Strokes the rectangles and draws the curves """ if (self.__context == None): return # stroke the outer rectangle self.__context.rectangle(0, 0, self.__rect.width, self.__rect.height) self.__set_context_color(self.__context, self.__colors["bg"]) self.__context.fill_preserve() self.__context.stroke() # stroke the inner rectangle self.__context.rectangle(self.__inner.x, self.__inner.y, self.__inner.width, self.__inner.height) self.__set_context_color(self.__context, self.__colors["bg"]) self.__context.fill_preserve() self.__context.stroke() # stroke the quad around self.__context.move_to(self.__inner.x, self.__inner.y + self.__inner.height) self.__context.line_to(self.__inner.x, self.__inner.y) self.__context.line_to(self.__inner.x + self.__inner.width - self.__mesh_x, self.__inner.y) self.__context.line_to(self.__inner.x + self.__inner.width - self.__mesh_x, self.__inner.y + self.__inner.height) self.__set_context_color(self.__context, (255, 255, 255)) self.__context.stroke() # draw the actual bandwidth curves self.__draw_bw(self.__in, self.__colors["fg_in"]) if self.draw_both: self.__draw_bw(self.__out, self.__colors["fg_out"]) # draw legend if self.draw_legend: self.__draw_legend(self.__inner.y + 15, self.legend1, self.__colors["fg_in"]) if self.draw_both: self.__draw_legend(self.__inner.y + 25, self.legend2, self.__colors["fg_out"]) # draw minimum, middle and max numbers self.__draw_num(self.__inner.height + self.__inner.y, self.__str_min, (255, 255, 255)) self.__draw_num(self.__rect.height/2, self.__str_mid, (255, 255, 255)) self.__draw_num(self.__inner.y, self.__str_max, (255, 255, 255)) def __draw_num(self, ypos, num, color): """ The leftmost column is used to draw info about maximum, minimum and average bw """ self.__set_context_color(self.__context, color) self.__context.move_to(5, ypos) self.__context.show_text(num) self.__context.stroke() def __draw_legend(self, ypos, text, color): """ The rightmost column is used to draw graph legend """ text_length = self.__context.text_extents(text)[2] self.__context.move_to(self.__inner.x + self.__inner.width - text_length - 20, ypos) self.__context.show_text(text) self.__set_context_color(self.__context, color) self.__context.stroke() def __draw_bw(self, bw_list, color): """ Draws a curve from points stored in bw_list in color """ self.__context.move_to(self.__inner.x, self.__inner.y + self.__inner.height) self.__set_context_color(self.__context, color) x = self.__inner.x + self.__mesh_x for i in bw_list[1:]: self.__context.line_to(x, i) x += self.__mesh_x self.__context.stroke() def __convert_one_hist(self, hist): """ Maps values from one history object to real coordinates of the drawing area """ converted = list() if self.__mesh_y == 0: return [self.__inner.height + self.__inner.y] * len(hist) for item in hist: converted.append((self.__inner.height - int(item / self.__mesh_y)) + self.__inner.y) return converted def __convert_points(self): """ The bandwidth history object has the bandwidth stored as bytes. This method converts the bytes into actual coordiantes of the rectangle displayed """ # compute the aspect ratio self.__mesh_x = float(self.__inner.width) / float(self.__size) self.__mesh_y = float(self.maxval) / float(self.__inner.height) self.__in = self.__convert_one_hist(self.__hist["in"]) self.__out = self.__convert_one_hist(self.__hist["out"]) def __get_max(self): """ Finds the maximum value in both incoming and outgoing queue """ if self.max != None: self.maxval = self.max else: if self.__hist["in"]: maxin = max(self.__hist["in"]) else: maxin = 0 if self.__hist["out"]: maxout = max(self.__hist["out"]) else: maxout = 0 self.maxval = max(maxin, maxout) def __text_size(self): """ Computes the size of the text and thus the left border """ val = self.maxval if val == 0 and self.maxval != 0: val = self.maxval self.__str_max = "%d %s" % (val, self.axes_text) if val != val/2: self.__str_mid = "%d %s" % (val/2, self.axes_text) else: self.__str_mid = "" LoadGraph.padding["left"] = self.__context.text_extents(self.__str_max)[2] + 10 def __on_size(self, rect): """ rect => a rectangle holding the size of the widget """ self.__rect = rect self.__inner.x = LoadGraph.padding["left"] self.__inner.y = LoadGraph.padding["top"] self.__inner.width = rect.width - LoadGraph.padding["right"] - self.__inner.x self.__inner.height = rect.height - LoadGraph.padding["bottom"] - self.__inner.y self.__convert_points() def on_expose(self, widget, cr): """ A signal handler that is called every time we need to redraw the widget """ self.__context = cr self.__get_max() self.__text_size() self.__on_size(widget.get_allocation()) self.__draw() return False def set_history(self, hist): """ Called typically on change of interface displayed """ self.__hist = hist self.__convert_points() def update(self): """ Redraws the area """ alloc = self.__widget.get_allocation() self.__widget.queue_draw_area(0, 0, alloc.width, alloc.height) def change_colors(self, col_in = None, col_out = None): """ Sets the colors to draw the curves with """ if ( col_in ) : self.__colors["fg_in"] = col_in if ( col_out ) : self.__colors["fg_out"] = col_out def set_color(self, *args, **kwargs): """ Sets the colors of the graph """ for key, value in list(kwargs.items()): self.__colors[key] = value class MonitorGui: # icon pattern - icons are pulled from drakx-net ICON_PATTERN="/usr/share/libDrakX/pixmaps/%s-16.png" ICON_CONNECTED="/usr/share/libDrakX/pixmaps/connected.png" (COLUMN_PROTO, COLUMN_LOC_ADDR, COLUMN_LOC_PORT, COLUMN_REM_ADDR, COLUMN_REM_PORT, COLUMN_STATUS) = list(range(6)) def __init__(self, default_iface=None): self.window = Gtk.Window() self.window.set_title(_("Network monitor")) self.window.set_default_size(640, 440) self.window.connect('delete-event', lambda *w: Gtk.main_quit()) self.main_vbox = Gtk.VBox() self.window.add(self.main_vbox) # notebook self.notebook = Gtk.Notebook() self.main_vbox.pack_start(self.notebook, True, True, 0) #self.notebook.connect('switch-page', self.show_net_status) # monitor self.monitor = Monitor() self.ifaces = self.monitor.readnet() self.enabled_ifaces = [] self.wireless_ifaces = list(filter(self.monitor.has_wireless, list(self.ifaces.keys()))) # load uptime log self.monitor.load_uptime_log() sorted_ifaces = list(self.ifaces.keys()) sorted_ifaces.sort() net=self.monitor.readnet() select_page=0 for iface in sorted_ifaces: device_exists, data_in, data_out = self.monitor.get_traffic(iface,net) self.ifaces[iface] = {'data_in': 0, 'data_out': 0, 'total_in': 0, 'total_out': 0, 'widget_in': None, 'widget_out': None, 'widget_speed_in': None, 'widget_speed_out': None, 'graph': None, 'histogram': [], 'address': "", } iface_stat, iface_label = self.build_iface_stat(iface) cur_page = self.notebook.append_page(iface_stat, iface_label) if default_iface and iface == default_iface: select_page = cur_page if self.monitor.has_network_accounting(iface): self.enabled_ifaces.append(iface) # finally, we have tabs for network connections network_stat_label, network_stat, self.connections = self.build_network_stat() # refresh current connections self.refresh_connections(None) cur_page = self.notebook.append_page(network_stat, network_stat_label) # global statusbar self.statusbar = Gtk.Statusbar() self.context_id = self.statusbar.get_context_id("Statusbar") self.main_vbox.pack_start(self.statusbar, False, False, padding=1) # add initial message self.statusbar.push(self.context_id, _("Please wait..")) # configure timer self.signals = queue.Queue() GLib.timeout_add_seconds(1, self.update) self.window.show_all() # do we have to select a default interface? if select_page: self.notebook.set_current_page(select_page) def update_tray_info(self): """Updates tray information""" dns_servers = self.monitor.get_dns() if dns_servers: dns_message = ", ".join(dns_servers) else: dns_message = _("Not found") # routes routes, default_routes = self.monitor.get_routes() if default_routes: gw_message = ", ".join(["%s (%s)" % (gw, iface) for gw, iface in default_routes]) else: gw_message = _("Not found") tray_message = _("Default routes: %(gw_message)s; DNS: %(dns_message)s") % {"gw_message" : gw_message, "dns_message" : dns_message} self.statusbar.pop(self.context_id) self.statusbar.push(self.context_id, tray_message) def update(self, interval=1): """Updates traffic counters (interval is in seconds)""" # check for pending signals self.check_signals() # TODO: move it to Monitor()? net=self.monitor.readnet() wifi_stats = self.monitor.wireless_stats() self.update_tray_info() for iface in self.ifaces: status = self.monitor.get_status(iface) old_data_in = self.ifaces[iface]['data_in'] old_data_out = self.ifaces[iface]['data_out'] total_in = self.ifaces[iface]['total_in'] total_out = self.ifaces[iface]['total_out'] device_exists, data_in, data_out = self.monitor.get_traffic(iface, net) # is it a wireless interface? if iface in self.wireless_ifaces: essid = self.monitor.wifi_get_essid(iface) mode = self.monitor.wifi_get_mode(iface) bitrate = self.monitor.wifi_get_bitrate(iface) ap = self.monitor.wifi_get_ap(iface) link = wifi_stats.get(iface, 0) # calculate link quality if "max_quality" in self.ifaces[iface]: max_quality = self.ifaces[iface]["max_quality"] if max_quality != 0: quality = link * 100.0 / max_quality else: quality = 0 else: quality = 0 else: essid = None mode = None bitrate = None ap = None quality = 0 # is it the first measure? if old_data_in == 0 and old_data_out == 0: old_data_in = data_in old_data_out = data_out # check if device exists if not device_exists: old_data_in = data_in old_data_out = data_out # check total download diff_in = data_in - old_data_in diff_out = data_out - old_data_out # checking for 32bits overflow if diff_in < 0: diff_in += 2**32 if diff_out < 0: diff_out += 2**32 total_in += diff_in total_out += diff_out # speed speed_in = diff_in / interval speed_out = diff_out / interval # update saved values self.ifaces[iface]['data_in'] = data_in self.ifaces[iface]['data_out'] = data_out self.ifaces[iface]['total_in'] = total_in self.ifaces[iface]['total_out'] = total_out # updating graphs for histogram, graph, value_in, value_out in [ ('histogram', 'graph', speed_in, speed_out), ('link_histogram', 'link_graph', quality, quality) ]: if histogram not in self.ifaces[iface] or graph not in self.ifaces[iface]: # skip invalid graphs continue # calculating histogram hist_in = self.ifaces[iface][histogram]['in'] hist_out = self.ifaces[iface][histogram]['out'] if hist_in: histo_in = reduce(lambda x, y: x+y, hist_in) / len(hist_in) else: histo_in = 0 if hist_out: histo_out = reduce(lambda x, y: x+y, hist_out) / len(hist_in) else: histo_out = 0 hist_in.append(value_in) if len(hist_in) > HISTOGRAM_SIZE: del hist_in[0] hist_out.append(value_out) if len(hist_out) > HISTOGRAM_SIZE: del hist_out[0] graph = self.ifaces[iface][graph] graph.update() # calculate average network traffic hist_in = self.ifaces[iface]['histogram']['in'] hist_out = self.ifaces[iface]['histogram']['out'] if hist_out: histo_out = reduce(lambda x, y: x+y, hist_out) / len(hist_in) else: histo_out = 0 # get the uptime uptime = self.monitor.get_uptime(iface) # update widgets ip, mac = self.monitor.get_address(iface) for widget, value in [('widget_in', self.monitor.format_size(total_in)), ('widget_out', self.monitor.format_size(total_out)), ('widget_speed_in', self.monitor.format_size(speed_in, "/s")), ('widget_speed_out', self.monitor.format_size(speed_out, "/s")), ('widget_ip_address', ip), ('widget_status', status), ('widget_hw_address', mac), ('widget_essid', essid), ('widget_mode', mode), ('widget_bitrate', bitrate), ('widget_ap', ap), ('quality', "%d%%" % quality), ('widget_uptime', uptime), ]: if widget in self.ifaces[iface]: # is it absolute value or pretty-formatted number? if value.__class__ == tuple: pretty_size, pretty_bytes = value if pretty_size == pretty_bytes: value = pretty_size + "\n" else: value =( f"{pretty_size:<18}\n({pretty_bytes})") self.ifaces[iface][widget].set_text(str(value)) GLib.timeout_add_seconds(interval, self.update) def show_statistics_dialog(self, widget, iface): """Shows statistics dialog""" dialog = Gtk.Dialog(_("Network statistics for %s") % iface, self.window, 0, (Gtk.STOCK_OK, Gtk.ResponseType.OK) ) # statistics vbox stats_vbox = dialog.vbox if self.monitor.has_network_accounting(iface): # graph graph_vnstat = Gtk.Image() pixbuf = self.load_graph_from_vnstat(iface, type="summary") graph_vnstat.set_from_pixbuf(pixbuf) stats_vbox.pack_start(graph_vnstat, True, True, 0) # buttons frame = Gtk.Frame.new(_("Network traffic statistics for %s") % iface) stats_vbox.add(frame) vbox = Gtk.VBox() frame.add(vbox) # summary first_button = Gtk.RadioButton.new_with_label_from_widget(None, _("Summary")) first_button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "summary")) vbox.pack_start(first_button, False, False, 0) # summary button = Gtk.RadioButton.new_with_label_from_widget(None, _("Hourly traffic")) button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "hourly")) vbox.pack_start(button, False, False, 0) button.join_group(first_button) # summary button = Gtk.RadioButton.new_with_label_from_widget(None, _("Daily traffic")) button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "daily")) vbox.pack_start(button, False, False, 0) button.join_group(first_button) # summary button = Gtk.RadioButton.new_with_label_from_widget(None, _("Monthly traffic")) button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "monthly")) vbox.pack_start(button, False, False, 0) button.join_group(first_button) # summary button = Gtk.RadioButton.new_with_label_from_widget(None, _("Top 10 traffic days")) button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "top")) vbox.pack_start(button, False, False, 0) button.join_group(first_button) button.set_active(True) else: label = Gtk.Label(label=_("Network accounting was not enabled on interface %s.\nPlease enable network accounting on the interface in order to view traffic statistics and restart your network connection to start collecting traffic statistics.") % iface) stats_vbox.add(label) stats_vbox.show_all() ret = dialog.run() dialog.destroy() def refresh_connections(self, widget): """Updates connections""" lstore = self.connections lstore.clear() for proto in self.monitor.protocols_list(): connections = self.monitor.get_connections(proto=proto) for loc_addr, loc_port, rem_addr, rem_port, status in connections: iter = lstore.append() lstore.set(iter, self.COLUMN_PROTO, proto, self.COLUMN_LOC_ADDR, loc_addr, self.COLUMN_LOC_PORT, loc_port, self.COLUMN_REM_ADDR, rem_addr, self.COLUMN_REM_PORT, rem_port, self.COLUMN_STATUS, status) def build_network_stat(self): """Builds graphical view for connections""" vbox = Gtk.VBox() sw = Gtk.ScrolledWindow() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) lstore = Gtk.ListStore( GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_INT, GObject.TYPE_STRING, GObject.TYPE_INT, GObject.TYPE_STRING ) treeview = Gtk.TreeView(model=lstore) # deprecated # treeview.set_rules_hint(True) treeview.set_search_column(self.COLUMN_LOC_ADDR) # treeview.connect('row-activated', self.expand_domain, lstore) # now building columns for c, descr in [ (self.COLUMN_PROTO, _("Protocol")), (self.COLUMN_LOC_ADDR, _("Local address")), (self.COLUMN_LOC_PORT, _("Local port")), (self.COLUMN_REM_ADDR, _("Remote address")), (self.COLUMN_REM_PORT, _("Remote port")), (self.COLUMN_STATUS, _("Connection status")) ]: renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(descr, renderer, text=c) column.set_sort_column_id(c) column.set_resizable(True) column.set_expand(True) treeview.append_column(column) sw.add(treeview) # build tab label widget = Gtk.HBox() try: icon = Gtk.Image() pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.ICON_CONNECTED) icon.set_from_pixbuf(pixbuf) widget.pack_start(icon, True, True, 0) except: traceback.print_exc() widget.pack_start(Gtk.Label(label=_("connections")), True, True, 0) widget.show_all() vbox.pack_start(sw, True, True, 0) button = Gtk.Button(label=_("Refresh")) button.connect('clicked', self.refresh_connections) vbox.pack_start(button, False, False, 0) return widget, vbox, lstore def build_iface_stat(self, iface): """Builds graphical view for interface""" traf_vbox = Gtk.VBox(spacing=5) # graph draw = Gtk.DrawingArea() traf_vbox.pack_start(draw, True, True, 0) histogram = {"in": [], "out": []} graph = LoadGraph(draw, histogram, HISTOGRAM_SIZE) draw.connect('draw', graph.on_expose) self.ifaces[iface]['graph'] = graph self.ifaces[iface]['histogram'] = histogram # configuring callbacks sizegroup1 = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) sizegroup2 = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) sizegroup3 = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) sizegroup4 = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) # traffic frame = Gtk.Frame.new(_("Traffic statistics")) traf_vbox.pack_start(frame, False, False, 0) table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False) frame.add(table) cur_row = 0 total_in_h, total_in = self.build_value_pair(sizegroup1, _("Downloaded:") + "\n", value_sizegroup=sizegroup2) self.ifaces[iface]["widget_in"] = total_in total_out_h, total_out = self.build_value_pair(sizegroup3, _("Uploaded:") + "\n", value_sizegroup=sizegroup4) self.ifaces[iface]["widget_out"] = total_out # speed speed_in_h, speed_in = self.build_value_pair(sizegroup1, _("Download speed:") + "\n", value_sizegroup=sizegroup2) self.ifaces[iface]["widget_speed_in"] = speed_in speed_out_h, speed_out = self.build_value_pair(sizegroup3, _("Upload speed:") + "\n", value_sizegroup=sizegroup4) self.ifaces[iface]["widget_speed_out"] = speed_out # pack items into table for items in [ [total_in_h, total_out_h], [speed_in_h, speed_out_h], ]: self.__add_row(table, cur_row, items) cur_row += 1 frame_global = Gtk.Frame.new(_("Interface settings")) traf_vbox.pack_start(frame_global, False, False, 0) table = Gtk.Table(n_rows=2, n_columns=2, homogeneous=False) frame_global.add(table) cur_row = 0 # interface iface_h, iface_p = self.build_value_pair(sizegroup1, _("Network interface:"), iface, value_sizegroup=sizegroup2) iface_s, iface_status = self.build_value_pair(sizegroup1, _("Device status:"), value_sizegroup=sizegroup2) self.ifaces[iface]["widget_status"] = iface_status iface_addr_s, iface_addr = self.build_value_pair(sizegroup3, _("IP Address:"), value_sizegroup=sizegroup4) self.ifaces[iface]["widget_ip_address"] = iface_addr iface_mac_s, iface_mac = self.build_value_pair(sizegroup3, _("Hardware address:"), value_sizegroup=sizegroup4) self.ifaces[iface]["widget_hw_address"] = iface_mac # pack items into table for items in [ [iface_h, iface_addr_s], [iface_s, iface_mac_s], ]: self.__add_row(table, cur_row, items) cur_row += 1 # wireless statistics if iface in self.wireless_ifaces: # essid essid_h, essid = self.build_value_pair(sizegroup1, _("Wireless ESSID:"), value_sizegroup=sizegroup2) self.ifaces[iface]["widget_essid"] = essid # mode mode_h, mode = self.build_value_pair(sizegroup3, _("Wireless mode:"), value_sizegroup=sizegroup4) self.ifaces[iface]["widget_mode"] = mode # bitrate bitrate_h, bitrate = self.build_value_pair(sizegroup1, _("Connection speed:"), value_sizegroup=sizegroup2) self.ifaces[iface]["widget_bitrate"] = bitrate # AP ap_h, ap = self.build_value_pair(sizegroup3, _("Access point or cell:"), value_sizegroup=sizegroup4) self.ifaces[iface]["widget_ap"] = ap # caching quality values self.ifaces[iface]["max_quality"] = self.monitor.wifi_get_max_quality(iface) # link quality info quality_h, quality = self.build_value_pair(sizegroup1, _("Link quality:"), value_sizegroup=sizegroup2) self.ifaces[iface]["quality"] = quality # link quality graph draw = Gtk.DrawingArea() histogram = {"in": [], "out": []} graph = LoadGraph(draw, histogram, HISTOGRAM_SIZE, min_height=40, axes_text="%", draw_both=False, max=100, draw_legend=False) draw.connect('draw', graph.on_expose) self.ifaces[iface]['link_graph'] = graph self.ifaces[iface]['link_histogram'] = histogram # pack everything into table for items in [ [essid_h, mode_h], [bitrate_h, ap_h], [quality_h, draw], ]: self.__add_row(table, cur_row, items) cur_row += 1 # statistics button frame_accounting = Gtk.Frame.new(_("Traffic accounting")) vbox = Gtk.VBox() frame_accounting.add(vbox) if self.monitor.has_network_accounting(iface): iface_u, iface_uptime = self.build_value_pair(sizegroup1, _("Connection time:")) self.ifaces[iface]["widget_uptime"] = iface_uptime vbox.pack_start(iface_u, False, False, 0) button = Gtk.Button(label=_("Show detailed network statistics")) button.connect('clicked', self.show_statistics_dialog, iface) vbox.pack_start(button, False, False, 0) else: label = Gtk.Label(label="\n".join(textwrap.wrap(_("Network accounting is not enabled for this interface. Please enable it in Network center (press Configure button next to the interface item, then check \"Enable traffic accounting\" item) in order to view detailed traffic statistics")))) vbox.pack_start(label, False, False, 0) traf_vbox.pack_start(frame_accounting, False, False, 0) # building notebook label icons traf_label = Gtk.HBox(homogeneous=False, spacing=2) if iface in self.wireless_ifaces: # wifi self.__load_interface_icon(traf_label, 'wireless') elif iface[:3] == 'ppp': # modem or peer-to-peer connection self.__load_interface_icon(traf_label, 'potsmodem') elif iface[:3] == 'eth': # ethernet self.__load_interface_icon(traf_label, 'ethernet') elif iface[:3] == 'pan': # ethernet self.__load_interface_icon(traf_label, 'bluetooth') traf_label.pack_start(Gtk.Label(label=iface), True, True, 0) traf_label.show_all() return traf_vbox, traf_label def __load_interface_icon(self, widget, icon_title): """Loads interface icon""" try: icon = Gtk.Image() pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.ICON_PATTERN % icon_title) icon.set_from_pixbuf(pixbuf) widget.pack_start(icon, True, True, 0) except: traceback.print_exc() def __add_row(self, table, row, items, markup=False, wrap=False): cur_pos = 1 for item in items: table.attach(item, cur_pos - 1, cur_pos, row, row + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0) cur_pos += 1 def build_widget_pair(self, container, w1, w2): """Puts two widgets side-by-side""" # finding left and right vboxes from container vbox_left, vbox_right = container vbox_left.pack_start(w1, False, False, 0) vbox_right.pack_start(w2, False, False, 0) def build_value_pair(self, sizegroup, text, value_text=None, value_sizegroup=None): """Builds a value pair""" hbox = Gtk.HBox(spacing=10) name = Gtk.Label(label=text) name.set_property("xalign", 0.0) hbox.pack_start(name, False, False, 0) value = Gtk.Label(label=value_text) value.set_property("xalign", 0.0) hbox.pack_start(value, False, False, 0) if sizegroup: sizegroup.add_widget(name) if value_sizegroup: value_sizegroup.add_widget(value) return hbox, value def update_stat_iface(self, widget, data): """Updates graphic statistics""" iface, graph, type = data pixbuf = self.load_graph_from_vnstat(iface, type) graph.set_from_pixbuf(pixbuf) def load_graph_from_vnstat(self, iface, type="hourly"): """Loads graph from vnstat. Right now uses vnstati to do all the dirty job""" # load image from data if type == "hourly": param="-h" elif type == "monthly": param="-m" elif type == "daily": param="-d" elif type == "top": param="-t" elif type == "summary": param="-s" else: # show summary if parameter is unknown print("Unknown parameter %s, showing summary.." % type) param="-s" pr = Popen(("vnstati %s -o - -i %s" % (param, iface)).split(),stdout=PIPE) data, _ = pr.communicate() loader = GdkPixbuf.PixbufLoader() loader.write(data) loader.close() pixbuf = loader.get_pixbuf() return pixbuf def check_signals(self): """Checks for received signals""" if not self.signals.empty(): s = self.signals.get() if s == signal.SIGHUP: # reload network configuration self.monitor.load_uptime_log() def queue_update(self, s): """Queue update for network devices""" self.signals.put(s) # {{{ usage def usage(): """Prints help message""" print("""net_monitor: network monitoring tool. Arguments to net_monitor: -h, --help displays this helpful message. -i, --defaultintf start monitoring the specified interface """) # }}} if __name__ == "__main__": # default monitoring interface iface = None # parse command line try: opt, args = getopt.getopt(sys.argv[1:], 'hi:', ['help', 'defaultintf=']) except getopt.error: usage() sys.exit(1) for o in opt: if o[0] == '-h' or o[0] == '--help': usage() sys.exit(0) elif o[0] == '-i' or o[0] == '--defaultintf': iface = o[1] monitor = MonitorGui(default_iface=iface) signal.signal(signal.SIGHUP, lambda s, f: monitor.queue_update(s)) Gtk.main()