satellite-gtk/satellite/widgets.py

90 lines
3.1 KiB
Python

# Copyright 2021-2022 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)