Created
July 13, 2021 22:56
-
-
Save brettviren/33dad4472a15b9d10f9eca4f095b53d2 to your computer and use it in GitHub Desktop.
reactive barpyrus config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import contextlib | |
import multiprocessing | |
import collections | |
import signal | |
from barpyrus import lemonbar, conky, widgets, hlwm, core | |
class Gruv: | |
# https://github.com/morhetz/gruvbox | |
BG0_H = '#1d2021' | |
BG0_S = '#32302f' | |
BG0 = '#282828' | |
BG1 = '#3c3836' | |
BG2 = '#504945' | |
BG3 = '#665c54' | |
BG4 = '#7c6f64' | |
FG0 = '#fbf1c7' | |
FG1 = '#ebdbb2' | |
FG2 = '#d5c4a1' | |
FG3 = '#bdae93' | |
FG4 = '#a89984' | |
FG = FG1 | |
BG = BG0_H | |
RED_DARK = '#cc241d' | |
GREEN_DARK = '#98971a' | |
YELLOW_DARK = '#d79921' | |
BLUE_DARK = '#458588' | |
PURPLE_DARK = '#b16286' | |
AQUA_DARK = '#689d6a' | |
GRAY_DARK = '#a89984' | |
ORANGE_DARK = '#d65d0e' | |
RED_LIGHT = '#fb4934' | |
GREEN_LIGHT = '#b8bb26' | |
YELLOW_LIGHT = '#fabd2f' | |
BLUE_LIGHT = '#83a598' | |
PURPLE_LIGHT = '#d3869b' | |
AQUA_LIGHT = '#8ec07c' | |
GRAY_LIGHT = '#928374' | |
ORANGE_LIGHT = '#fe8019' | |
ACCENT_COLOR = Gruv.ORANGE_DARK | |
@contextlib.contextmanager | |
def highlight_critical(cg, match, predicate='> 90'): | |
with cg.if_('match ${%s} %s' % (match, predicate)): | |
cg.fg(Gruv.RED_LIGHT) | |
yield | |
cg.fg(None) | |
def tag_renderer(taginfo, painter): | |
# if taginfo.empty: | |
# return | |
if taginfo.empty: | |
painter.fg(Gruv.PURPLE_DARK) | |
elif taginfo.urgent: | |
painter.fg(Gruv.RED_LIGHT) | |
else: | |
painter.fg(None) | |
if taginfo.here: | |
painter.bg(ACCENT_COLOR) | |
else: | |
painter.set_flag(painter.underline, taginfo.visible) | |
painter.ol(ACCENT_COLOR) | |
painter.space(3) | |
painter += taginfo.name | |
painter.space(3) | |
painter.bg() | |
painter.ol() | |
painter.set_flag(painter.underline, False) | |
painter.space(2) | |
def _cg_cpu_perc(cg, num): | |
with highlight_critical(cg, f'cpu cpu{num}'): | |
cg.var(f'cpu cpu{num}') | |
cg.text('% ') | |
def cg_cpu(cg): | |
# | |
symbol_chip = 0xe266 | |
with cg.temp_fg(ACCENT_COLOR): | |
cg.symbol(symbol_chip) | |
cg.space(5) | |
cpu_count = multiprocessing.cpu_count() | |
if cpu_count <= 4: | |
for cpu in range(cpu_count): | |
_cg_cpu_perc(cg, cpu + 1) | |
else: | |
# Too many numbers to digest, show total | |
_cg_cpu_perc(cg, 0) | |
def _cg_space_usage(cg, symbol, command): | |
with cg.temp_fg(ACCENT_COLOR): | |
cg.symbol(symbol) | |
cg.space(5) | |
with highlight_critical(cg, command): | |
cg.var(command) | |
cg.text('% ') | |
def cg_space(cg): | |
# | |
symbol_disk = 0xfaed | |
# | |
symbol_memory = 0xf85a | |
_cg_space_usage(cg, symbol_memory, 'memperc') | |
_cg_space_usage(cg, symbol_disk, 'fs_used_perc /') | |
# with cg.if_('mounted /mnt/sd'): | |
# _cg_space_usage(cg, 0xe1eb, 'fs_size /mnt/sd') | |
def cg_fan(cg): | |
with cg.if_('match ${ibm_fan} != 65535'): | |
with cg.temp_fg(ACCENT_COLOR): | |
# | |
cg.symbol(0xf70f) | |
cg.space(5) | |
with highlight_critical(cg, 'ibm_fan', '> 6000'): | |
cg.var('ibm_fan') | |
cg.text('rpm ') | |
def cg_temp(cg): | |
with cg.temp_fg(ACCENT_COLOR): | |
# | |
cg.symbol(0xe350) | |
cg.space(5) | |
cg.var('acpitemp') | |
cg.text('° ') | |
def cg_light(cg): | |
#'/sys/class/backlight/intel_backlight/actual_brightness' | |
#'/sys/class/backlight/intel_backlight/max_brightness' | |
with cg.temp_fg(ACCENT_COLOR): | |
# | |
cg.symbol(0xf835) | |
cg.space(5) | |
# fixme: two problems with "exec light": this exec is costlier | |
# than conky builtins and it returns a 2 digit decimal, eg 25.00 | |
# instead of 25 which is unecessarily precise. Okay, 3 problems: | |
# you need "light" installed. | |
cg.var('exec light') | |
cg.text(f'% ') | |
def cg_volume(cg): | |
cg.space(5) | |
# | |
symbol_volume = [0xf466, 0xf026, 0xf027, 0xf028] | |
levels_volume = [0, 33, 66, 100] | |
with cg.temp_fg(ACCENT_COLOR): | |
with cg.cases(): | |
for ind, icon in enumerate(symbol_volume): | |
cg.case('match $pa_sink_volume <= %d' % levels_volume[ind]) | |
cg.symbol(icon) | |
cg.else_() | |
cg.symbol(symbol_volume[-1]) # icon for > 100 percent | |
cg.var('pa_sink_volume') | |
def _cg_net_icon(cg, iface): | |
wifi_icons = [0xe217, 0xe218, 0xe219, 0xe21a] | |
wifi_delta = 100 / len(wifi_icons) | |
with cg.temp_fg(ACCENT_COLOR): | |
if iface == 'wlan': | |
with cg.cases(): | |
for i, icon in enumerate(wifi_icons[:-1]): | |
cg.case('match ${wireless_link_qual_perc wlan} < %d' % ((i+1)*wifi_delta)) | |
cg.symbol(icon) | |
cg.else_() | |
cg.symbol(wifi_icons[-1]) # icon for 100 percent | |
cg.space(5) | |
elif iface in ['eth', 'dock']: | |
cg.symbol(0xe0af) | |
elif iface in ['ppp0', 'bnep0']: | |
cg.symbol(0xe0f3) | |
else: | |
assert False | |
def cg_net(cg): | |
with cg.if_('up vpn0'): | |
with cg.temp_fg(Gruv.YELLOW_LIGHT): | |
cg.symbol(0xe0a6) | |
for iface in ['eth', 'dock', 'wlan', 'ppp0', 'bnep0']: | |
with cg.if_(f'up {iface}'), cg.if_('match "${addr %s}" != "No Address"' % iface): | |
_cg_net_icon(cg, iface) | |
if iface == 'wlan': | |
cg.var('wireless_essid wlan') | |
if iface not in ['ppp0', 'bnep0']: | |
cg.space(5) | |
cg.var(f'addr {iface}') | |
cg.space(5) | |
with cg.temp_fg(ACCENT_COLOR): | |
cg.symbol(0xe13c) | |
cg.var(f'downspeedf {iface}') | |
cg.text('K ') | |
cg.var(f'totaldown {iface}') | |
cg.space(5) | |
with cg.temp_fg(ACCENT_COLOR): | |
cg.symbol(0xe13b) | |
cg.var(f'upspeedf {iface}') | |
cg.text('K ') | |
cg.var(f'totalup {iface}') | |
cg.space(5) | |
def cg_battery(cg): | |
# first icon: 0 percent | |
# last icon: 100 percent | |
bat_icons = [ | |
# | |
0xf579,0xf57a,0xf57b,0xf57c,0xf57d,0xf57e,0xf57f,0xf580,0xf571,0xf578 | |
# 0xe242, 0xe243, 0xe244, 0xe245, 0xe246, | |
# 0xe247, 0xe248, 0xe249, 0xe24a, 0xe24b, | |
] | |
bat_delta = 100 / len(bat_icons) | |
with cg.if_('existing /sys/class/power_supply/BAT0'), cg.if_('match "$battery" != ""'): | |
cg.fg(ACCENT_COLOR) | |
with cg.if_('match "$battery" != "discharging $battery_percent%"'): | |
cg.symbol(0xe0db) | |
with cg.cases(): | |
for i, icon in enumerate(bat_icons[:-1]): | |
cg.case('match $battery_percent < %d' % ((i+1)*bat_delta)) | |
cg.symbol(icon) | |
cg.else_() | |
cg.symbol(bat_icons[-1]) # icon for 100 percent | |
cg.fg(None) | |
cg.space(5) | |
with highlight_critical(cg, 'battery_percent', '< 10'): | |
cg.var('battery_percent') | |
cg.text('% ') | |
cg.var('battery_time') | |
cg.space(5) | |
with cg.if_('existing /run/tlp/manual_mode'): | |
cg.fg(ACCENT_COLOR) | |
cg.symbol(0xe1d8) | |
cg.fg(None) | |
with cg.cases(): | |
cg.case('match ${cat /run/tlp/manual_mode} == 0') | |
cg.symbol(0xe0db) | |
cg.case('match ${cat /run/tlp/manual_mode} == 1') | |
cg.space(2) | |
cg.symbol(0xe03b) | |
cg.else_() | |
cg.text("unknown") | |
cg.fg(None) | |
cg.space(2) | |
def cg_time(cg): | |
with cg.temp_fg(ACCENT_COLOR): | |
# | |
cg.symbol(0xf43a) | |
cg.space(5) | |
cg.fg(ACCENT_COLOR) | |
cg.var('time %Y-%m-%d %a %H:%M') | |
cg.fg() | |
cg.space(5) | |
Geometry = collections.namedtuple('Geometry', ['x', 'y', 'width', 'height']) | |
def refresh_bar(*args): | |
bar, conky = refresh_bar.bar_conky | |
conky.proc.send_signal(signal.SIGUSR1) | |
painter = bar.painter() | |
painter.widget(bar.widget) | |
painter.flush() | |
refresh_bar.bar_conky = None | |
def main(): | |
hc = hlwm.connect() | |
# chain an "emit_hook refresh_panel" after eg command to run for | |
# volume change keybinding. | |
hc.enhook('refresh_panel', refresh_bar) | |
monitor = int(sys.argv[1]) if len(sys.argv) >= 2 else 0 | |
x, y, monitor_w, _monitor_h = hc.monitor_rect(monitor) | |
geom = Geometry(x, y, width=monitor_w, height=32) | |
hc(['pad', str(monitor), str(geom.height)]) | |
cg = conky.ConkyGenerator(lemonbar.textpainter()) | |
cg_volume(cg) | |
cg_light(cg) | |
cg_cpu(cg) | |
cg_space(cg) | |
cg_fan(cg) | |
cg_temp(cg) | |
cg_net(cg) | |
cg_battery(cg) | |
cg_time(cg) | |
conky_config = { | |
'update_interval': '10', | |
'update_interval_on_battery': '10', | |
} | |
# trayer_config = { | |
# 'tint': Gruv.BG.replace('#', '0x'), | |
# 'iconspacing': '5', | |
# 'padding': '5' | |
# } | |
bar = lemonbar.Lemonbar(geometry=geom, | |
cmd="/usr/local/bin/lemonbar", | |
font='DejaVu Sans', | |
symbol_font='FantasqueSansMono Nerd Font', | |
foreground=Gruv.FG, background=Gruv.BG) | |
title_theme = core.Theme(fg=Gruv.FG4, padding=(140, 0)) | |
left_widgets = [ | |
hlwm.HLWMTags(hc, monitor, tag_renderer=tag_renderer), | |
title_theme(hlwm.HLWMWindowTitle(hc)), | |
] | |
cw = conky.ConkyWidget(text=str(cg), config=conky_config) | |
right_widgets = [ cw ] | |
refresh_bar.bar_conky = bar, cw.conky | |
# if monitor == 0: | |
# right_widgets.append(trayer.TrayerWidget(args=trayer_config)) | |
bar.widget = widgets.ListLayout([ | |
widgets.RawLabel('%{l}'), | |
*left_widgets, | |
widgets.RawLabel('%{r}'), | |
*right_widgets, | |
]) | |
return bar | |
bar = main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This relies on herbstluftwm configured to do
emit_hook refresh_panel
whenever something may change that barpyrus might care about. For example the command can be added as part of achain
which, say changes volume or backlight brightness and which is bound to some key.The relevant bits to add "reactivity" are then in the
refresh_bar()
function which isenhook
'ed to therefresh_panel
event. The reaction relies on the Conky feature of resampling its configured values when it receives aSIGUSR1
(nominally meant for rereading its config, but the resampling happens as a side effect). Then, the rest of this function calls into the bits of barpyrus to flush to lemonbar.