lemonbar: Move all lemonbar parsing to units.
Remove i3 workspaces dependency on lemonbar. Implement sorting and alignment of modules
This commit is contained in:
@@ -1,28 +1,107 @@
|
||||
import sys
|
||||
import bisect # Sorting of list
|
||||
from enum import Enum
|
||||
import i3_lemonbar_config as conf
|
||||
import i3_lemonbar_config as config
|
||||
import i3_lemonbar_common as common
|
||||
|
||||
displays = ''
|
||||
workspaces = ''
|
||||
win_title = ''
|
||||
time = ''
|
||||
response = ''
|
||||
sr = config.sep_right
|
||||
slr = config.sep_l_right
|
||||
sl = config.sep_left
|
||||
sll = config.sep_l_left
|
||||
|
||||
blank = ' '
|
||||
|
||||
sr = conf.sep_right
|
||||
slr = conf.sep_l_right
|
||||
sl = conf.sep_left
|
||||
sll = conf.sep_l_left
|
||||
|
||||
class LemonUnit:
|
||||
def __init__(self, name, action = None, alt_scheme=None, external=None):
|
||||
class IconTextUnit:
|
||||
def __init__(self, name, order, action = None, alt_scheme=None, external=None):
|
||||
self.name = name
|
||||
self.action = action
|
||||
self.order = order
|
||||
self.alt_scheme = alt_scheme # None means default
|
||||
self.external = external
|
||||
self.items = [] # List of tuples (icon, text)
|
||||
self.modes = [common.bar_mode.normal]
|
||||
|
||||
def format(self):
|
||||
close = block(click='') if self.action is not None else ''
|
||||
|
||||
b_color = self.alt_scheme.back_color
|
||||
i_color = self.alt_scheme.icon_color
|
||||
t_color = self.alt_scheme.text_color
|
||||
|
||||
# Start with a major separator. Keep previous background color, set foreground color
|
||||
# of separator to background color of this unit
|
||||
blocks = []
|
||||
blocks.append(block(fg=b_color, append=sl))
|
||||
blocks.append(' ') # TODO perhaps more nice solution than manual spacing
|
||||
if len(self.items) >= 1:
|
||||
(icon, text) = self.items[0]
|
||||
blocks.append(block(click=self.action, fg=i_color, bg=b_color, font='2'
|
||||
, append=' ' + icon))
|
||||
blocks.append(' ')
|
||||
blocks.append(block(fg=t_color, font='1', append=text))
|
||||
if len(self.items) >= 2:
|
||||
for item in self.items[1:]:
|
||||
(icon, text) = item
|
||||
# Append minor separator
|
||||
blocks.append(' ')
|
||||
blocks.append(sll)
|
||||
|
||||
blocks.append(block(fg=i_color, bg=b_color, font='2', append=' ' + icon))
|
||||
blocks.append(' ')
|
||||
blocks.append(block(fg=t_color, font='1', append=text))
|
||||
|
||||
if len(self.items) >= 1:
|
||||
blocks.append(close)
|
||||
return ''.join(blocks)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.order < other.order
|
||||
|
||||
class ButtonsUnit:
|
||||
def __init__(self, name, order, alt_scheme=None, external=None):
|
||||
self.name = name
|
||||
self.order = order
|
||||
self.alt_scheme = alt_scheme # None means default
|
||||
self.external = external
|
||||
self.items = [] # List of tuples (icon, text, action)
|
||||
self.modes = [common.bar_mode.normal]
|
||||
|
||||
def format(self):
|
||||
b_color = self.alt_scheme.back_color
|
||||
i_color = self.alt_scheme.icon_color
|
||||
t_color = self.alt_scheme.text_color
|
||||
|
||||
# Start with a major separator. Keep previous background color, set foreground color
|
||||
# of separator to background color of this unit
|
||||
blocks = []
|
||||
blocks.append(block(fg=b_color, append=sl))
|
||||
blocks.append(' ')
|
||||
for item in self.items:
|
||||
(icon, text, action) = item
|
||||
close = block(click='') if action is not None else ''
|
||||
|
||||
blocks.append(block(click=action, fg=i_color, bg=b_color, font='2'
|
||||
, append=' ' + icon))
|
||||
blocks.append(' ')
|
||||
blocks.append(block(fg=t_color, font='1', append=text))
|
||||
blocks.append(close)
|
||||
|
||||
return ''.join(blocks)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.order < other.order
|
||||
|
||||
class CustomUnit:
|
||||
def __init__(self, name, format_function, order, action = None, alt_scheme=None, external=None):
|
||||
self.name = name
|
||||
self.action = action
|
||||
self.order = order
|
||||
self.alt_scheme = alt_scheme # None means default
|
||||
self.external = external
|
||||
self.format = format_function
|
||||
self.items = [] # List of tuples (icon, text)
|
||||
self.modes = [common.bar_mode.normal]
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.order < other.order
|
||||
|
||||
class LemonParser:
|
||||
# Handle parsing of units
|
||||
@@ -33,58 +112,42 @@ class LemonParser:
|
||||
self.units = []
|
||||
|
||||
def register_unit(self, unit):
|
||||
self.units.append(unit)
|
||||
# Keep the list sorted
|
||||
bisect.insort_left(self.units, unit)
|
||||
|
||||
def remove_unit(self, unit):
|
||||
self.remove(unit)
|
||||
self.units.remove(unit)
|
||||
|
||||
def format(self):
|
||||
formatted = []
|
||||
formatted = ['%{l}'] # Start left-justified
|
||||
previously_left_justified = True
|
||||
# Iterate over all units. Apply alternating color schemes
|
||||
alt = 1
|
||||
for unit in self.units:
|
||||
# Show only units appropriate for the current mode
|
||||
if common.mode not in unit.modes:
|
||||
continue
|
||||
|
||||
# Negative order means left justified, positive means right justified
|
||||
if previously_left_justified and unit.order > 0:
|
||||
# Switch to right justified
|
||||
formatted.append(block(fg='-', bg='-')) # Reset so the color doesn't fill
|
||||
formatted.append('%{r}')
|
||||
previously_left_justified = False
|
||||
|
||||
# Iterate over all units. Keep count to apply alternating color schemes
|
||||
for count, unit in enumerate(self.units):
|
||||
if unit.alt_scheme is None:
|
||||
# Apply default color scheme, which is alternating
|
||||
color_scheme = COLOR_SCHEME.A1 if count % 2 else COLOR_SCHEME.A2
|
||||
unit.alt_scheme = COLOR_SCHEME.A1 if alt == 1 else COLOR_SCHEME.A2
|
||||
else:
|
||||
# Apply alternate color scheme
|
||||
color_scheme = unit.alt_scheme
|
||||
# Keep alternate color scheme
|
||||
# INA is a special case
|
||||
if color_scheme == COLOR_SCHEME.INA:
|
||||
color_scheme = COLOR_SCHEME.A1_INA if count % 2 else COLOR_SCHEME.A2_INA
|
||||
if unit.alt_scheme == COLOR_SCHEME.INA:
|
||||
unit.alt_scheme = COLOR_SCHEME.A1_INA if alt == 1 else COLOR_SCHEME.A2_INA
|
||||
|
||||
close = block(click='') if unit.action is not None else ''
|
||||
|
||||
b_color = color_scheme.back_color
|
||||
i_color = color_scheme.icon_color
|
||||
t_color = color_scheme.text_color
|
||||
|
||||
# Start with a major separator. Keep previous background color, set foreground color
|
||||
# of separator to background color of this unit
|
||||
blocks = []
|
||||
blocks.append(block(fg=b_color, append=sl))
|
||||
blocks.append(' ') # TODO perhaps more nice soluting than manual spacing
|
||||
if len(unit.items) >= 1:
|
||||
(icon, text) = unit.items[0]
|
||||
blocks.append(block(click=unit.action, fg=i_color, bg=b_color, font='2'
|
||||
, append=' ' + icon))
|
||||
blocks.append(' ')
|
||||
blocks.append(block(fg=t_color, font='1', append=text))
|
||||
if len(unit.items) >= 2:
|
||||
for item in unit.items[1:]:
|
||||
(icon, text) = item
|
||||
# Append minor separator
|
||||
blocks.append(' ')
|
||||
blocks.append(sll)
|
||||
|
||||
blocks.append(block(fg=i_color, bg=b_color, font='2', append=' ' + icon))
|
||||
blocks.append(' ')
|
||||
blocks.append(block(fg=t_color, font='1', append=text))
|
||||
|
||||
if len(unit.items) >= 1:
|
||||
blocks.append(close)
|
||||
formatted.append(''.join(blocks))
|
||||
formatted.append(unit.format())
|
||||
alt = alt + 1 if alt + 1 <= 2 else 1
|
||||
|
||||
formatted.append(block(fg='-', bg='-')) # Not sure if needed
|
||||
return ' '.join(formatted)
|
||||
|
||||
g_parser = LemonParser()
|
||||
@@ -105,150 +168,25 @@ def block(fg = None, bg = None, font = None, click = None, append=''):
|
||||
rtn.append('T'+font)
|
||||
return ''.join(['%{', ' '.join(rtn), '}', append])
|
||||
|
||||
reset = block(fg='-', bg='-')
|
||||
reset_power = block(fg='-', bg=conf.color_poweropts)
|
||||
|
||||
class COLOR_SCHEME(Enum):
|
||||
A1 = (conf.color_sec_b1, conf.color_fore, conf.color_icon)
|
||||
A2 = (conf.color_sec_b2, conf.color_fore, conf.color_icon)
|
||||
A1 = (config.color_sec_b1, config.color_fore, config.color_icon)
|
||||
A2 = (config.color_sec_b2, config.color_fore, config.color_icon)
|
||||
INA = (None, None, None) # This one is special, dynamically changed to A1_INA or A2_INA
|
||||
A1_INA = (conf.color_sec_b1, conf.color_disable, conf.color_disable)
|
||||
A2_INA = (conf.color_sec_b2, conf.color_disable, conf.color_disable)
|
||||
CPU_ALERT= (conf.color_cpu, conf.color_back, conf.color_back)
|
||||
NET_ALERT= (conf.color_net, conf.color_back, conf.color_back)
|
||||
SPECIAL = (conf.color_head, conf.color_back, conf.color_back)
|
||||
A1_INA = (config.color_sec_b1, config.color_disable, config.color_disable)
|
||||
A2_INA = (config.color_sec_b2, config.color_disable, config.color_disable)
|
||||
CPU_ALERT= (config.color_cpu, config.color_back, config.color_back)
|
||||
NET_ALERT= (config.color_net, config.color_back, config.color_back)
|
||||
SPECIAL = (config.color_head, config.color_back, config.color_back)
|
||||
def __init__(self, back_color, text_color, icon_color):
|
||||
self.back_color = back_color
|
||||
self.text_color = text_color
|
||||
self.icon_color = icon_color
|
||||
|
||||
def single_sect(icon='', text='', click=None, alt=COLOR_SCHEME.A1):
|
||||
# If there is an action we need to close the block
|
||||
close = block(click='') if click is not None else ''
|
||||
if icon != '':
|
||||
icon = ' ' + icon
|
||||
return ' '.join([block(fg=alt.back_color, append=sl)
|
||||
, block(click=click, fg=alt.icon_color, bg=alt.back_color
|
||||
, font='2', append=icon)
|
||||
, block(fg=alt.text_color, font='1', append=text)
|
||||
, close])
|
||||
|
||||
def double_sect(text1 = '', text2 = '', icon1 = '', icon2 = '', click = None
|
||||
, alt=COLOR_SCHEME.A1):
|
||||
# If text_color None then icon_color will apply
|
||||
close = block(click='') if click is not None else ''
|
||||
return ''.join([block(fg=alt.back_color, append=sl), ' '
|
||||
, block(click=click, fg=alt.icon_color, bg=alt.back_color, font='2')
|
||||
, ' ', icon1, block(fg=alt.text_color, font='1'), ' ', text1
|
||||
, block(fg=alt.icon_color), sll, block(font='2')
|
||||
, icon2, block(fg=alt.text_color, font='1'), ' ', text2, ' ', close])
|
||||
|
||||
def control_sect(sec_color=None, head='', buttons=[], actions=[]):
|
||||
rtn = head
|
||||
for button, action in zip(buttons, actions):
|
||||
# If there is an action we need to close the block
|
||||
close = block(click='') if action is not None else ''
|
||||
rtn += ' '.join([block(click=action), button, close])
|
||||
return rtn
|
||||
|
||||
# Constants
|
||||
power_opts = ''.join([block(fg=conf.color_fore, bg=conf.color_poweropts)
|
||||
, ' Abort (Esc) | System (l) lock, (e) logout, (s) suspend, (h) hibernate'
|
||||
, ', (r) reboot, (Shift+s) shutdown'])
|
||||
controls = ' '.join([block(fg=conf.color_head, bg=conf.color_sec_b2)
|
||||
, conf.sep_right, block(fg=conf.color_head, bg=conf.color_sec_b2, click='mode cycle')
|
||||
, conf.icon_prog
|
||||
, block(fg=conf.color_sec_b2, bg='-', click='')
|
||||
, control_sect(head='', buttons=['on', 'off']
|
||||
, actions=['bluetooth power on', 'bluetooth power off'])
|
||||
, control_sect(head='PXC 550', buttons=['conn.', 'disc.']
|
||||
, actions=['bluetooth connect pxc550', 'bluetooth disconnect pxc550'])
|
||||
])
|
||||
def update_response(data):
|
||||
global response
|
||||
|
||||
resp = ' '.join(data)
|
||||
#common.logger.debug('Got response {}'.format(resp))
|
||||
response= single_sect(text=resp)
|
||||
|
||||
def update_displays(data):
|
||||
global displays
|
||||
|
||||
dsp_array = data[0].split(':')
|
||||
parsed_list = [block(click='displays', font='2')]
|
||||
for dsp in dsp_array[1:]:
|
||||
if dsp == 'eDP1':
|
||||
col_head = conf.color_head
|
||||
elif dsp == 'DP1':
|
||||
col_head = conf.color_vga
|
||||
elif dsp == 'HDMI2':
|
||||
col_head = conf.color_hdmi
|
||||
else:
|
||||
col_head = '#00000000' # Undefined
|
||||
parsed_list.append(block(fg=conf.color_back, bg=col_head))
|
||||
parsed_list.append(conf.icon_wsp)
|
||||
parsed_list.append(block(click=''))
|
||||
|
||||
displays = ' '.join(parsed_list)
|
||||
|
||||
def update_workspaces(data):
|
||||
global workspaces
|
||||
prefix = block(font='1', fg=conf.color_back, bg=conf.color_head)
|
||||
prefix_foc = ''.join([block(fg = conf.color_head, bg=conf.color_wsp)
|
||||
, conf.sep_right
|
||||
, block(fg=conf.color_back, bg=conf.color_wsp, font='1')])
|
||||
prefix_ina = block(fg=conf.color_back, bg=conf.color_head, font='1')
|
||||
wspces = []
|
||||
for entry in data: # entry for example FOC5___terms
|
||||
status = entry[0:3] # FOC or INA
|
||||
num = entry[3]
|
||||
name = entry[7:]
|
||||
full_name = ' '.join(['', num, name])
|
||||
current = ''.join([block(click=('i3-msg workspace' + full_name))
|
||||
, full_name, block(click='')])
|
||||
if status == "FOC":
|
||||
wspces.append(''.join([prefix_foc, current]))
|
||||
else:
|
||||
wspces.append(''.join([prefix_ina, current]))
|
||||
|
||||
workspaces = ''.join([prefix, ' '.join(wspces)])
|
||||
|
||||
def update_title(data):
|
||||
global win_title
|
||||
win_title = ' '.join([block(fg=conf.color_head, bg=conf.color_sec_b2)
|
||||
, conf.sep_right, block(fg=conf.color_head, bg=conf.color_sec_b2, click='mode cycle')
|
||||
, conf.icon_prog
|
||||
, block(fg=conf.color_sec_b2, bg='-')
|
||||
, ' '.join(data)])
|
||||
|
||||
def format_line():
|
||||
# Need to end with a reset block, otherwise the color fills the %{r} spacer
|
||||
if common.mode == common.bar_mode.normal:
|
||||
return ''.join(['%{l}', reset, displays, workspaces, win_title
|
||||
, '%{r}', g_parser.format(), reset])
|
||||
elif common.mode == common.bar_mode.power:
|
||||
return ''.join(['%{l}', reset, power_opts
|
||||
, '%{r}', g_parser.format(), reset_power])
|
||||
elif common.mode == common.bar_mode.control:
|
||||
return ''.join(['%{l}', reset, displays, workspaces, controls
|
||||
, '%{r}', response, time, reset])
|
||||
else:
|
||||
return ''.join(['%{l}', reset, displays, workspaces, 'Not normal'
|
||||
, '%{r}', time, reset])
|
||||
|
||||
parsers_dict = { 'WSP':update_workspaces
|
||||
,'WIN':update_title
|
||||
,'DISP':update_displays
|
||||
,'RESP':update_response
|
||||
}
|
||||
return g_parser.format()
|
||||
|
||||
def parse_line(line_in):
|
||||
''' Lines are
|
||||
|
||||
CNK_FAST Fri 21 Sep 19:45:18 VolXXX wlan_d wlan_u eth_d eth_u
|
||||
CNK_SLOW cpu mem disk_root disk_home batt bri lang disp
|
||||
WSPINA1___main INA2___web FOC5___terms INA6___stats
|
||||
'''
|
||||
# Parse lines that external programs have written to fifo
|
||||
|
||||
for unit in g_parser.units:
|
||||
if unit.external is not None:
|
||||
@@ -258,17 +196,6 @@ def parse_line(line_in):
|
||||
func(line_in[l:].split())
|
||||
break
|
||||
|
||||
try:
|
||||
for key,func in parsers_dict.items():
|
||||
l = len(key)
|
||||
if line_in[:l] == key:
|
||||
func(line_in[l:].split())
|
||||
break
|
||||
|
||||
except:
|
||||
print('Exception occured\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split()))
|
||||
raise
|
||||
|
||||
formatted_line = format_line()
|
||||
return formatted_line
|
||||
|
||||
|
||||
Reference in New Issue
Block a user