update
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Dict, List, Literal
|
||||
|
||||
import gi
|
||||
import psutil
|
||||
from fabric.utils import exec_shell_command, exec_shell_command_async, get_relative_path
|
||||
from gi.repository import Gdk, GLib, Gtk
|
||||
from loguru import logger
|
||||
|
||||
from .colors import Colors
|
||||
from .icons import distro_text_icons
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
||||
|
||||
class ExecutableNotFoundError(ImportError):
|
||||
"""Raised when an executable is not found."""
|
||||
|
||||
def __init__(self, executable_name: str):
|
||||
super().__init__(
|
||||
f"{Colors.ERROR}Executable {Colors.UNDERLINE}{executable_name}{Colors.RESET} not found. Please install it using your package manager." # noqa: E501
|
||||
)
|
||||
|
||||
|
||||
# Function to escape the markup
|
||||
def parse_markup(text):
|
||||
return text
|
||||
|
||||
|
||||
# support for multiple monitors
|
||||
def for_monitors(widget):
|
||||
n = Gdk.Display.get_default().get_n_monitors() if Gdk.Display.get_default() else 1
|
||||
return [widget(i) for i in range(n)]
|
||||
|
||||
|
||||
# Function to get the system icon theme
|
||||
def copy_theme(theme: str):
|
||||
destination_file = get_relative_path("../styles/theme.scss")
|
||||
source_file = get_relative_path(f"../styles/themes/{theme}.scss")
|
||||
|
||||
if not os.path.exists(source_file):
|
||||
logger.warning(
|
||||
f"{Colors.WARNING}Warning: The theme file '{theme}.scss' was not found. Using default theme." # noqa: E501
|
||||
)
|
||||
source_file = get_relative_path("../styles/themes/catpuccin-mocha.scss")
|
||||
|
||||
try:
|
||||
with open(source_file, "r") as source_file:
|
||||
content = source_file.read()
|
||||
|
||||
# Open the destination file in write mode
|
||||
with open(destination_file, "w") as destination_file:
|
||||
destination_file.write(content)
|
||||
logger.info(f"{Colors.INFO}[THEME] '{theme}' applied successfully.")
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error(
|
||||
f"{Colors.ERROR}Error: The theme file '{source_file}' was not found."
|
||||
)
|
||||
exit(1)
|
||||
|
||||
|
||||
# Merge the parsed data with the default configuration
|
||||
def merge_defaults(data: dict, defaults: dict):
|
||||
return {**defaults, **data}
|
||||
|
||||
|
||||
# Validate the widgets
|
||||
def validate_widgets(parsed_data, default_config):
|
||||
layout = parsed_data["layout"]
|
||||
for section in layout:
|
||||
for widget in layout[section]:
|
||||
if widget not in default_config:
|
||||
raise ValueError(
|
||||
f"Invalid widget {widget} found in section {section}. Please check the widget name." # noqa: E501
|
||||
)
|
||||
|
||||
|
||||
# Function to exclude keys from a dictionary )
|
||||
def exclude_keys(d: Dict, keys_to_exclude: List[str]) -> Dict:
|
||||
return {k: v for k, v in d.items() if k not in keys_to_exclude}
|
||||
|
||||
|
||||
# Function to format time in hours and minutes
|
||||
def format_time(secs: int):
|
||||
mm, _ = divmod(secs, 60)
|
||||
hh, mm = divmod(mm, 60)
|
||||
return "%d h %02d min" % (hh, mm)
|
||||
|
||||
|
||||
# Function to convert bytes to kilobytes, megabytes, or gigabytes
|
||||
def convert_bytes(bytes: int, to: Literal["kb", "mb", "gb"], format_spec=".1f"):
|
||||
multiplier = 1
|
||||
|
||||
if to == "mb":
|
||||
multiplier = 2
|
||||
elif to == "gb":
|
||||
multiplier = 3
|
||||
|
||||
return f"{format(bytes / (1024**multiplier), format_spec)}{to.upper()}"
|
||||
|
||||
|
||||
# Function to get the system uptime
|
||||
def uptime():
|
||||
boot_time = psutil.boot_time()
|
||||
now = datetime.datetime.now()
|
||||
|
||||
diff = now.timestamp() - boot_time
|
||||
|
||||
# Convert the difference in seconds to hours and minutes
|
||||
hours, remainder = divmod(diff, 3600)
|
||||
minutes, _ = divmod(remainder, 60)
|
||||
|
||||
return f"{int(hours):02}:{int(minutes):02}"
|
||||
|
||||
|
||||
# Function to convert seconds to milliseconds
|
||||
def convert_seconds_to_milliseconds(seconds: int):
|
||||
return seconds * 1000
|
||||
|
||||
|
||||
# Function to check if an icon exists, otherwise use a fallback icon
|
||||
def check_icon_exists(icon_name: str, fallback_icon: str) -> str:
|
||||
if Gtk.IconTheme.get_default().has_icon(icon_name):
|
||||
return icon_name
|
||||
return fallback_icon
|
||||
|
||||
|
||||
# Function to execute a shell command asynchronously
|
||||
def play_sound(file: str):
|
||||
exec_shell_command_async(f"play {file}", None)
|
||||
|
||||
|
||||
# Function to get the distro icon
|
||||
def get_distro_icon():
|
||||
distro_id = GLib.get_os_info("ID")
|
||||
|
||||
# Search for the icon in the list
|
||||
return distro_text_icons.get(distro_id, "")
|
||||
|
||||
|
||||
# Function to check if an executable exists
|
||||
def executable_exists(executable_name):
|
||||
executable_path = shutil.which(executable_name)
|
||||
return bool(executable_path)
|
||||
|
||||
|
||||
def send_notification(
|
||||
title: str,
|
||||
body: str,
|
||||
urgency: Literal["low", "normal", "critical"],
|
||||
icon=None,
|
||||
app_name="Application",
|
||||
timeout=None,
|
||||
):
|
||||
"""
|
||||
Sends a notification using the notify-send command.
|
||||
:param title: The title of the notification
|
||||
:param body: The message body of the notification
|
||||
:param urgency: The urgency of the notification ('low', 'normal', 'critical')
|
||||
:param icon: Optional icon for the notification
|
||||
:param app_name: The application name that is sending the notification
|
||||
:param timeout: Optional timeout in milliseconds (e.g., 5000 for 5 seconds)
|
||||
"""
|
||||
# Base command
|
||||
command = [
|
||||
"notify-send",
|
||||
"--urgency",
|
||||
urgency,
|
||||
"--app-name",
|
||||
app_name,
|
||||
title,
|
||||
body,
|
||||
]
|
||||
|
||||
# Add icon if provided
|
||||
if icon:
|
||||
command.extend(["--icon", icon])
|
||||
|
||||
if timeout is not None:
|
||||
command.extend(["-t", str(timeout)])
|
||||
|
||||
try:
|
||||
subprocess.run(command, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to send notification: {e}")
|
||||
|
||||
|
||||
# Function to get the relative time
|
||||
def get_relative_time(mins: int) -> str:
|
||||
# Seconds
|
||||
if mins == 0:
|
||||
return "now"
|
||||
|
||||
# Minutes
|
||||
if mins < 60:
|
||||
return f"{mins} minute{'s' if mins > 1 else ''} ago"
|
||||
|
||||
# Hours
|
||||
if mins < 1440:
|
||||
hours = mins // 60
|
||||
return f"{hours} hour{'s' if hours > 1 else ''} ago"
|
||||
|
||||
# Days
|
||||
days = mins // 1440
|
||||
return f"{days} day{'s' if days > 1 else ''} ago"
|
||||
|
||||
|
||||
# Function to get the percentage of a value
|
||||
def convert_to_percent(
|
||||
current: int | float, max: int | float, is_int=True
|
||||
) -> int | float:
|
||||
if is_int:
|
||||
return int((current / max) * 100)
|
||||
else:
|
||||
return (current / max) * 100
|
||||
|
||||
|
||||
# Function to ensure the directory exists
|
||||
def ensure_dir_exists(path: str):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
# Function to unique list
|
||||
def unique_list(lst) -> List:
|
||||
return list(set(lst))
|
||||
|
||||
|
||||
# Function to check if an app is running
|
||||
def is_app_running(app_name: str) -> bool:
|
||||
return len(exec_shell_command(f"pidof {app_name}")) != 0
|
||||
Reference in New Issue
Block a user