Lemonbar new!
This commit is contained in:
parent
ca47c8a8a2
commit
8857158edb
262
config/lemonbar/new.py
Executable file
262
config/lemonbar/new.py
Executable file
|
@ -0,0 +1,262 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import enum
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
# TODO Update strategies (periodic, inotify file)
|
||||||
|
# TODO Section order (priority system maybe ?)
|
||||||
|
# TODO Allow deletion of Bar, BarGroup and Section for screen changes
|
||||||
|
# TODO Optimize to use write() calls instead of string concatenation (writing
|
||||||
|
# BarGroup strings should be a good compromise)
|
||||||
|
|
||||||
|
|
||||||
|
class BarGroupType(enum.Enum):
|
||||||
|
LEFT = 0
|
||||||
|
RIGHT = 1
|
||||||
|
# MID_LEFT = 2
|
||||||
|
# MID_RIGHT = 3
|
||||||
|
|
||||||
|
|
||||||
|
class Bar:
|
||||||
|
"""
|
||||||
|
One bar for each screen
|
||||||
|
"""
|
||||||
|
|
||||||
|
everyone = set()
|
||||||
|
string = ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init():
|
||||||
|
Section.init()
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
Bar()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.screen = 0 # TODO
|
||||||
|
self.groups = dict()
|
||||||
|
|
||||||
|
for groupType in BarGroupType:
|
||||||
|
group = BarGroup(groupType, self)
|
||||||
|
self.groups[groupType] = group
|
||||||
|
|
||||||
|
self.childsChanged = False
|
||||||
|
|
||||||
|
self.everyone.add(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def addSectionAll(section, group, screens=None):
|
||||||
|
"""
|
||||||
|
.. note::
|
||||||
|
Add the section before updating it for the first time.
|
||||||
|
"""
|
||||||
|
assert isinstance(section, Section)
|
||||||
|
assert isinstance(group, BarGroupType)
|
||||||
|
# TODO screens selection
|
||||||
|
for bar in Bar.everyone:
|
||||||
|
bar.addSection(section, group=group)
|
||||||
|
|
||||||
|
def addSection(self, section, group):
|
||||||
|
assert isinstance(section, Section)
|
||||||
|
assert isinstance(group, BarGroupType)
|
||||||
|
self.groups[group].addSection(section)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self.childsChanged:
|
||||||
|
self.string = ""
|
||||||
|
|
||||||
|
self.childsChanged = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def updateAll():
|
||||||
|
|
||||||
|
for bar in Bar.everyone:
|
||||||
|
bar.update()
|
||||||
|
|
||||||
|
Bar.string = ""
|
||||||
|
|
||||||
|
|
||||||
|
class BarGroup:
|
||||||
|
"""
|
||||||
|
One for each group of each bar
|
||||||
|
"""
|
||||||
|
|
||||||
|
everyone = set()
|
||||||
|
|
||||||
|
def __init__(self, groupType, parent):
|
||||||
|
assert isinstance(groupType, BarGroupType)
|
||||||
|
assert isinstance(parent, Bar)
|
||||||
|
|
||||||
|
self.groupType = groupType
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
self.sections = list()
|
||||||
|
self.string = ''
|
||||||
|
self.form = ''
|
||||||
|
|
||||||
|
#: One of the sections that had their theme or visibility changed
|
||||||
|
self.childsThemeChanged = False
|
||||||
|
|
||||||
|
#: One of the sections that had their text (maybe their size) changed
|
||||||
|
self.childsTextChanged = False
|
||||||
|
|
||||||
|
BarGroup.everyone.add(self)
|
||||||
|
|
||||||
|
def addSection(self, section):
|
||||||
|
self.sections.append(section)
|
||||||
|
section.parents.add(self)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self.childsThemeChanged:
|
||||||
|
# TODO
|
||||||
|
self.form = ">".join([sec.curText for sec in self.sections
|
||||||
|
if sec.visible])
|
||||||
|
|
||||||
|
if self.childsTextChanged or self.childsThemeChanged:
|
||||||
|
print("118")
|
||||||
|
self.string = ">".join([sec.curText for sec in self.sections
|
||||||
|
if sec.visible])
|
||||||
|
print("|{0}|".format(self.string))
|
||||||
|
|
||||||
|
self.parent.childsChanged = True
|
||||||
|
|
||||||
|
self.childsThemeChanged = False
|
||||||
|
self.childsTextChanged = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def updateAll():
|
||||||
|
|
||||||
|
print("130")
|
||||||
|
|
||||||
|
for group in BarGroup.everyone:
|
||||||
|
group.update()
|
||||||
|
|
||||||
|
Bar.updateAll()
|
||||||
|
|
||||||
|
|
||||||
|
class SectionThread(threading.Thread):
|
||||||
|
def run(self):
|
||||||
|
while Section.somethingChanged.wait():
|
||||||
|
Section.updateAll()
|
||||||
|
while len(Section.sizeChanging) > 0:
|
||||||
|
time.sleep(0.1)
|
||||||
|
Section.updateAll()
|
||||||
|
|
||||||
|
|
||||||
|
class Section:
|
||||||
|
|
||||||
|
#: Sections that do not have their destination size
|
||||||
|
sizeChanging = set()
|
||||||
|
somethingChanged = threading.Event()
|
||||||
|
updateThread = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init():
|
||||||
|
Section.updateThread = SectionThread(daemon=True)
|
||||||
|
Section.updateThread.start()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
#: Displayed section
|
||||||
|
#: Note: A section can be empty and displayed!
|
||||||
|
self.visible = False
|
||||||
|
|
||||||
|
#: Displayed text
|
||||||
|
self.curText = ''
|
||||||
|
#: Displayed text size
|
||||||
|
self.curSize = 0
|
||||||
|
|
||||||
|
#: Destination text
|
||||||
|
self.dstText = ''
|
||||||
|
#: Destination size
|
||||||
|
self.dstSize = 0
|
||||||
|
|
||||||
|
#: Groups that have this section
|
||||||
|
self.parents = set()
|
||||||
|
|
||||||
|
def informParentsThemeChanged(self):
|
||||||
|
for parent in self.parents:
|
||||||
|
parent.childsThemeChanged = True
|
||||||
|
|
||||||
|
def informParentsTextChanged(self):
|
||||||
|
for parent in self.parents:
|
||||||
|
parent.childsTextChanged = True
|
||||||
|
|
||||||
|
def updateText(self, text):
|
||||||
|
if len(text):
|
||||||
|
self.dstText = ' {} '.format(text)
|
||||||
|
self.dstSize = len(self.dstText)
|
||||||
|
else:
|
||||||
|
self.dstSize = 0
|
||||||
|
|
||||||
|
if self.curSize == self.dstSize:
|
||||||
|
self.curText = self.dstText
|
||||||
|
self.informParentsTextChanged()
|
||||||
|
else:
|
||||||
|
Section.sizeChanging.add(self)
|
||||||
|
Section.somethingChanged.set()
|
||||||
|
|
||||||
|
def updateVisibility(self, visibility):
|
||||||
|
assert isinstance(visibility, bool)
|
||||||
|
|
||||||
|
self.visible = visibility
|
||||||
|
self.informParentsThemeChanged()
|
||||||
|
Section.somethingChanged.set()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fit(text, size):
|
||||||
|
t = len(text)
|
||||||
|
return text[:size] if t >= size else text + " " * (size - t)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
# TODO Might profit of a better logic
|
||||||
|
if not self.visible:
|
||||||
|
self.updateVisibility(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.dstSize > self.curSize:
|
||||||
|
self.curSize += 1
|
||||||
|
elif self.dstSize < self.curSize:
|
||||||
|
self.curSize -= 1
|
||||||
|
else:
|
||||||
|
# Visibility toggling must be done one step after curSize = 0
|
||||||
|
if self.dstSize == 0:
|
||||||
|
self.updateVisibility(False)
|
||||||
|
Section.sizeChanging.remove(self)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.curText = Section.fit(self.dstText, self.curSize)
|
||||||
|
self.informParentsTextChanged()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def updateAll():
|
||||||
|
"""
|
||||||
|
Process all sections for text size changes
|
||||||
|
"""
|
||||||
|
|
||||||
|
for sizeChanging in Section.sizeChanging.copy():
|
||||||
|
sizeChanging.update()
|
||||||
|
# print("{1:3d} |{0}|".format(sizeChanging.curText, sizeChanging.curSize))
|
||||||
|
|
||||||
|
BarGroup.updateAll()
|
||||||
|
|
||||||
|
Section.somethingChanged.clear()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
Bar.init()
|
||||||
|
sec = Section()
|
||||||
|
sec1 = Section()
|
||||||
|
Bar.addSectionAll(sec, BarGroupType.LEFT)
|
||||||
|
Bar.addSectionAll(sec1, BarGroupType.LEFT)
|
||||||
|
|
||||||
|
sec.updateText("Hello")
|
||||||
|
# sec1.updateText("world!")
|
||||||
|
time.sleep(2)
|
||||||
|
sec.updateText("Salut")
|
||||||
|
# sec1.updateText("le monde !")
|
||||||
|
time.sleep(2)
|
||||||
|
sec.updateText("")
|
||||||
|
# sec1.updateText("")
|
||||||
|
time.sleep(2)
|
|
@ -15,44 +15,53 @@ import difflib
|
||||||
# Constants
|
# Constants
|
||||||
FONT = "DejaVu Sans Mono for Powerline"
|
FONT = "DejaVu Sans Mono for Powerline"
|
||||||
|
|
||||||
thm = ['#002b36','#dc322f','#859900','#b58900','#268bd2','#6c71c4','#2aa198','#93a1a1','#657b83','#dc322f','#859900','#b58900','#268bd2','#6c71c4','#2aa198','#fdf6e3']
|
# TODO Update to be in sync with base16
|
||||||
|
thm = ['#002b36', '#dc322f', '#859900', '#b58900', '#268bd2', '#6c71c4',
|
||||||
|
'#2aa198', '#93a1a1', '#657b83', '#dc322f', '#859900', '#b58900',
|
||||||
|
'#268bd2', '#6c71c4', '#2aa198', '#fdf6e3']
|
||||||
fg = '#93a1a1'
|
fg = '#93a1a1'
|
||||||
bg = '#002b36'
|
bg = '#002b36'
|
||||||
|
|
||||||
THEMES = {
|
THEMES = {
|
||||||
'CENTER': (fg, bg),
|
'CENTER': (fg, bg),
|
||||||
'DEFAULT': (thm[0], thm[8]),
|
'DEFAULT': (thm[0], thm[8]),
|
||||||
'1': (thm[0], thm[9]),
|
'1': (thm[0], thm[9]),
|
||||||
'2': (thm[0], thm[10]),
|
'2': (thm[0], thm[10]),
|
||||||
'3': (thm[0], thm[11]),
|
'3': (thm[0], thm[11]),
|
||||||
'4': (thm[0], thm[12]),
|
'4': (thm[0], thm[12]),
|
||||||
'5': (thm[0], thm[13]),
|
'5': (thm[0], thm[13]),
|
||||||
'6': (thm[0], thm[14]),
|
'6': (thm[0], thm[14]),
|
||||||
'7': (thm[0], thm[15]),
|
'7': (thm[0], thm[15]),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Utils
|
# Utils
|
||||||
|
|
||||||
|
|
||||||
def fitText(text, size):
|
def fitText(text, size):
|
||||||
"""
|
"""
|
||||||
Add spaces or cut a string to be `size` characters long
|
Add spaces or cut a string to be `size` characters long
|
||||||
"""
|
"""
|
||||||
if size > 0:
|
if size > 0:
|
||||||
if len(text) >= size:
|
t = len(text)
|
||||||
|
if t >= size:
|
||||||
return text[:size]
|
return text[:size]
|
||||||
else:
|
else:
|
||||||
return ('%' + str(size) + 's') % text
|
diff = size - t
|
||||||
|
return text + " " * diff
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def fgColor(theme):
|
def fgColor(theme):
|
||||||
global THEMES
|
global THEMES
|
||||||
return THEMES[theme][0]
|
return THEMES[theme][0]
|
||||||
|
|
||||||
|
|
||||||
def bgColor(theme):
|
def bgColor(theme):
|
||||||
global THEMES
|
global THEMES
|
||||||
return THEMES[theme][1]
|
return THEMES[theme][1]
|
||||||
|
|
||||||
|
|
||||||
class Section:
|
class Section:
|
||||||
def __init__(self, theme='DEFAULT'):
|
def __init__(self, theme='DEFAULT'):
|
||||||
self.text = ''
|
self.text = ''
|
||||||
|
@ -108,12 +117,15 @@ class Section:
|
||||||
s += ''
|
s += ''
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
# Section definition
|
# Section definition
|
||||||
sTime = Section('3')
|
sTime = Section('3')
|
||||||
|
|
||||||
hostname = os.environ['HOSTNAME'].split('.')[0]
|
hostname = os.environ['HOSTNAME'].split('.')[0]
|
||||||
sHost = Section('2')
|
sHost = Section('2')
|
||||||
sHost.update(os.environ['USER'] + '@' + hostname.split('-')[-1] if '-' in hostname else hostname)
|
sHost.update(
|
||||||
|
os.environ['USER'] + '@' + hostname.split('-')[-1]
|
||||||
|
if '-' in hostname else hostname)
|
||||||
|
|
||||||
|
|
||||||
# Groups definition
|
# Groups definition
|
||||||
|
@ -123,6 +135,7 @@ gRight = [sTime, sHost]
|
||||||
# Bar handling
|
# Bar handling
|
||||||
bar = subprocess.Popen(['lemonbar', '-f', FONT, '-b'], stdin=subprocess.PIPE)
|
bar = subprocess.Popen(['lemonbar', '-f', FONT, '-b'], stdin=subprocess.PIPE)
|
||||||
|
|
||||||
|
|
||||||
def updateBar():
|
def updateBar():
|
||||||
global timeLastUpdate, timeUpdate
|
global timeLastUpdate, timeUpdate
|
||||||
global gLeft, gRight
|
global gLeft, gRight
|
||||||
|
@ -131,37 +144,55 @@ def updateBar():
|
||||||
text = ''
|
text = ''
|
||||||
for oi in range(len(outputs)):
|
for oi in range(len(outputs)):
|
||||||
output = outputs[oi]
|
output = outputs[oi]
|
||||||
gLeftFiltered = list(filter(lambda s: s.visible and (not s.output or s.output == output.name), gLeft))
|
gLeftFiltered = list(
|
||||||
|
filter(
|
||||||
|
lambda s: s.visible and (
|
||||||
|
not s.output or s.output == output.name),
|
||||||
|
gLeft))
|
||||||
tLeft = ''
|
tLeft = ''
|
||||||
l = len(gLeftFiltered)
|
l = len(gLeftFiltered)
|
||||||
for gi in range(l):
|
for gi in range(l):
|
||||||
g = gLeftFiltered[gi]
|
g = gLeftFiltered[gi]
|
||||||
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else 'CENTER' # Next visible section for transition
|
# Next visible section for transition
|
||||||
|
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else 'CENTER'
|
||||||
tLeft = tLeft + g.draw(True, nextTheme)
|
tLeft = tLeft + g.draw(True, nextTheme)
|
||||||
|
|
||||||
tRight = ''
|
tRight = ''
|
||||||
for gi in range(len(gRight)):
|
for gi in range(len(gRight)):
|
||||||
g = gRight[gi]
|
g = gRight[gi]
|
||||||
nextTheme = 'CENTER'
|
nextTheme = 'CENTER'
|
||||||
for gn in gRight[gi+1:]:
|
for gn in gRight[gi + 1:]:
|
||||||
if gn.visible:
|
if gn.visible:
|
||||||
nextTheme = gn.theme
|
nextTheme = gn.theme
|
||||||
break
|
break
|
||||||
tRight = g.draw(False, nextTheme) + tRight
|
tRight = g.draw(False, nextTheme) + tRight
|
||||||
text += '%{l}' + tLeft + '%{r}' + tRight + '%{B' + bgColor('CENTER') + '}' + '%{S' + str(oi + 1) + '}'
|
text += '%{l}' + tLeft + '%{r}' + tRight + \
|
||||||
|
'%{B' + bgColor('CENTER') + '}' + '%{S' + str(oi + 1) + '}'
|
||||||
|
|
||||||
bar.stdin.write(bytes(text + '\n', 'utf-8'))
|
bar.stdin.write(bytes(text + '\n', 'utf-8'))
|
||||||
bar.stdin.flush()
|
bar.stdin.flush()
|
||||||
|
|
||||||
|
|
||||||
# Values
|
# Values
|
||||||
i3 = i3ipc.Connection()
|
i3 = i3ipc.Connection()
|
||||||
outputs = []
|
outputs = []
|
||||||
|
|
||||||
|
|
||||||
def on_output():
|
def on_output():
|
||||||
global outputs
|
global outputs
|
||||||
outputs = sorted(sorted(list(filter(lambda o: o.active, i3.get_outputs())), key=lambda o: o.rect.y), key=lambda o: o.rect.x)
|
outputs = sorted(
|
||||||
|
sorted(
|
||||||
|
list(
|
||||||
|
filter(
|
||||||
|
lambda o: o.active,
|
||||||
|
i3.get_outputs())),
|
||||||
|
key=lambda o: o.rect.y),
|
||||||
|
key=lambda o: o.rect.x)
|
||||||
|
|
||||||
|
|
||||||
on_output()
|
on_output()
|
||||||
|
|
||||||
|
|
||||||
def on_workspace_focus():
|
def on_workspace_focus():
|
||||||
global i3
|
global i3
|
||||||
global gLeft
|
global gLeft
|
||||||
|
@ -170,6 +201,7 @@ def on_workspace_focus():
|
||||||
sNames = [s.name for s in gLeft]
|
sNames = [s.name for s in gLeft]
|
||||||
|
|
||||||
newGLeft = []
|
newGLeft = []
|
||||||
|
|
||||||
def actuate(section, workspace):
|
def actuate(section, workspace):
|
||||||
if workspace:
|
if workspace:
|
||||||
section.name = workspace.name
|
section.name = workspace.name
|
||||||
|
@ -189,21 +221,22 @@ def on_workspace_focus():
|
||||||
section.update('')
|
section.update('')
|
||||||
section.theme = '6'
|
section.theme = '6'
|
||||||
|
|
||||||
for tag, i, j, k, l in difflib.SequenceMatcher(None, sNames, wNames).get_opcodes():
|
for tag, i, j, k, l in difflib.SequenceMatcher(
|
||||||
if tag == 'equal': # If the workspaces didn't changed
|
None, sNames, wNames).get_opcodes():
|
||||||
for a in range(j-i):
|
if tag == 'equal': # If the workspaces didn't changed
|
||||||
workspace = workspaces[k+a]
|
for a in range(j - i):
|
||||||
section = gLeft[i+a]
|
workspace = workspaces[k + a]
|
||||||
|
section = gLeft[i + a]
|
||||||
actuate(section, workspace)
|
actuate(section, workspace)
|
||||||
newGLeft.append(section)
|
newGLeft.append(section)
|
||||||
if tag in ('delete', 'replace'): # If the workspaces were removed
|
if tag in ('delete', 'replace'): # If the workspaces were removed
|
||||||
for section in gLeft[i:j]:
|
for section in gLeft[i:j]:
|
||||||
if section.visible:
|
if section.visible:
|
||||||
actuate(section, None)
|
actuate(section, None)
|
||||||
newGLeft.append(section)
|
newGLeft.append(section)
|
||||||
else:
|
else:
|
||||||
del section
|
del section
|
||||||
if tag in ('insert', 'replace'): # If the workspaces were removed
|
if tag in ('insert', 'replace'): # If the workspaces were removed
|
||||||
for workspace in workspaces[k:l]:
|
for workspace in workspaces[k:l]:
|
||||||
section = Section()
|
section = Section()
|
||||||
actuate(section, workspace)
|
actuate(section, workspace)
|
||||||
|
@ -211,6 +244,8 @@ def on_workspace_focus():
|
||||||
gLeft = newGLeft
|
gLeft = newGLeft
|
||||||
|
|
||||||
updateBar()
|
updateBar()
|
||||||
|
|
||||||
|
|
||||||
on_workspace_focus()
|
on_workspace_focus()
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,20 +265,24 @@ def i3events(i3childPipe):
|
||||||
|
|
||||||
i3.main()
|
i3.main()
|
||||||
|
|
||||||
|
|
||||||
i3parentPipe, i3childPipe = multiprocessing.Pipe()
|
i3parentPipe, i3childPipe = multiprocessing.Pipe()
|
||||||
i3process = multiprocessing.Process(target=i3events, args=(i3childPipe,))
|
i3process = multiprocessing.Process(target=i3events, args=(i3childPipe,))
|
||||||
i3process.start()
|
i3process.start()
|
||||||
|
|
||||||
|
|
||||||
def updateValues():
|
def updateValues():
|
||||||
# Time
|
# Time
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
sTime.update(now.strftime('%x %X'))
|
sTime.update(now.strftime('%x %X'))
|
||||||
|
|
||||||
|
|
||||||
def updateAnimation():
|
def updateAnimation():
|
||||||
for s in set(gLeft + gRight):
|
for s in set(gLeft + gRight):
|
||||||
s.updateSize()
|
s.updateSize()
|
||||||
updateBar()
|
updateBar()
|
||||||
|
|
||||||
|
|
||||||
lastUpdate = 0
|
lastUpdate = 0
|
||||||
while True:
|
while True:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
@ -262,5 +301,3 @@ while True:
|
||||||
lastUpdate = now
|
lastUpdate = now
|
||||||
|
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue