frobarng: Some refactor
This commit is contained in:
parent
1ae7d6b447
commit
6644e85c30
|
@ -292,6 +292,8 @@ class Bar(ComposableText):
|
|||
self.actionIndex = 0
|
||||
self.actions: dict[str, typing.Callable] = dict()
|
||||
|
||||
self.periodicProviderTask: typing.Coroutine | None = None
|
||||
|
||||
i3 = i3ipc.Connection()
|
||||
for output in i3.get_outputs():
|
||||
if not output.active:
|
||||
|
@ -382,7 +384,8 @@ class Provider:
|
|||
self.modules: list[Module] = list()
|
||||
|
||||
async def run(self) -> None:
|
||||
raise NotImplementedError()
|
||||
# Not a NotImplementedError, otherwise can't combine all classes
|
||||
pass
|
||||
|
||||
|
||||
class MirrorProvider(Provider):
|
||||
|
@ -391,17 +394,16 @@ class MirrorProvider(Provider):
|
|||
self.module: Module
|
||||
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
self.module = self.modules[0]
|
||||
for module in self.modules[1:]:
|
||||
module.mirror(self.module)
|
||||
|
||||
|
||||
class SingleSectionProvider(MirrorProvider):
|
||||
SECTION_CLASS = Section
|
||||
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
self.section = self.SECTION_CLASS(parent=self.module)
|
||||
self.section = Section(parent=self.module)
|
||||
|
||||
|
||||
class StaticProvider(SingleSectionProvider):
|
||||
|
@ -419,7 +421,6 @@ class StatefulSection(Section):
|
|||
super().__init__(parent=parent, sortKey=sortKey)
|
||||
self.state = 0
|
||||
self.numberStates: int
|
||||
self.stateChanged = asyncio.Event()
|
||||
|
||||
self.setAction(Button.CLICK_LEFT, self.incrementState)
|
||||
self.setAction(Button.CLICK_RIGHT, self.decrementState)
|
||||
|
@ -432,14 +433,61 @@ class StatefulSection(Section):
|
|||
self.state -= 1
|
||||
self.changeState()
|
||||
|
||||
def setChangedState(self, callback: typing.Callable) -> None:
|
||||
self.callback = callback
|
||||
|
||||
def changeState(self) -> None:
|
||||
self.state %= self.numberStates
|
||||
self.stateChanged.set()
|
||||
self.stateChanged.clear()
|
||||
self.bar.taskGroup.create_task(self.callback())
|
||||
|
||||
|
||||
class StatefulProvider(SingleSectionProvider):
|
||||
SECTION_CLASS = StatefulSection
|
||||
class SingleStatefulSectionProvider(MirrorProvider):
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
self.section = StatefulSection(parent=self.module)
|
||||
|
||||
|
||||
class PeriodicProvider(Provider):
|
||||
async def init(self) -> None:
|
||||
pass
|
||||
|
||||
async def loop(self) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
async def task(cls, bar: Bar) -> None:
|
||||
providers = list()
|
||||
for provider in bar.providers:
|
||||
if isinstance(provider, PeriodicProvider):
|
||||
providers.append(provider)
|
||||
await provider.init()
|
||||
|
||||
while True:
|
||||
# TODO Block bar update during the periodic update of the loops
|
||||
loops = [provider.loop() for provider in providers]
|
||||
asyncio.gather(*loops)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
# Hardcoded to 1 second... not sure if we want some more than that,
|
||||
# and if the logic to check if a task should run would be a win
|
||||
# compared to the task itself
|
||||
remaining = 1 - now.microsecond / 1000000
|
||||
await asyncio.sleep(remaining)
|
||||
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
for module in self.modules:
|
||||
bar = module.getFirstParentOfType(Bar)
|
||||
assert bar
|
||||
if not bar.periodicProviderTask:
|
||||
bar.periodicProviderTask = PeriodicProvider.task(bar)
|
||||
bar.addLongRunningTask(bar.periodicProviderTask)
|
||||
|
||||
|
||||
class PeriodicStatefulProvider(SingleStatefulSectionProvider, PeriodicProvider):
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
self.section.setChangedState(self.loop)
|
||||
|
||||
|
||||
# Providers
|
||||
|
@ -470,8 +518,8 @@ class I3WindowTitleProvider(SingleSectionProvider):
|
|||
|
||||
|
||||
class I3WorkspacesProvider(Provider):
|
||||
# FIXME Custom names
|
||||
# FIXME Colors
|
||||
# TODO Custom names
|
||||
# TODO Colors
|
||||
|
||||
async def updateWorkspaces(self, i3: i3ipc.Connection) -> None:
|
||||
"""
|
||||
|
@ -578,14 +626,12 @@ class NetworkProviderSection(StatefulSection):
|
|||
self.numberStates = 5 if self.wifi else 4
|
||||
self.state = 1 if self.wifi else 0
|
||||
|
||||
async def updateOnStateChange(self) -> None:
|
||||
while True:
|
||||
await self.stateChanged.wait()
|
||||
await self.provider.updateIface(self.iface)
|
||||
self.setChangedState(self.update)
|
||||
|
||||
async def getText(self) -> str | None:
|
||||
async def update(self) -> None:
|
||||
if self.ignore or not self.provider.if_stats[self.iface].isup:
|
||||
return None
|
||||
self.setText(None)
|
||||
return
|
||||
text = self.icon
|
||||
|
||||
state = self.state + (0 if self.wifi else 1) # SSID
|
||||
|
@ -611,84 +657,71 @@ class NetworkProviderSection(StatefulSection):
|
|||
recv = self.provider.io_counters[self.iface].bytes_recv
|
||||
prevSent = self.provider.prev_io_counters[self.iface].bytes_sent
|
||||
sent = self.provider.io_counters[self.iface].bytes_sent
|
||||
recvDiff = recv - prevRecv
|
||||
sentDiff = sent - prevSent
|
||||
dt = self.provider.time - self.provider.prev_time
|
||||
recvDiff /= dt
|
||||
sentDiff /= dt
|
||||
|
||||
recvDiff = (recv - prevRecv) / dt
|
||||
sentDiff = (sent - prevSent) / dt
|
||||
text += f" ↓{humanSize(recvDiff)}↑{humanSize(sentDiff)}"
|
||||
|
||||
if state >= 4: # Counter
|
||||
text += f" ⇓{humanSize(recv)}⇑{humanSize(sent)}"
|
||||
|
||||
return text
|
||||
self.setText(text)
|
||||
|
||||
|
||||
class NetworkProvider(MirrorProvider):
|
||||
class NetworkProvider(MirrorProvider, PeriodicProvider):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.sections: dict[str, NetworkProviderSection] = dict()
|
||||
|
||||
async def updateIface(self, iface: str) -> None:
|
||||
section = self.sections[iface]
|
||||
section.setText(await section.getText())
|
||||
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
|
||||
async def init(self) -> None:
|
||||
loop = asyncio.get_running_loop()
|
||||
self.time = loop.time()
|
||||
self.io_counters = psutil.net_io_counters(pernic=True)
|
||||
|
||||
while True:
|
||||
# Separate TaskGroup in case it takes longer than one second,
|
||||
# it doesn't fill the main TaskGroup
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
self.prev_io_counters = self.io_counters
|
||||
self.prev_time = self.time
|
||||
# On-demand would only benefit if_addrs:
|
||||
# stats are used to determine display,
|
||||
# and we want to keep previous io_counters
|
||||
# so displaying stats is ~instant.
|
||||
self.time = loop.time()
|
||||
self.if_stats = psutil.net_if_stats()
|
||||
self.if_addrs = psutil.net_if_addrs()
|
||||
self.io_counters = psutil.net_io_counters(pernic=True)
|
||||
async def loop(self) -> None:
|
||||
loop = asyncio.get_running_loop()
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
self.prev_io_counters = self.io_counters
|
||||
self.prev_time = self.time
|
||||
# On-demand would only benefit if_addrs:
|
||||
# stats are used to determine display,
|
||||
# and we want to keep previous io_counters
|
||||
# so displaying stats is ~instant.
|
||||
self.time = loop.time()
|
||||
self.if_stats = psutil.net_if_stats()
|
||||
self.if_addrs = psutil.net_if_addrs()
|
||||
self.io_counters = psutil.net_io_counters(pernic=True)
|
||||
|
||||
for iface in self.if_stats:
|
||||
if iface not in self.sections:
|
||||
section = NetworkProviderSection(
|
||||
parent=self.module, iface=iface, provider=self
|
||||
)
|
||||
self.sections[iface] = section
|
||||
for iface in self.if_stats:
|
||||
section = self.sections.get(iface)
|
||||
if not section:
|
||||
section = NetworkProviderSection(
|
||||
parent=self.module, iface=iface, provider=self
|
||||
)
|
||||
self.sections[iface] = section
|
||||
|
||||
self.module.bar.addLongRunningTask(section.updateOnStateChange())
|
||||
tg.create_task(self.updateIface(iface))
|
||||
for iface, section in self.sections.items():
|
||||
if iface not in self.if_stats:
|
||||
section.setText(None)
|
||||
tg.create_task(asyncio.sleep(1))
|
||||
tg.create_task(section.update())
|
||||
for iface, section in self.sections.items():
|
||||
if iface not in self.if_stats:
|
||||
section.setText(None)
|
||||
|
||||
async def onStateChange(self, section: StatefulSection) -> None:
|
||||
assert isinstance(section, NetworkProviderSection)
|
||||
await section.update()
|
||||
|
||||
|
||||
class TimeProvider(StatefulProvider):
|
||||
class TimeProvider(PeriodicStatefulProvider):
|
||||
FORMATS = ["%H:%M", "%m-%d %H:%M:%S", "%a %y-%m-%d %H:%M:%S"]
|
||||
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
assert isinstance(self.section, StatefulSection)
|
||||
async def init(self) -> None:
|
||||
self.section.state = 1
|
||||
self.section.numberStates = len(self.FORMATS)
|
||||
|
||||
while True:
|
||||
now = datetime.datetime.now()
|
||||
format = self.FORMATS[self.section.state]
|
||||
self.section.setText(now.strftime(format))
|
||||
|
||||
remaining = 1 - now.microsecond / 1000000
|
||||
try:
|
||||
await asyncio.wait_for(self.section.stateChanged.wait(), remaining)
|
||||
except TimeoutError:
|
||||
pass
|
||||
async def loop(self) -> None:
|
||||
now = datetime.datetime.now()
|
||||
format = self.FORMATS[self.section.state]
|
||||
self.section.setText(now.strftime(format))
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
|
Loading…
Reference in a new issue