From 5865995d0fb2790d19fb8daf34ea0d36b8ac447b Mon Sep 17 00:00:00 2001 From: Matus Telgarsky Date: Tue, 27 Jan 2015 13:42:04 -0500 Subject: [PATCH 1/6] simplify util::make_graph rather than appending the upper limit, use it as the maximum. In the process fixes a display bug when extent == 0, and simplifies the addition of other drawing styles (which need not also work around this values logic). --- i3pystatus/core/util.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index 098644c..1f7410f 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -375,19 +375,14 @@ def make_graph(values, upper_limit=100.0): """ values = [float(n) for n in values] - # Add the upper limit to the end of the array so the graph doesn't distort - # as high values drop off the end. - values.append(float(upper_limit)) - bar = u'_▁▂▃▄▅▆▇█' bar_count = len(bar) - 1 - mn, mx = min(values), max(values) + mn, mx = min(values), float(upper_limit) extent = mx - mn if extent == 0: graph = '_' * len(values) else: - graph = ''.join(bar[int((n - mn) / extent * bar_count)] - for n in values[:len(values) - 1]) # Don't show the upper limit value. + graph = ''.join(bar[int((n - mn) / extent * bar_count)] for n in values) return graph From 49d2f3bb0a99bac9992baf68d84f832208236ac4 Mon Sep 17 00:00:00 2001 From: Matus Telgarsky Date: Tue, 27 Jan 2015 15:10:26 -0500 Subject: [PATCH 2/6] braille graph option --- i3pystatus/core/util.py | 32 ++++++++++++++++++++++++-------- i3pystatus/cpu_usage_graph.py | 7 +++++-- i3pystatus/network.py | 4 +++- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index 1f7410f..8c259ab 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -365,24 +365,40 @@ def internet(): return False -def make_graph(values, upper_limit=100.0): +def make_graph(values, upper_limit=100.0, style="blocks"): """ Draws a graph made of unicode characters. :param values: An array of values to graph. :param upper_limit: Maximum value for the y axis. + :param style: Drawing style (currently 'blocks' or 'braille'). :returns: Bar as a string """ values = [float(n) for n in values] - - bar = u'_▁▂▃▄▅▆▇█' - bar_count = len(bar) - 1 - mn, mx = min(values), float(upper_limit) + mn, mx = min(values), max(max(values), float(upper_limit)) extent = mx - mn - if extent == 0: - graph = '_' * len(values) + if style == 'blocks': + bar = u'_▁▂▃▄▅▆▇█' + bar_count = len(bar) - 1 + if extent == 0: + graph = '_' * len(values) + else: + graph = ''.join(bar[int((n - mn) / extent * bar_count)] for n in values) + elif style == 'braille': + #idea from https://github.com/asciimoo/drawille + #unicode values from http://en.wikipedia.org/wiki/Braille + v2 = values if len(values) % 2 == 0 else values + [mn] + l = len(v2) // 2 + if extent == 0: + graph = chr(0x2800) * l #should be visually equiv to ' ' + else: + graph = '' + for i in range(0, l, 2): + b1 = [ 0, 0x40, 0x44, 0x46, 0x47 ][ round(4 * (v2[i] - mn) / extent) ] + b2 = [ 0, 0x80, 0xa0, 0xb0, 0xb8 ][ round(4 * (v2[i + 1] - mn) / extent) ] + graph += chr(0x2800 + b1 + b2) else: - graph = ''.join(bar[int((n - mn) / extent * bar_count)] for n in values) + raise NotImplementedError("Graph drawing style '%s' unimplemented." % style) return graph diff --git a/i3pystatus/cpu_usage_graph.py b/i3pystatus/cpu_usage_graph.py index 1fd2b89..d8a99e5 100644 --- a/i3pystatus/cpu_usage_graph.py +++ b/i3pystatus/cpu_usage_graph.py @@ -24,10 +24,13 @@ class CpuUsageGraph(CpuUsage, ColorRangeModule): ("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'") + ("end_color", "Hex or English name for end of color range, eg '#FF0000' or 'red'"), + ("graph_width", "Width of the cpu usage graph"), + ("graph_style", "Graph style, currently 'blocks' or 'braille'"), ) graph_width = 15 + graph_style = 'blocks' format = '{cpu_graph}' cpu = 'usage_cpu' @@ -43,7 +46,7 @@ class CpuUsageGraph(CpuUsage, ColorRangeModule): self.cpu_readings.insert(0, core_reading) self.cpu_readings = self.cpu_readings[:self.graph_width] - graph = make_graph(self.cpu_readings, 100.0) + graph = make_graph(self.cpu_readings, 100.0, self.graph_style) format_options.update({'cpu_graph': graph}) color = self.get_gradient(core_reading, self.colors) diff --git a/i3pystatus/network.py b/i3pystatus/network.py index 9d021e9..85103fd 100644 --- a/i3pystatus/network.py +++ b/i3pystatus/network.py @@ -254,6 +254,7 @@ class Network(IntervalModule, ColorRangeModule): ("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", "Width of the network traffic graph"), + ("graph_style", "Graph style, currently 'blocks' or 'braille'"), ("upper_limit", "Expected max kb/s. This value controls how the network traffic graph is drawn and in what color"), ("graph_type", "Whether to draw the network traffic graph for input or output. " @@ -276,6 +277,7 @@ class Network(IntervalModule, ColorRangeModule): dynamic_color = True graph_type = 'input' graph_width = 15 + graph_style = 'blocks' upper_limit = 150.0 # Network traffic settings @@ -329,7 +331,7 @@ class Network(IntervalModule, ColorRangeModule): # 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] - return make_graph(self.kbs_arr, self.upper_limit) + return make_graph(self.kbs_arr, self.upper_limit, self.graph_style) def run(self): format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="", From 117a2acfff78574adc867a14aafe0d3ef1e4ad38 Mon Sep 17 00:00:00 2001 From: Matus Telgarsky Date: Tue, 27 Jan 2015 15:34:05 -0500 Subject: [PATCH 3/6] braille graphs: pep8 fixes --- i3pystatus/core/util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index 8c259ab..0b0a876 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -385,17 +385,17 @@ def make_graph(values, upper_limit=100.0, style="blocks"): else: graph = ''.join(bar[int((n - mn) / extent * bar_count)] for n in values) elif style == 'braille': - #idea from https://github.com/asciimoo/drawille - #unicode values from http://en.wikipedia.org/wiki/Braille + # idea from https://github.com/asciimoo/drawille + # unicode values from http://en.wikipedia.org/wiki/Braille v2 = values if len(values) % 2 == 0 else values + [mn] l = len(v2) // 2 if extent == 0: - graph = chr(0x2800) * l #should be visually equiv to ' ' + graph = chr(0x2800) * l # should be visually equiv to ' ' else: graph = '' for i in range(0, l, 2): - b1 = [ 0, 0x40, 0x44, 0x46, 0x47 ][ round(4 * (v2[i] - mn) / extent) ] - b2 = [ 0, 0x80, 0xa0, 0xb0, 0xb8 ][ round(4 * (v2[i + 1] - mn) / extent) ] + b1 = [0, 0x40, 0x44, 0x46, 0x47][round(4 * (v2[i] - mn) / extent)] + b2 = [0, 0x80, 0xa0, 0xb0, 0xb8][round(4 * (v2[i + 1] - mn) / extent)] graph += chr(0x2800 + b1 + b2) else: raise NotImplementedError("Graph drawing style '%s' unimplemented." % style) From 82db7a87a2db8772e2ddb60fd2146de5331fc030 Mon Sep 17 00:00:00 2001 From: Matus Telgarsky Date: Wed, 28 Jan 2015 16:54:42 -0500 Subject: [PATCH 4/6] more braille styles (ideas from drawille) added 'braille-peak' and 'braille-snake'. 'braille-peak' renders only the top point, and 'braille-snake' fills some in. I should have stressed earlier that I only got the braille drawing ideas after coming across drawille ( https://github.com/asciimoo/drawille ). --- i3pystatus/core/util.py | 43 ++++++++++++++++++++++++++--------- i3pystatus/cpu_usage_graph.py | 2 +- i3pystatus/network.py | 2 +- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index 0b0a876..d705b8d 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -371,7 +371,7 @@ def make_graph(values, upper_limit=100.0, style="blocks"): :param values: An array of values to graph. :param upper_limit: Maximum value for the y axis. - :param style: Drawing style (currently 'blocks' or 'braille'). + :param style: Drawing style ('blocks', 'braille-fill', 'braille-peak', or 'braille-snake'). :returns: Bar as a string """ values = [float(n) for n in values] @@ -384,19 +384,40 @@ def make_graph(values, upper_limit=100.0, style="blocks"): graph = '_' * len(values) else: graph = ''.join(bar[int((n - mn) / extent * bar_count)] for n in values) - elif style == 'braille': + elif style in ['braille-fill', 'braille-peak', 'braille-snake']: # idea from https://github.com/asciimoo/drawille # unicode values from http://en.wikipedia.org/wiki/Braille - v2 = values if len(values) % 2 == 0 else values + [mn] - l = len(v2) // 2 - if extent == 0: - graph = chr(0x2800) * l # should be visually equiv to ' ' + + vpad = values if len(values) % 2 == 0 else values + [mn] + vscale = [round(4 * (vp - mn) / extent) for vp in vpad] + l = len(vscale) // 2 + + # do the 2-character collapse separately for clarity + if 'fill' in style: + vbits = [[0, 0x40, 0x44, 0x46, 0x47][vs] for vs in vscale] + elif 'peak' in style: + vbits = [[0, 0x40, 0x04, 0x02, 0x01][vs] for vs in vscale] else: - graph = '' - for i in range(0, l, 2): - b1 = [0, 0x40, 0x44, 0x46, 0x47][round(4 * (v2[i] - mn) / extent)] - b2 = [0, 0x80, 0xa0, 0xb0, 0xb8][round(4 * (v2[i + 1] - mn) / extent)] - graph += chr(0x2800 + b1 + b2) + assert('snake' in style) + # there are a few choices for what to put last in vb2. + # arguable vscale[-1] from the _previous_ call is best. + vb2 = [vscale[0]] + vscale + [0] + vbits = [] + for i in range(1, l + 1): + c = 0 + for j in range(min(vb2[i - 1], vb2[i], vb2[i + 1]), vb2[i] + 1): + c |= [0, 0x40, 0x04, 0x02, 0x01][j] + vbits.append(c) + + # 2-character collapse + graph = '' + for i in range(0, l, 2): + b1 = vbits[i] + b2 = vbits[i + 1] + if b2 & 0x40: + b2 = b2 - 0x30 + b2 = b2 << 3 + graph += chr(0x2800 + b1 + b2) else: raise NotImplementedError("Graph drawing style '%s' unimplemented." % style) return graph diff --git a/i3pystatus/cpu_usage_graph.py b/i3pystatus/cpu_usage_graph.py index d8a99e5..e648db1 100644 --- a/i3pystatus/cpu_usage_graph.py +++ b/i3pystatus/cpu_usage_graph.py @@ -26,7 +26,7 @@ class CpuUsageGraph(CpuUsage, ColorRangeModule): ("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", "Width of the cpu usage graph"), - ("graph_style", "Graph style, currently 'blocks' or 'braille'"), + ("graph_style", "Graph style ('blocks', 'braille-fill', 'braille-peak', or 'braille-snake')"), ) graph_width = 15 diff --git a/i3pystatus/network.py b/i3pystatus/network.py index 85103fd..303838d 100644 --- a/i3pystatus/network.py +++ b/i3pystatus/network.py @@ -254,7 +254,7 @@ class Network(IntervalModule, ColorRangeModule): ("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", "Width of the network traffic graph"), - ("graph_style", "Graph style, currently 'blocks' or 'braille'"), + ("graph_style", "Graph style ('blocks', 'braille-fill', 'braille-peak', or 'braille-snake')"), ("upper_limit", "Expected max kb/s. This value controls how the network traffic graph is drawn and in what color"), ("graph_type", "Whether to draw the network traffic graph for input or output. " From ac2b49440122d8381c40bf939d48736cb9ac4561 Mon Sep 17 00:00:00 2001 From: Matus Telgarsky Date: Wed, 28 Jan 2015 19:39:17 -0500 Subject: [PATCH 5/6] util::make_graph lower_limit parameter None is now a choice for lower_limit and upper_limit as well, so that the graph can scale as before. For cpu_usage_graph and network, rather than exposing lower_limit in the interface, 0.0 is used as a default, since it is already used implicitly when choosing colors. --- i3pystatus/core/util.py | 11 ++++++++--- i3pystatus/cpu_usage_graph.py | 2 +- i3pystatus/network.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index d705b8d..614fd6d 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -365,18 +365,23 @@ def internet(): return False -def make_graph(values, upper_limit=100.0, style="blocks"): +def make_graph(values, lower_limit=0.0, upper_limit=100.0, style="blocks"): """ Draws a graph made of unicode characters. :param values: An array of values to graph. - :param upper_limit: Maximum value for the y axis. + :param lower_limit: Minimum value for the y axis (or None for dynamic). + :param upper_limit: Maximum value for the y axis (or None for dynamic). :param style: Drawing style ('blocks', 'braille-fill', 'braille-peak', or 'braille-snake'). :returns: Bar as a string """ + values = [float(n) for n in values] - mn, mx = min(values), max(max(values), float(upper_limit)) + mn, mx = min(values), max(values) + mn = mn if lower_limit == None else min(mn, float(lower_limit)) + mx = mx if upper_limit == None else max(mx, float(upper_limit)) extent = mx - mn + if style == 'blocks': bar = u'_▁▂▃▄▅▆▇█' bar_count = len(bar) - 1 diff --git a/i3pystatus/cpu_usage_graph.py b/i3pystatus/cpu_usage_graph.py index e648db1..4776348 100644 --- a/i3pystatus/cpu_usage_graph.py +++ b/i3pystatus/cpu_usage_graph.py @@ -46,7 +46,7 @@ class CpuUsageGraph(CpuUsage, ColorRangeModule): self.cpu_readings.insert(0, core_reading) self.cpu_readings = self.cpu_readings[:self.graph_width] - graph = make_graph(self.cpu_readings, 100.0, self.graph_style) + graph = make_graph(self.cpu_readings, 0.0, 100.0, self.graph_style) format_options.update({'cpu_graph': graph}) color = self.get_gradient(core_reading, self.colors) diff --git a/i3pystatus/network.py b/i3pystatus/network.py index 303838d..afb9ddb 100644 --- a/i3pystatus/network.py +++ b/i3pystatus/network.py @@ -331,7 +331,7 @@ class Network(IntervalModule, ColorRangeModule): # 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] - return make_graph(self.kbs_arr, self.upper_limit, self.graph_style) + return make_graph(self.kbs_arr, 0.0, self.upper_limit, self.graph_style) def run(self): format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="", From c71fecdf365a94c4b9e94f7b300732242f299234 Mon Sep 17 00:00:00 2001 From: Matus Telgarsky Date: Wed, 28 Jan 2015 19:55:32 -0500 Subject: [PATCH 6/6] util::make_graph pep8... (again, sorry) --- i3pystatus/core/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i3pystatus/core/util.py b/i3pystatus/core/util.py index 614fd6d..57e9467 100644 --- a/i3pystatus/core/util.py +++ b/i3pystatus/core/util.py @@ -378,8 +378,8 @@ def make_graph(values, lower_limit=0.0, upper_limit=100.0, style="blocks"): values = [float(n) for n in values] mn, mx = min(values), max(values) - mn = mn if lower_limit == None else min(mn, float(lower_limit)) - mx = mx if upper_limit == None else max(mx, float(upper_limit)) + mn = mn if lower_limit is None else min(mn, float(lower_limit)) + mx = mx if upper_limit is None else max(mx, float(upper_limit)) extent = mx - mn if style == 'blocks':