90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
# Copyright 2021-2023 Teemu Ikonen
|
|
# SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import importlib.resources as resources
|
|
|
|
import gi
|
|
gi.require_version('Gtk', '3.0')
|
|
gi.require_version('Gdk', '3.0')
|
|
from gi.repository import Gtk # noqa: E402
|
|
|
|
|
|
def text_barchart(data, highlights, height=None, width=30):
|
|
"""Return a string with a bar chart rendered as text.
|
|
|
|
data: Iterable of (label, value) tuples
|
|
highlights: List/set of labels for which the bar is highlighted
|
|
height Number of lines in the generated bar chart
|
|
width Width of the generated bar chart in chars
|
|
"""
|
|
sdata = [(d[0] if d[0] else '', int(d[1]) if d[1] else 0) for d in data]
|
|
sdata.sort(key=lambda x: x[1], reverse=True)
|
|
|
|
dstr = ''
|
|
axislines = 2 # x-axis needs this many lines
|
|
height = height if height is not None else len(sdata) + axislines
|
|
if (len(sdata) + axislines) < height:
|
|
barlines = len(sdata)
|
|
# # Add empty lines in the beginning
|
|
# dstr += '\n' * (height - len(sdata) - axislines)
|
|
else:
|
|
barlines = height - axislines - 1
|
|
|
|
xextra = 7 # Non-block chars in longest line
|
|
max_x = max(x[1] for x in sdata) if sdata else 0
|
|
xstep = 5 # xaxis grows by this step
|
|
max_xaxis = xstep * ((max_x + xstep) // xstep)
|
|
cmaxbar = width - xextra
|
|
scale = cmaxbar / max_x if max_x > 0 else 1.0
|
|
cmax_xaxis = cmaxbar + 3
|
|
for d in sdata[:barlines]:
|
|
block = '\u2585' if d[0] in highlights else '='
|
|
dstr += "%3s\u2502%s %d\n" % (d[0], block * int(scale * d[1]), d[1])
|
|
if barlines < len(sdata):
|
|
dstr += " \u256a\n"
|
|
elif (len(sdata) - axislines) < height:
|
|
# Add empty lines to y-axis
|
|
dstr += ' \u2502\n' * (height - len(sdata) - axislines)
|
|
dstr += " \u251c" + '\u2500' * (cmax_xaxis) + '\u2524\n'
|
|
dstr += " 0" + ' ' * (cmax_xaxis - 1) + str(max_xaxis)
|
|
return dstr
|
|
|
|
|
|
@Gtk.Template(string=resources.read_text('satellite', 'dataframe.ui'))
|
|
class DataFrame(Gtk.Bin):
|
|
__gtype_name__ = 'DataFrame'
|
|
header = Gtk.Template.Child()
|
|
grid = Gtk.Template.Child()
|
|
|
|
def __init__(self, rowtitles=[], values=[], **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.set_rowtitles(rowtitles)
|
|
if values:
|
|
self.set_values(values)
|
|
|
|
def empty(self):
|
|
self.grid.foreach(lambda x: self.grid.remove(x))
|
|
self.rows = 0
|
|
|
|
def set_rowtitles(self, rowtitles):
|
|
self.empty()
|
|
|
|
for num, title in enumerate(rowtitles):
|
|
tlabel = Gtk.Label(label=title)
|
|
tlabel.get_style_context().add_class("dim-label")
|
|
tlabel.set_halign(Gtk.Align.END)
|
|
vlabel = Gtk.Label(label="-")
|
|
vlabel.set_halign(Gtk.Align.START)
|
|
self.grid.attach(tlabel, 0, num, 1, 1)
|
|
self.grid.attach(vlabel, 1, num, 1, 1)
|
|
|
|
self.rows = len(rowtitles)
|
|
self.grid.show_all()
|
|
|
|
def set_values(self, values):
|
|
if len(values) != self.rows:
|
|
raise ValueError("Number of values does not match rows")
|
|
for num, val in enumerate(values):
|
|
label = self.grid.get_child_at(1, num)
|
|
label.set_text(val)
|