From 3901aa43f1abe12e99d00f4d97777f28957ae545 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 13:17:02 +0800 Subject: [PATCH 1/9] Added method to generate a list of hex color values between a start color and end color. --- i3pystatus/core/util.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index c5bdcf4..f6e91cd 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -3,6 +3,7 @@ import functools import re import socket import string +from colour import Color def lchop(string, prefix): @@ -423,3 +424,29 @@ def user_open(url_or_command): import subprocess subprocess.Popen(url_or_command, shell=True) + +def get_hex_color_range(start_color, end_color, quantity): + """ + Generates a list of quantity Hex colors from start_color to end_color. + + :param start_color: Hex or plain English color for start of range + :param end_color: Hex or plain English color for end of range + :param quantity: Number of colours to return + :return: A list of Hex color values + """ + raw_colors = [c.hex for c in list(Color(start_color).range_to(Color(end_color), quantity))] + colors = [] + for color in raw_colors: + + # i3bar expects the full Hex value but for some colors the colour + # module only returns partial values. So we need to convert these colors to the full + # Hex value. + if len(color) == 4: + fixed_color = "#" + for c in color[1:]: + fixed_color += c * 2 + colors.append(fixed_color) + else: + colors.append(color) + return colors + From a0d528f1d85791b22c0cab2d4c01e2026562c39e Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 14:25:14 +0800 Subject: [PATCH 2/9] Added methods for retrieving network information to make the class easier to extend. --- i3pystatus/network_traffic.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/i3pystatus/network_traffic.py b/i3pystatus/network_traffic.py index b4d2107..3000db8 100644 --- a/i3pystatus/network_traffic.py +++ b/i3pystatus/network_traffic.py @@ -31,15 +31,32 @@ class NetworkTraffic(IntervalModule): round_size = None pnic = None - def run(self): - pnic_before = self.pnic + pnic_before = None + + def update_counters(self): + self.pnic_before = self.pnic self.pnic = psutil.net_io_counters(pernic=True)[self.interface] - if not pnic_before: return + + def get_bytes_sent(self): + return (self.pnic.bytes_sent - self.pnic_before.bytes_sent) / self.divisor + + def get_bytes_received(self): + return (self.pnic.bytes_recv - self.pnic_before.bytes_recv) / self.divisor + + def get_packets_sent(self): + return self.pnic.packets_sent - self.pnic_before.packets_sent + + def get_packets_received(self): + return self.pnic.packets_recv - self.pnic_before.packets_recv + + def run(self): + self.update_counters() + if not self.pnic_before: return cdict = { - "bytes_sent": (self.pnic.bytes_sent - pnic_before.bytes_sent) / self.divisor, - "bytes_recv": (self.pnic.bytes_recv - pnic_before.bytes_recv) / self.divisor, - "packets_sent": self.pnic.packets_sent - pnic_before.packets_sent, - "packets_recv": self.pnic.packets_recv - pnic_before.packets_recv, + "bytes_sent": self.get_bytes_sent(), + "bytes_recv": self.get_bytes_received(), + "packets_sent": self.get_packets_sent(), + "packets_recv": self.get_packets_received(), } round_dict(cdict, self.round_size) cdict["interface"] = self.interface From 856bc5cc24ded0bc3c1872e6156ae5a09debe9e0 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 13:18:26 +0800 Subject: [PATCH 3/9] Added module to draw a network graph in Unicode. --- i3pystatus/network_graph.py | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 i3pystatus/network_graph.py diff --git a/i3pystatus/network_graph.py b/i3pystatus/network_graph.py new file mode 100644 index 0000000..e3caa32 --- /dev/null +++ b/i3pystatus/network_graph.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +from i3pystatus.network_traffic import NetworkTraffic +from i3pystatus.core.util import make_graph, get_hex_color_range + + +class NetworkGraph(NetworkTraffic): + """ + Shows Network activity as a Unicode graph + + Linux only + + Available formatters: + + {kbs} Float representing kb\s + {network_graph} Unicode network graph + + """ + settings = ( + ("format", "format string"), + ("graph_width", "Width of the graph"), + ("upper_limit", "Expected max kb/s. This value controls how the graph is drawn and in what color"), + ("graph_type", "Whether to draw the graph for input or output. " + "Allowed values 'input' or 'output'"), + ("divisor", "divide all byte values by this value"), + ("interface", "Interface to watch, eg 'eth0'"), + ("start_color", "Hex or English name for start of color range, eg '#00FF00' or 'green'"), + ("end_color", "Hex or English name for end of color range, eg '#FF0000' or 'red'") + ) + + format = "{network_graph}{kbs}KB/s" + start_color = "#00FF00" + end_color = 'red' + graph_type = 'input' + + interval = 1 + graph_width = 15 + upper_limit = 150.0 + + def init(self): + self.colors = get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit)) + self.kbs_arr = [0.0] * self.graph_width + + def get_gradient(self, value): + """ + Map a value to a color + :param value: Some value + :return: A Hex color code + """ + index = int(self.percentage(value, self.upper_limit)) + if index >= len(self.colors): + return self.colors[-1] + elif index < 0: + return self.colors[0] + else: + return self.colors[index] + + @staticmethod + def percentage(part, whole): + """ + Calculate percentage + """ + if whole == 0: + return 0 + return 100 * float(part) / float(whole) + + def run(self): + self.update_counters() + if not self.pnic_before: + return + + if self.graph_type == 'input': + kbs = self.get_bytes_received() + elif self.graph_type == 'output': + kbs = self.get_bytes_sent() + else: + raise Exception("graph_type must be either 'input' or 'output'!") + + # Cycle array by inserting at the start and chopping off the last element + self.kbs_arr.insert(0, kbs) + self.kbs_arr = self.kbs_arr[:self.graph_width] + + color = self.get_gradient(kbs) + network_graph = make_graph(self.kbs_arr, self.upper_limit) + + self.output = { + "full_text": self.format.format( + network_graph=network_graph, + kbs="{0:.1f}".format(round(kbs, 2)).rjust(6) + ), + 'color': color, + } From 48821e34ca5b4094145b043f7fc285777b2dae17 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 14:52:08 +0800 Subject: [PATCH 4/9] Moved get_hex_color_range() from util.py to network_graph.py to prevent breaking the build. --- i3pystatus/core/util.py | 27 --------------------------- i3pystatus/network_graph.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index f6e91cd..c5bdcf4 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -3,7 +3,6 @@ import functools import re import socket import string -from colour import Color def lchop(string, prefix): @@ -424,29 +423,3 @@ def user_open(url_or_command): import subprocess subprocess.Popen(url_or_command, shell=True) - -def get_hex_color_range(start_color, end_color, quantity): - """ - Generates a list of quantity Hex colors from start_color to end_color. - - :param start_color: Hex or plain English color for start of range - :param end_color: Hex or plain English color for end of range - :param quantity: Number of colours to return - :return: A list of Hex color values - """ - raw_colors = [c.hex for c in list(Color(start_color).range_to(Color(end_color), quantity))] - colors = [] - for color in raw_colors: - - # i3bar expects the full Hex value but for some colors the colour - # module only returns partial values. So we need to convert these colors to the full - # Hex value. - if len(color) == 4: - fixed_color = "#" - for c in color[1:]: - fixed_color += c * 2 - colors.append(fixed_color) - else: - colors.append(color) - return colors - diff --git a/i3pystatus/network_graph.py b/i3pystatus/network_graph.py index e3caa32..101b3cd 100644 --- a/i3pystatus/network_graph.py +++ b/i3pystatus/network_graph.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- +from colour import Color from i3pystatus.network_traffic import NetworkTraffic -from i3pystatus.core.util import make_graph, get_hex_color_range +from i3pystatus.core.util import make_graph class NetworkGraph(NetworkTraffic): @@ -37,9 +38,35 @@ class NetworkGraph(NetworkTraffic): upper_limit = 150.0 def init(self): - self.colors = get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit)) + self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit)) self.kbs_arr = [0.0] * self.graph_width + @staticmethod + def get_hex_color_range(start_color, end_color, quantity): + """ + Generates a list of quantity Hex colors from start_color to end_color. + + :param start_color: Hex or plain English color for start of range + :param end_color: Hex or plain English color for end of range + :param quantity: Number of colours to return + :return: A list of Hex color values + """ + raw_colors = [c.hex for c in list(Color(start_color).range_to(Color(end_color), quantity))] + colors = [] + for color in raw_colors: + + # i3bar expects the full Hex value but for some colors the colour + # module only returns partial values. So we need to convert these colors to the full + # Hex value. + if len(color) == 4: + fixed_color = "#" + for c in color[1:]: + fixed_color += c * 2 + colors.append(fixed_color) + else: + colors.append(color) + return colors + def get_gradient(self, value): """ Map a value to a color From 20363b9da04e311687c67e95b6c6e6285a5ff919 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 16:02:04 +0800 Subject: [PATCH 5/9] Moved color code into it's own class that other classes can inherit from. --- i3pystatus/core/color.py | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 i3pystatus/core/color.py diff --git a/i3pystatus/core/color.py b/i3pystatus/core/color.py new file mode 100644 index 0000000..c0326e4 --- /dev/null +++ b/i3pystatus/core/color.py @@ -0,0 +1,56 @@ +from colour import Color + + +class ColorRangeModule(object): + + start_color = "#00FF00" + end_color = 'red' + + @staticmethod + def get_hex_color_range(start_color, end_color, quantity): + """ + Generates a list of quantity Hex colors from start_color to end_color. + + :param start_color: Hex or plain English color for start of range + :param end_color: Hex or plain English color for end of range + :param quantity: Number of colours to return + :return: A list of Hex color values + """ + raw_colors = [c.hex for c in list(Color(start_color).range_to(Color(end_color), quantity))] + colors = [] + for color in raw_colors: + + # i3bar expects the full Hex value but for some colors the colour + # module only returns partial values. So we need to convert these colors to the full + # Hex value. + if len(color) == 4: + fixed_color = "#" + for c in color[1:]: + fixed_color += c * 2 + colors.append(fixed_color) + else: + colors.append(color) + return colors + + def get_gradient(self, value, colors, upper_limit=100): + """ + Map a value to a color + :param value: Some value + :return: A Hex color code + """ + index = int(self.percentage(value, upper_limit)) + if index >= len(colors): + return colors[-1] + elif index < 0: + return colors[0] + else: + return colors[index] + + @staticmethod + def percentage(part, whole): + """ + Calculate percentage + """ + if whole == 0: + return 0 + return 100 * float(part) / float(whole) \ No newline at end of file From 2e7df52394419a986b5d0209a9caae3d854a6d74 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 16:02:33 +0800 Subject: [PATCH 6/9] Modified NetworkGraph to use new ColorRangeModule class. --- i3pystatus/network_graph.py | 57 ++----------------------------------- 1 file changed, 3 insertions(+), 54 deletions(-) diff --git a/i3pystatus/network_graph.py b/i3pystatus/network_graph.py index 101b3cd..1bc4747 100644 --- a/i3pystatus/network_graph.py +++ b/i3pystatus/network_graph.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -from colour import Color +from i3pystatus.core.color import ColorRangeModule from i3pystatus.network_traffic import NetworkTraffic from i3pystatus.core.util import make_graph -class NetworkGraph(NetworkTraffic): +class NetworkGraph(NetworkTraffic, ColorRangeModule): """ Shows Network activity as a Unicode graph @@ -29,8 +29,6 @@ class NetworkGraph(NetworkTraffic): ) format = "{network_graph}{kbs}KB/s" - start_color = "#00FF00" - end_color = 'red' graph_type = 'input' interval = 1 @@ -41,55 +39,6 @@ class NetworkGraph(NetworkTraffic): self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit)) self.kbs_arr = [0.0] * self.graph_width - @staticmethod - def get_hex_color_range(start_color, end_color, quantity): - """ - Generates a list of quantity Hex colors from start_color to end_color. - - :param start_color: Hex or plain English color for start of range - :param end_color: Hex or plain English color for end of range - :param quantity: Number of colours to return - :return: A list of Hex color values - """ - raw_colors = [c.hex for c in list(Color(start_color).range_to(Color(end_color), quantity))] - colors = [] - for color in raw_colors: - - # i3bar expects the full Hex value but for some colors the colour - # module only returns partial values. So we need to convert these colors to the full - # Hex value. - if len(color) == 4: - fixed_color = "#" - for c in color[1:]: - fixed_color += c * 2 - colors.append(fixed_color) - else: - colors.append(color) - return colors - - def get_gradient(self, value): - """ - Map a value to a color - :param value: Some value - :return: A Hex color code - """ - index = int(self.percentage(value, self.upper_limit)) - if index >= len(self.colors): - return self.colors[-1] - elif index < 0: - return self.colors[0] - else: - return self.colors[index] - - @staticmethod - def percentage(part, whole): - """ - Calculate percentage - """ - if whole == 0: - return 0 - return 100 * float(part) / float(whole) - def run(self): self.update_counters() if not self.pnic_before: @@ -106,7 +55,7 @@ class NetworkGraph(NetworkTraffic): self.kbs_arr.insert(0, kbs) self.kbs_arr = self.kbs_arr[:self.graph_width] - color = self.get_gradient(kbs) + color = self.get_gradient(kbs, self.colors, self.upper_limit) network_graph = make_graph(self.kbs_arr, self.upper_limit) self.output = { From aa349c8ba9d251163ea746e70fb367f1c034ed4d Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 16:03:13 +0800 Subject: [PATCH 7/9] Added module for graphing cpu usage. --- i3pystatus/cpu_usage_graph.py | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 i3pystatus/cpu_usage_graph.py diff --git a/i3pystatus/cpu_usage_graph.py b/i3pystatus/cpu_usage_graph.py new file mode 100644 index 0000000..818c111 --- /dev/null +++ b/i3pystatus/cpu_usage_graph.py @@ -0,0 +1,52 @@ +from i3pystatus.core.color import ColorRangeModule +from i3pystatus.cpu_usage import CpuUsage +from i3pystatus.core.util import make_graph + + +class CpuUsageGraph(CpuUsage, ColorRangeModule): + """ + Shows CPU usage as a Unicode graph. + The first output will be inacurate. + + Linux only + + Available formatters: + + * {cpu_graph} graph of cpu usage. + * {usage} usage average of all cores + * {usage_cpu*} usage of one specific core. replace "*" by core number starting at 0 + * {usage_all} usage of all cores separate. usess natsort when available(relevant for more than 10 cores) + """ + + settings = ( + ("cpu", "cpu to monitor, choices are 'usage_cpu' for all or 'usage_cpu*'. R" + "eplace '*' by core number starting at 0."), + ("start_color", "Hex or English name for start of color range, eg '#00FF00' or 'green'"), + ("end_color", "Hex or English name for end of color range, eg '#FF0000' or 'red'") + ) + + graph_width = 15 + format = '{cpu_graph}' + cpu = 'usage_cpu' + + def init(self): + super().init() + self.cpu_readings = self.graph_width * [0] + self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(100)) + + def run(self): + format_options = self.get_usage() + core_reading = format_options[self.cpu] + + self.cpu_readings.insert(0, core_reading) + self.cpu_readings = self.cpu_readings[:self.graph_width] + + graph = make_graph(self.cpu_readings, 100.0) + format_options.update({'cpu_graph': graph}) + + color = self.get_gradient(core_reading, self.colors) + self.output = { + "full_text": self.format.format_map(format_options), + 'color': color + } + From 31a6d91b255148e8f2f41b4cda562c27daf129a7 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 18:10:33 +0800 Subject: [PATCH 8/9] Added color module to i3pystatus.rst. --- docs/i3pystatus.core.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/i3pystatus.core.rst b/docs/i3pystatus.core.rst index 1c59c92..ad192e5 100644 --- a/docs/i3pystatus.core.rst +++ b/docs/i3pystatus.core.rst @@ -74,3 +74,11 @@ core Package :undoc-members: :show-inheritance: +:mod:`color` Module +------------------ + +.. automodule:: i3pystatus.core.color + :members: + :undoc-members: + :show-inheritance: + From d14e7ddadc2568180d2ecd39bbb6163d3d939a72 Mon Sep 17 00:00:00 2001 From: facetoe Date: Sat, 11 Oct 2014 18:54:12 +0800 Subject: [PATCH 9/9] Documented dependencies on the PyPI colour module. --- i3pystatus/core/color.py | 5 +++++ i3pystatus/cpu_usage_graph.py | 2 ++ i3pystatus/network_graph.py | 2 ++ 3 files changed, 9 insertions(+) diff --git a/i3pystatus/core/color.py b/i3pystatus/core/color.py index c0326e4..6f36c37 100644 --- a/i3pystatus/core/color.py +++ b/i3pystatus/core/color.py @@ -2,6 +2,11 @@ from colour import Color class ColorRangeModule(object): + """ + Class to dynamically generate and select colors. + + Requires the PyPI package `colour` + """ start_color = "#00FF00" end_color = 'red' diff --git a/i3pystatus/cpu_usage_graph.py b/i3pystatus/cpu_usage_graph.py index 818c111..3d5a907 100644 --- a/i3pystatus/cpu_usage_graph.py +++ b/i3pystatus/cpu_usage_graph.py @@ -8,6 +8,8 @@ class CpuUsageGraph(CpuUsage, ColorRangeModule): Shows CPU usage as a Unicode graph. The first output will be inacurate. + Depends on the PyPI colour module - https://pypi.python.org/pypi/colour/0.0.5 + Linux only Available formatters: diff --git a/i3pystatus/network_graph.py b/i3pystatus/network_graph.py index 1bc4747..a827763 100644 --- a/i3pystatus/network_graph.py +++ b/i3pystatus/network_graph.py @@ -10,6 +10,8 @@ class NetworkGraph(NetworkTraffic, ColorRangeModule): Linux only + Requires the PyPI packages `psutil` and `colour`. + Available formatters: {kbs} Float representing kb\s