fix battery usage

This commit is contained in:
2025-09-29 11:36:33 +02:00
parent fe87de7580
commit c4e522f17a
5 changed files with 177 additions and 50 deletions

View File

@@ -15,10 +15,8 @@ from fabric.river.widgets import (
RiverActiveWindow, RiverActiveWindow,
get_river_connection, get_river_connection,
) )
from fabric.utils import (
invoke_repeater,
)
from fabric.widgets.circularprogressbar import CircularProgressBar from fabric.widgets.circularprogressbar import CircularProgressBar
from bar.services.system_stats import SystemStatsService
from bar.config import VINYL, BATTERY, BAR_HEIGHT, WINDOW_TITLE from bar.config import VINYL, BATTERY, BAR_HEIGHT, WINDOW_TITLE
@@ -134,14 +132,16 @@ class StatusBar(Window):
), ),
) )
invoke_repeater(1000, self.update_progress_bars) # Create system stats service with signal-based updates
self.system_stats_service = SystemStatsService(update_interval=3000)
self.system_stats_service.connect("stats-changed", self.update_progress_bars)
# Set the bar height # Set the bar height
self.set_size_request(-1, BAR_HEIGHT) self.set_size_request(-1, BAR_HEIGHT)
self.show_all() self.show_all()
def update_progress_bars(self): def update_progress_bars(self, service, cpu_percent, memory_percent):
self.ram_progress_bar.value = psutil.virtual_memory().percent / 100 """Update progress bars when system stats change"""
self.cpu_progress_bar.value = psutil.cpu_percent() / 100 self.cpu_progress_bar.value = cpu_percent
return True self.ram_progress_bar.value = memory_percent

View File

@@ -1,38 +1,13 @@
import psutil
from gi.repository import GLib from gi.repository import GLib
from fabric.widgets.box import Box from fabric.widgets.box import Box
from fabric.widgets.label import Label from fabric.widgets.label import Label
from fabric.widgets.image import Image from fabric.widgets.image import Image
from fabric import Fabricator from bar.services.battery import BatteryService
class BatteryProvider:
def __init__(self):
self.bat_percent = 0.0
self.bat_charging = None
self._update()
GLib.timeout_add_seconds(1, self._update)
def _update(self):
battery = psutil.sensors_battery()
if battery is None:
self.bat_percent = 0.0
self.bat_charging = None
else:
self.bat_percent = battery.percent
self.bat_charging = battery.power_plugged
return True
def get_battery(self):
return (self.bat_percent, self.bat_charging)
class Battery(Box): class Battery(Box):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(name="battery-widget", orientation="h", spacing=4, **kwargs) super().__init__(name="battery-widget", orientation="h", spacing=4, **kwargs)
self.bat_provider = BatteryProvider()
self.bat_icon = Image( self.bat_icon = Image(
name="bat-icon", icon_name="battery-full-symbolic", icon_size=16 name="bat-icon", icon_name="battery-full-symbolic", icon_size=16
@@ -40,18 +15,17 @@ class Battery(Box):
self.bat_label = Label(name="bat-label", label="100%") self.bat_label = Label(name="bat-label", label="100%")
self.bat_fabricator = Fabricator( # Create battery service with signal-based updates
poll_from=lambda *_: self.bat_provider.get_battery(), self.battery_service = BatteryService(update_interval=10000) # Check every 10 seconds
on_changed=self.update_battery, self.battery_service.connect("battery-changed", self.update_battery)
interval=1000,
stream=False,
default_value=(100, False),
)
self.children = [self.bat_icon, self.bat_label] self.children = [self.bat_icon, self.bat_label]
self.show_all() self.show_all()
GLib.idle_add(self.update_battery, None, self.bat_provider.get_battery()) # Initialize with current battery status
initial_percent = self.battery_service.percent
initial_charging = self.battery_service.charging
GLib.idle_add(self.update_battery, None, initial_percent, initial_charging)
def _icon_lookup(self, bat, charging): def _icon_lookup(self, bat, charging):
# Round to nearest 10 for level-based icons # Round to nearest 10 for level-based icons
@@ -62,19 +36,16 @@ class Battery(Box):
else: else:
return f"battery-level-{level}-symbolic" return f"battery-level-{level}-symbolic"
def update_battery(self, sender, battery_data): def update_battery(self, service, percent, charging):
value, charging = battery_data """Update battery display when battery status changes"""
icon_name = self._icon_lookup(percent, charging)
icon_name = self._icon_lookup(value, charging)
self.bat_icon.set_property("icon-name", icon_name) self.bat_icon.set_property("icon-name", icon_name)
self.bat_label.set_text(f"{int(value)}%") self.bat_label.set_text(f"{int(percent)}%")
if value < 20 and not charging: if percent < 20 and not charging:
self.bat_label.add_style_class("battery-low") self.bat_label.add_style_class("battery-low")
self.bat_icon.add_style_class("battery-low") self.bat_icon.add_style_class("battery-low")
else: else:
self.bat_label.remove_style_class("battery-low") self.bat_label.remove_style_class("battery-low")
self.bat_icon.remove_style_class("battery-low") self.bat_icon.remove_style_class("battery-low")
return True

View File

@@ -13,10 +13,29 @@ from fabric.widgets.stack import Stack
from ..widgets.circle_image import CircleImage from ..widgets.circle_image import CircleImage
import bar.modules.icons as icons import bar.modules.icons as icons
from bar.services.mpris import MprisPlayerManager, MprisPlayer from bar.services.mpris import MprisPlayerManager, MprisPlayer
from fabric import Fabricator
# from bar.modules.cavalcade import SpectrumRender # from bar.modules.cavalcade import SpectrumRender
def get_player_progress(fabricator, mpris_player):
"""Get player progress for Fabricator"""
if not mpris_player:
return (0, 0, 0.0)
try:
current = mpris_player.position
except Exception:
current = 0
try:
total = int(mpris_player.length or 0)
except Exception:
total = 0
progress = current / total if total > 0 else 0.0
return (current, total, progress)
def get_player_icon_markup_by_name(player_name): def get_player_icon_markup_by_name(player_name):
if player_name: if player_name:
pn = player_name.lower() pn = player_name.lower()

72
bar/services/battery.py Normal file
View File

@@ -0,0 +1,72 @@
import psutil
from fabric.core.service import Service, Signal
from fabric.utils import invoke_repeater
class BatteryService(Service):
@Signal
def battery_changed(self, percent: float, charging: bool) -> None:
"""Signal emitted when battery status changes"""
pass
def __init__(self, update_interval=10000, **kwargs): # Check every 10 seconds
super().__init__(**kwargs)
self._percent = 0.0
self._charging = False
self._update_interval = update_interval
self._timer_id = None
# Start periodic updates
self.start_monitoring()
def start_monitoring(self):
"""Start monitoring battery status"""
if self._timer_id is None:
# Get initial values
self._update_battery()
# Set up periodic updates
self._timer_id = invoke_repeater(self._update_interval, self._update_battery)
def stop_monitoring(self):
"""Stop monitoring battery status"""
if self._timer_id is not None:
from gi.repository import GLib
GLib.source_remove(self._timer_id)
self._timer_id = None
def _update_battery(self):
"""Update battery status and emit signal if changed"""
try:
# Use the same pattern as the example
bat_sen = psutil.sensors_battery()
if not bat_sen:
# No battery sensor available (desktop systems)
new_percent = 100.0 # Assume plugged in
new_charging = True
else:
new_percent = bat_sen.percent
new_charging = bat_sen.power_plugged
# Only emit signal if values changed
percent_changed = abs(new_percent - self._percent) > 0.5
charging_changed = new_charging != self._charging
if percent_changed or charging_changed:
self._percent = new_percent
self._charging = new_charging
self.battery_changed(new_percent, new_charging)
except Exception as e:
print(f"Error updating battery status: {e}")
return True # Keep the timer running
@property
def percent(self):
"""Get current battery percentage"""
return self._percent
@property
def charging(self):
"""Get current charging status"""
return self._charging

View File

@@ -0,0 +1,65 @@
import psutil
from fabric.core.service import Service, Signal
from fabric.utils import invoke_repeater
class SystemStatsService(Service):
@Signal
def stats_changed(self, cpu_percent: float, memory_percent: float) -> None:
"""Signal emitted when system stats change"""
pass
def __init__(self, update_interval=3000, **kwargs):
super().__init__(**kwargs)
self._cpu_percent = 0.0
self._memory_percent = 0.0
self._update_interval = update_interval
self._timer_id = None
# Start periodic updates
self.start_monitoring()
def start_monitoring(self):
"""Start monitoring system stats"""
if self._timer_id is None:
# Get initial values
self._update_stats()
# Set up periodic updates
self._timer_id = invoke_repeater(self._update_interval, self._update_stats)
def stop_monitoring(self):
"""Stop monitoring system stats"""
if self._timer_id is not None:
from gi.repository import GLib
GLib.source_remove(self._timer_id)
self._timer_id = None
def _update_stats(self):
"""Update system stats and emit signal if changed"""
try:
new_cpu = psutil.cpu_percent()
new_memory = psutil.virtual_memory().percent
# Only emit signal if values changed significantly (reduce noise)
cpu_changed = abs(new_cpu - self._cpu_percent) > 1.0
memory_changed = abs(new_memory - self._memory_percent) > 1.0
if cpu_changed or memory_changed:
self._cpu_percent = new_cpu
self._memory_percent = new_memory
self.stats_changed(new_cpu / 100, new_memory / 100)
except Exception as e:
print(f"Error updating system stats: {e}")
return True # Keep the timer running
@property
def cpu_percent(self):
"""Get current CPU percentage"""
return self._cpu_percent / 100
@property
def memory_percent(self):
"""Get current memory percentage"""
return self._memory_percent / 100