diff --git a/config/lemonbar/bar.py b/config/lemonbar/bar.py index 01ff0c5..962e797 100755 --- a/config/lemonbar/bar.py +++ b/config/lemonbar/bar.py @@ -12,9 +12,14 @@ if __name__ == "__main__": WORKSPACE_THEME = 0 FOCUS_THEME = 3 URGENT_THEME = 1 + CUSTOM_SUFFIXES = '▲■' - Bar.addSectionAll(I3WorkspacesProvider(theme=WORKSPACE_THEME, themeFocus=FOCUS_THEME, themeUrgent=URGENT_THEME, themeMode=URGENT_THEME), BarGroupType.LEFT) - + customNames = dict() + for i in range(len(CUSTOM_SUFFIXES)): + short = str(i+1) + full = short + ' ' + CUSTOM_SUFFIXES[i] + customNames[short] = full + Bar.addSectionAll(I3WorkspacesProvider(theme=WORKSPACE_THEME, themeFocus=FOCUS_THEME, themeUrgent=URGENT_THEME, themeMode=URGENT_THEME, customNames=customNames), BarGroupType.LEFT) # TODO Middle Bar.addSectionAll(MpdProvider(theme=7), BarGroupType.LEFT) diff --git a/config/lemonbar/display.py b/config/lemonbar/display.py index 7009f33..9e63896 100755 --- a/config/lemonbar/display.py +++ b/config/lemonbar/display.py @@ -54,7 +54,7 @@ class Bar: def init(): Section.init() - cmd = ['lemonbar', '-b'] + cmd = ['lemonbar', '-b', '-a', '64'] for font in Bar.FONTS: cmd += ["-f", "{}:size={}".format(font, Bar.FONTSIZE)] Bar.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, @@ -326,6 +326,7 @@ class Section: self.parents = set() self.icon = self.ICON + self.persistent = self.PERSISTENT def __str__(self): @@ -354,19 +355,15 @@ class Section: parent.childsTextChanged = True def updateText(self, text): - assert not isinstance(text, list) # Old behaviour, keep it until everything is cleaned if isinstance(text, str): text = Text(text) - if self.icon: - self.dstText[0] = ' ' + self.icon + ' ' + self.dstText[0] = None if (text is None and not self.persistent) else ((' ' + self.icon + ' ') if self.icon else ' ') + self.dstText[1] = text + self.dstText[2] = ' ' if self.dstText[1] is not None and len(self.dstText[1]) else None - if text is None: - self.dstSize = 3 if self.PERSISTENT else 0 - else: - self.dstText[1] = text - self.dstText.setSection(self) - self.dstSize = len(self.dstText) + self.dstSize = len(self.dstText) + self.dstText.setSection(self) if self.curSize == self.dstSize: if self.dstSize > 0: @@ -441,7 +438,7 @@ class Section: class StatefulSection(Section): - # TODO Allow to temporary expand the section (e.g. when important change) + # TODO FEAT Allow to temporary expand the section (e.g. when important change) NUMBER_STATES = None def __init__(self, *args, **kwargs): @@ -449,8 +446,8 @@ class StatefulSection(Section): self.state = 0 if hasattr(self, 'onChangeState'): self.onChangeState(self.state) - self.dstText.setDecorators(scrollUp=self.incrementState, - scrollDown=self.decrementState) + self.dstText.setDecorators(clickLeft=self.incrementState, + clickRight=self.decrementState) def incrementState(self): newState = min(self.state + 1, self.NUMBER_STATES - 1) @@ -617,13 +614,27 @@ class Text: return string def __str__(self): - # TODO OPTI - return self.text() + self._genFixs() + curString = self.prefix + for element in self.elements: + if element is None: + continue + else: + curString += str(element) + curString += self.suffix + return curString def __len__(self): - # TODO OPTI - string, size = self._text() - return size + curSize = 0 + for element in self.elements: + if element is None: + continue + elif isinstance(element, Text): + curSize += len(element) + else: + curSize += len(str(element)) + return curSize + def __getitem__(self, index): return self.elements[index] diff --git a/config/lemonbar/providers.py b/config/lemonbar/providers.py index b3f641a..2856ab3 100755 --- a/config/lemonbar/providers.py +++ b/config/lemonbar/providers.py @@ -99,94 +99,96 @@ class CpuProvider(AlertingSection, PeriodicUpdater): elif self.state >= 1: return Section.ramp(percent/100) - def __init__(self, theme=None, themeDanger=None, themeCritical=None): + def __init__(self, theme=None): AlertingSection.__init__(self, theme) PeriodicUpdater.__init__(self) self.changeInterval(1) -class RamProvider(Section, PeriodicUpdater): - # TODO Use AlertingSection +class RamProvider(AlertingSection, PeriodicUpdater): """ Shows free RAM """ + NUMBER_STATES = 4 + ICON = '' + def fetcher(self): mem = psutil.virtual_memory() - ramp = Section.ramp(1-mem.percent/100) - free = humanSize(mem.available) - theme = self.themeCritical if mem.percent >= 90 else \ - (self.themeDanger if mem.percent >= 75 else self.themeNormal) - self.updateTheme(theme) - return ' {}{}'.format(ramp, free) + freePerc = 1-mem.percent/100 + self.updateLevel(freePerc) - def __init__(self, theme=None, themeDanger=None, themeCritical=None): + if self.state < 1: + return None + + text = Text(Section.ramp(freePerc)) + if self.state >= 2: + freeStr = humanSize(mem.available) + text.append(freeStr) + if self.state >= 3: + totalStr = humanSize(mem.total) + text.append('/', totalStr) + + return text + + def __init__(self, theme=None): + AlertingSection.__init__(self, theme) 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) -class TemperatureProvider(Section, PeriodicUpdater): - # TODO Use AlertingSection +class TemperatureProvider(AlertingSection, PeriodicUpdater): + NUMBER_STATES = 2 RAMP = "" def fetcher(self): allTemp = psutil.sensors_temperatures() - if 'coretemp' not in allTemp: # TODO Opti Remove interval return '' - temp = allTemp['coretemp'][0] - theme = self.themeCritical if temp.current >= temp.critical else \ - (self.themeDanger if temp.current >= temp.high - else self.themeNormal) - self.updateTheme(theme) + self.warningThresold = temp.high + self.dangerThresold = temp.critical + self.updateLevel(temp.current) - ramp = Section.ramp(temp.current/temp.high, self.RAMP) - return '{} {:.0f}°C'.format(ramp, temp.current) + self.icon = Section.ramp(temp.current/temp.high, self.RAMP) + if self.state >= 1: + return '{:.0f}°C'.format(temp.current) - def __init__(self, theme=None, themeDanger=None, themeCritical=None): + def __init__(self, theme=None): + AlertingSection.__init__(self, theme) PeriodicUpdater.__init__(self) - Section.__init__(self, theme=theme) - self.themeNormal = theme or self.theme - self.themeDanger = themeDanger or self.theme - self.themeCritical = themeCritical or self.theme self.changeInterval(5) -class BatteryProvider(Section, PeriodicUpdater): - # TODO Use AlertingSection - +class BatteryProvider(AlertingSection, PeriodicUpdater): + NUMBER_STATES = 2 RAMP = "" def fetcher(self): bat = psutil.sensors_battery() - text = '' if not bat: - return text - if bat.power_plugged: - text += "" - text += Section.ramp(bat.percent/100, self.RAMP) - if bat.percent < 100: - text += ' {:.0f}%'.format(bat.percent) - theme = self.themeCritical if bat.percent < 10 else \ - (self.themeDanger if bat.percent < 25 else self.themeNormal) - self.updateTheme(theme) - return text + self.icon = None + return None - def __init__(self, theme=None, themeDanger=None, themeCritical=None): + self.icon = ("" if bat.power_plugged else "") + \ + Section.ramp(bat.percent/100, self.RAMP) + + self.updateLevel(1-bat.percent/100) + + if self.state < 1: + return + + return Text('{:.0f}%'.format(bat.percent)) + # TODO Time remaining (if the estimation is somewhat correct) + + def __init__(self, theme=None): + AlertingSection.__init__(self, theme) 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(5) + class PulseaudioProvider(Section, ThreadedUpdater): def __init__(self, theme=None): ThreadedUpdater.__init__(self) @@ -227,7 +229,7 @@ class PulseaudioProvider(Section, ThreadedUpdater): class NetworkProviderSection(StatefulSection, Updater): - NUMBER_STATES = 4 + NUMBER_STATES = 5 def actType(self): self.ssid = None @@ -269,6 +271,7 @@ class NetworkProviderSection(StatefulSection, Updater): return None text = [] + self.persistent = True self.actType() if self.showSsid and self.ssid: @@ -302,10 +305,10 @@ class NetworkProviderSection(StatefulSection, Updater): return ' '.join(text) def onChangeState(self, state): - self.showSsid = state >= 0 - self.showAddress = state >= 1 - self.showSpeed = state >= 2 - self.showTransfer = state >= 3 + self.showSsid = state >= 1 + self.showAddress = state >= 2 + self.showSpeed = state >= 3 + self.showTransfer = state >= 4 def __init__(self, iface, parent): Updater.__init__(self) @@ -408,7 +411,7 @@ class KeystoreProvider(Section, MergedUpdater): MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider()) Section.__init__(self, theme) -class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater): +class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater): # TODO OPTI Transform InotifyUpdater (watching notmuch folder should be # enough) COLORABLE_ICON = '' @@ -444,7 +447,7 @@ class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater): color = f.read().strip() self.colors[account] = color - self.changeInterval(10) + self.addPath(os.path.join(self.dir, '.notmuch', 'xapian')) class TodoProvider(ColorCountsSection, InotifyUpdater): @@ -453,12 +456,24 @@ class TodoProvider(ColorCountsSection, InotifyUpdater): COLORABLE_ICON = '' def updateCalendarList(self): + print(459) calendars = sorted(os.listdir(self.dir)) for calendar in calendars: # If the calendar wasn't in the list if calendar not in self.calendars: self.addPath(os.path.join(self.dir, calendar), refresh=False) + + # Fetching name + path = os.path.join(self.dir, calendar, 'displayname') + with open(path, 'r') as f: + self.names[calendar] = f.read().strip() + + # Fetching color + path = os.path.join(self.dir, calendar, 'color') + with open(path, 'r') as f: + self.colors[calendar] = f.read().strip() self.calendars = calendars + print(475, self.calendars) def __init__(self, dir, theme=None): """ @@ -470,34 +485,37 @@ class TodoProvider(ColorCountsSection, InotifyUpdater): assert os.path.isdir(self.dir) self.calendars = [] - self.addPath(self.dir) - - def getName(self, calendar): - path = os.path.join(self.dir, calendar, 'displayname') - with open(path, 'r') as f: - name = f.read().strip() - return name - - def getColor(self, calendar): - path = os.path.join(self.dir, calendar, 'color') - with open(path, 'r') as f: - color = f.read().strip() - return color + self.colors = dict() + self.names = dict() + self.updateCalendarList() + self.refreshData() def countUndone(self, calendar): - cmd = ["todo", "--porcelain", "list", self.getName(calendar)] + cmd = ["todo", "--porcelain", "list"] + if calendar: + cmd.append(self.names[calendar]) proc = subprocess.run(cmd, stdout=subprocess.PIPE) data = json.loads(proc.stdout) return len(data) def subfetcher(self): counts = [] - self.updateCalendarList() + + # TODO This an ugly optimisation that cuts on features, but todoman + # calls are very expensive so we keep that in the meanwhile + if self.state < 2: + c = self.countUndone(None) + if c > 0: + counts.append((c, '#00000')) + counts.append((0, '#FFFFF')) + return counts + # Optimisation ends here + for calendar in self.calendars: c = self.countUndone(calendar) if c <= 0: continue - counts.append((c, self.getColor(calendar))) + counts.append((c, self.colors[calendar])) return counts class I3WindowTitleProvider(Section, I3Updater): @@ -512,22 +530,42 @@ class I3WindowTitleProvider(Section, I3Updater): Section.__init__(self, theme=theme) self.on("window", self.on_window) +class I3WorkspacesProviderSection(Section): + def selectTheme(self): + if self.urgent: + return self.parent.themeUrgent + elif self.focused: + return self.parent.themeFocus + else: + return self.parent.themeNormal + + def show(self): + self.updateTheme(self.selectTheme()) + self.updateText(self.fullName if self.focused else self.shortName) + + def changeState(self, focused, urgent): + self.focused = focused + self.urgent = urgent + self.show() + + def setName(self, name): + self.shortName = name + self.fullName = self.parent.customNames[name] \ + if name in self.parent.customNames else name + + def __init__(self, name, parent): + Section.__init__(self) + self.parent = parent + self.setName(name) + + def empty(self): + self.updateTheme(self.parent.themeNormal) + self.updateText(None) + + class I3WorkspacesProvider(Section, I3Updater): # TODO Multi-screen - THEME_NORMAL = 0 - THEME_FOCUSED = 2 - THEME_URGENT = 1 - THEME_MODE = 1 - - def setName(self, section, origName): - # TODO Custom names - if origName: - section.fullName = origName - else: - section.fullName = '' - section.updateText(section.fullName) - def initialPopulation(self, parent): """ Called on init @@ -539,13 +577,14 @@ class I3WorkspacesProvider(Section, I3Updater): for workspace in workspaces: # if parent.display != workspace["display"]: # continue - theme = self.themeFocus if workspace["focused"] \ - else (self.themeUrgent if workspace["urgent"] - else self.theme) - section = Section(theme=theme) + + section = I3WorkspacesProviderSection(workspace["name"], self) + section.focused = workspace["focused"] + section.urgent = workspace["urgent"] + section.show() parent.addSectionAfter(lastSection, section) - self.setName(section, workspace["name"]) self.sections[workspace["num"]] = section + lastSection = section def on_workspace_init(self, i3, e): @@ -554,45 +593,52 @@ class I3WorkspacesProvider(Section, I3Updater): if i in self.sections: section = self.sections[i] else: + # Find the section just before while i not in self.sections.keys() and i > 0: i -= 1 prevSection = self.sections[i] if i != 0 else self.modeSection - section = Section() - self.sections[workspace.num] = section + + section = I3WorkspacesProviderSection(workspace.name, self) prevSection.appendAfter(section) - self.setName(section, workspace.name) + self.sections[workspace.num] = section + section.focused = workspace.focused + section.urgent = workspace.urgent + section.show() def on_workspace_empty(self, i3, e): - workspace = e.current - section = self.sections[workspace.num] - self.setName(section, None) + self.sections[e.current.num].empty() def on_workspace_focus(self, i3, e): - self.sections[e.current.num].updateTheme(self.themeFocus) - self.sections[e.old.num].updateTheme(self.theme) + self.sections[e.old.num].focused = False + self.sections[e.old.num].show() + self.sections[e.current.num].focused = True + self.sections[e.current.num].show() def on_workspace_urgent(self, i3, e): - self.sections[e.current.num].updateTheme(self.themeUrgent) + self.sections[e.current.num].urgent = e.current.urgent + self.sections[e.current.num].show() def on_workspace_rename(self, i3, e): - self.sections[e.current.num].updateText(e.name) + self.sections[e.current.num].setName(e.name) + self.sections[e.current.num].show() def on_mode(self, i3, e): if e.change == 'default': - self.modeSection.updateText('') + self.modeSection.updateText(None) for section in self.sections.values(): - section.updateText(section.fullName) + section.show() else: self.modeSection.updateText(e.change) for section in self.sections.values(): - section.updateText('') + section.empty() - def __init__(self, theme=0, themeFocus=3, themeUrgent=1, themeMode=2): + def __init__(self, theme=0, themeFocus=3, themeUrgent=1, themeMode=2, customNames=dict()): I3Updater.__init__(self) Section.__init__(self) - self.theme = theme + self.themeNormal = theme self.themeFocus = themeFocus self.themeUrgent = themeUrgent + self.customNames = customNames self.sections = dict() self.on("workspace::init", self.on_workspace_init) diff --git a/config/lemonbar/updaters.py b/config/lemonbar/updaters.py index 45c1ff0..f30dc03 100755 --- a/config/lemonbar/updaters.py +++ b/config/lemonbar/updaters.py @@ -235,9 +235,8 @@ class MergedUpdater(Updater): text = Text() for updater in self.updaters: text.append(self.texts[updater]) - # TODO OPTI After Text.__len__ - # if not len(text): - # return None + if not len(text): + return None return text def __init__(self, *args):