Huge Updates

This commit is contained in:
sankalp committed 2025-02-25 05:21:53 +05:45
1 parent 28f47aed3c
commit 1885a2dc30
30 files changed
+1421 -1260

No files matched your search

+2 -6
View File
@@ -1,10 +1,6 @@
from .battery import *
from .bluetooth import *
from .idle import *
from .stats import *
from .microphone import *
from .network import *
from .powerprofile import *
from .taskbar import *
from .volume import *
from .workspace import *
from .indicators import *
from .updates import *
+226 -59
View File
@@ -1,70 +1,237 @@
import psutil
from fabric import Fabricator
import subprocess
import re
from fabric.widgets.box import Box
from fabric.widgets.overlay import Overlay
from fabric.widgets.eventbox import EventBox
from fabric.widgets.button import Button
from fabric.widgets.label import Label
from fabric.widgets.circularprogressbar import CircularProgressBar
from snippets import MaterialIcon
from fabric.widgets.overlay import Overlay
from fabric.widgets.revealer import Revealer
from fabric.core.fabricator import Fabricator
from fabric.utils.helpers import exec_shell_command_async
from gi.repository import GLib
import snippets.iconss as icons
class BatteryLabel(Box):
ICONS_CHARGING = [
"battery_charging_20",
"battery_charging_20",
"battery_charging_20",
"battery_charging_30",
"battery_charging_30",
"battery_charging_50",
"battery_charging_60",
"battery_charging_80",
"battery_charging_80",
"battery_charging_90",
"battery_charging_full",
]
ICONS_NOT_CHARGING = [
"battery_alert",
"battery_1_bar",
"battery_1_bar",
"battery_2_bar",
"battery_2_bar",
"battery_3_bar",
"battery_4_bar",
"battery_4_bar",
"battery_5_bar",
"battery_6_bar",
"battery_full",
]
def __init__(self, **kwargs):
super().__init__(**kwargs)
Fabricator(interval=1000, poll_from=self.update_battery_status)
def update_battery_status(self, *_):
battery = psutil.sensors_battery()
if battery is None:
self.hide()
return
is_charging = battery.power_plugged if battery else False
icons = self.ICONS_CHARGING if is_charging else self.ICONS_NOT_CHARGING
battery_percent = round(battery.percent)
index = min(max(battery_percent // 10, 0), 10)
battery_icon = MaterialIcon(icons[index], size=16)
self.progress_bar = CircularProgressBar(
name="progress", line_style="round", line_width=2, size=24
super().__init__(
name="battery",
orientation="h",
spacing=0,
)
self.battery_overlay = Overlay(child=self.progress_bar, overlays=battery_icon)
self.battery_label = Label(label=f"{battery_percent}%", name="battery-label")
self.children = self.battery_label, self.battery_overlay
battery_percent = round(battery.percent)
self.progress_bar.value = battery_percent / 100
self.show() if battery_percent < 100 else self.hide()
self.set_tooltip_text(
f"Battery: {battery_percent}%\nStatus: {'Charging' if is_charging else 'Discharging'}"
self.bat_save = Button(
name="battery-save",
child=Label(name="battery-save-label", markup=icons.power_saving),
on_clicked=lambda *_: self.set_power_mode("powersave"),
)
self.bat_balanced = Button(
name="battery-balanced",
child=Label(name="battery-balanced-label", markup=icons.power_balanced),
on_clicked=lambda *_: self.set_power_mode("balanced"),
)
self.bat_perf = Button(
name="battery-performance",
child=Label(
name="battery-performance-label", markup=icons.power_performance
),
on_clicked=lambda *_: self.set_power_mode("performance"),
)
return True
# Attach mouse enter/leave events to buttons as well
for btn in [self.bat_save, self.bat_balanced, self.bat_perf]:
btn.connect("enter-notify-event", self.on_mouse_enter)
btn.connect("leave-notify-event", self.on_mouse_leave)
# Group the mode buttons into a container.
self.mode_switcher = Box(
name="power-mode-switcher",
orientation="h",
spacing=4,
children=[self.bat_save, self.bat_balanced, self.bat_perf],
)
self.bat_icon = Label(name="battery-icon", markup=icons.battery)
self.bat_circle = CircularProgressBar(
name="battery-circle",
value=0,
size=28,
line_width=2,
start_angle=180,
end_angle=360,
)
self.bat_overlay = Overlay(
name="battery-overlay",
visible=False,
child=self.bat_circle,
overlays=[self.bat_icon],
)
self.bat_level = Label(
name="battery-level",
label="100%",
)
self.bat_revealer = Revealer(
name="battery-revealer",
transition_duration=250,
transition_type="slide-left",
child=self.bat_level,
child_revealed=False,
)
# Create an inner container to hold the battery elements.
inner_container = Box(orientation="h", spacing=3)
inner_container.add(self.bat_overlay)
inner_container.add(self.bat_revealer)
inner_container.add(self.mode_switcher)
# Wrap the inner container in an EventBox.
self.event_box = EventBox(
events=["enter-notify-event", "leave-notify-event"],
name="battery-eventbox",
)
self.event_box.connect("enter-notify-event", self.on_mouse_enter)
self.event_box.connect("leave-notify-event", self.on_mouse_leave)
self.event_box.add(inner_container)
# Add the EventBox to the Battery widget.
self.add(self.event_box)
self.current_mode = None
# Initialize hide_timer for delayed hiding.
self.hide_timer = None
# Initialize counter to track hover status across all related widgets.
self.hover_counter = 0
# Initialize Fabricator for battery polling every 5 seconds.
self.batt_fabricator = Fabricator(
lambda *args, **kwargs: self.poll_battery(),
interval=1000,
stream=False,
default_value=0,
)
self.batt_fabricator.changed.connect(self.update_battery)
# Run update_battery immediately at startup.
GLib.idle_add(self.update_battery, None, self.poll_battery())
self.set_power_mode("balanced")
def on_mouse_enter(self, widget, event):
"""Reveal battery level on hover."""
self.hover_counter += 1
if self.hide_timer is not None:
GLib.source_remove(self.hide_timer)
self.hide_timer = None
self.bat_revealer.set_reveal_child(True)
return False
def on_mouse_leave(self, widget, event):
"""Schedule hiding the battery level after a 0.5s delay only if not hovering any element."""
if self.hover_counter > 0:
self.hover_counter -= 1
if self.hover_counter == 0:
if self.hide_timer is not None:
GLib.source_remove(self.hide_timer)
self.hide_timer = GLib.timeout_add(500, self.hide_revealer)
return False
def hide_revealer(self):
self.bat_revealer.set_reveal_child(False)
self.hide_timer = None
return False
def poll_battery(self):
"""
Polls the battery status by running the "acpi -b" command.
If no battery information is found, returns (0, None).
Otherwise, returns a tuple: (battery percentage as a float between 0 and 1, battery status string)
"""
try:
output = subprocess.check_output(["acpi", "-b"]).decode("utf-8").strip()
if "Battery" not in output:
return (0, None)
match_percent = re.search(r"(\d+)%", output)
match_status = re.search(r"Battery \d+: (\w+)", output)
if match_percent:
percent = int(match_percent.group(1))
status = match_status.group(1) if match_status else None
return (percent / 100.0, status)
except Exception:
pass
return (0, None)
def update_battery(self, sender, battery_data):
"""
Updates the battery widget...
"""
value, status = battery_data
if value == 0:
self.bat_overlay.set_visible(False)
self.bat_revealer.set_visible(False)
else:
self.bat_overlay.set_visible(True)
self.bat_revealer.set_visible(True)
self.bat_circle.set_value(value)
percentage = int(value * 100)
self.bat_level.set_label(f"{percentage}%")
# Actualizar el icono de la batería basado en el nivel y el estado de carga.
if percentage <= 15:
self.bat_icon.set_markup(icons.alert)
self.bat_icon.add_style_class("alert")
self.bat_circle.add_style_class("alert")
else:
self.bat_icon.remove_style_class("alert")
self.bat_circle.remove_style_class("alert")
if (
status == "Discharging"
): # Muestra discharging en cualquier nivel, incluso al 100%.
self.bat_icon.set_markup(icons.discharging)
elif percentage == 100:
self.bat_icon.set_markup(icons.battery)
elif status == "Charging":
self.bat_icon.set_markup(icons.charging)
else:
self.bat_icon.set_markup(icons.battery)
def set_power_mode(self, mode):
"""
Switches power mode by running the corresponding auto-cpufreq command.
mode: one of 'powersave', 'balanced', or 'performance'
"""
commands = {
"powersave": "sudo auto-cpufreq --force powersave",
"balanced": "sudo auto-cpufreq --force reset",
"performance": "sudo auto-cpufreq --force performance",
}
if mode in commands:
try:
exec_shell_command_async(commands[mode])
self.current_mode = mode
self.update_button_styles()
except Exception as err:
# Optionally, handle errors or display a notification.
print(f"Error setting power mode: {err}")
def update_button_styles(self):
"""
Optionally updates button styles to reflect the current mode.
Adjust the styling method based on your toolkit's capabilities.
"""
if self.current_mode == "powersave":
self.bat_save.add_style_class("active")
self.bat_balanced.remove_style_class("active")
self.bat_perf.remove_style_class("active")
elif self.current_mode == "balanced":
self.bat_save.remove_style_class("active")
self.bat_balanced.add_style_class("active")
self.bat_perf.remove_style_class("active")
elif self.current_mode == "performance":
self.bat_save.remove_style_class("active")
self.bat_balanced.remove_style_class("active")
self.bat_perf.add_style_class("active")
-34
View File
@@ -1,34 +0,0 @@
from fabric.bluetooth import BluetoothClient
from fabric.widgets.box import Box
from snippets import MaterialIcon
class BluetoothIndicator(Box):
ICON_ENABLED = "bluetooth"
ICON_DISABLED = "bluetooth_disabled"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bluetooth_client = BluetoothClient()
self.bluetooth_icon = None
self.bluetooth_client.connect("changed", self.update_bluetooth_status)
self.update_bluetooth_status()
def update_bluetooth_status(self, *_):
icon_label = (
self.ICON_ENABLED if self.bluetooth_client.enabled else self.ICON_DISABLED
)
new_icon = MaterialIcon(icon_label, size=16)
if self.bluetooth_icon:
self.remove(self.bluetooth_icon)
self.bluetooth_icon = new_icon
self.add(self.bluetooth_icon)
def on_destroy(self):
self.bluetooth_client.disconnect("changed", self.update_bluetooth_status)
+134
View File
@@ -0,0 +1,134 @@
import subprocess
from fabric import Fabricator
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from fabric.bluetooth import BluetoothClient
from services import NetworkClient, audio
import snippets.iconss as icons
class Indicators(Box):
def __init__(self, **kwargs):
super().__init__(orientation="h", spacing=2, **kwargs)
self.bluetooth_icon = Label(name="indicator-icon")
self.wifi_icon = Label(name="indicator-icon")
self.volume_icon_button = Label(name="indicator-icon")
self.microphone_icon = Label(name="indicator-icon")
self.idle_label = Label(name="indicator-icon")
self.night_label = Label(name="indicator-icon")
for widget in [
self.bluetooth_icon,
self.wifi_icon,
self.volume_icon_button,
self.microphone_icon,
self.idle_label,
self.night_label,
]:
self.add(widget)
self.audio_service = audio
self.bluetooth_client = BluetoothClient()
self.network_client = NetworkClient()
# Connect signals
self.bluetooth_client.connect("changed", self.update_bluetooth_status)
self.audio_service.connect("microphone_changed", self.update_mic_status)
self.audio_service.connect("changed", self.update_volume_status)
self.network_client.connect("device_ready", self.update_network_status)
if self.network_client.wifi_device:
self.network_client.wifi_device.connect(
"changed", self.update_network_status
)
if self.network_client.ethernet_device:
self.network_client.ethernet_device.connect(
"changed", self.update_network_status
)
Fabricator(interval=1000, poll_from=self.update_all_statuses)
self.update_bluetooth_status()
def update_all_statuses(self, *_args):
self.update_idle_night_status()
self.update_network_status()
def update_idle_night_status(self, *_):
"""Updates the idle and night mode status icons."""
self.idle_label.set_markup(
icons.coffee
if subprocess.run(
["pgrep", "-x", "wlinhibit"], capture_output=True
).returncode
== 0
else ""
)
self.night_label.set_markup(
icons.night
if subprocess.run(
["pgrep", "-x", "hyprsunset"], capture_output=True
).returncode
== 0
else ""
)
def update_volume_status(self, *_):
"""Updates the volume icon based on speaker status."""
stream = self.audio_service.speaker
if stream:
volume_level = stream.volume
self.volume_icon_button.set_markup(
icons.vol_off
if stream.muted
else (
icons.vol_mute
if volume_level == 0
else (icons.vol_medium if volume_level <= 50 else icons.vol_high)
)
)
def update_mic_status(self, *_):
"""Updates the microphone icon based on mute status."""
mic = self.audio_service.microphone
self.microphone_icon.set_markup(icons.mic_off if mic and mic.muted else "")
self.microphone_icon.set_visible(bool(mic))
def update_bluetooth_status(self, *_):
"""Updates the Bluetooth icon based on enabled status."""
self.bluetooth_icon.set_markup(
icons.bluetooth if self.bluetooth_client.enabled else icons.bluetooth_off
)
def update_network_status(self, *_):
primary_device = self.network_client.primary_device
if primary_device == "wifi" and self.network_client.wifi_device:
wifi_device = self.network_client.wifi_device
self.set_tooltip_text(self.network_client.wifi_device.ssid)
if wifi_device.enabled:
wifi_strength = wifi_device.strength
if wifi_strength == -1: # No active connection
icon_label = icons.wifi_off
else:
icon_label = {
80: icons.wifi,
60: icons.good_signal,
40: icons.moderate_signal,
20: icons.weak_signal,
0: icons.no_signal,
}.get(min(80, 20 * round(wifi_strength / 20)), icons.wifi_off)
else:
icon_label = icons.wifi_off
elif primary_device == "wired":
icon_label = icons.lan
else:
icon_label = icons.wifi_off
self.wifi_icon.set_markup(icon_label)
-33
View File
@@ -1,33 +0,0 @@
from fabric.audio.service import Audio
from fabric.widgets.box import Box
from snippets import MaterialIcon
class MicrophoneIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.audio_service = Audio()
self.microphone_icon = self.create_microphone_icon()
self.audio_service.connect("microphone_changed", self.update_microphone_status)
self.update_microphone_status()
self.children = (self.microphone_icon,)
def create_microphone_icon(self):
return MaterialIcon("mic_off", size=16)
def update_microphone_status(self, *args):
current_microphone = self.audio_service.microphone
if current_microphone:
is_muted = current_microphone.muted
icon_name = "mic_off" if is_muted else "mic"
self.microphone_icon.set_label(icon_name)
self.microphone_icon.set_visible(is_muted)
else:
self.microphone_icon.set_visible(False)
return True
-84
View File
@@ -1,84 +0,0 @@
import subprocess
import psutil
from fabric import Fabricator
from fabric.widgets.box import Box
from snippets import MaterialIcon
class NetworkIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.update_interval = 5000 # Increased interval to 5 seconds
self.last_status = None
self.wifi_strength_cache = None
self.fabricator = Fabricator(
interval=self.update_interval,
poll_from=self.update_network_status,
)
def update_network_status(self, *_):
current_status = self.get_network_status()
if current_status != self.last_status:
self.update_icon(current_status)
self.last_status = current_status
return True
def update_icon(self, current_status):
self.icon_label = MaterialIcon(current_status, size=16)
self.children = (self.icon_label,)
def get_network_status(self):
if self.is_ethernet_connected():
return "lan"
if self.is_wifi_connected():
return self.get_wifi_strength_icon()
return "signal_wifi_off"
def get_wifi_strength_icon(self):
wifi_strength = self.check_wifi_strength()
if wifi_strength is not None:
self.wifi_strength_cache = wifi_strength # Cache the result
else:
wifi_strength = self.wifi_strength_cache # Use cached value if available
return self.wifi_signal_icon(wifi_strength)
def wifi_signal_icon(self, strength):
if strength is None or strength <= -100:
return "signal_wifi_0_bar"
if strength > -50:
return "signal_wifi_4_bar"
if strength > -60:
return "network_wifi_3_bar"
if strength > -70:
return "network_wifi_2_bar"
return "network_wifi_1_bar"
def check_wifi_strength(self):
try:
result = subprocess.run(["iwconfig"], capture_output=True, text=True)
for line in result.stdout.splitlines():
if "Signal level" in line:
signal_level = line.split("Signal level=")[1].split(" ")[0]
return int(signal_level.strip().replace("dBm", ""))
return None
except Exception:
return None
def is_wifi_connected(self):
return any(
self._is_interface_up(interface, ["wlan", "wifi"])
for interface in psutil.net_if_addrs()
)
def is_ethernet_connected(self):
return any(
self._is_interface_up(interface, ["enp", "eth"])
for interface in psutil.net_if_addrs()
)
def _is_interface_up(self, interface, keywords):
if any(substring in interface.lower() for substring in keywords):
return psutil.net_if_stats().get(interface, {}).isup
return False
+63 -68
View File
@@ -1,88 +1,83 @@
from time import sleep
import psutil
from fabric import Fabricator
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.circularprogressbar import CircularProgressBar
from gi.repository import GLib
from fabric.widgets.label import Label
from fabric.widgets.box import Box
from fabric.widgets.overlay import Overlay
from snippets import MaterialIcon
from fabric.widgets.circularprogressbar import CircularProgressBar
import snippets.iconss as icons
class SystemInfo(Button):
ICONS = [
("CPU", "memory"),
("Swap", "swap_horiz"),
("RAM", "memory_alt"),
("Temp", "thermometer"),
]
class SystemInfo(Box):
ICONS = {
"CPU": icons.cpu,
"RAM": icons.memory,
"Swap": icons.swap,
"Disk": icons.disk,
"Temp": icons.temp,
}
def __init__(self):
super().__init__(child=self._initialize_content())
Fabricator(
poll_from=self._refresh_system_info,
stream=True,
on_changed=lambda fab, data: self._update_ui(data),
def __init__(self, **kwargs):
super().__init__(
name="system-info",
spacing=8,
h_align="center",
v_align="fill",
visible=True,
all_visible=True,
)
def _initialize_content(self):
self.box = Box(orientation="h", name="tray")
self.progress_bars = {}
self.icons = {}
self.progress_bars = {
system: CircularProgressBar(
name="stats-progress",
line_style="round",
line_width=3,
size=28,
start_at=0,
end_at=0.5,
start_angle=0,
end_angle=180,
)
for system in self.ICONS
}
for system, icon_name in self.ICONS:
self._create_system_item(system, icon_name)
for system, icon_name in self.ICONS.items():
overlay = Overlay(
child=self.progress_bars[system],
overlays=[Label(name="stats-icons", markup=icon_name)],
)
self.add(overlay)
return self.box
GLib.timeout_add_seconds(1, self._update_system_info)
def _create_system_item(self, system, icon_name):
self.progress_bars[system] = self._create_progress_bar()
self.icons[system] = self._create_icon(icon_name)
def _update_system_info(self):
ram_usage = psutil.virtual_memory().percent
swap_usage = psutil.swap_memory().percent
disk_usage = psutil.disk_usage("/").percent
cpu = psutil.cpu_percent(interval=0)
overlay = Overlay(
child=self.progress_bars[system], overlays=[self.icons[system]]
)
self.box.pack_start(overlay, False, False, 4)
usages = {
"CPU": cpu,
"RAM": ram_usage,
"Swap": swap_usage,
"Disk": disk_usage,
"Temp": self._get_device_temperature(),
}
def _create_progress_bar(self):
return CircularProgressBar(
name="progress",
line_style="round",
line_width=3,
size=28,
start_at=0,
end_at=0.5,
# start_angle=180,
end_angle=180,
)
for system, usage in usages.items():
self.progress_bars[system].value = usage / 100.0
self.progress_bars[system].set_tooltip_text(f"{system} {usage:.1f}%")
def _create_icon(self, icon_name):
return MaterialIcon(icon_name, 16)
def _refresh_system_info(self, fab: Fabricator):
while True:
yield {
"CPU": round(psutil.cpu_percent()),
"RAM": round(psutil.virtual_memory().percent),
"Swap": round(psutil.swap_memory().percent),
"Temp": self._get_device_temperature(),
}
sleep(1)
return True
@staticmethod
def _get_device_temperature():
try:
temps = psutil.sensors_temperatures()
if "coretemp" in temps:
return round(temps["coretemp"][0].current, 1)
elif "cpu_thermal" in temps:
return round(temps["cpu_thermal"][0].current, 1)
return None
for key in ("coretemp", "cpu_thermal"):
if key in temps:
return round(temps[key][0].current, 1)
except Exception:
return None
def _update_ui(self, usages):
for system, usage in usages.items():
self.progress_bars[system].value = usage / 100
self.progress_bars[system].set_tooltip_text(f"{system} {usage}%")
pass
return None
+1 -1
View File
@@ -20,7 +20,7 @@ class PagerClient(TypedDict):
class TaskBar(Box):
def __init__(self, icon_size: int = 20, **kwargs):
def __init__(self, icon_size: int = 22, **kwargs):
super().__init__(
orientation="h",
spacing=7,
+103
View File
@@ -0,0 +1,103 @@
import json
from fabric.utils import exec_shell_command_async, get_relative_path, invoke_repeater
from fabric.widgets.box import Box
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.label import Label
from fabric.widgets.stack import Stack
import snippets.iconss as icons
from loguru import logger
from gi.repository import Gtk
from fabric.widgets.button import Button
class UpdatesWidget(Button):
"""A widget to display the number of available updates."""
def __init__(
self,
**kwargs,
):
# Initialize the button with specific name and style
super().__init__(name="button-bar", **kwargs)
self.script_file = get_relative_path("../../../config/scripts/systemupdates.sh")
self.update_level_label = Label(
name="update-label",
label="0",
visible=False, # Initially hidden
)
self.update_icon = Label(name="update-icon", markup=icons.update)
self.updated_icon = Label(name="updated-icon", markup=icons.updated)
self.update_box = CenterBox(
center_children=[self.update_icon, self.update_level_label],
)
self.updated_box = CenterBox(
center_children=[self.updated_icon],
)
self.stack = Stack()
self.stack.add_named(self.update_box, "update_box")
self.stack.add_named(self.updated_box, "updated_box")
self.stack.set_visible_child_name("update_box")
self.children = Box(
children=self.stack,
)
# Ensure the stack resizes to the current visible child
self.stack.set_homogeneous(False)
self.stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
# Connect the button press event
self.connect("button-press-event", self.on_button_press)
# Set up a repeater to call the update method at specified intervals
invoke_repeater(60000, self.update, initial_call=True)
def update_values(self, value: str):
logger.info(f"[Updates] Received update JSON: {value}")
try:
value = json.loads(value)
except json.JSONDecodeError:
logger.error("[Updates] Failed to parse update JSON")
return False
update_count = str(value["total"])
self.update_level_label.set_label(update_count)
if update_count == "0":
self.stack.set_visible_child_name("updated_box")
self.update_level_label.set_visible(False)
else:
self.stack.set_visible_child_name("update_box")
self.update_level_label.set_visible(True)
self.set_tooltip_text(value.get("tooltip", ""))
return True
def on_button_press(self, _, event):
if event.button == 1:
exec_shell_command_async(
f"{self.script_file} -arch -up",
lambda _: None,
)
self.update()
return True
else:
self.update()
def update(self):
logger.info("[Updates] Checking for updates...")
# Execute the update script asynchronously and update values
exec_shell_command_async(
f"{self.script_file} -arch",
lambda output: self.update_values(output),
)
return True
-81
View File
@@ -1,81 +0,0 @@
from fabric import Fabricator
from fabric.utils import exec_shell_command
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from snippets import MaterialIcon
from services import audio
VOLUME_ICONS = {
"muted": "volume_off",
"low": "volume_mute",
"medium-low": "volume_down",
"medium-high": "volume_up",
"high": "volume_up",
}
class VolumeIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.audio_service = audio
self.volume_icon_button = self.create_volume_icon()
self.volume_icon_button.connect("button-press-event", self.on_button_press)
self.audio_service.connect("changed", self.update_volume_status)
Fabricator(interval=1000, poll_from=self.update_volume_status)
self.children = (self.volume_icon_button,)
def create_volume_icon(self):
icon = MaterialIcon(VOLUME_ICONS["muted"], size=16)
button = Button(child=icon)
button.icon_widget = icon
return button
def on_button_press(self, widget, event):
if event.button == 1:
self.toggle_mute()
elif event.button == 3:
self.open_audio_settings()
def toggle_mute(self):
current_stream = self.audio_service.speaker
if current_stream:
current_stream.muted = not current_stream.muted
# self.update_volume_status()
def open_audio_settings(self):
exec_shell_command("pavucontrol")
def update_volume_status(self, *_):
current_stream = self.audio_service.speaker
if current_stream:
self.update_icon(current_stream)
return True
def update_icon(self, stream):
if stream.muted:
icon_name = VOLUME_ICONS["muted"]
volume_percentage = 0
tooltip_text = "Volume Muted"
else:
volume_level = stream.volume
icon_name = self.get_volume_icon(volume_level)
volume_percentage = int(volume_level)
tooltip_text = f"Volume: {volume_percentage}%"
self.volume_icon_button.icon_widget.set_label(icon_name)
self.volume_icon_button.set_tooltip_text(tooltip_text)
def get_volume_icon(self, volume_level):
if volume_level == 0:
return VOLUME_ICONS["low"]
elif volume_level <= 33:
return VOLUME_ICONS["medium-low"]
elif volume_level <= 66:
return VOLUME_ICONS["medium-high"]
else:
return VOLUME_ICONS["high"]