Bar refactor

Nothing visible except more and less bugs, but it's waaaaay easier to
work with the code :)
This commit is contained in:
Geoffrey Frogeye 2018-09-06 17:00:46 +02:00
parent ae0c7c7c09
commit ca9d5e279e
4 changed files with 260 additions and 215 deletions

View file

@ -9,10 +9,6 @@ if __name__ == "__main__":
Bar.init() Bar.init()
Updater.init() Updater.init()
# Bar.addSectionAll(NotmuchUnreadProvider(dir='~/.mail/', theme=1), BarGroupType.RIGHT)
# Bar.addSectionAll(TimeProvider(theme=1), BarGroupType.RIGHT)
# else:
WORKSPACE_THEME = 0 WORKSPACE_THEME = 0
FOCUS_THEME = 3 FOCUS_THEME = 3
URGENT_THEME = 1 URGENT_THEME = 1
@ -28,10 +24,10 @@ if __name__ == "__main__":
SYSTEM_THEME = 2 SYSTEM_THEME = 2
DANGER_THEME = FOCUS_THEME DANGER_THEME = FOCUS_THEME
CRITICAL_THEME = URGENT_THEME CRITICAL_THEME = URGENT_THEME
Bar.addSectionAll(CpuProvider(theme=SYSTEM_THEME, themeDanger=DANGER_THEME, themeCritical=CRITICAL_THEME), BarGroupType.RIGHT) Bar.addSectionAll(CpuProvider(), BarGroupType.RIGHT)
Bar.addSectionAll(RamProvider(theme=SYSTEM_THEME, themeDanger=DANGER_THEME, themeCritical=CRITICAL_THEME), BarGroupType.RIGHT) Bar.addSectionAll(RamProvider(), BarGroupType.RIGHT)
Bar.addSectionAll(TemperatureProvider(theme=SYSTEM_THEME, themeDanger=DANGER_THEME, themeCritical=CRITICAL_THEME), BarGroupType.RIGHT) Bar.addSectionAll(TemperatureProvider(), BarGroupType.RIGHT)
Bar.addSectionAll(BatteryProvider(theme=SYSTEM_THEME, themeDanger=DANGER_THEME, themeCritical=CRITICAL_THEME), BarGroupType.RIGHT) Bar.addSectionAll(BatteryProvider(), BarGroupType.RIGHT)
# Peripherals # Peripherals
PERIPHERAL_THEME = 5 PERIPHERAL_THEME = 5

View file

@ -17,7 +17,6 @@ log = logging.getLogger()
# BarGroup strings should be a good compromise) # BarGroup strings should be a good compromise)
# TODO Use bytes rather than strings # TODO Use bytes rather than strings
# TODO Use default colors of lemonbar sometimes # TODO Use default colors of lemonbar sometimes
# TODO Mouse actions
# TODO Adapt bar height with font height # TODO Adapt bar height with font height
@ -144,7 +143,6 @@ class Bar:
# Color for empty sections # Color for empty sections
Bar.string += BarGroup.color(*Section.EMPTY) Bar.string += BarGroup.color(*Section.EMPTY)
# print(Bar.string)
Bar.process.stdin.write(bytes(Bar.string + '\n', 'utf-8')) Bar.process.stdin.write(bytes(Bar.string + '\n', 'utf-8'))
Bar.process.stdin.flush() Bar.process.stdin.flush()
@ -194,10 +192,6 @@ class BarGroup:
def bgColor(color): def bgColor(color):
return "%{B" + (color or '-') + "}" return "%{B" + (color or '-') + "}"
@staticmethod
def underlineColor(color):
return "%{U" + (color or '-') + "}"
@staticmethod @staticmethod
def color(fg, bg): def color(fg, bg):
return BarGroup.fgColor(fg) + BarGroup.bgColor(bg) return BarGroup.fgColor(fg) + BarGroup.bgColor(bg)
@ -291,6 +285,9 @@ class Section:
THEMES = list() THEMES = list()
EMPTY = (FGCOLOR, BGCOLOR) EMPTY = (FGCOLOR, BGCOLOR)
ICON = None
PERSISTENT = False
#: Sections that do not have their destination size #: Sections that do not have their destination size
sizeChanging = set() sizeChanging = set()
updateThread = SectionThread(daemon=True) updateThread = SectionThread(daemon=True)
@ -321,14 +318,15 @@ class Section:
self.curSize = 0 self.curSize = 0
#: Destination text #: Destination text
self.dstText = '' self.dstText = Text(' ', Text(), ' ')
#: Destination size #: Destination size
self.dstSize = 0 self.dstSize = 0
#: Groups that have this section #: Groups that have this section
self.parents = set() self.parents = set()
self.actions = set() self.icon = self.ICON
def __str__(self): def __str__(self):
try: try:
@ -355,92 +353,24 @@ class Section:
for parent in self.parents: for parent in self.parents:
parent.childsTextChanged = True parent.childsTextChanged = True
def parseParts(self, parts, bit='', clo=''):
# TODO OPTI Shall benefit of a major refactor, using Objects rather
# than dicts and a stack-based approac
if isinstance(parts, str):
parts = [parts]
bits = []
clos = []
for part in parts:
if isinstance(part, str):
for char in part:
bit += char
bits.append(bit)
clos.append(clo)
bit = ''
elif isinstance(part, dict):
newBit = ''
newClo = ''
if 'fgColor' in part:
newBit = newBit + BarGroup.fgColor(part['fgColor'])
newClo = BarGroup.fgColor(Section.THEMES[self.theme][0]) + newClo
if 'bgColor' in part:
newBit = newBit + BarGroup.bgColor(part['bgColor'])
newClo = BarGroup.bgColor(Section.THEMES[self.theme][0]) + newClo
if 'underlineColor' in part:
newBit = newBit + BarGroup.underlineColor(part['underlineColor'])
newClo = BarGroup.underlineColor(None) + newClo
if 'underline' in part:
newBit = newBit + '%{+u}'
newClo = '%{-u}' + newClo
if 'overline' in part:
newBit = newBit + '%{+o}'
newClo = '%{-o}' + newClo
if 'mouseLeft' in part:
handle = Bar.getFunctionHandle(part['mouseLeft'])
newBit = newBit + '%{A1:' + handle.decode() + ':}'
newClo = '%{A1}' + newClo
if 'mouseMiddle' in part:
handle = Bar.getFunctionHandle(part['mouseMiddle'])
newBit = newBit + '%{A2:' + handle.decode() + ':}'
newClo = '%{A2}' + newClo
if 'mouseRight' in part:
handle = Bar.getFunctionHandle(part['mouseRight'])
newBit = newBit + '%{A3:' + handle.decode() + ':}'
newClo = '%{A3}' + newClo
if 'mouseScrollUp' in part:
handle = Bar.getFunctionHandle(part['mouseScrollUp'])
newBit = newBit + '%{A4:' + handle.decode() + ':}'
newClo = '%{A4}' + newClo
if 'mouseScrollDown' in part:
handle = Bar.getFunctionHandle(part['mouseScrollDown'])
newBit = newBit + '%{A5:' + handle.decode() + ':}'
newClo = '%{A5}' + newClo
newBits, newClos = self.parseParts(part["cont"], bit=bit+newBit, clo=newClo+clo)
newBits[-1] += newClo # TODO Will sometimes display errors from lemonbar
bits += newBits
clos += newClos
else:
raise RuntimeError()
return bits, clos
def updateText(self, text): def updateText(self, text):
# TODO FEAT Actions assert not isinstance(text, list) # Old behaviour, keep it until everything is cleaned
# TODO OPTI When srcSize == dstSize, maybe the bit array isn't
# needed
oldActions = self.actions.copy()
if len(text):
if isinstance(text, str): if isinstance(text, str):
# TODO OPTI This common case text = Text(text)
text = [text]
self.dstBits, self.dstClos = self.parseParts([' '] + text + [' ']) if self.icon:
# TODO FEAT Half-spaces self.dstText[0] = ' ' + self.icon + ' '
self.dstText = ''.join(self.dstBits) if text is None:
self.dstSize = len(self.dstBits) self.dstSize = 3 if self.PERSISTENT else 0
else: else:
self.dstSize = 0 self.dstText[1] = text
self.dstText.setSection(self)
for action in oldActions: self.dstSize = len(self.dstText)
self.unregsiterAction(action)
if self.curSize == self.dstSize: if self.curSize == self.dstSize:
self.curText = self.dstText if self.dstSize > 0:
self.curText = str(self.dstText)
self.informParentsTextChanged() self.informParentsTextChanged()
else: else:
Section.sizeChanging.add(self) Section.sizeChanging.add(self)
@ -449,6 +379,8 @@ class Section:
def updateTheme(self, theme): def updateTheme(self, theme):
assert isinstance(theme, int) assert isinstance(theme, int)
assert theme < len(Section.THEMES) assert theme < len(Section.THEMES)
if theme == self.theme:
return
self.theme = theme self.theme = theme
self.informParentsThemeChanged() self.informParentsThemeChanged()
Section.somethingChanged.set() Section.somethingChanged.set()
@ -482,9 +414,7 @@ class Section:
Section.sizeChanging.remove(self) Section.sizeChanging.remove(self)
return return
closPos = self.curSize-1 self.curText = self.dstText.text(size=self.curSize, pad=True)
clos = self.dstClos[closPos] if closPos < len(self.dstClos) else ''
self.curText = ''.join(Section.fit(self.dstBits, self.curSize)) + clos
self.informParentsTextChanged() self.informParentsTextChanged()
@staticmethod @staticmethod
@ -512,13 +442,15 @@ class Section:
class StatefulSection(Section): class StatefulSection(Section):
# TODO Allow to temporary expand the section (e.g. when important change) # TODO Allow to temporary expand the section (e.g. when important change)
NUMBER_STATES = 4 NUMBER_STATES = None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
Section.__init__(self, *args, **kwargs) Section.__init__(self, *args, **kwargs)
self.state = 0 self.state = 0
if hasattr(self, 'onChangeState'): if hasattr(self, 'onChangeState'):
self.onChangeState(self.state) self.onChangeState(self.state)
self.dstText.setDecorators(scrollUp=self.incrementState,
scrollDown=self.decrementState)
def incrementState(self): def incrementState(self):
newState = min(self.state + 1, self.NUMBER_STATES - 1) newState = min(self.state + 1, self.NUMBER_STATES - 1)
@ -536,18 +468,9 @@ class StatefulSection(Section):
self.onChangeState(state) self.onChangeState(state)
self.refreshData() self.refreshData()
def updateText(self, text):
if not len(text):
return text
Section.updateText(self, [{
"mouseScrollUp": self.incrementState,
"mouseScrollDown": self.decrementState,
"cont": text
}])
class ColorCountsSection(StatefulSection): class ColorCountsSection(StatefulSection):
NUMBER_STATES = 3 NUMBER_STATES = 3
ICON = '?' COLORABLE_ICON = '?'
def __init__(self, theme=None): def __init__(self, theme=None):
StatefulSection.__init__(self, theme=theme) StatefulSection.__init__(self, theme=theme)
@ -556,59 +479,154 @@ class ColorCountsSection(StatefulSection):
counts = self.subfetcher() counts = self.subfetcher()
# Nothing # Nothing
if not len(counts): if not len(counts):
return '' return None
# Icon colored # Icon colored
elif self.state == 0 and len(counts) == 1: elif self.state == 0 and len(counts) == 1:
count, color = counts[0] count, color = counts[0]
return [{'fgColor': color, "cont": self.ICON}] return Text(self.COLORABLE_ICON, fg=color)
# Icon # Icon
elif self.state == 0 and len(counts) > 1: elif self.state == 0 and len(counts) > 1:
return self.ICON return Text(self.COLORABLE_ICON)
# Icon + Total # Icon + Total
elif self.state == 1 and len(counts) > 1: elif self.state == 1 and len(counts) > 1:
total = sum([count for count, color in counts]) total = sum([count for count, color in counts])
return [self.ICON, ' ', str(total)] return Text(self.COLORABLE_ICON, ' ', total)
# Icon + Counts # Icon + Counts
else: else:
text = [self.ICON] text = Text(self.COLORABLE_ICON)
for count, color in counts: for count, color in counts:
text += [' ', {'fgColor': color, "cont": str(count)}] text.append(' ', Text(count, fg=color))
return text return text
if __name__ == '__main__': class Text:
Bar.init() def _setElements(self, elements):
sec = Section(0) # TODO OPTI Concatenate consecutrive string
sech = Section(1) self.elements = list(elements)
sec1 = Section(2)
sec2 = Section(3)
sec2h = Section(4)
sec3 = Section(5)
Bar.addSectionAll(sec, BarGroupType.LEFT)
Bar.addSectionAll(sech, BarGroupType.LEFT)
Bar.addSectionAll(sec1, BarGroupType.LEFT)
Bar.addSectionAll(sec2, BarGroupType.RIGHT)
Bar.addSectionAll(sec2h, BarGroupType.RIGHT)
Bar.addSectionAll(sec3, BarGroupType.RIGHT)
time.sleep(1) def _setDecorators(self, decorators):
sec.updateText("A") # TODO OPTI Convert no decorator to strings
time.sleep(1) self.decorators = decorators
sec.updateText("") self.prefix = None
time.sleep(1) self.suffix = None
sec.updateText("Hello")
sec1.updateText("world!") def __init__(self, *args, **kwargs):
sec2.updateText("Salut") self._setElements(args)
sec2h.updateText("le") self._setDecorators(kwargs)
sec3.updateText("monde !") self.section = None
time.sleep(3)
sech.updateText("the") def append(self, *args):
sec2h.updateText("") self._setElements(self.elements + list(args))
time.sleep(2)
sec.updateText("") def prepend(self, *args):
sech.updateText("") self._setElements(list(args) + self.elements)
sec1.updateText("")
sec2.updateText("") def setElements(self, *args):
sec2h.updateText("") self._setElements(args)
sec3.updateText("")
time.sleep(5) def setDecorators(self, **kwargs):
self._setDecorators(kwargs)
def setSection(self, section):
assert isinstance(section, Section)
self.section = section
for element in self.elements:
if isinstance(element, Text):
element.setSection(section)
def _genFixs(self):
if self.prefix is not None and self.suffix is not None:
return
self.prefix = ''
self.suffix = ''
def nest(prefix, suffix):
self.prefix = self.prefix + '%{' + prefix + '}'
self.suffix = '%{' + suffix + '}' + self.suffix
def getColor(val):
# TODO Allow themes
assert isinstance(val, str) and len(val) == 7
return val
def button(number, function):
handle = Bar.getFunctionHandle(function)
nest('A' + number + ':' + handle.decode() + ':', 'A' + number)
for key, val in self.decorators.items():
if key == 'fg':
reset = self.section.THEMES[self.section.theme][0]
nest('F' + getColor(val), 'F' + reset)
elif key == 'bg':
reset = self.section.THEMES[self.section.theme][1]
nest('B' + getColor(val), 'B' + reset)
elif key == "clickLeft":
button('1', val)
elif key == "clickMiddle":
button('2', val)
elif key == "clickRight":
button('3', val)
elif key == "scrollUp":
button('4', val)
elif key == "scrollDown":
button('5', val)
else:
log.warn("Unkown decorator: {}".format(key))
def _text(self, size=None, pad=False):
self._genFixs()
curString = self.prefix
curSize = 0
remSize = size
for element in self.elements:
if element is None:
continue
elif isinstance(element, Text):
newString, newSize = element._text(size=remSize)
else:
newString = str(element)
if remSize is not None:
newString = newString[:remSize]
newSize = len(newString)
curString += newString
curSize += newSize
if remSize is not None:
remSize -= newSize
if remSize <= 0:
break
curString += self.suffix
if pad and remSize > 0:
curString += ' ' * remSize
curSize += remSize
if size is not None:
if pad:
assert size == curSize
else:
assert size >= curSize
return curString, curSize
def text(self, *args, **kwargs):
string, size = self._text(*args, **kwargs)
return string
def __str__(self):
# TODO OPTI
return self.text()
def __len__(self):
# TODO OPTI
string, size = self._text()
return size
def __getitem__(self, index):
return self.elements[index]
def __setitem__(self, index, data):
self.elements[index] = data

View file

@ -53,25 +53,60 @@ class TimeProvider(StatefulSection, PeriodicUpdater):
StatefulSection.__init__(self, theme) StatefulSection.__init__(self, theme)
self.changeInterval(1) # TODO OPTI When state < 1 self.changeInterval(1) # TODO OPTI When state < 1
class CpuProvider(Section, PeriodicUpdater): class AlertLevel(enum.Enum):
NORMAL = 0
WARNING = 1
DANGER = 2
class AlertingSection(StatefulSection):
# TODO EASE Correct settings for themes
THEMES = {AlertLevel.NORMAL: 2,
AlertLevel.WARNING: 3,
AlertLevel.DANGER: 1}
PERSISTENT = True
def getLevel(self, quantity):
if quantity > self.dangerThresold:
return AlertLevel.DANGER
elif quantity > self.warningThresold:
return AlertLevel.WARNING
else:
return AlertLevel.NORMAL
def updateLevel(self, quantity):
self.level = self.getLevel(quantity)
self.updateTheme(self.THEMES[self.level])
if self.level == AlertLevel.NORMAL:
return
# TODO Temporary update state
def __init__(self, theme):
StatefulSection.__init__(self, theme)
self.dangerThresold = 0.90
self.warningThresold = 0.75
class CpuProvider(AlertingSection, PeriodicUpdater):
NUMBER_STATES = 3
ICON = ''
def fetcher(self): def fetcher(self):
percents = psutil.cpu_percent(percpu=True)
percent = psutil.cpu_percent(percpu=False) percent = psutil.cpu_percent(percpu=False)
theme = self.themeCritical if percent >= 90 else \ self.updateLevel(percent/100)
(self.themeDanger if percent >= 75 else self.themeNormal) if self.state >= 2:
self.updateTheme(theme) percents = psutil.cpu_percent(percpu=True)
return '' + ''.join([Section.ramp(p/100) for p in percents]) return ''.join([Section.ramp(p/100) for p in percents])
elif self.state >= 1:
return Section.ramp(percent/100)
def __init__(self, theme=None, themeDanger=None, themeCritical=None): def __init__(self, theme=None, themeDanger=None, themeCritical=None):
AlertingSection.__init__(self, theme)
PeriodicUpdater.__init__(self) PeriodicUpdater.__init__(self)
Section.__init__(self, theme)
self.themeNormal = theme or self.theme
self.themeDanger = themeDanger or self.theme
self.themeCritical = themeCritical or self.theme
self.changeInterval(1) self.changeInterval(1)
class RamProvider(Section, PeriodicUpdater): class RamProvider(Section, PeriodicUpdater):
# TODO Use AlertingSection
""" """
Shows free RAM Shows free RAM
""" """
@ -93,7 +128,7 @@ class RamProvider(Section, PeriodicUpdater):
self.changeInterval(1) self.changeInterval(1)
class TemperatureProvider(Section, PeriodicUpdater): class TemperatureProvider(Section, PeriodicUpdater):
# TODO FEAT Change color when > high > critical # TODO Use AlertingSection
RAMP = "" RAMP = ""
@ -124,7 +159,7 @@ class TemperatureProvider(Section, PeriodicUpdater):
class BatteryProvider(Section, PeriodicUpdater): class BatteryProvider(Section, PeriodicUpdater):
# TODO Change color when < thresold% # TODO Use AlertingSection
RAMP = "" RAMP = ""
@ -194,24 +229,23 @@ class NetworkProviderSection(StatefulSection, Updater):
NUMBER_STATES = 4 NUMBER_STATES = 4
def getIcon(self): def actType(self):
self.ssid = None
if self.iface.startswith('eth') or self.iface.startswith('enp'): if self.iface.startswith('eth') or self.iface.startswith('enp'):
if 'u' in self.iface: if 'u' in self.iface:
return [''] self.icon = ''
else: else:
return [''] self.icon = ''
elif self.iface.startswith('wlan') or self.iface.startswith('wlp'): elif self.iface.startswith('wlan') or self.iface.startswith('wlp'):
if self.state > 0: self.icon = ''
if self.showSsid:
cmd = ["iwgetid", self.iface, "--raw"] cmd = ["iwgetid", self.iface, "--raw"]
p = subprocess.run(cmd, stdout=subprocess.PIPE) p = subprocess.run(cmd, stdout=subprocess.PIPE)
ssid = p.stdout.strip().decode() self.ssid = p.stdout.strip().decode()
return ['{}'.format(ssid)]
else:
return ['']
elif self.iface.startswith('tun') or self.iface.startswith('tap'): elif self.iface.startswith('tun') or self.iface.startswith('tap'):
return [''] self.icon = ''
else: else:
return ['?'] self.icon = '?'
def getAddresses(self): def getAddresses(self):
ipv4 = None ipv4 = None
@ -224,26 +258,28 @@ class NetworkProviderSection(StatefulSection, Updater):
return ipv4, ipv6 return ipv4, ipv6
def fetcher(self): def fetcher(self):
text = []
if self.iface not in self.parent.stats or \ if self.iface not in self.parent.stats or \
not self.parent.stats[self.iface].isup or \ not self.parent.stats[self.iface].isup or \
self.iface.startswith('lo'): self.iface.startswith('lo'):
return text return None
# Get addresses # Get addresses
ipv4, ipv6 = self.getAddresses() ipv4, ipv6 = self.getAddresses()
if ipv4 is None and ipv6 is None: if ipv4 is None and ipv6 is None:
return text return None
text = self.getIcon() text = []
self.actType()
if self.showSsid and self.ssid:
text.append(self.ssid)
if self.showAddress: if self.showAddress:
if ipv4: if ipv4:
netStrFull = '{}/{}'.format(ipv4.address, ipv4.netmask) netStrFull = '{}/{}'.format(ipv4.address, ipv4.netmask)
addr = ipaddress.IPv4Network(netStrFull, strict=False) addr = ipaddress.IPv4Network(netStrFull, strict=False)
addrStr = '{}/{}'.format(ipv4.address, addr.prefixlen) addrStr = '{}/{}'.format(ipv4.address, addr.prefixlen)
text += [addrStr] text.append(addrStr)
# TODO IPV6 # TODO IPV6
# if ipv6: # if ipv6:
# text += ' ' + ipv6.address # text += ' ' + ipv6.address
@ -255,16 +291,18 @@ class NetworkProviderSection(StatefulSection, Updater):
- self.parent.prevIO[self.iface].bytes_sent - self.parent.prevIO[self.iface].bytes_sent
recvDiff /= self.parent.dt recvDiff /= self.parent.dt
sentDiff /= self.parent.dt sentDiff /= self.parent.dt
text += ['{}{}'.format(humanSize(recvDiff), humanSize(sentDiff))] text.append('{}{}'.format(humanSize(recvDiff),
humanSize(sentDiff)))
if self.showTransfer: if self.showTransfer:
text += [' {}{}'.format( text.append('{}{}'.format(
humanSize(self.parent.IO[self.iface].bytes_recv), humanSize(self.parent.IO[self.iface].bytes_recv),
humanSize(self.parent.IO[self.iface].bytes_sent))] humanSize(self.parent.IO[self.iface].bytes_sent)))
return text return ' '.join(text)
def onChangeState(self, state): def onChangeState(self, state):
self.showSsid = state >= 0
self.showAddress = state >= 1 self.showAddress = state >= 1
self.showSpeed = state >= 2 self.showSpeed = state >= 2
self.showTransfer = state >= 3 self.showTransfer = state >= 3
@ -307,7 +345,7 @@ class NetworkProvider(Section, PeriodicUpdater):
for section in self.sections.values(): for section in self.sections.values():
section.refreshData() section.refreshData()
return '' return None
def addParent(self, parent): def addParent(self, parent):
self.parents.add(parent) self.parents.add(parent)
@ -328,15 +366,13 @@ class SshAgentProvider(PeriodicUpdater):
cmd = ["ssh-add", "-l"] cmd = ["ssh-add", "-l"]
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
if proc.returncode != 0: if proc.returncode != 0:
return '' return None
text = [] text = Text()
for line in proc.stdout.split(b'\n'): for line in proc.stdout.split(b'\n'):
if not len(line): if not len(line):
continue continue
fingerprint = line.split()[1] fingerprint = line.split()[1]
text += [{"cont": '', text.append(Text('', fg=randomColor(seed=fingerprint)))
"fgColor": randomColor(seed=fingerprint)
}]
return text return text
def __init__(self): def __init__(self):
@ -349,8 +385,8 @@ class GpgAgentProvider(PeriodicUpdater):
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
# proc = subprocess.run(cmd) # proc = subprocess.run(cmd)
if proc.returncode != 0: if proc.returncode != 0:
return '' return None
text = [] text = Text()
for line in proc.stdout.split(b'\n'): for line in proc.stdout.split(b'\n'):
if not len(line) or line == b'OK': if not len(line) or line == b'OK':
continue continue
@ -358,9 +394,7 @@ class GpgAgentProvider(PeriodicUpdater):
if spli[6] != b'1': if spli[6] != b'1':
continue continue
keygrip = spli[2] keygrip = spli[2]
text += [{"cont": '', text.append(Text('', fg=randomColor(seed=keygrip)))
"fgColor": randomColor(seed=keygrip)
}]
return text return text
def __init__(self): def __init__(self):
@ -368,14 +402,16 @@ class GpgAgentProvider(PeriodicUpdater):
self.changeInterval(5) self.changeInterval(5)
class KeystoreProvider(Section, MergedUpdater): class KeystoreProvider(Section, MergedUpdater):
ICON = ''
def __init__(self, theme=None): def __init__(self, theme=None):
MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider(), prefix=['']) MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider())
Section.__init__(self, theme) Section.__init__(self, theme)
class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater): class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater):
# TODO OPTI Transform InotifyUpdater (watching notmuch folder should be # TODO OPTI Transform InotifyUpdater (watching notmuch folder should be
# enough) # enough)
ICON = '' COLORABLE_ICON = ''
def subfetcher(self): def subfetcher(self):
db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY, path=self.dir) db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY, path=self.dir)
@ -414,7 +450,7 @@ class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater):
class TodoProvider(ColorCountsSection, InotifyUpdater): class TodoProvider(ColorCountsSection, InotifyUpdater):
# TODO OPT/UX Maybe we could get more data from the todoman python module # TODO OPT/UX Maybe we could get more data from the todoman python module
# TODO OPT Specific callback for specific directory # TODO OPT Specific callback for specific directory
ICON = '' COLORABLE_ICON = ''
def updateCalendarList(self): def updateCalendarList(self):
calendars = sorted(os.listdir(self.dir)) calendars = sorted(os.listdir(self.dir))
@ -575,6 +611,7 @@ class I3WorkspacesProvider(Section, I3Updater):
self.initialPopulation(parent) self.initialPopulation(parent)
class MpdProvider(Section, ThreadedUpdater): class MpdProvider(Section, ThreadedUpdater):
# TODO FEAT More informations and controls
MAX_LENGTH = 50 MAX_LENGTH = 50
@ -610,9 +647,7 @@ class MpdProvider(Section, ThreadedUpdater):
if len(infosStr) > MpdProvider.MAX_LENGTH: if len(infosStr) > MpdProvider.MAX_LENGTH:
infosStr = infosStr[:MpdProvider.MAX_LENGTH-1] + '' infosStr = infosStr[:MpdProvider.MAX_LENGTH-1] + ''
text = ["{}".format(infosStr)] return "{}".format(infosStr)
return text
def loop(self): def loop(self):
try: try:
@ -624,3 +659,4 @@ class MpdProvider(Section, ThreadedUpdater):
except BaseException as e: except BaseException as e:
log.error(e, exc_info=True) log.error(e, exc_info=True)

View file

@ -9,6 +9,7 @@ import time
import logging import logging
import coloredlogs import coloredlogs
import i3ipc import i3ipc
from display import Text
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger() log = logging.getLogger()
@ -231,23 +232,17 @@ class MergedUpdater(Updater):
# TODO OPTI Do not update until end of periodic batch # TODO OPTI Do not update until end of periodic batch
def fetcher(self): def fetcher(self):
text = [] text = Text()
for updater in self.updaters: for updater in self.updaters:
data = self.texts[updater] text.append(self.texts[updater])
if not len(data): # TODO OPTI After Text.__len__
continue # if not len(text):
if isinstance(data, str): # return None
data = [data] return text
text += data
if not len(text):
return ''
return self.prefix + text + self.suffix
def __init__(self, *args, prefix=[''], suffix=['']): def __init__(self, *args):
Updater.__init__(self) Updater.__init__(self)
self.prefix = prefix
self.suffix = suffix
self.updaters = [] self.updaters = []
self.texts = dict() self.texts = dict()