From 87c94a5ecac4cce0dda822270cbdfff99a6f5a94 Mon Sep 17 00:00:00 2001 From: elParaguayo Date: Mon, 13 Nov 2023 21:04:00 +0000 Subject: [PATCH] Better framerate fix for `Visualiser` Use a threading.Lock object and only release lock once FPS interval has elapsed. --- CHANGELOG | 1 + qtile_extras/widget/visualiser.py | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d904b582..69b24989 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +2023-11-13: [BUGFIX] A neater fix for the `Visualiser` CPU bug 2023-11-13: [BUGFIX] Fix `fraction` KeyError in `UpowerWidget` 2023-11-13: [FEATURE] Add `invert` option to `Visualiser` to draw bars from top down 2023-11-12: [BUGFIX] (Trying to) fix increasing CPU with `Visualiser` widget diff --git a/qtile_extras/widget/visualiser.py b/qtile_extras/widget/visualiser.py index 18d11a51..86b95b78 100644 --- a/qtile_extras/widget/visualiser.py +++ b/qtile_extras/widget/visualiser.py @@ -26,6 +26,7 @@ import time from contextlib import contextmanager from pathlib import Path +from threading import Lock from time import sleep import cairocffi @@ -51,6 +52,8 @@ bit_format = 8bit """ +fps_lock = Lock() + class Visualiser(base._Widget): """ @@ -188,6 +191,9 @@ def _stop(self): self._lock.close() self._lockfile.close() + if fps_lock.locked(): + fps_lock.release() + self._set_length() def _open_shm(self): @@ -228,14 +234,13 @@ def draw(self): self.drawer.draw(offsetx=self.offsetx, offsety=self.offsety, width=self.length) return - # We need to filter out calls that happen before the next interval - # If we don't do this, CPU usage increases horrifically - # Feels like there should be a better way though! - t = time.time() - diff = t - self._last_time - if diff < self._interval: + # We need to lock the redraw to our set framerate. We can't rely solely on timers + # as any call to bar.draw() by another widget will trigger a draw of the widget. + # We use a non-blocking lock and only allow the widget to draw if the lock was + # successfully acquired. The lock is only released after the required interval has + # elapsed. + if not fps_lock.acquire(blocking=False): return - self._last_time = t self._draw() @@ -253,7 +258,12 @@ def _draw(self): self.drawer.ctx.paint() self.drawer.draw(offsetx=self.offsetx, offsety=self.offsety, width=self.length) - self._timer = self.timeout_add(self._interval, self.draw) + self._timer = self.timeout_add(self._interval, self.loop) + + def loop(self): + # Release the lock and redraw. + fps_lock.release() + self.draw() def finalize(self): self._stop()