Run black on all Python scripts!

This commit is contained in:
Geoffrey Frogeye 2021-06-13 11:49:21 +02:00
parent fb6cfce656
commit cd9cbcaa28
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8
30 changed files with 1027 additions and 704 deletions

View file

@ -11,14 +11,23 @@ if __name__ == "__main__":
WORKSPACE_THEME = 0
FOCUS_THEME = 3
URGENT_THEME = 1
CUSTOM_SUFFIXES = '▲■'
CUSTOM_SUFFIXES = "▲■"
customNames = dict()
for i in range(len(CUSTOM_SUFFIXES)):
short = str(i+1)
full = short + ' ' + CUSTOM_SUFFIXES[i]
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)
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)

View file

@ -11,7 +11,7 @@ import logging
import coloredlogs
import updaters
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
log = logging.getLogger()
@ -62,12 +62,13 @@ class Bar:
Bar.running = True
Section.init()
cmd = ['lemonbar', '-b', '-a', '64']
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,
stdout=subprocess.PIPE)
Bar.stdoutThread = BarStdoutThread()
Bar.process = subprocess.Popen(
cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
Bar.stdoutThread = BarStdoutThread()
Bar.stdoutThread.start()
# Debug
@ -92,7 +93,7 @@ class Bar:
print(88)
try:
i3.on('ipc_shutdown', doStop)
i3.on("ipc_shutdown", doStop)
i3.main()
except BaseException:
print(93)
@ -114,7 +115,7 @@ class Bar:
if function in Bar.actionsF2H.keys():
return Bar.actionsF2H[function]
handle = '{:x}'.format(Bar.nextHandle).encode()
handle = "{:x}".format(Bar.nextHandle).encode()
Bar.nextHandle += 1
Bar.actionsF2H[function] = handle
@ -177,7 +178,7 @@ class Bar:
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()
@ -196,7 +197,7 @@ class BarGroup:
self.parent = parent
self.sections = list()
self.string = ''
self.string = ""
self.parts = []
#: One of the sections that had their theme or visibility changed
@ -220,11 +221,11 @@ class BarGroup:
@staticmethod
def fgColor(color):
return "%{F" + (color or '-') + "}"
return "%{F" + (color or "-") + "}"
@staticmethod
def bgColor(color):
return "%{B" + (color or '-') + "}"
return "%{B" + (color or "-") + "}"
@staticmethod
def color(fg, bg):
@ -243,8 +244,9 @@ class BarGroup:
oSec = secs[s + 1] if s < lenS - 1 else None
else:
oSec = secs[s - 1] if s > 0 else None
oTheme = Section.THEMES[oSec.theme] \
if oSec is not None else Section.EMPTY
oTheme = (
Section.THEMES[oSec.theme] if oSec is not None else Section.EMPTY
)
if self.groupType == BarGroupType.LEFT:
if s == 0:
@ -263,7 +265,6 @@ class BarGroup:
parts.append(BarGroup.color(*theme))
parts.append(sec)
# TODO OPTI Concatenate successive strings
self.parts = parts
@ -315,11 +316,26 @@ class Section:
# COLORS = ['#272822', '#383830', '#49483e', '#75715e', '#a59f85', '#f8f8f2',
# '#f5f4f1', '#f9f8f5', '#f92672', '#fd971f', '#f4bf75', '#a6e22e',
# '#a1efe4', '#66d9ef', '#ae81ff', '#cc6633']
COLORS = ['#181818', '#AB4642', '#A1B56C', '#F7CA88', '#7CAFC2', '#BA8BAF',
'#86C1B9', '#D8D8D8', '#585858', '#AB4642', '#A1B56C', '#F7CA88',
'#7CAFC2', '#BA8BAF', '#86C1B9', '#F8F8F8']
FGCOLOR = '#F8F8F2'
BGCOLOR = '#272822'
COLORS = [
"#181818",
"#AB4642",
"#A1B56C",
"#F7CA88",
"#7CAFC2",
"#BA8BAF",
"#86C1B9",
"#D8D8D8",
"#585858",
"#AB4642",
"#A1B56C",
"#F7CA88",
"#7CAFC2",
"#BA8BAF",
"#86C1B9",
"#F8F8F8",
]
FGCOLOR = "#F8F8F2"
BGCOLOR = "#272822"
THEMES = list()
EMPTY = (FGCOLOR, BGCOLOR)
@ -347,17 +363,18 @@ class Section:
if theme is None:
theme = Section.lastChosenTheme
Section.lastChosenTheme = (Section.lastChosenTheme + 1) \
% len(Section.THEMES)
Section.lastChosenTheme = (Section.lastChosenTheme + 1) % len(
Section.THEMES
)
self.theme = theme
#: Displayed text
self.curText = ''
self.curText = ""
#: Displayed text size
self.curSize = 0
#: Destination text
self.dstText = Text(' ', Text(), ' ')
self.dstText = Text(" ", Text(), " ")
#: Destination size
self.dstSize = 0
@ -367,13 +384,16 @@ class Section:
self.icon = self.ICON
self.persistent = self.PERSISTENT
def __str__(self):
try:
return "<{}><{}>{:01d}{}{:02d}/{:02d}" \
.format(self.curText, self.dstText,
self.theme, "+" if self.visible else "-",
self.curSize, self.dstSize)
return "<{}><{}>{:01d}{}{:02d}/{:02d}".format(
self.curText,
self.dstText,
self.theme,
"+" if self.visible else "-",
self.curSize,
self.dstSize,
)
except:
return super().__str__()
@ -399,9 +419,15 @@ class Section:
elif isinstance(text, Text) and not len(text.elements):
text = None
self.dstText[0] = None if (text is None and not self.persistent) else ((' ' + self.icon + ' ') if self.icon else ' ')
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
self.dstText[2] = (
" " if self.dstText[1] is not None and len(self.dstText[1]) else None
)
self.dstSize = len(self.dstText)
self.dstText.setSection(self)
@ -481,7 +507,7 @@ class Section:
elif p < 0:
return ramp[0]
else:
return ramp[round(p * (len(ramp)-1))]
return ramp[round(p * (len(ramp) - 1))]
class StatefulSection(Section):
@ -492,10 +518,11 @@ class StatefulSection(Section):
def __init__(self, *args, **kwargs):
Section.__init__(self, *args, **kwargs)
self.state = self.DEFAULT_STATE
if hasattr(self, 'onChangeState'):
if hasattr(self, "onChangeState"):
self.onChangeState(self.state)
self.setDecorators(clickLeft=self.incrementState,
clickRight=self.decrementState)
self.setDecorators(
clickLeft=self.incrementState, clickRight=self.decrementState
)
def incrementState(self):
newState = min(self.state + 1, self.NUMBER_STATES - 1)
@ -509,16 +536,17 @@ class StatefulSection(Section):
assert isinstance(state, int)
assert state < self.NUMBER_STATES
self.state = state
if hasattr(self, 'onChangeState'):
if hasattr(self, "onChangeState"):
self.onChangeState(state)
self.refreshData()
class ColorCountsSection(StatefulSection):
# TODO FEAT Blend colors when not expanded
# TODO FEAT Blend colors with importance of count
# TODO FEAT Allow icons instead of counts
NUMBER_STATES = 3
COLORABLE_ICON = '?'
COLORABLE_ICON = "?"
def __init__(self, theme=None):
StatefulSection.__init__(self, theme=theme)
@ -538,12 +566,12 @@ class ColorCountsSection(StatefulSection):
# Icon + Total
elif self.state == 1 and len(counts) > 1:
total = sum([count for count, color in counts])
return Text(self.COLORABLE_ICON, ' ', total)
return Text(self.COLORABLE_ICON, " ", total)
# Icon + Counts
else:
text = Text(self.COLORABLE_ICON)
for count, color in counts:
text.append(' ', Text(count, fg=color))
text.append(" ", Text(count, fg=color))
return text
@ -586,12 +614,12 @@ class Text:
if self.prefix is not None and self.suffix is not None:
return
self.prefix = ''
self.suffix = ''
self.prefix = ""
self.suffix = ""
def nest(prefix, suffix):
self.prefix = self.prefix + '%{' + prefix + '}'
self.suffix = '%{' + suffix + '}' + self.suffix
self.prefix = self.prefix + "%{" + prefix + "}"
self.suffix = "%{" + suffix + "}" + self.suffix
def getColor(val):
# TODO Allow themes
@ -600,27 +628,27 @@ class Text:
def button(number, function):
handle = Bar.getFunctionHandle(function)
nest('A' + number + ':' + handle.decode() + ':', 'A' + number)
nest("A" + number + ":" + handle.decode() + ":", "A" + number)
for key, val in self.decorators.items():
if val is None:
continue
if key == 'fg':
if key == "fg":
reset = self.section.THEMES[self.section.theme][0]
nest('F' + getColor(val), 'F' + reset)
elif key == 'bg':
nest("F" + getColor(val), "F" + reset)
elif key == "bg":
reset = self.section.THEMES[self.section.theme][1]
nest('B' + getColor(val), 'B' + reset)
nest("B" + getColor(val), "B" + reset)
elif key == "clickLeft":
button('1', val)
button("1", val)
elif key == "clickMiddle":
button('2', val)
button("2", val)
elif key == "clickRight":
button('3', val)
button("3", val)
elif key == "scrollUp":
button('4', val)
button("4", val)
elif key == "scrollDown":
button('5', val)
button("5", val)
else:
log.warn("Unkown decorator: {}".format(key))
@ -652,7 +680,7 @@ class Text:
curString += self.suffix
if pad and remSize > 0:
curString += ' ' * remSize
curString += " " * remSize
curSize += remSize
if size is not None:
@ -688,7 +716,6 @@ class Text:
curSize += len(str(element))
return curSize
def __getitem__(self, index):
return self.elements[index]

View file

@ -7,150 +7,188 @@ Debugging script
import i3ipc
import os
import psutil
# import alsaaudio
from time import time
import subprocess
i3 = i3ipc.Connection()
lemonbar = subprocess.Popen(['lemonbar', '-b'], stdin=subprocess.PIPE)
lemonbar = subprocess.Popen(["lemonbar", "-b"], stdin=subprocess.PIPE)
# Utils
def upChart(p):
block = ' ▁▂▃▄▅▆▇█'
return block[round(p * (len(block)-1))]
block = " ▁▂▃▄▅▆▇█"
return block[round(p * (len(block) - 1))]
def humanSizeOf(num, suffix='B'): # TODO Credit
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
def humanSizeOf(num, suffix="B"): # TODO Credit
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(num) < 1024.0:
return "%3.0f%2s%s" % (num, unit, suffix)
num /= 1024.0
return "%.0f%2s%s" % (num, 'Yi', suffix)
return "%.0f%2s%s" % (num, "Yi", suffix)
# Values
mode = ''
mode = ""
container = i3.get_tree().find_focused()
workspaces = i3.get_workspaces()
outputs = i3.get_outputs()
username = os.environ['USER']
hostname = os.environ['HOSTNAME']
if '-' in hostname:
hostname = hostname.split('-')[-1]
username = os.environ["USER"]
hostname = os.environ["HOSTNAME"]
if "-" in hostname:
hostname = hostname.split("-")[-1]
oldNetIO = dict()
oldTime = time()
def update():
activeOutputs = sorted(sorted(list(filter(lambda o: o.active, outputs)), key=lambda o: o.rect.y), key=lambda o: o.rect.x)
z = ''
activeOutputs = sorted(
sorted(list(filter(lambda o: o.active, outputs)), key=lambda o: o.rect.y),
key=lambda o: o.rect.x,
)
z = ""
for aOutput in range(len(activeOutputs)):
output = activeOutputs[aOutput]
# Mode || Workspaces
t = []
if (mode != ''):
if mode != "":
t.append(mode)
else:
t.append(' '.join([(w.name.upper() if w.focused else w.name) for w in workspaces if w.output == output.name]))
t.append(
" ".join(
[
(w.name.upper() if w.focused else w.name)
for w in workspaces
if w.output == output.name
]
)
)
# Windows Title
#if container:
# if container:
# t.append(container.name)
# CPU
t.append('C' + ''.join([upChart(p/100) for p in psutil.cpu_percent(percpu=True)]))
t.append(
"C" + "".join([upChart(p / 100) for p in psutil.cpu_percent(percpu=True)])
)
# Memory
t.append('M' + str(round(psutil.virtual_memory().percent)) + '% ' +
'S' + str(round(psutil.swap_memory().percent)) + '%')
t.append(
"M"
+ str(round(psutil.virtual_memory().percent))
+ "% "
+ "S"
+ str(round(psutil.swap_memory().percent))
+ "%"
)
# Disks
d = []
for disk in psutil.disk_partitions():
e = ''
if disk.device.startswith('/dev/sd'):
e += 'S' + disk.device[-2:].upper()
elif disk.device.startswith('/dev/mmcblk'):
e += 'M' + disk.device[-3] + disk.device[-1]
e = ""
if disk.device.startswith("/dev/sd"):
e += "S" + disk.device[-2:].upper()
elif disk.device.startswith("/dev/mmcblk"):
e += "M" + disk.device[-3] + disk.device[-1]
else:
e += '?'
e += ' '
e += str(round(psutil.disk_usage(disk.mountpoint).percent)) + '%'
e += "?"
e += " "
e += str(round(psutil.disk_usage(disk.mountpoint).percent)) + "%"
d.append(e)
t.append(' '.join(d))
t.append(" ".join(d))
# Network
netStats = psutil.net_if_stats()
netIO = psutil.net_io_counters(pernic=True)
net = []
for iface in filter(lambda i: i != 'lo' and netStats[i].isup, netStats.keys()):
s = ''
if iface.startswith('eth'):
s += 'E'
elif iface.startswith('wlan'):
s += 'W'
for iface in filter(lambda i: i != "lo" and netStats[i].isup, netStats.keys()):
s = ""
if iface.startswith("eth"):
s += "E"
elif iface.startswith("wlan"):
s += "W"
else:
s += '?'
s += "?"
s += ' '
s += " "
now = time()
global oldNetIO, oldTime
sent = ((oldNetIO[iface].bytes_sent if iface in oldNetIO else 0) - (netIO[iface].bytes_sent if iface in netIO else 0)) / (oldTime - now)
recv = ((oldNetIO[iface].bytes_recv if iface in oldNetIO else 0) - (netIO[iface].bytes_recv if iface in netIO else 0)) / (oldTime - now)
s += '' + humanSizeOf(abs(recv), 'B/s') + '' + humanSizeOf(abs(sent), 'B/s')
sent = (
(oldNetIO[iface].bytes_sent if iface in oldNetIO else 0)
- (netIO[iface].bytes_sent if iface in netIO else 0)
) / (oldTime - now)
recv = (
(oldNetIO[iface].bytes_recv if iface in oldNetIO else 0)
- (netIO[iface].bytes_recv if iface in netIO else 0)
) / (oldTime - now)
s += (
""
+ humanSizeOf(abs(recv), "B/s")
+ ""
+ humanSizeOf(abs(sent), "B/s")
)
oldNetIO = netIO
oldTime = now
net.append(s)
t.append(' '.join(net))
t.append(" ".join(net))
# Battery
if os.path.isdir('/sys/class/power_supply/BAT0'):
with open('/sys/class/power_supply/BAT0/charge_now') as f:
if os.path.isdir("/sys/class/power_supply/BAT0"):
with open("/sys/class/power_supply/BAT0/charge_now") as f:
charge_now = int(f.read())
with open('/sys/class/power_supply/BAT0/charge_full_design') as f:
with open("/sys/class/power_supply/BAT0/charge_full_design") as f:
charge_full = int(f.read())
t.append('B' + str(round(100*charge_now/charge_full)) + '%')
t.append("B" + str(round(100 * charge_now / charge_full)) + "%")
# Volume
# t.append('V ' + str(alsaaudio.Mixer('Master').getvolume()[0]) + '%')
t.append(username + '@' + hostname)
t.append(username + "@" + hostname)
# print(' - '.join(t))
# t = [output.name]
z += ' - '.join(t) + '%{S' + str(aOutput + 1) + '}'
#lemonbar.stdin.write(bytes(' - '.join(t), 'utf-8'))
#lemonbar.stdin.write(bytes('%{S' + str(aOutput + 1) + '}', 'utf-8'))
z += " - ".join(t) + "%{S" + str(aOutput + 1) + "}"
# lemonbar.stdin.write(bytes(' - '.join(t), 'utf-8'))
# lemonbar.stdin.write(bytes('%{S' + str(aOutput + 1) + '}', 'utf-8'))
lemonbar.stdin.write(bytes(z+'\n', 'utf-8'))
lemonbar.stdin.write(bytes(z + "\n", "utf-8"))
lemonbar.stdin.flush()
# Event listeners
def on_mode(i3, e):
global mode
if (e.change == 'default'):
mode = ''
else :
if e.change == "default":
mode = ""
else:
mode = e.change
update()
i3.on("mode", on_mode)
#def on_window_focus(i3, e):
# def on_window_focus(i3, e):
# global container
# container = e.container
# update()
#
#i3.on("window::focus", on_window_focus)
# i3.on("window::focus", on_window_focus)
def on_workspace_focus(i3, e):
global workspaces
workspaces = i3.get_workspaces()
update()
i3.on("workspace::focus", on_workspace_focus)
# Starting

View file

@ -16,22 +16,37 @@ import difflib
FONT = "DejaVu Sans Mono for Powerline"
# 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'
bg = '#002b36'
thm = [
"#002b36",
"#dc322f",
"#859900",
"#b58900",
"#268bd2",
"#6c71c4",
"#2aa198",
"#93a1a1",
"#657b83",
"#dc322f",
"#859900",
"#b58900",
"#268bd2",
"#6c71c4",
"#2aa198",
"#fdf6e3",
]
fg = "#93a1a1"
bg = "#002b36"
THEMES = {
'CENTER': (fg, bg),
'DEFAULT': (thm[0], thm[8]),
'1': (thm[0], thm[9]),
'2': (thm[0], thm[10]),
'3': (thm[0], thm[11]),
'4': (thm[0], thm[12]),
'5': (thm[0], thm[13]),
'6': (thm[0], thm[14]),
'7': (thm[0], thm[15]),
"CENTER": (fg, bg),
"DEFAULT": (thm[0], thm[8]),
"1": (thm[0], thm[9]),
"2": (thm[0], thm[10]),
"3": (thm[0], thm[11]),
"4": (thm[0], thm[12]),
"5": (thm[0], thm[13]),
"6": (thm[0], thm[14]),
"7": (thm[0], thm[15]),
}
# Utils
@ -49,7 +64,7 @@ def fitText(text, size):
diff = size - t
return text + " " * diff
else:
return ''
return ""
def fgColor(theme):
@ -63,20 +78,20 @@ def bgColor(theme):
class Section:
def __init__(self, theme='DEFAULT'):
self.text = ''
def __init__(self, theme="DEFAULT"):
self.text = ""
self.size = 0
self.toSize = 0
self.theme = theme
self.visible = False
self.name = ''
self.name = ""
def update(self, text):
if text == '':
if text == "":
self.toSize = 0
else:
if len(text) < len(self.text):
self.text = text + self.text[len(text):]
self.text = text + self.text[len(text) :]
else:
self.text = text
self.toSize = len(text) + 3
@ -93,39 +108,39 @@ class Section:
self.visible = self.size
return self.toSize == self.size
def draw(self, left=True, nextTheme='DEFAULT'):
s = ''
def draw(self, left=True, nextTheme="DEFAULT"):
s = ""
if self.visible:
if not left:
if self.theme == nextTheme:
s += ''
s += ""
else:
s += '%{F' + bgColor(self.theme) + '}'
s += '%{B' + bgColor(nextTheme) + '}'
s += ''
s += '%{F' + fgColor(self.theme) + '}'
s += '%{B' + bgColor(self.theme) + '}'
s += ' ' if self.size > 1 else ''
s += "%{F" + bgColor(self.theme) + "}"
s += "%{B" + bgColor(nextTheme) + "}"
s += ""
s += "%{F" + fgColor(self.theme) + "}"
s += "%{B" + bgColor(self.theme) + "}"
s += " " if self.size > 1 else ""
s += fitText(self.text, self.size - 3)
s += ' ' if self.size > 2 else ''
s += " " if self.size > 2 else ""
if left:
if self.theme == nextTheme:
s += ''
s += ""
else:
s += '%{F' + bgColor(self.theme) + '}'
s += '%{B' + bgColor(nextTheme) + '}'
s += ''
s += "%{F" + bgColor(self.theme) + "}"
s += "%{B" + bgColor(nextTheme) + "}"
s += ""
return s
# Section definition
sTime = Section('3')
sTime = Section("3")
hostname = os.environ['HOSTNAME'].split('.')[0]
sHost = Section('2')
hostname = os.environ["HOSTNAME"].split(".")[0]
sHost = Section("2")
sHost.update(
os.environ['USER'] + '@' + hostname.split('-')[-1]
if '-' in hostname else hostname)
os.environ["USER"] + "@" + hostname.split("-")[-1] if "-" in hostname else hostname
)
# Groups definition
@ -133,7 +148,7 @@ gLeft = []
gRight = [sTime, sHost]
# Bar handling
bar = subprocess.Popen(['lemonbar', '-f', FONT, '-b'], stdin=subprocess.PIPE)
bar = subprocess.Popen(["lemonbar", "-f", FONT, "-b"], stdin=subprocess.PIPE)
def updateBar():
@ -141,35 +156,45 @@ def updateBar():
global gLeft, gRight
global outputs
text = ''
text = ""
for oi in range(len(outputs)):
output = outputs[oi]
gLeftFiltered = list(
filter(
lambda s: s.visible and (
not s.output or s.output == output.name),
gLeft))
tLeft = ''
lambda s: s.visible and (not s.output or s.output == output.name), gLeft
)
)
tLeft = ""
l = len(gLeftFiltered)
for gi in range(l):
g = gLeftFiltered[gi]
# Next visible section for transition
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else 'CENTER'
nextTheme = gLeftFiltered[gi + 1].theme if gi + 1 < l else "CENTER"
tLeft = tLeft + g.draw(True, nextTheme)
tRight = ''
tRight = ""
for gi in range(len(gRight)):
g = gRight[gi]
nextTheme = 'CENTER'
for gn in gRight[gi + 1:]:
nextTheme = "CENTER"
for gn in gRight[gi + 1 :]:
if gn.visible:
nextTheme = gn.theme
break
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()
@ -182,12 +207,10 @@ def on_output():
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)
list(filter(lambda o: o.active, i3.get_outputs())), key=lambda o: o.rect.y
),
key=lambda o: o.rect.x,
)
on_output()
@ -209,34 +232,33 @@ def on_workspace_focus():
if workspace.visible:
section.update(workspace.name)
else:
section.update(workspace.name.split(' ')[0])
section.update(workspace.name.split(" ")[0])
if workspace.focused:
section.theme = '4'
section.theme = "4"
elif workspace.urgent:
section.theme = '1'
section.theme = "1"
else:
section.theme = '6'
section.theme = "6"
else:
section.update('')
section.theme = '6'
section.update("")
section.theme = "6"
for tag, i, j, k, l in difflib.SequenceMatcher(
None, sNames, wNames).get_opcodes():
if tag == 'equal': # If the workspaces didn't changed
for tag, i, j, k, l in difflib.SequenceMatcher(None, sNames, wNames).get_opcodes():
if tag == "equal": # If the workspaces didn't changed
for a in range(j - i):
workspace = workspaces[k + a]
section = gLeft[i + a]
actuate(section, workspace)
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]:
if section.visible:
actuate(section, None)
newGLeft.append(section)
else:
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]:
section = Section()
actuate(section, workspace)
@ -255,12 +277,14 @@ def i3events(i3childPipe):
# Proxy functions
def on_workspace_focus(i3, e):
global i3childPipe
i3childPipe.send('on_workspace_focus')
i3childPipe.send("on_workspace_focus")
i3.on("workspace::focus", on_workspace_focus)
def on_output(i3, e):
global i3childPipe
i3childPipe.send('on_output')
i3childPipe.send("on_output")
i3.on("output", on_output)
i3.main()
@ -274,7 +298,7 @@ i3process.start()
def updateValues():
# Time
now = datetime.datetime.now()
sTime.update(now.strftime('%x %X'))
sTime.update(now.strftime("%x %X"))
def updateAnimation():
@ -288,9 +312,9 @@ while True:
now = time.time()
if i3parentPipe.poll():
msg = i3parentPipe.recv()
if msg == 'on_workspace_focus':
if msg == "on_workspace_focus":
on_workspace_focus()
elif msg == 'on_output':
elif msg == "on_output":
on_output()
# TODO Restart lemonbar
else:

View file

@ -16,17 +16,18 @@ import mpd
import random
import math
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
log = logging.getLogger()
# TODO Generator class (for I3WorkspacesProvider, NetworkProvider and later
# PulseaudioProvider and MpdProvider)
def humanSize(num):
"""
Returns a string of width 3+3
"""
for unit in ('B ','KiB','MiB','GiB','TiB','PiB','EiB','ZiB'):
for unit in ("B ", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"):
if abs(num) < 1000:
if num >= 10:
return "{:3d}{}".format(int(num), unit)
@ -35,16 +36,15 @@ def humanSize(num):
num /= 1024.0
return "{:d}YiB".format(num)
def randomColor(seed=0):
random.seed(seed)
return '#{:02x}{:02x}{:02x}'.format(*[random.randint(0, 255) for _ in range(3)])
return "#{:02x}{:02x}{:02x}".format(*[random.randint(0, 255) for _ in range(3)])
class TimeProvider(StatefulSection, PeriodicUpdater):
FORMATS = ["%H:%M",
"%m-%d %H:%M:%S",
"%a %y-%m-%d %H:%M:%S"]
FORMATS = ["%H:%M", "%m-%d %H:%M:%S", "%a %y-%m-%d %H:%M:%S"]
NUMBER_STATES = len(FORMATS)
DEFAULT_STATE = 1
@ -55,18 +55,18 @@ class TimeProvider(StatefulSection, PeriodicUpdater):
def __init__(self, theme=None):
PeriodicUpdater.__init__(self)
StatefulSection.__init__(self, theme)
self.changeInterval(1) # TODO OPTI When state < 1
self.changeInterval(1) # TODO OPTI When state < 1
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}
THEMES = {AlertLevel.NORMAL: 2, AlertLevel.WARNING: 3, AlertLevel.DANGER: 1}
PERSISTENT = True
def getLevel(self, quantity):
@ -92,16 +92,16 @@ class AlertingSection(StatefulSection):
class CpuProvider(AlertingSection, PeriodicUpdater):
NUMBER_STATES = 3
ICON = ''
ICON = ""
def fetcher(self):
percent = psutil.cpu_percent(percpu=False)
self.updateLevel(percent/100)
self.updateLevel(percent / 100)
if self.state >= 2:
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)
return Section.ramp(percent / 100)
def __init__(self, theme=None):
AlertingSection.__init__(self, theme)
@ -113,12 +113,13 @@ class RamProvider(AlertingSection, PeriodicUpdater):
"""
Shows free RAM
"""
NUMBER_STATES = 4
ICON = ''
ICON = ""
def fetcher(self):
mem = psutil.virtual_memory()
freePerc = mem.percent/100
freePerc = mem.percent / 100
self.updateLevel(freePerc)
if self.state < 1:
@ -130,7 +131,7 @@ class RamProvider(AlertingSection, PeriodicUpdater):
text.append(freeStr)
if self.state >= 3:
totalStr = humanSize(mem.total)
text.append('/', totalStr)
text.append("/", totalStr)
return text
@ -146,18 +147,18 @@ class TemperatureProvider(AlertingSection, PeriodicUpdater):
def fetcher(self):
allTemp = psutil.sensors_temperatures()
if 'coretemp' not in allTemp:
if "coretemp" not in allTemp:
# TODO Opti Remove interval
return ''
temp = allTemp['coretemp'][0]
return ""
temp = allTemp["coretemp"][0]
self.warningThresold = temp.high
self.dangerThresold = temp.critical
self.updateLevel(temp.current)
self.icon = Section.ramp(temp.current/temp.high, self.RAMP)
self.icon = Section.ramp(temp.current / temp.high, self.RAMP)
if self.state >= 1:
return '{:.0f}°C'.format(temp.current)
return "{:.0f}°C".format(temp.current)
def __init__(self, theme=None):
AlertingSection.__init__(self, theme)
@ -176,15 +177,16 @@ class BatteryProvider(AlertingSection, PeriodicUpdater):
self.icon = None
return None
self.icon = ("" if bat.power_plugged else "") + \
Section.ramp(bat.percent/100, self.RAMP)
self.icon = ("" if bat.power_plugged else "") + Section.ramp(
bat.percent / 100, self.RAMP
)
self.updateLevel(1-bat.percent/100)
self.updateLevel(1 - bat.percent / 100)
if self.state < 1:
return
t = Text('{:.0f}%'.format(bat.percent))
t = Text("{:.0f}%".format(bat.percent))
if self.state < 2:
return t
@ -200,7 +202,6 @@ class BatteryProvider(AlertingSection, PeriodicUpdater):
self.changeInterval(5)
class PulseaudioProvider(StatefulSection, ThreadedUpdater):
NUMBER_STATES = 3
DEFAULT_STATE = 1
@ -208,28 +209,27 @@ class PulseaudioProvider(StatefulSection, ThreadedUpdater):
def __init__(self, theme=None):
ThreadedUpdater.__init__(self)
StatefulSection.__init__(self, theme)
self.pulseEvents = pulsectl.Pulse('event-handler')
self.pulseEvents = pulsectl.Pulse("event-handler")
self.pulseEvents.event_mask_set(pulsectl.PulseEventMaskEnum.sink)
self.pulseEvents.event_callback_set(self.handleEvent)
self.start()
self.refreshData()
def fetcher(self):
sinks = []
with pulsectl.Pulse('list-sinks') as pulse:
with pulsectl.Pulse("list-sinks") as pulse:
for sink in pulse.sink_list():
if sink.port_active.name == "analog-output-headphones":
icon = ""
elif sink.port_active.name == "analog-output-speaker":
icon = "" if sink.mute else ""
elif sink.port_active.name == "headset-output":
icon = ''
icon = ""
else:
icon = "?"
vol = pulse.volume_get_all_chans(sink)
fg = (sink.mute and '#333333') or (vol > 1 and '#FF0000') or None
fg = (sink.mute and "#333333") or (vol > 1 and "#FF0000") or None
t = Text(icon, fg=fg)
sinks.append(t)
@ -245,7 +245,7 @@ class PulseaudioProvider(StatefulSection, ThreadedUpdater):
vol -= 1
t.append(ramp)
else:
t.append(" {:2.0f}%".format(vol*100))
t.append(" {:2.0f}%".format(vol * 100))
return Text(*sinks)
@ -263,27 +263,27 @@ class NetworkProviderSection(StatefulSection, Updater):
def actType(self):
self.ssid = None
if self.iface.startswith('eth') or self.iface.startswith('enp'):
if 'u' in self.iface:
self.icon = ''
if self.iface.startswith("eth") or self.iface.startswith("enp"):
if "u" in self.iface:
self.icon = ""
else:
self.icon = ''
elif self.iface.startswith('wlan') or self.iface.startswith('wl'):
self.icon = ''
self.icon = ""
elif self.iface.startswith("wlan") or self.iface.startswith("wl"):
self.icon = ""
if self.showSsid:
cmd = ["iwgetid", self.iface, "--raw"]
p = subprocess.run(cmd, stdout=subprocess.PIPE)
self.ssid = p.stdout.strip().decode()
elif self.iface.startswith('tun') or self.iface.startswith('tap'):
self.icon = ''
elif self.iface.startswith('docker'):
self.icon = ''
elif self.iface.startswith('veth'):
self.icon = ''
elif self.iface.startswith('vboxnet'):
self.icon = ''
elif self.iface.startswith("tun") or self.iface.startswith("tap"):
self.icon = ""
elif self.iface.startswith("docker"):
self.icon = ""
elif self.iface.startswith("veth"):
self.icon = ""
elif self.iface.startswith("vboxnet"):
self.icon = ""
else:
self.icon = '?'
self.icon = "?"
def getAddresses(self):
ipv4 = None
@ -298,9 +298,11 @@ class NetworkProviderSection(StatefulSection, Updater):
def fetcher(self):
self.icon = None
self.persistent = False
if self.iface not in self.parent.stats or \
not self.parent.stats[self.iface].isup or \
self.iface.startswith('lo'):
if (
self.iface not in self.parent.stats
or not self.parent.stats[self.iface].isup
or self.iface.startswith("lo")
):
return None
# Get addresses
@ -317,30 +319,36 @@ class NetworkProviderSection(StatefulSection, Updater):
if self.showAddress:
if ipv4:
netStrFull = '{}/{}'.format(ipv4.address, ipv4.netmask)
netStrFull = "{}/{}".format(ipv4.address, ipv4.netmask)
addr = ipaddress.IPv4Network(netStrFull, strict=False)
addrStr = '{}/{}'.format(ipv4.address, addr.prefixlen)
addrStr = "{}/{}".format(ipv4.address, addr.prefixlen)
text.append(addrStr)
# TODO IPV6
# if ipv6:
# text += ' ' + ipv6.address
if self.showSpeed:
recvDiff = self.parent.IO[self.iface].bytes_recv \
recvDiff = (
self.parent.IO[self.iface].bytes_recv
- self.parent.prevIO[self.iface].bytes_recv
sentDiff = self.parent.IO[self.iface].bytes_sent \
)
sentDiff = (
self.parent.IO[self.iface].bytes_sent
- self.parent.prevIO[self.iface].bytes_sent
)
recvDiff /= self.parent.dt
sentDiff /= self.parent.dt
text.append('{}{}'.format(humanSize(recvDiff),
humanSize(sentDiff)))
text.append("{}{}".format(humanSize(recvDiff), humanSize(sentDiff)))
if self.showTransfer:
text.append('{}{}'.format(
humanSize(self.parent.IO[self.iface].bytes_recv),
humanSize(self.parent.IO[self.iface].bytes_sent)))
text.append(
"{}{}".format(
humanSize(self.parent.IO[self.iface].bytes_recv),
humanSize(self.parent.IO[self.iface].bytes_sent),
)
)
return ' '.join(text)
return " ".join(text)
def onChangeState(self, state):
self.showSsid = state >= 1
@ -402,32 +410,33 @@ class NetworkProvider(Section, PeriodicUpdater):
self.fetchData()
self.changeInterval(5)
class RfkillProvider(Section, PeriodicUpdater):
# TODO FEAT rfkill doesn't seem to indicate that the hardware switch is
# toggled
PATH = '/sys/class/rfkill'
PATH = "/sys/class/rfkill"
def fetcher(self):
t = Text()
for device in os.listdir(self.PATH):
with open(os.path.join(self.PATH, device, 'soft'), 'rb') as f:
softBlocked = f.read().strip() != b'0'
with open(os.path.join(self.PATH, device, 'hard'), 'rb') as f:
hardBlocked = f.read().strip() != b'0'
with open(os.path.join(self.PATH, device, "soft"), "rb") as f:
softBlocked = f.read().strip() != b"0"
with open(os.path.join(self.PATH, device, "hard"), "rb") as f:
hardBlocked = f.read().strip() != b"0"
if not hardBlocked and not softBlocked:
continue
with open(os.path.join(self.PATH, device, 'type'), 'rb') as f:
with open(os.path.join(self.PATH, device, "type"), "rb") as f:
typ = f.read().strip()
fg = (hardBlocked and '#CCCCCC') or (softBlocked and '#FF0000')
if typ == b'wlan':
icon = ''
elif typ == b'bluetooth':
icon = ''
fg = (hardBlocked and "#CCCCCC") or (softBlocked and "#FF0000")
if typ == b"wlan":
icon = ""
elif typ == b"bluetooth":
icon = ""
else:
icon = '?'
icon = "?"
t.append(Text(icon, fg=fg))
return t
@ -437,6 +446,7 @@ class RfkillProvider(Section, PeriodicUpdater):
Section.__init__(self, theme)
self.changeInterval(5)
class SshAgentProvider(PeriodicUpdater):
def fetcher(self):
cmd = ["ssh-add", "-l"]
@ -444,17 +454,18 @@ class SshAgentProvider(PeriodicUpdater):
if proc.returncode != 0:
return None
text = Text()
for line in proc.stdout.split(b'\n'):
for line in proc.stdout.split(b"\n"):
if not len(line):
continue
fingerprint = line.split()[1]
text.append(Text('', fg=randomColor(seed=fingerprint)))
text.append(Text("", fg=randomColor(seed=fingerprint)))
return text
def __init__(self):
PeriodicUpdater.__init__(self)
self.changeInterval(5)
class GpgAgentProvider(PeriodicUpdater):
def fetcher(self):
cmd = ["gpg-connect-agent", "keyinfo --list", "/bye"]
@ -463,39 +474,41 @@ class GpgAgentProvider(PeriodicUpdater):
if proc.returncode != 0:
return None
text = Text()
for line in proc.stdout.split(b'\n'):
if not len(line) or line == b'OK':
for line in proc.stdout.split(b"\n"):
if not len(line) or line == b"OK":
continue
spli = line.split()
if spli[6] != b'1':
if spli[6] != b"1":
continue
keygrip = spli[2]
text.append(Text('', fg=randomColor(seed=keygrip)))
text.append(Text("", fg=randomColor(seed=keygrip)))
return text
def __init__(self):
PeriodicUpdater.__init__(self)
self.changeInterval(5)
class KeystoreProvider(Section, MergedUpdater):
# TODO OPTI+FEAT Use ColorCountsSection and not MergedUpdater, this is useless
ICON = ''
ICON = ""
def __init__(self, theme=None):
MergedUpdater.__init__(self, SshAgentProvider(), GpgAgentProvider())
Section.__init__(self, theme)
class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
COLORABLE_ICON = ''
COLORABLE_ICON = ""
def subfetcher(self):
db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY, path=self.dir)
counts = []
for account in self.accounts:
queryStr = 'folder:/{}/ and tag:unread'.format(account)
queryStr = "folder:/{}/ and tag:unread".format(account)
query = notmuch.Query(db, queryStr)
nbMsgs = query.count_messages()
if account == 'frogeye':
if account == "frogeye":
global q
q = query
if nbMsgs < 1:
@ -504,7 +517,7 @@ class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
# db.close()
return counts
def __init__(self, dir='~/.mail/', theme=None):
def __init__(self, dir="~/.mail/", theme=None):
PeriodicUpdater.__init__(self)
ColorCountsSection.__init__(self, theme)
@ -512,23 +525,24 @@ class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
assert os.path.isdir(self.dir)
# Fetching account list
self.accounts = sorted([a for a in os.listdir(self.dir)
if not a.startswith('.')])
self.accounts = sorted(
[a for a in os.listdir(self.dir) if not a.startswith(".")]
)
# Fetching colors
self.colors = dict()
for account in self.accounts:
filename = os.path.join(self.dir, account, 'color')
with open(filename, 'r') as f:
filename = os.path.join(self.dir, account, "color")
with open(filename, "r") as f:
color = f.read().strip()
self.colors[account] = color
self.addPath(os.path.join(self.dir, '.notmuch', 'xapian'))
self.addPath(os.path.join(self.dir, ".notmuch", "xapian"))
class TodoProvider(ColorCountsSection, InotifyUpdater):
# TODO OPT/UX Maybe we could get more data from the todoman python module
# TODO OPT Specific callback for specific directory
COLORABLE_ICON = ''
COLORABLE_ICON = ""
def updateCalendarList(self):
calendars = sorted(os.listdir(self.dir))
@ -538,13 +552,13 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
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:
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:
path = os.path.join(self.dir, calendar, "color")
with open(path, "r") as f:
self.colors[calendar] = f.read().strip()
self.calendars = calendars
@ -579,8 +593,8 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
if self.state < 2:
c = self.countUndone(None)
if c > 0:
counts.append((c, '#00000'))
counts.append((0, '#FFFFF'))
counts.append((c, "#00000"))
counts.append((0, "#FFFFF"))
return counts
# Optimisation ends here
@ -591,6 +605,7 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
counts.append((c, self.colors[calendar]))
return counts
class I3WindowTitleProvider(Section, I3Updater):
# TODO FEAT To make this available from start, we need to find the
# `focused=True` element following the `focus` array
@ -603,6 +618,7 @@ class I3WindowTitleProvider(Section, I3Updater):
Section.__init__(self, theme=theme)
self.on("window", self.on_window)
class I3WorkspacesProviderSection(Section):
def selectTheme(self):
if self.urgent:
@ -626,12 +642,12 @@ class I3WorkspacesProviderSection(Section):
def setName(self, name):
self.shortName = name
self.fullName = self.parent.customNames[name] \
if name in self.parent.customNames else name
self.fullName = (
self.parent.customNames[name] if name in self.parent.customNames else name
)
def switchTo(self):
self.parent.i3.command('workspace {}'.format(self.shortName))
self.parent.i3.command("workspace {}".format(self.shortName))
def __init__(self, name, parent):
Section.__init__(self)
@ -652,7 +668,6 @@ class I3WorkspacesProviderSection(Section):
self.updateText(None)
class I3WorkspacesProvider(Section, I3Updater):
# TODO FEAT Multi-screen
@ -713,7 +728,7 @@ class I3WorkspacesProvider(Section, I3Updater):
self.sections[e.current.num].show()
def on_mode(self, i3, e):
if e.change == 'default':
if e.change == "default":
self.modeSection.updateText(None)
for section in self.sections.values():
section.tempShow()
@ -722,7 +737,9 @@ class I3WorkspacesProvider(Section, I3Updater):
for section in self.sections.values():
section.tempEmpty()
def __init__(self, theme=0, themeFocus=3, themeUrgent=1, themeMode=2, customNames=dict()):
def __init__(
self, theme=0, themeFocus=3, themeUrgent=1, themeMode=2, customNames=dict()
):
I3Updater.__init__(self)
Section.__init__(self)
self.themeNormal = theme
@ -746,13 +763,14 @@ class I3WorkspacesProvider(Section, I3Updater):
parent.addSection(self.modeSection)
self.initialPopulation(parent)
class MpdProvider(Section, ThreadedUpdater):
# TODO FEAT More informations and controls
MAX_LENGTH = 50
def connect(self):
self.mpd.connect('localhost', 6600)
self.mpd.connect("localhost", 6600)
def __init__(self, theme=None):
ThreadedUpdater.__init__(self)
@ -784,18 +802,16 @@ class MpdProvider(Section, ThreadedUpdater):
infosStr = " - ".join(infos)
if len(infosStr) > MpdProvider.MAX_LENGTH:
infosStr = infosStr[:MpdProvider.MAX_LENGTH-1] + ''
infosStr = infosStr[: MpdProvider.MAX_LENGTH - 1] + ""
return "{}".format(infosStr)
def loop(self):
try:
self.mpd.idle('player')
self.mpd.idle("player")
self.refreshData()
except mpd.base.ConnectionError as e:
log.warn(e, exc_info=True)
self.connect()
except BaseException as e:
log.error(e, exc_info=True)

View file

@ -11,13 +11,14 @@ import coloredlogs
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()
# TODO Sync bar update with PeriodicUpdater updates
notBusy = threading.Event()
class Updater:
@staticmethod
def init():
@ -52,8 +53,9 @@ class PeriodicUpdaterThread(threading.Thread):
counter = 0
while True:
notBusy.set()
if PeriodicUpdater.intervalsChanged \
.wait(timeout=PeriodicUpdater.intervalStep):
if PeriodicUpdater.intervalsChanged.wait(
timeout=PeriodicUpdater.intervalStep
):
# ↑ sleeps here
notBusy.clear()
PeriodicUpdater.intervalsChanged.clear()
@ -127,7 +129,6 @@ class PeriodicUpdater(Updater):
class InotifyUpdaterEventHandler(pyinotify.ProcessEvent):
def process_default(self, event):
# DEBUG
# from pprint import pprint
@ -155,8 +156,9 @@ class InotifyUpdater(Updater):
@staticmethod
def init():
notifier = pyinotify.ThreadedNotifier(InotifyUpdater.wm,
InotifyUpdaterEventHandler())
notifier = pyinotify.ThreadedNotifier(
InotifyUpdater.wm, InotifyUpdaterEventHandler()
)
notifier.start()
# TODO Mask for folders
@ -174,8 +176,7 @@ class InotifyUpdater(Updater):
self.dirpath = os.path.dirname(path)
self.filename = os.path.basename(path)
else:
raise FileNotFoundError("No such file or directory: '{}'"
.format(path))
raise FileNotFoundError("No such file or directory: '{}'".format(path))
# Register watch action
if self.dirpath not in InotifyUpdater.paths:
@ -266,4 +267,4 @@ class MergedUpdater(Updater):
updater.updateText = newUpdateText.__get__(updater, Updater)
self.updaters.append(updater)
self.texts[updater] = ''
self.texts[updater] = ""