Bar refactor
Nothing visible except more and less bugs, but it's waaaaay easier to work with the code :)
This commit is contained in:
parent
ae0c7c7c09
commit
ca9d5e279e
|
@ -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
|
||||||
|
|
|
@ -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,93 +353,25 @@ 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
|
if isinstance(text, str):
|
||||||
# needed
|
text = Text(text)
|
||||||
oldActions = self.actions.copy()
|
|
||||||
|
|
||||||
if len(text):
|
if self.icon:
|
||||||
if isinstance(text, str):
|
self.dstText[0] = ' ' + self.icon + ' '
|
||||||
# TODO OPTI This common case
|
|
||||||
text = [text]
|
|
||||||
|
|
||||||
self.dstBits, self.dstClos = self.parseParts([' '] + text + [' '])
|
if text is None:
|
||||||
# TODO FEAT Half-spaces
|
self.dstSize = 3 if self.PERSISTENT else 0
|
||||||
|
|
||||||
self.dstText = ''.join(self.dstBits)
|
|
||||||
self.dstSize = len(self.dstBits)
|
|
||||||
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.informParentsTextChanged()
|
self.curText = str(self.dstText)
|
||||||
|
self.informParentsTextChanged()
|
||||||
else:
|
else:
|
||||||
Section.sizeChanging.add(self)
|
Section.sizeChanging.add(self)
|
||||||
Section.somethingChanged.set()
|
Section.somethingChanged.set()
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue