initial commit

This commit is contained in:
sankalp committed 2025-02-13 16:40:47 +05:45
1 parent df38ce0c52
commit 15766be9c3
137 files changed
+23845 -9

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
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 *
+68
View File
@@ -0,0 +1,68 @@
import psutil
from fabric import Fabricator
from fabric.widgets.box import Box
from fabric.widgets.overlay import Overlay
from fabric.widgets.circularprogressbar import CircularProgressBar
from snippets import MaterialIcon
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=1, size=24
)
self.battery_overlay = Overlay(child=self.progress_bar, overlays=battery_icon)
self.children = 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'}"
)
return True
+34
View File
@@ -0,0 +1,34 @@
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)
+27
View File
@@ -0,0 +1,27 @@
from fabric import Fabricator
from fabric.utils import exec_shell_command
from fabric.widgets.box import Box
from snippets import MaterialIcon
class IdleIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.idle_icon = self.create_idle_icon()
self.fabricator = Fabricator(interval=1000, poll_from=self.check_idle_status)
self.children = [self.idle_icon]
def create_idle_icon(self):
return MaterialIcon("coffee", 16)
def check_idle_status(self, *_):
output = exec_shell_command("pidof wayland-idle-inhibitor.py")
is_idle = bool(str(output).strip())
self.update_idle_status(is_idle)
def update_idle_status(self, is_idle):
if is_idle:
self.idle_icon.set_visible(True)
else:
self.idle_icon.set_visible(False)
+33
View File
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,84 @@
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
+44
View File
@@ -0,0 +1,44 @@
from fabric import Fabricator
from fabric.utils import exec_shell_command
from fabric.widgets.box import Box
from snippets import MaterialIcon
PROFILE_ICONS = {
"power-saver": "data_saver_on",
"balanced": None,
"performance": "mode_heat",
}
class PowerProfileIndicator(Box):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.profile_icon = None
self.fabricator = Fabricator(interval=1000, poll_from=self.update_power_profile)
def update_power_profile(self, *_):
profile = self.get_power_profile()
self.update_icon(profile)
return True
def get_power_profile(self):
result = exec_shell_command("powerprofilesctl get")
return result.strip() or "power-saver"
def update_icon(self, profile):
icon_name = PROFILE_ICONS.get(profile)
new_icon = MaterialIcon(icon_name, 16) if icon_name else None
if self.profile_icon:
self.remove(self.profile_icon)
if new_icon:
self.profile_icon = new_icon
self.add(self.profile_icon)
else:
if self.profile_icon:
self.profile_icon.hide()
self.profile_icon = None
+94
View File
@@ -0,0 +1,94 @@
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 fabric.widgets.label import Label
from fabric.widgets.overlay import Overlay
from snippets import MaterialIcon
class SystemInfo(Button):
ICONS = [
("CPU", "settings_motion_mode"),
("Swap", "swap_horiz"),
("RAM", "memory_alt"),
("Temp", "thermometer"),
]
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 _initialize_content(self):
self.box = Box(orientation="h", name="tray")
self.progress_bars = {}
self.labels = {}
self.icons = {}
for system, icon_name in self.ICONS:
self._create_system_item(system, icon_name)
return self.box
def _create_system_item(self, system, icon_name):
self.progress_bars[system] = self._create_progress_bar()
self.labels[system] = self._create_label()
self.icons[system] = self._create_icon(icon_name)
overlay = Overlay(
child=self.progress_bars[system], overlays=[self.icons[system]]
)
self.box.pack_start(overlay, False, False, 0)
self.box.pack_start(self.labels[system], False, False, 0)
def _create_progress_bar(self):
return CircularProgressBar(
name="progress", line_style="round", line_width=1, size=24
)
def _create_label(self):
return Label(style="font-size:14px; margin: 4px;")
def _create_icon(self, icon_name):
return MaterialIcon(icon_name, size=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)
@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
except Exception:
return None
def _update_ui(self, usages):
for system, usage in usages.items():
if usage is None:
usage = 0
self.labels[system].set_label("N/A")
else:
usage = int(usage)
self.progress_bars[system].value = usage / 100
self.labels[system].set_label(f"{usage}")
self.progress_bars[system].set_tooltip_text(f"{system} Usage: {usage}%")
+154
View File
@@ -0,0 +1,154 @@
import json
import os
from typing import TypedDict
from fabric.hyprland.widgets import get_hyprland_connection
from fabric.utils import exec_shell_command_async
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.image import Image
from gi.repository import GdkPixbuf, GLib, Gtk
from loguru import logger
class PagerClient(TypedDict):
title: str
initialClass: str
mapped: bool
hidden: bool
address: str
class TaskBar(Box):
def __init__(self, icon_size: int = 20, **kwargs):
super().__init__(
orientation="h",
spacing=7,
name="taskbar",
**kwargs,
)
self.connection = get_hyprland_connection()
self.icon_size = icon_size
self.icon_theme = Gtk.IconTheme.get_default()
self.set_visible(False)
if self.connection.ready:
self.render_with_delay()
else:
self.connection.connect("event::ready", lambda *_: self.render_with_delay())
for event in (
"activewindow",
"openwindow",
"closewindow",
"changefloatingmode",
):
self.connection.connect("event::" + event, lambda *_: self.render(None))
def render_with_delay(self, *_):
GLib.timeout_add(101, self.render)
def render(self, *_):
self.children = []
clients = self.fetch_clients()
active_window_address = self.get_active_window_address()
visible_clients = [
client for client in clients if client["mapped"] and not client["hidden"]
]
if visible_clients:
for client in visible_clients:
window_class = client["initialClass"].lower()
icon = self.bake_window_icon(window_class)
button = Button(image=icon, tooltip_text=client["title"])
if client["address"] == active_window_address:
button.add_style_class("activated")
button.connect(
"button-press-event",
self.on_icon_click,
client["address"],
)
self.add(button)
self.set_visible(True)
self.show_all()
else:
self.set_visible(False)
self.set_visible(len(self.children) > 0)
if self.children:
self.show_all()
def get_active_window_address(self) -> str:
active_window_info = json.loads(
self.connection.send_command("j/activewindow").reply.decode()
)
return active_window_info.get("address", "")
def on_icon_click(self, widget, event, address):
exec_shell_command_async(
f"hyprctl dispatch focuswindow address:{address}", lambda *_: None
)
def fetch_clients(self) -> list[PagerClient]:
return json.loads(self.connection.send_command("j/clients").reply.decode())
def bake_window_icon(
self,
window_class: str,
fallback_icon: str = "image-missing",
) -> Image:
icon_name = self.get_icon_from_desktop_entry(window_class)
if icon_name:
pixbuf = self.load_icon(icon_name)
else:
pixbuf = self.load_icon(window_class, fallback_icon)
return Image(pixbuf=pixbuf, size=self.icon_size)
def get_icon_from_desktop_entry(self, window_class: str) -> str:
for data_dir in GLib.get_system_data_dirs():
applications_dir = os.path.join(data_dir, "applications")
if os.path.isdir(applications_dir):
for desktop_file in os.listdir(applications_dir):
if desktop_file.endswith(".desktop"):
file_path = os.path.join(applications_dir, desktop_file)
try:
with open(file_path) as f:
app_name = None
icon_name = None
for line in f:
if line.startswith("Name="):
app_name = line.split("=", 2)[1].strip().lower()
elif line.startswith("Icon=") and app_name:
icon_name = line.split("=", 2)[1].strip()
if window_class in app_name:
return icon_name
except Exception as e:
logger.error(f"Error reading {file_path}: {e}")
return None
def load_icon(
self,
icon_name: str,
fallback_icon: str = "image-missing",
) -> GdkPixbuf.Pixbuf:
try:
pixbuf = self.icon_theme.load_icon(
icon_name,
self.icon_size,
Gtk.IconLookupFlags.FORCE_SIZE,
)
except Exception:
pixbuf = self.icon_theme.load_icon(
fallback_icon,
self.icon_size,
Gtk.IconLookupFlags.FORCE_SIZE,
)
return pixbuf
+81
View File
@@ -0,0 +1,81 @@
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"]
+19
View File
@@ -0,0 +1,19 @@
import json
from fabric.hyprland.service import Hyprland
from fabric.widgets.label import Label
connection = Hyprland()
workspaceData = connection.send_command("j/activeworkspace").reply
activeWorkspace = json.loads(workspaceData.decode("utf-8"))["name"]
workspace = Label(label=f"Workspace {activeWorkspace}")
def on_workspace(obj, signal):
global activeWorkspace
activeWorkspace = json.loads(signal.data[0])
workspace.set_label(f"Workspace {activeWorkspace}")
connection.connect("event::workspace", on_workspace)