More bar things

This commit is contained in:
Geoffrey Frogeye 2018-09-06 22:07:32 +02:00
parent ca9d5e279e
commit d010c48306
4 changed files with 188 additions and 127 deletions

View file

@ -12,9 +12,14 @@ if __name__ == "__main__":
WORKSPACE_THEME = 0 WORKSPACE_THEME = 0
FOCUS_THEME = 3 FOCUS_THEME = 3
URGENT_THEME = 1 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 # TODO Middle
Bar.addSectionAll(MpdProvider(theme=7), BarGroupType.LEFT) Bar.addSectionAll(MpdProvider(theme=7), BarGroupType.LEFT)

View file

@ -54,7 +54,7 @@ class Bar:
def init(): def init():
Section.init() Section.init()
cmd = ['lemonbar', '-b'] cmd = ['lemonbar', '-b', '-a', '64']
for font in Bar.FONTS: for font in Bar.FONTS:
cmd += ["-f", "{}:size={}".format(font, Bar.FONTSIZE)] cmd += ["-f", "{}:size={}".format(font, Bar.FONTSIZE)]
Bar.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, Bar.process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
@ -326,6 +326,7 @@ class Section:
self.parents = set() self.parents = set()
self.icon = self.ICON self.icon = self.ICON
self.persistent = self.PERSISTENT
def __str__(self): def __str__(self):
@ -354,19 +355,15 @@ class Section:
parent.childsTextChanged = True parent.childsTextChanged = True
def updateText(self, text): def updateText(self, text):
assert not isinstance(text, list) # Old behaviour, keep it until everything is cleaned
if isinstance(text, str): if isinstance(text, str):
text = Text(text) text = Text(text)
if self.icon: self.dstText[0] = None if (text is None and not self.persistent) else ((' ' + self.icon + ' ') if self.icon else ' ')
self.dstText[0] = ' ' + self.icon + ' ' 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 = len(self.dstText)
self.dstSize = 3 if self.PERSISTENT else 0 self.dstText.setSection(self)
else:
self.dstText[1] = text
self.dstText.setSection(self)
self.dstSize = len(self.dstText)
if self.curSize == self.dstSize: if self.curSize == self.dstSize:
if self.dstSize > 0: if self.dstSize > 0:
@ -441,7 +438,7 @@ class Section:
class StatefulSection(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 NUMBER_STATES = None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -449,8 +446,8 @@ class StatefulSection(Section):
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, self.dstText.setDecorators(clickLeft=self.incrementState,
scrollDown=self.decrementState) clickRight=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)
@ -617,13 +614,27 @@ class Text:
return string return string
def __str__(self): def __str__(self):
# TODO OPTI self._genFixs()
return self.text() 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): def __len__(self):
# TODO OPTI curSize = 0
string, size = self._text() for element in self.elements:
return size if element is None:
continue
elif isinstance(element, Text):
curSize += len(element)
else:
curSize += len(str(element))
return curSize
def __getitem__(self, index): def __getitem__(self, index):
return self.elements[index] return self.elements[index]

View file

@ -99,94 +99,96 @@ class CpuProvider(AlertingSection, PeriodicUpdater):
elif self.state >= 1: elif self.state >= 1:
return Section.ramp(percent/100) return Section.ramp(percent/100)
def __init__(self, theme=None, themeDanger=None, themeCritical=None): def __init__(self, theme=None):
AlertingSection.__init__(self, theme) AlertingSection.__init__(self, theme)
PeriodicUpdater.__init__(self) PeriodicUpdater.__init__(self)
self.changeInterval(1) self.changeInterval(1)
class RamProvider(Section, PeriodicUpdater): class RamProvider(AlertingSection, PeriodicUpdater):
# TODO Use AlertingSection
""" """
Shows free RAM Shows free RAM
""" """
NUMBER_STATES = 4
ICON = ''
def fetcher(self): def fetcher(self):
mem = psutil.virtual_memory() mem = psutil.virtual_memory()
ramp = Section.ramp(1-mem.percent/100) freePerc = 1-mem.percent/100
free = humanSize(mem.available) self.updateLevel(freePerc)
theme = self.themeCritical if mem.percent >= 90 else \
(self.themeDanger if mem.percent >= 75 else self.themeNormal)
self.updateTheme(theme)
return '{}{}'.format(ramp, free)
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) 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 TemperatureProvider(Section, PeriodicUpdater):
# TODO Use AlertingSection
class TemperatureProvider(AlertingSection, PeriodicUpdater):
NUMBER_STATES = 2
RAMP = "" RAMP = ""
def fetcher(self): def fetcher(self):
allTemp = psutil.sensors_temperatures() allTemp = psutil.sensors_temperatures()
if 'coretemp' not in allTemp: if 'coretemp' not in allTemp:
# TODO Opti Remove interval # TODO Opti Remove interval
return '' return ''
temp = allTemp['coretemp'][0] temp = allTemp['coretemp'][0]
theme = self.themeCritical if temp.current >= temp.critical else \ self.warningThresold = temp.high
(self.themeDanger if temp.current >= temp.high self.dangerThresold = temp.critical
else self.themeNormal) self.updateLevel(temp.current)
self.updateTheme(theme)
ramp = Section.ramp(temp.current/temp.high, self.RAMP) self.icon = Section.ramp(temp.current/temp.high, self.RAMP)
return '{} {:.0f}°C'.format(ramp, temp.current) 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) 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) self.changeInterval(5)
class BatteryProvider(Section, PeriodicUpdater): class BatteryProvider(AlertingSection, PeriodicUpdater):
# TODO Use AlertingSection NUMBER_STATES = 2
RAMP = "" RAMP = ""
def fetcher(self): def fetcher(self):
bat = psutil.sensors_battery() bat = psutil.sensors_battery()
text = ''
if not bat: if not bat:
return text self.icon = None
if bat.power_plugged: return None
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
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) 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) self.changeInterval(5)
class PulseaudioProvider(Section, ThreadedUpdater): class PulseaudioProvider(Section, ThreadedUpdater):
def __init__(self, theme=None): def __init__(self, theme=None):
ThreadedUpdater.__init__(self) ThreadedUpdater.__init__(self)
@ -227,7 +229,7 @@ class PulseaudioProvider(Section, ThreadedUpdater):
class NetworkProviderSection(StatefulSection, Updater): class NetworkProviderSection(StatefulSection, Updater):
NUMBER_STATES = 4 NUMBER_STATES = 5
def actType(self): def actType(self):
self.ssid = None self.ssid = None
@ -269,6 +271,7 @@ class NetworkProviderSection(StatefulSection, Updater):
return None return None
text = [] text = []
self.persistent = True
self.actType() self.actType()
if self.showSsid and self.ssid: if self.showSsid and self.ssid:
@ -302,10 +305,10 @@ class NetworkProviderSection(StatefulSection, Updater):
return ' '.join(text) return ' '.join(text)
def onChangeState(self, state): def onChangeState(self, state):
self.showSsid = state >= 0 self.showSsid = state >= 1
self.showAddress = state >= 1 self.showAddress = state >= 2
self.showSpeed = state >= 2 self.showSpeed = state >= 3
self.showTransfer = state >= 3 self.showTransfer = state >= 4
def __init__(self, iface, parent): def __init__(self, iface, parent):
Updater.__init__(self) Updater.__init__(self)
@ -408,7 +411,7 @@ class KeystoreProvider(Section, MergedUpdater):
MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider()) MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider())
Section.__init__(self, theme) Section.__init__(self, theme)
class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater): class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
# TODO OPTI Transform InotifyUpdater (watching notmuch folder should be # TODO OPTI Transform InotifyUpdater (watching notmuch folder should be
# enough) # enough)
COLORABLE_ICON = '' COLORABLE_ICON = ''
@ -444,7 +447,7 @@ class NotmuchUnreadProvider(ColorCountsSection, PeriodicUpdater):
color = f.read().strip() color = f.read().strip()
self.colors[account] = color self.colors[account] = color
self.changeInterval(10) self.addPath(os.path.join(self.dir, '.notmuch', 'xapian'))
class TodoProvider(ColorCountsSection, InotifyUpdater): class TodoProvider(ColorCountsSection, InotifyUpdater):
@ -453,12 +456,24 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
COLORABLE_ICON = '' COLORABLE_ICON = ''
def updateCalendarList(self): def updateCalendarList(self):
print(459)
calendars = sorted(os.listdir(self.dir)) calendars = sorted(os.listdir(self.dir))
for calendar in calendars: for calendar in calendars:
# If the calendar wasn't in the list # If the calendar wasn't in the list
if calendar not in self.calendars: if calendar not in self.calendars:
self.addPath(os.path.join(self.dir, calendar), refresh=False) 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 self.calendars = calendars
print(475, self.calendars)
def __init__(self, dir, theme=None): def __init__(self, dir, theme=None):
""" """
@ -470,34 +485,37 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
assert os.path.isdir(self.dir) assert os.path.isdir(self.dir)
self.calendars = [] self.calendars = []
self.addPath(self.dir) self.colors = dict()
self.names = dict()
def getName(self, calendar): self.updateCalendarList()
path = os.path.join(self.dir, calendar, 'displayname') self.refreshData()
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
def countUndone(self, calendar): 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) proc = subprocess.run(cmd, stdout=subprocess.PIPE)
data = json.loads(proc.stdout) data = json.loads(proc.stdout)
return len(data) return len(data)
def subfetcher(self): def subfetcher(self):
counts = [] 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: for calendar in self.calendars:
c = self.countUndone(calendar) c = self.countUndone(calendar)
if c <= 0: if c <= 0:
continue continue
counts.append((c, self.getColor(calendar))) counts.append((c, self.colors[calendar]))
return counts return counts
class I3WindowTitleProvider(Section, I3Updater): class I3WindowTitleProvider(Section, I3Updater):
@ -512,22 +530,42 @@ class I3WindowTitleProvider(Section, I3Updater):
Section.__init__(self, theme=theme) Section.__init__(self, theme=theme)
self.on("window", self.on_window) 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): class I3WorkspacesProvider(Section, I3Updater):
# TODO Multi-screen # 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): def initialPopulation(self, parent):
""" """
Called on init Called on init
@ -539,13 +577,14 @@ class I3WorkspacesProvider(Section, I3Updater):
for workspace in workspaces: for workspace in workspaces:
# if parent.display != workspace["display"]: # if parent.display != workspace["display"]:
# continue # continue
theme = self.themeFocus if workspace["focused"] \
else (self.themeUrgent if workspace["urgent"] section = I3WorkspacesProviderSection(workspace["name"], self)
else self.theme) section.focused = workspace["focused"]
section = Section(theme=theme) section.urgent = workspace["urgent"]
section.show()
parent.addSectionAfter(lastSection, section) parent.addSectionAfter(lastSection, section)
self.setName(section, workspace["name"])
self.sections[workspace["num"]] = section self.sections[workspace["num"]] = section
lastSection = section lastSection = section
def on_workspace_init(self, i3, e): def on_workspace_init(self, i3, e):
@ -554,45 +593,52 @@ class I3WorkspacesProvider(Section, I3Updater):
if i in self.sections: if i in self.sections:
section = self.sections[i] section = self.sections[i]
else: else:
# Find the section just before
while i not in self.sections.keys() and i > 0: while i not in self.sections.keys() and i > 0:
i -= 1 i -= 1
prevSection = self.sections[i] if i != 0 else self.modeSection 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) 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): def on_workspace_empty(self, i3, e):
workspace = e.current self.sections[e.current.num].empty()
section = self.sections[workspace.num]
self.setName(section, None)
def on_workspace_focus(self, i3, e): def on_workspace_focus(self, i3, e):
self.sections[e.current.num].updateTheme(self.themeFocus) self.sections[e.old.num].focused = False
self.sections[e.old.num].updateTheme(self.theme) 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): 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): 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): def on_mode(self, i3, e):
if e.change == 'default': if e.change == 'default':
self.modeSection.updateText('') self.modeSection.updateText(None)
for section in self.sections.values(): for section in self.sections.values():
section.updateText(section.fullName) section.show()
else: else:
self.modeSection.updateText(e.change) self.modeSection.updateText(e.change)
for section in self.sections.values(): 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) I3Updater.__init__(self)
Section.__init__(self) Section.__init__(self)
self.theme = theme self.themeNormal = theme
self.themeFocus = themeFocus self.themeFocus = themeFocus
self.themeUrgent = themeUrgent self.themeUrgent = themeUrgent
self.customNames = customNames
self.sections = dict() self.sections = dict()
self.on("workspace::init", self.on_workspace_init) self.on("workspace::init", self.on_workspace_init)

View file

@ -235,9 +235,8 @@ class MergedUpdater(Updater):
text = Text() text = Text()
for updater in self.updaters: for updater in self.updaters:
text.append(self.texts[updater]) text.append(self.texts[updater])
# TODO OPTI After Text.__len__ if not len(text):
# if not len(text): return None
# return None
return text return text
def __init__(self, *args): def __init__(self, *args):